mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
refactor: Add dto for POST /credentials endpoint (#13306)
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
import { CreateCredentialDto } from '../create-credential.dto';
|
||||
|
||||
describe('CreateCredentialDto', () => {
|
||||
describe('Valid requests', () => {
|
||||
test.each([
|
||||
{
|
||||
name: 'with required fields',
|
||||
request: {
|
||||
name: 'My API Credentials',
|
||||
type: 'apiKey',
|
||||
data: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'with optional projectId',
|
||||
request: {
|
||||
name: 'My API Credentials',
|
||||
type: 'apiKey',
|
||||
data: {
|
||||
apiKey: '123',
|
||||
isAdmin: true,
|
||||
},
|
||||
projectId: 'project123',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'with data object',
|
||||
request: {
|
||||
name: 'My API Credentials',
|
||||
type: 'oauth2',
|
||||
data: {
|
||||
clientId: '123',
|
||||
clientSecret: 'secret',
|
||||
},
|
||||
},
|
||||
},
|
||||
])('should validate $name', ({ request }) => {
|
||||
const result = CreateCredentialDto.safeParse(request);
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
test('should not strip out properties from the data object', () => {
|
||||
const result = CreateCredentialDto.safeParse({
|
||||
name: 'My API Credentials',
|
||||
type: 'apiKey',
|
||||
data: {
|
||||
apiKey: '123',
|
||||
otherProperty: 'otherValue',
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data).toEqual({
|
||||
name: 'My API Credentials',
|
||||
type: 'apiKey',
|
||||
data: {
|
||||
apiKey: '123',
|
||||
otherProperty: 'otherValue',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Invalid requests', () => {
|
||||
test.each([
|
||||
{
|
||||
name: 'missing name',
|
||||
request: {
|
||||
type: 'apiKey',
|
||||
data: {},
|
||||
},
|
||||
expectedErrorPath: ['name'],
|
||||
},
|
||||
{
|
||||
name: 'empty name',
|
||||
request: {
|
||||
name: '',
|
||||
type: 'apiKey',
|
||||
data: {},
|
||||
},
|
||||
expectedErrorPath: ['name'],
|
||||
},
|
||||
{
|
||||
name: 'name too long',
|
||||
request: {
|
||||
name: 'a'.repeat(129),
|
||||
type: 'apiKey',
|
||||
data: {},
|
||||
},
|
||||
expectedErrorPath: ['name'],
|
||||
},
|
||||
{
|
||||
name: 'missing type',
|
||||
request: {
|
||||
name: 'My API Credentials',
|
||||
data: {},
|
||||
},
|
||||
expectedErrorPath: ['type'],
|
||||
},
|
||||
{
|
||||
name: 'empty type',
|
||||
request: {
|
||||
name: 'My API Credentials',
|
||||
type: '',
|
||||
data: {},
|
||||
},
|
||||
expectedErrorPath: ['type'],
|
||||
},
|
||||
{
|
||||
name: 'type too long',
|
||||
request: {
|
||||
name: 'My API Credentials',
|
||||
type: 'a'.repeat(33),
|
||||
data: {},
|
||||
},
|
||||
expectedErrorPath: ['type'],
|
||||
},
|
||||
{
|
||||
name: 'missing data',
|
||||
request: {
|
||||
name: 'My API Credentials',
|
||||
type: 'apiKey',
|
||||
},
|
||||
expectedErrorPath: ['data'],
|
||||
},
|
||||
{
|
||||
name: 'invalid data type',
|
||||
request: {
|
||||
name: 'My API Credentials',
|
||||
type: 'apiKey',
|
||||
data: 'invalid',
|
||||
},
|
||||
expectedErrorPath: ['data'],
|
||||
},
|
||||
])('should fail validation for $name', ({ request, expectedErrorPath }) => {
|
||||
const result = CreateCredentialDto.safeParse(request);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
|
||||
if (expectedErrorPath) {
|
||||
expect(result.error?.issues[0].path).toEqual(expectedErrorPath);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
import { z } from 'zod';
|
||||
import { Z } from 'zod-class';
|
||||
|
||||
export class CreateCredentialDto extends Z.class({
|
||||
name: z.string().min(1).max(128),
|
||||
type: z.string().min(1).max(32),
|
||||
data: z.record(z.string(), z.unknown()),
|
||||
projectId: z.string().optional(),
|
||||
}) {}
|
||||
@@ -39,6 +39,7 @@ export { CommunityRegisteredRequestDto } from './license/community-registered-re
|
||||
export { PullWorkFolderRequestDto } from './source-control/pull-work-folder-request.dto';
|
||||
export { PushWorkFolderRequestDto } from './source-control/push-work-folder-request.dto';
|
||||
|
||||
export { CreateCredentialDto } from './credentials/create-credential.dto';
|
||||
export { VariableListRequestDto } from './variables/variables-list-request.dto';
|
||||
export { CredentialsGetOneRequestQuery } from './credentials/credentials-get-one-request.dto';
|
||||
export { CredentialsGetManyRequestQuery } from './credentials/credentials-get-many-request.dto';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { CreateCredentialDto } from '@n8n/api-types';
|
||||
import {
|
||||
AiChatRequestDto,
|
||||
AiApplySuggestionRequestDto,
|
||||
@@ -14,7 +15,6 @@ import { FREE_AI_CREDITS_CREDENTIAL_NAME } from '@/constants';
|
||||
import { CredentialsService } from '@/credentials/credentials.service';
|
||||
import { Body, Post, RestController } from '@/decorators';
|
||||
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
|
||||
import type { CredentialRequest } from '@/requests';
|
||||
import { AuthenticatedRequest } from '@/requests';
|
||||
import { AiService } from '@/services/ai.service';
|
||||
import { UserService } from '@/services/user.service';
|
||||
@@ -84,18 +84,17 @@ export class AiController {
|
||||
try {
|
||||
const aiCredits = await this.aiService.createFreeAiCredits(req.user);
|
||||
|
||||
const credentialProperties: CredentialRequest.CredentialProperties = {
|
||||
const credentialProperties: CreateCredentialDto = {
|
||||
name: FREE_AI_CREDITS_CREDENTIAL_NAME,
|
||||
type: OPEN_AI_API_CREDENTIAL_TYPE,
|
||||
data: {
|
||||
apiKey: aiCredits.apiKey,
|
||||
url: aiCredits.url,
|
||||
},
|
||||
isManaged: true,
|
||||
projectId: payload?.projectId,
|
||||
};
|
||||
|
||||
const newCredential = await this.credentialsService.createCredential(
|
||||
const newCredential = await this.credentialsService.createManagedCredential(
|
||||
credentialProperties,
|
||||
req.user,
|
||||
);
|
||||
|
||||
@@ -487,7 +487,7 @@ export class CredentialsHelper extends ICredentialsHelper {
|
||||
}
|
||||
|
||||
export function createCredentialsFromCredentialsEntity(
|
||||
credential: CredentialsEntity,
|
||||
credential: ICredentialsDb,
|
||||
encrypt = false,
|
||||
): Credentials {
|
||||
const { id, name, type, data } = credential;
|
||||
|
||||
@@ -28,6 +28,7 @@ describe('CredentialsController', () => {
|
||||
);
|
||||
|
||||
let req: AuthenticatedRequest;
|
||||
let res = mock<Response>();
|
||||
beforeAll(() => {
|
||||
req = { user: { id: '123' } } as AuthenticatedRequest;
|
||||
});
|
||||
@@ -48,7 +49,7 @@ describe('CredentialsController', () => {
|
||||
id: newCredentialsPayload.projectId,
|
||||
});
|
||||
|
||||
credentialsService.createCredential.mockResolvedValue(createdCredentials);
|
||||
credentialsService.createUnmanagedCredential.mockResolvedValue(createdCredentials);
|
||||
|
||||
sharedCredentialsRepository.findCredentialOwningProject.mockResolvedValue(
|
||||
projectOwningCredentialData,
|
||||
@@ -56,11 +57,15 @@ describe('CredentialsController', () => {
|
||||
|
||||
// Act
|
||||
|
||||
const newApiKey = await credentialsController.createCredentials(req);
|
||||
const newApiKey = await credentialsController.createCredentials(
|
||||
req,
|
||||
res,
|
||||
newCredentialsPayload,
|
||||
);
|
||||
|
||||
// Assert
|
||||
|
||||
expect(credentialsService.createCredential).toHaveBeenCalledWith(
|
||||
expect(credentialsService.createUnmanagedCredential).toHaveBeenCalledWith(
|
||||
newCredentialsPayload,
|
||||
req.user,
|
||||
);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { nanoId, date } from 'minifaker';
|
||||
import { CREDENTIAL_ERRORS, CredentialDataError, Credentials, type ErrorReporter } from 'n8n-core';
|
||||
import { CREDENTIAL_EMPTY_VALUE, type ICredentialType } from 'n8n-workflow';
|
||||
|
||||
@@ -7,11 +6,7 @@ import { CREDENTIAL_BLANKING_VALUE } from '@/constants';
|
||||
import type { CredentialTypes } from '@/credential-types';
|
||||
import { CredentialsService } from '@/credentials/credentials.service';
|
||||
import type { CredentialsEntity } from '@/databases/entities/credentials-entity';
|
||||
import type { AuthenticatedRequest } from '@/requests';
|
||||
|
||||
import { createNewCredentialsPayload, credentialScopes } from './credentials.test-data';
|
||||
|
||||
let req = { user: { id: '123' } } as AuthenticatedRequest;
|
||||
import type { CredentialsRepository } from '@/databases/repositories/credentials.repository';
|
||||
|
||||
describe('CredentialsService', () => {
|
||||
const credType = mock<ICredentialType>({
|
||||
@@ -34,8 +29,9 @@ describe('CredentialsService', () => {
|
||||
|
||||
const errorReporter = mock<ErrorReporter>();
|
||||
const credentialTypes = mock<CredentialTypes>();
|
||||
const credentialsRepository = mock<CredentialsRepository>();
|
||||
const service = new CredentialsService(
|
||||
mock(),
|
||||
credentialsRepository,
|
||||
mock(),
|
||||
mock(),
|
||||
mock(),
|
||||
@@ -81,69 +77,6 @@ describe('CredentialsService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('createCredential', () => {
|
||||
it('it should create new credentials and return with scopes', async () => {
|
||||
// Arrange
|
||||
|
||||
const encryptedData = 'encryptedData';
|
||||
|
||||
const newCredentialPayloadData = createNewCredentialsPayload();
|
||||
|
||||
const newCredential = mock<CredentialsEntity>({
|
||||
name: newCredentialPayloadData.name,
|
||||
data: JSON.stringify(newCredentialPayloadData.data),
|
||||
type: newCredentialPayloadData.type,
|
||||
});
|
||||
|
||||
const encryptedDataResponse = {
|
||||
name: newCredentialPayloadData.name,
|
||||
type: newCredentialPayloadData.type,
|
||||
updatedAt: date(),
|
||||
data: encryptedData,
|
||||
};
|
||||
|
||||
const saveCredentialsResponse = {
|
||||
id: nanoId.nanoid(),
|
||||
name: newCredentialPayloadData.name,
|
||||
type: newCredentialPayloadData.type,
|
||||
updatedAt: encryptedDataResponse.updatedAt,
|
||||
createdAt: date(),
|
||||
data: encryptedDataResponse.data,
|
||||
isManaged: false,
|
||||
shared: undefined,
|
||||
};
|
||||
|
||||
service.prepareCreateData = jest.fn().mockReturnValue(newCredential);
|
||||
service.createEncryptedData = jest.fn().mockImplementation(() => encryptedDataResponse);
|
||||
service.save = jest.fn().mockResolvedValue(saveCredentialsResponse);
|
||||
service.getCredentialScopes = jest.fn().mockReturnValue(credentialScopes);
|
||||
|
||||
// Act
|
||||
|
||||
const createdCredential = await service.createCredential(newCredentialPayloadData, req.user);
|
||||
|
||||
// Assert
|
||||
|
||||
expect(service.prepareCreateData).toHaveBeenCalledWith(newCredentialPayloadData);
|
||||
expect(service.createEncryptedData).toHaveBeenCalledWith(null, newCredential);
|
||||
expect(service.save).toHaveBeenCalledWith(
|
||||
newCredential,
|
||||
encryptedDataResponse,
|
||||
req.user,
|
||||
newCredentialPayloadData.projectId,
|
||||
);
|
||||
expect(service.getCredentialScopes).toHaveBeenCalledWith(
|
||||
req.user,
|
||||
saveCredentialsResponse.id,
|
||||
);
|
||||
|
||||
expect(createdCredential).toEqual({
|
||||
...saveCredentialsResponse,
|
||||
scopes: credentialScopes,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('decrypt', () => {
|
||||
const data = {
|
||||
clientId: 'abc123',
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import type { CreateCredentialDto } from '@n8n/api-types';
|
||||
import type { Scope } from '@n8n/permissions';
|
||||
import { nanoId, date } from 'minifaker';
|
||||
import { randomString } from 'n8n-workflow';
|
||||
|
||||
import type { CredentialRequest } from '@/requests';
|
||||
|
||||
type NewCredentialWithSCopes = {
|
||||
scopes: Scope[];
|
||||
name: string;
|
||||
@@ -33,9 +32,7 @@ export const credentialScopes: Scope[] = [
|
||||
'credential:update',
|
||||
];
|
||||
|
||||
export const createNewCredentialsPayload = (
|
||||
payload?: Partial<CredentialRequest.CredentialProperties>,
|
||||
): CredentialRequest.CredentialProperties => {
|
||||
export const createNewCredentialsPayload = (payload?: Partial<CreateCredentialDto>) => {
|
||||
return {
|
||||
name,
|
||||
type,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
CreateCredentialDto,
|
||||
CredentialsGetManyRequestQuery,
|
||||
CredentialsGetOneRequestQuery,
|
||||
GenerateCredentialNameRequestQuery,
|
||||
@@ -7,6 +8,7 @@ import { GlobalConfig } from '@n8n/config';
|
||||
// eslint-disable-next-line n8n-local-rules/misplaced-n8n-typeorm-import
|
||||
import { In } from '@n8n/typeorm';
|
||||
import { Logger } from 'n8n-core';
|
||||
import type { ICredentialDataDecryptedObject } from 'n8n-workflow';
|
||||
import { deepCopy } from 'n8n-workflow';
|
||||
import { z } from 'zod';
|
||||
|
||||
@@ -24,14 +26,14 @@ import {
|
||||
RestController,
|
||||
ProjectScope,
|
||||
} from '@/decorators';
|
||||
import { Param, Query } from '@/decorators/args';
|
||||
import { Body, Param, Query } from '@/decorators/args';
|
||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||
import { ForbiddenError } from '@/errors/response-errors/forbidden.error';
|
||||
import { NotFoundError } from '@/errors/response-errors/not-found.error';
|
||||
import { EventService } from '@/events/event.service';
|
||||
import { License } from '@/license';
|
||||
import { listQueryMiddleware } from '@/middlewares';
|
||||
import { CredentialRequest } from '@/requests';
|
||||
import { AuthenticatedRequest, CredentialRequest } from '@/requests';
|
||||
import { NamingService } from '@/services/naming.service';
|
||||
import { UserManagementMailer } from '@/user-management/email';
|
||||
import * as utils from '@/utils';
|
||||
@@ -162,8 +164,15 @@ export class CredentialsController {
|
||||
}
|
||||
|
||||
@Post('/')
|
||||
async createCredentials(req: CredentialRequest.Create) {
|
||||
const newCredential = await this.credentialsService.createCredential(req.body, req.user);
|
||||
async createCredentials(
|
||||
req: AuthenticatedRequest,
|
||||
_: Response,
|
||||
@Body payload: CreateCredentialDto,
|
||||
) {
|
||||
const newCredential = await this.credentialsService.createUnmanagedCredential(
|
||||
payload,
|
||||
req.user,
|
||||
);
|
||||
|
||||
const project = await this.sharedCredentialsRepository.findCredentialOwningProject(
|
||||
newCredential.id,
|
||||
@@ -217,10 +226,12 @@ export class CredentialsController {
|
||||
req.body,
|
||||
decryptedData,
|
||||
);
|
||||
const newCredentialData = this.credentialsService.createEncryptedData(
|
||||
credentialId,
|
||||
preparedCredentialData,
|
||||
);
|
||||
const newCredentialData = this.credentialsService.createEncryptedData({
|
||||
id: credential.id,
|
||||
name: preparedCredentialData.name,
|
||||
type: preparedCredentialData.type,
|
||||
data: preparedCredentialData.data as unknown as ICredentialDataDecryptedObject,
|
||||
});
|
||||
|
||||
const responseData = await this.credentialsService.update(credentialId, newCredentialData);
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { CreateCredentialDto } from '@n8n/api-types';
|
||||
import { Service } from '@n8n/di';
|
||||
import type { Scope } from '@n8n/permissions';
|
||||
// eslint-disable-next-line n8n-local-rules/misplaced-n8n-typeorm-import
|
||||
@@ -44,6 +45,10 @@ export type CredentialsGetSharedOptions =
|
||||
| { allowGlobalScope: true; globalScope: Scope }
|
||||
| { allowGlobalScope: false };
|
||||
|
||||
type CreateCredentialOptions = CreateCredentialDto & {
|
||||
isManaged: boolean;
|
||||
};
|
||||
|
||||
@Service()
|
||||
export class CredentialsService {
|
||||
constructor(
|
||||
@@ -280,20 +285,6 @@ export class CredentialsService {
|
||||
});
|
||||
}
|
||||
|
||||
async prepareCreateData(
|
||||
data: CredentialRequest.CredentialProperties,
|
||||
): Promise<CredentialsEntity> {
|
||||
const { id, ...rest } = data;
|
||||
|
||||
// This saves us a merge but requires some type casting. These
|
||||
// types are compatible for this case.
|
||||
const newCredentials = this.credentialsRepository.create(rest as ICredentialsDb);
|
||||
|
||||
await validateEntity(newCredentials);
|
||||
|
||||
return newCredentials;
|
||||
}
|
||||
|
||||
async prepareUpdateData(
|
||||
data: CredentialRequest.CredentialProperties,
|
||||
decryptedData: ICredentialDataDecryptedObject,
|
||||
@@ -318,10 +309,18 @@ export class CredentialsService {
|
||||
return updateData;
|
||||
}
|
||||
|
||||
createEncryptedData(credentialId: string | null, data: CredentialsEntity): ICredentialsDb {
|
||||
const credentials = new Credentials({ id: credentialId, name: data.name }, data.type);
|
||||
createEncryptedData(credential: {
|
||||
id: string | null;
|
||||
name: string;
|
||||
type: string;
|
||||
data: ICredentialDataDecryptedObject;
|
||||
}): ICredentialsDb {
|
||||
const credentials = new Credentials(
|
||||
{ id: credential.id, name: credential.name },
|
||||
credential.type,
|
||||
);
|
||||
|
||||
credentials.setData(data.data as unknown as ICredentialDataDecryptedObject);
|
||||
credentials.setData(credential.data);
|
||||
|
||||
const newCredentialData = credentials.getDataToSave() as ICredentialsDb;
|
||||
|
||||
@@ -673,16 +672,36 @@ export class CredentialsService {
|
||||
* Create a new credential in user's account and return it along the scopes
|
||||
* If a projectId is send, then it also binds the credential to that specific project
|
||||
*/
|
||||
async createCredential(credentialsData: CredentialRequest.CredentialProperties, user: User) {
|
||||
const newCredential = await this.prepareCreateData(credentialsData);
|
||||
async createUnmanagedCredential(dto: CreateCredentialDto, user: User) {
|
||||
return await this.createCredential({ ...dto, isManaged: false }, user);
|
||||
}
|
||||
|
||||
const encryptedData = this.createEncryptedData(null, newCredential);
|
||||
/**
|
||||
* Create a new managed credential in user's account and return it along the scopes.
|
||||
* Managed credentials are managed by n8n and cannot be edited by the user.
|
||||
*/
|
||||
async createManagedCredential(dto: CreateCredentialDto, user: User) {
|
||||
return await this.createCredential({ ...dto, isManaged: true }, user);
|
||||
}
|
||||
|
||||
private async createCredential(opts: CreateCredentialOptions, user: User) {
|
||||
const encryptedCredential = this.createEncryptedData({
|
||||
id: null,
|
||||
name: opts.name,
|
||||
type: opts.type,
|
||||
data: opts.data as ICredentialDataDecryptedObject,
|
||||
});
|
||||
|
||||
const credentialEntity = this.credentialsRepository.create({
|
||||
...encryptedCredential,
|
||||
isManaged: opts.isManaged,
|
||||
});
|
||||
|
||||
const { shared, ...credential } = await this.save(
|
||||
newCredential,
|
||||
encryptedData,
|
||||
credentialEntity,
|
||||
encryptedCredential,
|
||||
user,
|
||||
credentialsData.projectId,
|
||||
opts.projectId,
|
||||
);
|
||||
|
||||
const scopes = await this.getCredentialScopes(user, credential.id);
|
||||
|
||||
@@ -147,8 +147,6 @@ export declare namespace CredentialRequest {
|
||||
isManaged?: boolean;
|
||||
}>;
|
||||
|
||||
type Create = AuthenticatedRequest<{}, {}, CredentialProperties>;
|
||||
|
||||
type Get = AuthenticatedRequest<{ credentialId: string }, {}, {}, Record<string, string>>;
|
||||
|
||||
type GetMany = AuthenticatedRequest<{}, {}, {}, ListQuery.Params & { includeScopes?: string }> & {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { GlobalConfig } from '@n8n/config';
|
||||
import { Container } from '@n8n/di';
|
||||
import type { Scope } from '@sentry/node';
|
||||
import * as a from 'assert';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { Credentials } from 'n8n-core';
|
||||
import type { ICredentialDataDecryptedObject } from 'n8n-workflow';
|
||||
@@ -17,6 +18,7 @@ import type { ListQuery } from '@/requests';
|
||||
import { CredentialsTester } from '@/services/credentials-tester.service';
|
||||
|
||||
import {
|
||||
decryptCredentialData,
|
||||
getCredentialById,
|
||||
saveCredential,
|
||||
shareCredentialWithProjects,
|
||||
@@ -777,11 +779,12 @@ describe('POST /credentials', () => {
|
||||
].sort(),
|
||||
);
|
||||
|
||||
const credential = await Container.get(CredentialsRepository).findOneByOrFail({ id });
|
||||
const credential = await getCredentialById(id);
|
||||
a.ok(credential);
|
||||
|
||||
expect(credential.name).toBe(payload.name);
|
||||
expect(credential.type).toBe(payload.type);
|
||||
expect(credential.data).not.toBe(payload.data);
|
||||
expect(await decryptCredentialData(credential)).toStrictEqual(payload.data);
|
||||
|
||||
const sharedCredential = await Container.get(SharedCredentialsRepository).findOneOrFail({
|
||||
relations: { project: true, credentials: true },
|
||||
|
||||
@@ -23,6 +23,13 @@ export async function encryptCredentialData(
|
||||
return Object.assign(credential, coreCredential.getDataToSave());
|
||||
}
|
||||
|
||||
export async function decryptCredentialData(credential: ICredentialsDb): Promise<unknown> {
|
||||
const { createCredentialsFromCredentialsEntity } = await import('@/credentials-helper');
|
||||
const coreCredential = createCredentialsFromCredentialsEntity(credential);
|
||||
|
||||
return coreCredential.getData();
|
||||
}
|
||||
|
||||
const emptyAttributes = {
|
||||
name: 'test',
|
||||
type: 'test',
|
||||
|
||||
@@ -12,6 +12,7 @@ import type {
|
||||
INodeCredentialTestResult,
|
||||
} from 'n8n-workflow';
|
||||
import axios from 'axios';
|
||||
import type { CreateCredentialDto } from '@n8n/api-types';
|
||||
|
||||
export async function getCredentialTypes(baseUrl: string): Promise<ICredentialType[]> {
|
||||
const { data } = await axios.get(baseUrl + 'types/credentials.json', { withCredentials: true });
|
||||
@@ -48,13 +49,9 @@ export async function getAllCredentialsForWorkflow(
|
||||
|
||||
export async function createNewCredential(
|
||||
context: IRestApiContext,
|
||||
data: ICredentialsDecrypted,
|
||||
projectId?: string,
|
||||
payload: CreateCredentialDto,
|
||||
): Promise<ICredentialsResponse> {
|
||||
return await makeRestApiRequest(context, 'POST', '/credentials', {
|
||||
...data,
|
||||
projectId,
|
||||
} as unknown as IDataObject);
|
||||
return await makeRestApiRequest(context, 'POST', '/credentials', payload);
|
||||
}
|
||||
|
||||
export async function deleteCredential(context: IRestApiContext, id: string): Promise<boolean> {
|
||||
|
||||
@@ -300,11 +300,12 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, () => {
|
||||
projectId?: string,
|
||||
): Promise<ICredentialsResponse> => {
|
||||
const settingsStore = useSettingsStore();
|
||||
const credential = await credentialsApi.createNewCredential(
|
||||
rootStore.restApiContext,
|
||||
data,
|
||||
const credential = await credentialsApi.createNewCredential(rootStore.restApiContext, {
|
||||
name: data.name,
|
||||
type: data.type,
|
||||
data: data.data ?? {},
|
||||
projectId,
|
||||
);
|
||||
});
|
||||
|
||||
if (data?.homeProject && !credential.homeProject) {
|
||||
credential.homeProject = data.homeProject as ProjectSharingData;
|
||||
|
||||
Reference in New Issue
Block a user