mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
refactor(core): Simplify ExternalSecretsProxy setup and move it to core (#16021)
This commit is contained in:
committed by
GitHub
parent
3e91f3253b
commit
2258a74518
@@ -4,6 +4,7 @@ import { ExecutionRepository } from '@n8n/db';
|
|||||||
import { WorkflowRepository } from '@n8n/db';
|
import { WorkflowRepository } from '@n8n/db';
|
||||||
import { Container } from '@n8n/di';
|
import { Container } from '@n8n/di';
|
||||||
import { mock } from 'jest-mock-extended';
|
import { mock } from 'jest-mock-extended';
|
||||||
|
import { ExternalSecretsProxy } from 'n8n-core';
|
||||||
import type { IWorkflowBase } from 'n8n-workflow';
|
import type { IWorkflowBase } from 'n8n-workflow';
|
||||||
import type {
|
import type {
|
||||||
IExecuteWorkflowInfo,
|
IExecuteWorkflowInfo,
|
||||||
@@ -23,7 +24,6 @@ import {
|
|||||||
SubworkflowPolicyChecker,
|
SubworkflowPolicyChecker,
|
||||||
} from '@/executions/pre-execution-checks';
|
} from '@/executions/pre-execution-checks';
|
||||||
import { ExternalHooks } from '@/external-hooks';
|
import { ExternalHooks } from '@/external-hooks';
|
||||||
import { SecretsHelper } from '@/secrets-helpers.ee';
|
|
||||||
import { UrlService } from '@/services/url.service';
|
import { UrlService } from '@/services/url.service';
|
||||||
import { WorkflowStatisticsService } from '@/services/workflow-statistics.service';
|
import { WorkflowStatisticsService } from '@/services/workflow-statistics.service';
|
||||||
import { Telemetry } from '@/telemetry';
|
import { Telemetry } from '@/telemetry';
|
||||||
@@ -86,12 +86,12 @@ describe('WorkflowExecuteAdditionalData', () => {
|
|||||||
const variablesService = mockInstance(VariablesService);
|
const variablesService = mockInstance(VariablesService);
|
||||||
variablesService.getAllCached.mockResolvedValue([]);
|
variablesService.getAllCached.mockResolvedValue([]);
|
||||||
const credentialsHelper = mockInstance(CredentialsHelper);
|
const credentialsHelper = mockInstance(CredentialsHelper);
|
||||||
const secretsHelper = mockInstance(SecretsHelper);
|
const externalSecretsProxy = mockInstance(ExternalSecretsProxy);
|
||||||
const eventService = mockInstance(EventService);
|
const eventService = mockInstance(EventService);
|
||||||
mockInstance(ExternalHooks);
|
mockInstance(ExternalHooks);
|
||||||
Container.set(VariablesService, variablesService);
|
Container.set(VariablesService, variablesService);
|
||||||
Container.set(CredentialsHelper, credentialsHelper);
|
Container.set(CredentialsHelper, credentialsHelper);
|
||||||
Container.set(SecretsHelper, secretsHelper);
|
Container.set(ExternalSecretsProxy, externalSecretsProxy);
|
||||||
const executionRepository = mockInstance(ExecutionRepository);
|
const executionRepository = mockInstance(ExecutionRepository);
|
||||||
mockInstance(Telemetry);
|
mockInstance(Telemetry);
|
||||||
const workflowRepository = mockInstance(WorkflowRepository);
|
const workflowRepository = mockInstance(WorkflowRepository);
|
||||||
@@ -306,7 +306,7 @@ describe('WorkflowExecuteAdditionalData', () => {
|
|||||||
userId: undefined,
|
userId: undefined,
|
||||||
setExecutionStatus: expect.any(Function),
|
setExecutionStatus: expect.any(Function),
|
||||||
variables: mockVariables,
|
variables: mockVariables,
|
||||||
secretsHelpers: secretsHelper,
|
externalSecretsProxy,
|
||||||
startRunnerTask: expect.any(Function),
|
startRunnerTask: expect.any(Function),
|
||||||
logAiEvent: expect.any(Function),
|
logAiEvent: expect.any(Function),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
ObjectStoreService,
|
ObjectStoreService,
|
||||||
DataDeduplicationService,
|
DataDeduplicationService,
|
||||||
ErrorReporter,
|
ErrorReporter,
|
||||||
|
ExternalSecretsProxy,
|
||||||
} from 'n8n-core';
|
} from 'n8n-core';
|
||||||
import { ensureError, sleep, UserError } from 'n8n-workflow';
|
import { ensureError, sleep, UserError } from 'n8n-workflow';
|
||||||
|
|
||||||
@@ -278,6 +279,7 @@ export abstract class BaseCommand extends Command {
|
|||||||
async initExternalSecrets() {
|
async initExternalSecrets() {
|
||||||
const secretsManager = Container.get(ExternalSecretsManager);
|
const secretsManager = Container.get(ExternalSecretsManager);
|
||||||
await secretsManager.init();
|
await secretsManager.init();
|
||||||
|
Container.get(ExternalSecretsProxy).setManager(secretsManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
initWorkflowHistory() {
|
initWorkflowHistory() {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Container } from '@n8n/di';
|
|||||||
import Csrf from 'csrf';
|
import Csrf from 'csrf';
|
||||||
import type { Response } from 'express';
|
import type { Response } from 'express';
|
||||||
import { captor, mock } from 'jest-mock-extended';
|
import { captor, mock } from 'jest-mock-extended';
|
||||||
import { Cipher, type InstanceSettings } from 'n8n-core';
|
import { Cipher, type InstanceSettings, ExternalSecretsProxy } from 'n8n-core';
|
||||||
import type { IWorkflowExecuteAdditionalData } from 'n8n-workflow';
|
import type { IWorkflowExecuteAdditionalData } from 'n8n-workflow';
|
||||||
import nock from 'nock';
|
import nock from 'nock';
|
||||||
|
|
||||||
@@ -19,7 +19,6 @@ import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
|||||||
import { NotFoundError } from '@/errors/response-errors/not-found.error';
|
import { NotFoundError } from '@/errors/response-errors/not-found.error';
|
||||||
import { ExternalHooks } from '@/external-hooks';
|
import { ExternalHooks } from '@/external-hooks';
|
||||||
import type { OAuthRequest } from '@/requests';
|
import type { OAuthRequest } from '@/requests';
|
||||||
import { SecretsHelper } from '@/secrets-helpers.ee';
|
|
||||||
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data';
|
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data';
|
||||||
import { mockInstance } from '@test/mocking';
|
import { mockInstance } from '@test/mocking';
|
||||||
|
|
||||||
@@ -28,7 +27,7 @@ jest.mock('@/workflow-execute-additional-data');
|
|||||||
describe('OAuth1CredentialController', () => {
|
describe('OAuth1CredentialController', () => {
|
||||||
mockInstance(Logger);
|
mockInstance(Logger);
|
||||||
mockInstance(ExternalHooks);
|
mockInstance(ExternalHooks);
|
||||||
mockInstance(SecretsHelper);
|
mockInstance(ExternalSecretsProxy);
|
||||||
mockInstance(VariablesService, {
|
mockInstance(VariablesService, {
|
||||||
getAllCached: async () => [],
|
getAllCached: async () => [],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Container } from '@n8n/di';
|
|||||||
import Csrf from 'csrf';
|
import Csrf from 'csrf';
|
||||||
import { type Response } from 'express';
|
import { type Response } from 'express';
|
||||||
import { captor, mock } from 'jest-mock-extended';
|
import { captor, mock } from 'jest-mock-extended';
|
||||||
import { Cipher, type InstanceSettings } from 'n8n-core';
|
import { Cipher, type InstanceSettings, ExternalSecretsProxy } from 'n8n-core';
|
||||||
import type { IWorkflowExecuteAdditionalData } from 'n8n-workflow';
|
import type { IWorkflowExecuteAdditionalData } from 'n8n-workflow';
|
||||||
import nock from 'nock';
|
import nock from 'nock';
|
||||||
|
|
||||||
@@ -19,7 +19,6 @@ import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
|||||||
import { NotFoundError } from '@/errors/response-errors/not-found.error';
|
import { NotFoundError } from '@/errors/response-errors/not-found.error';
|
||||||
import { ExternalHooks } from '@/external-hooks';
|
import { ExternalHooks } from '@/external-hooks';
|
||||||
import type { OAuthRequest } from '@/requests';
|
import type { OAuthRequest } from '@/requests';
|
||||||
import { SecretsHelper } from '@/secrets-helpers.ee';
|
|
||||||
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data';
|
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data';
|
||||||
import { mockInstance } from '@test/mocking';
|
import { mockInstance } from '@test/mocking';
|
||||||
|
|
||||||
@@ -27,7 +26,7 @@ jest.mock('@/workflow-execute-additional-data');
|
|||||||
|
|
||||||
describe('OAuth2CredentialController', () => {
|
describe('OAuth2CredentialController', () => {
|
||||||
mockInstance(Logger);
|
mockInstance(Logger);
|
||||||
mockInstance(SecretsHelper);
|
mockInstance(ExternalSecretsProxy);
|
||||||
mockInstance(VariablesService, {
|
mockInstance(VariablesService, {
|
||||||
getAllCached: async () => [],
|
getAllCached: async () => [],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -327,8 +327,6 @@ export class CredentialsHelper extends ICredentialsHelper {
|
|||||||
return decryptedDataOriginal;
|
return decryptedDataOriginal;
|
||||||
}
|
}
|
||||||
|
|
||||||
await additionalData?.secretsHelpers?.waitForInit();
|
|
||||||
|
|
||||||
return await this.applyDefaultsAndOverwrites(
|
return await this.applyDefaultsAndOverwrites(
|
||||||
additionalData,
|
additionalData,
|
||||||
decryptedDataOriginal,
|
decryptedDataOriginal,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Container } from '@n8n/di';
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import type { AxiosRequestConfig, Method } from 'axios';
|
import type { AxiosRequestConfig, Method } from 'axios';
|
||||||
import { Agent as HTTPSAgent } from 'https';
|
import { Agent as HTTPSAgent } from 'https';
|
||||||
|
import { ExternalSecretsProxy } from 'n8n-core';
|
||||||
import { jsonParse, MessageEventBusDestinationTypeNames } from 'n8n-workflow';
|
import { jsonParse, MessageEventBusDestinationTypeNames } from 'n8n-workflow';
|
||||||
import type {
|
import type {
|
||||||
MessageEventBusDestinationOptions,
|
MessageEventBusDestinationOptions,
|
||||||
@@ -14,7 +15,6 @@ import type {
|
|||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import { CredentialsHelper } from '@/credentials-helper';
|
import { CredentialsHelper } from '@/credentials-helper';
|
||||||
import { SecretsHelper } from '@/secrets-helpers.ee';
|
|
||||||
|
|
||||||
import { MessageEventBusDestination } from './message-event-bus-destination.ee';
|
import { MessageEventBusDestination } from './message-event-bus-destination.ee';
|
||||||
import { eventMessageGenericDestinationTestEvent } from '../event-message-classes/event-message-generic';
|
import { eventMessageGenericDestinationTestEvent } from '../event-message-classes/event-message-generic';
|
||||||
@@ -103,7 +103,7 @@ export class MessageEventBusDestinationWebhook
|
|||||||
if (foundCredential) {
|
if (foundCredential) {
|
||||||
const credentialsDecrypted = await this.credentialsHelper?.getDecrypted(
|
const credentialsDecrypted = await this.credentialsHelper?.getDecrypted(
|
||||||
{
|
{
|
||||||
secretsHelpers: Container.get(SecretsHelper),
|
externalSecretsProxy: Container.get(ExternalSecretsProxy),
|
||||||
} as unknown as IWorkflowExecuteAdditionalData,
|
} as unknown as IWorkflowExecuteAdditionalData,
|
||||||
foundCredential[1],
|
foundCredential[1],
|
||||||
foundCredential[0],
|
foundCredential[0],
|
||||||
|
|||||||
@@ -352,10 +352,10 @@ describe('External Secrets Manager', () => {
|
|||||||
expect(manager.getSecretNames('dummy')).toEqual(['test1', 'test2']);
|
expect(manager.getSecretNames('dummy')).toEqual(['test1', 'test2']);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should return undefined when provider does not exist', async () => {
|
test('should return an empty array when provider does not exist', async () => {
|
||||||
await manager.init();
|
await manager.init();
|
||||||
|
|
||||||
expect(manager.getSecretNames('nonexistent')).toBeUndefined();
|
expect(manager.getSecretNames('nonexistent')).toBeEmptyArray();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Logger } from '@n8n/backend-common';
|
|||||||
import { SettingsRepository } from '@n8n/db';
|
import { SettingsRepository } from '@n8n/db';
|
||||||
import { OnPubSubEvent, OnShutdown } from '@n8n/decorators';
|
import { OnPubSubEvent, OnShutdown } from '@n8n/decorators';
|
||||||
import { Service } from '@n8n/di';
|
import { Service } from '@n8n/di';
|
||||||
import { Cipher } from 'n8n-core';
|
import { Cipher, type IExternalSecretsManager } from 'n8n-core';
|
||||||
import { jsonParse, type IDataObject, ensureError, UnexpectedError } from 'n8n-workflow';
|
import { jsonParse, type IDataObject, ensureError, UnexpectedError } from 'n8n-workflow';
|
||||||
|
|
||||||
import { EventService } from '@/events/event.service';
|
import { EventService } from '@/events/event.service';
|
||||||
@@ -19,7 +19,7 @@ import { ExternalSecretsConfig } from './external-secrets.config';
|
|||||||
import type { ExternalSecretsSettings, SecretsProvider, SecretsProviderSettings } from './types';
|
import type { ExternalSecretsSettings, SecretsProvider, SecretsProviderSettings } from './types';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class ExternalSecretsManager {
|
export class ExternalSecretsManager implements IExternalSecretsManager {
|
||||||
private providers: Record<string, SecretsProvider> = {};
|
private providers: Record<string, SecretsProvider> = {};
|
||||||
|
|
||||||
private initializingPromise?: Promise<void>;
|
private initializingPromise?: Promise<void>;
|
||||||
@@ -211,7 +211,7 @@ export class ExternalSecretsManager {
|
|||||||
return provider in this.providers;
|
return provider in this.providers;
|
||||||
}
|
}
|
||||||
|
|
||||||
getProviderNames(): string[] | undefined {
|
getProviderNames(): string[] {
|
||||||
return Object.keys(this.providers);
|
return Object.keys(this.providers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,8 +223,8 @@ export class ExternalSecretsManager {
|
|||||||
return this.getProvider(provider)?.hasSecret(name) ?? false;
|
return this.getProvider(provider)?.hasSecret(name) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSecretNames(provider: string): string[] | undefined {
|
getSecretNames(provider: string): string[] {
|
||||||
return this.getProvider(provider)?.getSecretNames();
|
return this.getProvider(provider)?.getSecretNames() ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllSecretNames(): Record<string, string[]> {
|
getAllSecretNames(): Record<string, string[]> {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { IExecutionResponse } from '@n8n/db';
|
|||||||
import type { ExecutionRepository } from '@n8n/db';
|
import type { ExecutionRepository } from '@n8n/db';
|
||||||
import { mock } from 'jest-mock-extended';
|
import { mock } from 'jest-mock-extended';
|
||||||
import type { WorkflowExecute as ActualWorkflowExecute } from 'n8n-core';
|
import type { WorkflowExecute as ActualWorkflowExecute } from 'n8n-core';
|
||||||
|
import { ExternalSecretsProxy } from 'n8n-core';
|
||||||
import { mockInstance } from 'n8n-core/test/utils';
|
import { mockInstance } from 'n8n-core/test/utils';
|
||||||
import type { IPinData, ITaskData, IWorkflowExecuteAdditionalData } from 'n8n-workflow';
|
import type { IPinData, ITaskData, IWorkflowExecuteAdditionalData } from 'n8n-workflow';
|
||||||
import { Workflow, type IRunExecutionData, type WorkflowExecuteMode } from 'n8n-workflow';
|
import { Workflow, type IRunExecutionData, type WorkflowExecuteMode } from 'n8n-workflow';
|
||||||
@@ -11,7 +12,6 @@ import { CredentialsHelper } from '@/credentials-helper';
|
|||||||
import { VariablesService } from '@/environments.ee/variables/variables.service.ee';
|
import { VariablesService } from '@/environments.ee/variables/variables.service.ee';
|
||||||
import { ExternalHooks } from '@/external-hooks';
|
import { ExternalHooks } from '@/external-hooks';
|
||||||
import type { ManualExecutionService } from '@/manual-execution.service';
|
import type { ManualExecutionService } from '@/manual-execution.service';
|
||||||
import { SecretsHelper } from '@/secrets-helpers.ee';
|
|
||||||
import { WorkflowStatisticsService } from '@/services/workflow-statistics.service';
|
import { WorkflowStatisticsService } from '@/services/workflow-statistics.service';
|
||||||
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data';
|
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data';
|
||||||
import { WorkflowStaticDataService } from '@/workflows/workflow-static-data.service';
|
import { WorkflowStaticDataService } from '@/workflows/workflow-static-data.service';
|
||||||
@@ -23,7 +23,7 @@ mockInstance(VariablesService, {
|
|||||||
getAllCached: jest.fn().mockResolvedValue([]),
|
getAllCached: jest.fn().mockResolvedValue([]),
|
||||||
});
|
});
|
||||||
mockInstance(CredentialsHelper);
|
mockInstance(CredentialsHelper);
|
||||||
mockInstance(SecretsHelper);
|
mockInstance(ExternalSecretsProxy);
|
||||||
mockInstance(WorkflowStaticDataService);
|
mockInstance(WorkflowStaticDataService);
|
||||||
mockInstance(WorkflowStatisticsService);
|
mockInstance(WorkflowStatisticsService);
|
||||||
mockInstance(ExternalHooks);
|
mockInstance(ExternalHooks);
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
import { Service } from '@n8n/di';
|
|
||||||
import type { SecretsHelpersBase } from 'n8n-workflow';
|
|
||||||
|
|
||||||
import { ExternalSecretsManager } from './external-secrets.ee/external-secrets-manager.ee';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class SecretsHelper implements SecretsHelpersBase {
|
|
||||||
constructor(private service: ExternalSecretsManager) {}
|
|
||||||
|
|
||||||
async update() {
|
|
||||||
if (!this.service.initialized) {
|
|
||||||
await this.service.init();
|
|
||||||
}
|
|
||||||
await this.service.updateSecrets();
|
|
||||||
}
|
|
||||||
|
|
||||||
async waitForInit() {
|
|
||||||
if (!this.service.initialized) {
|
|
||||||
await this.service.init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getSecret(provider: string, name: string) {
|
|
||||||
return this.service.getSecret(provider, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
hasSecret(provider: string, name: string): boolean {
|
|
||||||
return this.service.hasSecret(provider, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
hasProvider(provider: string): boolean {
|
|
||||||
return this.service.hasProvider(provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
listProviders(): string[] {
|
|
||||||
return this.service.getProviderNames() ?? [];
|
|
||||||
}
|
|
||||||
|
|
||||||
listSecrets(provider: string): string[] {
|
|
||||||
return this.service.getSecretNames(provider) ?? [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,7 @@ import { Logger } from '@n8n/backend-common';
|
|||||||
import { GlobalConfig } from '@n8n/config';
|
import { GlobalConfig } from '@n8n/config';
|
||||||
import { ExecutionRepository, WorkflowRepository } from '@n8n/db';
|
import { ExecutionRepository, WorkflowRepository } from '@n8n/db';
|
||||||
import { Container } from '@n8n/di';
|
import { Container } from '@n8n/di';
|
||||||
import { WorkflowExecute } from 'n8n-core';
|
import { ExternalSecretsProxy, WorkflowExecute } from 'n8n-core';
|
||||||
import { UnexpectedError, Workflow } from 'n8n-workflow';
|
import { UnexpectedError, Workflow } from 'n8n-workflow';
|
||||||
import type {
|
import type {
|
||||||
IDataObject,
|
IDataObject,
|
||||||
@@ -46,7 +46,6 @@ import {
|
|||||||
import type { UpdateExecutionPayload } from '@/interfaces';
|
import type { UpdateExecutionPayload } from '@/interfaces';
|
||||||
import { NodeTypes } from '@/node-types';
|
import { NodeTypes } from '@/node-types';
|
||||||
import { Push } from '@/push';
|
import { Push } from '@/push';
|
||||||
import { SecretsHelper } from '@/secrets-helpers.ee';
|
|
||||||
import { UrlService } from '@/services/url.service';
|
import { UrlService } from '@/services/url.service';
|
||||||
import { TaskRequester } from '@/task-runners/task-managers/task-requester';
|
import { TaskRequester } from '@/task-runners/task-managers/task-requester';
|
||||||
import { findSubworkflowStart } from '@/utils';
|
import { findSubworkflowStart } from '@/utils';
|
||||||
@@ -388,7 +387,7 @@ export async function getBase(
|
|||||||
userId,
|
userId,
|
||||||
setExecutionStatus,
|
setExecutionStatus,
|
||||||
variables,
|
variables,
|
||||||
secretsHelpers: Container.get(SecretsHelper),
|
externalSecretsProxy: Container.get(ExternalSecretsProxy),
|
||||||
async startRunnerTask(
|
async startRunnerTask(
|
||||||
additionalData: IWorkflowExecuteAdditionalData,
|
additionalData: IWorkflowExecuteAdditionalData,
|
||||||
jobType: string,
|
jobType: string,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { WebhookEntity } from '@n8n/db';
|
|||||||
import { WorkflowRepository } from '@n8n/db';
|
import { WorkflowRepository } from '@n8n/db';
|
||||||
import { Container } from '@n8n/di';
|
import { Container } from '@n8n/di';
|
||||||
import { mock } from 'jest-mock-extended';
|
import { mock } from 'jest-mock-extended';
|
||||||
import { InstanceSettings } from 'n8n-core';
|
import { InstanceSettings, ExternalSecretsProxy } from 'n8n-core';
|
||||||
import { FormTrigger } from 'n8n-nodes-base/nodes/Form/FormTrigger.node';
|
import { FormTrigger } from 'n8n-nodes-base/nodes/Form/FormTrigger.node';
|
||||||
import { ScheduleTrigger } from 'n8n-nodes-base/nodes/Schedule/ScheduleTrigger.node';
|
import { ScheduleTrigger } from 'n8n-nodes-base/nodes/Schedule/ScheduleTrigger.node';
|
||||||
import { NodeApiError, Workflow } from 'n8n-workflow';
|
import { NodeApiError, Workflow } from 'n8n-workflow';
|
||||||
@@ -19,7 +19,6 @@ import { ExecutionService } from '@/executions/execution.service';
|
|||||||
import { ExternalHooks } from '@/external-hooks';
|
import { ExternalHooks } from '@/external-hooks';
|
||||||
import { NodeTypes } from '@/node-types';
|
import { NodeTypes } from '@/node-types';
|
||||||
import { Push } from '@/push';
|
import { Push } from '@/push';
|
||||||
import { SecretsHelper } from '@/secrets-helpers.ee';
|
|
||||||
import * as WebhookHelpers from '@/webhooks/webhook-helpers';
|
import * as WebhookHelpers from '@/webhooks/webhook-helpers';
|
||||||
import { WebhookService } from '@/webhooks/webhook.service';
|
import { WebhookService } from '@/webhooks/webhook.service';
|
||||||
import * as AdditionalData from '@/workflow-execute-additional-data';
|
import * as AdditionalData from '@/workflow-execute-additional-data';
|
||||||
@@ -33,7 +32,7 @@ import { mockInstance } from '../shared/mocking';
|
|||||||
|
|
||||||
mockInstance(ActiveExecutions);
|
mockInstance(ActiveExecutions);
|
||||||
mockInstance(Push);
|
mockInstance(Push);
|
||||||
mockInstance(SecretsHelper);
|
mockInstance(ExternalSecretsProxy);
|
||||||
mockInstance(ExecutionService);
|
mockInstance(ExecutionService);
|
||||||
mockInstance(WorkflowService);
|
mockInstance(WorkflowService);
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import type {
|
|||||||
|
|
||||||
import type {
|
import type {
|
||||||
ExecutionLifecycleHookName,
|
ExecutionLifecycleHookName,
|
||||||
ExecutionLifecyleHookHandlers,
|
ExecutionLifecycleHookHandlers,
|
||||||
} from '../execution-lifecycle-hooks';
|
} from '../execution-lifecycle-hooks';
|
||||||
import { ExecutionLifecycleHooks } from '../execution-lifecycle-hooks';
|
import { ExecutionLifecycleHooks } from '../execution-lifecycle-hooks';
|
||||||
|
|
||||||
@@ -46,12 +46,14 @@ describe('ExecutionLifecycleHooks', () => {
|
|||||||
describe('addHandler()', () => {
|
describe('addHandler()', () => {
|
||||||
const hooksHandlers =
|
const hooksHandlers =
|
||||||
mock<{
|
mock<{
|
||||||
[K in keyof ExecutionLifecyleHookHandlers]: ExecutionLifecyleHookHandlers[K][number];
|
[K in keyof ExecutionLifecycleHookHandlers]: ExecutionLifecycleHookHandlers[K][number];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const testCases: Array<{
|
const testCases: Array<{
|
||||||
hook: ExecutionLifecycleHookName;
|
hook: ExecutionLifecycleHookName;
|
||||||
args: Parameters<ExecutionLifecyleHookHandlers[keyof ExecutionLifecyleHookHandlers][number]>;
|
args: Parameters<
|
||||||
|
ExecutionLifecycleHookHandlers[keyof ExecutionLifecycleHookHandlers][number]
|
||||||
|
>;
|
||||||
}> = [
|
}> = [
|
||||||
{ hook: 'nodeExecuteBefore', args: ['testNode', mock<ITaskStartedData>()] },
|
{ hook: 'nodeExecuteBefore', args: ['testNode', mock<ITaskStartedData>()] },
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
import { mock } from 'jest-mock-extended';
|
||||||
|
|
||||||
|
import { ExternalSecretsProxy, type IExternalSecretsManager } from '../external-secrets-proxy';
|
||||||
|
|
||||||
|
describe('ExternalSecretsProxy', () => {
|
||||||
|
let proxy: ExternalSecretsProxy;
|
||||||
|
const manager = mock<IExternalSecretsManager>();
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
proxy = new ExternalSecretsProxy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('update', () => {
|
||||||
|
it('should update secrets when manager is set', async () => {
|
||||||
|
manager.updateSecrets.mockResolvedValue();
|
||||||
|
proxy.setManager(manager);
|
||||||
|
|
||||||
|
await proxy.update();
|
||||||
|
|
||||||
|
expect(manager.updateSecrets).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not throw when updating without a manager', async () => {
|
||||||
|
await expect(proxy.update()).resolves.not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSecret', () => {
|
||||||
|
it('should get secret from manager', () => {
|
||||||
|
const secretValue = { key: 'value' };
|
||||||
|
manager.getSecret.mockReturnValue(secretValue);
|
||||||
|
proxy.setManager(manager);
|
||||||
|
|
||||||
|
const result = proxy.getSecret('aws', 'api-key');
|
||||||
|
|
||||||
|
expect(manager.getSecret).toHaveBeenCalledWith('aws', 'api-key');
|
||||||
|
expect(result).toBe(secretValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return undefined when getting secret without a manager', () => {
|
||||||
|
const result = proxy.getSecret('aws', 'api-key');
|
||||||
|
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('hasSecret', () => {
|
||||||
|
it('should check if secret exists', () => {
|
||||||
|
manager.hasSecret.mockReturnValue(true);
|
||||||
|
proxy.setManager(manager);
|
||||||
|
|
||||||
|
const result = proxy.hasSecret('aws', 'api-key');
|
||||||
|
|
||||||
|
expect(manager.hasSecret).toHaveBeenCalledWith('aws', 'api-key');
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false when checking secret without a manager', () => {
|
||||||
|
const result = proxy.hasSecret('aws', 'api-key');
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('hasProvider', () => {
|
||||||
|
it('should check if provider exists', () => {
|
||||||
|
manager.hasProvider.mockReturnValue(true);
|
||||||
|
proxy.setManager(manager);
|
||||||
|
|
||||||
|
const result = proxy.hasProvider('aws');
|
||||||
|
|
||||||
|
expect(manager.hasProvider).toHaveBeenCalledWith('aws');
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false when checking provider without a manager', () => {
|
||||||
|
const result = proxy.hasProvider('aws');
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('listProviders', () => {
|
||||||
|
it('should list providers', () => {
|
||||||
|
const providers = ['aws', 'gcp', 'azure'];
|
||||||
|
manager.getProviderNames.mockReturnValue(providers);
|
||||||
|
proxy.setManager(manager);
|
||||||
|
|
||||||
|
const result = proxy.listProviders();
|
||||||
|
|
||||||
|
expect(manager.getProviderNames).toHaveBeenCalledTimes(1);
|
||||||
|
expect(result).toEqual(providers);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return empty array when listing providers without a manager', () => {
|
||||||
|
const result = proxy.listProviders();
|
||||||
|
|
||||||
|
expect(result).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('listSecrets', () => {
|
||||||
|
it('should list secrets for a provider', () => {
|
||||||
|
const secrets = ['api-key', 'api-secret', 'token'];
|
||||||
|
manager.getSecretNames.mockReturnValue(secrets);
|
||||||
|
proxy.setManager(manager);
|
||||||
|
|
||||||
|
const result = proxy.listSecrets('aws');
|
||||||
|
|
||||||
|
expect(manager.getSecretNames).toHaveBeenCalledWith('aws');
|
||||||
|
expect(result).toEqual(secrets);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return empty array when listing secrets without a manager', () => {
|
||||||
|
const result = proxy.listSecrets('aws');
|
||||||
|
|
||||||
|
expect(result).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -11,7 +11,7 @@ import type {
|
|||||||
WorkflowExecuteMode,
|
WorkflowExecuteMode,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
export type ExecutionLifecyleHookHandlers = {
|
export type ExecutionLifecycleHookHandlers = {
|
||||||
nodeExecuteBefore: Array<
|
nodeExecuteBefore: Array<
|
||||||
(
|
(
|
||||||
this: ExecutionLifecycleHooks,
|
this: ExecutionLifecycleHooks,
|
||||||
@@ -56,7 +56,7 @@ s */
|
|||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ExecutionLifecycleHookName = keyof ExecutionLifecyleHookHandlers;
|
export type ExecutionLifecycleHookName = keyof ExecutionLifecycleHookHandlers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains hooks that trigger at specific events in an execution's lifecycle. Every hook has an array of callbacks to run.
|
* Contains hooks that trigger at specific events in an execution's lifecycle. Every hook has an array of callbacks to run.
|
||||||
@@ -77,7 +77,7 @@ export type ExecutionLifecycleHookName = keyof ExecutionLifecyleHookHandlers;
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export class ExecutionLifecycleHooks {
|
export class ExecutionLifecycleHooks {
|
||||||
readonly handlers: ExecutionLifecyleHookHandlers = {
|
readonly handlers: ExecutionLifecycleHookHandlers = {
|
||||||
nodeExecuteAfter: [],
|
nodeExecuteAfter: [],
|
||||||
nodeExecuteBefore: [],
|
nodeExecuteBefore: [],
|
||||||
nodeFetchedData: [],
|
nodeFetchedData: [],
|
||||||
@@ -92,18 +92,18 @@ export class ExecutionLifecycleHooks {
|
|||||||
readonly workflowData: IWorkflowBase,
|
readonly workflowData: IWorkflowBase,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
addHandler<Hook extends keyof ExecutionLifecyleHookHandlers>(
|
addHandler<Hook extends keyof ExecutionLifecycleHookHandlers>(
|
||||||
hookName: Hook,
|
hookName: Hook,
|
||||||
...handlers: Array<ExecutionLifecyleHookHandlers[Hook][number]>
|
...handlers: Array<ExecutionLifecycleHookHandlers[Hook][number]>
|
||||||
): void {
|
): void {
|
||||||
// @ts-expect-error FIX THIS
|
// @ts-expect-error FIX THIS
|
||||||
this.handlers[hookName].push(...handlers);
|
this.handlers[hookName].push(...handlers);
|
||||||
}
|
}
|
||||||
|
|
||||||
async runHook<
|
async runHook<
|
||||||
Hook extends keyof ExecutionLifecyleHookHandlers,
|
Hook extends keyof ExecutionLifecycleHookHandlers,
|
||||||
Params extends unknown[] = Parameters<
|
Params extends unknown[] = Parameters<
|
||||||
Exclude<ExecutionLifecyleHookHandlers[Hook], undefined>[number]
|
Exclude<ExecutionLifecycleHookHandlers[Hook], undefined>[number]
|
||||||
>,
|
>,
|
||||||
>(hookName: Hook, parameters: Params) {
|
>(hookName: Hook, parameters: Params) {
|
||||||
const hooks = this.handlers[hookName];
|
const hooks = this.handlers[hookName];
|
||||||
@@ -116,9 +116,3 @@ export class ExecutionLifecycleHooks {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module 'n8n-workflow' {
|
|
||||||
interface IWorkflowExecuteAdditionalData {
|
|
||||||
hooks?: ExecutionLifecycleHooks;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
43
packages/core/src/execution-engine/external-secrets-proxy.ts
Normal file
43
packages/core/src/execution-engine/external-secrets-proxy.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { Service } from '@n8n/di';
|
||||||
|
|
||||||
|
export interface IExternalSecretsManager {
|
||||||
|
updateSecrets(): Promise<void>;
|
||||||
|
hasSecret(provider: string, name: string): boolean;
|
||||||
|
getSecret(provider: string, name: string): unknown;
|
||||||
|
getSecretNames(provider: string): string[];
|
||||||
|
hasProvider(provider: string): boolean;
|
||||||
|
getProviderNames(): string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class ExternalSecretsProxy {
|
||||||
|
private manager?: IExternalSecretsManager;
|
||||||
|
|
||||||
|
setManager(manager: IExternalSecretsManager) {
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
async update() {
|
||||||
|
await this.manager?.updateSecrets();
|
||||||
|
}
|
||||||
|
|
||||||
|
getSecret(provider: string, name: string) {
|
||||||
|
return this.manager?.getSecret(provider, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasSecret(provider: string, name: string): boolean {
|
||||||
|
return !!this.manager && this.manager.hasSecret(provider, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasProvider(provider: string): boolean {
|
||||||
|
return !!this.manager && this.manager.hasProvider(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
listProviders(): string[] {
|
||||||
|
return this.manager?.getProviderNames() ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
listSecrets(provider: string): string[] {
|
||||||
|
return this.manager?.getSecretNames(provider) ?? [];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,13 @@
|
|||||||
|
import type { ExecutionLifecycleHooks } from './execution-lifecycle-hooks';
|
||||||
|
import type { ExternalSecretsProxy } from './external-secrets-proxy';
|
||||||
|
|
||||||
|
declare module 'n8n-workflow' {
|
||||||
|
interface IWorkflowExecuteAdditionalData {
|
||||||
|
hooks?: ExecutionLifecycleHooks;
|
||||||
|
externalSecretsProxy: ExternalSecretsProxy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export * from './active-workflows';
|
export * from './active-workflows';
|
||||||
export * from './interfaces';
|
export * from './interfaces';
|
||||||
export * from './routing-node';
|
export * from './routing-node';
|
||||||
@@ -6,3 +16,4 @@ export * from './partial-execution-utils';
|
|||||||
export * from './node-execution-context/utils/execution-metadata';
|
export * from './node-execution-context/utils/execution-metadata';
|
||||||
export * from './workflow-execute';
|
export * from './workflow-execute';
|
||||||
export { ExecutionLifecycleHooks } from './execution-lifecycle-hooks';
|
export { ExecutionLifecycleHooks } from './execution-lifecycle-hooks';
|
||||||
|
export { ExternalSecretsProxy, type IExternalSecretsManager } from './external-secrets-proxy';
|
||||||
|
|||||||
@@ -1,24 +1,20 @@
|
|||||||
import { mock } from 'jest-mock-extended';
|
import { mock } from 'jest-mock-extended';
|
||||||
import { LoggerProxy } from 'n8n-workflow';
|
import { LoggerProxy } from 'n8n-workflow';
|
||||||
import type {
|
import type { IDataObject, IRunExecutionData, IWorkflowExecuteAdditionalData } from 'n8n-workflow';
|
||||||
IDataObject,
|
|
||||||
IRunExecutionData,
|
|
||||||
IWorkflowExecuteAdditionalData,
|
|
||||||
SecretsHelpersBase,
|
|
||||||
} from 'n8n-workflow';
|
|
||||||
|
|
||||||
import { PLACEHOLDER_EMPTY_EXECUTION_ID } from '@/constants';
|
import { PLACEHOLDER_EMPTY_EXECUTION_ID } from '@/constants';
|
||||||
|
import type { ExternalSecretsProxy } from '@/execution-engine/external-secrets-proxy';
|
||||||
|
|
||||||
import { getAdditionalKeys } from '../get-additional-keys';
|
import { getAdditionalKeys } from '../get-additional-keys';
|
||||||
|
|
||||||
describe('getAdditionalKeys', () => {
|
describe('getAdditionalKeys', () => {
|
||||||
const secretsHelpers = mock<SecretsHelpersBase>();
|
const externalSecretsProxy = mock<ExternalSecretsProxy>();
|
||||||
const additionalData = mock<IWorkflowExecuteAdditionalData>({
|
const additionalData = mock<IWorkflowExecuteAdditionalData>({
|
||||||
executionId: '123',
|
executionId: '123',
|
||||||
webhookWaitingBaseUrl: 'https://webhook.test',
|
webhookWaitingBaseUrl: 'https://webhook.test',
|
||||||
formWaitingBaseUrl: 'https://form.test',
|
formWaitingBaseUrl: 'https://form.test',
|
||||||
variables: { testVar: 'value' },
|
variables: { testVar: 'value' },
|
||||||
secretsHelpers,
|
externalSecretsProxy,
|
||||||
});
|
});
|
||||||
|
|
||||||
const runExecutionData = mock<IRunExecutionData>({
|
const runExecutionData = mock<IRunExecutionData>({
|
||||||
@@ -30,11 +26,11 @@ describe('getAdditionalKeys', () => {
|
|||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
LoggerProxy.init(mock());
|
LoggerProxy.init(mock());
|
||||||
secretsHelpers.hasProvider.mockReturnValue(true);
|
externalSecretsProxy.hasProvider.mockReturnValue(true);
|
||||||
secretsHelpers.hasSecret.mockReturnValue(true);
|
externalSecretsProxy.hasSecret.mockReturnValue(true);
|
||||||
secretsHelpers.getSecret.mockReturnValue('secret-value');
|
externalSecretsProxy.getSecret.mockReturnValue('secret-value');
|
||||||
secretsHelpers.listSecrets.mockReturnValue(['secret1']);
|
externalSecretsProxy.listSecrets.mockReturnValue(['secret1']);
|
||||||
secretsHelpers.listProviders.mockReturnValue(['provider1']);
|
externalSecretsProxy.listProviders.mockReturnValue(['provider1']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use placeholder execution ID when none provided', () => {
|
it('should use placeholder execution ID when none provided', () => {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ function buildSecretsValueProxy(value: IDataObject): unknown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getSecretsProxy(additionalData: IWorkflowExecuteAdditionalData): IDataObject {
|
export function getSecretsProxy(additionalData: IWorkflowExecuteAdditionalData): IDataObject {
|
||||||
const secretsHelpers = additionalData.secretsHelpers;
|
const { externalSecretsProxy } = additionalData;
|
||||||
return new Proxy(
|
return new Proxy(
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
@@ -31,7 +31,7 @@ export function getSecretsProxy(additionalData: IWorkflowExecuteAdditionalData):
|
|||||||
if (typeof providerName !== 'string') {
|
if (typeof providerName !== 'string') {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (secretsHelpers.hasProvider(providerName)) {
|
if (externalSecretsProxy.hasProvider(providerName)) {
|
||||||
return new Proxy(
|
return new Proxy(
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
@@ -39,13 +39,13 @@ export function getSecretsProxy(additionalData: IWorkflowExecuteAdditionalData):
|
|||||||
if (typeof secretName !== 'string') {
|
if (typeof secretName !== 'string') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!secretsHelpers.hasSecret(providerName, secretName)) {
|
if (!externalSecretsProxy.hasSecret(providerName, secretName)) {
|
||||||
throw new ExpressionError('Could not load secrets', {
|
throw new ExpressionError('Could not load secrets', {
|
||||||
description:
|
description:
|
||||||
'The credential in use tries to use secret from an external store that could not be found',
|
'The credential in use tries to use secret from an external store that could not be found',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const retValue = secretsHelpers.getSecret(providerName, secretName);
|
const retValue = externalSecretsProxy.getSecret(providerName, secretName);
|
||||||
if (typeof retValue === 'object' && retValue !== null) {
|
if (typeof retValue === 'object' && retValue !== null) {
|
||||||
return buildSecretsValueProxy(retValue as IDataObject);
|
return buildSecretsValueProxy(retValue as IDataObject);
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,7 @@ export function getSecretsProxy(additionalData: IWorkflowExecuteAdditionalData):
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
ownKeys() {
|
ownKeys() {
|
||||||
return secretsHelpers.listSecrets(providerName);
|
return externalSecretsProxy.listSecrets(providerName);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -69,7 +69,7 @@ export function getSecretsProxy(additionalData: IWorkflowExecuteAdditionalData):
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
ownKeys() {
|
ownKeys() {
|
||||||
return secretsHelpers.listProviders();
|
return externalSecretsProxy.listProviders();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2374,7 +2374,6 @@ export interface IWorkflowExecuteAdditionalData {
|
|||||||
executionTimeoutTimestamp?: number;
|
executionTimeoutTimestamp?: number;
|
||||||
userId?: string;
|
userId?: string;
|
||||||
variables: IDataObject;
|
variables: IDataObject;
|
||||||
secretsHelpers: SecretsHelpersBase;
|
|
||||||
logAiEvent: (eventName: AiEvent, payload: AiEventPayload) => void;
|
logAiEvent: (eventName: AiEvent, payload: AiEventPayload) => void;
|
||||||
parentCallbackManager?: CallbackManager;
|
parentCallbackManager?: CallbackManager;
|
||||||
startRunnerTask<T, E = unknown>(
|
startRunnerTask<T, E = unknown>(
|
||||||
@@ -2870,17 +2869,6 @@ export interface ICheckProcessedContextData {
|
|||||||
|
|
||||||
export type N8nAIProviderType = 'openai' | 'unknown';
|
export type N8nAIProviderType = 'openai' | 'unknown';
|
||||||
|
|
||||||
export interface SecretsHelpersBase {
|
|
||||||
update(): Promise<void>;
|
|
||||||
waitForInit(): Promise<void>;
|
|
||||||
|
|
||||||
getSecret(provider: string, name: string): unknown;
|
|
||||||
hasSecret(provider: string, name: string): boolean;
|
|
||||||
hasProvider(provider: string): boolean;
|
|
||||||
listProviders(): string[];
|
|
||||||
listSecrets(provider: string): string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Functionality = 'regular' | 'configuration-node' | 'pairedItem';
|
export type Functionality = 'regular' | 'configuration-node' | 'pairedItem';
|
||||||
|
|
||||||
export type CallbackManager = CallbackManagerLC;
|
export type CallbackManager = CallbackManagerLC;
|
||||||
|
|||||||
Reference in New Issue
Block a user