mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-10 06:34:05 -08:00
🔀 Merge branch 'FileMakeNode' of https://github.com/airmoi/n8n into airmoi-FileMakeNode
This commit is contained in:
commit
b51c707563
39
packages/nodes-base/credentials/FileMaker.credentials.ts
Normal file
39
packages/nodes-base/credentials/FileMaker.credentials.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
import {
|
||||
ICredentialType,
|
||||
NodePropertyTypes,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
|
||||
export class FileMaker implements ICredentialType {
|
||||
name = 'FileMaker';
|
||||
displayName = 'FileMaker';
|
||||
properties = [
|
||||
{
|
||||
displayName: 'Host',
|
||||
name: 'host',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Database',
|
||||
name: 'db',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Login',
|
||||
name: 'login',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Password',
|
||||
name: 'password',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
typeOptions: {
|
||||
password: true,
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
];
|
||||
}
|
916
packages/nodes-base/nodes/FileMaker/FileMaker.node.ts
Normal file
916
packages/nodes-base/nodes/FileMaker/FileMaker.node.ts
Normal file
|
@ -0,0 +1,916 @@
|
|||
import {IExecuteFunctions} from 'n8n-core';
|
||||
import {
|
||||
IDataObject,
|
||||
ILoadOptionsFunctions,
|
||||
INodeExecutionData, INodePropertyOptions,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
|
||||
import {OptionsWithUri} from 'request';
|
||||
import {
|
||||
layoutsApiRequest,
|
||||
getFields,
|
||||
getPortals,
|
||||
getScripts,
|
||||
getToken,
|
||||
parseSort,
|
||||
parsePortals,
|
||||
parseQuery,
|
||||
parseScripts,
|
||||
parseFields,
|
||||
logout
|
||||
} from "./GenericFunctions";
|
||||
|
||||
export class FileMaker implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'FileMaker',
|
||||
name: 'filemaker',
|
||||
icon: 'file:filemaker.png',
|
||||
group: ['input'],
|
||||
version: 1,
|
||||
description: 'Retrieve data from FileMaker data API.',
|
||||
defaults: {
|
||||
name: 'FileMaker',
|
||||
color: '#665533',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'FileMaker',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Action',
|
||||
name: 'action',
|
||||
type: 'options',
|
||||
default: 'record',
|
||||
options: [
|
||||
/*{
|
||||
name: 'Login',
|
||||
value: 'login',
|
||||
},
|
||||
{
|
||||
name: 'Logout',
|
||||
value: 'logout',
|
||||
},*/
|
||||
{
|
||||
name: 'Find Records',
|
||||
value: 'find',
|
||||
},
|
||||
{
|
||||
name: 'get Records',
|
||||
value: 'records',
|
||||
},
|
||||
{
|
||||
name: 'Get Records By Id',
|
||||
value: 'record',
|
||||
},
|
||||
{
|
||||
name: 'Perform Script',
|
||||
value: 'performscript',
|
||||
},
|
||||
{
|
||||
name: 'Create Record',
|
||||
value: 'create',
|
||||
},
|
||||
{
|
||||
name: 'Edit Record',
|
||||
value: 'edit',
|
||||
},
|
||||
{
|
||||
name: 'Duplicate Record',
|
||||
value: 'duplicate',
|
||||
},
|
||||
{
|
||||
name: 'Delete Record',
|
||||
value: 'delete',
|
||||
},
|
||||
],
|
||||
description: 'Action to perform.',
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
// shared
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Layout',
|
||||
name: 'layout',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getLayouts',
|
||||
},
|
||||
options: [],
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {},
|
||||
placeholder: 'Layout Name',
|
||||
description: 'FileMaker Layout Name.',
|
||||
},
|
||||
{
|
||||
displayName: 'Record Id',
|
||||
name: 'recid',
|
||||
type: 'number',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'record',
|
||||
'edit',
|
||||
'delete',
|
||||
'duplicate',
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Record ID',
|
||||
description: 'Internal Record ID returned by get (recordid)',
|
||||
},
|
||||
{
|
||||
displayName: 'offset',
|
||||
name: 'offset',
|
||||
placeholder: '0',
|
||||
description: 'The record number of the first record in the range of records.',
|
||||
type: 'number',
|
||||
default: '1',
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'records',
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
displayName: 'limit',
|
||||
name: 'limit',
|
||||
placeholder: '100',
|
||||
description: 'The maximum number of records that should be returned. If not specified, the default value is 100.',
|
||||
type: 'number',
|
||||
default: '100',
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'records',
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
displayName: 'Get portals',
|
||||
name: 'getPortals',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Should we get portal data as well ?',
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'record',
|
||||
'records',
|
||||
'find',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Portals',
|
||||
name: 'portals',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
multipleValueButtonText: 'Add portal',
|
||||
loadOptionsMethod: 'getPortals',
|
||||
},
|
||||
options: [],
|
||||
default: [],
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'record',
|
||||
'records',
|
||||
'find',
|
||||
],
|
||||
getPortals: [
|
||||
true,
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Portals',
|
||||
description: 'The portal result set to return. Use the portal object name or portal table name. If this parameter is omitted, the API will return all portal objects and records in the layout. For best performance, pass the portal object name or portal table name.',
|
||||
},
|
||||
// ----------------------------------
|
||||
// find/records
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Response Layout',
|
||||
name: 'responseLayout',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getResponseLayouts',
|
||||
},
|
||||
options: [],
|
||||
default: '',
|
||||
required: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find'
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Queries',
|
||||
name: 'queries',
|
||||
placeholder: 'Add query',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Queries ',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
name: 'query',
|
||||
displayName: 'Query',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Fields',
|
||||
name: 'fields',
|
||||
placeholder: 'Add field',
|
||||
type: 'fixedCollection',
|
||||
default: {},
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
options: [{
|
||||
name: 'field',
|
||||
displayName: 'Field',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'name',
|
||||
type: 'options',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getFields',
|
||||
},
|
||||
options: [],
|
||||
description: 'Search Field',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Value to search',
|
||||
},
|
||||
]
|
||||
}
|
||||
],
|
||||
description: 'Field Name',
|
||||
},
|
||||
{
|
||||
displayName: 'Omit',
|
||||
name: 'omit',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Sort data ?',
|
||||
name: 'setSort',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Should we sort data ?',
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'record',
|
||||
'records',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Sort',
|
||||
name: 'sortParametersUi',
|
||||
placeholder: 'Add Sort Rules',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
setSort: [
|
||||
true,
|
||||
],
|
||||
action: [
|
||||
'find',
|
||||
'records',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Sort rules',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
name: 'rules',
|
||||
displayName: 'Rules',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'name',
|
||||
type: 'options',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getFields',
|
||||
},
|
||||
options: [],
|
||||
description: 'Field Name.',
|
||||
},
|
||||
{
|
||||
displayName: 'Order',
|
||||
name: 'value',
|
||||
type: 'options',
|
||||
default: 'ascend',
|
||||
options: [
|
||||
{
|
||||
name: 'Ascend',
|
||||
value: 'ascend'
|
||||
},
|
||||
{
|
||||
name: 'Descend',
|
||||
value: 'descend'
|
||||
},
|
||||
],
|
||||
description: 'Sort order.',
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Before find script',
|
||||
name: 'setScriptBefore',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Define a script to be run before the action specified by the API call and after the subsequent sort.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'record',
|
||||
'records',
|
||||
],
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Script Name',
|
||||
name: 'scriptBefore',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getScripts',
|
||||
},
|
||||
options: [],
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'record',
|
||||
'records',
|
||||
],
|
||||
setScriptBefore: [
|
||||
true
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Script Name',
|
||||
description: 'The name of the FileMaker script to be run after the action specified by the API call and after the subsequent sort.',
|
||||
},
|
||||
{
|
||||
displayName: 'Script Parameter',
|
||||
name: 'scriptBeforeParam',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'record',
|
||||
'records',
|
||||
],
|
||||
setScriptBefore: [
|
||||
true
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Script Parameters',
|
||||
description: 'A parameter for the FileMaker script.',
|
||||
},
|
||||
{
|
||||
displayName: 'Before sort script',
|
||||
name: 'setScriptSort',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Define a script to be run after the action specified by the API call but before the subsequent sort.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'record',
|
||||
'records',
|
||||
],
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Script Name',
|
||||
name: 'scriptSort',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getScripts',
|
||||
},
|
||||
options: [],
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'record',
|
||||
'records',
|
||||
],
|
||||
setScriptSort: [
|
||||
true
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Script Name',
|
||||
description: 'The name of the FileMaker script to be run after the action specified by the API call but before the subsequent sort.',
|
||||
},
|
||||
{
|
||||
displayName: 'Script Parameter',
|
||||
name: 'scriptSortParam',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'record',
|
||||
'records',
|
||||
],
|
||||
setScriptSort: [
|
||||
true
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Script Parameters',
|
||||
description: 'A parameter for the FileMaker script.',
|
||||
},
|
||||
{
|
||||
displayName: 'After sort script',
|
||||
name: 'setScriptAfter',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Define a script to be run after the action specified by the API call but before the subsequent sort.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'record',
|
||||
'records',
|
||||
],
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Script Name',
|
||||
name: 'scriptAfter',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getScripts',
|
||||
},
|
||||
options: [],
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'record',
|
||||
'records',
|
||||
],
|
||||
setScriptAfter: [
|
||||
true
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Script Name',
|
||||
description: 'The name of the FileMaker script to be run after the action specified by the API call and after the subsequent sort.',
|
||||
},
|
||||
{
|
||||
displayName: 'Script Parameter',
|
||||
name: 'scriptAfterParam',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'record',
|
||||
'records',
|
||||
],
|
||||
setScriptAfter: [
|
||||
true
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Script Parameters',
|
||||
description: 'A parameter for the FileMaker script.',
|
||||
},
|
||||
// ----------------------------------
|
||||
// create/edit
|
||||
// ----------------------------------
|
||||
/*{
|
||||
displayName: 'fieldData',
|
||||
name: 'fieldData',
|
||||
placeholder: '{"field1": "value", "field2": "value", ...}',
|
||||
description: 'Additional fields to add.',
|
||||
type: 'string',
|
||||
default: '{}',
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'create',
|
||||
'edit',
|
||||
],
|
||||
},
|
||||
}
|
||||
},*/
|
||||
{
|
||||
displayName: 'Mod Id',
|
||||
name: 'modId',
|
||||
description: 'The last modification ID. When you use modId, a record is edited only when the modId matches.',
|
||||
type: 'number',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'edit',
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
displayName: 'Fields',
|
||||
name: 'fieldsParametersUi',
|
||||
placeholder: 'Add field',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'create',
|
||||
'edit',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Fields to define',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
name: 'fields',
|
||||
displayName: 'Fields',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'name',
|
||||
type: 'options',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getFields',
|
||||
},
|
||||
options: [],
|
||||
description: 'Field Name.',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
// ----------------------------------
|
||||
// performscript
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Script Name',
|
||||
name: 'script',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getScripts',
|
||||
},
|
||||
options: [],
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'performscript'
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Script Name',
|
||||
description: 'The name of the FileMaker script to be run.',
|
||||
},
|
||||
{
|
||||
displayName: 'Script Parameter',
|
||||
name: 'scriptParam',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'performscript'
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Script Parameters',
|
||||
description: 'A parameter for the FileMaker script.',
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
methods = {
|
||||
loadOptions: {
|
||||
// Get all the available topics to display them to user so that he can
|
||||
// select them easily
|
||||
async getLayouts(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
|
||||
let layouts;
|
||||
try {
|
||||
layouts = await layoutsApiRequest.call(this);
|
||||
} catch (err) {
|
||||
throw new Error(`FileMaker Error: ${err}`);
|
||||
}
|
||||
for (const layout of layouts) {
|
||||
returnData.push({
|
||||
name: layout.name,
|
||||
value: layout.name,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
async getResponseLayouts(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
returnData.push({
|
||||
name: 'Use main layout',
|
||||
value: '',
|
||||
});
|
||||
|
||||
let layouts;
|
||||
try {
|
||||
layouts = await layoutsApiRequest.call(this);
|
||||
} catch (err) {
|
||||
throw new Error(`FileMaker Error: ${err}`);
|
||||
}
|
||||
for (const layout of layouts) {
|
||||
returnData.push({
|
||||
name: layout.name,
|
||||
value: layout.name,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
|
||||
async getFields(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
|
||||
let fields;
|
||||
try {
|
||||
fields = await getFields.call(this);
|
||||
} catch (err) {
|
||||
throw new Error(`FileMaker Error: ${err}`);
|
||||
}
|
||||
for (const field of fields) {
|
||||
returnData.push({
|
||||
name: field.name,
|
||||
value: field.name,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
|
||||
async getScripts(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
|
||||
let scripts;
|
||||
try {
|
||||
scripts = await getScripts.call(this);
|
||||
} catch (err) {
|
||||
throw new Error(`FileMaker Error: ${err}`);
|
||||
}
|
||||
for (const script of scripts) {
|
||||
if (!script.isFolder) {
|
||||
returnData.push({
|
||||
name: script.name,
|
||||
value: script.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
|
||||
async getPortals(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
|
||||
let portals;
|
||||
try {
|
||||
portals = await getPortals.call(this);
|
||||
} catch (err) {
|
||||
throw new Error(`FileMaker Error: ${err}`);
|
||||
}
|
||||
Object.keys(portals).forEach((portal) => {
|
||||
returnData.push({
|
||||
name: portal,
|
||||
value: portal,
|
||||
});
|
||||
});
|
||||
|
||||
return returnData;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
const returnData: INodeExecutionData[] = [];
|
||||
|
||||
const credentials = this.getCredentials('FileMaker');
|
||||
|
||||
if (credentials === undefined) {
|
||||
throw new Error('No credentials got returned!');
|
||||
}
|
||||
|
||||
let token;
|
||||
try {
|
||||
token = await getToken.call(this);
|
||||
} catch (e) {
|
||||
throw new Error(`Login fail: ${e}`);
|
||||
}
|
||||
|
||||
let requestOptions: OptionsWithUri;
|
||||
|
||||
const host = credentials.host as string;
|
||||
const database = credentials.db as string;
|
||||
|
||||
const url = `https://${host}/fmi/data/v1`;
|
||||
|
||||
const action = this.getNodeParameter('action', 0) as string;
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
// Reset all values
|
||||
requestOptions = {
|
||||
uri: '',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
},
|
||||
method: 'GET',
|
||||
json: true
|
||||
};
|
||||
|
||||
const layout = this.getNodeParameter('layout', 0) as string;
|
||||
|
||||
if (action === 'record') {
|
||||
const recid = this.getNodeParameter('recid', 0) as string;
|
||||
requestOptions.uri = url + `/databases/${database}/layouts/${layout}/records/${recid}`;
|
||||
requestOptions.qs = {
|
||||
'portal': JSON.stringify(parsePortals.call(this)),
|
||||
...parseScripts.call(this)
|
||||
};
|
||||
} else if (action === 'records') {
|
||||
requestOptions.uri = url + `/databases/${database}/layouts/${layout}/records`;
|
||||
requestOptions.qs = {
|
||||
'_offset': this.getNodeParameter('offset', 0),
|
||||
'_limit': this.getNodeParameter('limit', 0),
|
||||
'portal': JSON.stringify(parsePortals.call(this)),
|
||||
...parseScripts.call(this)
|
||||
};
|
||||
const sort = parseSort.call(this);
|
||||
if (sort) {
|
||||
requestOptions.body.sort = sort;
|
||||
}
|
||||
} else if (action === 'find') {
|
||||
requestOptions.uri = url + `/databases/${database}/layouts/${layout}/_find`;
|
||||
requestOptions.method = 'POST';
|
||||
requestOptions.body = {
|
||||
'query': parseQuery.call(this),
|
||||
'offset': this.getNodeParameter('offset', 0),
|
||||
'limit': this.getNodeParameter('limit', 0),
|
||||
'layout.response': this.getNodeParameter('responseLayout', 0),
|
||||
...parseScripts.call(this)
|
||||
};
|
||||
const sort = parseSort.call(this);
|
||||
if (sort) {
|
||||
requestOptions.body.sort = sort;
|
||||
}
|
||||
} else if (action === 'create') {
|
||||
requestOptions.uri = url + `/databases/${database}/layouts/${layout}/records`;
|
||||
requestOptions.method = 'POST';
|
||||
requestOptions.headers!['Content-Type'] = 'application/json';
|
||||
|
||||
//TODO: handle portalData
|
||||
requestOptions.body = {
|
||||
fieldData: {...parseFields.call(this)},
|
||||
portalData: {},
|
||||
...parseScripts.call(this)
|
||||
};
|
||||
} else if (action === 'edit') {
|
||||
const recid = this.getNodeParameter('recid', 0) as string;
|
||||
requestOptions.uri = url + `/databases/${database}/layouts/${layout}/records/${recid}`;
|
||||
requestOptions.method = 'PATCH';
|
||||
requestOptions.headers!['Content-Type'] = 'application/json';
|
||||
|
||||
//TODO: handle portalData
|
||||
requestOptions.body = {
|
||||
fieldData: {...parseFields.call(this)},
|
||||
portalData: {},
|
||||
...parseScripts.call(this)
|
||||
};
|
||||
} else if (action === 'performscript') {
|
||||
const scriptName = this.getNodeParameter('script', 0) as string;
|
||||
requestOptions.uri = url + `/databases/${database}/layouts/${layout}/script/${scriptName}`;
|
||||
requestOptions.qs = {
|
||||
'script.param': this.getNodeParameter('scriptParam', 0),
|
||||
};
|
||||
} else if (action === 'duplicate') {
|
||||
const recid = this.getNodeParameter('recid', 0) as string;
|
||||
requestOptions.uri = url + `/databases/${database}/layouts/${layout}/records/${recid}`;
|
||||
requestOptions.method = 'POST';
|
||||
requestOptions.headers!['Content-Type'] = 'application/json';
|
||||
requestOptions.qs = {
|
||||
...parseScripts.call(this)
|
||||
};
|
||||
} else if (action === 'delete') {
|
||||
const recid = this.getNodeParameter('recid', 0) as string;
|
||||
requestOptions.uri = url + `/databases/${database}/layouts/${layout}/records/${recid}`;
|
||||
requestOptions.method = 'DELETE';
|
||||
requestOptions.qs = {
|
||||
...parseScripts.call(this)
|
||||
};
|
||||
} else {
|
||||
throw new Error(`The action "${action}" is not implemented yet!`);
|
||||
}
|
||||
|
||||
// Now that the options are all set make the actual http request
|
||||
let response;
|
||||
try {
|
||||
response = await this.helpers.request(requestOptions);
|
||||
} catch (error) {
|
||||
response = error.response.body;
|
||||
}
|
||||
|
||||
if (typeof response === 'string') {
|
||||
throw new Error('Response body is not valid JSON. Change "Response Format" to "String"');
|
||||
}
|
||||
await logout.call(this, token);
|
||||
|
||||
returnData.push({json: response});
|
||||
}
|
||||
|
||||
return this.prepareOutputData(returnData);
|
||||
}
|
||||
}
|
372
packages/nodes-base/nodes/FileMaker/GenericFunctions.ts
Normal file
372
packages/nodes-base/nodes/FileMaker/GenericFunctions.ts
Normal file
|
@ -0,0 +1,372 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
IExecuteSingleFunctions
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
ICredentialDataDecryptedObject,
|
||||
IDataObject,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {OptionsWithUri} from 'request';
|
||||
import {Url} from "url";
|
||||
|
||||
interface ScriptsOptions {
|
||||
script?: any; //tslint:disable-line:no-any
|
||||
'script.param'?: any; //tslint:disable-line:no-any
|
||||
'script.prerequest'?: any; //tslint:disable-line:no-any
|
||||
'script.prerequest.param'?: any; //tslint:disable-line:no-any
|
||||
'script.presort'?: any; //tslint:disable-line:no-any
|
||||
'script.presort.param'?: any; //tslint:disable-line:no-any
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an API request to ActiveCampaign
|
||||
*
|
||||
* @param {IHookFunctions} this
|
||||
* @param {string} method
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
export async function layoutsApiRequest(this: ILoadOptionsFunctions | IExecuteFunctions | IExecuteSingleFunctions): Promise<any> { // tslint:disable-line:no-any
|
||||
const token = await getToken.call(this);
|
||||
const credentials = this.getCredentials('FileMaker');
|
||||
|
||||
if (credentials === undefined) {
|
||||
throw new Error('No credentials got returned!');
|
||||
}
|
||||
const host = credentials.host as string;
|
||||
const db = credentials.db as string;
|
||||
|
||||
const url = `https://${host}/fmi/data/v1/databases/${db}/layouts`;
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
},
|
||||
method: 'GET',
|
||||
uri: url,
|
||||
json: true
|
||||
};
|
||||
|
||||
try {
|
||||
const responseData = await this.helpers.request!(options);
|
||||
return responseData.response.layouts;
|
||||
|
||||
} catch (error) {
|
||||
// If that data does not exist for some reason return the actual error
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an API request to ActiveCampaign
|
||||
*
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
export async function getFields(this: ILoadOptionsFunctions): Promise<any> { // tslint:disable-line:no-any
|
||||
const token = await getToken.call(this);
|
||||
const credentials = this.getCredentials('FileMaker');
|
||||
const layout = this.getCurrentNodeParameter('layout') as string;
|
||||
|
||||
if (credentials === undefined) {
|
||||
throw new Error('No credentials got returned!');
|
||||
}
|
||||
const host = credentials.host as string;
|
||||
const db = credentials.db as string;
|
||||
|
||||
const url = `https://${host}/fmi/data/v1/databases/${db}/layouts/${layout}`;
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
},
|
||||
method: 'GET',
|
||||
uri: url,
|
||||
json: true
|
||||
};
|
||||
|
||||
try {
|
||||
const responseData = await this.helpers.request!(options);
|
||||
return responseData.response.fieldMetaData;
|
||||
|
||||
} catch (error) {
|
||||
// If that data does not exist for some reason return the actual error
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make an API request to ActiveCampaign
|
||||
*
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
export async function getPortals(this: ILoadOptionsFunctions): Promise<any> { // tslint:disable-line:no-any
|
||||
const token = await getToken.call(this);
|
||||
const credentials = this.getCredentials('FileMaker');
|
||||
const layout = this.getCurrentNodeParameter('layout') as string;
|
||||
|
||||
if (credentials === undefined) {
|
||||
throw new Error('No credentials got returned!');
|
||||
}
|
||||
const host = credentials.host as string;
|
||||
const db = credentials.db as string;
|
||||
|
||||
const url = `https://${host}/fmi/data/v1/databases/${db}/layouts/${layout}`;
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
},
|
||||
method: 'GET',
|
||||
uri: url,
|
||||
json: true
|
||||
};
|
||||
|
||||
try {
|
||||
const responseData = await this.helpers.request!(options);
|
||||
return responseData.response.portalMetaData;
|
||||
|
||||
} catch (error) {
|
||||
// If that data does not exist for some reason return the actual error
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an API request to ActiveCampaign
|
||||
*
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
export async function getScripts(this: ILoadOptionsFunctions): Promise<any> { // tslint:disable-line:no-any
|
||||
const token = await getToken.call(this);
|
||||
const credentials = this.getCredentials('FileMaker');
|
||||
const layout = this.getCurrentNodeParameter('layout') as string;
|
||||
|
||||
if (credentials === undefined) {
|
||||
throw new Error('No credentials got returned!');
|
||||
}
|
||||
const host = credentials.host as string;
|
||||
const db = credentials.db as string;
|
||||
|
||||
const url = `https://${host}/fmi/data/v1/databases/${db}/scripts`;
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
},
|
||||
method: 'GET',
|
||||
uri: url,
|
||||
json: true
|
||||
};
|
||||
|
||||
try {
|
||||
const responseData = await this.helpers.request!(options);
|
||||
return responseData.response.scripts;
|
||||
|
||||
} catch (error) {
|
||||
// If that data does not exist for some reason return the actual error
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getToken(this: ILoadOptionsFunctions | IExecuteFunctions | IExecuteSingleFunctions): Promise<any> { // tslint:disable-line:no-any
|
||||
const credentials = this.getCredentials('FileMaker');
|
||||
if (credentials === undefined) {
|
||||
throw new Error('No credentials got returned!');
|
||||
}
|
||||
|
||||
const host = credentials.host as string;
|
||||
const db = credentials.db as string;
|
||||
const login = credentials.login as string;
|
||||
const password = credentials.password as string;
|
||||
|
||||
const url = `https://${host}/fmi/data/v1/databases/${db}/sessions`;
|
||||
|
||||
let requestOptions: OptionsWithUri;
|
||||
// Reset all values
|
||||
requestOptions = {
|
||||
uri: url,
|
||||
headers: {},
|
||||
method: 'POST',
|
||||
json: true
|
||||
//rejectUnauthorized: !this.getNodeParameter('allowUnauthorizedCerts', itemIndex, false) as boolean,
|
||||
};
|
||||
requestOptions.auth = {
|
||||
user: login as string,
|
||||
pass: password as string,
|
||||
};
|
||||
requestOptions.body = {
|
||||
"fmDataSource": [
|
||||
{
|
||||
"database": host,
|
||||
"username": login as string,
|
||||
"password": password as string
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await this.helpers.request!(requestOptions);
|
||||
|
||||
if (typeof response === 'string') {
|
||||
throw new Error('Response body is not valid JSON. Change "Response Format" to "String"');
|
||||
}
|
||||
|
||||
return response.response.token;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
let errorMessage;
|
||||
if (error.response) {
|
||||
errorMessage = error.response.body.messages[0].message + '(' + error.response.body.messages[0].message + ')';
|
||||
} else {
|
||||
errorMessage = `${error.message} (${error.name})`;
|
||||
}
|
||||
if (errorMessage !== undefined) {
|
||||
throw errorMessage;
|
||||
}
|
||||
throw error.message;
|
||||
}
|
||||
}
|
||||
|
||||
export async function logout(this: ILoadOptionsFunctions | IExecuteFunctions | IExecuteSingleFunctions, token: string): Promise<any> { // tslint:disable-line:no-any
|
||||
const credentials = this.getCredentials('FileMaker');
|
||||
if (credentials === undefined) {
|
||||
throw new Error('No credentials got returned!');
|
||||
}
|
||||
|
||||
const host = credentials.host as string;
|
||||
const db = credentials.db as string;
|
||||
|
||||
const url = `https://${host}/fmi/data/v1/databases/${db}/sessions/${token}`;
|
||||
|
||||
let requestOptions: OptionsWithUri;
|
||||
// Reset all values
|
||||
requestOptions = {
|
||||
uri: url,
|
||||
headers: {},
|
||||
method: 'DELETE',
|
||||
json: true
|
||||
//rejectUnauthorized: !this.getNodeParameter('allowUnauthorizedCerts', itemIndex, false) as boolean,
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await this.helpers.request!(requestOptions);
|
||||
|
||||
if (typeof response === 'string') {
|
||||
throw new Error('Response body is not valid JSON. Change "Response Format" to "String"');
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
const errorMessage = error.response.body.messages[0].message + '(' + error.response.body.messages[0].message + ')';
|
||||
|
||||
if (errorMessage !== undefined) {
|
||||
throw errorMessage;
|
||||
}
|
||||
throw error.response.body;
|
||||
}
|
||||
}
|
||||
|
||||
export function parseSort(this: IExecuteFunctions): object | null {
|
||||
let sort;
|
||||
const setSort = this.getNodeParameter('setSort', 0, false);
|
||||
if (!setSort) {
|
||||
sort = null;
|
||||
} else {
|
||||
sort = [];
|
||||
const sortParametersUi = this.getNodeParameter('sortParametersUi', 0, {}) as IDataObject;
|
||||
if (sortParametersUi.rules !== undefined) {
|
||||
// @ts-ignore
|
||||
for (const parameterData of sortParametersUi!.rules as IDataObject[]) {
|
||||
// @ts-ignore
|
||||
sort.push({
|
||||
'fieldName': parameterData!.name as string,
|
||||
'sortOrder': parameterData!.value
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return sort;
|
||||
}
|
||||
|
||||
|
||||
export function parseScripts(this: IExecuteFunctions): object | null {
|
||||
const setScriptAfter = this.getNodeParameter('setScriptAfter', 0, false);
|
||||
const setScriptBefore = this.getNodeParameter('setScriptBefore', 0, false);
|
||||
const setScriptSort = this.getNodeParameter('setScriptSort', 0, false);
|
||||
|
||||
if (!setScriptAfter && setScriptBefore && setScriptSort) {
|
||||
return {};
|
||||
} else {
|
||||
const scripts = {} as ScriptsOptions;
|
||||
if (setScriptAfter) {
|
||||
scripts.script = this.getNodeParameter('scriptAfter', 0);
|
||||
scripts!['script.param'] = this.getNodeParameter('scriptAfter', 0);
|
||||
}
|
||||
if (setScriptBefore) {
|
||||
scripts['script.prerequest'] = this.getNodeParameter('scriptBefore', 0);
|
||||
scripts['script.prerequest.param'] = this.getNodeParameter('scriptBeforeParam', 0);
|
||||
}
|
||||
if (setScriptSort) {
|
||||
scripts['script.presort'] = this.getNodeParameter('scriptSort', 0);
|
||||
scripts['script.presort.param'] = this.getNodeParameter('scriptSortParam', 0);
|
||||
}
|
||||
return scripts;
|
||||
}
|
||||
}
|
||||
|
||||
export function parsePortals(this: IExecuteFunctions): object | null {
|
||||
let portals;
|
||||
const getPortals = this.getNodeParameter('getPortals', 0);
|
||||
if (!getPortals) {
|
||||
portals = [];
|
||||
} else {
|
||||
portals = this.getNodeParameter('portals', 0);
|
||||
}
|
||||
// @ts-ignore
|
||||
return portals;
|
||||
}
|
||||
|
||||
|
||||
export function parseQuery(this: IExecuteFunctions): object | null {
|
||||
let queries;
|
||||
const queriesParamUi = this.getNodeParameter('queries', 0, {}) as IDataObject;
|
||||
if (queriesParamUi.query !== undefined) {
|
||||
// @ts-ignore
|
||||
queries = [];
|
||||
for (const queryParam of queriesParamUi!.query as IDataObject[]) {
|
||||
const query = {
|
||||
'omit': queryParam.omit ? 'true' : 'false',
|
||||
};
|
||||
// @ts-ignore
|
||||
for (const field of queryParam!.fields!.field as IDataObject[]) {
|
||||
// @ts-ignore
|
||||
query[field.name] = field!.value;
|
||||
}
|
||||
queries.push(query);
|
||||
}
|
||||
} else {
|
||||
queries = null;
|
||||
}
|
||||
// @ts-ignore
|
||||
return queries;
|
||||
}
|
||||
|
||||
export function parseFields(this: IExecuteFunctions): object | null {
|
||||
let fieldData;
|
||||
const fieldsParametersUi = this.getNodeParameter('fieldsParametersUi', 0, {}) as IDataObject;
|
||||
if (fieldsParametersUi.fields !== undefined) {
|
||||
// @ts-ignore
|
||||
fieldData = {};
|
||||
for (const field of fieldsParametersUi!.fields as IDataObject[]) {
|
||||
// @ts-ignore
|
||||
fieldData[field.name] = field!.value;
|
||||
}
|
||||
} else {
|
||||
fieldData = null;
|
||||
}
|
||||
// @ts-ignore
|
||||
return fieldData;
|
||||
}
|
||||
|
BIN
packages/nodes-base/nodes/FileMaker/filemaker.png
Normal file
BIN
packages/nodes-base/nodes/FileMaker/filemaker.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
|
@ -360,7 +360,6 @@ export class HttpRequest implements INodeType {
|
|||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Body Parameter
|
||||
{
|
||||
displayName: 'Body Parameters',
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
"dist/credentials/ChargebeeApi.credentials.js",
|
||||
"dist/credentials/DropboxApi.credentials.js",
|
||||
"dist/credentials/FreshdeskApi.credentials.js",
|
||||
"dist/credentials/FileMaker.credentials.js",
|
||||
"dist/credentials/GithubApi.credentials.js",
|
||||
"dist/credentials/GitlabApi.credentials.js",
|
||||
"dist/credentials/GoogleApi.credentials.js",
|
||||
|
@ -84,6 +85,7 @@
|
|||
"dist/nodes/EmailSend.node.js",
|
||||
"dist/nodes/ErrorTrigger.node.js",
|
||||
"dist/nodes/ExecuteCommand.node.js",
|
||||
"dist/nodes/FileMaker/FileMaker.node.js",
|
||||
"dist/nodes/Freshdesk/Freshdesk.node.js",
|
||||
"dist/nodes/Function.node.js",
|
||||
"dist/nodes/FunctionItem.node.js",
|
||||
|
|
Loading…
Reference in a new issue