2
0
mirror of https://github.com/frappe/books.git synced 2025-02-08 23:18:31 +00:00

incr: improve error display

- show duplicate error instead of sqlite fail dump
This commit is contained in:
18alantom 2022-08-29 18:05:33 +05:30
parent 4ebf232e18
commit d90d113981
5 changed files with 125 additions and 17 deletions

View File

@ -19,6 +19,7 @@ import { markRaw } from 'vue';
import { isPesa } from '../utils/index'; import { isPesa } from '../utils/index';
import { import {
areDocValuesEqual, areDocValuesEqual,
getInsertionError,
getMissingMandatoryMessage, getMissingMandatoryMessage,
getPreDefaultValues, getPreDefaultValues,
setChildDocIdx, setChildDocIdx,
@ -682,7 +683,12 @@ export class Doc extends Observable<DocValue | Doc[]> {
await this._preSync(); await this._preSync();
const validDict = this.getValidDict(false, true); const validDict = this.getValidDict(false, true);
const data = await this.fyo.db.insert(this.schemaName, validDict); let data: DocValueMap;
try {
data = await this.fyo.db.insert(this.schemaName, validDict);
} catch (err) {
throw getInsertionError(err as Error, validDict);
}
await this._syncValues(data); await this._syncValues(data);
this.fyo.telemetry.log(Verb.Created, this.schemaName); this.fyo.telemetry.log(Verb.Created, this.schemaName);

View File

@ -1,6 +1,7 @@
import { Fyo } from 'fyo'; import { Fyo } from 'fyo';
import { DocValue } from 'fyo/core/types'; import { DocValue, DocValueMap } from 'fyo/core/types';
import { isPesa } from 'fyo/utils'; import { isPesa } from 'fyo/utils';
import { DuplicateEntryError } from 'fyo/utils/errors';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import { Money } from 'pesa'; import { Money } from 'pesa';
import { Field, FieldType, FieldTypeEnum } from 'schemas/types'; import { Field, FieldType, FieldTypeEnum } from 'schemas/types';
@ -114,3 +115,37 @@ export function setChildDocIdx(childDocs: Doc[]) {
childDocs[idx].idx = +idx; childDocs[idx].idx = +idx;
} }
} }
export function getInsertionError(err: Error, validDict: DocValueMap): Error {
if (err.message.includes('UNIQUE constraint failed:')) {
return getDuplicateEntryError(err, validDict);
}
return err;
}
export function getDuplicateEntryError(
err: Error,
validDict: DocValueMap
): Error | DuplicateEntryError {
const matches = err.message.match(/UNIQUE constraint failed:\s(\w+)\.(\w+)$/);
if (!matches) {
return err;
}
const schemaName = matches[1];
const fieldname = matches[2];
if (!schemaName || !fieldname) {
return err;
}
const duplicateEntryError = new DuplicateEntryError(err.message, false);
duplicateEntryError.stack = err.stack;
duplicateEntryError.more = {
schemaName,
fieldname,
value: validDict[fieldname],
};
return duplicateEntryError;
}

View File

