ydb-nodejs-sdk icon indicating copy to clipboard operation
ydb-nodejs-sdk copied to clipboard

JSONDocument Value сериализуется как объект и приводит к ошибке

Open nikolaymatrosov opened this issue 1 year ago • 7 comments

Bug Report

Environment and system information:

  • ydb-nodejs-sdk version: 5.8.0
  • Node.js version: does'n matter
  • NPM version: does'n matter
  • Operation System: does'n matter

Current behavior:

Сейчас корректно работает только строка размеченная как JSONDocument. Но это очень неудобно на уровне модели постоянно помнить, что нужно сериализовать данные в строку. Было бы удобнее унести это глубже.

Expected behavior:

Я могу разметить свойство с объектом @declareType(Types.JSON_DOCUMENT) и он возьмет на себя всю «магию».

Steps to reproduce:

Если в наследнике TypedData разметить свойство с типом object декоратором с типом JSON_DOCUMENT

export class Event<T> extends TypedData {

    @declareType(Types.JSON_DOCUMENT)
    public payload: object;
...
}

То при передаче Event.asTypedCollection([event]) в параметры метода session.execute мы получим

{
  "type": {
    "listType": {
      "item": {
        "structType": {
          "members": [
            {
              "name": "payload",
              "type": {
                "typeId": 4612
              }
            }
          ]
        }
      }
    }
  },
  "value": {
    "items": [
      {
        "items": [
          {
            "textValue": { <=== Вот тут проблема. Объект, а нужна строка.
              "key": "value",
              ...
            }
          }
        ]
      }
    ]
  }
}

Внимание надо обратить на то что в "textValue" попадает объект. В итоге в логах я вижу:

Error: Unexpected transport error code 13! Error itself: {"code":13,"details":"Request message serialization failure: The \"string\" argument must be of type string or an instance of Buffer or ArrayBuffer. Received an instance of Object","metadata":{}}

Вот такой вариант не приводит к ошибкам, но пользоваться им не удобно.

export class Event<T> extends TypedData {

    @declareType(Types.JSON_DOCUMENT)
    public payload: string; <== Различие в типе свойства модели и соответсвенно во всем остальном коде, который его исползует.
...
}

Related code:

Мне кажется проблема, что в preparePrimitiveValue объект не сериализуется в строку. В этом месте можно было бы проверять, что для JSONDocument'а, а возможно и просто JSON, передан объект, и тогда прогонять его через JSON.stringify. https://github.com/ydb-platform/ydb-nodejs-sdk/blob/main/src/types.ts#L475

nikolaymatrosov avatar Feb 15 '25 20:02 nikolaymatrosov

Пока обошел проблему, добавив у класса Event оверрайд метода

getValue(propertyKey: string): Ydb.IValue {
        const val = super.getValue(propertyKey);
        if (propertyKey === 'payload') {
            return {
                textValue: JSON.stringify(val.textValue),
            }
        }
        return val;
    }

Но это костыль.

nikolaymatrosov avatar Feb 15 '25 21:02 nikolaymatrosov

Привет! Спасибо за issue! Я постараюсь посмотреть, что я могу сделать. Подскажи, пожалуйста, что бы ты хотел передавать как Types.JSON_DOCUMENT, там будут примитивные типы (string, number, boolean, null, bigint, Long) ?

polRk avatar Feb 26 '25 14:02 polRk

Я бы хотел передавать там объекты или массивы.

там будут примитивные типы (string, number, boolean, null, bigint, Long) ?

Если речь про свойства эти объектов, то да. Однако я не ожидаю, что это будут плоские объекты.

Скорее есть ожидание, что это должны быть объекты переживающие JSON.parse(JSON.stringify())

var assert = require('assert');

const good = {foo: 1}
assert.deepEqual(good, JSON.parse(JSON.stringify(good)))

const bad = {foo: new Date()}
assert.deepEqual(bad, JSON.parse(JSON.stringify(bad)))

nikolaymatrosov avatar Feb 26 '25 15:02 nikolaymatrosov

Если речь про свойства эти объектов, то да.

А если так?

export class Event<T> extends TypedData {

    @declareType(Types.JSON_DOCUMENT)
    public payload: string;

    @declareType(Types.JSON_DOCUMENT)
    public payload2: number;

    @declareType(Types.JSON_DOCUMENT)
    public payload3: boolean;

    @declareType(Types.JSON_DOCUMENT)
    public payload3: bigint;

    @declareType(Types.JSON_DOCUMENT)
    public payload3: Long;

    @declareType(Types.JSON_DOCUMENT)
    public payload3: Date;

    @declareType(Types.JSON_DOCUMENT)
    public payload3: { toString(): string };

    @declareType(Types.JSON_DOCUMENT)
    public payload3: { toJSON(): string };
...
}

polRk avatar Feb 27 '25 18:02 polRk

А можно что-то такого вообще сделать? https://github.com/typegoose/typegoose?tab=readme-ov-file Чтобы не дублировать типы, а обойтись @prop()

nikolaymatrosov avatar Mar 04 '25 17:03 nikolaymatrosov

А можно что-то такого вообще сделать? https://github.com/typegoose/typegoose?tab=readme-ov-file Чтобы не дублировать типы, а обойтись @prop()

Вообще есть планы закопать апи с декораторами... Добавить автоматический inference в нативные типы js если это возможно. А так же поддержать работу любых объектов, которые реализуют интерфейс с методами а-ля toYDB(jsNative): ydbValue, toNative(ydbValue): jsNative, нейминг не окончательный.

Пока я готов пофиксить конкретный баг, но не делать что-то новое в этом направлении.

polRk avatar Mar 05 '25 19:03 polRk

И в планах к концу года - внедрение в популярные ORM (Drizzle, TypeORM, Prisma, ...). Отсюда и желание закопать апи с декораторами, это есть в ормках. Пока декораторы нативно не появятся в ECMAScript - это все experimental

polRk avatar Mar 05 '25 19:03 polRk