throw new Exception('Unable to parse image', 500);

$image->crop((int) $width, (int) $height);

keep keep keep keep replace keep keep keep

throw new Exception('Unable to parse image', 500, Exception::AVATAR_CANNOT_PARSE_IMAGE);

<?php

/**
 * List of server wide error codes and their respective messages.
 */

use Appwrite\Extend\Exception;

return [
    /** General Errors */
    Exception::UNKNOWN_ORIGIN => [
        'name' => Exception::UNKNOWN_ORIGIN,
        'description' => 'The request originated from a non-whitelisted origin. If you trust this origin, please add it as a platform in the Appwrite console.',
        'statusCode' => 403,
    ],
    Exception::SERVICE_DISABLED => [
        'name' => Exception::SERVICE_DISABLED,
        'description' => 'The requested service is disabled. You can enable/disable a service from the Appwrite console or by contacting the project owner.',
        'statusCode' => 503,
    ],
    Exception::UNAUTHORIZED_SCOPE => [
        'name' => Exception::UNAUTHORIZED_SCOPE,
        'description' => 'The current user or API key does not have the required scopes to access the requested resource.',
        'statusCode' => 401,
    ],
    Exception::STORAGE_ERROR => [
        'name' => Exception::STORAGE_ERROR,
        'description' => 'Storage error',
        'statusCode' => 500,
    ],
    Exception::RATE_LIMIT_EXCEEDED => [
        'name' => Exception::RATE_LIMIT_EXCEEDED,
        'description' => 'Rate limit for the current endpoint has been exceeded. Please try again after some time.',
        'statusCode' => 429,
    ],
    Exception::SMTP_DISABLED => [
        'name' => Exception::SMTP_DISABLED,
        'description' => 'SMTP is disabled on your Appwrite instance. Please contact your project ',
        'statusCode' => 503,
    ],

    /** Project Errors */
    Exception::PROJECT_NOT_FOUND => [
        'name' => Exception::PROJECT_NOT_FOUND,
        'description' => 'The requested project could not be found. Please check the value of the X-Appwrite-Project header to ensure the correct project ID is being used.',
        'statusCode' => 404,
    ],
    Exception::PROJECT_UNKNOWN => [
        'name' => Exception::PROJECT_UNKNOWN,
        'description' => 'The project ID is either missing or not valid. Please check the value of the X-Appwrite-Project header to ensure the correct project ID is being used.',
        'statusCode' => 400,
    ],

    /** User Errors */
    Exception::USER_COUNT_EXCEEDED => [
        'name' => Exception::USER_COUNT_EXCEEDED,
        'description' => 'The current project has exceeded the maximum number of users. Please check your user limit in the Appwrite console.',
        'statusCode' => 501,
    ],
    Exception::USER_EMAIL_NOT_WHITELISTED => [
        'name' => Exception::USER_EMAIL_NOT_WHITELISTED,
        'description' => 'The user\'s email is not part of the whitelist. Please check the _APP_CONSOLE_WHITELIST_EMAILS environment variable of your Appwrite server.',
        'statusCode' => 401,
    ],
    Exception::USER_PASSWORD_RESET_REQUIRED => [
        'name' => Exception::USER_PASSWORD_RESET_REQUIRED,
        'description' => 'The current user requires a password reset.',
        'statusCode' => 412,
    ],
    Exception::USER_IP_NOT_WHITELISTED => [
        'name' => Exception::USER_IP_NOT_WHITELISTED,
        'description' => 'The user\'s IP address is not part of the whitelist. Please check the _APP_CONSOLE_WHITELIST_IPS environment variable of your Appwrite server.',
        'statusCode' => 401,
    ],
    Exception::USER_INVALID_CREDENTIALS => [
        'name' => Exception::USER_INVALID_CREDENTIALS,
        'description' => 'Invalid credentials. Please check the email and password.',
        'statusCode' => 401,
    ],
    Exception::USER_ALREADY_EXISTS => [
        'name' => Exception::USER_ALREADY_EXISTS,
        'description' => 'A user with the same email ID already exists in your project.',
        'statusCode' => 409,
    ],
    Exception::USER_INVALID_TOKEN => [
        'name' => Exception::USER_INVALID_TOKEN,
        'description' => 'Invalid token.',
        'statusCode' => 401,
    ],
    Exception::USER_BLOCKED => [
        'name' => Exception::USER_BLOCKED,
        'description' => 'The current user has been blocked. Please contact the project administrator for more information.',
        'statusCode' => 401,
    ],
    Exception::USER_ANONYMOUS_CONSOLE_PROHIBITED => [
        'name' => Exception::USER_ANONYMOUS_CONSOLE_PROHIBITED,
        'description' => 'Anonymous users cannot be created for console project.',
        'statusCode' => 401,
    ],
    Exception::USER_SESSION_ALREADY_EXISTS => [
        'name' => Exception::USER_SESSION_ALREADY_EXISTS,
        'description' => 'Cannot create anonymous user when a session is active.',
        'statusCode' => 401,
    ],
    Exception::USER_CREATION_FAILED => [
        'name' => Exception::USER_CREATION_FAILED,
        'description' => 'There was an internal server error while creating the user.',
        'statusCode' => 500,
    ],
    Exception::USER_NOT_FOUND => [
        'name' => Exception::USER_NOT_FOUND,
        'description' => 'User with the requested ID could not be found.',
        'statusCode' => 404,
    ],
    Exception::USER_EMAIL_ALREADY_EXISTS => [
        'name' => Exception::USER_EMAIL_ALREADY_EXISTS,
        'description' => 'Another user with the same email already exists in the current project.',
        'statusCode' => 409,
    ],
    Exception::USER_PASSWORD_MISMATCH => [
        'name' => Exception::USER_PASSWORD_MISMATCH,
        'description' => 'Passwords do not match. Please recheck.',
        'statusCode' => 400,
    ],
    Exception::USER_SESSION_NOT_FOUND => [
        'name' => Exception::USER_SESSION_NOT_FOUND,
        'description' => 'The current user session could not be found.',
        'statusCode' => 404,
    ],
    Exception::USER_AUTH_METHOD_UNSUPPORTED => [
        'name' => Exception::USER_AUTH_METHOD_UNSUPPORTED,
        'description' => 'The requested authentication method is either disabled or unsupported.',
        'statusCode' => 501,
    ],

    /** OAuth Errors */
    Exception::OAUTH_PROVIDER_DISABLED => [
        'name' => Exception::OAUTH_PROVIDER_DISABLED,
        'description' => 'The chosen OAuth provider is disabled. Please contact your project administrator for more information.',
        'statusCode' => 412,
    ],
    Exception::OAUTH_PROVIDER_UNSUPPORTED => [
        'name' => Exception::OAUTH_PROVIDER_UNSUPPORTED,
        'description' => 'The chosen OAuth provider is unsupported.',
        'statusCode' => 501,
    ],
    Exception::OAUTH_INVALID_LOGIN_STATE_PARAMS => [
        'name' => Exception::OAUTH_INVALID_LOGIN_STATE_PARAMS,
        'description' => 'Failed to parse the login state params from the OAuth provider.',
        'statusCode' => 500,
    ],
    Exception::OAUTH_INVALID_SUCCESS_URL => [
        'name' => Exception::OAUTH_INVALID_SUCCESS_URL,
        'description' => 'Invalid URL received for OAuth success redirect.',
        'statusCode' => 400,
    ],
    Exception::OAUTH_INVALID_FAILURE_URL => [
        'name' => Exception::OAUTH_INVALID_FAILURE_URL,
        'description' => 'Invalid URL received for OAuth failure redirect.',
        'statusCode' => 400,
    ],
    Exception::OAUTH_ACCESS_TOKEN_FAILED => [
        'name' => Exception::OAUTH_ACCESS_TOKEN_FAILED,
        'description' => 'Failed to obtain access token from the OAuth provider.',
        'statusCode' => 500,
    ],
    Exception::OAUTH_MISSING_USER_ID => [
        'name' => Exception::OAUTH_MISSING_USER_ID,
        'description' => 'Failed to obtain user id from the OAuth provider.',
        'statusCode' => 400,
    ],

    /** Avatars */
    Exception::AVATAR_SET_NOT_FOUND => [
        'name' => Exception::AVATAR_SET_NOT_FOUND,
        'description' => 'The requested avatar set could not be found.',
        'statusCode' => 404
    ],
    Exception::AVATAR_NOT_FOUND => [
        'name' => Exception::AVATAR_NOT_FOUND,
        'description' => 'The request avatar could not be found.',
        'statusCode' => 404,
    ],
    Exception::IMAGIC_EXTENSION_MISSING => [
        'name' => Exception::IMAGIC_EXTENSION_MISSING,
        'description' => 'The Imagic extension could not be found.',
        'statusCode' => 500,
    ],
    Exception::AVATAR_IMAGE_NOT_FOUND => [
        'name' => Exception::AVATAR_IMAGE_NOT_FOUND,
        'description' => 'The requested image was not found.',
        'statusCode' => 404,
    ],

    /** Files */
    Exception::FILE_NOT_FOUND => [
        'name' => Exception::FILE_NOT_FOUND,
        'description' => 'The requested file could not be found.',
        'statusCode' => 404,
    ],
    Exception::FILE_NOT_READABLE => [
        'name' => Exception::FILE_NOT_READABLE,
        'description' => 'There was an error reading the file from disk.',
        'statusCode' => 500,
    ],
];


let sdk = new Appwrite();

sdk
    .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
    .setProject('5df5acd0d48c2') // Your project ID
;

let promise = sdk.account.updateEmail('email@example.com', 'password');

promise.then(function (response) {
    console.log(response); // Success
}, function (error) {
    console.log(error); // Failure
});


require 'appwrite'

client = Appwrite::Client.new

client
    .set_endpoint('https://[HOSTNAME_OR_IP]/v1') # Your API Endpoint
    .set_project('5df5acd0d48c2') # Your project ID
    .set_jwt('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ...') # Your secret JSON Web Token

account = Appwrite::Account.new(client)

response = account.delete_session(session_id: '[SESSION_ID]')

puts response.inspect


use Appwrite;

Client client = new Client();

client
  .SetEndPoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
  .SetProject("5df5acd0d48c2") // Your project ID
  .SetKey("919c2d18fb5d4...a2ae413da83346ad2") // Your secret API key
;

Storage storage = new Storage(client);

HttpResponseMessage result = await storage.ListFiles();


const AVATAR_SET_NOT_FOUND             = 'avatar_set_not_found';

const AVATAR_NOT_FOUND                 = 'avatar_not_found';

const IMAGIC_EXTENSION_MISSING         = 'imagic_extension_missing';

const AVATAR_IMAGE_NOT_FOUND           = 'avatar_image_not_found';

const FILE_NOT_FOUND                   = 'file_not_found';

const AVATAR_CANNOT_PARSE_IMAGE        = 'avatar_cannot_parse_image';


let sdk = new Appwrite();

sdk
    .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
    .setProject('5df5acd0d48c2') // Your project ID
    .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key
;

let promise = sdk.health.getQueueWebhooks();

promise.then(function (response) {
    console.log(response); // Success
}, function (error) {
    console.log(error); // Failure
});


appwrite users updateVerification --userId="[USER_ID]" --emailVerification=""


import Appwrite

