Использование when/альтернатив внутри when

Мне нужно проверить схему с помощью joi.

Объект такой:

  1. Существует обязательное свойство с именем «тип», значения которого могут быть TYPE1, TYPE2, TYPE3.
  2. Если type === 'TYPE1', мы должны ожидать свойства one, two и three на том же уровне.
  3. Если type === 'TYPE2', мы должны ожидать свойства four, five и six на том же уровне.
  4. If type === 'TYPE3', we must expect a subtype property at the same level, with possible values A and B.
    • If subtype === 'A', then we must expect properties seven, eight, nine at the same level.
    • Если subtype === 'B', то следует ожидать свойства ten, eleven, twelve на том же уровне.

Вот что я придумал:

import { object, string, validate } from 'joi';

const obj1 = { // valid
  type: 'TYPE1',
  one: '01',
  two: '02',
  three: '03',
};
const obj2 = { // valid
  type: 'TYPE2',
  four: '04',
  five: '05',
  six: '06',
};
const obj3 = { // valid
  type: 'TYPE3',
  subtype: 'A',
  seven: '07',
  eight: '08',
  nine: '09',
};
const obj4 = { // valid
  type: 'TYPE3',
  subtype: 'B',
  ten: '10',
  eleven: '11',
  twelve: '12',
};

const joiSchema = object({
  // point 1
  type: string().valid('TYPE1', 'TYPE2', 'TYPE3').required(),
})
.when(
  // point 2
  object({ type: 'TYPE1' }).unknown(),
  {
    then: object({
      one: string().required(),
      two: string().required(),
      three: string().required(),
    }),
  },
)
.when(
  // point 3
  object({ type: 'TYPE2' }).unknown(),
  {
    then: object({
      four: string().required(),
      five: string().required(),
      six: string().required(),
    }),
  },
)
.when(
  // point 4
  object({ type: 'TYPE3' }).unknown(),
  {
    then: object(
      // if type === 'TYPE3', check for one of those schemas
      {
        // point 4
        subtype: string().valid('A', 'B'),
      },
    ).when(
      // point 4.1
      object({ subtype: 'A' }).unknown(),
      {
        then: object({
          seven: string().required(),
          eight: string().required(),
          nine: string().required(),
        }),
      },
    ).when(
      // point 4.2
      object({ subtype: 'B' }).unknown(),
      {
        then: object({
          ten: string().required(),
          eleven: string().required(),
          twelve: string().required(),
        }),
      },
    ),
  },
);

const result1 = validate(obj1, joiSchema);
console.log('Validating obj1')
if (result1.error) {
  console.error(result1.error.message);
}

const result2 = validate(obj2, joiSchema);
console.log('Validating obj2')
if (result2.error) {
  console.error(result2.error.message);
}

const result3 = validate(obj3, joiSchema);
console.log('Validating obj3')
if (result3.error) {
  console.error(result3.error.message);
}

const result4 = validate(obj4, joiSchema);
console.log('Validating obj4')
if (result4.error) {
  console.error(result4.error.message);
}

Этот фрагмент доступен для тестирования здесь: https://repl.it/@AlessandroDe5/Joi-MCVE< /а>

Он показывает эту ошибку:

AssertionError [ERR_ASSERTION]: Cannot merge type object with another type: alternatives
    at new AssertionError (internal/errors.js:315:11)
    at Object.exports.assert (/home/runner/node_modules/hoek/lib/index.js:559:11)
    at internals.Object.concat (/home/runner/node_modules/joi/lib/types/any/index.js:148:14)
    at internals.Alternatives.when (/home/runner/node_modules/joi/lib/types/alternatives/index.js:131:52)
    at index.ts:52:6
    at Script.runInThisContext (vm.js:65:33)
    at startRepl (/usr/local/lib/node_modules/ts-node-fm/src/bin.ts:157:12)
    at Object.<anonymous> (/usr/local/lib/node_modules/ts-node-fm/src/bin.ts:66:1)
    at Module._compile (internal/modules/cjs/loader.js:654:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:665:10)

