mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 04:47:29 -08:00
✨ Add BambooHR Node (#2471)
* Feature // Created BambooHR Node, Init Simple Api Calls for Employees * BambooHR Added EmployeesFiles, CompanyFiles API Operations * BambooHR / Refactor the code * Bamboo HR Node // Refactor + Optimization of Employee Api Requests * Bamboo HR Node // Refactor + Optimization of EmployeeFiles Api Requests * Bamboo HR Node // Refactor + Optimization of CompanyFiles Api Requests * Bamboo HR Node // Add Reports - Get Reports by ID * Bamboo HR Node // Format BambooAPI Credential files * Bamboo HR Node // Added Account Information API operations * Bamboo HR Node //Add Https Reposnse Code for All API Operations, Implement new Http Helper * Bamboo HR Node // Added Tabular Data Api Operations * Bamboo HR Node // Added Time Off Api Operations * Bamboo HR Node //Fixed bugs for AccountInformation Operation, Uploading Employees File, TimeOff EstimationTime * Bamboo HR Node // Update AccountInformation - UpdateFields Api operation * Bamboo HR Node //Fixed Add and Update Table rows // Tabular Data API Operations * Update AccountInformation - Added TimeOff Operations - Get Requests, Create Request, Create History Item, Fixed Get Types * Bamboo HR Node // Fixed Adjust Time - TimeOff API Operation, Wrote comments, Finished TimeOff Api Operations * Bamboo HR Node // Add Multi choice Property for Employee Operations, Expand and Add new Optional Fields for Creating and Updating Employee, Write Comments, Fixed Employee API operations * Bamboo HR Node // Add Multi choice Property + Update Fields and Write Comments for Employee Files & Company Files & Reports Acc Info API Operation * N8N-2603 Move Company Name Prop to Credentials, Changed Execution Function * N8N-2603 Obtain Employee ID and bind to the response after create an employee, Refactor GetDirectoy to GetAll + update the output of the response * N8N-2603 Refactor, Added ID in responses, Added Pagination to some operations * N8N-2603 Refactor Employee Files to Employee File * N8N-2603 Refactor CompanyFiles to Company File, Refactor CompanyFile:Create -> CompanyFile:addCategory * N8N-2603 Refactor employeeFile:create -> employeeFile:addCategory, Get rid off Account Information resources * N8N-2603 EmployeeFile:Update -> Change ShareWithEmployee Parameter to be boolean * N8N-2603 CompanyFIle:update -> Change shareWithCompany prop to boolean * N8N-2603 Added Load Options for getTimeOffTypeId, Rename AdditionalFields to UpdateFields * N8N-2603 Updated Logo and Border * N8N-2603 Refactor Employees to Employee * N8N-2603 Linter Fixes * N8N-2603 Refactor EmployeeFile:get -> EmployeeFile:download, CompanyFile:get -> CompanyFile:download * N8N-2603 Linter fix * N8N-2603 Linter Fixes * N8N-2603 Hotfix * N8N-2603 Fixed EmployeeFile:Download * N8N-2603 Updated Assertion * N8N-2603 Remove unnecesary description, optimized code, created separate loadOptions file * N8N-2603 Added Download Function for CompanyFile operation * N8N-2603 Added DateTime Fields instead of string, Removed Color prop from Node * N8N-2603 Refactor Del to Delete * N8N-2603 Added Upload Employ File Operation * N8N-2603 Updated Possible Types of the Request * N8N-2603 Fixed Linter Errors * N8N-2603 Hotfix Upload Employee File Description * N8N-2603 Added options to download the report * ⚡ Improvements * ⚡ Improvements * ⚡ Simplify node * ⚡ Fix linting issue * ⚡ Improvements * ⚡ Fix returned mimeType Co-authored-by: ricardo <ricardoespinoza105@gmail.com> Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
parent
49bf786e5b
commit
8cefafa47d
236
package-lock.json
generated
236
package-lock.json
generated
|
@ -13561,6 +13561,15 @@
|
|||
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz",
|
||||
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw=="
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"array-union": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
|
||||
|
@ -13596,6 +13605,21 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"optional": true
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
|
@ -13658,6 +13682,58 @@
|
|||
"worker-rpc": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"fork-ts-checker-webpack-plugin-v5": {
|
||||
"version": "npm:fork-ts-checker-webpack-plugin@5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.1.tgz",
|
||||
"integrity": "sha512-SVi+ZAQOGbtAsUWrZvGzz38ga2YqjWvca1pXQFUArIVXqli0lLoDQ8uS0wg0kSpcwpZmaW5jVCZXQebkyUQSsw==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.8.3",
|
||||
"@types/json-schema": "^7.0.5",
|
||||
"chalk": "^4.1.0",
|
||||
"cosmiconfig": "^6.0.0",
|
||||
"deepmerge": "^4.2.2",
|
||||
"fs-extra": "^9.0.0",
|
||||
"memfs": "^3.1.2",
|
||||
"minimatch": "^3.0.4",
|
||||
"schema-utils": "2.7.0",
|
||||
"semver": "^7.3.2",
|
||||
"tapable": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "7.3.5",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
|
||||
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"lru-cache": "^6.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
|
||||
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"at-least-node": "^1.0.0",
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
"universalify": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"glob-parent": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
|
||||
|
@ -13692,6 +13768,12 @@
|
|||
"slash": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"optional": true
|
||||
},
|
||||
"ignore": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
|
||||
|
@ -13720,6 +13802,16 @@
|
|||
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
|
||||
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"micromatch": {
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
|
||||
|
@ -13755,6 +13847,17 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
|
||||
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.4",
|
||||
"ajv": "^6.12.2",
|
||||
"ajv-keywords": "^3.4.1"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
|
@ -13765,6 +13868,15 @@
|
|||
"resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
|
||||
"integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A=="
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"to-regex-range": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
|
||||
|
@ -13858,6 +13970,12 @@
|
|||
"requires": {
|
||||
"tslib": "^1.8.1"
|
||||
}
|
||||
},
|
||||
"universalify": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
|
||||
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -23351,124 +23469,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"fork-ts-checker-webpack-plugin-v5": {
|
||||
"version": "npm:fork-ts-checker-webpack-plugin@5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.1.tgz",
|
||||
"integrity": "sha512-SVi+ZAQOGbtAsUWrZvGzz38ga2YqjWvca1pXQFUArIVXqli0lLoDQ8uS0wg0kSpcwpZmaW5jVCZXQebkyUQSsw==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.8.3",
|
||||
"@types/json-schema": "^7.0.5",
|
||||
"chalk": "^4.1.0",
|
||||
"cosmiconfig": "^6.0.0",
|
||||
"deepmerge": "^4.2.2",
|
||||
"fs-extra": "^9.0.0",
|
||||
"memfs": "^3.1.2",
|
||||
"minimatch": "^3.0.4",
|
||||
"schema-utils": "2.7.0",
|
||||
"semver": "^7.3.2",
|
||||
"tapable": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"optional": true
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
|
||||
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"at-least-node": "^1.0.0",
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
"universalify": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"optional": true
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
|
||||
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.4",
|
||||
"ajv": "^6.12.2",
|
||||
"ajv-keywords": "^3.4.1"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "7.3.5",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
|
||||
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"lru-cache": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"universalify": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
|
||||
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
|
|
24
packages/nodes-base/credentials/BambooHRApi.credentials.ts
Normal file
24
packages/nodes-base/credentials/BambooHRApi.credentials.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import {
|
||||
ICredentialType,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export class BambooHRApi implements ICredentialType {
|
||||
name = 'bambooHRApi';
|
||||
displayName = 'BambooHR API';
|
||||
documentationUrl = 'bambooHR';
|
||||
properties: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Subdomain',
|
||||
name: 'subdomain',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'API Key',
|
||||
name: 'apiKey',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
];
|
||||
}
|
34
packages/nodes-base/nodes/BambooHR/BambooHR.node.ts
Normal file
34
packages/nodes-base/nodes/BambooHR/BambooHR.node.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
INodeType,
|
||||
INodeTypeBaseDescription,
|
||||
INodeTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { router } from './v1/actions/router';
|
||||
import { versionDescription } from './v1/actions/versionDescription';
|
||||
import { loadOptions } from './v1/methods';
|
||||
import { credentialTest } from './v1/methods';
|
||||
|
||||
export class BambooHR implements INodeType {
|
||||
description: INodeTypeDescription;
|
||||
|
||||
constructor(baseDescription: INodeTypeBaseDescription) {
|
||||
this.description = {
|
||||
...baseDescription,
|
||||
...versionDescription,
|
||||
};
|
||||
}
|
||||
|
||||
methods = {
|
||||
loadOptions,
|
||||
credentialTest,
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions) {
|
||||
return [await router.call(this)];
|
||||
}
|
||||
}
|
BIN
packages/nodes-base/nodes/BambooHR/bambooHR.png
Normal file
BIN
packages/nodes-base/nodes/BambooHR/bambooHR.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
33
packages/nodes-base/nodes/BambooHR/v1/actions/Interfaces.ts
Normal file
33
packages/nodes-base/nodes/BambooHR/v1/actions/Interfaces.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import {
|
||||
AllEntities,
|
||||
Entity,
|
||||
PropertiesOf,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
type BambooHRMap = {
|
||||
employee: 'create' | 'get' | 'getAll' | 'update';
|
||||
employeeDocument: 'delete' | 'download' | 'get' | 'getAll' | 'update' | 'upload';
|
||||
file: 'delete' | 'download' | 'getAll' | 'update';
|
||||
companyReport: 'get';
|
||||
};
|
||||
|
||||
export type BambooHR = AllEntities<BambooHRMap>;
|
||||
|
||||
export type BambooHRFile = Entity<BambooHRMap, 'file'>;
|
||||
export type BambooHREmployee = Entity<BambooHRMap, 'employee'>;
|
||||
export type BambooHREmployeeDocument = Entity<BambooHRMap, 'employeeDocument'>;
|
||||
export type BambooHRCompanyReport = Entity<BambooHRMap, 'companyReport'>;
|
||||
|
||||
export type FileProperties = PropertiesOf<BambooHRFile>;
|
||||
export type EmployeeProperties = PropertiesOf<BambooHREmployee>;
|
||||
export type EmployeeDocumentProperties = PropertiesOf<BambooHREmployeeDocument>;
|
||||
export type CompanyReportProperties = PropertiesOf<BambooHRCompanyReport>;
|
||||
|
||||
export interface IAttachment {
|
||||
fields: {
|
||||
item?: object[];
|
||||
};
|
||||
actions: {
|
||||
item?: object[];
|
||||
};
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
import { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const companyReportGetDescription: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Report ID',
|
||||
name: 'reportId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
resource: [
|
||||
'companyReport',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'ID of the report. You can get the report number by hovering over the report name on the reports page and grabbing the ID.',
|
||||
},
|
||||
{
|
||||
displayName: 'Format',
|
||||
name: 'format',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'CSV',
|
||||
value: 'CSV',
|
||||
},
|
||||
{
|
||||
name: 'JSON',
|
||||
value: 'JSON',
|
||||
},
|
||||
{
|
||||
name: 'PDF',
|
||||
value: 'PDF',
|
||||
},
|
||||
{
|
||||
name: 'XLS',
|
||||
value: 'XLS',
|
||||
},
|
||||
{
|
||||
name: 'XML',
|
||||
value: 'XML',
|
||||
},
|
||||
],
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
resource: [
|
||||
'companyReport',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: 'JSON',
|
||||
description: 'The output format for the report',
|
||||
},
|
||||
{
|
||||
displayName: 'Put Output In Field',
|
||||
name: 'output',
|
||||
type: 'string',
|
||||
default: 'data',
|
||||
required: true,
|
||||
description: 'The name of the output field to put the binary file data in',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
resource: [
|
||||
'companyReport',
|
||||
],
|
||||
},
|
||||
hide: {
|
||||
format: [
|
||||
'JSON',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
resource: [
|
||||
'companyReport',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Duplicate Field Filtering',
|
||||
name: 'fd',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to apply the standard duplicate field filtering or not',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
|
@ -0,0 +1,65 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
apiRequest,
|
||||
} from '../../../transport';
|
||||
|
||||
export async function get(this: IExecuteFunctions, index: number) {
|
||||
const body: IDataObject = {};
|
||||
const requestMethod = 'GET';
|
||||
const items = this.getInputData();
|
||||
|
||||
//meta data
|
||||
const reportId = this.getNodeParameter('reportId', index) as string;
|
||||
const format = this.getNodeParameter('format', 0) as string;
|
||||
const fd = this.getNodeParameter('options.fd', index, true) as boolean;
|
||||
|
||||
//endpoint
|
||||
const endpoint = `reports/${reportId}/?format=${format}&fd=${fd}`;
|
||||
|
||||
if (format === 'JSON') {
|
||||
const responseData = await apiRequest.call(this, requestMethod, endpoint, body, {}, { resolveWithFullResponse: true });
|
||||
return this.helpers.returnJsonArray(responseData.body);
|
||||
}
|
||||
|
||||
const output: string = this.getNodeParameter('output', index) as string;
|
||||
|
||||
const response = await apiRequest.call(this, requestMethod, endpoint, body, {} as IDataObject,
|
||||
{ encoding: null, json: false, resolveWithFullResponse: true });
|
||||
let mimeType = response.headers['content-type'] as string | undefined;
|
||||
mimeType = mimeType ? mimeType.split(';').find(value => value.includes('/')) : undefined;
|
||||
const contentDisposition = response.headers['content-disposition'];
|
||||
const fileNameRegex = /(?<=filename=").*\b/;
|
||||
const match = fileNameRegex.exec(contentDisposition);
|
||||
let fileName = '';
|
||||
|
||||
// file name was found
|
||||
if (match !== null) {
|
||||
fileName = match[0];
|
||||
}
|
||||
|
||||
const newItem: INodeExecutionData = {
|
||||
json: items[index].json,
|
||||
binary: {},
|
||||
};
|
||||
|
||||
if (items[index].binary !== undefined) {
|
||||
// Create a shallow copy of the binary data so that the old
|
||||
// data references which do not get changed still stay behind
|
||||
// but the incoming data does not get changed.
|
||||
Object.assign(newItem.binary, items[index].binary);
|
||||
}
|
||||
|
||||
newItem.binary = {
|
||||
[output]: await this.helpers.prepareBinaryData(response.body as unknown as Buffer, fileName, mimeType),
|
||||
};
|
||||
|
||||
return this.prepareOutputData(newItem as unknown as INodeExecutionData[]);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { get as execute } from './execute';
|
||||
import { companyReportGetDescription as description } from './description';
|
||||
|
||||
export {
|
||||
description,
|
||||
execute,
|
||||
};
|
|
@ -0,0 +1,34 @@
|
|||
import * as get from './get';
|
||||
|
||||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export {
|
||||
get,
|
||||
};
|
||||
|
||||
export const descriptions: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'companyReport',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get a company report',
|
||||
},
|
||||
],
|
||||
default: 'get',
|
||||
description: '',
|
||||
},
|
||||
...get.description,
|
||||
];
|
|
@ -0,0 +1,95 @@
|
|||
import {
|
||||
EmployeeProperties,
|
||||
} from '../../Interfaces';
|
||||
|
||||
import {
|
||||
createEmployeeSharedDescription,
|
||||
} from './shareDescription';
|
||||
|
||||
export const employeeCreateDescription: EmployeeProperties = [
|
||||
{
|
||||
displayName: 'Synced with Trax Payroll',
|
||||
name: 'synced',
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
resource: [
|
||||
'employee',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether the employee to create was added to a pay schedule synced with Trax Payroll',
|
||||
},
|
||||
{
|
||||
displayName: 'First Name',
|
||||
name: 'firstName',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
resource: [
|
||||
'employee',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Name',
|
||||
name: 'lastName',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
resource: [
|
||||
'employee',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
...createEmployeeSharedDescription(true),
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
resource: [
|
||||
'employee',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
...createEmployeeSharedDescription(false),
|
||||
{
|
||||
displayName: 'Work Email',
|
||||
name: 'workEmail',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Work Phone',
|
||||
name: 'workPhone',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
|
@ -0,0 +1,99 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
apiRequest,
|
||||
} from '../../../transport';
|
||||
|
||||
import * as moment from 'moment';
|
||||
|
||||
import {
|
||||
capitalCase
|
||||
} from 'change-case';
|
||||
|
||||
export async function create(this: IExecuteFunctions, index: number): Promise<INodeExecutionData[]> {
|
||||
const body: IDataObject = {};
|
||||
const requestMethod = 'POST';
|
||||
const endpoint = 'employees';
|
||||
|
||||
//body parameters
|
||||
body.firstName = this.getNodeParameter('firstName', index) as string;
|
||||
body.lastName = this.getNodeParameter('lastName', index) as string;
|
||||
|
||||
const additionalFields = this.getNodeParameter('additionalFields', index) as IDataObject;
|
||||
const synced = this.getNodeParameter('synced', index) as boolean;
|
||||
|
||||
if (synced) {
|
||||
Object.assign(body, { address: this.getNodeParameter('address.value', index, {}) as IDataObject });
|
||||
Object.assign(body, { payRate: this.getNodeParameter('payRate.value', index, {}) as IDataObject });
|
||||
body.department = this.getNodeParameter('department', index) as string;
|
||||
body.dateOfBirth = this.getNodeParameter('dateOfBirth', index);
|
||||
body.division = this.getNodeParameter('division', index) as string;
|
||||
body.employeeNumber = this.getNodeParameter('employeeNumber', index) as string;
|
||||
body.exempt = this.getNodeParameter('exempt', index) as string;
|
||||
body.gender = this.getNodeParameter('gender', index) as string;
|
||||
body.hireDate = this.getNodeParameter('hireDate', index) as string;
|
||||
body.location = this.getNodeParameter('location', index) as string;
|
||||
body.maritalStatus = this.getNodeParameter('maritalStatus', index) as string;
|
||||
body.mobilePhone = this.getNodeParameter('mobilePhone', index) as string;
|
||||
body.paidPer = this.getNodeParameter('paidPer', index) as string;
|
||||
body.payType = this.getNodeParameter('payType', index) as string;
|
||||
body.preferredName = this.getNodeParameter('preferredName', index) as string;
|
||||
body.ssn = this.getNodeParameter('ssn', index) as string;
|
||||
} else {
|
||||
Object.assign(body, { address: this.getNodeParameter('additionalFields.address.value', index, {}) as IDataObject });
|
||||
Object.assign(body, { payRate: this.getNodeParameter('additionalFields.payRate.value', index, {}) as IDataObject });
|
||||
delete additionalFields.address;
|
||||
delete additionalFields.payRate;
|
||||
}
|
||||
|
||||
Object.assign(body, additionalFields);
|
||||
|
||||
if (body.gender) {
|
||||
body.gender = capitalCase(body.gender as string);
|
||||
}
|
||||
|
||||
if (body.dateOfBirth) {
|
||||
body.dateOfBirth = moment(body.dateOfBirth as string).format('YYYY-MM-DD');
|
||||
}
|
||||
|
||||
if (body.exempt) {
|
||||
body.exempt = capitalCase(body.exempt as string);
|
||||
}
|
||||
|
||||
if (body.hireDate) {
|
||||
body.hireDate = moment(body.hireDate as string).format('YYYY-MM-DD');
|
||||
}
|
||||
|
||||
if (body.maritalStatus) {
|
||||
body.maritalStatus = capitalCase(body.maritalStatus as string);
|
||||
}
|
||||
|
||||
if (body.payType) {
|
||||
body.payType = capitalCase(body.payType as string);
|
||||
}
|
||||
|
||||
if (body.paidPer) {
|
||||
body.paidPer = capitalCase(body.paidPer as string);
|
||||
}
|
||||
|
||||
if (!Object.keys(body.payRate as IDataObject).length) {
|
||||
delete body.payRate;
|
||||
}
|
||||
|
||||
//response
|
||||
const responseData = await apiRequest.call(this, requestMethod, endpoint, body, {}, { resolveWithFullResponse: true });
|
||||
|
||||
//obtain employeeID
|
||||
const rawEmployeeId = responseData.headers.location.lastIndexOf('/');
|
||||
const employeeId = responseData.headers.location.substring(rawEmployeeId + 1);
|
||||
|
||||
//return
|
||||
return this.helpers.returnJsonArray({ id: employeeId });
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { create as execute } from './execute';
|
||||
import { employeeCreateDescription as description } from './description';
|
||||
|
||||
export {
|
||||
description,
|
||||
execute,
|
||||
};
|
|
@ -0,0 +1,324 @@
|
|||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const createEmployeeSharedDescription = (sync = false): INodeProperties[] => {
|
||||
let elements = [
|
||||
{
|
||||
displayName: 'Address',
|
||||
name: 'address',
|
||||
placeholder: 'Address',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: false,
|
||||
},
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
name: 'value',
|
||||
displayName: 'Address',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Line 1',
|
||||
name: 'address1',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Line 2',
|
||||
name: 'address2',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'City',
|
||||
name: 'city',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'State',
|
||||
name: 'state',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'Florida',
|
||||
description: 'The full name of the state/province',
|
||||
},
|
||||
{
|
||||
displayName: 'Country',
|
||||
name: 'country',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'United States',
|
||||
description: 'The name of the country. Must exist in the BambooHR country list',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Date of Birth',
|
||||
name: 'dateOfBirth',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Department',
|
||||
name: 'department',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getDepartments',
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Division',
|
||||
name: 'division',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getDivisions',
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Employee Number',
|
||||
name: 'employeeNumber',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'FLSA Overtime Status',
|
||||
name: 'exempt',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Exempt',
|
||||
value: 'exempt',
|
||||
},
|
||||
{
|
||||
name: 'Non-exempt',
|
||||
value: 'non-exempt',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Gender',
|
||||
name: 'gender',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Female',
|
||||
value: 'female',
|
||||
},
|
||||
{
|
||||
name: 'Male',
|
||||
value: 'male',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Hire Date',
|
||||
name: 'hireDate',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Location',
|
||||
name: 'location',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getEmployeeLocations',
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Marital Status',
|
||||
name: 'maritalStatus',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Single',
|
||||
value: 'single',
|
||||
},
|
||||
{
|
||||
name: 'Married',
|
||||
value: 'married',
|
||||
},
|
||||
{
|
||||
name: 'Domestic Partnership',
|
||||
value: 'domesticPartnership',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Mobile Phone',
|
||||
name: 'mobilePhone',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Pay Per',
|
||||
name: 'paidPer',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Hour',
|
||||
value: 'hour',
|
||||
},
|
||||
{
|
||||
name: 'Day',
|
||||
value: 'day',
|
||||
},
|
||||
{
|
||||
name: 'Week',
|
||||
value: 'week',
|
||||
},
|
||||
{
|
||||
name: 'Month',
|
||||
value: 'month',
|
||||
},
|
||||
{
|
||||
name: 'Quater',
|
||||
value: 'quater',
|
||||
},
|
||||
{
|
||||
name: 'Year',
|
||||
value: 'year',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Pay Rate',
|
||||
name: 'payRate',
|
||||
placeholder: 'Add Pay Rate',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: false,
|
||||
},
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
name: 'value',
|
||||
displayName: 'Pay Rate',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: '20.00',
|
||||
},
|
||||
{
|
||||
displayName: 'Currency',
|
||||
name: 'currency',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'USD',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Pay Type',
|
||||
name: 'payType',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Hourly',
|
||||
value: 'hourly',
|
||||
},
|
||||
{
|
||||
name: 'Salary',
|
||||
value: 'salary',
|
||||
},
|
||||
{
|
||||
name: 'Commission',
|
||||
value: 'commission',
|
||||
},
|
||||
{
|
||||
name: 'Exception Hourly',
|
||||
value: 'exceptionHourly',
|
||||
},
|
||||
{
|
||||
name: 'Monthly',
|
||||
value: 'monthly',
|
||||
},
|
||||
{
|
||||
name: 'Weekly',
|
||||
value: 'weekly',
|
||||
},
|
||||
{
|
||||
name: 'Piece Rate',
|
||||
value: 'pieceRate',
|
||||
},
|
||||
{
|
||||
name: 'Contract',
|
||||
value: 'contract',
|
||||
},
|
||||
{
|
||||
name: 'Daily',
|
||||
value: 'daily',
|
||||
},
|
||||
{
|
||||
name: 'Pro Rata',
|
||||
value: 'proRata',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Preferred Name',
|
||||
name: 'preferredName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Social Security Number',
|
||||
name: 'ssn',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: '123-45-6789',
|
||||
description: 'A standard United States Social Security number, with dashes',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
if (sync === true) {
|
||||
elements = elements.map(element => {
|
||||
return Object.assign(element, {
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'employee',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
synced: [
|
||||
true,
|
||||
],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
});
|
||||
});
|
||||
return elements;
|
||||
} else {
|
||||
elements = elements.map(element => {
|
||||
return Object.assign(element, {
|
||||
displayOptions: {
|
||||
show: {
|
||||
'/synced': [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
return elements;
|
||||
};
|
|
@ -0,0 +1,54 @@
|
|||
import {
|
||||
EmployeeProperties,
|
||||
} from '../../Interfaces';
|
||||
|
||||
export const employeeGetDescription: EmployeeProperties = [
|
||||
{
|
||||
displayName: 'Employee ID',
|
||||
name: 'employeeId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
resource: [
|
||||
'employee',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
resource: [
|
||||
'employee',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Fields',
|
||||
name: 'fields',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getEmployeeFields',
|
||||
},
|
||||
default: [
|
||||
'all',
|
||||
],
|
||||
description: 'Set of fields to get from employee data',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
|
@ -0,0 +1,37 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
apiRequest,
|
||||
} from '../../../transport';
|
||||
|
||||
export async function get(this: IExecuteFunctions, index: number): Promise<INodeExecutionData[]> {
|
||||
const body: IDataObject = {};
|
||||
const requestMethod = 'GET';
|
||||
|
||||
//meta data
|
||||
const id = this.getNodeParameter('employeeId', index) as string;
|
||||
|
||||
//query parameters
|
||||
let fields = this.getNodeParameter('options.fields', index, ['all']) as string[];
|
||||
|
||||
if (fields.includes('all')) {
|
||||
const { fields: allFields } = await apiRequest.call(this, requestMethod, 'employees/directory', body);
|
||||
fields = allFields.map((field: IDataObject) => field.id);
|
||||
}
|
||||
|
||||
//endpoint
|
||||
const endpoint = `employees/${id}?fields=${fields}`;
|
||||
|
||||
//response
|
||||
const responseData = await apiRequest.call(this, requestMethod, endpoint, body);
|
||||
|
||||
//return
|
||||
return this.helpers.returnJsonArray(responseData);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { get as execute } from './execute';
|
||||
import { employeeGetDescription as description } from './description';
|
||||
|
||||
export {
|
||||
description,
|
||||
execute,
|
||||
};
|
|
@ -0,0 +1,45 @@
|
|||
import { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const employeeGetAllDescription: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'employee',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Whether to return all results',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 5,
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 1000,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'employee',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The number of results to return',
|
||||
},
|
||||
];
|
|
@ -0,0 +1,33 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
apiRequest,
|
||||
} from '../../../transport';
|
||||
|
||||
export async function getAll(this: IExecuteFunctions, index: number): Promise<INodeExecutionData[]> {
|
||||
const body: IDataObject = {};
|
||||
const requestMethod = 'GET';
|
||||
const endpoint = 'employees/directory';
|
||||
|
||||
//limit parameters
|
||||
const returnAll = this.getNodeParameter('returnAll', 0, false) as boolean;
|
||||
const limit = this.getNodeParameter('limit', 0, 0) as number;
|
||||
|
||||
//response
|
||||
const responseData = await apiRequest.call(this, requestMethod, endpoint, body);
|
||||
|
||||
//return limited result
|
||||
if (!returnAll && responseData.employees.length > limit) {
|
||||
return this.helpers.returnJsonArray(responseData.employees.slice(0, limit));
|
||||
}
|
||||
|
||||
//return all result
|
||||
return this.helpers.returnJsonArray(responseData.employees);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { getAll as execute } from './execute';
|
||||
import { employeeGetAllDescription as description } from './description';
|
||||
|
||||
export {
|
||||
description,
|
||||
execute,
|
||||
};
|
|
@ -0,0 +1,58 @@
|
|||
import * as create from './create';
|
||||
import * as get from './get';
|
||||
import * as getAll from './getAll';
|
||||
import * as update from './update';
|
||||
|
||||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export {
|
||||
create,
|
||||
get,
|
||||
getAll,
|
||||
update
|
||||
};
|
||||
|
||||
export const descriptions: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'employee',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create an employee',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get an employee',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
description: 'Get all employees',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update an employee',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
description: '',
|
||||
},
|
||||
...create.description,
|
||||
...get.description,
|
||||
...getAll.description,
|
||||
...update.description,
|
||||
];
|
|
@ -0,0 +1,75 @@
|
|||
import {
|
||||
EmployeeProperties,
|
||||
} from '../../Interfaces';
|
||||
import { updateEmployeeSharedDescription } from './sharedDescription';
|
||||
|
||||
export const employeeUpdateDescription: EmployeeProperties = [
|
||||
{
|
||||
displayName: 'Employee ID',
|
||||
name: 'employeeId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'employee',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Synced with Trax Payroll',
|
||||
name: 'synced',
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'employee',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether the employee to create was added to a pay schedule synced with Trax Payroll',
|
||||
},
|
||||
...updateEmployeeSharedDescription(true),
|
||||
{
|
||||
displayName: 'Update Fields',
|
||||
name: 'updateFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'employee',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
...updateEmployeeSharedDescription(false),
|
||||
{
|
||||
displayName: 'Work Email',
|
||||
name: 'workEmail',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Work Phone',
|
||||
name: 'workPhone',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
|
@ -0,0 +1,107 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
apiRequest,
|
||||
} from '../../../transport';
|
||||
|
||||
import * as moment from 'moment';
|
||||
|
||||
import {
|
||||
capitalCase,
|
||||
} from 'change-case';
|
||||
|
||||
export async function update(this: IExecuteFunctions, index: number): Promise<INodeExecutionData[]> {
|
||||
let body: IDataObject = {};
|
||||
const requestMethod = 'POST';
|
||||
|
||||
//meta data
|
||||
const id = this.getNodeParameter('employeeId', index) as string;
|
||||
|
||||
//endpoint
|
||||
const endpoint = `employees/${id}`;
|
||||
|
||||
//body parameters
|
||||
body = this.getNodeParameter('updateFields', index) as IDataObject;
|
||||
|
||||
const updateFields = this.getNodeParameter('updateFields', index) as IDataObject;
|
||||
const synced = this.getNodeParameter('synced', index) as boolean;
|
||||
|
||||
if (synced) {
|
||||
Object.assign(body, { address: this.getNodeParameter('address.value', index, {}) as IDataObject });
|
||||
Object.assign(body, { payRate: this.getNodeParameter('payRate.value', index, {}) as IDataObject });
|
||||
body.firstName = this.getNodeParameter('firstName', index) as string;
|
||||
body.lastName = this.getNodeParameter('lastName', index) as string;
|
||||
body.department = this.getNodeParameter('department', index) as string;
|
||||
body.dateOfBirth = this.getNodeParameter('dateOfBirth', index) as string;
|
||||
body.division = this.getNodeParameter('division', index) as string;
|
||||
body.employeeNumber = this.getNodeParameter('employeeNumber', index) as string;
|
||||
body.exempt = this.getNodeParameter('exempt', index) as string;
|
||||
body.gender = this.getNodeParameter('gender', index) as string;
|
||||
body.hireDate = this.getNodeParameter('hireDate', index) as string;
|
||||
body.location = this.getNodeParameter('location', index) as string;
|
||||
body.maritalStatus = this.getNodeParameter('maritalStatus', index) as string;
|
||||
body.mobilePhone = this.getNodeParameter('mobilePhone', index) as string;
|
||||
body.paidPer = this.getNodeParameter('paidPer', index) as string;
|
||||
body.payType = this.getNodeParameter('payType', index) as string;
|
||||
body.preferredName = this.getNodeParameter('preferredName', index) as string;
|
||||
body.ssn = this.getNodeParameter('ssn', index) as string;
|
||||
} else {
|
||||
|
||||
if (!Object.keys(updateFields).length) {
|
||||
throw new NodeOperationError(this.getNode(), 'At least one fields must be updated');
|
||||
}
|
||||
|
||||
Object.assign(body, { address: this.getNodeParameter('updateFields.address.value', index, {}) as IDataObject });
|
||||
Object.assign(body, { payRate: this.getNodeParameter('updateFields.payRate.value', index, {}) as IDataObject });
|
||||
delete updateFields.address;
|
||||
delete updateFields.payRate;
|
||||
}
|
||||
|
||||
Object.assign(body, updateFields);
|
||||
|
||||
if (body.gender) {
|
||||
body.gender = capitalCase(body.gender as string);
|
||||
}
|
||||
|
||||
if (body.dateOfBirth) {
|
||||
body.dateOfBirth = moment(body.dateOfBirth as string).format('YYYY-MM-DD');
|
||||
}
|
||||
|
||||
if (body.exempt) {
|
||||
body.exempt = capitalCase(body.exempt as string);
|
||||
}
|
||||
|
||||
if (body.hireDate) {
|
||||
body.hireDate = moment(body.hireDate as string).format('YYYY-MM-DD');
|
||||
}
|
||||
|
||||
if (body.maritalStatus) {
|
||||
body.maritalStatus = capitalCase(body.maritalStatus as string);
|
||||
}
|
||||
|
||||
if (body.payType) {
|
||||
body.payType = capitalCase(body.payType as string);
|
||||
}
|
||||
|
||||
if (body.paidPer) {
|
||||
body.paidPer = capitalCase(body.paidPer as string);
|
||||
}
|
||||
|
||||
if (!Object.keys(body.payRate as IDataObject).length) {
|
||||
delete body.payRate;
|
||||
}
|
||||
|
||||
await apiRequest.call(this, requestMethod, endpoint, body);
|
||||
|
||||
//return
|
||||
return this.helpers.returnJsonArray({ success: true });
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import { update as execute } from './execute';
|
||||
import { employeeUpdateDescription as description } from './description';
|
||||
|
||||
export {
|
||||
description,
|
||||
execute,
|
||||
};
|
|
@ -0,0 +1,350 @@
|
|||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const updateEmployeeSharedDescription = (sync = false): INodeProperties[] => {
|
||||
let elements = [
|
||||
{
|
||||
displayName: 'Address',
|
||||
name: 'addasasress',
|
||||
placeholder: 'Address',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: false,
|
||||
},
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
name: 'value',
|
||||
displayName: 'Address',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Line 1',
|
||||
name: 'address1',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Line 2',
|
||||
name: 'address2',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'City',
|
||||
name: 'city',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'State',
|
||||
name: 'state',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'Florida',
|
||||
description: 'The full name of the state/province',
|
||||
},
|
||||
{
|
||||
displayName: 'Country',
|
||||
name: 'country',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'United States',
|
||||
description: 'The name of the country. Must exist in the BambooHR country list',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Date of Birth',
|
||||
name: 'dateOfBirth',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Department',
|
||||
name: 'department',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getDepartments',
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Division',
|
||||
name: 'division',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getDivisions',
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Employee Number',
|
||||
name: 'employeeNumber',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'First Name',
|
||||
name: 'firstName',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
'synced': [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Name',
|
||||
name: 'lastName',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
'synced': [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'FLSA Overtime Status',
|
||||
name: 'exempt',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Exempt',
|
||||
value: 'exempt',
|
||||
},
|
||||
{
|
||||
name: 'Non-exempt',
|
||||
value: 'non-exempt',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Gender',
|
||||
name: 'gender',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Female',
|
||||
value: 'female',
|
||||
},
|
||||
{
|
||||
name: 'Male',
|
||||
value: 'male',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Hire Date',
|
||||
name: 'hireDate',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Location',
|
||||
name: 'location',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getEmployeeLocations',
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Marital Status',
|
||||
name: 'maritalStatus',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Single',
|
||||
value: 'single',
|
||||
},
|
||||
{
|
||||
name: 'Married',
|
||||
value: 'married',
|
||||
},
|
||||
{
|
||||
name: 'Domestic Partnership',
|
||||
value: 'domesticPartnership',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Mobile Phone',
|
||||
name: 'mobilePhone',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Pay Per',
|
||||
name: 'paidPer',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Hour',
|
||||
value: 'hour',
|
||||
},
|
||||
{
|
||||
name: 'Day',
|
||||
value: 'day',
|
||||
},
|
||||
{
|
||||
name: 'Week',
|
||||
value: 'week',
|
||||
},
|
||||
{
|
||||
name: 'Month',
|
||||
value: 'month',
|
||||
},
|
||||
{
|
||||
name: 'Quater',
|
||||
value: 'quater',
|
||||
},
|
||||
{
|
||||
name: 'Year',
|
||||
value: 'year',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Pay Rate',
|
||||
name: 'payRate',
|
||||
placeholder: 'Add Pay Rate',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: false,
|
||||
},
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
name: 'value',
|
||||
displayName: 'Pay Rate',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: '20.00',
|
||||
},
|
||||
{
|
||||
displayName: 'Currency',
|
||||
name: 'currency',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'USD',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Pay Type',
|
||||
name: 'payType',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Hourly',
|
||||
value: 'hourly',
|
||||
},
|
||||
{
|
||||
name: 'Salary',
|
||||
value: 'salary',
|
||||
},
|
||||
{
|
||||
name: 'Commission',
|
||||
value: 'commission',
|
||||
},
|
||||
{
|
||||
name: 'Exception Hourly',
|
||||
value: 'exceptionHourly',
|
||||
},
|
||||
{
|
||||
name: 'Monthly',
|
||||
value: 'monthly',
|
||||
},
|
||||
{
|
||||
name: 'Weekly',
|
||||
value: 'weekly',
|
||||
},
|
||||
{
|
||||
name: 'Piece Rate',
|
||||
value: 'pieceRate',
|
||||
},
|
||||
{
|
||||
name: 'Contract',
|
||||
value: 'contract',
|
||||
},
|
||||
{
|
||||
name: 'Daily',
|
||||
value: 'daily',
|
||||
},
|
||||
{
|
||||
name: 'Pro Rata',
|
||||
value: 'proRata',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Preferred Name',
|
||||
name: 'preferredName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Social Security Number',
|
||||
name: 'ssn',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: '123-45-6789',
|
||||
description: 'A standard United States Social Security number, with dashes',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
if (sync === true) {
|
||||
elements = elements.map(element => {
|
||||
return Object.assign(element, {
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'employee',
|
||||
],
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
synced: [
|
||||
true,
|
||||
],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
});
|
||||
});
|
||||
return elements;
|
||||
} else {
|
||||
elements = elements.map(element => {
|
||||
return Object.assign(element, {
|
||||
displayOptions: {
|
||||
show: {
|
||||
'/synced': [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
return elements;
|
||||
};
|
|
@ -0,0 +1,42 @@
|
|||
import {
|
||||
EmployeeDocumentProperties,
|
||||
} from '../../Interfaces';
|
||||
|
||||
export const employeeDocumentDelDescription: EmployeeDocumentProperties = [
|
||||
{
|
||||
displayName: 'Employee ID',
|
||||
name: 'employeeId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'delete',
|
||||
],
|
||||
resource: [
|
||||
'employeeDocument',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'ID of the employee',
|
||||
},
|
||||
{
|
||||
displayName: 'File ID',
|
||||
name: 'fileId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'delete',
|
||||
],
|
||||
resource: [
|
||||
'employeeDocument',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'ID of the employee file',
|
||||
},
|
||||
];
|
|
@ -0,0 +1,30 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
apiRequest,
|
||||
} from '../../../transport';
|
||||
|
||||
export async function del(this: IExecuteFunctions, index: number): Promise<INodeExecutionData[]> {
|
||||
const body: IDataObject = {};
|
||||
const requestMethod = 'DELETE';
|
||||
|
||||
//meta data
|
||||
const id: string = this.getNodeParameter('employeeId', index) as string;
|
||||
const fileId: string = this.getNodeParameter('fileId', index) as string;
|
||||
|
||||
//endpoint
|
||||
const endpoint = `employees/${id}/files/${fileId}`;
|
||||
|
||||
//response
|
||||
await apiRequest.call(this, requestMethod, endpoint, body);
|
||||
|
||||
//return
|
||||
return this.helpers.returnJsonArray({ success: true });
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { del as execute } from './execute';
|
||||
import { employeeDocumentDelDescription as description } from './description';
|
||||
|
||||
export {
|
||||
description,
|
||||
execute,
|
||||
};
|
|
@ -0,0 +1,60 @@
|
|||
import {
|
||||
EmployeeDocumentProperties,
|
||||
} from '../../Interfaces';
|
||||
|
||||
export const employeeDocumentDownloadDescription: EmployeeDocumentProperties = [
|
||||
{
|
||||
displayName: 'Employee ID',
|
||||
name: 'employeeId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'download',
|
||||
],
|
||||
resource: [
|
||||
'employeeDocument',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'ID of the employee',
|
||||
},
|
||||
{
|
||||
displayName: 'File ID',
|
||||
name: 'fileId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'download',
|
||||
],
|
||||
resource: [
|
||||
'employeeDocument',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'ID of the employee file',
|
||||
},
|
||||
{
|
||||
displayName: 'Put Output In Field',
|
||||
name: 'output',
|
||||
type: 'string',
|
||||
default: 'data',
|
||||
required: true,
|
||||
description: 'The name of the output field to put the binary file data in',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'download',
|
||||
],
|
||||
resource: [
|
||||
'employeeDocument',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
|
@ -0,0 +1,59 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
apiRequest,
|
||||
} from '../../../transport';
|
||||
|
||||
export async function download(this: IExecuteFunctions, index: number) {
|
||||
const body: IDataObject = {};
|
||||
const requestMethod = 'GET';
|
||||
const items = this.getInputData();
|
||||
|
||||
//meta data
|
||||
const id: string = this.getNodeParameter('employeeId', index) as string;
|
||||
const fileId: string = this.getNodeParameter('fileId', index) as string;
|
||||
const output: string = this.getNodeParameter('output', index) as string;
|
||||
|
||||
//endpoint
|
||||
const endpoint = `employees/${id}/files/${fileId}/`;
|
||||
|
||||
//response
|
||||
const response = await apiRequest.call(this, requestMethod, endpoint, body, {} as IDataObject,
|
||||
{ encoding: null, json: false, resolveWithFullResponse: true });
|
||||
let mimeType = response.headers['content-type'] as string | undefined;
|
||||
mimeType = mimeType ? mimeType.split(';').find(value => value.includes('/')) : undefined;
|
||||
const contentDisposition = response.headers['content-disposition'];
|
||||
const fileNameRegex = /(?<=filename=").*\b/;
|
||||
const match = fileNameRegex.exec(contentDisposition);
|
||||
let fileName = '';
|
||||
|
||||
// file name was found
|
||||
if (match !== null) {
|
||||
fileName = match[0];
|
||||
}
|
||||
|
||||
const newItem: INodeExecutionData = {
|
||||
json: items[index].json,
|
||||
binary: {},
|
||||
};
|
||||
|
||||
if (items[index].binary !== undefined) {
|
||||
// Create a shallow copy of the binary data so that the old
|
||||
// data references which do not get changed still stay behind
|
||||
// but the incoming data does not get changed.
|
||||
Object.assign(newItem.binary, items[index].binary);
|
||||
}
|
||||
|
||||
newItem.binary = {
|
||||
[output]: await this.helpers.prepareBinaryData(response.body as unknown as Buffer, fileName, mimeType),
|
||||
};
|
||||
|
||||
return this.prepareOutputData(newItem as unknown as INodeExecutionData[]);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { download as execute } from './execute';
|
||||
import { employeeDocumentDownloadDescription as description } from './description';
|
||||
|
||||
export {
|
||||
description,
|
||||
execute,
|
||||
};
|
|
@ -0,0 +1,81 @@
|
|||
import {
|
||||
EmployeeDocumentProperties,
|
||||
} from '../../Interfaces';
|
||||
|
||||
export const employeeDocumentGetAllDescription: EmployeeDocumentProperties = [
|
||||
{
|
||||
displayName: 'Employee ID',
|
||||
name: 'employeeId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
resource: [
|
||||
'employeeDocument',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
resource: [
|
||||
'employeeDocument',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Whether to return all results',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 5,
|
||||
description: 'The number of results to return',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 1000,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
resource: [
|
||||
'employeeDocument',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Simplify Output',
|
||||
name: 'simplifyOutput',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
resource: [
|
||||
'employeeDocument',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Whether to simplify the output or not',
|
||||
},
|
||||
];
|
|
@ -0,0 +1,58 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
apiRequest,
|
||||
} from '../../../transport';
|
||||
|
||||
export async function getAll(this: IExecuteFunctions, index: number): Promise<INodeExecutionData[]> {
|
||||
const body: IDataObject = {};
|
||||
const requestMethod = 'GET';
|
||||
|
||||
//meta data
|
||||
const id = this.getNodeParameter('employeeId', index) as string;
|
||||
|
||||
//limit parameters
|
||||
const simplifyOutput: boolean = this.getNodeParameter('simplifyOutput', index) as boolean;
|
||||
const returnAll: boolean = this.getNodeParameter('returnAll', 0, false) as boolean;
|
||||
const limit: number = this.getNodeParameter('limit', 0, 0) as number;
|
||||
|
||||
//endpoint
|
||||
const endpoint = `employees/${id}/files/view/`;
|
||||
|
||||
//response
|
||||
const responseData = await apiRequest.call(this, requestMethod, endpoint, body);
|
||||
|
||||
const onlyFilesArray = [];
|
||||
|
||||
//return only files without categories
|
||||
if (simplifyOutput) {
|
||||
for (let i = 0; i < responseData.categories.length; i++) {
|
||||
if (responseData.categories[i].hasOwnProperty('files')) {
|
||||
for (let j = 0; j < responseData.categories[i].files.length; j++) {
|
||||
onlyFilesArray.push(responseData.categories[i].files[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!returnAll && onlyFilesArray.length > limit) {
|
||||
return this.helpers.returnJsonArray(onlyFilesArray.slice(0, limit));
|
||||
} else {
|
||||
return this.helpers.returnJsonArray(onlyFilesArray);
|
||||
}
|
||||
}
|
||||
|
||||
//return limited result
|
||||
if (!returnAll && responseData.categories.length > limit) {
|
||||
return this.helpers.returnJsonArray(responseData.categories.slice(0, limit));
|
||||
}
|
||||
|
||||
//return
|
||||
return this.helpers.returnJsonArray(responseData.categories);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { getAll as execute } from './execute';
|
||||
import { employeeDocumentGetAllDescription as description } from './description';
|
||||
|
||||
export {
|
||||
description,
|
||||
execute,
|
||||
};
|
|
@ -0,0 +1,65 @@
|
|||
import * as del from './del';
|
||||
import * as download from './download';
|
||||
import * as getAll from './getAll';
|
||||
import * as update from './update';
|
||||
import * as upload from './upload';
|
||||
|
||||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export {
|
||||
del,
|
||||
download,
|
||||
getAll,
|
||||
update,
|
||||
upload,
|
||||
};
|
||||
|
||||
export const descriptions: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'employeeDocument',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete an employee document',
|
||||
},
|
||||
{
|
||||
name: 'Download',
|
||||
value: 'download',
|
||||
description: 'Download an employee document',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
description: 'Get all employee document',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update an employee document',
|
||||
},
|
||||
{
|
||||
name: 'Upload',
|
||||
value: 'upload',
|
||||
description: 'Upload an employee document',
|
||||
},
|
||||
],
|
||||
default: 'delete',
|
||||
},
|
||||
...del.description,
|
||||
...download.description,
|
||||
...getAll.description,
|
||||
...update.description,
|
||||
...upload.description,
|
||||
];
|
|
@ -0,0 +1,86 @@
|
|||
import {
|
||||
EmployeeDocumentProperties,
|
||||
} from '../../Interfaces';
|
||||
|
||||
export const employeeDocumentUpdateDescription: EmployeeDocumentProperties = [
|
||||
{
|
||||
displayName: 'Employee ID',
|
||||
name: 'employeeId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'employeeDocument',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'File ID',
|
||||
name: 'fileId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'employeeDocument',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Update Fields',
|
||||
name: 'updateFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'employeeDocument',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Employee Document Category Name/ID',
|
||||
name: 'categoryId',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getEmployeeDocumentCategories',
|
||||
loadOptionsDependsOn: [
|
||||
'employeeId',
|
||||
],
|
||||
},
|
||||
default: '',
|
||||
description: 'ID of the new category of the file',
|
||||
},
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'New name of the file',
|
||||
},
|
||||
{
|
||||
displayName: 'Share with Employee',
|
||||
name: 'shareWithEmployee',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether this file is shared or not',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
|
@ -0,0 +1,34 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
apiRequest,
|
||||
} from '../../../transport';
|
||||
|
||||
export async function update(this: IExecuteFunctions, index: number): Promise<INodeExecutionData[]> {
|
||||
let body: IDataObject = {};
|
||||
const requestMethod = 'POST';
|
||||
|
||||
//meta data
|
||||
const id = this.getNodeParameter('employeeId', index) as string;
|
||||
const fileId = this.getNodeParameter('fileId', index) as string;
|
||||
|
||||
//endpoint
|
||||
const endpoint = `employees/${id}/files/${fileId}`;
|
||||
|
||||
//body parameters
|
||||
body = this.getNodeParameter('updateFields', index) as IDataObject;
|
||||
body.shareWithEmployee ? body.shareWithEmployee = 'yes' : body.shareWithEmployee = 'no';
|
||||
|
||||
//response
|
||||
await apiRequest.call(this, requestMethod, endpoint, body);
|
||||
|
||||
//return
|
||||
return this.helpers.returnJsonArray({ success: true });
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { update as execute } from './execute';
|
||||
import { employeeDocumentUpdateDescription as description } from './description';
|
||||
|
||||
export {
|
||||
description,
|
||||
execute,
|
||||
};
|
|
@ -0,0 +1,85 @@
|
|||
import {
|
||||
EmployeeDocumentProperties,
|
||||
} from '../../Interfaces';
|
||||
|
||||
export const employeeDocumentUploadDescription: EmployeeDocumentProperties = [
|
||||
{
|
||||
displayName: 'Employee ID',
|
||||
name: 'employeeId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'upload',
|
||||
],
|
||||
resource: [
|
||||
'employeeDocument',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'ID of the employee',
|
||||
},
|
||||
{
|
||||
displayName: 'Employee Document Category ID',
|
||||
name: 'categoryId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'upload',
|
||||
],
|
||||
resource: [
|
||||
'employeeDocument',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Input Data Field Name',
|
||||
name: 'binaryPropertyName',
|
||||
type: 'string',
|
||||
default: 'data',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'upload',
|
||||
],
|
||||
resource: [
|
||||
'employeeDocument',
|
||||
],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The name of the input field containing the binary file data to be uploaded. Supported file types: PNG, JPEG',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'upload',
|
||||
],
|
||||
resource: [
|
||||
'employeeDocument',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Share with Employee',
|
||||
name: 'share',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether this file is shared or not',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
|
@ -0,0 +1,66 @@
|
|||
import {
|
||||
BINARY_ENCODING,
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IBinaryData,
|
||||
IBinaryKeyData,
|
||||
IDataObject, NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
apiRequest,
|
||||
} from '../../../transport';
|
||||
|
||||
export async function upload(this: IExecuteFunctions, index: number) {
|
||||
let body: IDataObject = {};
|
||||
const requestMethod = 'POST';
|
||||
|
||||
const items = this.getInputData();
|
||||
|
||||
const category = this.getNodeParameter('categoryId', index) as string;
|
||||
const options = this.getNodeParameter('options', index) as IDataObject;
|
||||
|
||||
if (items[index].binary === undefined) {
|
||||
throw new NodeOperationError(this.getNode(), 'No binary data exists on item!');
|
||||
}
|
||||
|
||||
const propertyNameUpload = this.getNodeParameter('binaryPropertyName', index) as string;
|
||||
|
||||
if (items[index]!.binary![propertyNameUpload] === undefined) {
|
||||
throw new NodeOperationError(this.getNode(), `No binary data property "${propertyNameUpload}" does not exists on item!`);
|
||||
}
|
||||
|
||||
const item = items[index].binary as IBinaryKeyData;
|
||||
|
||||
const binaryData = item[propertyNameUpload] as IBinaryData;
|
||||
|
||||
const binaryDataBuffer = await this.helpers.getBinaryDataBuffer(index, propertyNameUpload);
|
||||
|
||||
const id: string = this.getNodeParameter('employeeId', index) as string;
|
||||
|
||||
body = {
|
||||
json: false,
|
||||
formData: {
|
||||
file: {
|
||||
value: binaryDataBuffer,
|
||||
options: {
|
||||
filename: binaryData.fileName,
|
||||
contentType: binaryData.mimeType,
|
||||
},
|
||||
},
|
||||
fileName: binaryData.fileName,
|
||||
category,
|
||||
},
|
||||
resolveWithFullResponse: true,
|
||||
};
|
||||
|
||||
if (options.hasOwnProperty('share')) {
|
||||
Object.assign(body.formData, (options.share) ? { share: 'yes' } : { share: 'no' });
|
||||
}
|
||||
//endpoint
|
||||
const endpoint = `employees/${id}/files`;
|
||||
const { headers } = await apiRequest.call(this, requestMethod, endpoint, {}, {}, body);
|
||||
return this.helpers.returnJsonArray({ fileId: headers.location.split('/').pop() });
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { upload as execute } from './execute';
|
||||
import { employeeDocumentUploadDescription as description } from './description';
|
||||
|
||||
export {
|
||||
description,
|
||||
execute,
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
import {
|
||||
FileProperties,
|
||||
} from '../../Interfaces';
|
||||
|
||||
export const fileDelDescription: FileProperties = [
|
||||
{
|
||||
displayName: 'File ID',
|
||||
name: 'fileId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'delete',
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'ID of the file',
|
||||
},
|
||||
];
|
|
@ -0,0 +1,29 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
apiRequest,
|
||||
} from '../../../transport';
|
||||
|
||||
export async function del(this: IExecuteFunctions, index: number): Promise<INodeExecutionData[]> {
|
||||
const body: IDataObject = {};
|
||||
const requestMethod = 'DELETE';
|
||||
|
||||
//meta data
|
||||
const fileId: string = this.getNodeParameter('fileId', index) as string;
|
||||
|
||||
//endpoint
|
||||
const endpoint = `files/${fileId}`;
|
||||
|
||||
//response
|
||||
await apiRequest.call(this, requestMethod, endpoint, body);
|
||||
|
||||
//return
|
||||
return this.helpers.returnJsonArray({ success: true });
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { del as execute } from './execute';
|
||||
import { fileDelDescription as description } from './description';
|
||||
|
||||
export {
|
||||
description,
|
||||
execute,
|
||||
};
|
|
@ -0,0 +1,42 @@
|
|||
import {
|
||||
FileProperties,
|
||||
} from '../../Interfaces';
|
||||
|
||||
export const fileDownloadDescription: FileProperties = [
|
||||
{
|
||||
displayName: 'File ID',
|
||||
name: 'fileId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'download',
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'ID of the file',
|
||||
},
|
||||
{
|
||||
displayName: 'Put Output In Field',
|
||||
name: 'output',
|
||||
type: 'string',
|
||||
default: 'data',
|
||||
required: true,
|
||||
description: 'The name of the output field to put the binary file data in',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'download',
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
|
@ -0,0 +1,59 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
apiRequest,
|
||||
} from '../../../transport';
|
||||
|
||||
export async function download(this: IExecuteFunctions, index: number) {
|
||||
const body: IDataObject = {};
|
||||
const requestMethod = 'GET';
|
||||
const items = this.getInputData();
|
||||
|
||||
//meta data
|
||||
const fileId: string = this.getNodeParameter('fileId', index) as string;
|
||||
const output: string = this.getNodeParameter('output', index) as string;
|
||||
|
||||
//endpoint
|
||||
const endpoint = `files/${fileId}/`;
|
||||
|
||||
//response
|
||||
const response = await apiRequest.call(this, requestMethod, endpoint, body, {} as IDataObject,
|
||||
{ encoding: null, json: false, resolveWithFullResponse: true });
|
||||
|
||||
let mimeType = response.headers['content-type'] as string | undefined;
|
||||
mimeType = mimeType ? mimeType.split(';').find(value => value.includes('/')) : undefined;
|
||||
const contentDisposition = response.headers['content-disposition'];
|
||||
const fileNameRegex = /(?<=filename=").*\b/;
|
||||
const match = fileNameRegex.exec(contentDisposition);
|
||||
let fileName = '';
|
||||
|
||||
// file name was found
|
||||
if (match !== null) {
|
||||
fileName = match[0];
|
||||
}
|
||||
|
||||
const newItem: INodeExecutionData = {
|
||||
json: items[index].json,
|
||||
binary: {},
|
||||
};
|
||||
|
||||
if (items[index].binary !== undefined) {
|
||||
// Create a shallow copy of the binary data so that the old
|
||||
// data references which do not get changed still stay behind
|
||||
// but the incoming data does not get changed.
|
||||
Object.assign(newItem.binary, items[index].binary);
|
||||
}
|
||||
|
||||
newItem.binary = {
|
||||
[output]: await this.helpers.prepareBinaryData(response.body as unknown as Buffer, fileName, mimeType),
|
||||
};
|
||||
|
||||
return this.prepareOutputData(newItem as unknown as INodeExecutionData[]);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { download as execute } from './execute';
|
||||
import { fileDownloadDescription as description } from './description';
|
||||
|
||||
export {
|
||||
description,
|
||||
execute,
|
||||
};
|
|
@ -0,0 +1,64 @@
|
|||
import {
|
||||
FileProperties,
|
||||
} from '../../Interfaces';
|
||||
|
||||
export const fileGetAllDescription: FileProperties = [
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Whether to return all results',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 5,
|
||||
description: 'The number of results to return',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 1000,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Simplify Output',
|
||||
name: 'simplifyOutput',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Whether to simplify the output or not',
|
||||
},
|
||||
];
|
|
@ -0,0 +1,53 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
apiRequest,
|
||||
} from '../../../transport';
|
||||
|
||||
export async function getAll(this: IExecuteFunctions, index: number): Promise<INodeExecutionData[]> {
|
||||
const body: IDataObject = {};
|
||||
const requestMethod = 'GET';
|
||||
const endpoint = 'files/view';
|
||||
|
||||
//limit parameters
|
||||
const simplifyOutput: boolean = this.getNodeParameter('simplifyOutput', index) as boolean;
|
||||
const returnAll: boolean = this.getNodeParameter('returnAll', 0, false) as boolean;
|
||||
const limit: number = this.getNodeParameter('limit', 0, 0) as number;
|
||||
|
||||
//response
|
||||
const responseData = await apiRequest.call(this, requestMethod, endpoint, body);
|
||||
|
||||
const onlyFilesArray = [];
|
||||
|
||||
//return only files without categories
|
||||
if (simplifyOutput) {
|
||||
for (let i = 0; i < responseData.categories.length; i++) {
|
||||
if (responseData.categories[i].hasOwnProperty('files')) {
|
||||
for (let j = 0; j < responseData.categories[i].files.length; j++) {
|
||||
onlyFilesArray.push(responseData.categories[i].files[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!returnAll && onlyFilesArray.length > limit) {
|
||||
return this.helpers.returnJsonArray(onlyFilesArray.slice(0, limit));
|
||||
} else {
|
||||
return this.helpers.returnJsonArray(onlyFilesArray);
|
||||
}
|
||||
}
|
||||
|
||||
//return limited result
|
||||
if (!returnAll && responseData.categories.length > limit) {
|
||||
return this.helpers.returnJsonArray(responseData.categories.slice(0, limit));
|
||||
}
|
||||
|
||||
//return
|
||||
return this.helpers.returnJsonArray(responseData.categories);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { getAll as execute } from './execute';
|
||||
import { fileGetAllDescription as description } from './description';
|
||||
|
||||
export {
|
||||
description,
|
||||
execute,
|
||||
};
|
66
packages/nodes-base/nodes/BambooHR/v1/actions/file/index.ts
Normal file
66
packages/nodes-base/nodes/BambooHR/v1/actions/file/index.ts
Normal file
|
@ -0,0 +1,66 @@
|
|||
import * as del from './del';
|
||||
import * as download from './download';
|
||||
import * as getAll from './getAll';
|
||||
import * as update from './update';
|
||||
import * as upload from './upload';
|
||||
|
||||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export {
|
||||
del,
|
||||
download,
|
||||
getAll,
|
||||
update,
|
||||
upload,
|
||||
};
|
||||
|
||||
export const descriptions: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete a company file',
|
||||
},
|
||||
{
|
||||
name: 'Download',
|
||||
value: 'download',
|
||||
description: 'Download a company file',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
description: 'Get all company files',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update a company file',
|
||||
},
|
||||
{
|
||||
name: 'Upload',
|
||||
value: 'upload',
|
||||
description: 'Upload a company file',
|
||||
},
|
||||
],
|
||||
default: 'delete',
|
||||
description: '',
|
||||
},
|
||||
...del.description,
|
||||
...download.description,
|
||||
...getAll.description,
|
||||
...update.description,
|
||||
...upload.description,
|
||||
];
|
|
@ -0,0 +1,67 @@
|
|||
import {
|
||||
FileProperties,
|
||||
} from '../../Interfaces';
|
||||
|
||||
export const fileUpdateDescription: FileProperties = [
|
||||
{
|
||||
displayName: 'File ID',
|
||||
name: 'fileId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'ID of the file',
|
||||
},
|
||||
{
|
||||
displayName: 'Update Fields',
|
||||
name: 'updateFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Category Name/ID',
|
||||
name: 'categoryId',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getCompanyFileCategories',
|
||||
},
|
||||
default: '',
|
||||
description: 'Move the file to a different category',
|
||||
},
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'New name of the file',
|
||||
},
|
||||
{
|
||||
displayName: 'Share with Employee',
|
||||
name: 'shareWithEmployee',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether this file is shared or not',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
|
@ -0,0 +1,34 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
apiRequest,
|
||||
} from '../../../transport';
|
||||
|
||||
export async function update(this: IExecuteFunctions, index: number): Promise<INodeExecutionData[]> {
|
||||
const body: IDataObject = {};
|
||||
const requestMethod = 'POST';
|
||||
|
||||
//meta data
|
||||
const fileId: string = this.getNodeParameter('fileId', index) as string;
|
||||
|
||||
//endpoint
|
||||
const endpoint = `files/${fileId}`;
|
||||
|
||||
//body parameters
|
||||
const shareWithEmployee = this.getNodeParameter('updateFields.shareWithEmployee', index, true) as boolean;
|
||||
|
||||
body.shareWithEmployee = shareWithEmployee ? 'yes' : 'no';
|
||||
|
||||
//response
|
||||
await apiRequest.call(this, requestMethod, endpoint, body);
|
||||
|
||||
//return
|
||||
return this.helpers.returnJsonArray({ success: true });
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { update as execute } from './execute';
|
||||
import { fileUpdateDescription as description } from './description';
|
||||
|
||||
export {
|
||||
description,
|
||||
execute,
|
||||
};
|
|
@ -0,0 +1,68 @@
|
|||
import { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const fileUploadDescription: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Input Data Field Name',
|
||||
name: 'binaryPropertyName',
|
||||
type: 'string',
|
||||
default: 'data',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'upload',
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The name of the input field containing the binary file data to be uploaded. Supported file types: PNG, JPEG',
|
||||
},
|
||||
{
|
||||
displayName: 'Category Name/ID',
|
||||
name: 'categoryId',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getCompanyFileCategories',
|
||||
},
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'upload',
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'upload',
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Share with Employee',
|
||||
name: 'share',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether this file is shared or not',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
|
@ -0,0 +1,63 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IBinaryData,
|
||||
IBinaryKeyData,
|
||||
IDataObject,
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
apiRequest,
|
||||
} from '../../../transport';
|
||||
|
||||
export async function upload(this: IExecuteFunctions, index: number) {
|
||||
let body: IDataObject = {};
|
||||
const requestMethod = 'POST';
|
||||
|
||||
const items = this.getInputData();
|
||||
|
||||
const category = this.getNodeParameter('categoryId', index) as string;
|
||||
const share = this.getNodeParameter('options.share', index, true) as boolean;
|
||||
|
||||
if (items[index].binary === undefined) {
|
||||
throw new NodeOperationError(this.getNode(), 'No binary data exists on item!');
|
||||
}
|
||||
|
||||
const propertyNameUpload = this.getNodeParameter('binaryPropertyName', index) as string;
|
||||
|
||||
if (items[index]!.binary![propertyNameUpload] === undefined) {
|
||||
throw new NodeOperationError(this.getNode(), `No binary data property "${propertyNameUpload}" does not exists on item!`);
|
||||
}
|
||||
|
||||
const item = items[index].binary as IBinaryKeyData;
|
||||
|
||||
const binaryData = item[propertyNameUpload] as IBinaryData;
|
||||
|
||||
const binaryDataBuffer = await this.helpers.getBinaryDataBuffer(index, propertyNameUpload);
|
||||
|
||||
body = {
|
||||
json: false,
|
||||
formData: {
|
||||
file: {
|
||||
value: binaryDataBuffer,
|
||||
options: {
|
||||
filename: binaryData.fileName,
|
||||
contentType: binaryData.mimeType,
|
||||
},
|
||||
},
|
||||
fileName: binaryData.fileName,
|
||||
category,
|
||||
},
|
||||
resolveWithFullResponse: true,
|
||||
};
|
||||
|
||||
Object.assign(body.formData, (share) ? { share: 'yes' } : { share: 'no' });
|
||||
|
||||
//endpoint
|
||||
const endpoint = `files`;
|
||||
const { headers } = await apiRequest.call(this, requestMethod, endpoint, {}, {}, body);
|
||||
return this.helpers.returnJsonArray({ fileId: headers.location.split('/').pop() });
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { upload as execute } from './execute';
|
||||
import { fileUploadDescription as description } from './description';
|
||||
|
||||
export {
|
||||
description,
|
||||
execute,
|
||||
};
|
57
packages/nodes-base/nodes/BambooHR/v1/actions/router.ts
Normal file
57
packages/nodes-base/nodes/BambooHR/v1/actions/router.ts
Normal file
|
@ -0,0 +1,57 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
INodeExecutionData,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import * as employee from './employee';
|
||||
import * as employeeDocument from './employeeDocument';
|
||||
import * as file from './file';
|
||||
import * as companyReport from './companyReport';
|
||||
|
||||
import { BambooHR } from './Interfaces';
|
||||
|
||||
export async function router(this: IExecuteFunctions): Promise<INodeExecutionData[]> {
|
||||
const items = this.getInputData();
|
||||
const operationResult: INodeExecutionData[] = [];
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const resource = this.getNodeParameter<BambooHR>('resource', i);
|
||||
const operation = this.getNodeParameter('operation', i);
|
||||
|
||||
const bamboohr = {
|
||||
resource,
|
||||
operation,
|
||||
} as BambooHR;
|
||||
|
||||
if (bamboohr.operation === 'delete') {
|
||||
//@ts-ignore
|
||||
bamboohr.operation = 'del';
|
||||
}
|
||||
|
||||
try {
|
||||
if (bamboohr.resource === 'employee') {
|
||||
operationResult.push(...await employee[bamboohr.operation].execute.call(this, i));
|
||||
} else if (bamboohr.resource === 'employeeDocument') {
|
||||
//@ts-ignore
|
||||
operationResult.push(...await employeeDocument[bamboohr.operation].execute.call(this, i));
|
||||
} else if (bamboohr.resource === 'file') {
|
||||
//@ts-ignore
|
||||
operationResult.push(...await file[bamboohr.operation].execute.call(this, i));
|
||||
} else if (bamboohr.resource === 'companyReport') {
|
||||
//@ts-ignore
|
||||
operationResult.push(...await companyReport[bamboohr.operation].execute.call(this, i));
|
||||
}
|
||||
} catch (err) {
|
||||
if (this.continueOnFail()) {
|
||||
operationResult.push({ json: this.getInputData(i)[0].json, error: err });
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return operationResult;
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
import {
|
||||
INodeTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import * as file from './file';
|
||||
import * as employee from './employee';
|
||||
import * as employeeDocument from './employeeDocument';
|
||||
import * as companyReport from './companyReport';
|
||||
|
||||
export const versionDescription: INodeTypeDescription = {
|
||||
credentials: [
|
||||
{
|
||||
name: 'bambooHRApi',
|
||||
required: true,
|
||||
testedBy: 'bambooHrApiCredentialTest',
|
||||
},
|
||||
],
|
||||
defaults: {
|
||||
name: 'BambooHR',
|
||||
},
|
||||
description: 'Consume BambooHR API',
|
||||
displayName: 'BambooHR',
|
||||
group: ['transform'],
|
||||
icon: 'file:bambooHR.png',
|
||||
inputs: ['main'],
|
||||
name: 'bambooHR',
|
||||
outputs: ['main'],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Company Report',
|
||||
value: 'companyReport',
|
||||
},
|
||||
{
|
||||
name: 'Employee',
|
||||
value: 'employee',
|
||||
},
|
||||
{
|
||||
name: 'Employee Document',
|
||||
value: 'employeeDocument',
|
||||
},
|
||||
{
|
||||
name: 'File',
|
||||
value: 'file',
|
||||
},
|
||||
],
|
||||
default: 'employee',
|
||||
},
|
||||
...employee.descriptions,
|
||||
...employeeDocument.descriptions,
|
||||
...file.descriptions,
|
||||
...companyReport.descriptions,
|
||||
],
|
||||
subtitle: '={{$parameter["resource"] + ": " + $parameter["operation"]}}',
|
||||
version: 1,
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
import {
|
||||
ICredentialDataDecryptedObject,
|
||||
ICredentialsDecrypted,
|
||||
ICredentialTestFunctions,
|
||||
IHttpRequestOptions,
|
||||
NodeCredentialTestResult,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export async function bambooHrApiCredentialTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise<NodeCredentialTestResult> {
|
||||
try {
|
||||
await validateCredentials.call(this, credential.data as ICredentialDataDecryptedObject);
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 'Error',
|
||||
message: 'The API Key included in the request is invalid',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: 'OK',
|
||||
message: 'Connection successful!',
|
||||
} as NodeCredentialTestResult;
|
||||
}
|
||||
|
||||
async function validateCredentials(this: ICredentialTestFunctions, decryptedCredentials: ICredentialDataDecryptedObject): Promise<any> { // tslint:disable-line:no-any
|
||||
const credentials = decryptedCredentials;
|
||||
|
||||
const {
|
||||
subdomain,
|
||||
apiKey,
|
||||
} = credentials as {
|
||||
subdomain: string,
|
||||
apiKey: string,
|
||||
};
|
||||
|
||||
const options: IHttpRequestOptions = {
|
||||
method: 'GET',
|
||||
auth: {
|
||||
username: apiKey as string,
|
||||
password: 'x',
|
||||
},
|
||||
url: `https://api.bamboohr.com/api/gateway.php/${subdomain}/v1/employees/directory`,
|
||||
};
|
||||
|
||||
return await this.helpers.request(options);
|
||||
}
|
2
packages/nodes-base/nodes/BambooHR/v1/methods/index.ts
Normal file
2
packages/nodes-base/nodes/BambooHR/v1/methods/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export * as loadOptions from './loadOptions';
|
||||
export * as credentialTest from './credentialTest';
|
176
packages/nodes-base/nodes/BambooHR/v1/methods/loadOptions.ts
Normal file
176
packages/nodes-base/nodes/BambooHR/v1/methods/loadOptions.ts
Normal file
|
@ -0,0 +1,176 @@
|
|||
import {
|
||||
IDataObject,
|
||||
ILoadOptionsFunctions,
|
||||
INodePropertyOptions,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
apiRequest,
|
||||
} from '../transport';
|
||||
|
||||
// Get all the available channels
|
||||
export async function getTimeOffTypeID(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const body: IDataObject = {};
|
||||
const requestMethod = 'GET';
|
||||
const endPoint = 'meta/time_off/types';
|
||||
|
||||
const response = await apiRequest.call(this, requestMethod, endPoint, body);
|
||||
const timeOffTypeIds = response.body.timeOffTypes;
|
||||
|
||||
for (const item of timeOffTypeIds) {
|
||||
returnData.push({
|
||||
name: item.name,
|
||||
value: item.id,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
}
|
||||
|
||||
export async function getCompanyFileCategories(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const body: IDataObject = {};
|
||||
const requestMethod = 'GET';
|
||||
const endPoint = 'files/view/';
|
||||
|
||||
const response = await apiRequest.call(this, requestMethod, endPoint, body);
|
||||
const categories = response.categories;
|
||||
|
||||
for (const category of categories) {
|
||||
returnData.push({
|
||||
name: category.name,
|
||||
value: category.id,
|
||||
});
|
||||
}
|
||||
|
||||
returnData.sort(sort);
|
||||
|
||||
return returnData;
|
||||
}
|
||||
|
||||
export async function getEmployeeDocumentCategories(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const body: IDataObject = {};
|
||||
const requestMethod = 'GET';
|
||||
const id = this.getCurrentNodeParameter('employeeId') as string;
|
||||
|
||||
const endPoint = `employees/${id}/files/view/`;
|
||||
|
||||
const response = await apiRequest.call(this, requestMethod, endPoint, body);
|
||||
const categories = response.categories;
|
||||
|
||||
for (const category of categories) {
|
||||
returnData.push({
|
||||
name: category.name,
|
||||
value: category.id,
|
||||
});
|
||||
}
|
||||
|
||||
returnData.sort(sort);
|
||||
|
||||
return returnData;
|
||||
}
|
||||
|
||||
export async function getEmployeeLocations(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const body: IDataObject = {};
|
||||
const requestMethod = 'GET';
|
||||
const endPoint = 'meta/lists/';
|
||||
|
||||
//do not request all data?
|
||||
const fields = await apiRequest.call(this, requestMethod, endPoint, body, {}) as [{ fieldId: number, options: [{ id: number, name: string }] }];
|
||||
|
||||
const options = fields.filter(field => field.fieldId === 18)[0].options;
|
||||
|
||||
for (const option of options) {
|
||||
returnData.push({
|
||||
name: option.name,
|
||||
value: option.id,
|
||||
});
|
||||
}
|
||||
|
||||
returnData.sort(sort);
|
||||
|
||||
return returnData;
|
||||
}
|
||||
|
||||
export async function getDepartments(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const body: IDataObject = {};
|
||||
const requestMethod = 'GET';
|
||||
const endPoint = 'meta/lists/';
|
||||
|
||||
//do not request all data?
|
||||
const fields = await apiRequest.call(this, requestMethod, endPoint, body, {}) as [{ fieldId: number, options: [{ id: number, name: string }] }];
|
||||
|
||||
const options = fields.filter(field => field.fieldId === 4)[0].options;
|
||||
|
||||
for (const option of options) {
|
||||
returnData.push({
|
||||
name: option.name,
|
||||
value: option.id,
|
||||
});
|
||||
}
|
||||
|
||||
returnData.sort(sort);
|
||||
|
||||
return returnData;
|
||||
}
|
||||
|
||||
export async function getDivisions(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const body: IDataObject = {};
|
||||
const requestMethod = 'GET';
|
||||
const endPoint = 'meta/lists/';
|
||||
|
||||
//do not request all data?
|
||||
const fields = await apiRequest.call(this, requestMethod, endPoint, body, {}) as [{ fieldId: number, options: [{ id: number, name: string }] }];
|
||||
|
||||
const options = fields.filter(field => field.fieldId === 1355)[0].options;
|
||||
|
||||
for (const option of options) {
|
||||
returnData.push({
|
||||
name: option.name,
|
||||
value: option.id,
|
||||
});
|
||||
}
|
||||
|
||||
returnData.sort(sort);
|
||||
|
||||
return returnData;
|
||||
}
|
||||
|
||||
export async function getEmployeeFields(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const body: IDataObject = {};
|
||||
const requestMethod = 'GET';
|
||||
const endPoint = 'employees/directory';
|
||||
|
||||
const { fields } = await apiRequest.call(this, requestMethod, endPoint, body);
|
||||
|
||||
for (const field of fields) {
|
||||
returnData.push({
|
||||
name: field.name || field.id,
|
||||
value: field.id,
|
||||
});
|
||||
}
|
||||
|
||||
returnData.sort(sort);
|
||||
|
||||
returnData.unshift({
|
||||
name: '[All]',
|
||||
value: 'all',
|
||||
});
|
||||
|
||||
return returnData;
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
const sort = (a, b) => {
|
||||
if (a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase()) { return -1; }
|
||||
if (a.name.toLocaleLowerCase() > b.name.toLocaleLowerCase()) { return 1; }
|
||||
return 0;
|
||||
};
|
||||
|
||||
|
||||
|
73
packages/nodes-base/nodes/BambooHR/v1/transport/index.ts
Normal file
73
packages/nodes-base/nodes/BambooHR/v1/transport/index.ts
Normal file
|
@ -0,0 +1,73 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
IHookFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
NodeApiError,
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
OptionsWithUrl,
|
||||
} from 'request';
|
||||
|
||||
/**
|
||||
* Make an API request to Mattermost
|
||||
*/
|
||||
export async function apiRequest(
|
||||
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions,
|
||||
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
|
||||
endpoint: string,
|
||||
body: string[] | IDataObject = {},
|
||||
query: IDataObject = {},
|
||||
option: IDataObject = {},
|
||||
) {
|
||||
const credentials = await this.getCredentials('bambooHRApi');
|
||||
|
||||
if (!credentials) {
|
||||
throw new NodeOperationError(this.getNode(), 'No credentials returned!');
|
||||
}
|
||||
|
||||
//set-up credentials
|
||||
const apiKey = credentials.apiKey;
|
||||
const subdomain = credentials.subdomain;
|
||||
|
||||
//set-up uri
|
||||
const uri = `https://api.bamboohr.com/api/gateway.php/${subdomain}/v1/${endpoint}`;
|
||||
|
||||
const options: OptionsWithUrl = {
|
||||
method,
|
||||
body,
|
||||
qs: query,
|
||||
url: uri,
|
||||
auth: {
|
||||
username: apiKey as string,
|
||||
password: 'x',
|
||||
},
|
||||
json: true,
|
||||
};
|
||||
|
||||
if (Object.keys(option).length) {
|
||||
Object.assign(options, option);
|
||||
}
|
||||
|
||||
if (!Object.keys(body).length) {
|
||||
delete options.body;
|
||||
}
|
||||
|
||||
if (!Object.keys(query).length) {
|
||||
delete options.qs;
|
||||
}
|
||||
|
||||
try {
|
||||
//@ts-ignore
|
||||
return await this.helpers.request(options);
|
||||
} catch (error) {
|
||||
const description = error?.response?.headers['x-bamboohr-error-messsage'] || '';
|
||||
const message = error?.message || '';
|
||||
throw new NodeApiError(this.getNode(), error, { message, description });
|
||||
}
|
||||
}
|
|
@ -44,6 +44,7 @@
|
|||
"dist/credentials/AutomizyApi.credentials.js",
|
||||
"dist/credentials/AutopilotApi.credentials.js",
|
||||
"dist/credentials/Aws.credentials.js",
|
||||
"dist/credentials/BambooHRApi.credentials.js",
|
||||
"dist/credentials/BannerbearApi.credentials.js",
|
||||
"dist/credentials/BaserowApi.credentials.js",
|
||||
"dist/credentials/BeeminderApi.credentials.js",
|
||||
|
@ -347,6 +348,7 @@
|
|||
"dist/nodes/Aws/SQS/AwsSqs.node.js",
|
||||
"dist/nodes/Aws/Textract/AwsTextract.node.js",
|
||||
"dist/nodes/Aws/Transcribe/AwsTranscribe.node.js",
|
||||
"dist/nodes/BambooHR/BambooHR.node.js",
|
||||
"dist/nodes/Bannerbear/Bannerbear.node.js",
|
||||
"dist/nodes/Baserow/Baserow.node.js",
|
||||
"dist/nodes/Beeminder/Beeminder.node.js",
|
||||
|
|
Loading…
Reference in a new issue