func main() {
    let client = Client()
      .setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
      .setProject("5df5acd0d48c2") // Your project ID
      .setKey("919c2d18fb5d4...a2ae413da83346ad2") // Your secret API key

    let users = Users(client)
    users.updateEmail(
        userId: "[USER_ID]",
        email: "email@example.com"
    ) { result in
        switch result {
        case .failure(let error):
            print(error.message)
        case .success(let user):
            print(String(describing: user)
        }
    }
}


keep keep keep replace keep keep keep keep keep

import { Workflow } from './Workflow';
import { WorkflowHooks } from './WorkflowHooks';
import * as express from 'express';

export type IAllExecuteFunctions = IExecuteFunctions | IExecuteSingleFunctions | IHookFunctions | ILoadOptionsFunctions | IPollFunctions | ITriggerFunctions | IWebhookFunctions;

export interface IBinaryData {
	[key: string]: string | undefined;
	data: string;
	mimeType: string;
	fileName?: string;
	fileExtension?: string;
}

export interface IOAuth2Options {
	includeCredentialsOnRefreshOnBody?: boolean;
	property?: string;
	tokenType?: string;
	keepBearer?: boolean;
}

export interface IConnection {
	// The node the connection is to
	node: string;

	// The type of the input on destination node (for example "main")
	type: string;

	// The output/input-index of destination node (if node has multiple inputs/outputs of the same type)
	index: number;
}

export interface IExecutionError {
	message: string;
	node?: string;
	stack?: string;
}

// Get used to gives nodes access to credentials
export interface IGetCredentials {
	get(type: string, name: string): Promise<ICredentialsEncrypted>;
}

export abstract class ICredentials {
	name: string;
	type: string;
	data: string | undefined;
	nodesAccess: ICredentialNodeAccess[];

	constructor(name: string, type: string, nodesAccess: ICredentialNodeAccess[], data?: string) {
		this.name = name;
		this.type = type;
		this.nodesAccess = nodesAccess;
		this.data = data;
	}

	abstract getData(encryptionKey: string, nodeType?: string): ICredentialDataDecryptedObject;
	abstract getDataKey(key: string, encryptionKey: string, nodeType?: string): CredentialInformation;
	abstract getDataToSave(): ICredentialsEncrypted;
	abstract hasNodeAccess(nodeType: string): boolean;
	abstract setData(data: ICredentialDataDecryptedObject, encryptionKey: string): void;
	abstract setDataKey(key: string, data: CredentialInformation, encryptionKey: string): void;
}

// Defines which nodes are allowed to access the credentials and
// when that access got grented from which user
export interface ICredentialNodeAccess {
	nodeType: string;
	user?: string;
	date?: Date;
}

export interface ICredentialsDecrypted {
	name: string;
	type: string;
	nodesAccess: ICredentialNodeAccess[];
	data?: ICredentialDataDecryptedObject;
}

export interface ICredentialsEncrypted {
	name: string;
	type: string;
	nodesAccess: ICredentialNodeAccess[];
	data?: string;
}

export abstract class ICredentialsHelper {
	encryptionKey: string;
	workflowCredentials: IWorkflowCredentials;

	constructor(workflowCredentials: IWorkflowCredentials, encryptionKey: string) {
		this.encryptionKey = encryptionKey;
		this.workflowCredentials = workflowCredentials;
	}

	abstract getCredentials(name: string, type: string): ICredentials;
	abstract getDecrypted(name: string, type: string): ICredentialDataDecryptedObject;
	abstract updateCredentials(name: string, type: string, data: ICredentialDataDecryptedObject): Promise<void>;
}

export interface ICredentialType {
	name: string;
	displayName: string;
	extends?: string[];
	properties: INodeProperties[];
	documentationUrl?: string;
	__overwrittenProperties?: string[];
}

export interface ICredentialTypes {
	credentialTypes?: {
		[key: string]: ICredentialType
	};
	init(credentialTypes?: { [key: string]: ICredentialType }): Promise<void>;
	getAll(): ICredentialType[];
	getByName(credentialType: string): ICredentialType;
}

// The way the credentials get saved in the database (data encrypted)
export interface ICredentialData {
	name: string;
	data: string; // Contains the access data as encrypted JSON string
	nodesAccess: ICredentialNodeAccess[];
}

// The encrypted credentials which the nodes can access
export type CredentialInformation = string | number | boolean | IDataObject;

// The encrypted credentials which the nodes can access
export interface ICredentialDataDecryptedObject {
	[key: string]: CredentialInformation;
}

// First array index: The output/input-index (if node has multiple inputs/outputs of the same type)
// Second array index: The different connections (if one node is connected to multiple nodes)
export type NodeInputConnections = IConnection[][];

export interface INodeConnections {
	// Input name
	[key: string]: NodeInputConnections;
}

export interface IConnections {
	// Node name
	[key: string]: INodeConnections;
}

export type GenericValue = string | object | number | boolean | undefined | null;

export interface IDataObject {
	[key: string]: GenericValue | IDataObject | GenericValue[] | IDataObject[];
}

export interface IGetExecutePollFunctions {
	(workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode): IPollFunctions;
}

export interface IGetExecuteTriggerFunctions {
	(workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode): ITriggerFunctions;
}

export interface IGetExecuteFunctions {
	(workflow: Workflow, runExecutionData: IRunExecutionData, runIndex: number, connectionInputData: INodeExecutionData[], inputData: ITaskDataConnections, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode): IExecuteFunctions;
}

export interface IGetExecuteSingleFunctions {
	(workflow: Workflow, runExecutionData: IRunExecutionData, runIndex: number, connectionInputData: INodeExecutionData[], inputData: ITaskDataConnections, node: INode, itemIndex: number, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode): IExecuteSingleFunctions;
}

export interface IGetExecuteHookFunctions {
	(workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, isTest?: boolean, webhookData?: IWebhookData): IHookFunctions;
}

export interface IGetExecuteWebhookFunctions {
	(workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, webhookData: IWebhookData): IWebhookFunctions;
}

export interface IExecuteData {
	data: ITaskDataConnections;
	node: INode;
}

export type IContextObject = {
	[key: string]: any; // tslint:disable-line:no-any
};

export interface IExecuteContextData {
	// Keys are: "flow" | "node:<NODE_NAME>"
	[key: string]: IContextObject;
}

export interface IExecuteFunctions {
	continueOnFail(): boolean;
	evaluateExpression(expression: string, itemIndex: number): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[];
	executeWorkflow(workflowInfo: IExecuteWorkflowInfo, inputData?: INodeExecutionData[]): Promise<any>; // tslint:disable-line:no-any
	getContext(type: string): IContextObject;
	getCredentials(type: string): ICredentialDataDecryptedObject | undefined;
	getInputData(inputIndex?: number, inputName?: string): INodeExecutionData[];
	getMode(): WorkflowExecuteMode;
	getNode(): INode;
	getNodeParameter(parameterName: string, itemIndex: number, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object; //tslint:disable-line:no-any
	getWorkflowDataProxy(itemIndex: number): IWorkflowDataProxyData;
	getWorkflowStaticData(type: string): IDataObject;
	getRestApiUrl(): string;
	getTimezone(): string;
	getWorkflow(): IWorkflowMetadata;
	prepareOutputData(outputData: INodeExecutionData[], outputIndex?: number): Promise<INodeExecutionData[][]>;
	helpers: {
		[key: string]: (...args: any[]) => any //tslint:disable-line:no-any
	};
}

export interface IExecuteSingleFunctions {
	continueOnFail(): boolean;
	evaluateExpression(expression: string, itemIndex: number | undefined): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[];
	getContext(type: string): IContextObject;
	getCredentials(type: string): ICredentialDataDecryptedObject | undefined;
	getInputData(inputIndex?: number, inputName?: string): INodeExecutionData;
	getMode(): WorkflowExecuteMode;
	getNode(): INode;
	getNodeParameter(parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object; //tslint:disable-line:no-any
	getRestApiUrl(): string;
	getTimezone(): string;
	getWorkflow(): IWorkflowMetadata;
	getWorkflowDataProxy(): IWorkflowDataProxyData;
	getWorkflowStaticData(type: string): IDataObject;
	helpers: {
		[key: string]: (...args: any[]) => any //tslint:disable-line:no-any
	};
}

export interface IExecuteWorkflowInfo {
	code?: IWorkflowBase;
	id?: string;
}

export interface ILoadOptionsFunctions {
	getCredentials(type: string): ICredentialDataDecryptedObject | undefined;
	getNode(): INode;
	getNodeParameter(parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object; //tslint:disable-line:no-any
	getCurrentNodeParameter(parameterName: string): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object | undefined;
	getCurrentNodeParameters(): INodeParameters | undefined;
	getTimezone(): string;
	getRestApiUrl(): string;
	helpers: {
		[key: string]: ((...args: any[]) => any) | undefined; //tslint:disable-line:no-any
	};
}

export interface IHookFunctions {
	getCredentials(type: string): ICredentialDataDecryptedObject | undefined;
	getMode(): WorkflowExecuteMode;
	getNode(): INode;
	getNodeWebhookUrl: (name: string) => string | undefined;
	getNodeParameter(parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object; //tslint:disable-line:no-any
	getTimezone(): string;
	getWebhookDescription(name: string): IWebhookDescription | undefined;
	getWebhookName(): string;
	getWorkflow(): IWorkflowMetadata;
	getWorkflowStaticData(type: string): IDataObject;
	helpers: {
		[key: string]: (...args: any[]) => any //tslint:disable-line:no-any
	};
}

export interface IPollFunctions {
	__emit(data: INodeExecutionData[][]): void;
	getCredentials(type: string): ICredentialDataDecryptedObject | undefined;
	getMode(): WorkflowExecuteMode;
	getNode(): INode;
	getNodeParameter(parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object; //tslint:disable-line:no-any
	getRestApiUrl(): string;
	getTimezone(): string;
	getWorkflow(): IWorkflowMetadata;
	getWorkflowStaticData(type: string): IDataObject;
	helpers: {
		[key: string]: (...args: any[]) => any //tslint:disable-line:no-any
	};
}

export interface ITriggerFunctions {
	emit(data: INodeExecutionData[][]): void;
	getCredentials(type: string): ICredentialDataDecryptedObject | undefined;
	getMode(): WorkflowExecuteMode;
	getNode(): INode;
	getNodeParameter(parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object; //tslint:disable-line:no-any
	getRestApiUrl(): string;
	getTimezone(): string;
	getWorkflow(): IWorkflowMetadata;
	getWorkflowStaticData(type: string): IDataObject;
	helpers: {
		[key: string]: (...args: any[]) => any //tslint:disable-line:no-any
	};
}

export interface IWebhookFunctions {
	getBodyData(): IDataObject;
	getCredentials(type: string): ICredentialDataDecryptedObject | undefined;
	getHeaderData(): object;
	getMode(): WorkflowExecuteMode;
	getNode(): INode;
	getNodeParameter(parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object; //tslint:disable-line:no-any
	getNodeWebhookUrl: (name: string) => string | undefined;
	getQueryData(): object;
	getRequestObject(): express.Request;
	getResponseObject(): express.Response;
	getTimezone(): string;
	getWebhookName(): string;
	getWorkflowStaticData(type: string): IDataObject;
	getWorkflow(): IWorkflowMetadata;
	prepareOutputData(outputData: INodeExecutionData[], outputIndex?: number): Promise<INodeExecutionData[][]>;
	helpers: {
		[key: string]: (...args: any[]) => any //tslint:disable-line:no-any
	};
}

export interface INodeCredentials {
	[key: string]: string;
}

export interface INode {
	name: string;
	typeVersion: number;
	type: string;
	position: [number, number];
	disabled?: boolean;
	notesInFlow?: boolean;
	retryOnFail?: boolean;
	maxTries?: number;
	waitBetweenTries?: number;
	alwaysOutputData?: boolean;
	executeOnce?: boolean;
	continueOnFail?: boolean;
	parameters: INodeParameters;
	credentials?: INodeCredentials;
	webhookId?: string;
}

export interface INodes {
	[key: string]: INode;
}

export interface IObservableObject {
	[key: string]: any; // tslint:disable-line:no-any
	__dataChanged: boolean;
}

export interface IBinaryKeyData {
	[key: string]: IBinaryData;
}

export interface INodeExecutionData {
	[key: string]: IDataObject | IBinaryKeyData | undefined;
	// TODO: Rename this one as json does not really fit as it is not json (which is a string) it is actually a JS object
	json: IDataObject;
	// json: object;
	// json?: object;
	binary?: IBinaryKeyData;
}

export interface INodeExecuteFunctions {
	getExecutePollFunctions: IGetExecutePollFunctions;
	getExecuteTriggerFunctions: IGetExecuteTriggerFunctions;
	getExecuteFunctions: IGetExecuteFunctions;
	getExecuteSingleFunctions: IGetExecuteSingleFunctions;
	getExecuteHookFunctions: IGetExecuteHookFunctions;
	getExecuteWebhookFunctions: IGetExecuteWebhookFunctions;
}

// The values a node property can have
export type NodeParameterValue = string | number | boolean | undefined | null;

export interface INodeParameters {
	// TODO: Later also has to be possible to add multiple ones with the name name. So array has to be possible
	[key: string]: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[];
}

export type NodePropertyTypes = 'boolean' | 'collection' | 'color' | 'dateTime' | 'fixedCollection' | 'hidden' | 'json' | 'multiOptions' | 'number' | 'options' | 'string';

export type EditorTypes = 'code';

export interface INodePropertyTypeOptions {
	alwaysOpenEditWindow?: boolean; // Supported by: string
	editor?: EditorTypes;        // Supported by: string
	loadOptionsDependsOn?: string[];  // Supported by: options
	loadOptionsMethod?: string;  // Supported by: options
	maxValue?: number;           // Supported by: number
	minValue?: number;           // Supported by: number
	multipleValues?: boolean;    // Supported by: <All>
	multipleValueButtonText?: string;    // Supported when "multipleValues" set to true
	numberPrecision?: number;    // Supported by: number
	numberStepSize?: number;     // Supported by: number
	password?: boolean;          // Supported by: string
	rows?: number;               // Supported by: string
	showAlpha?: boolean;         // Supported by: color
	[key: string]: boolean | number | string | EditorTypes | undefined | string[];
}

export interface IDisplayOptions {
	hide?: {
		[key: string]: NodeParameterValue[];
	};
	show?: {
		[key: string]: NodeParameterValue[];
	};
}

export interface INodeProperties {
	displayName: string;
	name: string;
	type: NodePropertyTypes;
	typeOptions?: INodePropertyTypeOptions;
	default: NodeParameterValue | INodeParameters | INodeParameters[] | NodeParameterValue[];
	description?: string;
	displayOptions?: IDisplayOptions;
	options?: Array<INodePropertyOptions | INodeProperties | INodePropertyCollection >;
	placeholder?: string;
	isNodeSetting?: boolean;
	noDataExpression?: boolean;
	required?: boolean;
}

export interface INodePropertyOptions {
	name: string;
	value: string | number;
	description?: string;
}

export interface INodePropertyCollection {
	displayName: string;
	name: string;
	values: INodeProperties[];
}

export interface IParameterDependencies {
	[key: string]: string[];
}

export interface IPollResponse {
	closeFunction?: () => Promise<void>;
}

export interface ITriggerResponse {
	closeFunction?: () => Promise<void>;
	// To manually trigger the run
	manualTriggerFunction?: () => Promise<void>;
	// Gets added automatically at manual workflow runs resolves with
	// the first emitted data
	manualTriggerResponse?: Promise<INodeExecutionData[][]>;
}

export interface INodeType {
	description: INodeTypeDescription;
	execute?(this: IExecuteFunctions): Promise<INodeExecutionData[][] | null>;
	executeSingle?(this: IExecuteSingleFunctions): Promise<INodeExecutionData>;
	poll?(this: IPollFunctions): Promise<INodeExecutionData[][] | null>;
	trigger?(this: ITriggerFunctions): Promise<ITriggerResponse | undefined>;
	webhook?(this: IWebhookFunctions): Promise<IWebhookResponseData>;
	hooks?: {
		[key: string]: (this: IHookFunctions) => Promise<boolean>;
	};
	methods?: {
		loadOptions?: {
			[key: string]: (this: ILoadOptionsFunctions) => Promise<INodePropertyOptions[]>;
		}
	};
	webhookMethods?: {
		[key: string]: IWebhookSetupMethods;
	};
}

export type WebhookSetupMethodNames = 'checkExists' | 'create' | 'delete';

export interface IWebhookSetupMethods {
	[key: string]: ((this: IHookFunctions) => Promise<boolean>) | undefined;
	checkExists?: (this: IHookFunctions) => Promise<boolean>;
	create?: (this: IHookFunctions) => Promise<boolean>;
	delete?: (this: IHookFunctions) => Promise<boolean>;
}

export interface INodeCredentialDescription {
	name: string;
	required?: boolean;
	displayOptions?: IDisplayOptions;
}

export type INodeIssueTypes = 'credentials' | 'execution' | 'parameters' | 'typeUnknown';

export interface INodeIssueObjectProperty {
	[key: string]: string[];
}

export interface INodeIssueData {
	node: string;
	type: INodeIssueTypes;
	value: boolean | string | string[] | INodeIssueObjectProperty;
}

export interface INodeIssues {
	execution?: boolean;
	credentials?: INodeIssueObjectProperty;
	parameters?: INodeIssueObjectProperty;
	typeUnknown?: boolean;
	[key: string]: undefined | boolean | INodeIssueObjectProperty;
}

export interface IWorfklowIssues {
	[key: string]: INodeIssues;
}

export interface INodeTypeDescription {
	displayName: string;
	name: string;
	icon?: string;
	group: string[];
	version: number;
	description: string;
	defaults: INodeParameters;
	inputs: string[];
	inputNames?: string[];
	outputs: string[];
	outputNames?: string[];
	properties: INodeProperties[];
	credentials?: INodeCredentialDescription[];
	maxNodes?: number; // How many nodes of that type can be created in a workflow
	polling?: boolean;
	subtitle?: string;
	hooks?: {
		[key: string]: INodeHookDescription[] | undefined;
		activate?: INodeHookDescription[];
		deactivate?: INodeHookDescription[];
	};
	webhooks?: IWebhookDescription[];
}

export interface INodeHookDescription {
	method: string;
}

export interface IWebhookData {
	httpMethod: WebhookHttpMethod;
	node: string;
	path: string;
	webhookDescription: IWebhookDescription;
	workflowId: string;
	workflowExecuteAdditionalData: IWorkflowExecuteAdditionalData;
}

export interface IWebhookDescription {
	[key: string]: WebhookHttpMethod | WebhookResponseMode | boolean | string | undefined;
	httpMethod: WebhookHttpMethod | string;
	isFullPath?: boolean;
	name: string;
	path: string;
	responseBinaryPropertyName?: string;
	responseContentType?: string;
	responsePropertyName?: string;
	responseMode?: WebhookResponseMode | string;
	responseData?: WebhookResponseData | string;
}

export interface IWorkflowDataProxyData {
	$binary: any; // tslint:disable-line:no-any
	$data: any; // tslint:disable-line:no-any
	$env: any; // tslint:disable-line:no-any
	$evaluateExpression: any; // tslint:disable-line:no-any
	$item: any; // tslint:disable-line:no-any
	$items: any; // tslint:disable-line:no-any
	$json: any; // tslint:disable-line:no-any
	$node: any; // tslint:disable-line:no-any
	$parameter: any; // tslint:disable-line:no-any
	$workflow: any; // tslint:disable-line:no-any
}

export interface IWorkflowMetadata {
	id?: number | string;
	name?: string;
	active: boolean;
}

export type WebhookHttpMethod = 'GET' | 'POST' | 'HEAD' | 'OPTIONS';

export interface IWebhookResponseData {
	workflowData?: INodeExecutionData[][];
	webhookResponse?: any; // tslint:disable-line:no-any
	noWebhookResponse?: boolean;
}

export type WebhookResponseData = 'allEntries' | 'firstEntryJson' | 'firstEntryBinary';
export type WebhookResponseMode = 'onReceived' | 'lastNode';

export interface INodeTypes {
	nodeTypes: INodeTypeData;
	init(nodeTypes?: INodeTypeData): Promise<void>;
	getAll(): INodeType[];
	getByName(nodeType: string): INodeType | undefined;
}

export interface INodeTypeData {
	[key: string]: {
		type: INodeType;
		sourcePath: string;
	};
}

export interface IRun {
	data: IRunExecutionData;
	finished?: boolean;
	mode: WorkflowExecuteMode;
	startedAt: Date;
	stoppedAt: Date;
}

// Contains all the data which is needed to execute a workflow and so also to
// start restart it again after it did fail.
// The RunData, ExecuteData and WaitForExecution contain often the same data.
export interface IRunExecutionData {
	startData?: {
		destinationNode?: string;
		runNodeFilter?: string[];
	};
	resultData: {
		error?: IExecutionError;
		runData: IRunData;
		lastNodeExecuted?: string;
	};
	executionData?: {
		contextData: IExecuteContextData;
		nodeExecutionStack: IExecuteData[];
		waitingExecution: IWaitingForExecution;
	};
}

export interface IRunData {
	// node-name: result-data
	[key: string]: ITaskData[];
}

// The data that gets returned when a node runs
export interface ITaskData {
	startTime: number;
	executionTime: number;
	data?: ITaskDataConnections;
	error?: IExecutionError;
}

// The data for al the different kind of connectons (like main) and all the indexes
export interface ITaskDataConnections {
	// Key for each input type and because there can be multiple inputs of the same type it is an array
	// null is also allowed because if we still need data for a later while executing the workflow set teompoary to null
	// the nodes get as input TaskDataConnections which is identical to this one except that no null is allowed.
	[key: string]: Array<INodeExecutionData[] | null>;
}

// Keeps data while workflow gets executed and allows when provided to restart execution
export interface IWaitingForExecution {
	// Node name
	[key: string]: {
		// Run index
		[key: number]: ITaskDataConnections
	};
}

export interface IWorkflowBase {
	id?: number | string | any; // tslint:disable-line:no-any
	name: string;
	active: boolean;
	createdAt: Date;
	updatedAt: Date;
	nodes: INode[];
	connections: IConnections;
	settings?: IWorkflowSettings;
	staticData?: IDataObject;
}

export interface IWorkflowCredentials {
	// Credential type
	[key: string]: {
		// Name
		[key: string]: ICredentialsEncrypted;
	};
}

export interface IWorkflowExecuteHooks {
	[key: string]: Array<((...args: any[]) => Promise<void>)> | undefined; // tslint:disable-line:no-any
	nodeExecuteAfter?: Array<((nodeName: string, data: ITaskData) => Promise<void>)>;
	nodeExecuteBefore?: Array<((nodeName: string) => Promise<void>)>;
	workflowExecuteAfter?: Array<((data: IRun, newStaticData: IDataObject) => Promise<void>)>;
	workflowExecuteBefore?: Array<(() => Promise<void>)>;
}

export interface IWorkflowExecuteAdditionalData {
	credentials: IWorkflowCredentials;
	credentialsHelper: ICredentialsHelper;
	encryptionKey: string;
	executeWorkflow: (workflowInfo: IExecuteWorkflowInfo, additionalData: IWorkflowExecuteAdditionalData, inputData?: INodeExecutionData[]) => Promise<any>; // tslint:disable-line:no-any
	// hooks?: IWorkflowExecuteHooks;
	hooks?: WorkflowHooks;
	httpResponse?: express.Response;
	httpRequest?: express.Request;
	restApiUrl: string;
	timezone: string;
	webhookBaseUrl: string;
	webhookTestBaseUrl: string;
	currentNodeParameters? : INodeParameters;
}

export type WorkflowExecuteMode = 'cli' | 'error' | 'integrated' | 'internal' | 'manual' | 'retry' | 'trigger' | 'webhook';

export interface IWorkflowHooksOptionalParameters {
	parentProcessMode?: string;
	retryOf?: string;
	sessionId?: string;
}

export interface IWorkflowSettings {
	[key: string]: IDataObject | string | number | boolean | undefined;
}


# n8n - Workflow Automation Tool

![n8n.io - Workflow Automation](https://raw.githubusercontent.com/n8n-io/n8n/master/assets/n8n-logo.png)

n8n is an extendable workflow automation tool. With a [fair-code](http://faircode.io) distribution model, n8n will always have visible source code, be available to self-host, and allow you to add your own custom functions, logic and apps. n8n's node-based approach makes it highly versatile, enabling you to connect anything to everything.

## Demo

[:tv: A short demo (< 3 min)](https://www.youtube.com/watch?v=3w7xIMKLVAg) which shows how to create a simple workflow which
automatically sends a new Slack notification every time a Github repository
received or lost a star.

## Available integrations

n8n has 170+ different nodes to automate workflows. The list can be found on: [https://n8n.io/nodes](https://n8n.io/nodes)

## Documentation

The official n8n documentation can be found under: [https://docs.n8n.io](https://docs.n8n.io)

Additional information and example workflows on the n8n.io website: [https://n8n.io](https://n8n.io)

## Usage

- :books: Learn [how to **install** and **use** it from the command line](https://github.com/n8n-io/n8n/tree/master/packages/cli/README.md)
- :whale: Learn [how to run n8n in **Docker**](https://github.com/n8n-io/n8n/tree/master/docker/images/n8n/README.md)

## Start

Execute: `npm run start`

## Hosted n8n

If you are interested in a hosted version of n8n on our infrastructure please contact us via:
[hosting@n8n.io](mailto:hosting@n8n.io)

## Support

If you have problems or questions go to our forum, we will then try to help you asap:

[https://community.n8n.io](https://community.n8n.io)

## Jobs

If you are interested in working for n8n and so shape the future of the project
check out our job posts:

[https://n8n.join.com](https://n8n.join.com)

## What does n8n mean and how do you pronounce it?

**Short answer:** It means "nodemation" and it is pronounced as n-eight-n.

**Long answer:** "I get that question quite often (more often than I expected)
so I decided it is probably best to answer it here. While looking for a
good name for the project with a free domain I realized very quickly that all the
good ones I could think of were already taken. So, in the end, I chose
nodemation. 'node-' in the sense that it uses a Node-View and that it uses
Node.js and '-mation' for 'automation' which is what the project is supposed to help with.
However, I did not like how long the name was and I could not imagine writing
something that long every time in the CLI. That is when I then ended up on
'n8n'." - **Jan Oberhauser, Founder and CEO, n8n.io**

## Development Setup

Have you found a bug :bug: ? Or maybe you have a nice feature :sparkles: to contribute ? The [CONTRIBUTING guide](https://github.com/n8n-io/n8n/blob/master/CONTRIBUTING.md) will help you get your development environment ready in minutes.

## License

n8n is [fair-code](http://faircode.io) licensed under [**Apache 2.0 with Commons Clause**](https://github.com/n8n-io/n8n/blob/master/packages/cli/LICENSE.md).

Additional information about license can be found in the [FAQ](https://docs.n8n.io/#/faq?id=license).


import {
	ICredentialType,
	NodePropertyTypes,
} from 'n8n-workflow';

export class PipedriveApi implements ICredentialType {
	name = 'pipedriveApi';
	displayName = 'Pipedrive API';
	documentationUrl = 'pipedrive';
	properties = [
		{
			displayName: 'API Token',
			name: 'apiToken',
			type: 'string' as NodePropertyTypes,
			default: '',
		},
	];
}


export interface IAttachment {
	file_data?: string;
	file_name?: string;
}

export interface IEmail {
	address_field?: string;
	attachments?: IAttachment[];
	contacts: number[];
	html_content?: string;
	plain_content?: string;
	subject?: string;
	user_id: number;
}


const credentialType = this.$store.getters.credentialType(credentialTypeName);

if (credentialType.documentationUrl !== undefined) {

return 'https://docs.n8n.io/credentials/' + credentialType.documentationUrl + '/?utm_source=n8n_app&utm_medium=left_nav_menu&utm_campaign=create_new_credentials_modal';


import { INodeProperties } from "n8n-workflow";

export const payoutOperations = [
	{
		displayName: 'Operation',
		name: 'operation',
		type: 'options',
		displayOptions: {
			show: {
				resource: [
					'payout',
				],
			},
		},
		options: [
			{
				name: 'Create',
				value: 'create',
				description: 'Create a batch payout',
			},
			{
				name: 'Get',
				value: 'get',
				description: 'Show batch payout details',
			},
		],
		default: 'create',
		description: 'The operation to perform.',
	},
] as INodeProperties[];

export const payoutFields = [

/* -------------------------------------------------------------------------- */
/*                                payout:create                               */
/* -------------------------------------------------------------------------- */

	{
		displayName: 'Sender Batch ID',
		name: 'senderBatchId',
		type: 'string',
		required: true,
		displayOptions: {
			show: {
				resource: [
					'payout',
				],
				operation: [
					'create',
				],
			},
		},
		default: '',
		description: 'A sender-specified ID number. Tracks the payout in an accounting system.',
	},
	{
		displayName: 'JSON Parameters',
		name: 'jsonParameters',
		type: 'boolean',
		default: false,
		description: '',
		displayOptions: {
			show: {
				resource: [
					'payout',
				],
				operation: [
					'create',
				],
			},
		},
	},
	{
		displayName: 'Items',
		name: 'itemsUi',
		placeholder: 'Add Item',
		type: 'fixedCollection',
		displayOptions: {
			show: {
				resource: [
					'payout',
				],
				operation: [
					'create',
				],
				jsonParameters: [
					false,
				],
			},
		},
		typeOptions: {
			multipleValues: true,
		},
		default: {},
		options: [
			{
				name: 'itemsValues',
				displayName: 'Item',
				values: [
					{
						displayName: 'Recipient Type',
						name: 'recipientType',
						type: 'options',
						options: [
							{
								name: 'Phone',
								value: 'phone',
								description: 'The unencrypted phone number',
							},
							{
								name: 'Email',
								value: 'email',
								description: 'The unencrypted email.',
							},
							{
								name: 'PayPal ID',
								value: 'paypalId',
								description: 'The encrypted PayPal account number.',
							},
						],
						default: 'email',
						description: 'The ID type that identifies the recipient of the payment.',
					},
					{
						displayName: 'Receiver Value',
						name: 'receiverValue',
						type: 'string',
						required: true,
						default: '',
						description: 'The receiver of the payment. Corresponds to the recipient_type value
in the request. Max length: 127 characters.',
					},
					{
						displayName: 'Currency',
						name: 'currency',
						type: 'options',
						options: [
							{
								name: 'Australian dollar',
								value: 'AUD',
							},
							{
								name: 'Brazilian real',
								value: 'BRL',
							},
							{
								name: 'Canadian dollar',
								value: 'CAD',
							},
							{
								name: 'Czech koruna',
								value: 'CZK',
							},
							{
								name: 'Danish krone',
								value: 'DKK',
							},
							{
								name: 'Euro',
								value: 'EUR',
							},
							{
								name: 'United States dollar',
								value: 'USD',
							},
						],
						default: 'USD',
						description: 'Currency',
					},
					{
						displayName: 'Amount',
						name: 'amount',
						type: 'string',
						required: true,
						default: '',
						description: 'The value, which might be',
					},
					{
						displayName: 'Note',
						name: 'note',
						type: 'string',
						required: false,
						default: '',
						description: 'The sender-specified note for notifications. Supports up to
4000 ASCII characters and 1000 non-ASCII characters.',
					},
					{
						displayName: 'Sender Item ID',
						name: 'senderItemId',
						type: 'string',
						default: '',
						description: 'The sender-specified ID number. Tracks the payout in an accounting system.',
					},
					{
						displayName: 'Recipient Wallet',
						name: 'recipientWallet',
						type: 'options',
						options: [
							{
								name: 'PayPal',
								value: 'paypal',
								description: 'PayPal Wallet',
							},
							{
								name: 'Venmo',
								value: 'venmo',
								description: 'Venmo Wallet',
							},
						],
						default: 'paypal',
						description: 'The recipient wallet',
					},
				],
			},
		],
	},
	{
		displayName: 'Items',
		name: 'itemsJson',
		type: 'json',
		typeOptions: {
			alwaysOpenEditWindow: true,
		},
		description: 'An array of individual payout items.',
		displayOptions: {
			show: {
				resource: [
					'payout',
				],
				operation: [
					'create',
				],
				jsonParameters: [
					true,
				],
			},
		},
	},
	{
		displayName: 'Additional Fields',
		name: 'additionalFields',
		type: 'collection',
		placeholder: 'Add Field',
		default: {},
		displayOptions: {
			show: {
				resource: [
					'payout',
				],
				operation: [
					'create',
				],
			},
		},
		options: [
			{
				displayName: 'Email Subject',
				name: 'emailSubject',
				type: 'string',
				default: '',
				description: 'The subject line for the email that PayPal sends when payment
for a payout item completes. The subject line is the same for all
recipients. Max length: 255 characters.',
			},
			{
				displayName: 'Email Message',
				name: 'emailMessage',
				type: 'string',
				default: '',
				description: 'The email message that PayPal sends when the payout item completes.
The message is the same for all recipients.',
			},
			{
				displayName: 'Note',
				name: 'note',
				type: 'string',
				default: '',
				description: 'The payouts and item-level notes are concatenated in the email.
Max length: 1000 characters.',
			},
		],
	},

	/* -------------------------------------------------------------------------- */
	/*                                 payout:get                              */
	/* -------------------------------------------------------------------------- */

	{
		displayName: 'Payout Batch Id',
		name: 'payoutBatchId',
		type: 'string',
		required: true,
		displayOptions: {
			show: {
				resource: [
					'payout',
				],
				operation: [
					'get',
				],
			},
		},
		description: 'The ID of the payout for which to show details.',
	},
	{
		displayName: 'Return All',
		name: 'returnAll',
		type: 'boolean',
		default: false,
		displayOptions: {
			show: {
				resource: [
					'payout',
				],
				operation: [
					'get',
				],
			},
		},
		description: 'If all results should be returned or only up to a given limit.',
	},
	{
		displayName: 'Limit',
		name: 'limit',
		type: 'number',
		typeOptions: {
			maxValue: 1000,
			minValue: 1,
		},
		default: 100,
		displayOptions: {
			show: {
				resource: [
					'payout',
				],
				operation: [
					'get',
				],
				returnAll: [
					false,
				],
			},
		},
		description: 'If all results should be returned or only up to a given limit.',
	},

] as INodeProperties[];

export const payoutItemOperations = [
	{
		displayName: 'Operation',
		name: 'operation',
		type: 'options',
		displayOptions: {
			show: {
				resource: [
					'payoutItem',
				],
			},
		},
		options: [
			{
				name: 'Cancel',
				value: 'cancel',
				description: 'Cancels an unclaimed payout item',
			},
			{
				name: 'Get',
				value: 'get',
				description: 'Show payout item details',
			},
		],
		default: 'get',
		description: 'The operation to perform.',
	},
] as INodeProperties[];

export const payoutItemFields = [

	/* -------------------------------------------------------------------------- */
	/*                                 payoutItem:get                                 */
	/* -------------------------------------------------------------------------- */
	{
		displayName: 'Payout Item Id',
		name: 'payoutItemId',
		type: 'string',
		default: '',
		required: true,
		displayOptions: {
			show: {
				resource: [
					'payoutItem',
				],
				operation: [
					'get',
				],
			},
		},
		description: 'The ID of the payout item for which to show details.',
	},

	/* -------------------------------------------------------------------------- */
	/*                                payoutItem:cancel                               */
	/* -------------------------------------------------------------------------- */

	{
		displayName: 'Payout Item Id',
		name: 'payoutItemId',
		type: 'string',
		default: '',
		required: true,
		displayOptions: {
			show: {
				resource: [
					'payoutItem',
				],
				operation: [
					'cancel',
				],
			},
		},
		description: 'The ID of the payout item to cancel.',
	},
] as INodeProperties[];


import {
	INodeProperties,
 } from 'n8n-workflow';

export const contactOperations = [
	{
		displayName: 'Operation',
		name: 'operation',
		type: 'options',
		displayOptions: {
			show: {
				resource: [
					'contact',
				],
			},
		},
		options: [
			{
				name: 'Create/Update',
				value: 'upsert',
				description: 'Create/Update a contact',
			},
			{
				name: 'Delete',
				value: 'delete',
				description: 'Delete a contact',
			},
			{
				name: 'Get',
				value: 'get',
				description: 'Get a contact',
			},
			{
				name: 'Get All',
				value: 'getAll',
				description: 'Get all contacts',
			},
			{
				name: 'Get Recently Created/Updated',
				value: 'getRecentlyCreatedUpdated',
				description: 'Get recently created/updated contacts',
			},
			{
				name: 'Search',
				value: 'search',
				description: 'Search contacts',
			},
		],
		default: 'upsert',
		description: 'The operation to perform.',
	},
] as INodeProperties[];

export const contactFields = [

/* -------------------------------------------------------------------------- */
/*                                contact:upsert                              */
/* -------------------------------------------------------------------------- */
	{
		displayName: 'Email',
		name: 'email',
		type: 'string',
		required: true,
		displayOptions: {
			show: {
				resource: [
					'contact',
				],
				operation: [
					'upsert',
				],
			},
		},
		default: '',
	},
	{
		displayName: 'Resolve Data',
		name: 'resolveData',
		type: 'boolean',
		displayOptions: {
			show: {
				resource: [
					'contact',
				],
				operation: [
					'upsert',
				],
			},
		},
		default: true,
		description: 'By default the response only includes the ID. If this option gets activated it
will resolve the data automatically.',
	},
	{
		displayName: 'Additional Fields',
		name: 'additionalFields',
		type: 'collection',
		placeholder: 'Add Field',
		default: {},
		displayOptions: {
			show: {
				resource: [
					'contact',
				],
				operation: [
					'upsert',
				],
			},
		},
		options: [
			{
				displayName: 'Annual Revenue',
				name: 'annualRevenue',
				type: 'number',
				typeOptions: {
					minValue: 0,
				},
				default: 0,
			},
			{
				displayName: 'Associated Company ID',
				name: 'associatedCompanyId',
				type: 'options',
				typeOptions: {
					loadOptionsMethod:'getCompanies' ,
				},
				default: '',
				description: 'Companies associated with the ticket',
			},
			{
				displayName: 'City',
				name: 'city',
				type: 'string',
				default: '',
			},
			{
				displayName: 'Clicked Facebook Ad',
				name: 'clickedFacebookAd',
				type: 'string',
				default: '',
			},
			{
				displayName: 'Close Date',
				name: 'closeDate',
				type: 'dateTime',
				default: '',
			},
			{
				displayName: 'Company Name',
				name: 'companyName',
				type: 'string',
				default: '',
			},
			{
				displayName: 'Company Size',
				name: 'companySize',
				type: 'string',
				default: '',
			},
			{
				displayName: 'Contact Owner',
				name: 'contactOwner',
				type: 'options',
				typeOptions: {
					loadOptionsMethod: 'getOwners',
				},
				default: '',
			},
			{
				displayName: 'Country/Region',
				name: 'country',
				type: 'string',
				default: '',
			},
			{
				displayName: 'Custom Properties',
				name: 'customPropertiesUi',
				placeholder: 'Add Custom Property',
				type: 'fixedCollection',
				typeOptions: {
					multipleValues: true,
				},
				default: {},
				options: [
					{
						name: 'customPropertiesValues',
						displayName: 'Custom Property',
						values: [
							{
								displayName: 'Property',
								name: 'property',
								type: 'options',
								typeOptions: {
									loadOptionsMethod: 'getContactCustomProperties',
								},
								default: '',
								description: 'Name of the property.',
							},
							{
								displayName: 'Value',
								name: 'value',
								type: 'string',
								default: '',
								description: 'Value of the property',
							},
						],
					},
				],
			},
			{
				displayName: 'Date of Birth',
				name: 'dateOfBirth',
				type: 'dateTime',
				default: '',
			},
			{
				displayName: 'Degree',
				name: 'degree',
				type: 'string',
				default: '',
			},
			{
				displayName: 'Facebook Click ID',
				name: 'facebookClickId',
				type: 'string',
				default: '',
			},
			{
				displayName: 'Fax Number',
				name: 'faxNumber',
				type: 'string',
				default: '',
			},
			{
				displayName: 'Field Of Study',
				name: 'fieldOfStudy',
				type: 'string',
				default: '',
				description: `A contact's field of study. This property is required for the Facebook Ads Integration. This property will be automatically synced via the Lead Ads tool`,
			},
			{
				displayName: 'First Name',
				name: 'firstName',
				type: 'string',
				default: '',
				description: `A contact's first name`,
			},
			{
				displayName: 'Gender',
				name: 'gender',
				type: 'string',
				default: '',
			},
			{
				displayName: 'Google Ad Click ID',
				name: 'googleAdClickId',
				type: 'string',
				default: '',
			},
			{
				displayName: 'Graduation Date',
				name: 'graduationDate',
				type: 'dateTime',
				default: '',
				description: `A contact's graduation date. This property is required for the Facebook Ads Integration. This property will be automatically synced via the Lead Ads tool`,
			},
			{
				displayName: 'Industry',
				name: 'industry',
				type: 'string',
				default: '',
				description: 'The industry a contact is in',
			},
			{
				displayName: 'Job Function',
				name: 'jobFunction',
				type: 'string',
				default: '',
				description: `A contact's job function. This property is required for the Facebook Ads Integration. This property will be automatically synced via the Lead Ads tool`,
			},
			{
				displayName: 'Job Title',
				name: 'jobTitle',
				type: 'string',
				default: '',
				description: `A contact's job title`,
			},
			{
				displayName: 'Last Name',
				name: 'lastName',
				type: 'string',
				default: '',
				description: `A contact's last name`,
			},
			{
				displayName: 'Lead Status',
				name: 'leadStatus',
				type: 'options',
				typeOptions: {
					loadOptionsMethod: 'getContactLeadStatuses',
				},
				default: '',
				description: `The contact's sales, prospecting or outreach status`,
			},
			{
				displayName: 'Legal Basic For Processing Contact Data',
				name: 'processingContactData',
				type: 'options',
				typeOptions: {
					loadOptionsMethod: 'getContactLealBasics',
				},
				default: '',
				description: `Legal basis for processing contact's data; 'Not applicable' will exempt the contact from GDPR protections`,
			},
			{
				displayName: 'Lifecycle Stage',
				name: 'lifeCycleStage',
				type: 'options',
				typeOptions: {
					loadOptionsMethod: 'getContactLifeCycleStages',
				},
				default: '',
				description: `The qualification of contacts to sales readiness. It can be set through imports, forms, workflows, and manually on a per contact basis.`,
			},
			{
				displayName: 'Marital Status',
				name: 'maritalStatus',
				type: 'string',
				default: '',
				description: `A contact's marital status. This property is required for the Facebook Ads Integration. This property will be automatically synced via the Lead Ads tool`,
			},
			{
				displayName: 'Membership Note',
				name: 'membershipNote',
				type: 'string',
				typeOptions: {
					alwaysOpenEditWindow: true,
				},
				default: '',
				description: `The notes relating to the contact's content membership.`,
			},
			{
				displayName: 'Message',
				name: 'message',
				type: 'string',
				typeOptions: {
					alwaysOpenEditWindow: true,
				},
				default: '',
				description: 'A default property to be used for any message or comments a contact may want to leave on a form.',
			},
			{
				displayName: 'Mobile Phone Number',
				name: 'mobilePhoneNumber',
				type: 'string',
				default: '',
				description: `A contact's mobile phone number`,
			},
			{
				displayName: 'Number Of Employees',
				name: 'numberOfEmployees',
				type: 'options',
				typeOptions: {
					loadOptionsMethod: 'getContactNumberOfEmployees',
				},
				default: '',
				description: 'The number of company employees',
			},
			{
				displayName: 'Original Source',
				name: 'originalSource',
				type: 'options',
				typeOptions: {
					loadOptionsMethod: 'getContactOriginalSources',
				},
				default: '',
				description: `The first known source through which a contact found your website. Source is automatically set by HubSpot, but may be updated manually.`,
			},
			{
				displayName: 'Phone Number',
				name: 'phoneNumber',
				type: 'string',
				default: '',
				description: `A contact's primary phone number`,
			},
			{
				displayName: 'Properties',
				name: 'properties',
				type: 'multiOptions',
				typeOptions: {
					loadOptionsMethod: 'getContactProperties',
				},
				displayOptions: {
					show: {
						'/resolveData': [
							true,
						],
					},
				},
				default: '',
				description: `Used to include specific company properties in the results.

				By default, the results will only include company ID and will not include the values for any properties for your companys.

				Including this parameter will include the data for the specified property in the results.

				You can include this parameter multiple times to request multiple properties separed by ,.`,
			},
			{
				displayName: 'Postal Code',
				name: 'postalCode',
				type: 'string',
				default: '',
				description: `The contact's zip code. This might be set via import, form, or integration.`,
			},
			{
				displayName: 'Preffered Language',
				name: 'prefferedLanguage',
				type: 'options',
				typeOptions: {
					loadOptionsMethod: 'getContactPrefferedLanguages',
				},
				default: '',
				description: `Set your contact's preferred language for communications. This property can be changed from an import, form, or integration.`,
			},
			{
				displayName: 'Relationship Status',
				name: 'relationshipStatus',
				type: 'string',
				default: '',
				description: `A contact's relationship status. This property is required for the Facebook Ads Integration. This property will be automatically synced via the Lead Ads tool`,
			},
			{
				displayName: 'Salutation',
				name: 'salutation',
				type: 'string',
				default: '',
				description: `The title used to address a contact`,
			},
			{
				displayName: 'School',
				name: 'school',
				type: 'string',
				default: '',
				description: `A contact's school. This property is required for the Facebook Ads Integration. This property will be automatically synced via the Lead Ads tool`,
			},
			{
				displayName: 'Seniority',
				name: 'seniority',
				type: 'string',
				default: '',
				description: `A contact's seniority. This property is required for the Facebook Ads Integration. This property will be automatically synced via the Lead Ads tool`,
			},
			{
				displayName: 'Start Date',
				name: 'startDate',
				type: 'dateTime',
				default: '',
				description: `A contact's start date. This property is required for the Facebook Ads Integration. This property will be automatically synced via the Lead Ads tool`,
			},
			{
				displayName: 'State/Region',
				name: 'stateRegion',
				type: 'string',
				default: '',
				description: `The contact's state of residence. This might be set via import, form, or integration.`,
			},
			{
				displayName: 'Status',
				name: 'status',
				type: 'options',
				typeOptions: {
					loadOptionsMethod: 'getContactStatuses',
				},
				default: '',
				description: `The status of the contact's content membership.`,
			},
			{
				displayName: 'Street Address',
				name: 'streetAddress',
				type: 'string',
				default: '',
				description: `A contact's street address, including apartment or unit #`,
			},
			{
				displayName: 'Twitter Username',
				name: 'twitterUsername',
				type: 'string',
				default: '',
				description: `The contact's Twitter handle. This is set by HubSpot using the contact's email address.`,
			},
			{
				displayName: 'Website URL',
				name: 'websiteUrl',
				type: 'string',
				default: '',
				description: `The contact's company website`,
			},
			{
				displayName: 'Work Email',
				name: 'workEmail',
				type: 'string',
				default: '',
				description: `A contact's work email. This property is required for the Facebook Ads Integration. This property will be automatically synced via the Lead Ads tool`,
			},
		],
	},
/* -------------------------------------------------------------------------- */
/*                                  contact:get                               */
/* -------------------------------------------------------------------------- */
	{
		displayName: 'Contact ID',
		name: 'contactId',
		type: 'string',
		required: true,
		displayOptions: {
			show: {
				resource: [
					'contact',
				],
				operation: [
					'get',
				],
			},
		},
		default: '',
		description: 'Unique identifier for a particular contact',
	},
	{
		displayName: 'Additional Fields',
		name: 'additionalFields',
		type: 'collection',
		placeholder: 'Add Field',
		default: {},
		displayOptions: {
			show: {
				resource: [
					'contact',
				],
				operation: [
					'get',
				],
			},
		},
		options: [
			{
				displayName: 'Form Submission Mode',
				name: 'formSubmissionMode',
				type: 'options',
				options: [
					{
						name: 'All',
						value: 'all',
					},
					{
						name: 'None',
						value: 'none',
					},
					{
						name: 'Newest',
						value: 'newest',
					},
					{
						name: 'Oldest',
						value: 'oldest',
					},
				],
				default: 'all',
				description: `Specify which form submissions should be fetched.`,
			},
			{
				displayName: 'List Memberships',
				name: 'listMerberships',
				type: 'boolean',
				default: true,
				description: 'Whether current list memberships should be fetched for the contact.',
			},
			{
				displayName: 'Properties',
				name: 'properties',
				type: 'multiOptions',
				typeOptions: {
					loadOptionsMethod: 'getContactProperties',
				},
				default: '',
				description: `Used to include specific company properties in the results.

				By default, the results will only include company ID and will not include the values for any properties for your companys.

				Including this parameter will include the data for the specified property in the results.

				You can include this parameter multiple times to request multiple properties separed by ,.`,
			},
			{
				displayName: 'Property Mode',
				name: 'propertyMode',
				type: 'options',
				options: [
					{
						name: 'Value And History',
						value: 'valueAndHistory',
					},
					{
						name: 'Value Only',
						value: 'valueOnly',
					},
				],
				default: 'valueAndHistory',
				description: `Specify if the current value for a property should be fetched, or the value and all the historical values for that property.`,
			},
		],
	},
/* -------------------------------------------------------------------------- */
/*                                 contact:getAll                             */
/* -------------------------------------------------------------------------- */
	{
		displayName: 'Return All',
		name: 'returnAll',
		type: 'boolean',
		displayOptions: {
			show: {
				resource: [
					'contact',
				],
				operation: [
					'getAll',
				],
			},
		},
		default: false,
		description: 'If all results should be returned or only up to a given limit.',
	},
	{
		displayName: 'Limit',
		name: 'limit',
		type: 'number',
		displayOptions: {
			show: {
				resource: [
					'contact',
				],
				operation: [
					'getAll',
				],
				returnAll: [
					false,
				],
			},
		},
		typeOptions: {
			minValue: 1,
			maxValue: 250,
		},
		default: 100,
		description: 'How many results to return.',
	},
	{
		displayName: 'Additional Fields',
		name: 'additionalFields',
		type: 'collection',
		placeholder: 'Add Field',
		default: {},
		displayOptions: {
			show: {
				resource: [
					'contact',
				],
				operation: [
					'getAll',
				],
			},
		},
		options: [
			{
				displayName: 'Form Submission Mode',
				name: 'formSubmissionMode',
				type: 'options',
				options: [
					{
						name: 'All',
						value: 'all',
					},
					{
						name: 'None',
						value: 'none',
					},
					{
						name: 'Newest',
						value: 'newest',
					},
					{
						name: 'Oldest',
						value: 'oldest',
					},
				],
				default: 'all',
				description: `Specify which form submissions should be fetched.`,
			},
			{
				displayName: 'List Memberships',
				name: 'listMerberships',
				type: 'boolean',
				default: true,
				description: 'Whether current list memberships should be fetched for the contact.',
			},
			{
				displayName: 'Properties',
				name: 'properties',
				type: 'multiOptions',
				typeOptions: {
					loadOptionsMethod: 'getContactProperties',
				},
				default: '',
				description: `Used to include specific company properties in the results.

				By default, the results will only include company ID and will not include the values for any properties for your companys.

				Including this parameter will include the data for the specified property in the results.

				You can include this parameter multiple times to request multiple properties separed by ,.`,
			},
			{
				displayName: 'Property Mode',
				name: 'propertyMode',
				type: 'options',
				options: [
					{
						name: 'Value And History',
						value: 'valueAndHistory',
					},
					{
						name: 'Value Only',
						value: 'valueOnly',
					},
				],
				default: 'valueAndHistory',
				description: `Specify if the current value for a property should be fetched, or the value and all the historical values for that property.`,
			},
		],
	},
/* -------------------------------------------------------------------------- */
/*                                 contact:delete                             */
/* -------------------------------------------------------------------------- */
	{
		displayName: 'Contact ID',
		name: 'contactId',
		type: 'string',
		required: true,
		displayOptions: {
			show: {
				resource: [
					'contact',
				],
				operation: [
					'delete',
				],
			},
		},
		default: '',
		description: 'Unique identifier for a particular contact',
	},
/* -------------------------------------------------------------------------- */
/*               contact:getRecentlyCreatedUpdated                            */
/* -------------------------------------------------------------------------- */
	{
		displayName: 'Return All',
		name: 'returnAll',
		type: 'boolean',
		displayOptions: {
			show: {
				resource: [
					'contact',
				],
				operation: [
					'getRecentlyCreatedUpdated',
				],
			},
		},
		default: false,
		description: 'If all results should be returned or only up to a given limit.',
	},
	{
		displayName: 'Limit',
		name: 'limit',
		type: 'number',
		displayOptions: {
			show: {
				resource: [
					'contact',
				],
				operation: [
					'getRecentlyCreatedUpdated',
				],
				returnAll: [
					false,
				],
			},
		},
		typeOptions: {
			minValue: 1,
			maxValue: 250,
		},
		default: 100,
		description: 'How many results to return.',
	},
	{
		displayName: 'Filters',
		name: 'filters',
		type: 'collection',
		placeholder: 'Add Filter',
		default: {},
		displayOptions: {
			show: {
				resource: [
					'contact',
				],
				operation: [
					'getRecentlyCreatedUpdated',
				],
			},
		},
		options: [
			{
				displayName: 'Form Submission Mode',
				name: 'formSubmissionMode',
				type: 'options',
				options: [
					{
						name: 'All',
						value: 'all',
					},
					{
						name: 'None',
						value: 'none',
					},
					{
						name: 'Newest',
						value: 'newest',
					},
					{
						name: 'Oldest',
						value: 'oldest',
					},
				],
				default: 'all',
				description: `Specify which form submissions should be fetched.`,
			},
			{
				displayName: 'List Memberships',
				name: 'listMerberships',
				type: 'boolean',
				default: true,
				description: 'Whether current list memberships should be fetched for the contact.',
			},
			{
				displayName: 'Properties',
				name: 'properties',
				type: 'multiOptions',
				typeOptions: {
					loadOptionsMethod: 'getContactProperties',
				},
				default: '',
				description: `Used to include specific company properties in the results.

				By default, the results will only include company ID and will not include the values for any properties for your companys.

				Including this parameter will include the data for the specified property in the results.

				You can include this parameter multiple times to request multiple properties separed by ,.`,
			},
			{
				displayName: 'Property Mode',
				name: 'propertyMode',
				type: 'options',
				options: [
					{
						name: 'Value And History',
						value: 'valueAndHistory',
					},
					{
						name: 'Value Only',
						value: 'valueOnly',
					},
				],
				default: 'valueAndHistory',
				description: `Specify if the current value for a property should be fetched, or the value and all the historical values for that property.`,
			},
		],
	},

//*-------------------------------------------------------------------------- */
/*                                 contact:search                             */
/* -------------------------------------------------------------------------- */
	{
		displayName: 'Return All',
		name: 'returnAll',
		type: 'boolean',
		displayOptions: {
			show: {
				resource: [
					'contact',
				],
				operation: [
					'search',
				],
			},
		},
		default: false,
		description: 'If all results should be returned or only up to a given limit.',
	},
	{
		displayName: 'Limit',
		name: 'limit',
		type: 'number',
		displayOptions: {
			show: {
				resource: [
					'contact',
				],
				operation: [
					'search',
				],
				returnAll: [
					false,
				],
			},
		},
		typeOptions: {
			minValue: 1,
			maxValue: 250,
		},
		default: 100,
		description: 'How many results to return.',
	},
	{
		displayName: 'Filter Groups',
		name: 'filterGroupsUi',
		type: 'fixedCollection',
		default: '',
		placeholder: 'Add Filter Group',
		typeOptions: {
			multipleValues: true,
		},
		required: false,
		displayOptions: {
			show: {
				resource: [
					'contact',
				],
				operation: [
					'search',
				],
			},
		},
		options: [
			{
				name: 'filterGroupsValues',
				displayName: 'Filter Group',
				values: [
					{
						displayName: 'Filters',
						name: 'filtersUi',
						type: 'fixedCollection',
						default: '',
						placeholder: 'Add Filter',
						typeOptions: {
							multipleValues: true,
						},
						required: false,
						options: [
							{
								name: 'filterValues',
								displayName: 'Filter',
								values: [
									{
										displayName: 'Property Name',
										name: 'propertyName',
										type: 'options',
										typeOptions: {
											loadOptionsMethod: 'getContactProperties',
										},
										default: '',
									},
									{
										displayName: 'Operator',
										name: 'operator',
										type: 'options',
										options: [
											{
												name: 'Equal',
												value: 'EQ',
											},
											{
												name: 'Not Equal',
												value: 'NEQ',
											},
											{
												name: 'Less Than',
												value: 'LT',
											},
											{
												name: 'Less Than Or Equal',
												value: 'LTE',
											},
											{
												name: 'Greater Than',
												value: 'GT',
											},
											{
												name: 'Greater Than Or Equal',
												value: 'GTE',
											},
											{
												name: 'Is Known',
												value: 'HAS_PROPERTY',
											},
											{
												name: 'Is Unknown',
												value: 'NOT_HAS_PROPERTY',
											},
											{
												name: 'Contains Exactly',
												value: 'CONSTAIN_TOKEN',
											},
											{
												name: `Doesn't Contain Exactly`,
												value: 'NOT_CONSTAIN_TOKEN',
											},
										],
										default: 'EQ',
									},
									{
										displayName: 'Value',
										name: 'value',
										displayOptions: {
											hide: {
												operator: [
													'HAS_PROPERTY',
													'NOT_HAS_PROPERTY',
												],
											},
										},
										type: 'string',
										default: '',
									},
								],
							},
						],
						description: 'Use filters to limit the results to only CRM objects with matching property values. More info here',
					},
				],
			},
		],
		description: `When multiple filters are provided within a filterGroup, they will be combined using a logical AND operator.

		When multiple filterGroups are provided, they will be combined using a logical OR operator.

		The system supports a maximum of three filterGroups with up to three filters each.

		More info here`,
	},
	{
		displayName: 'Additional Fields',
		name: 'additionalFields',
		type: 'collection',
		placeholder: 'Add Field',
		default: {},
		displayOptions: {
			show: {
				resource: [
					'contact',
				],
				operation: [
					'search',
				],
			},
		},
		options: [
			{
				displayName: 'Direction',
				name: 'direction',
				type: 'options',
				options: [
					{
						name: 'ASC',
						value: 'ASCENDING',
					},
					{
						name: 'DESC',
						value: 'DESCENDING',
					},
				],
				default: 'DESCENDING',
				description: 'Defines the direction in which search results are ordered. Default value is DESC.',
			},
			{
				displayName: 'Fields',
				name: 'properties',
				type: 'multiOptions',
				typeOptions: {
					loadOptionsMethod: 'getContactProperties',
				},
				default: [
					'firstname',
					'lastname',
					'email',
				],
				description: `Used to include specific company properties in the results.

				By default, the results will only include company ID and will not include the values for any properties for your companys.

				Including this parameter will include the data for the specified property in the results.

				You can include this parameter multiple times to request multiple properties separed by ,.`,
			},
			{
				displayName: 'Query',
				name: 'query',
				type: 'string',
				default: '',
				description: 'Perform a text search against all property values for an object type',
			},
			{
				displayName: 'Sort By',
				name: 'sortBy',
				type: 'options',
				typeOptions: {
					loadOptionsMethod: 'getContactProperties',
				},
				default: 'createdate',
			},
		],
	},
] as INodeProperties[];


import {
	IExecuteFunctions,
	IExecuteSingleFunctions,
	ILoadOptionsFunctions
} from 'n8n-core';
import { cockpitApiRequest } from './GenericFunctions';

export async function getSingleton(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, resourceName: string): Promise<any> { // tslint:disable-line:no-any
	return cockpitApiRequest.call(this, 'get', `/singletons/get/${resourceName}`);
}

export async function getAllSingletonNames(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions): Promise<string[]> {
	return cockpitApiRequest.call(this, 'GET', `/singletons/listSingletons`, {});
}


<div v-if="showDocumentHelp && nodeType" class="doc-help-wrapper">

<svg id="help-logo" v-if="showDocumentHelp && nodeType" :href="'https://docs.n8n.io/nodes/' + nodeType.name" target="_blank" width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">

<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">

<svg id="help-logo" v-if="showDocumentHelp && nodeType" :href="documentationUrl" target="_blank" width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">


import {
	ICredentialType,
	NodePropertyTypes,
} from 'n8n-workflow';

export class SentryIoApi implements ICredentialType {
	name = 'sentryIoApi';
	displayName = 'Sentry.io API';
	documentationUrl = 'sentryIo';
	properties = [
		{
			displayName: 'Token',
			name: 'token',
			type: 'string' as NodePropertyTypes,
			default: '',
		},
	];
}


import {
	IDataObject,
} from 'n8n-workflow';

import {
	IExecuteFunctions,
	ILoadOptionsFunctions,
} from 'n8n-core';

import {
	googleApiRequest,
} from './GenericFunctions';

import {
	utils as xlsxUtils,
} from 'xlsx';

export interface ISheetOptions {
	scope: string[];
}

export interface IGoogleAuthCredentials {
	email: string;
	privateKey: string;
}

export interface ISheetUpdateData {
	range: string;
	values: string[][];
}

export interface ILookupValues {
	lookupColumn: string;
	lookupValue: string;
}

export interface IToDeleteRange {
	amount: number;
	startIndex: number;
	sheetId: number;
}

export interface IToDelete {
	[key: string]: IToDeleteRange[] | undefined;
	columns?: IToDeleteRange[];
	rows?: IToDeleteRange[];
}

export type ValueInputOption = 'RAW' | 'USER_ENTERED';

export type ValueRenderOption = 'FORMATTED_VALUE' | 'FORMULA' | 'UNFORMATTED_VALUE';

export class GoogleSheet {
	id: string;
	executeFunctions: IExecuteFunctions | ILoadOptionsFunctions;

	constructor(spreadsheetId: string, executeFunctions: IExecuteFunctions | ILoadOptionsFunctions, options?: ISheetOptions | undefined) {
		// options = <SheetOptions>options || {};
		if (!options) {
			options = {} as ISheetOptions;
		}

		this.executeFunctions = executeFunctions;
		this.id = spreadsheetId;
	}

	/**
	 * Encodes the range that also none latin character work
	 *
	 * @param {string} range
	 * @returns {string}
	 * @memberof GoogleSheet
	 */
	encodeRange(range: string): string {
		if (range.includes('!')) {
			const [sheet, ranges] = range.split('!');
			range = `${encodeURIComponent(sheet)}!${ranges}`;
		}
		return range;
	}

	/**
	 * Clears values from a sheet
	 *
	 * @param {string} range
	 * @returns {Promise<object>}
	 * @memberof GoogleSheet
	 */
	async clearData(range: string): Promise<object> {

		const body = {
			spreadsheetId: this.id,
			range,
		};

		const response = await googleApiRequest.call(this.executeFunctions, 'POST', `/v4/spreadsheets/${this.id}/values/${range}:clear`, body);

		return response;
	}

	/**
	 * Returns the cell values
	 */
	async getData(range: string, valueRenderMode: ValueRenderOption): Promise<string[][] | undefined> {

		const query = {
			valueRenderOption: valueRenderMode,
		};

		const response = await googleApiRequest.call(this.executeFunctions, 'GET', `/v4/spreadsheets/${this.id}/values/${range}`, {}, query);

		return response.values as string[][] | undefined;
	}

	/**
	 * Returns the sheets in a Spreadsheet
	 */
	async spreadsheetGetSheets() {

		const query = {
			fields: 'sheets.properties',
		};

		const response = await googleApiRequest.call(this.executeFunctions, 'GET', `/v4/spreadsheets/${this.id}`, {}, query);

		return response;
	}

	/**
	 * Sets values in one or more ranges of a spreadsheet.
	 */
	async spreadsheetBatchUpdate(requests: IDataObject[]) { // tslint:disable-line:no-any

		const body = {
			requests,
		};

		const response = await googleApiRequest.call(this.executeFunctions, 'POST', `/v4/spreadsheets/${this.id}:batchUpdate`, body);

		return response;
	}

	/**
	 * Sets the cell values
	 */
	async batchUpdate(updateData: ISheetUpdateData[], valueInputMode: ValueInputOption) {

		const body = {
			data: updateData,
			valueInputOption: valueInputMode,
		};

		const response = await googleApiRequest.call(this.executeFunctions, 'POST', `/v4/spreadsheets/${this.id}/values:batchUpdate`, body);

		return response;
	}

	/**
	 * Sets the cell values
	 */
	async setData(range: string, data: string[][], valueInputMode: ValueInputOption) {

		const body = {
			valueInputOption: valueInputMode,
			values: data,
		};

		const response = await googleApiRequest.call(this.executeFunctions, 'POST', `/v4/spreadsheets/${this.id}/values/${range}`, body);

		return response;
	}

	/**
	 * Appends the cell values
	 */
	async appendData(range: string, data: string[][], valueInputMode: ValueInputOption) {

		const body = {
			range: decodeURIComponent(range),
			values: data,
		};

		const query = {
			valueInputOption: valueInputMode,
		};

		const response = await googleApiRequest.call(this.executeFunctions, 'POST', `/v4/spreadsheets/${this.id}/values/${range}:append`, body, query);

		return response;
	}

	/**
	 * Returns the given sheet data in a structured way
	 */
	structureData(inputData: string[][], startRow: number, keys: string[], addEmpty?: boolean): IDataObject[] {
		const returnData = [];

		let tempEntry: IDataObject, rowIndex: number, columnIndex: number, key: string;

		for (rowIndex = startRow; rowIndex < inputData.length; rowIndex++) {
			tempEntry = {};
			for (columnIndex = 0; columnIndex < inputData[rowIndex].length; columnIndex++) {
				key = keys[columnIndex];
				if (key) {
					// Only add the data for which a key was given and ignore all others
					tempEntry[key] = inputData[rowIndex][columnIndex];
				}
			}
			if (Object.keys(tempEntry).length || addEmpty === true) {
				// Only add the entry if data got found to not have empty ones
				returnData.push(tempEntry);
			}
		}

		return returnData;
	}

	/**
	 * Returns the given sheet data in a structured way using
	 * the startRow as the one with the name of the key
	 */
	structureArrayDataByColumn(inputData: string[][], keyRow: number, dataStartRow: number): IDataObject[] {

		const keys: string[] = [];

		if (keyRow < 0 || dataStartRow < keyRow || keyRow >= inputData.length) {
			// The key row does not exist so it is not possible to structure data
			return [];
		}

		// Create the keys array
		for (let columnIndex = 0; columnIndex < inputData[keyRow].length; columnIndex++) {
			keys.push(inputData[keyRow][columnIndex]);
		}

		return this.structureData(inputData, dataStartRow, keys);
	}

	async appendSheetData(inputData: IDataObject[], range: string, keyRowIndex: number, valueInputMode: ValueInputOption): Promise<string[][]> {
		const data = await this.convertStructuredDataToArray(inputData, range, keyRowIndex);
		return this.appendData(range, data, valueInputMode);
	}

	getColumnWithOffset(startColumn: string, offset: number): string {
		const columnIndex = xlsxUtils.decode_col(startColumn) + offset;
		return xlsxUtils.encode_col(columnIndex);
	}

	/**
	 * Updates data in a sheet
	 *
	 * @param {IDataObject[]} inputData Data to update Sheet with
	 * @param {string} indexKey The name of the key which gets used to know which rows to update
	 * @param {string} range The range to look for data
	 * @param {number} keyRowIndex Index of the row which contains the keys
	 * @param {number} dataStartRowIndex Index of the first row which contains data
	 * @returns {Promise<string[][]>}
	 * @memberof GoogleSheet
	 */
	async updateSheetData(inputData: IDataObject[], indexKey: string, range: string, keyRowIndex: number, dataStartRowIndex: number, valueInputMode: ValueInputOption, valueRenderMode: ValueRenderOption): Promise<string[][]> {
		// Get current data in Google Sheet
		let rangeStart: string, rangeEnd: string, rangeFull: string;
		let sheet: string | undefined = undefined;
		if (range.includes('!')) {
			[sheet, rangeFull] = range.split('!');
		} else {
			rangeFull = range;
		}
		[rangeStart, rangeEnd] = rangeFull.split(':');

		const rangeStartSplit = rangeStart.match(/([a-zA-Z]{1,10})([0-9]{0,10})/);
		const rangeEndSplit = rangeEnd.match(/([a-zA-Z]{1,10})([0-9]{0,10})/);

		if (rangeStartSplit === null || rangeStartSplit.length !== 3 || rangeEndSplit === null || rangeEndSplit.length !== 3) {
			throw new Error(`The range "${range}" is not valid.`);
		}

		const keyRowRange = `${sheet ? sheet + '!' : ''}${rangeStartSplit[1]}${dataStartRowIndex}:${rangeEndSplit[1]}${dataStartRowIndex}`;

		const sheetDatakeyRow = await this.getData(this.encodeRange(keyRowRange), valueRenderMode);

		if (sheetDatakeyRow === undefined) {
			throw new Error('Could not retrieve the key row!');
		}

		const keyColumnOrder = sheetDatakeyRow[0];

		const keyIndex = keyColumnOrder.indexOf(indexKey);

		if (keyIndex === -1) {
			throw new Error(`Could not find column for key "${indexKey}"!`);
		}

		const startRowIndex = rangeStartSplit[2] || '';
		const endRowIndex = rangeEndSplit[2] || '';

		const keyColumn = this.getColumnWithOffset(rangeStartSplit[1], keyIndex);
		const keyColumnRange = `${sheet ? sheet + '!' : ''}${keyColumn}${startRowIndex}:${keyColumn}${endRowIndex}`;

		const sheetDataKeyColumn = await this.getData(this.encodeRange(keyColumnRange), valueRenderMode);

		if (sheetDataKeyColumn === undefined) {
			throw new Error('Could not retrieve the key column!');
		}

		// TODO: The data till here can be cached optionally. Maybe add an option which can
		//       can be activated if it is used in a loop and nothing else updates the data.

		// Remove the first row which contains the key
		sheetDataKeyColumn.shift();

		// Create an Array which all the key-values of the Google Sheet
		const keyColumnIndexLookup = sheetDataKeyColumn.map((rowContent) => rowContent[0]);

		const updateData: ISheetUpdateData[] = [];
		let itemKey: string | number | undefined | null;
		let propertyName: string;
		let itemKeyIndex: number;
		let updateRowIndex: number;
		let updateColumnName: string;
		for (const inputItem of inputData) {
			itemKey = inputItem[indexKey] as string;
			// if ([undefined, null].includes(inputItem[indexKey] as string | undefined | null)) {
			if (itemKey === undefined || itemKey === null) {
				// Item does not have the indexKey so we can ignore it
				continue;
			}

			// Item does have the key so check if it exists in Sheet
			itemKeyIndex = keyColumnIndexLookup.indexOf(itemKey as string);
			if (itemKeyIndex === -1) {
				// Key does not exist in the Sheet so it can not be updated so skip it
				continue;
			}

			// Get the row index in which the data should be updated
			updateRowIndex = keyColumnIndexLookup.indexOf(itemKey) + dataStartRowIndex + 1;

			// Check all the properties in the sheet and check which ones exist on the
			// item and should be updated
			for (propertyName of keyColumnOrder) {
				if (propertyName === indexKey) {
					// Ignore the key itself as that does not get changed it gets
					// only used to find the correct row to update
					continue;
				}
				if (inputItem[propertyName] === undefined || inputItem[propertyName] === null) {
					// Property does not exist so skip it
					continue;
				}

				// Property exists so add it to the data to update

				// Get the column name in which the property data can be found
				updateColumnName = this.getColumnWithOffset(rangeStartSplit[1], keyColumnOrder.indexOf(propertyName));

				updateData.push({
					range: `${sheet ? sheet + '!' : ''}${updateColumnName}${updateRowIndex}`,
					values: [
						[
							inputItem[propertyName] as string,
						],
					],
				});

			}
		}

		return this.batchUpdate(updateData, valueInputMode);
	}

	/**
	 * Looks for a specific value in a column and if it gets found it returns the whole row
	 *
	 * @param {string[][]} inputData Data to to check for lookup value in
	 * @param {number} keyRowIndex Index of the row which contains the keys
	 * @param {number} dataStartRowIndex Index of the first row which contains data
	 * @param {ILookupValues[]} lookupValues The lookup values which decide what data to return
	 * @param {boolean} [returnAllMatches] Returns all the found matches instead of only the first one
	 * @returns {Promise<IDataObject[]>}
	 * @memberof GoogleSheet
	 */
	async lookupValues(inputData: string[][], keyRowIndex: number, dataStartRowIndex: number, lookupValues: ILookupValues[], returnAllMatches?: boolean): Promise<IDataObject[]> {
		const keys: string[] = [];

		if (keyRowIndex < 0 || dataStartRowIndex < keyRowIndex || keyRowIndex >= inputData.length) {
			// The key row does not exist so it is not possible to look up the data
			throw new Error(`The key row does not exist!`);
		}

		// Create the keys array
		for (let columnIndex = 0; columnIndex < inputData[keyRowIndex].length; columnIndex++) {
			keys.push(inputData[keyRowIndex][columnIndex]);
		}

		const returnData = [
			inputData[keyRowIndex],
		];

		// Loop over all the lookup values and try to find a row to return
		let rowIndex: number;
		let returnColumnIndex: number;
		lookupLoop:
		for (const lookupValue of lookupValues) {
			returnColumnIndex = keys.indexOf(lookupValue.lookupColumn);

			if (returnColumnIndex === -1) {
				throw new Error(`The column "${lookupValue.lookupColumn}" could not be found!`);
			}

			// Loop over all the items and find the one with the matching value
			for (rowIndex = dataStartRowIndex; rowIndex < inputData.length; rowIndex++) {
				if (inputData[rowIndex][returnColumnIndex]?.toString() === lookupValue.lookupValue.toString()) {
					returnData.push(inputData[rowIndex]);

					if (returnAllMatches !== true) {
						continue lookupLoop;
					}
				}
			}

			// If value could not be found add an empty one that the order of
			// the returned items stays the same
			if (returnAllMatches !== true) {
				returnData.push([]);
			}
		}

		return this.structureData(returnData, 1, keys, true);
	}

	async convertStructuredDataToArray(inputData: IDataObject[], range: string, keyRowIndex: number): Promise<string[][]> {
		let startColumn, endColumn;
		let sheet: string | undefined = undefined;
		if (range.includes('!')) {
			[sheet, range] = range.split('!');
		}
		[startColumn, endColumn] = range.split(':');

		let getRange = `${startColumn}${keyRowIndex + 1}:${endColumn}${keyRowIndex + 1}`;

		if (sheet !== undefined) {
			getRange = `${sheet}!${getRange}`;
		}

		const keyColumnData = await this.getData(getRange, 'UNFORMATTED_VALUE');

		if (keyColumnData === undefined) {
			throw new Error('Could not retrieve the column data!');
		}

		const keyColumnOrder = keyColumnData[0];

		const setData: string[][] = [];

		let rowData: string[] = [];
		inputData.forEach((item) => {
			rowData = [];
			keyColumnOrder.forEach((key) => {
				if (item.hasOwnProperty(key) && item[key]) {
					rowData.push(item[key]!.toString());
				} else {
					rowData.push('');
				}
			});
			setData.push(rowData);
		});

		return setData;
	}
}


import {
	ICredentialType,
	NodePropertyTypes,
} from 'n8n-workflow';

export class MicrosoftOAuth2Api implements ICredentialType {
	name = 'microsoftOAuth2Api';
	extends = [
		'oAuth2Api',
	];
	displayName = 'Microsoft OAuth2 API';
	documentationUrl = 'microsoft';
	properties = [
		//info about the tenantID
		//https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols#endpoints
		{
			displayName: 'Authorization URL',
			name: 'authUrl',
			type: 'string' as NodePropertyTypes,
			default: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
		},
		{
			displayName: 'Access Token URL',
			name: 'accessTokenUrl',
			type: 'string' as NodePropertyTypes,
			default: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
		},
		{
			displayName: 'Auth URI Query Parameters',
			name: 'authQueryParameters',
			type: 'hidden' as NodePropertyTypes,
			default: 'response_mode=query',
		},
		{
			displayName: 'Authentication',
			name: 'authentication',
			type: 'hidden' as NodePropertyTypes,
			default: 'body',
		},
	];
}


<div v-if="showDocumentHelp && nodeType" class="text">

Need help? Open {{nodeType.displayName}} documentation

keep keep keep keep keep replace keep keep keep keep


/**
 * Creates an array of elements split into groups the length of `size`.
 * If `array` can't be split evenly, the final chunk will be the remaining
 * elements.
 *
 * @param {Array} array The array to process.
 * @param {number} [size=1] The length of each chunk
 * @returns {Array} Returns the new array of chunks.
 * @example
 *
 * chunk(['a', 'b', 'c', 'd'], 2)
 * // => [['a', 'b'], ['c', 'd']]
 *
 * chunk(['a', 'b', 'c', 'd'], 3)
 * // => [['a', 'b', 'c'], ['d']]
 */
export function chunk(array: any[], size = 1) { // tslint:disable-line:no-any
	const length = array == null ? 0 : array.length;
	if (!length || size < 1) {
		return [];
	}
	let index = 0;
	let resIndex = 0;
	const result = new Array(Math.ceil(length / size));

	while (index < length) {
		result[resIndex++] = array.slice(index, (index += size));
	}
	return result;
}

/**
 * Takes a multidimensional array and converts it to a one-dimensional array.
 *
 * @param {Array} nestedArray The array to be flattened.
 * @returns {Array} Returns the new flattened array.
 * @example
 *
 * flatten([['a', 'b'], ['c', 'd']])
 * // => ['a', 'b', 'c', 'd']
 *
 */
export function flatten(nestedArray: any[][]) { // tslint:disable-line:no-any
	const result = [];

	(function loop(array: any[]) { // tslint:disable-line:no-any
		for (let i = 0; i < array.length; i++) {
			if (Array.isArray(array[i])) {
				loop(array[i]);
			} else {
				result.push(array[i]);
			}
		}
	})(nestedArray);

	return result;
}


export * from './CredentialsEntity';
export * from './ExecutionEntity';
export * from './WorkflowEntity';
export * from './WebhookEntity';


import {
	ICredentialType,
	NodePropertyTypes,
} from 'n8n-workflow';

export class GithubOAuth2Api implements ICredentialType {
	name = 'githubOAuth2Api';
	extends = [
		'oAuth2Api',
	];
	displayName = 'Github OAuth2 API';
	documentationUrl = 'github';
	properties = [
		{
			displayName: 'Github Server',
			name: 'server',
			type: 'string' as NodePropertyTypes,
			default: 'https://api.github.com',
			description: 'The server to connect to. Does only have to get changed if Github Enterprise gets used.',
		},
		{
			displayName: 'Authorization URL',
			name: 'authUrl',
			type: 'hidden' as NodePropertyTypes,
			default: 'https://github.com/login/oauth/authorize',
			required: true,
		},
		{
			displayName: 'Access Token URL',
			name: 'accessTokenUrl',
			type: 'hidden' as NodePropertyTypes,
			default: 'https://github.com/login/oauth/access_token',
			required: true,
		},
		{
			displayName: 'Scope',
			name: 'scope',
			type: 'hidden' as NodePropertyTypes,
			default: 'repo,admin:repo_hook,admin:org,admin:org_hook,gist,notifications,user,write:packages,read:packages,delete:packages,worfklow',
		},
		{
			displayName: 'Auth URI Query Parameters',
			name: 'authQueryParameters',
			type: 'hidden' as NodePropertyTypes,
			default: '',
		},
		{
			displayName: 'Authentication',
			name: 'authentication',
			type: 'hidden' as NodePropertyTypes,
			default: 'header',
		},
	];
}


nodeType (): INodeTypeDescription | null {

keep keep keep keep add keep keep keep keep

if (this.nodeType.documentationUrl && this.nodeType.documentationUrl.startsWith('http')) {

return 'https://docs.n8n.io/nodes/' + (this.nodeType.documentationUrl || this.nodeType.name) + '?utm_source=n8n_app&utm_medium=node_settings_modal-credential_link&utm_campaign=' + this.nodeType.name;


import { get } from 'lodash';
import { IExecuteFunctions } from 'n8n-core';
import {
	IDataObject,
	INodeExecutionData,
	INodeType,
	INodeTypeDescription,
} from 'n8n-workflow';

export class Discord implements INodeType {
	description: INodeTypeDescription = {
		displayName: 'Discord',
		name: 'discord',
		icon: 'file:discord.png',
		group: ['output'],
		version: 1,
		description: 'Sends data to Discord',
		defaults: {
			name: 'Discord',
			color: '#7289da',
		},
		inputs: ['main'],
		outputs: ['main'],
		properties: [
			{
				displayName: 'Webhook URL',
				name: 'webhookUri',
				type: 'string',
				typeOptions: {
					alwaysOpenEditWindow: true,
				},
				default: '',
				description: 'The webhook url.',
			},
			{
				displayName: 'Text',
				name: 'text',
				type: 'string',
				typeOptions: {
					alwaysOpenEditWindow: true,
				},
				default: '',
				description: 'The text to send.',
			},
		],
	};

	async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
		const items = this.getInputData();
		const returnData: IDataObject[] = [];

		const requestMethod = 'POST';

		// For Post
		let body: IDataObject;

		for (let i = 0; i < items.length; i++) {
			const webhookUri = this.getNodeParameter('webhookUri', i) as string;
			body = {};

			body.content = this.getNodeParameter('text', i) as string;

			const options = {
				method: requestMethod,
				body,
				uri: `${webhookUri}`,
				headers: {
					'content-type': 'application/json; charset=utf-8',
				},
				json: true,
			};

			let maxTries = 5;
			do {
				try {
					await this.helpers.request(options);
					break;
				} catch (error) {
					if (error.statusCode === 429) {
						// Waiting rating limit
						await new Promise((resolve) => {
							setTimeout(async () => {
								resolve();
							}, get(error, 'response.body.retry_after', 150));
						});
					} else {
						// If it's another error code then return the JSON response
						throw error;
					}
				}

			} while (--maxTries);

			if (maxTries <= 0) {
				throw new Error('Could not send message. Max. amount of rate-limit retries got reached.');
			}

			returnData.push({success: true});
		}

		return [this.helpers.returnJsonArray(returnData)];
	}
}


import {
	IExecuteFunctions,
	IHookFunctions,
} from 'n8n-core';

import {
	OptionsWithUri,
} from 'request';

/**
 * Make an API request to NextCloud
 *
 * @param {IHookFunctions} this
 * @param {string} method
 * @param {string} url
 * @param {object} body
 * @returns {Promise<any>}
 */
export async function nextCloudApiRequest(this: IHookFunctions | IExecuteFunctions, method: string, endpoint: string, body: object | string | Buffer, headers?: object, encoding?: null | undefined, query?: object): Promise<any> { // tslint:disable-line:no-any
	const options : OptionsWithUri = {
		headers,
		method,
		body,
		qs: {},
		uri: '',
		json: false,
	};

	if (encoding === null) {
		options.encoding = null;
	}

	const authenticationMethod = this.getNodeParameter('authentication', 0);

	try {
		if (authenticationMethod === 'accessToken') {
			const credentials = this.getCredentials('nextCloudApi');
			if (credentials === undefined) {
				throw new Error('No credentials got returned!');
			}

			options.auth = {
				user: credentials.user as string,
				pass: credentials.password as string,
			};

			options.uri = `${credentials.webDavUrl}/${encodeURI(endpoint)}`;

			return await this.helpers.request(options);
		} else {
			const credentials = this.getCredentials('nextCloudOAuth2Api');
			if (credentials === undefined) {
				throw new Error('No credentials got returned!');
			}

			options.uri = `${credentials.webDavUrl}/${encodeURI(endpoint)}`;

			return await this.helpers.requestOAuth2!.call(this, 'nextCloudOAuth2Api', options);
		}
	} catch (error) {
		throw new Error(`NextCloud Error. Status Code: ${error.statusCode}. Message: ${error.message}`);
	}
}