zadam / trilium

Build your personal knowledge base with Trilium Notes
GNU Affero General Public License v3.0
27.2k stars 1.9k forks source link

(Bug report) Returning Object from backend API throws a JSON circular structure error #2265

Closed QuentinLeCaignec closed 2 years ago

QuentinLeCaignec commented 3 years ago

Preflight Checklist

Trilium Version

0.48.3

What operating system are you using?

Ubuntu

What is your setup?

Local + server sync

Operating System Version

Kubuntu 5.13.13-051313-generic

Expected Behavior

Some of my scripts that were previously working have broken with the 0.48 updates, which was expected. I've not run into issues with the async/await changes, but there seems to have been changes to the API which have modified some behaviour.

In my scripts, I regularly called the backend API to fetch a Note, parent Note, and get labels/values from them. As far as I know the API doc is still the same and this should still work in the same way with the same methods, apart from the small async/await changes.

Actual Behavior

I've reduced the error I found to this little testing bit with consistent results :

const map_parameters = await api.runOnServer(() => {
        const parentNote = api.startNote.getParentNotes()[0];
        //return parentNote;
        return {parentNote};
});
console.log(map_parameters);

Returning only a variable such as parentNote works fine and gives me the note in the console. Returning an Object in {} brackets, whether it contains one or multiple variables, throws a strange error.

20:48:25 error:  Error when calling POST /api/script/exec: 500 - Converting circular structure to JSON
    --> starting at object with constructor 'Note'
    |     property 'children' -> object with constructor 'Array'
    |     index 0 -> object with constructor 'Note'
    |     property 'parents' -> object with constructor 'Array'
    --- index 0 closes the circle

ws.js:17 
20:48:25 Error when calling POST /api/script/exec: 500 - Converting circular structure to JSON
    --> starting at object with constructor 'Note'
    |     property 'children' -> object with constructor 'Array'
    |     index 0 -> object with constructor 'Note'
    |     property 'parents' -> object with constructor 'Array'
    --- index 0 closes the circle
N @ ws.js:17
throwError @ toast.js:87
c @ server.js:106
async function (async)
c @ server.js:104
(anonymous) @ server.js:169
emit @ events.js:315
onMessage @ electron/js2c/renderer_init.js:93

toast.js:89 
Uncaught (in promise) Error: Error when calling POST /api/script/exec: 500 - Converting circular structure to JSON
    --> starting at object with constructor 'Note'
    |     property 'children' -> object with constructor 'Array'
    |     index 0 -> object with constructor 'Note'
    |     property 'parents' -> object with constructor 'Array'
    --- index 0 closes the circle
    at Object.throwError (toast.js:89)
    at c (server.js:106)
    at async EventEmitter.<anonymous> (server.js:165)
throwError @ toast.js:89
c @ server.js:106
async function (async)
(anonymous) @ server.js:169
emit @ events.js:315
onMessage @ electron/js2c/renderer_init.js:93

The short version of it from what I'm seeing is some problem with the way JSON is being returned, maybe a format change, but it doesn't like returning Objects from the API and becomes stuck in a circular structure when trying to convert to JSON.

I have another similar script which is working when I'm returning an Array from api.runOnServer(), this array contains multiple Objects with Notes and Values, so the problem isn't the Note object structure in itself but specifically when returning an Object directly instead of a variable or an Array.

I've also tried putting this testing code inside of an async function (which gets rids of the warning "parsing error unknown token api" but that's always been there and still worked), same results.

Additional Information

I'm wondering if there has been more changes to the API that have not been documented, since even the scripts that are working seem to be slightly off in their behaviour but maybe that's just my imagination. I swear one of my widgets seems much slower to load.

zadam commented 3 years ago

Hi, you need to change the returning like this:

return {
    parentNote: parentNote.getPojo()
};

parentNote is a complex object (which self-references itself) and cannot be serialized into JSON. It's true that this did not fail in the previous versions but still lead to bad behavior (essentially whole object graph was transferred instead of just one object which was slow).

I tried to cover most cases by auto-detection of these entities in the responses so e.g. returning object directly, or an array thereof will automagically work. But keys like parentNote will not be converted automatically leading to this failure.

QuentinLeCaignec commented 2 years ago

Thank you! That makes a lot of sense. I'm assuming you mean any Note object can't be serialized and not just the parent note. I fixed my script now :)

Was this specified in the documentation before as the proper way to return this object? If so my mistake, I must have missed it. If not, maybe that should be said in the docs?