feat(API): Add Public API endpoint for updating variables (#15315)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™
2025-05-12 16:50:24 +02:00
committed by GitHub
parent 2674d7fe61
commit 52bf9203f0
4 changed files with 82 additions and 31 deletions

View File

@@ -30,7 +30,7 @@ export const RESOURCES = {
export const API_KEY_RESOURCES = {
tag: [...DEFAULT_OPERATIONS] as const,
workflow: [...DEFAULT_OPERATIONS, 'move', 'activate', 'deactivate'] as const,
variable: ['create', 'delete', 'list'] as const,
variable: DEFAULT_OPERATIONS,
securityAudit: ['generate'] as const,
project: ['create', 'update', 'delete', 'list'] as const,
user: ['read', 'list', 'create', 'changeRole', 'delete'] as const,

View File

@@ -8,9 +8,35 @@ delete:
parameters:
- $ref: '../schemas/parameters/variableId.yml'
responses:
'204':
'201':
description: Operation successful.
'401':
$ref: '../../../../shared/spec/responses/unauthorized.yml'
'404':
$ref: '../../../../shared/spec/responses/notFound.yml'
put:
x-eov-operation-id: updateVariable
x-eov-operation-handler: v1/handlers/variables/variables.handler
tags:
- Variables
summary: Update a variable
description: Update a variable from your instance.
requestBody:
description: Payload for variable to update.
content:
application/json:
schema:
$ref: '../schemas/variable.yml'
required: true
responses:
'204':
description: Operation successful.
'400':
$ref: '../../../../shared/spec/responses/badRequest.yml'
'401':
$ref: '../../../../shared/spec/responses/unauthorized.yml'
'403':
$ref: '../../../../shared/spec/responses/forbidden.yml'
'404':
$ref: '../../../../shared/spec/responses/notFound.yml'

View File

@@ -27,6 +27,15 @@ export = {
res.status(201).send();
},
],
updateVariable: [
isLicensed('feat:variables'),
apiKeyHasScopeWithGlobalScopeFallback({ scope: 'variable:update' }),
async (req: VariablesRequest.Update, res: Response) => {
await Container.get(VariablesController).updateVariable(req);
res.status(204).send();
},
],
deleteVariable: [
isLicensed('feat:variables'),
apiKeyHasScopeWithGlobalScopeFallback({ scope: 'variable:delete' }),

View File

@@ -1,3 +1,5 @@
import type { User, Variables } from '@n8n/db';
import { FeatureNotLicensedError } from '@/errors/feature-not-licensed.error';
import { createOwnerWithApiKey } from '@test-integration/db/users';
import { createVariable, getVariableOrFail } from '@test-integration/db/variables';
@@ -6,7 +8,9 @@ import { setupTestServer } from '@test-integration/utils';
import * as testDb from '../shared/test-db';
describe('Variables in Public API', () => {
let owner: User;
const testServer = setupTestServer({ endpointGroups: ['publicApi'] });
const licenseErrorMessage = new FeatureNotLicensedError('feat:variables').message;
beforeAll(async () => {
await testDb.init();
@@ -14,6 +18,8 @@ describe('Variables in Public API', () => {
beforeEach(async () => {
await testDb.truncate(['Variables', 'User']);
owner = await createOwnerWithApiKey();
});
describe('GET /variables', () => {
@@ -22,7 +28,6 @@ describe('Variables in Public API', () => {
* Arrange
*/
testServer.license.enable('feat:variables');
const owner = await createOwnerWithApiKey();
const variables = await Promise.all([createVariable(), createVariable(), createVariable()]);
/**
@@ -45,12 +50,6 @@ describe('Variables in Public API', () => {
});
it('if not licensed, should reject', async () => {
/**
* Arrange
*/
const owner = await createOwnerWithApiKey();
/**
* Act
*/
@@ -60,10 +59,7 @@ describe('Variables in Public API', () => {
* Assert
*/
expect(response.status).toBe(403);
expect(response.body).toHaveProperty(
'message',
new FeatureNotLicensedError('feat:variables').message,
);
expect(response.body).toHaveProperty('message', licenseErrorMessage);
});
});
@@ -73,7 +69,6 @@ describe('Variables in Public API', () => {
* Arrange
*/
testServer.license.enable('feat:variables');
const owner = await createOwnerWithApiKey();
const variablePayload = { key: 'key', value: 'value' };
/**
@@ -97,7 +92,6 @@ describe('Variables in Public API', () => {
/**
* Arrange
*/
const owner = await createOwnerWithApiKey();
const variablePayload = { key: 'key', value: 'value' };
/**
@@ -112,21 +106,52 @@ describe('Variables in Public API', () => {
* Assert
*/
expect(response.status).toBe(403);
expect(response.body).toHaveProperty(
'message',
new FeatureNotLicensedError('feat:variables').message,
);
expect(response.body).toHaveProperty('message', licenseErrorMessage);
});
});
describe('PUT /variables/:id', () => {
const variablePayload = { key: 'updatedKey', value: 'updatedValue' };
let variable: Variables;
beforeEach(async () => {
variable = await createVariable();
});
it('if licensed, should update a variable', async () => {
testServer.license.enable('feat:variables');
const response = await testServer
.publicApiAgentFor(owner)
.put(`/variables/${variable.id}`)
.send(variablePayload);
expect(response.status).toBe(204);
const updatedVariable = await getVariableOrFail(variable.id);
expect(updatedVariable).toEqual(expect.objectContaining(variablePayload));
});
it('if not licensed, should reject', async () => {
const response = await testServer
.publicApiAgentFor(owner)
.put(`/variables/${variable.id}`)
.send(variablePayload);
expect(response.status).toBe(403);
expect(response.body).toHaveProperty('message', licenseErrorMessage);
});
});
describe('DELETE /variables/:id', () => {
let variable: Variables;
beforeEach(async () => {
variable = await createVariable();
});
it('if licensed, should delete a variable', async () => {
/**
* Arrange
*/
testServer.license.enable('feat:variables');
const owner = await createOwnerWithApiKey();
const variable = await createVariable();
/**
* Act
@@ -143,12 +168,6 @@ describe('Variables in Public API', () => {
});
it('if not licensed, should reject', async () => {
/**
* Arrange
*/
const owner = await createOwnerWithApiKey();
const variable = await createVariable();
/**
* Act
*/
@@ -160,10 +179,7 @@ describe('Variables in Public API', () => {
* Assert
*/
expect(response.status).toBe(403);
expect(response.body).toHaveProperty(
'message',
new FeatureNotLicensedError('feat:variables').message,
);
expect(response.body).toHaveProperty('message', licenseErrorMessage);
});
});
});