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,
+ };
+}