diff --git a/app/components/ModelEditor.tsx b/app/components/ModelEditor.tsx index f6ed6f6..b853ae1 100644 --- a/app/components/ModelEditor.tsx +++ b/app/components/ModelEditor.tsx @@ -9,13 +9,13 @@ import { CasbinConfSupport } from '@/app/components/editor/casbin-mode/casbin-co import { linter, lintGutter } from '@codemirror/lint'; import { casbinLinter } from '@/app/utils/casbinLinter'; import { newModel } from 'casbin'; -import { setError } from '@/app/utils/errorManager'; import { buttonPlugin } from '@/app/components/editor/ButtonPlugin'; import { extractPageContent } from '@/app/utils/contentExtractor'; import { useLang } from '@/app/context/LangContext'; import SidePanelChat from '@/app/components/SidePanelChat'; import { example } from '@/app/components/editor/casbin-mode/example'; import { clsx } from 'clsx'; +import { parseError, setError } from '@/app/utils/errorManager'; export const ModelEditor = () => { const [modelText, setModelText] = useState(''); @@ -43,7 +43,7 @@ export const ModelEditor = () => { await newModel(text); setError(null); } catch (e) { - setError((e as Error).message); + setError(parseError((e as Error).message)); } }, []); diff --git a/app/components/editor/hooks/useRunTest.tsx b/app/components/editor/hooks/useRunTest.tsx index 70f0964..67233c6 100755 --- a/app/components/editor/hooks/useRunTest.tsx +++ b/app/components/editor/hooks/useRunTest.tsx @@ -13,7 +13,7 @@ // limitations under the License. import { createCasbinEngine } from '../CasbinEngine'; -import { setError } from '@/app/utils/errorManager'; +import { setError, parseError } from '@/app/utils/errorManager'; interface RunTestProps { model: string; @@ -85,7 +85,7 @@ async function enforcer(props: RunTestProps) { const errorMessage = e instanceof Error ? e.message : 'Unknown error occurred'; props.onResponse(
{errorMessage}
); props.onResponse([]); - setError(errorMessage); + setError(parseError(errorMessage)); } } diff --git a/app/components/editor/index.tsx b/app/components/editor/index.tsx index ceced5d..627e434 100755 --- a/app/components/editor/index.tsx +++ b/app/components/editor/index.tsx @@ -21,12 +21,12 @@ import { buttonPlugin } from './ButtonPlugin'; import { useLang } from '@/app/context/LangContext'; import LanguageMenu from '@/app/components/LanguageMenu'; import { linter, lintGutter } from '@codemirror/lint'; -import { casbinLinter } from '@/app/utils/casbinLinter'; import { toast, Toaster } from 'react-hot-toast'; import { CustomConfigPanel } from './CustomConfigPanel'; import { loadingOverlay } from './LoadingOverlayExtension'; import useEngineVersions from './hooks/useEngineVersions'; import { MessageWithTooltip } from './MessageWithTooltip'; +import { casbinLinter, policyLinter, requestLinter } from '@/app/utils/casbinLinter'; export const EditorScreen = () => { const { @@ -328,6 +328,8 @@ export const EditorScreen = () => { indentUnit.of(' '), EditorView.lineWrapping, buttonPlugin(openDrawerWithMessage, extractContent, 'policy'), + linter(policyLinter), + lintGutter(), ]} basicSetup={{ lineNumbers: true, @@ -397,6 +399,8 @@ export const EditorScreen = () => { indentUnit.of(' '), EditorView.lineWrapping, buttonPlugin(openDrawerWithMessage, extractContent, 'request'), + linter(requestLinter), + lintGutter(), ]} basicSetup={{ lineNumbers: true, diff --git a/app/utils/casbinLinter.ts b/app/utils/casbinLinter.ts index ae36057..17f483c 100644 --- a/app/utils/casbinLinter.ts +++ b/app/utils/casbinLinter.ts @@ -1,31 +1,36 @@ import { Diagnostic } from '@codemirror/lint'; import { EditorView } from '@codemirror/view'; import { getError } from './errorManager'; +import { ErrorType } from './errorHandler'; -export const casbinLinter = (view: EditorView): Diagnostic[] => { - const diagnostics: Diagnostic[] = []; +function createLinter(errorType: ErrorType) { + return (view: EditorView): Diagnostic[] => { + const diagnostics: Diagnostic[] = []; + const error = getError(); - const runTestError = getError(); - if (runTestError) { - const lineMatch = runTestError.match(/line (\d+)/); - if (lineMatch) { - const errorLine = parseInt(lineMatch[1], 10); - const line = view.state.doc.line(errorLine); - diagnostics.push({ - from: line.from, - to: line.to, - severity: 'error', - message: runTestError, - }); - } else { - diagnostics.push({ - from: 0, - to: view.state.doc.length, - severity: 'error', - message: runTestError, - }); + if (error && error.type === errorType) { + if (error.line) { + const line = view.state.doc.line(error.line); + diagnostics.push({ + from: line.from, + to: line.to, + severity: 'error', + message: error.message, + }); + } else { + diagnostics.push({ + from: 0, + to: view.state.doc.length, + severity: 'error', + message: error.message, + }); + } } - } - return diagnostics; -}; + return diagnostics; + }; +} + +export const casbinLinter = createLinter(ErrorType.MODEL); +export const policyLinter = createLinter(ErrorType.POLICY); +export const requestLinter = createLinter(ErrorType.REQUEST); diff --git a/app/utils/errorHandler.ts b/app/utils/errorHandler.ts new file mode 100644 index 0000000..b596f84 --- /dev/null +++ b/app/utils/errorHandler.ts @@ -0,0 +1,87 @@ +export enum ErrorType { + MODEL = 'model', + POLICY = 'policy', + REQUEST = 'request', + UNKNOWN = 'unknown', +} + +interface ErrorDetector { + type: ErrorType; + detect: (error: string) => boolean; +} + +// Request +const REQUEST_ERROR_PATTERNS = [] as const; + +// Policy +const POLICY_ERROR_PATTERNS = [] as const; + +// Model +const MODEL_ERROR_PATTERNS = ['missing required sections'] as const; + +export const errorDetectors: ErrorDetector[] = [ + { + type: ErrorType.REQUEST, + detect: (error: string) => { + return ( + REQUEST_ERROR_PATTERNS.some((pattern) => { + return error.toLowerCase().includes(pattern); + }) || error.toLowerCase().includes('rvals:') + ); + }, + }, + { + type: ErrorType.POLICY, + detect: (error: string) => { + return POLICY_ERROR_PATTERNS.some((pattern) => { + return error.toLowerCase().includes(pattern); + }); + }, + }, + { + type: ErrorType.MODEL, + detect: (error: string) => { + return ( + MODEL_ERROR_PATTERNS.some((pattern) => { + return error.toLowerCase().includes(pattern); + }) || + (!error.toLowerCase().includes('request') && !error.toLowerCase().includes('policy') && !error.toLowerCase().includes('rvals:')) + ); + }, + }, +]; + +export function getErrorType(error: string): ErrorType { + for (const detector of errorDetectors) { + if (detector.detect(error)) { + return detector.type; + } + } + return ErrorType.UNKNOWN; +} + +export interface ErrorHandlers { + onModelError?: (error: string) => void; + onPolicyError?: (error: string) => void; + onRequestError?: (error: string) => void; + onUnknownError?: (error: string) => void; +} + +export function handleError(error: string, handlers: ErrorHandlers): void { + const errorType = getErrorType(error); + + switch (errorType) { + case ErrorType.MODEL: + handlers.onModelError?.(error); + break; + case ErrorType.POLICY: + handlers.onPolicyError?.(error); + break; + case ErrorType.REQUEST: + handlers.onRequestError?.(error); + break; + case ErrorType.UNKNOWN: + handlers.onUnknownError?.(error); + break; + } +} diff --git a/app/utils/errorManager.ts b/app/utils/errorManager.ts index 857cb5c..2ae6ee4 100644 --- a/app/utils/errorManager.ts +++ b/app/utils/errorManager.ts @@ -1,7 +1,26 @@ -let currentError: string | null = null; +import { ErrorType, getErrorType } from './errorHandler'; -export const setError = (error: string | null) => { +interface ErrorState { + message: string; + type: ErrorType; + line?: number; +} + +let currentError: ErrorState | null = null; + +export function setError(error: ErrorState | null) { currentError = error; -}; +} + +export function getError() { + return currentError; +} -export const getError = () => {return currentError}; \ No newline at end of file +export function parseError(errorMessage: string): ErrorState { + const lineMatch = errorMessage.match(/line (\d+)/); + return { + message: errorMessage, + type: getErrorType(errorMessage), + line: lineMatch ? parseInt(lineMatch[1], 10) : undefined, + }; +}