mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-02 08:27:29 -08:00
fix(editor): Add documentation link to insufficient quota message (#11777)
This commit is contained in:
parent
b8c7075545
commit
1987363f79
55
packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/helpers/error-handling.test.ts
vendored
Normal file
55
packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/helpers/error-handling.test.ts
vendored
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import { RateLimitError } from 'openai';
|
||||||
|
import { OpenAIError } from 'openai/error';
|
||||||
|
|
||||||
|
import { openAiFailedAttemptHandler, getCustomErrorMessage, isOpenAiError } from './error-handling';
|
||||||
|
|
||||||
|
describe('error-handling', () => {
|
||||||
|
describe('getCustomErrorMessage', () => {
|
||||||
|
it('should return the correct custom error message for known error codes', () => {
|
||||||
|
expect(getCustomErrorMessage('insufficient_quota')).toBe(
|
||||||
|
'Insufficient quota detected. <a href="https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-langchain.openai/common-issues/#insufficient-quota" target="_blank">Learn more</a> about resolving this issue',
|
||||||
|
);
|
||||||
|
expect(getCustomErrorMessage('rate_limit_exceeded')).toBe('OpenAI: Rate limit reached');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return undefined for unknown error codes', () => {
|
||||||
|
expect(getCustomErrorMessage('unknown_error_code')).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isOpenAiError', () => {
|
||||||
|
it('should return true if the error is an instance of OpenAIError', () => {
|
||||||
|
const error = new OpenAIError('Test error');
|
||||||
|
expect(isOpenAiError(error)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if the error is not an instance of OpenAIError', () => {
|
||||||
|
const error = new Error('Test error');
|
||||||
|
expect(isOpenAiError(error)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('openAiFailedAttemptHandler', () => {
|
||||||
|
it('should handle RateLimitError and modify the error message', () => {
|
||||||
|
const error = new RateLimitError(
|
||||||
|
429,
|
||||||
|
{ code: 'rate_limit_exceeded' },
|
||||||
|
'Rate limit exceeded',
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
openAiFailedAttemptHandler(error);
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toBe(error);
|
||||||
|
expect(e.message).toBe('OpenAI: Rate limit reached');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw the error if it is not a RateLimitError', () => {
|
||||||
|
const error = new Error('Test error');
|
||||||
|
|
||||||
|
expect(() => openAiFailedAttemptHandler(error)).not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,8 +1,9 @@
|
||||||
import { OpenAIError } from 'openai/error';
|
|
||||||
import { RateLimitError } from 'openai';
|
import { RateLimitError } from 'openai';
|
||||||
|
import { OpenAIError } from 'openai/error';
|
||||||
|
|
||||||
const errorMap: Record<string, string> = {
|
const errorMap: Record<string, string> = {
|
||||||
insufficient_quota: 'OpenAI: Insufficient quota',
|
insufficient_quota:
|
||||||
|
'Insufficient quota detected. <a href="https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-langchain.openai/common-issues/#insufficient-quota" target="_blank">Learn more</a> about resolving this issue',
|
||||||
rate_limit_exceeded: 'OpenAI: Rate limit reached',
|
rate_limit_exceeded: 'OpenAI: Rate limit reached',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -23,7 +24,10 @@ export const openAiFailedAttemptHandler = (error: any) => {
|
||||||
const customErrorMessage = getCustomErrorMessage(errorCode);
|
const customErrorMessage = getCustomErrorMessage(errorCode);
|
||||||
|
|
||||||
if (customErrorMessage) {
|
if (customErrorMessage) {
|
||||||
error.message = customErrorMessage;
|
if (error.error) {
|
||||||
|
(error.error as { message: string }).message = customErrorMessage;
|
||||||
|
error.message = customErrorMessage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import NodeExecutionErrorMessage from '@/components/NodeExecutionErrorMessage.vue';
|
||||||
|
import { createComponentRenderer } from '@/__tests__/render';
|
||||||
|
|
||||||
|
const renderComponent = createComponentRenderer(NodeExecutionErrorMessage);
|
||||||
|
|
||||||
|
describe('NodeExecutionErrorMessage', () => {
|
||||||
|
it('renders the component', () => {
|
||||||
|
const { getByTestId } = renderComponent({
|
||||||
|
props: {
|
||||||
|
nodeName: 'Test Node',
|
||||||
|
errorMessage: 'An error occurred',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(getByTestId('sanitized-error-message')).toHaveTextContent('An error occurred');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders sanitized HTML in error message', () => {
|
||||||
|
const { getByTestId } = renderComponent({
|
||||||
|
props: {
|
||||||
|
nodeName: 'Test Node',
|
||||||
|
errorMessage:
|
||||||
|
'Insufficient quota detected. <a href="https://docs.n8n.io/" target="_blank">Learn more</a>',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(getByTestId('sanitized-error-message')).toContainHTML(
|
||||||
|
'Insufficient quota detected. <a href="https://docs.n8n.io/" target="_blank">Learn more</a>',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the link with the correct text', () => {
|
||||||
|
const { getByText } = renderComponent({
|
||||||
|
props: {
|
||||||
|
nodeName: 'Test Node',
|
||||||
|
errorMessage: 'An error occurred',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(getByText('Open node')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the link with the correct data attributes', () => {
|
||||||
|
const { getByText } = renderComponent({
|
||||||
|
props: {
|
||||||
|
nodeName: 'Test Node',
|
||||||
|
errorMessage: 'An error occurred',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const link = getByText('Open node');
|
||||||
|
expect(link.getAttribute('data-action')).toBe('openNodeDetail');
|
||||||
|
expect(link.getAttribute('data-action-parameter-node')).toBe('Test Node');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not render error message when it is not provided', () => {
|
||||||
|
const { queryByText } = renderComponent({
|
||||||
|
props: {
|
||||||
|
nodeName: 'Test Node',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(queryByText('An error occurred')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sanitizes malicious script in error message', () => {
|
||||||
|
const { getByTestId } = renderComponent({
|
||||||
|
props: {
|
||||||
|
nodeName: 'Test Node',
|
||||||
|
errorMessage: '<img src=x onerror=alert(1)>',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(getByTestId('sanitized-error-message')).toContainHTML('<img src="x">');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sanitizes malicious script in error message with nested tags', () => {
|
||||||
|
const { getByTestId } = renderComponent({
|
||||||
|
props: {
|
||||||
|
nodeName: 'Test Node',
|
||||||
|
errorMessage: '<div><img src=x onerror=alert(1)></div>',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(getByTestId('sanitized-error-message')).toContainHTML('<div><img src="x"></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sanitizes malicious script in error message with script tag', () => {
|
||||||
|
const { container } = renderComponent({
|
||||||
|
props: {
|
||||||
|
nodeName: 'Test Node',
|
||||||
|
errorMessage: '<script>alert(1)</script>',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(container.querySelector('script')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,5 +1,6 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useI18n } from '@/composables/useI18n';
|
import { useI18n } from '@/composables/useI18n';
|
||||||
|
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
@ -10,7 +11,8 @@ defineProps<{
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
{{ errorMessage }}<br />
|
<span v-n8n-html="errorMessage" data-test-id="sanitized-error-message"></span>
|
||||||
|
<br />
|
||||||
<a data-action="openNodeDetail" :data-action-parameter-node="nodeName">{{
|
<a data-action="openNodeDetail" :data-action-parameter-node="nodeName">{{
|
||||||
i18n.baseText('node.executionError.openNode')
|
i18n.baseText('node.executionError.openNode')
|
||||||
}}</a>
|
}}</a>
|
||||||
|
|
Loading…
Reference in a new issue