mirror of
https://github.com/formbricks/formbricks.git
synced 2026-01-04 09:29:42 -06:00
Add Pre-Commit Hook for linting & formatting (#767)
* add pre-commit hook for linting & formatting * add husky * update n8n package json, update package-lock file
This commit is contained in:
4
.husky/pre-commit
Executable file
4
.husky/pre-commit
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
pnpm lint-staged
|
||||
22
package.json
22
package.json
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "turbo",
|
||||
"name": "formbricks",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
@@ -19,19 +19,35 @@
|
||||
"db:push": "turbo run db:push",
|
||||
"go": "turbo run go --concurrency 16",
|
||||
"dev": "turbo run dev --parallel",
|
||||
"pre-commit": "lint-staged",
|
||||
"start": "turbo run start --parallel",
|
||||
"format": "prettier --write \"**/*.{ts,tsx,md}\"",
|
||||
"generate": "turbo run generate",
|
||||
"lint": "turbo run lint",
|
||||
"release": "turbo run build --filter=js... && turbo run build --filter=n8n... && changeset publish",
|
||||
"test": "turbo run test"
|
||||
"release": "turbo run build --filter=js... && turbo run build --filter=n8n-node... && changeset publish",
|
||||
"test": "turbo run test",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@changesets/cli": "^2.26.2",
|
||||
"husky": "^8.0.3",
|
||||
"lint-staged": "^14.0.1",
|
||||
"rimraf": "^5.0.1",
|
||||
"tsx": "^3.12.7",
|
||||
"turbo": "latest"
|
||||
},
|
||||
"lint-staged": {
|
||||
"(apps|packages)/**/*.{js,ts,jsx,tsx}": [
|
||||
"prettier --write",
|
||||
"eslint --fix"
|
||||
],
|
||||
"*.json": [
|
||||
"prettier --write"
|
||||
],
|
||||
"packages/database/schema.prisma": [
|
||||
"prisma format"
|
||||
]
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
},
|
||||
|
||||
53
packages/n8n-node/.eslintrc.js
Normal file
53
packages/n8n-node/.eslintrc.js
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @type {import('@types/eslint').ESLint.ConfigData}
|
||||
*/
|
||||
module.exports = {
|
||||
root: true,
|
||||
|
||||
env: {
|
||||
browser: true,
|
||||
es6: true,
|
||||
node: true,
|
||||
},
|
||||
|
||||
parser: "@typescript-eslint/parser",
|
||||
|
||||
parserOptions: {
|
||||
project: ["./tsconfig.json"],
|
||||
sourceType: "module",
|
||||
extraFileExtensions: [".json"],
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
|
||||
ignorePatterns: [".eslintrc.js", "**/*.js", "**/node_modules/**", "**/dist/**"],
|
||||
|
||||
overrides: [
|
||||
{
|
||||
files: ["package.json"],
|
||||
plugins: ["eslint-plugin-n8n-nodes-base"],
|
||||
extends: ["plugin:n8n-nodes-base/community"],
|
||||
rules: {
|
||||
"n8n-nodes-base/community-package-json-name-still-default": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["./credentials/**/*.ts"],
|
||||
plugins: ["eslint-plugin-n8n-nodes-base"],
|
||||
extends: ["plugin:n8n-nodes-base/credentials"],
|
||||
rules: {
|
||||
"n8n-nodes-base/cred-class-field-documentation-url-missing": "off",
|
||||
"n8n-nodes-base/cred-class-field-documentation-url-miscased": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["./nodes/**/*.ts"],
|
||||
plugins: ["eslint-plugin-n8n-nodes-base"],
|
||||
extends: ["plugin:n8n-nodes-base/nodes"],
|
||||
rules: {
|
||||
"n8n-nodes-base/node-execute-block-missing-continue-on-fail": "off",
|
||||
"n8n-nodes-base/node-resource-description-filename-against-convention": "off",
|
||||
"n8n-nodes-base/node-param-fixed-collection-type-unsorted-items": "off",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
16
packages/n8n-node/.eslintrc.prepublish.js
Normal file
16
packages/n8n-node/.eslintrc.prepublish.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @type {import('@types/eslint').ESLint.ConfigData}
|
||||
*/
|
||||
module.exports = {
|
||||
extends: "./.eslintrc.js",
|
||||
|
||||
overrides: [
|
||||
{
|
||||
files: ["package.json"],
|
||||
plugins: ["eslint-plugin-n8n-nodes-base"],
|
||||
rules: {
|
||||
"n8n-nodes-base/community-package-json-name-still-default": "error",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
40
packages/n8n-node/credentials/FormbricksApi.credentials.ts
Normal file
40
packages/n8n-node/credentials/FormbricksApi.credentials.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from "n8n-workflow";
|
||||
|
||||
export class FormbricksApi implements ICredentialType {
|
||||
name = "formbricksApi";
|
||||
displayName = "Formbricks API";
|
||||
properties: INodeProperties[] = [
|
||||
{
|
||||
displayName: "Host",
|
||||
name: "host",
|
||||
description:
|
||||
'The address of your Formbricks instance. For Formbricks Cloud this is "https://app.formbricks.com". If you are hosting Formbricks yourself, it\'s the address where you can reach your instance.',
|
||||
type: "string",
|
||||
default: "https://app.formbricks.com",
|
||||
},
|
||||
{
|
||||
displayName: "API Key",
|
||||
name: "apiKey",
|
||||
description:
|
||||
'Your Formbricks API-Key. You can create a new API-Key in the Product Settings. Please read our <a href="https://formbricks.com/docs/api/api-key-setup">API Key Docs</a> for more details.',
|
||||
type: "string",
|
||||
typeOptions: { password: true },
|
||||
default: "",
|
||||
},
|
||||
];
|
||||
|
||||
authenticate: IAuthenticateGeneric = {
|
||||
type: "generic",
|
||||
properties: {
|
||||
headers: {
|
||||
"x-Api-Key": "={{$credentials.apiKey}}",
|
||||
},
|
||||
},
|
||||
};
|
||||
test: ICredentialTestRequest | undefined = {
|
||||
request: {
|
||||
baseURL: "={{$credentials.host}}/api/v1",
|
||||
url: "=/me",
|
||||
},
|
||||
};
|
||||
}
|
||||
16
packages/n8n-node/gulpfile.js
Normal file
16
packages/n8n-node/gulpfile.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const path = require("path");
|
||||
const { task, src, dest } = require("gulp");
|
||||
|
||||
task("build:icons", copyIcons);
|
||||
|
||||
function copyIcons() {
|
||||
const nodeSource = path.resolve("nodes", "**", "*.{png,svg}");
|
||||
const nodeDestination = path.resolve("dist", "nodes");
|
||||
|
||||
src(nodeSource).pipe(dest(nodeDestination));
|
||||
|
||||
const credSource = path.resolve("credentials", "**", "*.{png,svg}");
|
||||
const credDestination = path.resolve("dist", "credentials");
|
||||
|
||||
return src(credSource).pipe(dest(credDestination));
|
||||
}
|
||||
18
packages/n8n-node/nodes/Formbricks/Formbricks.node.json
Normal file
18
packages/n8n-node/nodes/Formbricks/Formbricks.node.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"node": "n8n-nodes-base.Formbricks",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": ["Communication"],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/credentials/formbricks"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.formbricks/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
155
packages/n8n-node/nodes/Formbricks/Formbricks.node.ts
Normal file
155
packages/n8n-node/nodes/Formbricks/Formbricks.node.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
import { INodeType, INodeTypeDescription, IWebhookFunctions, IWebhookResponseData } from "n8n-workflow";
|
||||
|
||||
import { apiRequest, getSurveys } from "./GenericFunctions";
|
||||
import { IHookFunctions } from "n8n-core";
|
||||
|
||||
export class Formbricks implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: "Formbricks",
|
||||
name: "formbricks",
|
||||
icon: "file:formbricks.svg",
|
||||
group: ["trigger"],
|
||||
version: 1,
|
||||
subtitle: '=Surveys: {{$parameter["surveyIds"]}}',
|
||||
description: "Open Source Surveys & Experience Management Solution",
|
||||
defaults: {
|
||||
name: "Formbricks",
|
||||
},
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
|
||||
inputs: [],
|
||||
outputs: ["main"],
|
||||
credentials: [
|
||||
{
|
||||
name: "formbricksApi",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
webhooks: [
|
||||
{
|
||||
name: "default",
|
||||
httpMethod: "POST",
|
||||
responseMode: "onReceived",
|
||||
path: "webhook",
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: "Events",
|
||||
name: "events",
|
||||
type: "multiOptions",
|
||||
options: [
|
||||
{
|
||||
name: "Response Created",
|
||||
value: "responseCreated",
|
||||
description:
|
||||
"Triggers when a new response is created for a survey. Normally triggered after the first question was answered.",
|
||||
},
|
||||
{
|
||||
name: "Response Updated",
|
||||
value: "responseUpdated",
|
||||
description: "Triggers when a response is updated within a survey",
|
||||
},
|
||||
{
|
||||
name: "Response Finished",
|
||||
value: "responseFinished",
|
||||
description: "Triggers when a response is marked as finished",
|
||||
},
|
||||
],
|
||||
default: [],
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-multi-options
|
||||
displayName: "Survey",
|
||||
name: "surveyIds",
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-description-wrong-for-dynamic-multi-options
|
||||
description:
|
||||
'Survey which should trigger workflow. Only trigger this node for a specific survey within the environment. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>.',
|
||||
type: "multiOptions",
|
||||
typeOptions: {
|
||||
loadOptionsMethod: "getSurveys",
|
||||
},
|
||||
options: [],
|
||||
default: [],
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
methods = {
|
||||
loadOptions: {
|
||||
getSurveys,
|
||||
},
|
||||
};
|
||||
|
||||
webhookMethods = {
|
||||
default: {
|
||||
async checkExists(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookData = this.getWorkflowStaticData("node");
|
||||
const webhookUrl = this.getNodeWebhookUrl("default");
|
||||
const surveyIds = this.getNodeParameter("surveyIds") as Array<string>;
|
||||
|
||||
const endpoint = "/webhooks";
|
||||
|
||||
try {
|
||||
const response = await apiRequest.call(this, "GET", endpoint, {});
|
||||
for (const webhook of response.data) {
|
||||
for (const surveyId of webhook.surveyIds) {
|
||||
if (surveyIds.includes(surveyId) && webhook.url === webhookUrl) {
|
||||
webhookData.webhookId = webhook.id;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
async create(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookData = this.getWorkflowStaticData("node");
|
||||
const webhookUrl = this.getNodeWebhookUrl("default");
|
||||
const surveyIds = this.getNodeParameter("surveyIds") as Array<string>;
|
||||
const events = this.getNodeParameter("events");
|
||||
|
||||
const body = {
|
||||
url: webhookUrl,
|
||||
triggers: events,
|
||||
surveyIds: surveyIds,
|
||||
};
|
||||
const endpoint = "/webhooks";
|
||||
|
||||
try {
|
||||
const response = await apiRequest.call(this, "POST", endpoint, body);
|
||||
webhookData.webhookId = response.data.id;
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
async delete(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookData = this.getWorkflowStaticData("node");
|
||||
if (webhookData.webhookId !== undefined) {
|
||||
const endpoint = `/webhooks/${webhookData.webhookId}`;
|
||||
|
||||
try {
|
||||
await apiRequest.call(this, "DELETE", endpoint, {});
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
// Remove from the static workflow data so that it is clear
|
||||
// that no webhooks are registered anymore
|
||||
delete webhookData.webhookId;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
|
||||
const bodyData = this.getBodyData();
|
||||
// getting bodyData as string, so need to JSON parse it to convert to an object
|
||||
return {
|
||||
workflowData: [this.helpers.returnJsonArray(JSON.parse(bodyData as any))],
|
||||
};
|
||||
}
|
||||
}
|
||||
69
packages/n8n-node/nodes/Formbricks/GenericFunctions.ts
Normal file
69
packages/n8n-node/nodes/Formbricks/GenericFunctions.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { IHookFunctions, ILoadOptionsFunctions } from "n8n-core";
|
||||
import {
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
IHttpRequestMethods,
|
||||
IHttpRequestOptions,
|
||||
INodePropertyOptions,
|
||||
JsonObject,
|
||||
NodeApiError,
|
||||
NodeOperationError,
|
||||
} from "n8n-workflow";
|
||||
|
||||
/**
|
||||
* Make an API request to Formbricks
|
||||
*/
|
||||
export async function apiRequest(
|
||||
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions,
|
||||
method: IHttpRequestMethods,
|
||||
resource: string,
|
||||
body: object,
|
||||
query: IDataObject = {},
|
||||
option: IDataObject = {}
|
||||
): Promise<any> {
|
||||
const credentials = await this.getCredentials("formbricksApi");
|
||||
|
||||
let options: IHttpRequestOptions = {
|
||||
baseURL: `${credentials.host}/api/v1`,
|
||||
method,
|
||||
body,
|
||||
qs: query,
|
||||
url: resource,
|
||||
headers: {
|
||||
"x-Api-Key": credentials.apiKey,
|
||||
},
|
||||
};
|
||||
|
||||
if (!Object.keys(query).length) {
|
||||
delete options.qs;
|
||||
}
|
||||
|
||||
options = Object.assign({}, options, option);
|
||||
try {
|
||||
return await this.helpers.httpRequestWithAuthentication.call(this, "formbricksApi", options);
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the available surveys
|
||||
*/
|
||||
export async function getSurveys(this: ILoadOptionsFunctions): Promise<any> {
|
||||
const endpoint = "/surveys";
|
||||
const responseData = await apiRequest.call(this, "GET", endpoint, {});
|
||||
|
||||
if (!responseData.data) {
|
||||
throw new NodeOperationError(this.getNode(), "No data got returned");
|
||||
}
|
||||
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
for (const data of responseData.data) {
|
||||
returnData.push({
|
||||
name: data.name,
|
||||
value: data.id,
|
||||
});
|
||||
}
|
||||
|
||||
return returnData;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
@@ -24,11 +24,12 @@
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "tsc && gulp build:icons",
|
||||
"clean": "rimraf .turbo node_modules dist",
|
||||
"dev": "tsc --watch",
|
||||
"format": "prettier nodes credentials --write",
|
||||
"lint": "eslint nodes credentials package.json",
|
||||
"lintfix": "eslint nodes credentials package.json --fix",
|
||||
"prepublishOnly": "npm run build && npm run lint -c .eslintrc.prepublish.js nodes credentials package.json"
|
||||
"prepublishOnly": "pnpm run build && pnpm run lint -c .eslintrc.prepublish.js nodes credentials package.json"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
@@ -43,13 +44,11 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/request-promise-native": "~1.0.15",
|
||||
"@typescript-eslint/parser": "~5.45",
|
||||
"eslint-plugin-n8n-nodes-base": "^1.11.0",
|
||||
"gulp": "^4.0.2",
|
||||
"n8n-core": "*",
|
||||
"n8n-workflow": "*",
|
||||
"typescript": "~4.8.4"
|
||||
"n8n-core": "^1.1.1",
|
||||
"n8n-workflow": "^1.1.1"
|
||||
}
|
||||
}
|
||||
25
packages/n8n-node/tsconfig.json
Normal file
25
packages/n8n-node/tsconfig.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"target": "es2019",
|
||||
"lib": ["es2019", "es2020", "es2022.error"],
|
||||
"removeComments": true,
|
||||
"useUnknownInCatchVariables": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noUnusedLocals": true,
|
||||
"strictNullChecks": true,
|
||||
"preserveConstEnums": true,
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"incremental": true,
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "./dist/"
|
||||
},
|
||||
"include": ["credentials/**/*", "nodes/**/*", "nodes/**/*.json", "package.json"]
|
||||
}
|
||||
83
packages/n8n-node/tslint.json
Normal file
83
packages/n8n-node/tslint.json
Normal file
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"linterOptions": {
|
||||
"exclude": ["node_modules/**/*"]
|
||||
},
|
||||
"defaultSeverity": "error",
|
||||
"jsRules": {},
|
||||
"rules": {
|
||||
"array-type": [true, "array-simple"],
|
||||
"arrow-return-shorthand": true,
|
||||
"ban": [
|
||||
true,
|
||||
{
|
||||
"name": "Array",
|
||||
"message": "tsstyle#array-constructor"
|
||||
}
|
||||
],
|
||||
"ban-types": [
|
||||
true,
|
||||
["Object", "Use {} instead."],
|
||||
["String", "Use 'string' instead."],
|
||||
["Number", "Use 'number' instead."],
|
||||
["Boolean", "Use 'boolean' instead."]
|
||||
],
|
||||
"class-name": true,
|
||||
"curly": [true, "ignore-same-line"],
|
||||
"forin": true,
|
||||
"jsdoc-format": true,
|
||||
"label-position": true,
|
||||
"indent": [true, "tabs", 2],
|
||||
"member-access": [true, "no-public"],
|
||||
"new-parens": true,
|
||||
"no-angle-bracket-type-assertion": true,
|
||||
"no-any": true,
|
||||
"no-arg": true,
|
||||
"no-conditional-assignment": true,
|
||||
"no-construct": true,
|
||||
"no-debugger": true,
|
||||
"no-default-export": true,
|
||||
"no-duplicate-variable": true,
|
||||
"no-inferrable-types": true,
|
||||
"ordered-imports": [
|
||||
true,
|
||||
{
|
||||
"import-sources-order": "any",
|
||||
"named-imports-order": "case-insensitive"
|
||||
}
|
||||
],
|
||||
"no-namespace": [true, "allow-declarations"],
|
||||
"no-reference": true,
|
||||
"no-string-throw": true,
|
||||
"no-unused-expression": true,
|
||||
"no-var-keyword": true,
|
||||
"object-literal-shorthand": true,
|
||||
"only-arrow-functions": [true, "allow-declarations", "allow-named-functions"],
|
||||
"prefer-const": true,
|
||||
"radix": true,
|
||||
"semicolon": [true, "always", "ignore-bound-class-methods"],
|
||||
"switch-default": true,
|
||||
"trailing-comma": [
|
||||
true,
|
||||
{
|
||||
"multiline": {
|
||||
"objects": "always",
|
||||
"arrays": "always",
|
||||
"functions": "always",
|
||||
"typeLiterals": "ignore"
|
||||
},
|
||||
"esSpecCompliant": true
|
||||
}
|
||||
],
|
||||
"triple-equals": [true, "allow-null-check"],
|
||||
"use-isnan": true,
|
||||
"quotes": ["error", "single"],
|
||||
"variable-name": [
|
||||
true,
|
||||
"check-format",
|
||||
"ban-keywords",
|
||||
"allow-leading-underscore",
|
||||
"allow-trailing-underscore"
|
||||
]
|
||||
},
|
||||
"rulesDirectory": []
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = tab
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[package.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
@@ -1,53 +0,0 @@
|
||||
/**
|
||||
* @type {import('@types/eslint').ESLint.ConfigData}
|
||||
*/
|
||||
module.exports = {
|
||||
root: true,
|
||||
|
||||
env: {
|
||||
browser: true,
|
||||
es6: true,
|
||||
node: true,
|
||||
},
|
||||
|
||||
parser: "@typescript-eslint/parser",
|
||||
|
||||
parserOptions: {
|
||||
project: ["./tsconfig.json"],
|
||||
sourceType: "module",
|
||||
extraFileExtensions: [".json"],
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
|
||||
ignorePatterns: [".eslintrc.js", "**/*.js", "**/node_modules/**", "**/dist/**"],
|
||||
|
||||
overrides: [
|
||||
{
|
||||
files: ["package.json"],
|
||||
plugins: ["eslint-plugin-n8n-nodes-base"],
|
||||
extends: ["plugin:n8n-nodes-base/community"],
|
||||
rules: {
|
||||
"n8n-nodes-base/community-package-json-name-still-default": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["./credentials/**/*.ts"],
|
||||
plugins: ["eslint-plugin-n8n-nodes-base"],
|
||||
extends: ["plugin:n8n-nodes-base/credentials"],
|
||||
rules: {
|
||||
"n8n-nodes-base/cred-class-field-documentation-url-missing": "off",
|
||||
"n8n-nodes-base/cred-class-field-documentation-url-miscased": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["./nodes/**/*.ts"],
|
||||
plugins: ["eslint-plugin-n8n-nodes-base"],
|
||||
extends: ["plugin:n8n-nodes-base/nodes"],
|
||||
rules: {
|
||||
"n8n-nodes-base/node-execute-block-missing-continue-on-fail": "off",
|
||||
"n8n-nodes-base/node-resource-description-filename-against-convention": "off",
|
||||
"n8n-nodes-base/node-param-fixed-collection-type-unsorted-items": "off",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
/**
|
||||
* @type {import('@types/eslint').ESLint.ConfigData}
|
||||
*/
|
||||
module.exports = {
|
||||
extends: "./.eslintrc.js",
|
||||
|
||||
overrides: [
|
||||
{
|
||||
files: ['package.json'],
|
||||
plugins: ['eslint-plugin-n8n-nodes-base'],
|
||||
rules: {
|
||||
'n8n-nodes-base/community-package-json-name-still-default': 'error',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
7
packages/n8n/.vscode/extensions.json
vendored
7
packages/n8n/.vscode/extensions.json
vendored
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"EditorConfig.EditorConfig",
|
||||
"esbenp.prettier-vscode",
|
||||
]
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
import { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from "n8n-workflow";
|
||||
|
||||
export class FormbricksApi implements ICredentialType {
|
||||
name = "formbricksApi";
|
||||
displayName = "Formbricks API";
|
||||
properties: INodeProperties[] = [
|
||||
{
|
||||
displayName: "Host",
|
||||
name: "host",
|
||||
description:
|
||||
'The address of your Formbricks instance. For Formbricks Cloud this is "https://app.formbricks.com". If you are hosting Formbricks yourself, it\'s the address where you can reach your instance.',
|
||||
type: "string",
|
||||
default: "https://app.formbricks.com",
|
||||
},
|
||||
{
|
||||
displayName: "API Key",
|
||||
name: "apiKey",
|
||||
description:
|
||||
'Your Formbricks API-Key. You can create a new API-Key in the Product Settings. Please read our <a href="https://formbricks.com/docs/api/api-key-setup">API Key Docs</a> for more details.',
|
||||
type: "string",
|
||||
typeOptions: { password: true },
|
||||
default: "",
|
||||
},
|
||||
];
|
||||
|
||||
authenticate: IAuthenticateGeneric = {
|
||||
type: "generic",
|
||||
properties: {
|
||||
headers: {
|
||||
"x-Api-Key": "={{$credentials.apiKey}}",
|
||||
},
|
||||
},
|
||||
};
|
||||
test: ICredentialTestRequest | undefined = {
|
||||
request: {
|
||||
baseURL: "={{$credentials.host}}/api/v1",
|
||||
url: "=/me",
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
const path = require('path');
|
||||
const { task, src, dest } = require('gulp');
|
||||
|
||||
task('build:icons', copyIcons);
|
||||
|
||||
function copyIcons() {
|
||||
const nodeSource = path.resolve('nodes', '**', '*.{png,svg}');
|
||||
const nodeDestination = path.resolve('dist', 'nodes');
|
||||
|
||||
src(nodeSource).pipe(dest(nodeDestination));
|
||||
|
||||
const credSource = path.resolve('credentials', '**', '*.{png,svg}');
|
||||
const credDestination = path.resolve('dist', 'credentials');
|
||||
|
||||
return src(credSource).pipe(dest(credDestination));
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"node": "n8n-nodes-base.Formbricks",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": ["Communication"],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/credentials/formbricks"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.formbricks/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
import { INodeType, INodeTypeDescription, IWebhookFunctions, IWebhookResponseData } from "n8n-workflow";
|
||||
|
||||
import { apiRequest, getSurveys } from "./GenericFunctions";
|
||||
import { IHookFunctions } from "n8n-core";
|
||||
|
||||
export class Formbricks implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: "Formbricks",
|
||||
name: "formbricks",
|
||||
icon: "file:formbricks.svg",
|
||||
group: ["trigger"],
|
||||
version: 1,
|
||||
subtitle: '=Surveys: {{$parameter["surveyIds"]}}',
|
||||
description: "Open Source Surveys & Experience Management Solution",
|
||||
defaults: {
|
||||
name: "Formbricks",
|
||||
},
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
|
||||
inputs: [],
|
||||
outputs: ["main"],
|
||||
credentials: [
|
||||
{
|
||||
name: "formbricksApi",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
webhooks: [
|
||||
{
|
||||
name: "default",
|
||||
httpMethod: "POST",
|
||||
responseMode: "onReceived",
|
||||
path: "webhook",
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: "Events",
|
||||
name: "events",
|
||||
type: "multiOptions",
|
||||
options: [
|
||||
{
|
||||
name: "Response Created",
|
||||
value: "responseCreated",
|
||||
description:
|
||||
"Triggers when a new response is created for a survey. Normally triggered after the first question was answered.",
|
||||
},
|
||||
{
|
||||
name: "Response Updated",
|
||||
value: "responseUpdated",
|
||||
description: "Triggers when a response is updated within a survey",
|
||||
},
|
||||
{
|
||||
name: "Response Finished",
|
||||
value: "responseFinished",
|
||||
description: "Triggers when a response is marked as finished",
|
||||
},
|
||||
],
|
||||
default: [],
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-multi-options
|
||||
displayName: "Survey",
|
||||
name: "surveyIds",
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-description-wrong-for-dynamic-multi-options
|
||||
description:
|
||||
'Survey which should trigger workflow. Only trigger this node for a specific survey within the environment. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>.',
|
||||
type: "multiOptions",
|
||||
typeOptions: {
|
||||
loadOptionsMethod: "getSurveys",
|
||||
},
|
||||
options: [],
|
||||
default: [],
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
methods = {
|
||||
loadOptions: {
|
||||
getSurveys,
|
||||
},
|
||||
};
|
||||
|
||||
webhookMethods = {
|
||||
default: {
|
||||
async checkExists(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookData = this.getWorkflowStaticData("node");
|
||||
const webhookUrl = this.getNodeWebhookUrl("default");
|
||||
const surveyIds = this.getNodeParameter("surveyIds") as Array<string>;
|
||||
|
||||
const endpoint = "/webhooks";
|
||||
|
||||
try {
|
||||
const response = await apiRequest.call(this, "GET", endpoint, {});
|
||||
for (const webhook of response.data) {
|
||||
for (const surveyId of webhook.surveyIds) {
|
||||
if (surveyIds.includes(surveyId) && webhook.url === webhookUrl) {
|
||||
webhookData.webhookId = webhook.id;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
async create(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookData = this.getWorkflowStaticData("node");
|
||||
const webhookUrl = this.getNodeWebhookUrl("default");
|
||||
const surveyIds = this.getNodeParameter("surveyIds") as Array<string>;
|
||||
const events = this.getNodeParameter("events");
|
||||
|
||||
const body = {
|
||||
url: webhookUrl,
|
||||
triggers: events,
|
||||
surveyIds: surveyIds,
|
||||
};
|
||||
const endpoint = "/webhooks";
|
||||
|
||||
try {
|
||||
const response = await apiRequest.call(this, "POST", endpoint, body);
|
||||
webhookData.webhookId = response.data.id;
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
async delete(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookData = this.getWorkflowStaticData("node");
|
||||
if (webhookData.webhookId !== undefined) {
|
||||
const endpoint = `/webhooks/${webhookData.webhookId}`;
|
||||
|
||||
try {
|
||||
await apiRequest.call(this, "DELETE", endpoint, {});
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
// Remove from the static workflow data so that it is clear
|
||||
// that no webhooks are registered anymore
|
||||
delete webhookData.webhookId;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
|
||||
const bodyData = this.getBodyData();
|
||||
// getting bodyData as string, so need to JSON parse it to convert to an object
|
||||
return {
|
||||
workflowData: [this.helpers.returnJsonArray(JSON.parse(bodyData as any))],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
import { IHookFunctions, ILoadOptionsFunctions } from "n8n-core";
|
||||
import {
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
IHttpRequestMethods,
|
||||
IHttpRequestOptions,
|
||||
INodePropertyOptions,
|
||||
JsonObject,
|
||||
NodeApiError,
|
||||
NodeOperationError,
|
||||
} from "n8n-workflow";
|
||||
|
||||
/**
|
||||
* Make an API request to Formbricks
|
||||
*/
|
||||
export async function apiRequest(
|
||||
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions,
|
||||
method: IHttpRequestMethods,
|
||||
resource: string,
|
||||
body: object,
|
||||
query: IDataObject = {},
|
||||
option: IDataObject = {}
|
||||
): Promise<any> {
|
||||
const credentials = await this.getCredentials("formbricksApi");
|
||||
|
||||
let options: IHttpRequestOptions = {
|
||||
baseURL: `${credentials.host}/api/v1`,
|
||||
method,
|
||||
body,
|
||||
qs: query,
|
||||
url: resource,
|
||||
headers: {
|
||||
"x-Api-Key": credentials.apiKey,
|
||||
},
|
||||
};
|
||||
|
||||
if (!Object.keys(query).length) {
|
||||
delete options.qs;
|
||||
}
|
||||
|
||||
options = Object.assign({}, options, option);
|
||||
try {
|
||||
return await this.helpers.httpRequestWithAuthentication.call(this, "formbricksApi", options);
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the available surveys
|
||||
*/
|
||||
export async function getSurveys(this: ILoadOptionsFunctions): Promise<any> {
|
||||
const endpoint = "/surveys";
|
||||
const responseData = await apiRequest.call(this, "GET", endpoint, {});
|
||||
|
||||
if (!responseData.data) {
|
||||
throw new NodeOperationError(this.getNode(), "No data got returned");
|
||||
}
|
||||
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
for (const data of responseData.data) {
|
||||
returnData.push({
|
||||
name: data.name,
|
||||
value: data.id,
|
||||
});
|
||||
}
|
||||
|
||||
return returnData;
|
||||
}
|
||||
3765
packages/n8n/pnpm-lock.yaml
generated
3765
packages/n8n/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"target": "es2019",
|
||||
"lib": ["es2019", "es2020", "es2022.error"],
|
||||
"removeComments": true,
|
||||
"useUnknownInCatchVariables": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noUnusedLocals": true,
|
||||
"strictNullChecks": true,
|
||||
"preserveConstEnums": true,
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"incremental": true,
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "./dist/",
|
||||
},
|
||||
"include": [
|
||||
"credentials/**/*",
|
||||
"nodes/**/*",
|
||||
"nodes/**/*.json",
|
||||
"package.json",
|
||||
],
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
{
|
||||
"linterOptions": {
|
||||
"exclude": ["node_modules/**/*"]
|
||||
},
|
||||
"defaultSeverity": "error",
|
||||
"jsRules": {},
|
||||
"rules": {
|
||||
"array-type": [true, "array-simple"],
|
||||
"arrow-return-shorthand": true,
|
||||
"ban": [
|
||||
true,
|
||||
{
|
||||
"name": "Array",
|
||||
"message": "tsstyle#array-constructor"
|
||||
}
|
||||
],
|
||||
"ban-types": [
|
||||
true,
|
||||
["Object", "Use {} instead."],
|
||||
["String", "Use 'string' instead."],
|
||||
["Number", "Use 'number' instead."],
|
||||
["Boolean", "Use 'boolean' instead."]
|
||||
],
|
||||
"class-name": true,
|
||||
"curly": [true, "ignore-same-line"],
|
||||
"forin": true,
|
||||
"jsdoc-format": true,
|
||||
"label-position": true,
|
||||
"indent": [true, "tabs", 2],
|
||||
"member-access": [true, "no-public"],
|
||||
"new-parens": true,
|
||||
"no-angle-bracket-type-assertion": true,
|
||||
"no-any": true,
|
||||
"no-arg": true,
|
||||
"no-conditional-assignment": true,
|
||||
"no-construct": true,
|
||||
"no-debugger": true,
|
||||
"no-default-export": true,
|
||||
"no-duplicate-variable": true,
|
||||
"no-inferrable-types": true,
|
||||
"ordered-imports": [
|
||||
true,
|
||||
{
|
||||
"import-sources-order": "any",
|
||||
"named-imports-order": "case-insensitive"
|
||||
}
|
||||
],
|
||||
"no-namespace": [true, "allow-declarations"],
|
||||
"no-reference": true,
|
||||
"no-string-throw": true,
|
||||
"no-unused-expression": true,
|
||||
"no-var-keyword": true,
|
||||
"object-literal-shorthand": true,
|
||||
"only-arrow-functions": [true, "allow-declarations", "allow-named-functions"],
|
||||
"prefer-const": true,
|
||||
"radix": true,
|
||||
"semicolon": [true, "always", "ignore-bound-class-methods"],
|
||||
"switch-default": true,
|
||||
"trailing-comma": [
|
||||
true,
|
||||
{
|
||||
"multiline": {
|
||||
"objects": "always",
|
||||
"arrays": "always",
|
||||
"functions": "always",
|
||||
"typeLiterals": "ignore"
|
||||
},
|
||||
"esSpecCompliant": true
|
||||
}
|
||||
],
|
||||
"triple-equals": [true, "allow-null-check"],
|
||||
"use-isnan": true,
|
||||
"quotes": ["error", "single"],
|
||||
"variable-name": [
|
||||
true,
|
||||
"check-format",
|
||||
"ban-keywords",
|
||||
"allow-leading-underscore",
|
||||
"allow-trailing-underscore"
|
||||
]
|
||||
},
|
||||
"rulesDirectory": []
|
||||
}
|
||||
1866
pnpm-lock.yaml
generated
1866
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user