@ -1,9 +1,14 @@
export class BaseError extends Error { export class BaseError extends Error {
more: Record<string, unknown> = {};
message: string; message: string;
statusCode: number; statusCode: number;
shouldStore: boolean; shouldStore: boolean;
constructor(statusCode: number, message: string, shouldStore: boolean = true) { constructor(
statusCode: number,
message: string,
shouldStore: boolean = true
) {
super(message); super(message);
this.name = 'BaseError'; this.name = 'BaseError';
this.statusCode = statusCode; this.statusCode = statusCode;
@ -96,7 +101,7 @@ export function getDbError(err: Error) {
return CannotCommitError; return CannotCommitError;
} }
if (err.message.includes('SQLITE_CONSTRAINT: UNIQUE constraint failed:')) { if (err.message.includes('UNIQUE constraint failed:')) {
return DuplicateEntryError; return DuplicateEntryError;
} }

View File

@ -94,8 +94,8 @@ export async function handleErrorWithDialog(
const errorMessage = getErrorMessage(error, doc); const errorMessage = getErrorMessage(error, doc);
await handleError(false, error, { errorMessage, doc }); await handleError(false, error, { errorMessage, doc });
const name = error.name ?? t`Error`; const label = getErrorLabel(error);
const options: MessageDialogOptions = { message: name, detail: errorMessage }; const options: MessageDialogOptions = { message: label, detail: errorMessage };
if (reportError) { if (reportError) {
options.detail = truncate(options.detail, { length: 128 }); options.detail = truncate(options.detail, { length: 128 });
@ -106,7 +106,7 @@ export async function handleErrorWithDialog(
reportIssue(getErrorLogObject(error, { errorMessage })); reportIssue(getErrorLogObject(error, { errorMessage }));
}, },
}, },
{ label: t`OK`, action() {} }, { label: t`Cancel`, action() {} },
]; ];
} }
@ -196,3 +196,52 @@ export function reportIssue(errorLogObj?: ErrorLog) {
const urlQuery = getIssueUrlQuery(errorLogObj); const urlQuery = getIssueUrlQuery(errorLogObj);
ipcRenderer.send(IPC_MESSAGES.OPEN_EXTERNAL, urlQuery); ipcRenderer.send(IPC_MESSAGES.OPEN_EXTERNAL, urlQuery);
} }
function getErrorLabel(error: Error) {
const name = error.name;
if (!name) {
return t`Error`;
}
if (name === 'BaseError') {
return t`Error`;
}
if (name === 'ValidationError') {
return t`Validation Error`;
}
if (name === 'NotFoundError') {
return t`Not Found`;
}
if (name === 'ForbiddenError') {
return t`Forbidden Error`;
}
if (name === 'DuplicateEntryError') {
return t`Duplicate Entry`;
}
if (name === 'LinkValidationError') {
return t`Link Validation Error`;
}
if (name === 'MandatoryError') {
return t`Mandatory Error`;
}
if (name === 'DatabaseError') {
return t`Database Error`;
}
if (name === 'CannotCommitError') {
return t`Cannot Commit Error`;
}
if (name === 'NotImplemented') {
return t`Error`;
}
return t`Error`;
}

View File

@ -4,9 +4,14 @@
import { t } from 'fyo'; import { t } from 'fyo';
import { Doc } from 'fyo/model/doc'; import { Doc } from 'fyo/model/doc';
import { isPesa } from 'fyo/utils'; import { isPesa } from 'fyo/utils';
import { DuplicateEntryError, LinkValidationError } from 'fyo/utils/errors'; import {
BaseError,
DuplicateEntryError,
LinkValidationError
} from 'fyo/utils/errors';
import { Money } from 'pesa'; import { Money } from 'pesa';
import { Field, FieldType, FieldTypeEnum } from 'schemas/types'; import { Field, FieldType, FieldTypeEnum } from 'schemas/types';
import { fyo } from 'src/initFyo';
export function stringifyCircular( export function stringifyCircular(
obj: unknown, obj: unknown,
@ -24,7 +29,8 @@ export function stringifyCircular(
} }
if (cacheValue.includes(value)) { if (cacheValue.includes(value)) {
const circularKey = cacheKey[cacheValue.indexOf(value)] || '{self}'; const circularKey: string =
cacheKey[cacheValue.indexOf(value)] || '{self}';
return ignoreCircular ? undefined : `[Circular:${circularKey}]`; return ignoreCircular ? undefined : `[Circular:${circularKey}]`;
} }
@ -84,16 +90,23 @@ export function convertPesaValuesToFloat(obj: Record<string, unknown>) {
} }
export function getErrorMessage(e: Error, doc?: Doc): string { export function getErrorMessage(e: Error, doc?: Doc): string {
let errorMessage = e.message || t`An error occurred.`; const errorMessage = e.message || t`An error occurred.`;
const { schemaName, name }: { schemaName?: string; name?: string } = let { schemaName, name } = doc ?? {};
doc ?? {}; if (!doc) {
const canElaborate = !!(schemaName && name); schemaName = (e as BaseError).more?.schemaName as string | undefined;
name = (e as BaseError).more?.value as string | undefined;
}
if (e instanceof LinkValidationError && canElaborate) { if (!schemaName || !name) {
errorMessage = t`${schemaName} ${name} is linked with existing records.`; return errorMessage;
} else if (e instanceof DuplicateEntryError && canElaborate) { }
errorMessage = t`${schemaName} ${name} already exists.`;
const label = fyo.db.schemaMap[schemaName]?.label ?? schemaName;
if (e instanceof LinkValidationError) {
return t`${label} ${name} is linked with existing records.`;
} else if (e instanceof DuplicateEntryError) {
return t`${label} ${name} already exists.`;
} }
return errorMessage; return errorMessage;