Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into feature-sub-workflo…
Browse files Browse the repository at this point in the history
…w-inputs
  • Loading branch information
netroy committed Dec 20, 2024
2 parents 48cb43e + 2f21404 commit ef3bb55
Show file tree
Hide file tree
Showing 34 changed files with 117 additions and 83 deletions.
46 changes: 46 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Great that you are here and you want to contribute to n8n
- [Actual n8n setup](#actual-n8n-setup)
- [Start](#start)
- [Development cycle](#development-cycle)
- [Community PR Guidelines](#community-pr-guidelines)
- [Test suite](#test-suite)
- [Unit tests](#unit-tests)
- [E2E tests](#e2e-tests)
Expand Down Expand Up @@ -191,6 +192,51 @@ automatically build your code, restart the backend and refresh the frontend
```
1. Commit code and [create a pull request](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork)

---

### Community PR Guidelines

#### **1. Change Request/Comment**

Please address the requested changes or provide feedback within 14 days. If there is no response or updates to the pull request during this time, it will be automatically closed. The PR can be reopened once the requested changes are applied.

#### **2. General Requirements**

- **Follow the Style Guide:**
- Ensure your code adheres to n8n's coding standards and conventions (e.g., formatting, naming, indentation). Use linting tools where applicable.
- **TypeScript Compliance:**
- Do not use `ts-ignore` .
- Ensure code adheres to TypeScript rules.
- **Avoid Repetitive Code:**
- Reuse existing components, parameters, and logic wherever possible instead of redefining or duplicating them.
- For nodes: Use the same parameter across multiple operations rather than defining a new parameter for each operation (if applicable).
- **Testing Requirements:**
- PRs **must include tests**:
- Unit tests
- Workflow tests for nodes (example [here](https://github.com/n8n-io/n8n/tree/master/packages/nodes-base/nodes/Switch/V3/test))
- UI tests (if applicable)
- **Typos:**
- Use a spell-checking tool, such as [**Code Spell Checker**](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker), to avoid typos.

#### **3. PR Specific Requirements**

- **Small PRs Only:**
- Focus on a single feature or fix per PR.
- **Naming Convention:**
- Follow [n8n's PR Title Conventions](https://github.com/n8n-io/n8n/blob/master/.github/pull_request_title_conventions.md#L36).
- **New Nodes:**
- PRs that introduce new nodes will be **auto-closed** unless they are explicitly requested by the n8n team and aligned with an agreed project scope. However, you can still explore [building your own nodes](https://docs.n8n.io/integrations/creating-nodes/) , as n8n offers the flexibility to create your own custom nodes.
- **Typo-Only PRs:**
- Typos are not sufficient justification for a PR and will be rejected.

#### **4. Workflow Summary for Non-Compliant PRs**

- **No Tests:** If tests are not provided, the PR will be auto-closed after **14 days**.
- **Non-Small PRs:** Large or multifaceted PRs will be returned for segmentation.
- **New Nodes/Typo PRs:** Automatically rejected if not aligned with project scope or guidelines.

---

### Test suite

#### Unit tests
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
"private": true,
"engines": {
"node": ">=20.15",
"pnpm": ">=9.5"
"pnpm": ">=9.15"
},
"packageManager": "pnpm@9.6.0",
"packageManager": "pnpm@9.15.1",
"scripts": {
"prepare": "node scripts/prepare.mjs",
"preinstall": "node scripts/block-npm-install.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { AxiosError, AxiosRequestConfig } from 'axios';
import axios from 'axios';

export class N8nApiClient {
constructor(public readonly apiBaseUrl: string) {}
constructor(readonly apiBaseUrl: string) {}

async waitForInstanceToBecomeOnline(): Promise<void> {
const HEALTH_ENDPOINT = 'healthz';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@ class MemoryChatBufferSingleton {
this.memoryBuffer = new Map();
}

public static getInstance(): MemoryChatBufferSingleton {
static getInstance(): MemoryChatBufferSingleton {
if (!MemoryChatBufferSingleton.instance) {
MemoryChatBufferSingleton.instance = new MemoryChatBufferSingleton();
}
return MemoryChatBufferSingleton.instance;
}

public async getMemory(
async getMemory(
sessionKey: string,
memoryParams: BufferWindowMemoryInput,
): Promise<BufferWindowMemory> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class MemoryVectorStoreManager {
this.vectorStoreBuffer = new Map();
}

public static getInstance(embeddings: Embeddings): MemoryVectorStoreManager {
static getInstance(embeddings: Embeddings): MemoryVectorStoreManager {
if (!MemoryVectorStoreManager.instance) {
MemoryVectorStoreManager.instance = new MemoryVectorStoreManager(embeddings);
} else {
Expand All @@ -27,7 +27,7 @@ export class MemoryVectorStoreManager {
return MemoryVectorStoreManager.instance;
}

public async getVectorStore(memoryKey: string): Promise<MemoryVectorStore> {
async getVectorStore(memoryKey: string): Promise<MemoryVectorStore> {
let vectorStoreInstance = this.vectorStoreBuffer.get(memoryKey);

if (!vectorStoreInstance) {
Expand All @@ -38,7 +38,7 @@ export class MemoryVectorStoreManager {
return vectorStoreInstance;
}

public async addDocuments(
async addDocuments(
memoryKey: string,
documents: Document[],
clearStore?: boolean,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class BuiltInsParser {
/**
* Parses which built-in variables are accessed in the given code
*/
public parseUsedBuiltIns(code: string): Result<BuiltInsParserState, Error> {
parseUsedBuiltIns(code: string): Result<BuiltInsParserState, Error> {
return toResult(() => {
const wrappedCode = `async function VmCodeWrapper() { ${code} }`;
const ast = parse(wrappedCode, { ecmaVersion: 2025, sourceType: 'module' });
Expand Down
5 changes: 5 additions & 0 deletions packages/@n8n_io/eslint-config/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,11 @@ const config = (module.exports = {
*/
'@typescript-eslint/return-await': ['error', 'always'],

/**
* https://typescript-eslint.io/rules/explicit-member-accessibility/
*/
'@typescript-eslint/explicit-member-accessibility': ['error', { accessibility: 'no-public' }],

// ----------------------------------
// eslint-plugin-import
// ----------------------------------
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/collaboration/collaboration.state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class CollaborationState {
* After how many minutes of inactivity a user should be removed
* as being an active user of a workflow.
*/
public readonly inactivityCleanUpTime = 15 * Time.minutes.toMilliseconds;
readonly inactivityCleanUpTime = 15 * Time.minutes.toMilliseconds;

constructor(private readonly cache: CacheService) {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export class AddMockedNodesColumnToTestDefinition1733133775640 implements Revers
const mockedNodesColumnName = escape.columnName('mockedNodes');

await runQuery(
`ALTER TABLE ${tableName} ADD COLUMN ${mockedNodesColumnName} JSON DEFAULT '[]' NOT NULL`,
`ALTER TABLE ${tableName} ADD COLUMN ${mockedNodesColumnName} JSON DEFAULT ('[]') NOT NULL`,
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class TestRunRepository extends Repository<TestRun> {
super(TestRun, dataSource.manager);
}

public async createTestRun(testDefinitionId: string) {
async createTestRun(testDefinitionId: string) {
const testRun = this.create({
status: 'new',
testDefinition: { id: testDefinitionId },
Expand All @@ -21,15 +21,15 @@ export class TestRunRepository extends Repository<TestRun> {
return await this.save(testRun);
}

public async markAsRunning(id: string) {
async markAsRunning(id: string) {
return await this.update(id, { status: 'running', runAt: new Date() });
}

public async markAsCompleted(id: string, metrics: AggregatedTestRunMetrics) {
async markAsCompleted(id: string, metrics: AggregatedTestRunMetrics) {
return await this.update(id, { status: 'completed', completedAt: new Date(), metrics });
}

public async getMany(testDefinitionId: string, options: ListQuery.Options) {
async getMany(testDefinitionId: string, options: ListQuery.Options) {
const findManyOptions: FindManyOptions<TestRun> = {
where: { testDefinition: { id: testDefinitionId } },
order: { createdAt: 'DESC' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export class SourceControlExportService {
}
}

public rmFilesFromExportFolder(filesToBeDeleted: Set<string>): Set<string> {
rmFilesFromExportFolder(filesToBeDeleted: Set<string>): Set<string> {
try {
filesToBeDeleted.forEach((e) => rmSync(e));
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export class SourceControlImportService {
);
}

public async getRemoteVersionIdsFromFiles(): Promise<SourceControlWorkflowVersionId[]> {
async getRemoteVersionIdsFromFiles(): Promise<SourceControlWorkflowVersionId[]> {
const remoteWorkflowFiles = await glob('*.json', {
cwd: this.workflowExportFolder,
absolute: true,
Expand All @@ -91,7 +91,7 @@ export class SourceControlImportService {
);
}

public async getLocalVersionIdsFromDb(): Promise<SourceControlWorkflowVersionId[]> {
async getLocalVersionIdsFromDb(): Promise<SourceControlWorkflowVersionId[]> {
const localWorkflows = await Container.get(WorkflowRepository).find({
select: ['id', 'name', 'versionId', 'updatedAt'],
});
Expand Down Expand Up @@ -119,7 +119,7 @@ export class SourceControlImportService {
}) as SourceControlWorkflowVersionId[];
}

public async getRemoteCredentialsFromFiles(): Promise<
async getRemoteCredentialsFromFiles(): Promise<
Array<ExportableCredential & { filename: string }>
> {
const remoteCredentialFiles = await glob('*.json', {
Expand All @@ -146,9 +146,7 @@ export class SourceControlImportService {
>;
}

public async getLocalCredentialsFromDb(): Promise<
Array<ExportableCredential & { filename: string }>
> {
async getLocalCredentialsFromDb(): Promise<Array<ExportableCredential & { filename: string }>> {
const localCredentials = await Container.get(CredentialsRepository).find({
select: ['id', 'name', 'type'],
});
Expand All @@ -160,7 +158,7 @@ export class SourceControlImportService {
})) as Array<ExportableCredential & { filename: string }>;
}

public async getRemoteVariablesFromFile(): Promise<Variables[]> {
async getRemoteVariablesFromFile(): Promise<Variables[]> {
const variablesFile = await glob(SOURCE_CONTROL_VARIABLES_EXPORT_FILE, {
cwd: this.gitFolder,
absolute: true,
Expand All @@ -174,11 +172,11 @@ export class SourceControlImportService {
return [];
}

public async getLocalVariablesFromDb(): Promise<Variables[]> {
async getLocalVariablesFromDb(): Promise<Variables[]> {
return await this.variablesService.getAllCached();
}

public async getRemoteTagsAndMappingsFromFile(): Promise<{
async getRemoteTagsAndMappingsFromFile(): Promise<{
tags: TagEntity[];
mappings: WorkflowTagMapping[];
}> {
Expand All @@ -197,7 +195,7 @@ export class SourceControlImportService {
return { tags: [], mappings: [] };
}

public async getLocalTagsAndMappingsFromDb(): Promise<{
async getLocalTagsAndMappingsFromDb(): Promise<{
tags: TagEntity[];
mappings: WorkflowTagMapping[];
}> {
Expand All @@ -210,7 +208,7 @@ export class SourceControlImportService {
return { tags: localTags, mappings: localMappings };
}

public async importWorkflowFromWorkFolder(candidates: SourceControlledFile[], userId: string) {
async importWorkflowFromWorkFolder(candidates: SourceControlledFile[], userId: string) {
const personalProject =
await Container.get(ProjectRepository).getPersonalProjectForUserOrFail(userId);
const workflowManager = this.activeWorkflowManager;
Expand Down Expand Up @@ -297,7 +295,7 @@ export class SourceControlImportService {
}>;
}

public async importCredentialsFromWorkFolder(candidates: SourceControlledFile[], userId: string) {
async importCredentialsFromWorkFolder(candidates: SourceControlledFile[], userId: string) {
const personalProject =
await Container.get(ProjectRepository).getPersonalProjectForUserOrFail(userId);
const candidateIds = candidates.map((c) => c.id);
Expand Down Expand Up @@ -371,7 +369,7 @@ export class SourceControlImportService {
return importCredentialsResult.filter((e) => e !== undefined);
}

public async importTagsFromWorkFolder(candidate: SourceControlledFile) {
async importTagsFromWorkFolder(candidate: SourceControlledFile) {
let mappedTags;
try {
this.logger.debug(`Importing tags from file ${candidate.file}`);
Expand Down Expand Up @@ -433,7 +431,7 @@ export class SourceControlImportService {
return mappedTags;
}

public async importVariablesFromWorkFolder(
async importVariablesFromWorkFolder(
candidate: SourceControlledFile,
valueOverrides?: {
[key: string]: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,22 @@ export class SourceControlPreferencesService {
this.sshKeyName = path.join(this.sshFolder, SOURCE_CONTROL_SSH_KEY_NAME);
}

public get sourceControlPreferences(): SourceControlPreferences {
get sourceControlPreferences(): SourceControlPreferences {
return {
...this._sourceControlPreferences,
connected: this._sourceControlPreferences.connected ?? false,
};
}

// merge the new preferences with the existing preferences when setting
public set sourceControlPreferences(preferences: Partial<SourceControlPreferences>) {
set sourceControlPreferences(preferences: Partial<SourceControlPreferences>) {
this._sourceControlPreferences = SourceControlPreferences.merge(
preferences,
this._sourceControlPreferences,
);
}

public isSourceControlSetup() {
isSourceControlSetup() {
return (
this.isSourceControlLicensedAndEnabled() &&
this.getPreferences().repositoryUrl &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export class SourceControlService {
});
}

public async sanityCheck(): Promise<void> {
async sanityCheck(): Promise<void> {
try {
const foldersExisted = sourceControlFoldersExistCheck(
[this.gitFolder, this.sshFolder],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ export class TestRunnerService {
/**
* Creates a new test run for the given test definition.
*/
public async runTest(user: User, test: TestDefinition): Promise<void> {
async runTest(user: User, test: TestDefinition): Promise<void> {
const workflow = await this.workflowRepository.findById(test.workflowId);
assert(workflow, 'Workflow not found');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export class MessageEventBusLogWriter {
this.globalConfig = Container.get(GlobalConfig);
}

public get worker(): Worker | undefined {
get worker(): Worker | undefined {
return this._worker;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/executions/execution.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ export class ExecutionService {
}
}

public async annotate(
async annotate(
executionId: string,
updateData: ExecutionRequest.ExecutionUpdatePayload,
sharedWorkflowIds: string[],
Expand Down
10 changes: 5 additions & 5 deletions packages/cli/src/push/__tests__/websocket.push.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ import { mockInstance } from '@test/mocking';
jest.useFakeTimers();

class MockWebSocket extends EventEmitter {
public isAlive = true;
isAlive = true;

public ping = jest.fn();
ping = jest.fn();

public send = jest.fn();
send = jest.fn();

public terminate = jest.fn();
terminate = jest.fn();

public close = jest.fn();
close = jest.fn();
}

const createMockWebSocket = () => new MockWebSocket() as unknown as jest.Mocked<WebSocket>;
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/push/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const useWebSockets = config.getEnv('push.backend') === 'websocket';
*/
@Service()
export class Push extends TypedEmitter<PushEvents> {
public isBidirectional = useWebSockets;
isBidirectional = useWebSockets;

private backend = useWebSockets ? Container.get(WebSocketPush) : Container.get(SSEPush);

Expand Down
Loading

0 comments on commit ef3bb55

Please sign in to comment.