Skip to content

Commit

Permalink
feat: Enhance error handling with type-specific linters and error par…
Browse files Browse the repository at this point in the history
…sing (casbin#210)
  • Loading branch information
HashCookie authored Jan 19, 2025
1 parent 02883dd commit 2fd22b8
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 33 deletions.
4 changes: 2 additions & 2 deletions app/components/ModelEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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('');
Expand Down Expand Up @@ -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));
}
}, []);

Expand Down
4 changes: 2 additions & 2 deletions app/components/editor/hooks/useRunTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -85,7 +85,7 @@ async function enforcer(props: RunTestProps) {
const errorMessage = e instanceof Error ? e.message : 'Unknown error occurred';
props.onResponse(<div className="text-red-500">{errorMessage}</div>);
props.onResponse([]);
setError(errorMessage);
setError(parseError(errorMessage));
}
}

Expand Down
6 changes: 5 additions & 1 deletion app/components/editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -328,6 +328,8 @@ export const EditorScreen = () => {
indentUnit.of(' '),
EditorView.lineWrapping,
buttonPlugin(openDrawerWithMessage, extractContent, 'policy'),
linter(policyLinter),
lintGutter(),
]}
basicSetup={{
lineNumbers: true,
Expand Down Expand Up @@ -397,6 +399,8 @@ export const EditorScreen = () => {
indentUnit.of(' '),
EditorView.lineWrapping,
buttonPlugin(openDrawerWithMessage, extractContent, 'request'),
linter(requestLinter),
lintGutter(),
]}
basicSetup={{
lineNumbers: true,
Expand Down
53 changes: 29 additions & 24 deletions app/utils/casbinLinter.ts
Original file line number Diff line number Diff line change
@@ -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);
87 changes: 87 additions & 0 deletions app/utils/errorHandler.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
27 changes: 23 additions & 4 deletions app/utils/errorManager.ts
Original file line number Diff line number Diff line change
@@ -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};
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,
};
}

0 comments on commit 2fd22b8

Please sign in to comment.