Skip to content

Commit

Permalink
Merge pull request #1734 from riganti/errormsg-coercer-debugname
Browse files Browse the repository at this point in the history
Include real type names in client-side error messages
  • Loading branch information
tomasherceg authored Dec 1, 2023
2 parents 13b2239 + ea31718 commit 9e93d76
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -248,16 +248,19 @@ type TypeMap = {
}

type DynamicTypeMetadata = {
debugName?: string,
type: "dynamic"
}

type ObjectTypeMetadata = {
type: "object",
debugName?: string,
properties: { [prop: string]: PropertyMetadata }
}

type EnumTypeMetadata = {
type: "enum",
debugName?: string,
values: { [name: string]: number },
isFlags?: boolean
}
Expand Down
6 changes: 3 additions & 3 deletions src/Framework/Framework/Resources/Scripts/metadata/coercer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { CoerceError } from "../shared-classes";
import { keys } from "../utils/objects";
import { tryCoerceEnum } from "./enums";
import { primitiveTypes } from "./primitiveTypes";
import { getObjectTypeInfo, getTypeInfo } from "./typeMap";
import { formatTypeName, getObjectTypeInfo, getTypeInfo } from "./typeMap";

/**
* Validates type of value
Expand Down Expand Up @@ -45,7 +45,7 @@ export function tryCoerce(value: any, type: TypeDefinition | null | undefined, o
}
}
}
return new CoerceError(`Unsupported type metadata ${JSON.stringify(type)}!`);
return new CoerceError(`Unsupported type metadata ${formatTypeName(type)}!`);
}

const result = core();
Expand Down Expand Up @@ -100,7 +100,7 @@ function tryCoerceArray(value: any, innerType: TypeDefinition, originalValue: an
return { value: items, wasCoerced: true };
}
}
return new CoerceError(`Value '${JSON.stringify(value)}' is not an array of type '${JSON.stringify(innerType)}'.`);
return new CoerceError(`Value '${JSON.stringify(value)}' is not an array of type '${formatTypeName(innerType)}'.`);
}

function tryCoercePrimitiveType(value: any, type: string): CoerceResult {
Expand Down
25 changes: 25 additions & 0 deletions src/Framework/Framework/Resources/Scripts/metadata/typeMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,28 @@ export function areObjectTypesEqual(currentValue: any, newVal: any): boolean {
}
return false;
}


export function formatTypeName(type: TypeDefinition, prefix = "", suffix = ""): string {
if (!compileConstants.debug)
return JSON.stringify(type)

if (typeof type === "string") {
let debugName = types[type]?.debugName
if (debugName)
return `${prefix}${debugName}${suffix} (${prefix}${type}${suffix})`
else
return prefix + type + suffix
}
if (Array.isArray(type)) {
return formatTypeName(type[0], prefix, "[]" + suffix)
}
if (type.type == "nullable") {
return formatTypeName(type.inner, prefix, "?" + suffix)
}
if (type.type == "dynamic") {
return prefix + "dynamic" + suffix
}
const typeCheck: never = type
return undefined as any
}
4 changes: 2 additions & 2 deletions src/Framework/Framework/Resources/Scripts/state-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { createArray, defineConstantProperty, isPrimitive, keys } from "./utils/objects";
import { DotvvmEvent } from "./events";
import { extendToObservableArrayIfRequired } from "./serialization/deserialize"
import { areObjectTypesEqual, getObjectTypeInfo } from "./metadata/typeMap";
import { areObjectTypesEqual, formatTypeName, getObjectTypeInfo } from "./metadata/typeMap";
import { coerce } from "./metadata/coercer";
import { patchViewModel } from "./postback/updater";
import { hackInvokeNotifySubscribers } from "./utils/knockout";
Expand Down Expand Up @@ -166,7 +166,7 @@ class FakeObservableObject<T extends object> implements UpdatableObjectExtension
}
}
} else if (!isDynamic && p.indexOf("$") !== 0) {
logWarning("state-manager", `Unknown property '${p}' set on an object of type ${typeId}.`);
logWarning("state-manager", `Unknown property '${p}' set on an object of type ${formatTypeName(typeId)}.`);
}

this[internalPropCache][p] = newObs
Expand Down
12 changes: 12 additions & 0 deletions src/Framework/Framework/Resources/Scripts/tests/coercer.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { tryCoerce } from "../metadata/coercer"
import { formatTypeName } from "../metadata/typeMap";
import { initDotvvm } from "./helper";

initDotvvm({
Expand All @@ -9,6 +10,7 @@ initDotvvm({
typeMetadata: {
t1: {
type: "object",
debugName: "MyType1",
properties: {
a: {
type: "String"
Expand Down Expand Up @@ -607,3 +609,13 @@ test("dynamic - valid, object of unknown type, nested known object", () => {
expect(result.wasCoerced).toBeFalsy();
expect(result.value).toEqual({ inner: { $type: "t1", a: "aa" } });
})

test("formatTypeName", () => {
expect(formatTypeName("t1")).toBe("MyType1 (t1)");
expect(formatTypeName("t2")).toBe("t2");
expect(formatTypeName([ "t1" ])).toBe("MyType1[] (t1[])");
expect(formatTypeName([ "t2" ])).toBe("t2[]");
expect(formatTypeName({ type: "nullable", inner: "t1" })).toBe("MyType1? (t1?)");
expect(formatTypeName({ type: "nullable", inner: "t2" })).toBe("t2?");
expect(formatTypeName([ { type:"nullable", inner: [ [ "t1" ] ] }])).toBe("MyType1[][]?[] (t1[][]?[])");
})

0 comments on commit 9e93d76

Please sign in to comment.