joi icon indicating copy to clipboard operation
joi copied to clipboard

how do I convert a string into a number? and use number checks

Open xenoterracide opened this issue 2 years ago • 1 comments

Context

[email protected]
v16.14.2
Darwin 5c52309d33e3 20.6.0 Darwin Kernel Version 20.6.0: Tue Apr 19 21:04:45 PDT 2022; root:xnu-7195.141.29~1/RELEASE_X86_64 x86_64 i386 Darwin

How can we help?

I'm trying to check whether one converted object is greater than another, problem is it's not converting

  1) env
       custom conversion:
     ValidationError: "RETRY_TIMEOUT_DURATION" must be a number
      at Object.exports.process (/Users/nqy642/IdeaProjects/exceptionsone/node_modules/joi/lib/errors.js:193:16)
      at Object.internals.entry (/Users/nqy642/IdeaProjects/exceptionsone/node_modules/joi/lib/validator.js:153:26)
      at Object.exports.entry (/Users/nqy642/IdeaProjects/exceptionsone/node_modules/joi/lib/validator.js:27:30)
      at internals.Base.validate (/Users/nqy642/IdeaProjects/exceptionsone/node_modules/joi/lib/base.js:548:26)
      at Object.internals.assert (/Users/nqy642/IdeaProjects/exceptionsone/node_modules/joi/lib/index.js:225:27)
      at Object.attempt (/Users/nqy642/IdeaProjects/exceptionsone/node_modules/joi/lib/index.js:107:26)
      at Object.validateRequired (/Users/nqy642/IdeaProjects/exceptionsone/packages/util/src/validate.ts:20:37)
      at Context.<anonymous> (src/framework/util/env.spec.ts:32:19)
      at processImmediate (node:internal/timers:466:21)
      at process.topLevelDomainCallback (node:domain:152:15)
      at process.callbackTrampoline (node:internal/async_hooks:128:24)
import Joi from 'joi'
import parse from 'parse-duration'
import { Primitive, SafeDictionary } from 'ts-essentials'

export type EnvKey =
  | 'RETRY_TIMEOUT_DURATION'
  | 'AXIOS_TIMEOUT_DURATION'

export type Env = SafeDictionary<Exclude<Primitive, symbol>, EnvKey> & {
  RETRY_TIMEOUT_DURATION: number
  AXIOS_TIMEOUT_DURATION: number
}

const convertDuration = (envVar: string): number => parse(envVar)

export const envValidator = Joi.object<Env>({
  AXIOS_TIMEOUT_DURATION: Joi.number().empty().default(30000).custom(convertDuration),
  RETRY_TIMEOUT_DURATION: Joi.number()
    .greater(Joi.ref('AXIOS_TIMEOUT_DURATION'))
    .empty()
    .default(40000)
    .custom(convertDuration),
})
  it('custom conversion', () => {
    const environment = {
      RETRY_TIMEOUT_DURATION: '40s',
    }
    const valid = Joi.attempt(environment, envValidator)

    expect(valid.RETRY_TIMEOUT_DURATION).to.be.eq(40000)
  })

if I use Joi.string() it seems to work except that I can't use .greater() because it's not on string.

xenoterracide avatar Jun 23 '22 21:06 xenoterracide

In the past, I have used Joi extensions to do this:

Extension:

'use strict';

const parse = require('parse-duration');

function JoiDurationExtension(Joi) {
    return ({
        type: 'number',
        base: Joi.number(),
        coerce(value, helpers) {
            if (helpers.schema.$_getRule('duration')) {
                return {
                    value: parse(value),
                };
            }

            return { value };
        },
        rules: {
            duration: {
                convert: true,
                method() {
                    return this.$_addRule('duration');
                },
                validate(value, helpers) {
                    return true;
                },
            },
        },
    });
};

module.exports = JoiDurationExtension;

Usage:

'use strict';

const assert = require('assert');
const BaseJoi = require('joi');
const JoiDuration = require('./joi-duration');

const Joi = BaseJoi.extend(JoiDuration);
const Schema = Joi.number().duration().max(40001).prefs({ convert: true });

const value = Joi.attempt('40s', Schema);

assert.equal(typeof value, 'number', 'duration was cohersed');
assert.equal(value, 40000, 'duration was parsed');

Joi.assert('41s', Schema); // fails

jonathansamines avatar Sep 04 '22 22:09 jonathansamines