Если убрать вложенный when (пункт 4, 4.1, 4.2) работает без ошибок и естественно отклоняет два последних объекта.

Я также попытался заменить when на alternatives:

.when(
  // point 4
  object({ type: 'TYPE3' }).unknown(),
  {
    then: alternatives().try(
      object({
        subtype: string().valid('A'),
        seven: string().required(),
        eight: string().required(),
        nine: string().required(),
      }),
    ).when(
      object({
        subtype: string().valid('B'),
        ten: string().required(),
        eleven: string().required(),
        twelve: string().required(),
      }),
    ),
  },
);

Как и следовало ожидать, я получаю точно такую ​​​​же ошибку.

Есть ли способ как-то выполнить задачу?


person BackSlash    schedule 22.05.2019    source источник
comment
Вы уже проверили эту проблему github? github.com/hapijs/joi/issues/1021 Это очень похоже на вашу проблему . Однако довольно странно, что ошибка возникает в строке 52.   -  person briosheje    schedule 22.05.2019
comment
@briosheje Да, я просмотрел эту проблему, к сожалению, они говорят использовать when/is/then/otherwise, что ограничивает, потому что у меня может быть только две ветки (then, otherwise). Мой MCVE упрощен, мой реальный сценарий состоит примерно из 10 различных схем, которые различаются в зависимости от subtype, поэтому я не могу использовать это решение здесь. Возможно, я мог бы связать when/otherwise, чтобы воспроизвести сценарий if/else if, но это выглядит уродливо. Это единственное решение?   -  person BackSlash    schedule 22.05.2019
comment
Не уверен, лично я никогда раньше не использовал joi. Возможно, у кого-то есть другие идеи. В противном случае вы можете открыть другую проблему или повторно открыть существующую, возможно, автор или команда могут оказать вам дополнительную помощь.   -  person briosheje    schedule 22.05.2019


Ответы (1)


Я предлагаю другой подход с использованием Joi.alternatives(), таким образом вы можете избежать вложенных когда, поэтому ваша схема должна выглядеть так:

Joi.alternatives().try(
    Joi.object({
        type: Joi.string().valid('TYPE1').required(),
        one: Joi.string().required(),
        two: Joi.string().required(),
        three: Joi.string().required(),
    }),
    Joi.object({
        type: Joi.string().valid('TYPE2').required(),
        four: Joi.string().required(),
        five: Joi.string().required(),
        six: Joi.string().required(),
    }),
    Joi.object({
        type: Joi.string().valid('TYPE3').required(),
        subtype: Joi.string().valid('A', 'B')
    })
        .when(Joi.object({ subtype: 'A' }).unknown(), {
            then: Joi.object({
                seven: Joi.string().required(),
                eight: Joi.string().required(),
                nine: Joi.string().required(),
            }),
        })
        .when(Joi.object({ subtype: 'B' }).unknown(), {
            then: Joi.object({
                ten: Joi.string().required(),
                eleven: Joi.string().required(),
                twelve: Joi.string().required(),
            }),
        }),
);
person soltex    schedule 24.05.2019
comment
К сожалению, как сказано в комментариях, у меня намного больше свойств и возможных значений, чем показано в вопросе (я сделал короткий пример, чтобы продемонстрировать проблему, мой реальный сценарий больше), поэтому это приведет к большому количеству ненужных повторений. В итоге я отказался от joi в пользу или ajv, которые поддерживают проверку по JSON-Schema draft 07 (который поддерживает вложенные операторы when и намного более удобочитаем, чем js-схема Joi). Спасибо большое! - person BackSlash; 24.05.2019
comment
Без проблем! но да, я согласен, Джой не может быть правильным вариантом для сложных схем. - person soltex; 24.05.2019