core: validator: support 'convert' as default action

fix #505, close #506
pull/507/head
undefined 2 years ago
parent 02c0d7aaa8
commit 1124ed9506

@ -385,12 +385,12 @@ export class ContestEditHandler extends Handler {
@param('pids', Types.Content)
@param('rated', Types.Boolean)
@param('code', Types.String, true)
@param('autoHide', Types.Boolean, true)
@param('autoHide', Types.Boolean)
@param('assign', Types.CommaSeperatedArray, true)
@param('lock', Types.UnsignedInt, true)
@param('contestDuration', Types.Float, true)
@param('maintainer', Types.NumericArray, true)
@param('allowViewCode', Types.Boolean, true)
@param('allowViewCode', Types.Boolean)
async postUpdate(
domainId: string, tid: ObjectID, beginAtDate: string, beginAtTime: string, duration: number,
title: string, content: string, rule: string, _pids: string, rated = false,

@ -358,7 +358,7 @@ class DiscussionRawHandler extends DiscussionHandler {
@param('drid', Types.ObjectID, true)
@param('drrid', Types.ObjectID, true)
@param('time', Types.UnsignedInt, true)
@param('all', Types.Boolean, true)
@param('all', Types.Boolean)
async get(domainId: string, did: ObjectID, drid: ObjectID, drrid: ObjectID, ts: number, all = false) {
if (all) {
this.response.body.history = await discussion.getHistory(domainId, drrid || drid || did);

@ -297,7 +297,7 @@ class SystemUserPrivHandler extends SystemHandler {
@requireSudo
@param('uid', Types.Int)
@param('priv', Types.UnsignedInt)
@param('system', Types.Boolean, true)
@param('system', Types.Boolean)
async post(domainId: string, uid: number, priv: number, editSystem: boolean) {
if (!editSystem) {
const udoc = await user.getById(domainId, uid);

@ -379,7 +379,7 @@ export class ProblemDetailHandler extends ContestDetailBaseHandler {
}
@query('tid', Types.ObjectID, true)
@query('pjax', Types.Boolean, true)
@query('pjax', Types.Boolean)
async get(...args: any[]) {
// Navigate to current additional file download
// e.g. ![img](a.jpg) will navigate to ![img](./pid/file/a.jpg)

@ -32,7 +32,7 @@ class RecordListHandler extends ContestDetailBaseHandler {
@param('lang', Types.String, true)
@param('status', Types.Int, true)
@param('fullStatus', Types.Boolean)
@param('allDomain', Types.Boolean, true)
@param('allDomain', Types.Boolean)
async get(
domainId: string, page = 1, pid?: string | number, tid?: ObjectID,
uidOrName?: string, lang?: string, status?: number, full = false,

@ -8,7 +8,7 @@ import saslprep from 'saslprep';
type InputType = string | number | Record<string, any> | any[];
export type Converter<T> = (value: any) => T;
export type Validator<Loose extends boolean = true> = (value: Loose extends true ? any : InputType) => boolean;
export type Type<T> = readonly [Converter<T>, Validator<false>?, boolean?];
export type Type<T> = readonly [Converter<T>, Validator<false>?, (boolean | 'convert')?];
export interface Types {
// String outputs
@ -95,7 +95,7 @@ export const Types: Types = {
Float: [(v) => +v, (v) => Number.isFinite(+v)],
ObjectID: [(v) => new ObjectID(v), ObjectID.isValid],
Boolean: [(v) => v && !['false', 'off'].includes(v), null, true],
Boolean: [(v) => !!(v && !['false', 'off'].includes(v)), null, 'convert'],
Date: [
(v) => {
const d = v.split('-');

@ -9,7 +9,7 @@ type ClassDecorator = <T extends new (...args: any[]) => any>(Class: T) => T ext
export interface ParamOption<T> {
name: string,
source: 'all' | 'get' | 'post' | 'route',
isOptional?: boolean,
isOptional?: boolean | 'convert',
convert?: Converter<T>,
validate?: Validator,
}
@ -60,6 +60,8 @@ function _descriptor(v: ParamOption<any>) {
if (item.validate && !item.validate(value)) throw new ValidationError(item.name);
if (item.convert) c.push(item.convert(value));
else c.push(value);
} else if (item.isOptional === 'convert') {
c.push(item.convert ? item.convert(value) : value);
} else c.push(undefined);
}
return originalMethod.call(this, ...c);

Loading…
Cancel
Save