first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled

This commit is contained in:
2026-03-17 16:22:57 +03:30
commit 3d5eaf9445
15349 changed files with 2847338 additions and 0 deletions
+114
View File
@@ -0,0 +1,114 @@
# AGENTS.md
Guidance for node development in the nodes-base package.
## Node Structure
Every node implements the `INodeType` interface with:
- `description: INodeTypeDescription` - Node metadata and UI configuration
- `execute?()` - For programmatic nodes
- `poll?()` - For polling triggers (set `polling: true` in description)
- `trigger?()` - For generic triggers
- `webhook?()` - For webhook triggers
- `webhookMethods?` - Webhook lifecycle (checkExists, create, delete)
- `methods?` - loadOptions, listSearch, credentialTest, resourceMapping
## Node Types
### Programmatic Nodes
Use `execute` function for custom logic. Example: `nodes/Discord/v2/DiscordV2.node.ts`
### Declarative Nodes
Use `requestDefaults` and routing configuration instead of `execute`. Example: `nodes/Okta/Okta.node.ts`
### Trigger Nodes
- **Webhook triggers**: Implement `webhook` and `webhookMethods` (checkExists, create, delete). Example: `nodes/Microsoft/Teams/MicrosoftTeamsTrigger.node.ts`
- **Polling triggers**: Set `polling: true` and implement `poll`. Use `getWorkflowStaticData('node')` to persist state. Example: `nodes/Google/Gmail/GmailTrigger.node.ts`
- **Generic triggers**: Implement `trigger` function. Example: `nodes/MQTT/MqttTrigger.node.ts`
## Node Parameters
Common parameter types:
- `string` - Text input
- `options` - Dropdown (static or dynamic via `loadOptionsMethod`)
- `resourceLocator` - Select by list, ID, or URL
- `collection` - Key-value pairs
- `fixedCollection` - Structured collections
Use `displayOptions` to show/hide fields based on other parameters. Use `noDataExpression: true` for resource/operation selectors.
## Versioning
- **Light versioning**: Use version arrays in description: `version: [3, 3.1, 3.2]`
- **Full versioning**: Use `VersionedNodeType` class with separate version implementations. Example: `nodes/Set/Set.node.ts`
## Credentials
Credentials are defined in `credentials/` directory and implement `ICredentialType`:
- `name` - Internal identifier
- `displayName` - Human-readable name
- `properties` - Credential fields
- `authenticate` - Authentication configuration (generic or custom function)
- `test` - Credential test request
Nodes can test credentials via `methods.credentialTest`.
## Testing
### Unit Tests
- Use `jest-mock-extended` for mocking interfaces
- Use `nock` for HTTP mocking
- Mock all external dependencies
- Test happy paths, error handling, edge cases, and binary data
### Workflow Tests
- Use `NodeTestHarness` with JSON workflow definitions
- Mock external APIs with nock
- Use `pnpm test` for running tests. Example: `cd packages/nodes-base/ && pnpm test TestFileName`
## Common Development Tasks
### Creating a New Node
1. Create directory: `nodes/YourService/`
2. Create `YourService.node.ts` implementing `INodeType`
3. Add icon SVG files in node directory
4. Define credentials in `credentials/` if needed
5. Write tests following testing guidelines
6. Register in `package.json` nodes array if needed
### Adding Dynamic Options
Add `loadOptionsMethod` to parameter's `typeOptions` and implement method in `methods.loadOptions`.
### Adding Resource Locator
Change parameter type to `'resourceLocator'`, define modes (list, id, url), add `searchListMethod` for list mode, add `extractValue` regex for URL mode.
## Best Practices
### TypeScript
- Never use `any` type - use proper types or `unknown`
- Avoid type casting with `as` - use type guards instead
- Define interfaces for API responses
### Error Handling
- Use `NodeOperationError` for user-facing errors
- Use `NodeApiError` for API-related errors
- Support `continueOnFail` option when appropriate
### Code Organization
- Separate operation/field descriptions into separate files
- Create reusable API request helpers in GenericFunctions
- Use kebab-case for files, PascalCase for classes
### UI/UX
- Use clear `displayName` and `description` fields
- Set sensible default values
- Use `displayOptions` to show/hide fields conditionally
## Example Nodes
- Declarative: `nodes/Okta/Okta.node.ts`
- Programmatic: `nodes/Discord/v2/DiscordV2.node.ts`
- Webhook Trigger: `nodes/Microsoft/Teams/MicrosoftTeamsTrigger.node.ts`
- Polling Trigger: `nodes/Google/Gmail/GmailTrigger.node.ts`
- Generic Trigger: `nodes/MQTT/MqttTrigger.node.ts`
- Versioned: `nodes/Set/Set.node.ts`
+1
View File
@@ -0,0 +1 @@
@AGENTS.md
+13
View File
@@ -0,0 +1,13 @@
![n8n.io - Workflow Automation](https://user-images.githubusercontent.com/65276001/173571060-9f2f6d7b-bac0-43b6-bdb2-001da9694058.png)
# n8n-nodes-base
The nodes which are included by default in n8n
```
npm install n8n-nodes-base -g
```
## License
You can find the license information [here](https://github.com/n8n-io/n8n/blob/master/README.md#license)
+36
View File
@@ -0,0 +1,36 @@
To write unit tests it is suggested to use AI for
help.
## Approach for standard unit tests
1. **Create the test file**: Decide which node you want to test and create a test file with the corresponding name in the test folder. For example, for a node in `nodeA/v2/NodeAV2.node.ts`, create a test file in `nodeA/v2/test/NodeAV2.test.ts`.
2. **Use AI assistance**: Send this prompt to your AI tool (Cursor, Copilot, Claude, etc.):
```
Using guidelines in @TESTING_PROMPT.md, write tests for @NodeAV2.node.ts in @NodeAV2.node.test.ts
```
Make sure file names after `@` are detected and referenced by your tool.
You can improve the prompt by asking to cover specific test cases.
3. **Review and refine**: Thoroughly review the generated tests, make necessary fixes, and remove redundant tests. __Even if generated by AI, it's still your responsibility to ensure tests are working and reasonable.__
## Approach for workflow unit tests
Workflow unit tests are tests that use user predefined workflows in json format and NodeTestHarness helper that runs the workflow. This is closer to integration tests.
For these tests you can follow the guidelines defined above, but with some modifications:
- Use `TESTING_PROMPT_WORKFLOW.md` instead
- Use a different prompt. It's also important to specify a credentials schema if any credentials are being used, because AI struggles with identifying the schema. You can use the following prompt:
```
I need you to write workflow unit tests for @NodeAV2.node.ts in @NodeAV2.node.test.ts
using guidelines in @TESTING_PROMPT_WORKFLOW.md
You should test each resource and operation in the node
After writing a first test make sure it passes, then write other tests.
To mock credentials use this schema
oauth2: {
scope: '',
oauthTokenData: {
access_token: 'ACCESSTOKEN',
},
}
```
+473
View File
@@ -0,0 +1,473 @@
# AI Agent Prompt: Writing Reliable Unit Tests for n8n Nodes
You are an expert AI agent specialized in writing comprehensive, reliable unit tests for n8n nodes in the `@packages/nodes-base` folder. Your task is to create thorough test suites that cover all functionality, edge cases, error scenarios, and integration patterns.
## Core Testing Principles
### 1. Test Structure and Organization
- **File Naming**: Use `.test.ts` extension, place in `test/` or `__tests__/` directories
- **Test Organization**: Group tests by functionality using `describe()` blocks. Test concrete operations and resources.
- **Test Naming**: Use descriptive test names that explain the expected behavior
- **Setup/Teardown**: Use `beforeEach()` and `afterEach()` for consistent test isolation
### 3. Testing guidelines
- **Don't add useless comments** such as "Arrange, Assert, Act" or "Mock something".
- **Always work from within the package directory** when running tests. E.g. for a node in nodes-base enter `packages/nodes-base` or for langchain node enter `packages/@n8n/nodes-langchain`
- **Use `pnpm test <file_name>`** for running tests
- **Mock all external dependencies** in unit tests
### 4. Essential Test Categories
Always include tests for:
- **Happy Path**: Normal operation with valid inputs
- **Error Handling**: Invalid inputs, API failures
- **Edge Cases**: Empty data, null values, boundary conditions
- **Binary Data**: File uploads, downloads, data streams
- **Authentication**: Credential handling, token refresh
- **Rate Limiting**: API throttling, retry logic
- **Data Transformation**: Input/output data processing
- **Node Versioning**: Different node type versions
## Mocking Strategies
### 1. Core n8n Interfaces Mocking
```typescript
import { mock, mockDeep } from 'jest-mock-extended';
import type { IExecuteFunctions, IWebhookFunctions, INode } from 'n8n-workflow';
// Standard execute functions mock
const mockExecuteFunctions = mockDeep<IExecuteFunctions>();
// Webhook functions mock
const mockWebhookFunctions = mock<IWebhookFunctions>();
// Node mock
const mockNode = mock<INode>({
id: 'test-node',
name: 'Test Node',
type: 'n8n-nodes-base.test',
typeVersion: 1,
position: [0, 0],
parameters: {},
});
```
### 2. Common Mock Patterns
```typescript
// Input data mocking
mockExecuteFunctions.getInputData.mockReturnValue([
{ json: { test: 'data' } },
{ json: { another: 'item' } }
]);
// Node parameter mocking
mockExecuteFunctions.getNodeParameter.mockImplementation((paramName: string) => {
const mockParams = {
'operation': 'create',
'resource': 'user',
'name': 'Test User',
'email': 'test@example.com'
};
return mockParams[paramName];
});
// Credentials mocking
mockExecuteFunctions.getCredentials.mockResolvedValue({
accessToken: 'test-token',
baseUrl: 'https://api.example.com'
});
// Binary data mocking
mockExecuteFunctions.helpers.prepareBinaryData.mockResolvedValue({
data: 'base64data',
mimeType: 'text/plain',
fileName: 'test.txt'
});
```
### 3. External API Mocking
```typescript
// Using jest.spyOn for API functions
const apiRequestSpy = jest.spyOn(GenericFunctions, 'apiRequest');
apiRequestSpy.mockResolvedValue({
id: '123',
name: 'Test Item',
status: 'active'
});
// Using nock for HTTP mocking
import nock from 'nock';
beforeEach(() => {
nock('https://api.example.com')
.get('/users')
.reply(200, { users: [{ id: 1, name: 'John' }] });
});
afterEach(() => {
nock.cleanAll();
});
```
### 4. Database and External Service Mocking
```typescript
// Database mocking
const mockDataTable = mock<IDataStoreProjectService>({
getColumns: jest.fn(),
addColumn: jest.fn(),
updateRow: jest.fn(),
});
// Redis client mocking
const mockClient = mock<RedisClient>();
const createClient = jest.fn().mockReturnValue(mockClient);
jest.mock('redis', () => ({ createClient }));
```
## Test Implementation Patterns
### 1. Basic Node Execution Test
```typescript
describe('Node Execution', () => {
beforeEach(() => {
jest.clearAllMocks();
mockExecuteFunctions.getInputData.mockReturnValue([{ json: {} }]);
mockExecuteFunctions.getNode.mockReturnValue(mockNode);
});
it('should execute successfully with valid parameters', async () => {
// Setup mocks
mockExecuteFunctions.getNodeParameter.mockImplementation((param) => {
const params = { operation: 'create', name: 'Test' };
return params[param];
});
apiRequestSpy.mockResolvedValue({ id: '123', name: 'Test' });
// Execute
const result = await node.execute.call(mockExecuteFunctions);
// Assertions
expect(result).toEqual([[
{ json: { id: '123', name: 'Test' }, pairedItem: { item: 0 } }
]]);
expect(apiRequestSpy).toHaveBeenCalledWith('POST', '/items', { name: 'Test' });
});
});
```
### 2. Error Handling Tests
```typescript
describe('Error Handling', () => {
it('should throw error for invalid credentials', async () => {
mockExecuteFunctions.getCredentials.mockRejectedValue(
new Error('Invalid credentials')
);
await expect(node.execute.call(mockExecuteFunctions))
.rejects.toThrow('Invalid credentials');
});
it('should handle API errors gracefully', async () => {
apiRequestSpy.mockRejectedValue(new Error('API Error'));
mockExecuteFunctions.continueOnFail.mockReturnValue(true);
const result = await node.execute.call(mockExecuteFunctions);
expect(result[0][0].json).toHaveProperty('error');
});
it('should validate required parameters', async () => {
mockExecuteFunctions.getNodeParameter.mockReturnValue(undefined);
await expect(node.execute.call(mockExecuteFunctions))
.rejects.toThrow(NodeOperationError);
});
});
```
### 3. Binary Data Testing
```typescript
describe('Binary Data Handling', () => {
it('should process binary files correctly', async () => {
const mockBinaryData = {
data: 'base64data',
mimeType: 'image/png',
fileName: 'test.png'
};
mockExecuteFunctions.helpers.assertBinaryData.mockReturnValue(mockBinaryData);
mockExecuteFunctions.helpers.prepareBinaryData.mockResolvedValue(mockBinaryData);
const result = await node.execute.call(mockExecuteFunctions);
expect(result[0][0].binary).toBeDefined();
expect(mockExecuteFunctions.helpers.prepareBinaryData).toHaveBeenCalled();
});
it('should handle file upload operations', async () => {
const fileBuffer = Buffer.from('test file content');
mockExecuteFunctions.helpers.getBinaryStream.mockResolvedValue(fileBuffer);
// Test file upload logic
const result = await node.execute.call(mockExecuteFunctions);
expect(result[0][0].json).toHaveProperty('fileId');
});
});
```
### 4. Webhook Testing
```typescript
describe('Webhook Operations', () => {
it('should handle GET requests', async () => {
const mockRequest = { method: 'GET', query: { id: '123' } };
const mockResponse = { render: jest.fn(), send: jest.fn() };
mockWebhookFunctions.getRequestObject.mockReturnValue(mockRequest);
mockWebhookFunctions.getResponseObject.mockReturnValue(mockResponse);
await node.webhook(mockWebhookFunctions);
expect(mockResponse.render).toHaveBeenCalledWith('template', expect.any(Object));
});
it('should process POST data', async () => {
const mockRequest = {
method: 'POST',
body: { name: 'Test', email: 'test@example.com' }
};
mockWebhookFunctions.getRequestObject.mockReturnValue(mockRequest);
mockWebhookFunctions.getBodyData.mockReturnValue(mockRequest.body);
const result = await node.webhook(mockWebhookFunctions);
expect(result.workflowData).toBeDefined();
expect(result.workflowData[0][0].json).toEqual(mockRequest.body);
});
});
```
### 5. Data Transformation Testing
```typescript
describe('Data Processing', () => {
it('should transform input data correctly', async () => {
const inputData = [
{ json: { firstName: 'John', lastName: 'Doe' } },
{ json: { firstName: 'Jane', lastName: 'Smith' } }
];
mockExecuteFunctions.getInputData.mockReturnValue(inputData);
const result = await node.execute.call(mockExecuteFunctions);
expect(result[0]).toHaveLength(2);
expect(result[0][0].json).toHaveProperty('fullName', 'John Doe');
});
it('should handle empty input gracefully', async () => {
mockExecuteFunctions.getInputData.mockReturnValue([]);
const result = await node.execute.call(mockExecuteFunctions);
expect(result).toEqual([[]]);
});
});
```
## Advanced Testing Patterns
### 1. Using NodeTestHarness for Integration Tests
```typescript
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
describe('Integration Tests', () => {
new NodeTestHarness().setupTests({
credentials: {
'testApi': { accessToken: 'test-token' }
},
nock: {
baseUrl: 'https://api.example.com',
mocks: [{
method: 'get',
path: '/users',
statusCode: 200,
responseBody: { users: [] }
}]
}
});
});
```
### 2. Testing Node Methods and Properties
```typescript
describe('Node Methods', () => {
it('should have required methods defined', () => {
expect(node.methods.credentialTest).toBeDefined();
expect(node.methods.loadOptions).toBeDefined();
expect(node.methods.listSearch).toBeDefined();
});
it('should validate credential test method', async () => {
const mockCredentialTestFunctions = mock<ICredentialTestFunctions>();
mockCredentialTestFunctions.getCredentials.mockResolvedValue({
accessToken: 'test-token'
});
const result = await node.methods.credentialTest.testApiCredentialTest.call(
mockCredentialTestFunctions
);
expect(result).toEqual({ status: 'OK' });
});
});
```
### 3. Testing Load Options
```typescript
describe('Load Options', () => {
it('should load resource options', async () => {
const mockLoadOptionsFunctions = mock<ILoadOptionsFunctions>();
mockLoadOptionsFunctions.getCredentials.mockResolvedValue({
accessToken: 'test-token'
});
apiRequestSpy.mockResolvedValue([
{ id: '1', name: 'Option 1' },
{ id: '2', name: 'Option 2' }
]);
const result = await node.methods.loadOptions.resourceOptions.call(
mockLoadOptionsFunctions
);
expect(result).toEqual([
{ name: 'Option 1', value: '1' },
{ name: 'Option 2', value: '2' }
]);
});
});
```
## Testing Guidelines
### 1. Test Coverage Requirements
- **Minimum 80% code coverage** for all node files
- **100% coverage** for critical error handling paths
- **Test all public methods** and exported functions
- **Cover all conditional branches** and edge cases
### 2. Test Data Management
- Use **realistic test data** that mirrors production scenarios
- Create **reusable test fixtures** for common data patterns
- Use **factory functions** for generating test data
- **Clean up test data** in afterEach hooks
### 3. Assertion Best Practices
```typescript
// Use specific assertions
expect(result).toEqual(expectedData);
expect(mockFunction).toHaveBeenCalledWith(expectedArgs);
expect(mockFunction).toHaveBeenCalledTimes(1);
// Test error messages
expect(() => functionCall()).toThrow('Expected error message');
// Test async operations
await expect(asyncFunction()).resolves.toEqual(expectedResult);
await expect(asyncFunction()).rejects.toThrow(Error);
```
### 4. Performance and Reliability
- **Mock external dependencies** to ensure test reliability
- **Use deterministic test data** for consistent results
- **Test timeout scenarios** for long-running operations
- **Validate memory usage** for large data processing
### 5. Documentation and Maintenance
- **Document complex test scenarios** with inline comments
- **Use descriptive test names** that explain the test purpose
- **Group related tests** logically in describe blocks
- **Keep tests independent** - no test should depend on another
## Common Anti-Patterns to Avoid
1. **Don't test implementation details** - focus on behavior
2. **Don't use real external APIs** in unit tests
3. **Don't skip error handling tests** - they're critical
4. **Don't use hardcoded values** - use constants or factories
5. **Don't ignore async operations** - always await promises
6. **Don't test multiple concerns** in a single test case
## Example Complete Test Suite
```typescript
import { mock, mockDeep } from 'jest-mock-extended';
import type { IExecuteFunctions, INode } from 'n8n-workflow';
import { NodeOperationError } from 'n8n-workflow';
import { TestNode } from '../TestNode';
import * as GenericFunctions from '../GenericFunctions';
describe('TestNode', () => {
let node: TestNode;
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
const apiRequestSpy = jest.spyOn(GenericFunctions, 'apiRequest');
beforeEach(() => {
node = new TestNode();
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
jest.clearAllMocks();
});
afterEach(() => {
jest.resetAllMocks();
});
describe('execute', () => {
beforeEach(() => {
mockExecuteFunctions.getInputData.mockReturnValue([{ json: {} }]);
mockExecuteFunctions.getNode.mockReturnValue({
id: 'test',
name: 'Test Node',
type: 'n8n-nodes-base.test',
typeVersion: 1,
position: [0, 0],
parameters: {}
});
});
describe('successful execution', () => {
it('should process data correctly', async () => {
mockExecuteFunctions.getNodeParameter.mockImplementation((param) => {
const params = { operation: 'create', name: 'Test Item' };
return params[param];
});
apiRequestSpy.mockResolvedValue({ id: '123', name: 'Test Item' });
const result = await node.execute.call(mockExecuteFunctions);
expect(result).toEqual([[
{ json: { id: '123', name: 'Test Item' }, pairedItem: { item: 0 } }
]]);
expect(apiRequestSpy).toHaveBeenCalledWith('POST', '/items', { name: 'Test Item' });
});
});
describe('error handling', () => {
it('should handle API errors with continueOnFail', async () => {
mockExecuteFunctions.getNodeParameter.mockReturnValue('create');
mockExecuteFunctions.continueOnFail.mockReturnValue(true);
apiRequestSpy.mockRejectedValue(new Error('API Error'));
const result = await node.execute.call(mockExecuteFunctions);
expect(result[0][0].json).toHaveProperty('error', 'API Error');
});
});
});
});
```
@@ -0,0 +1,562 @@
# AI Agent Prompt: Writing Reliable Workflow Unit Tests for n8n Nodes
You are an expert AI agent specialized in writing comprehensive, reliable workflow unit tests for n8n nodes in the `@packages/nodes-base` folder. Your task is to create thorough test suites that use `.workflow.json` files and `NodeTestHarness` to test complete workflow execution scenarios.
## Core Guidelines
- **Don't add useless comments** such as "Arrange, Assert, Act" or "Mock something"
- **Always work from within the package directory** when running tests
- **Use `pnpm test`** for running tests. Example: `cd packages/nodes-base/ && pnpm test TestFileName
## Essential Test Structure
### Basic Test Setup
```typescript
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
describe('NodeName', () => {
describe('Run Test Workflow', () => {
beforeAll(() => {
const mock = nock('https://api.example.com');
mock.post('/endpoint').reply(200, mockResponse);
mock.get('/data').reply(200, mockData);
});
new NodeTestHarness().setupTests();
});
});
```
### Advanced Test with Credentials
```typescript
describe('NodeName', () => {
const credentials = {
nodeApi: {
accessToken: 'test-token',
baseUrl: 'https://api.example.com',
},
};
describe('Run Test Workflow', () => {
beforeAll(() => {
const mock = nock(credentials.nodeApi.baseUrl);
mock.post('/users').reply(200, userCreateResponse);
});
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['workflow.json'],
assertBinaryData: true
});
});
});
```
## Workflow JSON Structure
### Basic Workflow Template
```json
{
"name": "NodeName Test Workflow",
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [0, 0],
"id": "trigger-id",
"name": "When clicking 'Execute Workflow'"
},
{
"parameters": {
"operation": "create",
"resource": "user",
"name": "Test User",
"email": "test@example.com"
},
"type": "n8n-nodes-base.nodeName",
"typeVersion": 1,
"position": [200, 0],
"id": "node-id",
"name": "Node Operation",
"credentials": {
"nodeApi": {
"id": "credential-id",
"name": "Test Credentials"
}
}
}
],
"pinData": {
"Node Operation": [
{
"json": {
"id": "123",
"name": "Test User",
"email": "test@example.com",
"status": "active"
}
}
]
},
"connections": {
"When clicking 'Execute Workflow'": {
"main": [
[
{
"node": "Node Operation",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
}
}
```
## Node Parameter Types
### Basic Parameters
```json
{
"displayName": "Parameter Name",
"name": "parameterName",
"type": "string|number|boolean|options",
"default": "defaultValue",
"required": true
}
```
### Collection Parameters
```json
{
"displayName": "Additional Fields",
"name": "additionalFields",
"type": "collection",
"default": {},
"options": [
{
"displayName": "Custom Field",
"name": "customField",
"type": "string",
"default": ""
}
]
}
```
### Fixed Collection Parameters
```json
{
"displayName": "Fields to Set",
"name": "fields",
"type": "fixedCollection",
"typeOptions": {
"multipleValues": true
},
"options": [
{
"name": "values",
"displayName": "Values",
"values": [
{
"displayName": "Name",
"name": "name",
"type": "string",
"default": ""
}
]
}
]
}
```
## HTTP Mocking with Nock
### Basic API Mocking
```typescript
beforeAll(() => {
const mock = nock('https://api.example.com');
// Mock GET request
mock.get('/users')
.reply(200, {
users: [
{ id: 1, name: 'User 1' },
{ id: 2, name: 'User 2' }
]
});
// Mock POST request
mock.post('/users', {
name: 'Test User',
email: 'test@example.com'
})
.reply(201, {
id: 123,
name: 'Test User',
email: 'test@example.com',
status: 'active'
});
// Mock error responses
mock.get('/error-endpoint')
.reply(500, { error: 'Internal Server Error' });
});
```
### Advanced Mocking
```typescript
beforeAll(() => {
const mock = nock('https://api.example.com');
// Mock with headers
mock.get('/protected-endpoint')
.matchHeader('Authorization', 'Bearer test-token')
.reply(200, { data: 'protected' });
// Mock with query parameters
mock.get('/search')
.query({ q: 'test', limit: 10 })
.reply(200, { results: [] });
// Mock with request body validation
mock.post('/validate', (body) => {
return body.name && body.email;
})
.reply(200, { valid: true });
});
```
### Credentials Mocking
Some workflows require credentials for NodeHarness. If the execution result of a test is null it means that workflow has invalid inputs. Very often it's misconfigured credentials.
```typescript
const credentials = {
googleAnalyticsOAuth2: {
scope: '',
oauthTokenData: {
access_token: 'ACCESSTOKEN',
},
}
}
```
```typescript
const credentials = {
aws: {
region: 'eu-central-1',
accessKeyId: 'test',
secretAccessKey: 'test',
},
}
```
```typescript
wordpressApi: {
url: 'https://myblog.com',
allowUnauthorizedCerts: false,
username: 'nodeqa',
password: 'fake-password',
},
```
```typescript
const credentials = {
telegramApi: {
accessToken: 'testToken',
baseUrl: 'https://api.telegram.org',
},
};
```
## Binary Data Testing
### Binary Data Workflow
```json
{
"pinData": {
"Upload Node": [
{
"json": {
"fileId": "123",
"fileName": "test.txt",
"fileSize": 1024,
"mimeType": "text/plain"
},
"binary": {
"data": {
"data": "base64data",
"mimeType": "text/plain",
"fileName": "test.txt"
}
}
}
]
}
}
```
### Binary Data Test Setup
```typescript
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['binary.workflow.json'],
assertBinaryData: true
});
```
## Error Scenario Testing
### Error Workflow
```json
{
"pinData": {
"Error Node": [
{
"json": {
"error": "User not found",
"message": "Invalid request",
"code": 404
}
}
]
}
}
```
### Error Mock Setup
```typescript
beforeAll(() => {
const mock = nock('https://api.example.com');
mock.get('/users/nonexistent')
.reply(404, { error: 'User not found' });
});
```
## Advanced Workflow Patterns
### Switch Node Testing
```json
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"conditions": [
{
"leftValue": "={{ $json.status }}",
"rightValue": "active",
"operator": {
"type": "string",
"operation": "equals"
}
}
]
},
"outputKey": "Active Users"
}
]
}
}
}
```
### Set Node Testing
```json
{
"parameters": {
"fields": {
"values": [
{
"name": "processed",
"stringValue": "true"
},
{
"name": "timestamp",
"stringValue": "={{ new Date().toISOString() }}"
}
]
}
}
}
```
### Code Node Testing
```json
{
"parameters": {
"jsCode": "return [\n { id: 1, name: 'Item 1' },\n { id: 2, name: 'Item 2' }\n]"
}
}
```
## Credential Types
### API Key Credentials
```json
{
"credentials": {
"openAiApi": {
"id": "openai-cred-id",
"name": "OpenAI API Key"
}
}
}
```
### OAuth2 Credentials
```json
{
"credentials": {
"slackOAuth2Api": {
"id": "slack-oauth-id",
"name": "Slack OAuth2"
}
}
}
```
### Database Credentials
```json
{
"credentials": {
"postgres": {
"id": "postgres-cred-id",
"name": "PostgreSQL Database"
}
}
}
```
## Essential Test Categories
Always include tests for:
- **Complete Workflow Execution**: End-to-end workflow scenarios
- **API Integration**: External API calls with proper mocking
- **Data Flow**: Input data transformation through multiple nodes
- **Error Scenarios**: Workflow execution with API failures
- **Binary Data Handling**: File uploads, downloads, and processing
- **Authentication**: Credential handling across workflow execution
- **Node Interactions**: Multiple nodes working together
- **Conditional Logic**: Switch nodes, conditional execution paths
## Best Practices
### Workflow JSON Design
- **Trigger Node**: Always start with `n8n-nodes-base.manualTrigger`
- **Node Parameters**: Include all required parameters with realistic values
- **Node Connections**: Define clear data flow between nodes
- **Pin Data**: Provide expected outputs for validation
- **Credentials**: Reference appropriate credential types
### Mock Setup
- **Mock all external API calls** to ensure test reliability
- **Use realistic response data** that matches expected outputs
- **Test both success and error scenarios**
- **Include proper HTTP status codes**
- **Clean up mocks** between test runs
### Test Organization
- **Group related workflows** in the same test file
- **Use descriptive test names** that explain the scenario
- **Keep workflow JSON files** in the same directory as test files
- **Use consistent naming conventions** for workflow files
## Common Anti-Patterns to Avoid
1. **Don't use real external APIs** in workflow tests
2. **Don't skip pinData** - it's essential for output validation
3. **Don't forget to mock all API calls** - missing mocks cause test failures
4. **Don't use hardcoded credentials** - use test credentials
5. **Don't ignore error scenarios** - test both success and failure cases
6. **Don't create overly complex workflows** - keep them focused and testable
7. **Don't forget to clean up nock mocks** between tests
8. **Don't use production data** in test workflows
9. **Don't skip credential testing** - test authentication flows
10. **Don't ignore node version differences** - test multiple node versions
## Complete Example
```typescript
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
describe('NodeName', () => {
const credentials = {
nodeApi: {
accessToken: 'test-token',
baseUrl: 'https://api.example.com',
},
};
describe('Basic Operations', () => {
beforeAll(() => {
const mock = nock(credentials.nodeApi.baseUrl);
mock.get('/users')
.reply(200, {
users: [
{ id: 1, name: 'User 1', email: 'user1@example.com' },
{ id: 2, name: 'User 2', email: 'user2@example.com' }
]
});
mock.post('/users', {
name: 'Test User',
email: 'test@example.com'
})
.reply(201, {
id: 123,
name: 'Test User',
email: 'test@example.com',
status: 'active'
});
});
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['basic.workflow.json']
});
});
describe('Error Handling', () => {
beforeAll(() => {
const mock = nock(credentials.nodeApi.baseUrl);
mock.get('/users')
.reply(500, { error: 'Internal Server Error' });
});
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['error.workflow.json']
});
});
describe('Binary Data Operations', () => {
beforeAll(() => {
const mock = nock(credentials.nodeApi.baseUrl);
mock.post('/upload')
.reply(200, {
fileId: '123',
fileName: 'test.txt',
fileSize: 1024,
mimeType: 'text/plain'
});
});
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['binary.workflow.json'],
assertBinaryData: true
});
});
});
```
+7
View File
@@ -0,0 +1,7 @@
{
"$schema": "../../node_modules/@biomejs/biome/configuration_schema.json",
"extends": ["../../biome.jsonc"],
"formatter": {
"ignore": ["nodes/**/test/*.json", "nodes/**/__schema__/**/*.json"]
}
}
@@ -0,0 +1,40 @@
import type {
ICredentialDataDecryptedObject,
ICredentialTestRequest,
ICredentialType,
IHttpRequestOptions,
INodeProperties,
} from 'n8n-workflow';
export class ActionNetworkApi implements ICredentialType {
name = 'actionNetworkApi';
displayName = 'Action Network API';
documentationUrl = 'actionnetwork';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
test: ICredentialTestRequest = {
request: {
baseURL: 'https://actionnetwork.org/api/v2',
url: '/events?per_page=1',
},
};
async authenticate(
credentials: ICredentialDataDecryptedObject,
requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> {
requestOptions.headers = { 'OSDI-API-Token': credentials.apiKey };
return requestOptions;
}
}
@@ -0,0 +1,46 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class ActiveCampaignApi implements ICredentialType {
name = 'activeCampaignApi';
displayName = 'ActiveCampaign API';
documentationUrl = 'activecampaign';
properties: INodeProperties[] = [
{
displayName: 'API URL',
name: 'apiUrl',
type: 'string',
default: '',
},
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
'Api-Token': '={{$credentials.apiKey}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: '={{$credentials.apiUrl}}',
url: '/api/3/fields',
},
};
}
@@ -0,0 +1,25 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class AcuitySchedulingApi implements ICredentialType {
name = 'acuitySchedulingApi';
displayName = 'Acuity Scheduling API';
documentationUrl = 'acuityscheduling';
properties: INodeProperties[] = [
{
displayName: 'User ID',
name: 'userId',
type: 'string',
default: '',
},
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
}
@@ -0,0 +1,53 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class AcuitySchedulingOAuth2Api implements ICredentialType {
name = 'acuitySchedulingOAuth2Api';
extends = ['oAuth2Api'];
displayName = 'AcuityScheduling OAuth2 API';
documentationUrl = 'acuityscheduling';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden',
default: 'https://acuityscheduling.com/oauth2/authorize',
required: true,
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden',
default: 'https://acuityscheduling.com/oauth2/token',
required: true,
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden',
default: 'api-v1',
required: true,
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden',
default: '',
},
{
displayName: 'Authentication',
name: 'authentication',
type: 'hidden',
default: 'body',
},
];
}
@@ -0,0 +1,38 @@
import type { IAuthenticateGeneric, ICredentialType, INodeProperties } from 'n8n-workflow';
export class AdaloApi implements ICredentialType {
name = 'adaloApi';
displayName = 'Adalo API';
documentationUrl = 'adalo';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
description:
'The Adalo API is available on paid Adalo plans, find more information <a href="https://help.adalo.com/integrations/the-adalo-api" target="_blank">here</a>',
},
{
displayName: 'App ID',
name: 'appId',
type: 'string',
default: '',
description:
'You can get App ID from the URL of your app. For example, if your app URL is <strong>https://app.adalo.com/apps/1234567890/screens</strong>, then your App ID is <strong>1234567890</strong>.',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '=Bearer {{$credentials.apiKey}}',
},
},
};
}
@@ -0,0 +1,19 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class AffinityApi implements ICredentialType {
name = 'affinityApi';
displayName = 'Affinity API';
documentationUrl = 'affinity';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
}
@@ -0,0 +1,35 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class AgileCrmApi implements ICredentialType {
name = 'agileCrmApi';
displayName = 'AgileCRM API';
documentationUrl = 'agilecrm';
properties: INodeProperties[] = [
{
displayName: 'Email',
name: 'email',
type: 'string',
placeholder: 'name@email.com',
default: '',
},
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
{
displayName: 'Subdomain',
name: 'subdomain',
type: 'string',
default: '',
placeholder: 'example',
description:
'If the domain is https://example.agilecrm.com "example" would have to be entered',
},
];
}
@@ -0,0 +1,35 @@
import type { IAuthenticateGeneric, ICredentialType, INodeProperties } from 'n8n-workflow';
export class AirtableApi implements ICredentialType {
name = 'airtableApi';
displayName = 'Airtable API';
documentationUrl = 'airtable';
properties: INodeProperties[] = [
{
displayName:
"This type of connection (API Key) was deprecated and can't be used anymore. Please create a new credential of type 'Access Token' instead.",
name: 'deprecated',
type: 'notice',
default: '',
},
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
qs: {
api_key: '={{$credentials.apiKey}}',
},
},
};
}
@@ -0,0 +1,52 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
const scopes = ['schema.bases:read', 'data.records:read', 'data.records:write'];
export class AirtableOAuth2Api implements ICredentialType {
name = 'airtableOAuth2Api';
extends = ['oAuth2Api'];
displayName = 'Airtable OAuth2 API';
documentationUrl = 'airtable';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'pkce',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden',
default: 'https://airtable.com/oauth2/v1/authorize',
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden',
default: 'https://airtable.com/oauth2/v1/token',
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden',
default: `${scopes.join(' ')}`,
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden',
default: '',
},
{
displayName: 'Authentication',
name: 'authentication',
type: 'hidden',
default: 'header',
},
];
}
@@ -0,0 +1,49 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class AirtableTokenApi implements ICredentialType {
name = 'airtableTokenApi';
displayName = 'Airtable Personal Access Token API';
documentationUrl = 'airtable';
properties: INodeProperties[] = [
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string',
typeOptions: { password: true },
default: '',
},
{
displayName: `Make sure you enabled the following scopes for your token:<br>
<code>data.records:read</code><br>
<code>data.records:write</code><br>
<code>schema.bases:read</code><br>
`,
name: 'notice',
type: 'notice',
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '=Bearer {{$credentials.accessToken}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: 'https://api.airtable.com/v0/meta/whoami',
},
};
}
@@ -0,0 +1,53 @@
import type {
IAuthenticateGeneric,
ICredentialType,
ICredentialTestRequest,
INodeProperties,
} from 'n8n-workflow';
import { BASE_URL } from '../nodes/Airtop/constants';
export class AirtopApi implements ICredentialType {
name = 'airtopApi';
displayName = 'Airtop API';
documentationUrl = 'airtop';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
default: '',
description:
'The Airtop API key. You can create one at <a href="https://portal.airtop.ai/api-keys" target="_blank">Airtop</a> for free.',
required: true,
typeOptions: {
password: true,
},
noDataExpression: true,
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '=Bearer {{$credentials.apiKey}}',
'api-key': '={{$credentials.apiKey}}',
},
},
};
test: ICredentialTestRequest = {
request: {
method: 'GET',
baseURL: BASE_URL,
url: '/sessions',
qs: {
limit: 10,
},
},
};
}
@@ -0,0 +1,50 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
Icon,
} from 'n8n-workflow';
export class AlienVaultApi implements ICredentialType {
name = 'alienVaultApi';
displayName = 'AlienVault API';
documentationUrl = 'alienvault';
icon: Icon = 'file:icons/AlienVault.png';
httpRequestNode = {
name: 'AlienVault',
docsUrl: 'https://otx.alienvault.com/api',
apiBaseUrl: 'https://otx.alienvault.com/api/v1/',
};
properties: INodeProperties[] = [
{
displayName: 'OTX Key',
name: 'accessToken',
type: 'string',
typeOptions: { password: true },
required: true,
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
'X-OTX-API-KEY': '={{$credentials.accessToken}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: 'https://otx.alienvault.com',
url: '/api/v1/user/me',
},
};
}
@@ -0,0 +1,50 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class Amqp implements ICredentialType {
name = 'amqp';
displayName = 'AMQP';
documentationUrl = 'amqp';
properties: INodeProperties[] = [
{
displayName: 'Hostname',
name: 'hostname',
type: 'string',
placeholder: 'e.g. localhost',
default: '',
},
{
displayName: 'Port',
name: 'port',
type: 'number',
default: 5672,
},
{
displayName: 'User',
name: 'username',
type: 'string',
placeholder: 'e.g. guest',
default: '',
},
{
displayName: 'Password',
name: 'password',
type: 'string',
placeholder: 'e.g. guest',
typeOptions: {
password: true,
},
default: '',
},
{
displayName: 'Transport Type',
name: 'transportType',
type: 'string',
placeholder: 'e.g. tcp',
default: '',
hint: 'Optional transport type to use, either tcp or tls',
},
];
}
@@ -0,0 +1,40 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class ApiTemplateIoApi implements ICredentialType {
name = 'apiTemplateIoApi';
displayName = 'APITemplate.io API';
documentationUrl = 'apitemplateio';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
'X-API-KEY': '={{$credentials.apiKey}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: 'https://api.apitemplate.io/v1',
url: '/list-templates',
},
};
}
@@ -0,0 +1,28 @@
import type { IAuthenticateGeneric, ICredentialType, INodeProperties } from 'n8n-workflow';
export class AsanaApi implements ICredentialType {
name = 'asanaApi';
displayName = 'Asana API';
documentationUrl = 'asana';
properties: INodeProperties[] = [
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '=Bearer {{$credentials.accessToken}}',
},
},
};
}
@@ -0,0 +1,52 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class AsanaOAuth2Api implements ICredentialType {
name = 'asanaOAuth2Api';
extends = ['oAuth2Api'];
displayName = 'Asana OAuth2 API';
documentationUrl = 'asana';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden',
default: 'https://app.asana.com/-/oauth_authorize',
required: true,
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden',
default: 'https://app.asana.com/-/oauth_token',
required: true,
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden',
default: '',
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden',
default: '',
},
{
displayName: 'Authentication',
name: 'authentication',
type: 'hidden',
default: 'body',
},
];
}
@@ -0,0 +1,94 @@
import type {
IAuthenticateGeneric,
ICredentialDataDecryptedObject,
ICredentialTestRequest,
ICredentialType,
IHttpRequestHelper,
INodeProperties,
} from 'n8n-workflow';
export class Auth0ManagementApi implements ICredentialType {
name = 'auth0ManagementApi';
displayName = 'Auth0 Management API';
documentationUrl = 'auth0management';
icon = { light: 'file:icons/Auth0.svg', dark: 'file:icons/Auth0.dark.svg' } as const;
httpRequestNode = {
name: 'Auth0',
docsUrl: 'https://auth0.com/docs/api/management/v2',
apiBaseUrlPlaceholder: 'https://your-tenant.auth0.com/api/v2/users/',
};
properties: INodeProperties[] = [
{
displayName: 'Session Token',
name: 'sessionToken',
type: 'hidden',
typeOptions: {
expirable: true,
password: true,
},
default: '',
},
{
displayName: 'Auth0 Domain',
name: 'domain',
type: 'string',
required: true,
default: 'your-domain.eu.auth0.com',
},
{
displayName: 'Client ID',
name: 'clientId',
type: 'string',
required: true,
default: '',
},
{
displayName: 'Client Secret',
name: 'clientSecret',
type: 'string',
typeOptions: {
password: true,
},
required: true,
default: '',
},
];
async preAuthentication(this: IHttpRequestHelper, credentials: ICredentialDataDecryptedObject) {
const { access_token } = (await this.helpers.httpRequest({
method: 'POST',
url: `https://${credentials.domain}/oauth/token`,
body: {
client_id: credentials.clientId,
client_secret: credentials.clientSecret,
audience: `https://${credentials.domain}/api/v2/`,
grant_type: 'client_credentials',
},
headers: {
'Content-Type': 'application/json',
},
})) as { access_token: string };
return { sessionToken: access_token };
}
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '=Bearer {{$credentials.sessionToken}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: '=https://{{$credentials.domain}}',
url: '/api/v2/clients',
},
};
}
@@ -0,0 +1,19 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class AutopilotApi implements ICredentialType {
name = 'autopilotApi';
displayName = 'Autopilot API';
documentationUrl = 'autopilot';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
}
@@ -0,0 +1,102 @@
import type {
ICredentialDataDecryptedObject,
ICredentialType,
IHttpRequestOptions,
INodeProperties,
} from 'n8n-workflow';
import type { AwsIamCredentialsType, AWSRegion } from './common/aws/types';
import {
awsCredentialsTest,
awsGetSignInOptionsAndUpdateRequest,
signOptions,
} from './common/aws/utils';
import { awsCustomEndpoints, awsRegionProperty } from './common/aws/descriptions';
export class Aws implements ICredentialType {
name = 'aws';
displayName = 'AWS (IAM)';
documentationUrl = 'aws';
icon = { light: 'file:icons/AWS.svg', dark: 'file:icons/AWS.dark.svg' } as const;
properties: INodeProperties[] = [
awsRegionProperty,
{
displayName: 'Access Key ID',
name: 'accessKeyId',
type: 'string',
default: '',
},
{
displayName: 'Secret Access Key',
name: 'secretAccessKey',
type: 'string',
default: '',
typeOptions: {
password: true,
},
},
{
displayName: 'Temporary Security Credentials',
name: 'temporaryCredentials',
description: 'Support for temporary credentials from AWS STS',
type: 'boolean',
default: false,
},
{
displayName: 'Session Token',
name: 'sessionToken',
type: 'string',
displayOptions: {
show: {
temporaryCredentials: [true],
},
},
default: '',
typeOptions: {
password: true,
},
},
...awsCustomEndpoints,
];
async authenticate(
rawCredentials: ICredentialDataDecryptedObject,
requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> {
const credentials = rawCredentials as AwsIamCredentialsType;
const service = requestOptions.qs?.service as string;
const path = (requestOptions.qs?.path as string) ?? '';
const method = requestOptions.method;
let region = credentials.region;
if (requestOptions.qs?._region) {
region = requestOptions.qs._region as AWSRegion;
delete requestOptions.qs._region;
}
const { signOpts, url } = awsGetSignInOptionsAndUpdateRequest(
requestOptions,
credentials,
path,
method,
service,
region,
);
const securityHeaders = {
accessKeyId: `${credentials.accessKeyId}`.trim(),
secretAccessKey: `${credentials.secretAccessKey}`.trim(),
sessionToken: credentials.temporaryCredentials
? `${credentials.sessionToken}`.trim()
: undefined,
};
return signOptions(requestOptions, signOpts, securityHeaders, url, method);
}
test = awsCredentialsTest;
}
@@ -0,0 +1,172 @@
import type {
ICredentialDataDecryptedObject,
ICredentialType,
IHttpRequestOptions,
INodeProperties,
} from 'n8n-workflow';
import { ApplicationError } from 'n8n-workflow';
import { type AwsAssumeRoleCredentialsType, type AWSRegion } from './common/aws/types';
import { awsCustomEndpoints, awsRegionProperty } from './common/aws/descriptions';
import {
assumeRole,
awsCredentialsTest,
awsGetSignInOptionsAndUpdateRequest,
signOptions,
} from './common/aws/utils';
export class AwsAssumeRole implements ICredentialType {
name = 'awsAssumeRole';
displayName = 'AWS (Assume Role)';
documentationUrl = 'awsassumerole';
icon = { light: 'file:icons/AWS.svg', dark: 'file:icons/AWS.dark.svg' } as const;
properties: INodeProperties[] = [
awsRegionProperty,
{
displayName: 'Use System Credentials',
name: 'useSystemCredentialsForRole',
description:
'Use system credentials (environment variables, container role, etc.) to call STS.AssumeRole. Access to AWS system credentials is disabled by default and must be explicitly enabled. See <a href="https://docs.n8n.io/integrations/credentials/awsassumerole/">documentation</a> for more information.',
type: 'boolean',
default: false,
displayOptions: {
hideOnCloud: true,
},
},
{
displayName: 'STS Access Key ID',
name: 'stsAccessKeyId',
description: 'Access Key ID to use for the STS.AssumeRole call',
// eslint-disable-next-line n8n-nodes-base/cred-class-field-type-options-password-missing
type: 'string',
displayOptions: {
show: {
useSystemCredentialsForRole: [false],
},
},
required: true,
default: '',
},
{
displayName: 'STS Access Key Secret',
name: 'stsSecretAccessKey',
description: 'Secret Access Key to use for the STS.AssumeRole call',
type: 'string',
displayOptions: {
show: {
useSystemCredentialsForRole: [false],
},
},
required: true,
default: '',
typeOptions: {
password: true,
},
},
{
displayName: 'STS Session Token (optional)',
name: 'stsSessionToken',
description: 'Session Token to use for the STS.AssumeRole call',
type: 'string',
displayOptions: {
show: {
useSystemCredentialsForRole: [false],
},
},
default: '',
typeOptions: {
password: true,
},
},
{
displayName: 'Role ARN',
name: 'roleArn',
description: 'The ARN of the role to assume (e.g., arn:aws:iam::123456789012:role/MyRole)',
type: 'string',
required: true,
default: '',
placeholder: 'arn:aws:iam::123456789012:role/MyRole',
},
{
displayName: 'External ID',
name: 'externalId',
description:
"External ID for cross-account role assumption (should be required by your role's trust policy)",
type: 'string',
required: true,
default: '',
typeOptions: {
password: true,
},
},
{
displayName: 'Role Session Name',
name: 'roleSessionName',
description: 'Name for the role session',
type: 'string',
required: true,
default: 'n8n-session',
},
...awsCustomEndpoints,
];
async authenticate(
decryptedCredentials: ICredentialDataDecryptedObject,
requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> {
const credentials = decryptedCredentials as AwsAssumeRoleCredentialsType;
const service = requestOptions.qs?.service as string;
const path = (requestOptions.qs?.path as string) ?? '';
const method = requestOptions.method;
let region = credentials.region;
if (requestOptions.qs?._region) {
region = requestOptions.qs._region as AWSRegion;
delete requestOptions.qs._region;
}
let finalCredentials = credentials;
let securityHeaders: {
accessKeyId: string;
secretAccessKey: string;
sessionToken: string;
};
if (!credentials.roleArn || credentials.roleArn.trim() === '') {
throw new ApplicationError('Role ARN is required when assuming a role.');
}
if (!credentials.externalId || credentials.externalId.trim() === '') {
throw new ApplicationError('External ID is required when assuming a role.');
}
if (!credentials.roleSessionName || credentials.roleSessionName.trim() === '') {
throw new ApplicationError('Role Session Name is required when assuming a role.');
}
try {
securityHeaders = await assumeRole(credentials, region);
finalCredentials = { ...credentials, ...securityHeaders };
} catch (error) {
console.error('Failed to assume role:', error);
throw new ApplicationError(
`Failed to assume role: ${error instanceof Error ? error.message : 'Unknown error'}`,
);
}
const { signOpts, url } = awsGetSignInOptionsAndUpdateRequest(
requestOptions,
finalCredentials,
path,
method,
service,
region,
);
return signOptions(requestOptions, signOpts, securityHeaders, url, method);
}
test = awsCredentialsTest;
}
@@ -0,0 +1,38 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class AzureStorageOAuth2Api implements ICredentialType {
name = 'azureStorageOAuth2Api';
displayName = 'Azure Storage OAuth2 API';
extends = ['microsoftOAuth2Api'];
documentationUrl = 'azurestorage';
properties: INodeProperties[] = [
{
displayName: 'Account',
name: 'account',
type: 'string',
default: '',
},
{
displayName: 'Base URL',
name: 'baseUrl',
type: 'hidden',
default: '=https://{{ $self["account"] }}.blob.core.windows.net',
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden',
default: 'https://storage.azure.com/.default',
},
{
displayName: 'Microsoft Graph API Base URL',
name: 'graphApiBaseUrl',
type: 'hidden',
default: 'https://graph.microsoft.com',
},
];
}
@@ -0,0 +1,115 @@
import type {
ICredentialDataDecryptedObject,
ICredentialTestRequest,
ICredentialType,
IHttpRequestOptions,
INodeProperties,
} from 'n8n-workflow';
import { createHmac } from 'node:crypto';
import {
getCanonicalizedHeadersString,
getCanonicalizedResourceString,
HeaderConstants,
XMsVersion,
} from '../nodes/Microsoft/Storage/GenericFunctions';
export class AzureStorageSharedKeyApi implements ICredentialType {
name = 'azureStorageSharedKeyApi';
displayName = 'Azure Storage Shared Key API';
documentationUrl = 'azurestorage';
properties: INodeProperties[] = [
{
displayName: 'Account',
name: 'account',
description: 'Account name',
type: 'string',
default: '',
},
{
displayName: 'Key',
name: 'key',
description: 'Account key',
type: 'string',
typeOptions: {
password: true,
},
default: '',
},
{
displayName: 'Base URL',
name: 'baseUrl',
type: 'hidden',
default: '=https://{{ $self["account"] }}.blob.core.windows.net',
},
];
async authenticate(
credentials: ICredentialDataDecryptedObject,
requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> {
if (requestOptions.qs) {
for (const [key, value] of Object.entries(requestOptions.qs)) {
if (value === undefined) {
delete requestOptions.qs[key];
}
}
}
if (requestOptions.headers) {
for (const [key, value] of Object.entries(requestOptions.headers)) {
if (value === undefined) {
delete requestOptions.headers[key];
}
}
}
requestOptions.method ??= 'GET';
requestOptions.headers ??= {};
requestOptions.headers[HeaderConstants.X_MS_VERSION] ??= XMsVersion;
requestOptions.headers[HeaderConstants.X_MS_DATE] ??= new Date().toUTCString();
const stringToSign: string = [
requestOptions.method.toUpperCase(),
requestOptions.headers[HeaderConstants.CONTENT_LANGUAGE] ?? '',
requestOptions.headers[HeaderConstants.CONTENT_ENCODING] ?? '',
requestOptions.headers[HeaderConstants.CONTENT_LENGTH] ?? '',
requestOptions.headers[HeaderConstants.CONTENT_MD5] ?? '',
requestOptions.headers[HeaderConstants.CONTENT_TYPE] ?? '',
requestOptions.headers[HeaderConstants.DATE] ?? '',
requestOptions.headers[HeaderConstants.IF_MODIFIED_SINCE] ?? '',
requestOptions.headers[HeaderConstants.IF_MATCH] ?? '',
requestOptions.headers[HeaderConstants.IF_NONE_MATCH] ?? '',
requestOptions.headers[HeaderConstants.IF_UNMODIFIED_SINCE] ?? '',
requestOptions.headers[HeaderConstants.RANGE] ?? '',
getCanonicalizedHeadersString(requestOptions) +
getCanonicalizedResourceString(requestOptions, credentials),
].join('\n');
const signature: string = createHmac('sha256', Buffer.from(credentials.key as string, 'base64'))
.update(stringToSign, 'utf8')
.digest('base64');
requestOptions.headers[HeaderConstants.AUTHORIZATION] =
`SharedKey ${credentials.account as string}:${signature}`;
return requestOptions;
}
test: ICredentialTestRequest = {
request: {
baseURL: '={{$credentials.baseUrl}}',
url: '/',
headers: {
'x-ms-date': '={{ new Date().toUTCString() }}',
'x-ms-version': '2021-12-02',
},
qs: {
comp: 'list',
},
},
};
}
@@ -0,0 +1,25 @@
import type { 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',
typeOptions: { password: true },
default: '',
},
];
}
@@ -0,0 +1,19 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class BannerbearApi implements ICredentialType {
name = 'bannerbearApi';
displayName = 'Bannerbear API';
documentationUrl = 'bannerbear';
properties: INodeProperties[] = [
{
displayName: 'Project API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
}
@@ -0,0 +1,35 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
// https://api.baserow.io/api/redoc/#section/Authentication
export class BaserowApi implements ICredentialType {
name = 'baserowApi';
displayName = 'Baserow API';
documentationUrl = 'baserow';
properties: INodeProperties[] = [
{
displayName: 'Host',
name: 'host',
type: 'string',
default: 'https://api.baserow.io',
},
{
displayName: 'Username',
name: 'username',
type: 'string',
default: '',
},
{
displayName: 'Password',
name: 'password',
type: 'string',
default: '',
typeOptions: {
password: true,
},
},
];
}
@@ -0,0 +1,40 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class BeeminderApi implements ICredentialType {
name = 'beeminderApi';
displayName = 'Beeminder API';
documentationUrl = 'beeminder';
properties: INodeProperties[] = [
{
displayName: 'Auth Token',
name: 'authToken',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
body: {
auth_token: '={{$credentials.authToken}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: 'https://www.beeminder.com/api/v1',
url: '/users/me.json',
},
};
}
@@ -0,0 +1,52 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class BeeminderOAuth2Api implements ICredentialType {
name = 'beeminderOAuth2Api';
extends = ['oAuth2Api'];
displayName = 'Beeminder OAuth2 API';
documentationUrl = 'beeminder';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden',
default: 'https://www.beeminder.com/apps/authorize',
required: true,
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden',
default: 'https://www.beeminder.com/apps/authorize',
required: true,
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden',
default: 'response_type=token',
},
{
displayName: 'Authentication',
name: 'authentication',
type: 'hidden',
default: 'body',
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden',
default: '',
},
];
}
@@ -0,0 +1,52 @@
import type {
ICredentialDataDecryptedObject,
ICredentialTestRequest,
ICredentialType,
IHttpRequestOptions,
INodeProperties,
} from 'n8n-workflow';
export class BitbucketAccessTokenApi implements ICredentialType {
name = 'bitbucketAccessTokenApi';
displayName = 'Bitbucket Access Token API';
documentationUrl = 'bitbuckettokenapi';
properties: INodeProperties[] = [
{
displayName: 'Email',
name: 'email',
type: 'string',
default: '',
},
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
async authenticate(
credentials: ICredentialDataDecryptedObject,
requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> {
const encodedApiKey = Buffer.from(`${credentials.email}:${credentials.accessToken}`).toString(
'base64',
);
if (!requestOptions.headers) {
requestOptions.headers = {};
}
requestOptions.headers.Authorization = `Basic ${encodedApiKey}`;
return requestOptions;
}
test: ICredentialTestRequest = {
request: {
baseURL: 'https://api.bitbucket.org/2.0',
url: '/user',
},
};
}
@@ -0,0 +1,25 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class BitbucketApi implements ICredentialType {
name = 'bitbucketApi';
displayName = 'Bitbucket API';
documentationUrl = 'bitbucket';
properties: INodeProperties[] = [
{
displayName: 'Username',
name: 'username',
type: 'string',
default: '',
},
{
displayName: 'App Password',
name: 'appPassword',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
}
@@ -0,0 +1,19 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class BitlyApi implements ICredentialType {
name = 'bitlyApi';
displayName = 'Bitly API';
documentationUrl = 'bitly';
properties: INodeProperties[] = [
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
}
@@ -0,0 +1,72 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class BitlyOAuth2Api implements ICredentialType {
name = 'bitlyOAuth2Api';
displayName = 'Bitly OAuth2 API';
documentationUrl = 'bitly';
extends = ['oAuth2Api'];
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden',
default: 'https://bitly.com/oauth/authorize',
required: true,
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden',
default: 'https://api-ssl.bitly.com/oauth/access_token',
required: true,
},
{
displayName: 'Client ID',
name: 'clientId',
type: 'string',
default: '',
required: true,
},
{
displayName: 'Client Secret',
name: 'clientSecret',
type: 'string',
typeOptions: {
password: true,
},
default: '',
required: true,
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden',
default: '',
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden',
default: '',
description:
'For some services additional query parameters have to be set which can be defined here',
placeholder: '',
},
{
displayName: 'Authentication',
name: 'authentication',
type: 'hidden',
default: 'body',
},
];
}
@@ -0,0 +1,55 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
// https://bitwarden.com/help/article/public-api/#authentication
export class BitwardenApi implements ICredentialType {
name = 'bitwardenApi';
displayName = 'Bitwarden API';
documentationUrl = 'bitwarden';
properties: INodeProperties[] = [
{
displayName: 'Client ID',
name: 'clientId',
type: 'string',
default: '',
},
{
displayName: 'Client Secret',
name: 'clientSecret',
type: 'string',
typeOptions: { password: true },
default: '',
},
{
displayName: 'Environment',
name: 'environment',
type: 'options',
default: 'cloudHosted',
options: [
{
name: 'Cloud-Hosted',
value: 'cloudHosted',
},
{
name: 'Self-Hosted',
value: 'selfHosted',
},
],
},
{
displayName: 'Self-Hosted Domain',
name: 'domain',
type: 'string',
default: '',
placeholder: 'https://www.mydomain.com',
displayOptions: {
show: {
environment: ['selfHosted'],
},
},
},
];
}
@@ -0,0 +1,52 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class BoxOAuth2Api implements ICredentialType {
name = 'boxOAuth2Api';
extends = ['oAuth2Api'];
displayName = 'Box OAuth2 API';
documentationUrl = 'box';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden',
default: 'https://account.box.com/api/oauth2/authorize',
required: true,
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden',
default: 'https://api.box.com/oauth2/token',
required: true,
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden',
default: '',
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden',
default: '',
},
{
displayName: 'Authentication',
name: 'authentication',
type: 'hidden',
default: 'body',
},
];
}
@@ -0,0 +1,40 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class BrandfetchApi implements ICredentialType {
name = 'brandfetchApi';
displayName = 'Brandfetch API';
documentationUrl = 'brandfetch';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '=Bearer {{$credentials.apiKey}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: 'https://api.brandfetch.io',
url: '/v2/brands/brandfetch.com',
},
};
}
@@ -0,0 +1,41 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class BrevoApi implements ICredentialType {
// keep sendinblue name for backward compatibility
name = 'sendInBlueApi';
displayName = 'Brevo';
documentationUrl = 'brevo';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
'api-key': '={{$credentials.apiKey}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: 'https://api.brevo.com/v3',
url: '/account',
},
};
}
@@ -0,0 +1,69 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class BubbleApi implements ICredentialType {
name = 'bubbleApi';
displayName = 'Bubble API';
documentationUrl = 'bubble';
properties: INodeProperties[] = [
{
displayName: 'API Token',
name: 'apiToken',
type: 'string',
typeOptions: { password: true },
default: '',
},
{
displayName: 'App Name',
name: 'appName',
type: 'string',
default: '',
},
{
displayName: 'Environment',
name: 'environment',
type: 'options',
default: 'live',
options: [
{
name: 'Development',
value: 'development',
},
{
name: 'Live',
value: 'live',
},
],
},
{
displayName: 'Hosting',
name: 'hosting',
type: 'options',
default: 'bubbleHosted',
options: [
{
name: 'Bubble-Hosted',
value: 'bubbleHosted',
},
{
name: 'Self-Hosted',
value: 'selfHosted',
},
],
},
{
displayName: 'Domain',
name: 'domain',
type: 'string',
placeholder: 'mydomain.com',
default: '',
displayOptions: {
show: {
hosting: ['selfHosted'],
},
},
},
];
}
@@ -0,0 +1,46 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class CalApi implements ICredentialType {
name = 'calApi';
displayName = 'Cal API';
documentationUrl = 'cal';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
{
displayName: 'Host',
name: 'host',
type: 'string',
default: 'https://api.cal.com',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
qs: {
apiKey: '={{$credentials.apiKey}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: '={{$credentials.host}}',
url: '=/v1/memberships',
},
};
}
@@ -0,0 +1,57 @@
import type {
ICredentialDataDecryptedObject,
ICredentialTestRequest,
ICredentialType,
IHttpRequestOptions,
INodeProperties,
} from 'n8n-workflow';
const getAuthenticationType = (data: string): 'accessToken' | 'apiKey' => {
// The access token is a JWT, so it will always include dots to separate
// header, payoload and signature.
return data.includes('.') ? 'accessToken' : 'apiKey';
};
export class CalendlyApi implements ICredentialType {
name = 'calendlyApi';
displayName = 'Calendly API';
documentationUrl = 'calendly';
properties: INodeProperties[] = [
// Change name to Personal Access Token once API Keys
// are deprecated
{
displayName: 'API Key or Personal Access Token',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
async authenticate(
credentials: ICredentialDataDecryptedObject,
requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> {
//check whether the token is an API Key or an access token
const { apiKey } = credentials as { apiKey: string };
const tokenType = getAuthenticationType(apiKey);
// remove condition once v1 is deprecated
// and only inject credentials as an access token
if (tokenType === 'accessToken') {
requestOptions.headers!.Authorization = `Bearer ${apiKey}`;
} else {
requestOptions.headers!['X-TOKEN'] = apiKey;
}
return requestOptions;
}
test: ICredentialTestRequest = {
request: {
baseURL: 'https://calendly.com',
url: '/api/v1/users/me',
},
};
}
@@ -0,0 +1,52 @@
import type { ICredentialType, INodeProperties, Icon } from 'n8n-workflow';
export class CalendlyOAuth2Api implements ICredentialType {
name = 'calendlyOAuth2Api';
extends = ['oAuth2Api'];
displayName = 'Calendly OAuth2 API';
documentationUrl = 'calendly';
icon: Icon = 'file:icons/Calendly.svg';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden',
default: 'https://auth.calendly.com/oauth/authorize',
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden',
default: 'https://auth.calendly.com/oauth/token',
},
{
displayName: 'Authentication',
name: 'authentication',
type: 'hidden',
default: 'header',
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden',
default: '',
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden',
default: '',
},
];
}
@@ -0,0 +1,51 @@
import type { IAuthenticateGeneric, ICredentialType, INodeProperties } from 'n8n-workflow';
export class CarbonBlackApi implements ICredentialType {
name = 'carbonBlackApi';
displayName = 'Carbon Black API';
icon = { light: 'file:icons/vmware.svg', dark: 'file:icons/vmware.dark.svg' } as const;
documentationUrl = 'carbonblack';
httpRequestNode = {
name: 'Carbon Black',
docsUrl: 'https://developer.carbonblack.com/reference',
apiBaseUrl: '',
};
properties: INodeProperties[] = [
{
displayName: 'URL',
name: 'apiUrl',
type: 'string',
placeholder: 'https://defense.conferdeploy.net/',
default: '',
},
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string',
typeOptions: { password: true },
required: true,
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
'X-Auth-Token': '={{$credentials.accessToken}}',
},
},
};
// test: ICredentialTestRequest = {
// request: {
// baseURL: '={{$credentials.apiUrl}}',
// url: 'integrationServices/v3/auditlogs',
// },
// };
}
@@ -0,0 +1,25 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class ChargebeeApi implements ICredentialType {
name = 'chargebeeApi';
displayName = 'Chargebee API';
documentationUrl = 'chargebee';
properties: INodeProperties[] = [
{
displayName: 'Account Name',
name: 'accountName',
type: 'string',
default: '',
},
{
displayName: 'Api Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
}
@@ -0,0 +1,19 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class CircleCiApi implements ICredentialType {
name = 'circleCiApi';
displayName = 'CircleCI API';
documentationUrl = 'circleci';
properties: INodeProperties[] = [
{
displayName: 'Personal API Token',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
}
@@ -0,0 +1,44 @@
import type { IAuthenticateGeneric, ICredentialType, INodeProperties } from 'n8n-workflow';
export class CiscoMerakiApi implements ICredentialType {
name = 'ciscoMerakiApi';
displayName = 'Cisco Meraki API';
documentationUrl = 'ciscomeraki';
icon = { light: 'file:icons/Cisco.svg', dark: 'file:icons/Cisco.dark.svg' } as const;
httpRequestNode = {
name: 'Cisco Meraki',
docsUrl: 'https://developer.cisco.com/meraki/api/',
apiBaseUrl: 'https://api.meraki.com/api/v1/',
};
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
required: true,
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
'X-Cisco-Meraki-API-Key': '={{$credentials.apiKey}}',
},
},
};
// test: ICredentialTestRequest = {
// request: {
// baseURL: 'https://api.meraki.com/api/v1',
// url: '/organizations',
// },
// };
}
@@ -0,0 +1,122 @@
import axios from 'axios';
import type {
ICredentialDataDecryptedObject,
ICredentialTestRequest,
ICredentialType,
IHttpRequestOptions,
INodeProperties,
} from 'n8n-workflow';
export class CiscoSecureEndpointApi implements ICredentialType {
name = 'ciscoSecureEndpointApi';
displayName = 'Cisco Secure Endpoint (AMP) API';
documentationUrl = 'ciscosecureendpoint';
icon = { light: 'file:icons/Cisco.svg', dark: 'file:icons/Cisco.dark.svg' } as const;
httpRequestNode = {
name: 'Cisco Secure Endpoint',
docsUrl: 'https://developer.cisco.com/docs/secure-endpoint/',
apiBaseUrl: '',
};
properties: INodeProperties[] = [
{
displayName: 'Region',
name: 'region',
type: 'options',
options: [
{
name: 'Asia Pacific, Japan, and China',
value: 'apjc.amp',
},
{
name: 'Europe',
value: 'eu.amp',
},
{
name: 'North America',
value: 'amp',
},
],
default: 'amp',
},
{
displayName: 'Client ID',
name: 'clientId',
type: 'string',
default: '',
required: true,
},
{
displayName: 'Client Secret',
name: 'clientSecret',
type: 'string',
typeOptions: {
password: true,
},
default: '',
required: true,
},
];
async authenticate(
credentials: ICredentialDataDecryptedObject,
requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> {
const clientId = credentials.clientId as string;
const clientSecret = credentials.clientSecret as string;
const region = credentials.region as string;
const secureXToken = await axios({
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
},
auth: {
username: clientId,
password: clientSecret,
},
method: 'POST',
data: new URLSearchParams({
grant_type: 'client_credentials',
}).toString(),
url: `https://visibility.${region}.cisco.com/iroh/oauth2/token`,
});
const secureEndpointToken = await axios({
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
Authorization: `Bearer ${secureXToken.data.access_token}`,
},
method: 'POST',
data: new URLSearchParams({
grant_type: 'client_credentials',
}).toString(),
url: `https://api.${region}.cisco.com/v3/access_tokens`,
});
const requestOptionsWithAuth: IHttpRequestOptions = {
...requestOptions,
headers: {
...requestOptions.headers,
Authorization: `Bearer ${secureEndpointToken.data.access_token}`,
},
};
return requestOptionsWithAuth;
}
test: ICredentialTestRequest = {
request: {
baseURL: '=https://api.{{$credentials.region}}.cisco.com',
url: '/v3/organizations',
qs: {
size: 10,
},
},
};
}
@@ -0,0 +1,89 @@
import type {
IAuthenticateGeneric,
ICredentialDataDecryptedObject,
ICredentialTestRequest,
ICredentialType,
IHttpRequestHelper,
INodeProperties,
} from 'n8n-workflow';
export class CiscoUmbrellaApi implements ICredentialType {
name = 'ciscoUmbrellaApi';
displayName = 'Cisco Umbrella API';
documentationUrl = 'ciscoumbrella';
icon = { light: 'file:icons/Cisco.svg', dark: 'file:icons/Cisco.dark.svg' } as const;
httpRequestNode = {
name: 'Cisco Umbrella',
docsUrl: 'https://developer.cisco.com/docs/cloud-security/',
apiBaseUrl: 'https://api.umbrella.com/',
};
properties: INodeProperties[] = [
{
displayName: 'Session Token',
name: 'sessionToken',
type: 'hidden',
typeOptions: {
expirable: true,
},
default: '',
},
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
required: true,
default: '',
},
{
displayName: 'Secret',
name: 'secret',
type: 'string',
typeOptions: {
password: true,
},
required: true,
default: '',
},
];
async preAuthentication(this: IHttpRequestHelper, credentials: ICredentialDataDecryptedObject) {
const url = 'https://api.umbrella.com';
const { access_token } = (await this.helpers.httpRequest({
method: 'POST',
url: `${
url.endsWith('/') ? url.slice(0, -1) : url
}/auth/v2/token?grant_type=client_credentials`,
auth: {
username: credentials.apiKey as string,
password: credentials.secret as string,
},
headers: {
'Content-Type': 'x-www-form-urlencoded',
},
})) as { access_token: string };
return { sessionToken: access_token };
}
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '=Bearer {{$credentials.sessionToken}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: 'https://api.umbrella.com',
url: '/users',
},
};
}
@@ -0,0 +1,55 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class CiscoWebexOAuth2Api implements ICredentialType {
name = 'ciscoWebexOAuth2Api';
extends = ['oAuth2Api'];
displayName = 'Cisco Webex OAuth2 API';
documentationUrl = 'ciscowebex';
icon = { light: 'file:icons/Cisco.svg', dark: 'file:icons/Cisco.dark.svg' } as const;
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden',
default: 'https://webexapis.com/v1/authorize',
required: true,
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden',
default: 'https://webexapis.com/v1/access_token',
required: true,
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden',
default:
'spark:memberships_read meeting:recordings_read spark:kms meeting:schedules_read spark:rooms_read spark:messages_write spark:memberships_write meeting:recordings_write meeting:preferences_read spark:messages_read meeting:schedules_write',
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden',
default: '',
},
{
displayName: 'Authentication',
name: 'authentication',
type: 'hidden',
default: 'body',
},
];
}
@@ -0,0 +1,19 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class ClearbitApi implements ICredentialType {
name = 'clearbitApi';
displayName = 'Clearbit API';
documentationUrl = 'clearbit';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
}
@@ -0,0 +1,40 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class ClickUpApi implements ICredentialType {
name = 'clickUpApi';
displayName = 'ClickUp API';
documentationUrl = 'clickup';
properties: INodeProperties[] = [
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '={{$credentials.accessToken}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: 'https://api.clickup.com/api/v2',
url: '/team',
},
};
}
@@ -0,0 +1,52 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class ClickUpOAuth2Api implements ICredentialType {
name = 'clickUpOAuth2Api';
extends = ['oAuth2Api'];
displayName = 'ClickUp OAuth2 API';
documentationUrl = 'clickup';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden',
default: 'https://app.clickup.com/api',
required: true,
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden',
default: 'https://api.clickup.com/api/v2/oauth/token',
required: true,
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden',
default: '',
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden',
default: '',
},
{
displayName: 'Authentication',
name: 'authentication',
type: 'hidden',
default: 'body',
},
];
}
@@ -0,0 +1,40 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class ClockifyApi implements ICredentialType {
name = 'clockifyApi';
displayName = 'Clockify API';
documentationUrl = 'clockify';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
'X-Api-Key': '={{$credentials.apiKey}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: 'https://api.clockify.me/api/v1',
url: '/workspaces',
},
};
}
@@ -0,0 +1,39 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class CloudflareApi implements ICredentialType {
name = 'cloudflareApi';
displayName = 'Cloudflare API';
documentationUrl = 'cloudflare';
properties: INodeProperties[] = [
{
displayName: 'API Token',
name: 'apiToken',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '=Bearer {{$credentials.apiToken}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: 'https://api.cloudflare.com/client/v4/user/tokens/verify',
},
};
}
@@ -0,0 +1,26 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class CockpitApi implements ICredentialType {
name = 'cockpitApi';
displayName = 'Cockpit API';
documentationUrl = 'cockpit';
properties: INodeProperties[] = [
{
displayName: 'Cockpit URL',
name: 'url',
type: 'string',
default: '',
placeholder: 'https://example.com',
},
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
}
@@ -0,0 +1,28 @@
import type { ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
export class CodaApi implements ICredentialType {
name = 'codaApi';
displayName = 'Coda API';
documentationUrl = 'coda';
properties: INodeProperties[] = [
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
test: ICredentialTestRequest = {
request: {
baseURL: 'https://coda.io/apis/v1/whoami',
headers: {
Authorization: '=Bearer {{$credentials.accessToken}}',
},
},
};
}
@@ -0,0 +1,39 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
//https://www.contentful.com/developers/docs/references/authentication/
export class ContentfulApi implements ICredentialType {
name = 'contentfulApi';
displayName = 'Contentful API';
documentationUrl = 'contentful';
properties: INodeProperties[] = [
{
displayName: 'Space ID',
name: 'spaceId',
type: 'string',
default: '',
required: true,
description: 'The ID for the Contentful space',
},
{
displayName: 'Content Delivery API Access Token',
name: 'ContentDeliveryaccessToken',
type: 'string',
typeOptions: { password: true },
default: '',
description:
'Access token that has access to the space. Can be left empty if only Delivery API should be used.',
},
{
displayName: 'Content Preview API Access Token',
name: 'ContentPreviewaccessToken',
type: 'string',
typeOptions: { password: true },
default: '',
description:
'Access token that has access to the space. Can be left empty if only Preview API should be used.',
},
];
}
@@ -0,0 +1,62 @@
import type {
IAuthenticateGeneric,
Icon,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class ConvertApi implements ICredentialType {
name = 'convertApi';
displayName = 'ConvertAPI';
documentationUrl = 'convertapi';
icon: Icon = 'file:icons/ConvertApi.png';
httpRequestNode = {
name: 'ConvertAPI',
docsUrl: 'https://docs.convertapi.com/docs/getting-started',
apiBaseUrl: 'https://v2.convertapi.com/',
};
properties: INodeProperties[] = [
{
displayName: 'API Token',
name: 'apiToken',
type: 'string',
typeOptions: {
password: true,
},
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '=Bearer {{$credentials.apiToken}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: 'https://v2.convertapi.com',
url: '/convert/docx/to/pdf',
ignoreHttpStatusErrors: true,
},
rules: [
{
type: 'responseSuccessBody',
properties: {
key: 'Code',
value: 4013,
message: 'API Token or Secret is invalid.',
},
},
],
};
}
@@ -0,0 +1,54 @@
import type {
ICredentialDataDecryptedObject,
ICredentialTestRequest,
ICredentialType,
IHttpRequestOptions,
INodeProperties,
} from 'n8n-workflow';
import { getUrl } from './common/http';
export class ConvertKitApi implements ICredentialType {
name = 'convertKitApi';
displayName = 'ConvertKit API';
documentationUrl = 'convertkit';
properties: INodeProperties[] = [
{
displayName: 'API Secret',
name: 'apiSecret',
type: 'string',
default: '',
typeOptions: {
password: true,
},
},
];
async authenticate(credentials: ICredentialDataDecryptedObject, options: IHttpRequestOptions) {
const url = getUrl(options);
const secret = {
api_secret: credentials.apiSecret as string,
};
// it's a webhook so include the api secret on the body
if (url?.includes('/automations/hooks')) {
options.body = options.body || {};
if (typeof options.body === 'object') {
Object.assign(options.body, secret);
}
} else {
options.qs = options.qs || {};
if (typeof options.qs === 'object') {
Object.assign(options.qs, secret);
}
}
return options;
}
test: ICredentialTestRequest = {
request: {
url: 'https://api.convertkit.com/v3/account',
},
};
}
@@ -0,0 +1,51 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class CopperApi implements ICredentialType {
name = 'copperApi';
displayName = 'Copper API';
documentationUrl = 'copper';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
required: true,
type: 'string',
typeOptions: { password: true },
default: '',
},
{
displayName: 'Email',
name: 'email',
required: true,
type: 'string',
placeholder: 'name@email.com',
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
'X-PW-AccessToken': '={{$credentials.apiKey}}',
'X-PW-Application': 'developer_api',
'X-PW-UserEmail': '={{$credentials.email}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: 'https://api.copper.com/developer_api/v1/',
url: 'users/me',
},
};
}
@@ -0,0 +1,48 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class CortexApi implements ICredentialType {
name = 'cortexApi';
displayName = 'Cortex API';
documentationUrl = 'cortex';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'cortexApiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
{
displayName: 'Cortex Instance',
name: 'host',
type: 'string',
description: 'The URL of the Cortex instance',
default: '',
placeholder: 'https://localhost:9001',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '=Bearer {{$credentials.cortexApiKey}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: '={{$credentials.host}}',
url: '/api/analyzer',
},
};
}
@@ -0,0 +1,65 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class CrateDb implements ICredentialType {
name = 'crateDb';
displayName = 'CrateDB';
documentationUrl = 'cratedb';
properties: INodeProperties[] = [
{
displayName: 'Host',
name: 'host',
type: 'string',
default: 'localhost',
},
{
displayName: 'Database',
name: 'database',
type: 'string',
default: 'doc',
},
{
displayName: 'User',
name: 'user',
type: 'string',
default: 'crate',
},
{
displayName: 'Password',
name: 'password',
type: 'string',
typeOptions: {
password: true,
},
default: '',
},
{
displayName: 'SSL',
name: 'ssl',
type: 'options',
options: [
{
name: 'Allow',
value: 'allow',
},
{
name: 'Disable',
value: 'disable',
},
{
name: 'Require',
value: 'require',
},
],
default: 'disable',
},
{
displayName: 'Port',
name: 'port',
type: 'number',
default: 5432,
},
];
}
@@ -0,0 +1,93 @@
import type {
IAuthenticateGeneric,
ICredentialDataDecryptedObject,
ICredentialTestRequest,
ICredentialType,
IHttpRequestHelper,
INodeProperties,
} from 'n8n-workflow';
export class CrowdStrikeOAuth2Api implements ICredentialType {
name = 'crowdStrikeOAuth2Api';
displayName = 'CrowdStrike OAuth2 API';
documentationUrl = 'crowdstrike';
icon = { light: 'file:icons/CrowdStrike.svg', dark: 'file:icons/CrowdStrike.dark.svg' } as const;
httpRequestNode = {
name: 'CrowdStrike',
docsUrl: 'https://developer.crowdstrike.com/',
apiBaseUrl: '',
};
properties: INodeProperties[] = [
{
displayName: 'Session Token',
name: 'sessionToken',
type: 'hidden',
typeOptions: {
expirable: true,
},
default: '',
},
{
displayName: 'URL',
name: 'url',
type: 'string',
required: true,
default: '',
},
{
displayName: 'Client ID',
name: 'clientId',
type: 'string',
required: true,
default: '',
},
{
displayName: 'Client Secret',
name: 'clientSecret',
type: 'string',
typeOptions: {
password: true,
},
required: true,
default: '',
},
];
async preAuthentication(this: IHttpRequestHelper, credentials: ICredentialDataDecryptedObject) {
const url = credentials.url as string;
const { access_token } = (await this.helpers.httpRequest({
method: 'POST',
url: `${url.endsWith('/') ? url.slice(0, -1) : url}/oauth2/token`,
body: {
client_id: credentials.clientId,
client_secret: credentials.clientSecret,
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
})) as { access_token: string };
return { sessionToken: access_token };
}
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '=Bearer {{$credentials.sessionToken}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: '={{$credentials?.url}}',
url: 'user-management/queries/users/v1',
},
};
}
@@ -0,0 +1,37 @@
import type { ICredentialType, INodeProperties, Icon, ThemeIconColor } from 'n8n-workflow';
export class Crypto implements ICredentialType {
name = 'crypto';
displayName = 'Crypto';
documentationUrl = 'crypto';
icon: Icon = 'fa:key';
iconColor: ThemeIconColor = 'green';
properties: INodeProperties[] = [
{
displayName: 'Hmac Secret',
name: 'hmacSecret',
type: 'string',
description: 'Secret used in the Hmac action',
typeOptions: {
password: true,
},
default: '',
},
{
displayName: 'Private Key',
name: 'signPrivateKey',
type: 'string',
description: 'Private Key used in the Sign action',
typeOptions: {
rows: 4,
password: true,
},
default: '',
},
];
}
@@ -0,0 +1,43 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class CurrentsApi implements ICredentialType {
name = 'currentsApi';
displayName = 'Currents API';
documentationUrl = 'https://docs.currents.dev/api';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
required: true,
description: 'API key from Currents Dashboard (Organization > API & Record Keys)',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '=Bearer {{$credentials.apiKey}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: 'https://api.currents.dev/v1',
url: '/projects',
method: 'GET',
},
};
}
@@ -0,0 +1,93 @@
import { ApplicationError } from '@n8n/errors';
import type {
ICredentialDataDecryptedObject,
ICredentialType,
IHttpRequestOptions,
INodeProperties,
} from 'n8n-workflow';
export class CustomerIoApi implements ICredentialType {
name = 'customerIoApi';
displayName = 'Customer.io API';
documentationUrl = 'customerio';
properties: INodeProperties[] = [
{
displayName: 'Tracking API Key',
name: 'trackingApiKey',
type: 'string',
typeOptions: { password: true },
default: '',
description: 'Required for tracking API',
required: true,
},
{
displayName: 'Region',
name: 'region',
type: 'options',
options: [
{
name: 'EU region',
value: 'track-eu.customer.io',
},
{
name: 'Global region',
value: 'track.customer.io',
},
],
default: 'track.customer.io',
description: 'Should be set based on your account region',
hint: 'The region will be omitted when being used with the HTTP node',
required: true,
},
{
displayName: 'Tracking Site ID',
name: 'trackingSiteId',
type: 'string',
default: '',
description: 'Required for tracking API',
},
{
displayName: 'App API Key',
name: 'appApiKey',
type: 'string',
typeOptions: { password: true },
default: '',
description: 'Required for App API',
},
];
async authenticate(
credentials: ICredentialDataDecryptedObject,
requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> {
// @ts-ignore
const url = new URL(requestOptions.url ? requestOptions.url : requestOptions.uri);
if (
url.hostname === 'track.customer.io' ||
url.hostname === 'track-eu.customer.io' ||
url.hostname === 'api.customer.io' ||
url.hostname === 'api-eu.customer.io'
) {
const basicAuthKey = Buffer.from(
`${credentials.trackingSiteId}:${credentials.trackingApiKey}`,
).toString('base64');
// @ts-ignore
Object.assign(requestOptions.headers, { Authorization: `Basic ${basicAuthKey}` });
} else if (
url.hostname === 'beta-api.customer.io' ||
url.hostname === 'beta-api-eu.customer.io'
) {
// @ts-ignore
Object.assign(requestOptions.headers, {
Authorization: `Bearer ${credentials.appApiKey as string}`,
});
} else {
throw new ApplicationError('Unknown way of authenticating', { level: 'warning' });
}
return requestOptions;
}
}
@@ -0,0 +1,73 @@
import type {
ICredentialDataDecryptedObject,
ICredentialTestRequest,
ICredentialType,
IHttpRequestOptions,
INodeProperties,
} from 'n8n-workflow';
export class DatadogApi implements ICredentialType {
name = 'datadogApi';
displayName = 'Datadog API';
documentationUrl = 'datadog';
icon = { light: 'file:icons/Datadog.svg', dark: 'file:icons/Datadog.svg' } as const;
httpRequestNode = {
name: 'Datadog',
docsUrl: 'https://docs.datadoghq.com/api/latest/',
apiBaseUrlPlaceholder: 'https://api.datadoghq.com/api/v1/metrics',
};
properties: INodeProperties[] = [
{
displayName: 'URL',
name: 'url',
required: true,
type: 'string',
default: 'https://api.datadoghq.com',
},
{
displayName: 'API Key',
name: 'apiKey',
required: true,
type: 'string',
typeOptions: { password: true },
default: '',
},
{
displayName: 'APP Key',
name: 'appKey',
required: false,
type: 'string',
default: '',
typeOptions: { password: true },
description: 'For some endpoints, you also need an Application key.',
},
];
async authenticate(
credentials: ICredentialDataDecryptedObject,
requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> {
requestOptions.headers = {
'DD-API-KEY': credentials.apiKey,
'DD-APPLICATION-KEY': credentials.appKey,
};
if (!requestOptions.headers['DD-APPLICATION-KEY']) {
delete requestOptions.headers['DD-APPLICATION-KEY'];
}
return requestOptions;
}
test: ICredentialTestRequest = {
request: {
baseURL: '={{$credentials.url}}',
url: '/api/v1/validate',
method: 'GET',
},
};
}
@@ -0,0 +1,57 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class DeepLApi implements ICredentialType {
name = 'deepLApi';
displayName = 'DeepL API';
documentationUrl = 'deepl';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
{
displayName: 'API Plan',
name: 'apiPlan',
type: 'options',
options: [
{
name: 'Pro Plan',
value: 'pro',
},
{
name: 'Free Plan',
value: 'free',
},
],
default: 'pro',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
qs: {
auth_key: '={{$credentials.apiKey}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL:
'={{$credentials.apiPlan === "pro" ? "https://api.deepl.com/v2" : "https://api-free.deepl.com/v2" }}',
url: '/usage',
},
};
}
@@ -0,0 +1,26 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class DemioApi implements ICredentialType {
name = 'demioApi';
displayName = 'Demio API';
documentationUrl = 'demio';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
{
displayName: 'API Secret',
name: 'apiSecret',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
}
@@ -0,0 +1,67 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class DfirIrisApi implements ICredentialType {
name = 'dfirIrisApi';
displayName = 'DFIR-IRIS API';
documentationUrl = 'dfiriris';
icon = { light: 'file:icons/DfirIris.svg', dark: 'file:icons/DfirIris.svg' } as const;
httpRequestNode = {
name: 'DFIR-IRIS',
docsUrl: 'https://docs.dfir-iris.org/operations/api/',
apiBaseUrlPlaceholder: 'http://<yourserver_ip>/manage/cases/list',
};
properties: INodeProperties[] = [
{
displayName: 'Base URL',
name: 'baseUrl',
type: 'string',
default: '',
placeholder: 'e.g. https://localhost',
description:
'The API endpoints are reachable on the same Address and port as the web interface.',
required: true,
},
{
displayName: 'API Key',
name: 'apiKey',
required: true,
type: 'string',
typeOptions: { password: true },
default: '',
},
{
displayName: 'Ignore SSL Issues (Insecure)',
name: 'skipSslCertificateValidation',
type: 'boolean',
default: false,
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '=Bearer {{$credentials.apiKey}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: '={{$credentials.baseUrl}}',
url: '/api/ping',
method: 'GET',
skipSslCertificateValidation: '={{$credentials.skipSslCertificateValidation}}',
},
};
}
@@ -0,0 +1,19 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class DhlApi implements ICredentialType {
name = 'dhlApi';
displayName = 'DHL API';
documentationUrl = 'dhl';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
}
@@ -0,0 +1,43 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class DiscordBotApi implements ICredentialType {
name = 'discordBotApi';
displayName = 'Discord Bot API';
documentationUrl = 'discord';
properties: INodeProperties[] = [
{
displayName: 'Bot Token',
name: 'botToken',
type: 'string',
default: '',
required: true,
typeOptions: {
password: true,
},
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '=Bot {{$credentials.botToken}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: 'https://discord.com/api/v10/',
url: '/users/@me/guilds',
},
};
}
@@ -0,0 +1,90 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
const defaultScopes = ['identify', 'guilds', 'guilds.join', 'bot'];
export class DiscordOAuth2Api implements ICredentialType {
name = 'discordOAuth2Api';
extends = ['oAuth2Api'];
displayName = 'Discord OAuth2 API';
documentationUrl = 'discord';
properties: INodeProperties[] = [
{
displayName: 'Bot Token',
name: 'botToken',
type: 'string',
default: '',
typeOptions: {
password: true,
},
},
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden',
default: 'https://discord.com/api/oauth2/authorize',
required: true,
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden',
default: 'https://discord.com/api/oauth2/token',
required: true,
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden',
default: 'permissions=1642758929655',
},
{
displayName: 'Custom Scopes',
name: 'customScopes',
type: 'boolean',
default: false,
description: 'Define custom scopes',
},
{
displayName:
'The default scopes needed for the node to work are already set, If you change these the node may not function correctly.',
name: 'customScopesNotice',
type: 'notice',
default: '',
displayOptions: {
show: {
customScopes: [true],
},
},
},
{
displayName: 'Enabled Scopes',
name: 'enabledScopes',
type: 'string',
displayOptions: {
show: {
customScopes: [true],
},
},
default: defaultScopes.join(' '),
description: 'Scopes that should be enabled',
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden',
default:
'={{$self["customScopes"] ? $self["enabledScopes"] : "' + defaultScopes.join(' ') + '"}}',
},
];
}
@@ -0,0 +1,29 @@
import type { ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
export class DiscordWebhookApi implements ICredentialType {
name = 'discordWebhookApi';
displayName = 'Discord Webhook';
documentationUrl = 'discord';
properties: INodeProperties[] = [
{
displayName: 'Webhook URL',
name: 'webhookUri',
type: 'string',
required: true,
default: '',
placeholder: 'https://discord.com/api/webhooks/ID/TOKEN',
typeOptions: {
password: true,
},
},
];
test: ICredentialTestRequest = {
request: {
baseURL: '={{ $credentials.webhookUri }}',
},
};
}
@@ -0,0 +1,64 @@
import type {
ICredentialDataDecryptedObject,
ICredentialTestRequest,
ICredentialType,
IHttpRequestOptions,
INodeProperties,
} from 'n8n-workflow';
export class DiscourseApi implements ICredentialType {
name = 'discourseApi';
displayName = 'Discourse API';
documentationUrl = 'discourse';
properties: INodeProperties[] = [
{
displayName: 'URL',
name: 'url',
required: true,
type: 'string',
default: '',
},
{
displayName: 'API Key',
name: 'apiKey',
required: true,
type: 'string',
typeOptions: { password: true },
default: '',
},
{
displayName: 'Username',
name: 'username',
required: true,
type: 'string',
default: '',
},
];
async authenticate(
credentials: ICredentialDataDecryptedObject,
requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> {
requestOptions.headers = {
'Api-Key': credentials.apiKey,
'Api-Username': credentials.username,
};
if (requestOptions.method === 'GET') {
delete requestOptions.body;
}
return requestOptions;
}
test: ICredentialTestRequest = {
request: {
baseURL: '={{$credentials.url}}',
url: '/groups.json',
method: 'GET',
},
};
}
@@ -0,0 +1,21 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class DisqusApi implements ICredentialType {
name = 'disqusApi';
displayName = 'Disqus API';
documentationUrl = 'disqus';
properties: INodeProperties[] = [
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string',
typeOptions: { password: true },
default: '',
description:
'Visit your account details page, and grab the Access Token. See <a href="https://disqus.com/api/docs/auth/">Disqus auth</a>.',
},
];
}
@@ -0,0 +1,21 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class DriftApi implements ICredentialType {
name = 'driftApi';
displayName = 'Drift API';
documentationUrl = 'drift';
properties: INodeProperties[] = [
{
displayName: 'Personal Access Token',
name: 'accessToken',
type: 'string',
typeOptions: { password: true },
default: '',
description:
'Visit your account details page, and grab the Access Token. See <a href="https://devdocs.drift.com/docs/quick-start">Drift auth</a>.',
},
];
}
@@ -0,0 +1,52 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class DriftOAuth2Api implements ICredentialType {
name = 'driftOAuth2Api';
extends = ['oAuth2Api'];
displayName = 'Drift OAuth2 API';
documentationUrl = 'drift';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden',
default: 'https://dev.drift.com/authorize',
required: true,
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden',
default: 'https://driftapi.com/oauth2/token',
required: true,
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden',
default: '',
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden',
default: '',
},
{
displayName: 'Authentication',
name: 'authentication',
type: 'hidden',
default: 'body',
},
];
}
@@ -0,0 +1,57 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class DropboxApi implements ICredentialType {
name = 'dropboxApi';
displayName = 'Dropbox API';
documentationUrl = 'dropbox';
properties: INodeProperties[] = [
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string',
typeOptions: { password: true },
default: '',
},
{
displayName: 'APP Access Type',
name: 'accessType',
type: 'options',
options: [
{
name: 'App Folder',
value: 'folder',
},
{
name: 'Full Dropbox',
value: 'full',
},
],
default: 'full',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '=Bearer {{$credentials.accessToken}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: 'https://api.dropboxapi.com/2',
url: '/users/get_current_account',
method: 'POST',
},
};
}
@@ -0,0 +1,70 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
const scopes = ['files.content.write', 'files.content.read', 'sharing.read', 'account_info.read'];
export class DropboxOAuth2Api implements ICredentialType {
name = 'dropboxOAuth2Api';
extends = ['oAuth2Api'];
displayName = 'Dropbox OAuth2 API';
documentationUrl = 'dropbox';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden',
default: 'https://www.dropbox.com/oauth2/authorize',
required: true,
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden',
default: 'https://api.dropboxapi.com/oauth2/token',
required: true,
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden',
default: scopes.join(' '),
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden',
default: 'token_access_type=offline&force_reapprove=true',
},
{
displayName: 'Authentication',
name: 'authentication',
type: 'hidden',
default: 'header',
},
{
displayName: 'APP Access Type',
name: 'accessType',
type: 'options',
options: [
{
name: 'App Folder',
value: 'folder',
},
{
name: 'Full Dropbox',
value: 'full',
},
],
default: 'full',
},
];
}
@@ -0,0 +1,45 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class DropcontactApi implements ICredentialType {
name = 'dropcontactApi';
displayName = 'Dropcontact API';
documentationUrl = 'dropcontact';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
'user-agent': 'n8n',
'X-Access-Token': '={{$credentials.apiKey}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: 'https://api.dropcontact.io',
url: '/batch',
method: 'POST',
body: {
data: [{ email: '' }],
},
},
};
}
@@ -0,0 +1,37 @@
import type { IAuthenticateGeneric, ICredentialType, INodeProperties } from 'n8n-workflow';
export class DynatraceApi implements ICredentialType {
name = 'dynatraceApi';
displayName = 'DynatraceAPI';
documentationUrl = 'dynatrace';
icon = { light: 'file:icons/Dynatrace.svg', dark: 'file:icons/Dynatrace.svg' } as const;
httpRequestNode = {
name: 'Dynatrace',
docsUrl: 'https://docs.dynatrace.com/docs/dynatrace-api',
apiBaseUrlPlaceholder: 'https://{your-environment-id}.live.dynatrace.com/api/v2/events',
};
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
required: true,
type: 'string',
typeOptions: { password: true },
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '=Api-Token {{$credentials.apiKey}}',
},
},
};
}
@@ -0,0 +1,131 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class ERPNextApi implements ICredentialType {
name = 'erpNextApi';
displayName = 'ERPNext API';
documentationUrl = 'erpnext';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
{
displayName: 'API Secret',
name: 'apiSecret',
type: 'string',
typeOptions: { password: true },
default: '',
},
{
displayName: 'Environment',
name: 'environment',
type: 'options',
default: 'cloudHosted',
options: [
{
name: 'Cloud-Hosted',
value: 'cloudHosted',
},
{
name: 'Self-Hosted',
value: 'selfHosted',
},
],
},
{
displayName: 'Subdomain',
name: 'subdomain',
type: 'string',
default: '',
placeholder: 'n8n',
description:
'Subdomain of cloud-hosted ERPNext instance. For example, "n8n" is the subdomain in: <code>https://n8n.erpnext.com</code>',
displayOptions: {
show: {
environment: ['cloudHosted'],
},
},
},
{
displayName: 'Domain',
name: 'domain',
type: 'options',
default: 'erpnext.com',
options: [
{
name: 'erpnext.com',
value: 'erpnext.com',
},
{
name: 'frappe.cloud',
value: 'frappe.cloud',
},
],
description: 'Domain for your cloud hosted ERPNext instance.',
displayOptions: {
show: {
environment: ['cloudHosted'],
},
},
},
{
displayName: 'Domain',
name: 'domain',
type: 'string',
default: '',
placeholder: 'https://www.mydomain.com',
description: 'Fully qualified domain name of self-hosted ERPNext instance',
displayOptions: {
show: {
environment: ['selfHosted'],
},
},
},
{
displayName: 'Ignore SSL Issues (Insecure)',
name: 'allowUnauthorizedCerts',
type: 'boolean',
description: 'Whether to connect even if SSL certificate validation is not possible',
default: false,
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '=token {{$credentials.apiKey}}:{{$credentials.apiSecret}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL:
'={{$credentials.environment === "cloudHosted" ? "https://" + $credentials.subdomain + "." + $credentials.domain : $credentials.domain}}',
url: '/api/method/frappe.auth.get_logged_user',
skipSslCertificateValidation: '={{ $credentials.allowUnauthorizedCerts }}',
},
rules: [
{
type: 'responseSuccessBody',
properties: {
key: 'message',
value: undefined,
message: 'Unable to authenticate, Check the credentials and the url',
},
},
],
};
}
@@ -0,0 +1,22 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class EgoiApi implements ICredentialType {
name = 'egoiApi';
displayName = 'E-Goi API';
documentationUrl = 'egoi';
properties: INodeProperties[] = [
// The credentials to get from user and save encrypted.
// Properties can be defined exactly in the same way
// as node properties.
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
}
@@ -0,0 +1,111 @@
import type {
ICredentialDataDecryptedObject,
ICredentialTestRequest,
ICredentialType,
IHttpRequestOptions,
INodeProperties,
} from 'n8n-workflow';
export class ElasticSecurityApi implements ICredentialType {
name = 'elasticSecurityApi';
displayName = 'Elastic Security API';
documentationUrl = 'elasticsecurity';
properties: INodeProperties[] = [
{
displayName: 'Base URL',
name: 'baseUrl',
type: 'string',
default: '',
placeholder: 'e.g. https://mydeployment.kb.us-central1.gcp.cloud.es.io:9243',
description: "Referred to as Kibana 'endpoint' in the Elastic deployment dashboard",
required: true,
},
{
displayName: 'Type',
name: 'type',
type: 'options',
options: [
{
name: 'API Key',
value: 'apiKey',
},
{
name: 'Basic Auth',
value: 'basicAuth',
},
],
default: 'basicAuth',
},
{
displayName: 'Username',
name: 'username',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
type: ['basicAuth'],
},
},
},
{
displayName: 'Password',
name: 'password',
type: 'string',
typeOptions: {
password: true,
},
default: '',
required: true,
displayOptions: {
show: {
type: ['basicAuth'],
},
},
},
{
displayName: 'API Key',
name: 'apiKey',
required: true,
type: 'string',
typeOptions: { password: true },
default: '',
displayOptions: {
show: {
type: ['apiKey'],
},
},
},
];
async authenticate(
credentials: ICredentialDataDecryptedObject,
requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> {
if (credentials.type === 'apiKey') {
requestOptions.headers = {
Authorization: `ApiKey ${credentials.apiKey}`,
};
} else {
requestOptions.auth = {
username: credentials.username as string,
password: credentials.password as string,
};
requestOptions.headers = {
'kbn-xsrf': true,
};
}
return requestOptions;
}
test: ICredentialTestRequest = {
request: {
baseURL: '={{$credentials.baseUrl}}',
url: '/api/endpoint/metadata',
method: 'GET',
},
};
}
@@ -0,0 +1,64 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class ElasticsearchApi implements ICredentialType {
name = 'elasticsearchApi';
displayName = 'Elasticsearch API';
documentationUrl = 'elasticsearch';
properties: INodeProperties[] = [
{
displayName: 'Username',
name: 'username',
type: 'string',
default: '',
},
{
displayName: 'Password',
name: 'password',
type: 'string',
typeOptions: {
password: true,
},
default: '',
},
{
displayName: 'Base URL',
name: 'baseUrl',
type: 'string',
default: '',
placeholder: 'https://mydeployment.es.us-central1.gcp.cloud.es.io:9243',
description: "Referred to as Elasticsearch 'endpoint' in the Elastic deployment dashboard",
},
{
displayName: 'Ignore SSL Issues (Insecure)',
name: 'ignoreSSLIssues',
type: 'boolean',
default: false,
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
auth: {
username: '={{$credentials.username}}',
password: '={{$credentials.password}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: '={{$credentials.baseUrl}}'.replace(/\/$/, ''),
url: '/_xpack?human=false',
skipSslCertificateValidation: '={{$credentials.ignoreSSLIssues}}',
},
};
}
@@ -0,0 +1,19 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class EmeliaApi implements ICredentialType {
name = 'emeliaApi';
displayName = 'Emelia API';
documentationUrl = 'emelia';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
}
@@ -0,0 +1,19 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class EventbriteApi implements ICredentialType {
name = 'eventbriteApi';
displayName = 'Eventbrite API';
documentationUrl = 'eventbrite';
properties: INodeProperties[] = [
{
displayName: 'Private Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
}
@@ -0,0 +1,52 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class EventbriteOAuth2Api implements ICredentialType {
name = 'eventbriteOAuth2Api';
extends = ['oAuth2Api'];
displayName = 'Eventbrite OAuth2 API';
documentationUrl = 'eventbrite';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden',
default: 'https://www.eventbrite.com/oauth/authorize',
required: true,
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden',
default: 'https://www.eventbrite.com/oauth/token',
required: true,
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden',
default: '',
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden',
default: '',
},
{
displayName: 'Authentication',
name: 'authentication',
type: 'hidden',
default: 'body',
},
];
}
@@ -0,0 +1,45 @@
import type { IAuthenticateGeneric, ICredentialType, INodeProperties, Icon } from 'n8n-workflow';
export class F5BigIpApi implements ICredentialType {
name = 'f5BigIpApi';
displayName = 'F5 Big-IP API';
documentationUrl = 'f5bigip';
icon: Icon = 'file:icons/F5.svg';
httpRequestNode = {
name: 'F5 Big-IP',
docsUrl: 'https://clouddocs.f5.com/api/',
apiBaseUrl: '',
};
properties: INodeProperties[] = [
{
displayName: 'Username',
name: 'username',
type: 'string',
default: '',
required: true,
},
{
displayName: 'Password',
name: 'password',
type: 'string',
typeOptions: { password: true },
default: '',
required: true,
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
auth: {
username: '={{$credentials.username}}',
password: '={{$credentials.password}}',
},
},
};
}
@@ -0,0 +1,40 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class FacebookGraphApi implements ICredentialType {
name = 'facebookGraphApi';
displayName = 'Facebook Graph API';
documentationUrl = 'facebookgraph';
properties: INodeProperties[] = [
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
qs: {
access_token: '={{$credentials.accessToken}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: 'https://graph.facebook.com/v8.0',
url: '/me',
},
};
}
@@ -0,0 +1,23 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class FacebookGraphAppApi implements ICredentialType {
name = 'facebookGraphAppApi';
displayName = 'Facebook Graph API (App)';
documentationUrl = 'facebookapp';
extends = ['facebookGraphApi'];
properties: INodeProperties[] = [
{
displayName: 'App Secret',
name: 'appSecret',
type: 'string',
typeOptions: { password: true },
default: '',
description:
'(Optional) When the app secret is set the node will verify this signature to validate the integrity and origin of the payload',
},
];
}
@@ -0,0 +1,53 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class FacebookLeadAdsOAuth2Api implements ICredentialType {
name = 'facebookLeadAdsOAuth2Api';
extends = ['oAuth2Api'];
displayName = 'Facebook Lead Ads OAuth2 API';
documentationUrl = 'facebookleadads';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden',
default: 'https://www.facebook.com/v17.0/dialog/oauth',
required: true,
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden',
default: 'https://graph.facebook.com/v17.0/oauth/access_token',
required: true,
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden',
default:
'leads_retrieval pages_show_list pages_manage_metadata pages_manage_ads business_management',
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden',
default: '',
},
{
displayName: 'Authentication',
name: 'authentication',
type: 'hidden',
default: 'header',
},
];
}
@@ -0,0 +1,19 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class FigmaApi implements ICredentialType {
name = 'figmaApi';
displayName = 'Figma API';
documentationUrl = 'figma';
properties: INodeProperties[] = [
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
}
@@ -0,0 +1,39 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class FileMaker implements ICredentialType {
name = 'fileMaker';
displayName = 'FileMaker API';
documentationUrl = 'filemaker';
properties: INodeProperties[] = [
{
displayName: 'Host',
name: 'host',
type: 'string',
default: '',
},
{
displayName: 'Database',
name: 'db',
type: 'string',
default: '',
},
{
displayName: 'Login',
name: 'login',
type: 'string',
default: '',
},
{
displayName: 'Password',
name: 'password',
type: 'string',
typeOptions: {
password: true,
},
default: '',
},
];
}
@@ -0,0 +1,50 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class FilescanApi implements ICredentialType {
name = 'filescanApi';
displayName = 'Filescan API';
documentationUrl = 'filescan';
icon = { light: 'file:icons/Filescan.svg', dark: 'file:icons/Filescan.svg' } as const;
httpRequestNode = {
name: 'Filescan',
docsUrl: 'https://www.filescan.io/api/docs',
apiBaseUrlPlaceholder: 'https://www.filescan.io/api/system/do-healthcheck',
};
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
required: true,
type: 'string',
typeOptions: { password: true },
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
'X-Api-Key': '={{$credentials.apiKey}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: 'https://www.filescan.io/api',
url: '/system/do-healthcheck',
method: 'GET',
},
};
}

Some files were not shown because too many files have changed in this diff Show More