Handle Perform script

Handle Perform script in queries
Handle create/edit
This commit is contained in:
Romain Dunand 2019-11-12 02:01:43 +01:00
parent b30c9bf242
commit 0852a74469
2 changed files with 581 additions and 69 deletions

View file

@ -9,7 +9,19 @@ import {
import {OptionsWithUri} from 'request';
import {layoutsApiRequest, getFields, getPortals, getToken, logout} from "./GenericFunctions";
import {
layoutsApiRequest,
getFields,
getPortals,
getScripts,
getToken,
parseSort,
parsePortals,
parseQuery,
parseScripts,
parseFields,
logout
} from "./GenericFunctions";
export class FileMaker implements INodeType {
description: INodeTypeDescription = {
@ -96,11 +108,6 @@ export class FileMaker implements INodeType {
default: '',
required: true,
displayOptions: {
hide: {
action: [
'performscript'
],
},
},
placeholder: 'Layout Name',
description: 'FileMaker Layout Name.',
@ -124,7 +131,6 @@ export class FileMaker implements INodeType {
placeholder: 'Record ID',
description: 'Internal Record ID returned by get (recordid)',
},
{
displayName: 'offset',
name: 'offset',
@ -163,6 +169,15 @@ export class FileMaker implements INodeType {
type: 'boolean',
default: false,
description: 'Should we get portal data as well ?',
displayOptions: {
show: {
action: [
'record',
'records',
'find',
],
},
},
},
{
displayName: 'Portals',
@ -193,12 +208,107 @@ export class FileMaker implements INodeType {
// ----------------------------------
// find/records
// ----------------------------------
{
displayName: 'Response Layout',
name: 'responseLayout',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getLayouts',
},
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',
@ -258,6 +368,192 @@ export class FileMaker implements INodeType {
},
],
},
{
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
// ----------------------------------
@ -278,15 +574,27 @@ export class FileMaker implements INodeType {
}
},
{
displayName: 'Fields',
name: 'Fields',
type: 'collection',
typeOptions: {
loadOptionsMethod: 'getFields',
},
options: [],
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: '',
required: true,
displayOptions: {
show: {
action: [
'edit',
],
},
}
},
{
displayName: 'Fields',
name: 'fieldsParametersUi',
placeholder: 'Add field',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
displayOptions: {
show: {
action: [
@ -295,8 +603,33 @@ export class FileMaker implements INodeType {
],
},
},
placeholder: 'Layout Name',
description: 'FileMaker Layout Name.',
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
@ -318,8 +651,24 @@ export class FileMaker implements INodeType {
],
},
},
placeholder: 'Layout Name',
description: 'FileMaker Layout Name.',
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.',
},
]
};
@ -363,6 +712,26 @@ export class FileMaker implements INodeType {
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[] = [];
@ -396,85 +765,89 @@ export class FileMaker implements INodeType {
if (credentials === undefined) {
throw new Error('No credentials got returned!');
}
const token = await getToken.call(this);
const staticData = this.getWorkflowStaticData('global');
// Operations which overwrite the returned data
const overwriteDataOperations = [];
// Operations which overwrite the returned data and return arrays
// and has so to be merged with the data of other items
const overwriteDataOperationsArray = [];
let requestOptions: OptionsWithUri;
const host = credentials.host as string;
const database = credentials.db as string;
//const layout = this.getNodeParameter('layout', 0, null) as string;
//const recid = this.getNodeParameter('recid', 0, null) as number;
const url = `https://${host}/fmi/data/v1`;
//const fullOperation = `${resource}:${operation}`;
for (let i = 0; i < items.length; i++) {
// Reset all values
requestOptions = {
uri: '',
headers: {},
headers: {
'Authorization': `Bearer ${token}`,
},
method: 'GET',
json: true
//rejectUnauthorized: !this.getNodeParameter('allowUnauthorizedCerts', itemIndex, false) as boolean,
};
const layout = this.getNodeParameter('layout', 0) as string;
const token = await getToken.call(this);
if (action === 'record') {
const recid = this.getNodeParameter('recid', 0) as string;
requestOptions.uri = url + `/databases/${database}/layouts/${layout}/records/${recid}`;
requestOptions.method = 'GET';
requestOptions.headers = {
'Authorization': `Bearer ${token}`,
requestOptions.qs = {
'portal': JSON.stringify(parsePortals.call(this)),
...parseScripts.call(this)
};
} else if (action === 'records') {
requestOptions.uri = url + `/databases/${database}/layouts/${layout}/records`;
requestOptions.method = 'GET';
requestOptions.headers = {
'Authorization': `Bearer ${token}`,
};
//Handle Sort
let sort;
const setSort = this.getNodeParameter('setSort', 0, false);
if (setSort) {
sort = null;
} else {
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
});
}
}
}
//handle portals
let portals;
const getPortals = this.getNodeParameter('getPortals', 0);
if (!getPortals) {
portals = [];
} else {
portals = this.getNodeParameter('portals', 0);
}
requestOptions.qs = {
'_offset': this.getNodeParameter('offset', 0),
'_limit': this.getNodeParameter('limit', 0),
'_sort': JSON.stringify(sort),
'portal': JSON.stringify(portals),
'_sort': JSON.stringify(parseSort.call(this)),
'portal': JSON.stringify(parsePortals.call(this)),
...parseScripts.call(this)
};
} 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 {
throw new Error(`The action "${action}" is not implemented yet!`);

View file

@ -122,6 +122,42 @@ export async function getPortals(this: ILoadOptionsFunctions): Promise<any> { //
}
}
/**
* 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) {
@ -219,3 +255,106 @@ export async function logout(this: ILoadOptionsFunctions | IExecuteFunctions | I
}
}
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 = {
};
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;
}