kite-framework
Version:
Modern, fast, flexible HTTP-JSON-RPC framework
362 lines (361 loc) • 11.6 kB
TypeScript
/***
* Copyright (c) 2017 [Arthur Xie]
* <https://github.com/kite-js/kite>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
import 'reflect-metadata';
import { Class } from '../types/class';
/**
* Annotate classes as Kite models
*
* A Kite model is:
* + a class map roughly to client request parameters
* + a class map exactly to DB table (MongoDB document)
* + often used as return values & data access object
*
* ## Description
* As descripted above, Kite models are used for data access: mapping data from requests and transfer to
* database services, or mapping from database outputs and respond to client.
*
* The filter function named "_$filter" is created for Kite model at script loading time, custom
* filter function is also alowed, if "_$filter" appears in a Kite model, Kite will not create
* new filter function and will use original _$filter() to filter input paramters instead.
*
* "_$filter" function should declare as:
* ```typescript
* @Model() User {
* name: string;
* password: string;
*
* _$filter(inputs: any) {
* // code your custom filter here
* }
* }
* ```
*
* __NOTE__
*
* When a Kite model is used for mapping client inputs, Kite will create an object with "new" operator
* `new User()` when client request comes in, without any constructor parameter, so if a Kite mode is
* coded like following, it'll not work as expected:
* ```typescript
* @Model()
* class User {
* @In() name: string;
* @In() password: string;
* @In() type: string;
*
* constructor(type: string) {
* this.type = type;
* }
* }
* ```
*
* It works fine if you manually initialize an object like below:
* > let user = new User("admin"); // user.type = "admin"
*
* In Kite, it will not work as expected, an object is created like:
*
* > let user2 = new User();
*
* or ( if $cleanModel is set to `true`)
*
* > let user2 = Object.create(User.prototype);
*/
export declare function Model(globalRule?: FilterRule): <T extends Class>(constructor: T) => void;
/**
* Array type definition, use only in FilterRule
*/
export interface ArrayType {
/**
* template of input array, etc:
* + Simple array: "Array<Number>", this is also the default value
* + Two-Dimensional array: "Array<Array<String>>"
*
* Kite will resolve this template to filter inputs, this field is only used for
* describing array dimensional formats
*/
template?: string;
/**
* element type of array template, can be one of:
* + Number
* + String
* + Boolean
* + Date
* + Kite Model
* + Any other types can be set value by `new Type(input)`
*
* if field `template` is declared clearly with the following types, this field can be omitted:
* + Number
* + String
* + Boolean
* + Date
*/
elementType?: Function;
}
/**
* Client inputs parameters filter rules
*/
export interface FilterRule {
/**
* type: boolean - indicates a input field is required or not, default is "false"
* + `true` - the field is required, if it's omitted from input, Kite will throw an
* + `fasle` - the field is optional, on error will be thrown even if it's omitted
*
* @type boolean
*/
required?: boolean;
/**
* available values for this input field, default is `undefined` - no value
* checking for this field. If an array is given, Kite will check input value
* by searching given array, for example:
* ```typescript
* @In({
* values: ['Java', 'Javascript', 'PHP', 'GO', 'Python']
* })
* language: string;
* ```
* means input parameter "language" should be one of
* _'Java', 'Javascript', 'PHP', 'GO', * 'Python'_.
* If only one value is given to this array, the input field is limmited to
* this value.
*
*/
values?: string[] | number[];
/**
* define the minimal value of input data, this filter supports the following types:
* + __`Number`__ - model property type is number, filter value must be a number
* + __`String`__ - model property type is string, filter value must be a string
* + __`Date`__ - model property type is Date, filter value can be `string` or `number` or `Date`,
* if string or number is given, this value should be possible to convert to a `Date` object:
* - if the value is a number, it must be a integer value representing the number of
* milliseconds since 1 January 1970 00:00:00 UTC
* - if the value is a string, it represents a date which should be recognized by `Date.parse()` method
*
* min value check for other types is not supported.
*
* examples:
* ```typescript
* @Model()
* export class Foo {
* @In({
* min: 18
* })
* age: number;
*
* @In({
* min: '2017'
* })
* year: string;
*
* @In({
* min: '2017-09-01'
* })
* startDate: Date;
*
* @In({
* min: new Date('2017-09-01')
* })
* startDate2: Date;
* }
* ```
*/
min?: number | string | Date;
/**
* define the maximal value of input data, this filter supports the following types:
* + __`Number`__ - model property type is number, filter value must be a number
* + __`String`__ - model property type is string, filter value must be a string
* + __`Date`__ - model property type is Date, filter value can be `string` or `number` or `Date`,
* if string or number is given, this value should be possible to convert to a `Date` object:
* - if the value is a number, it must be a integer value representing the number of
* milliseconds since 1 January 1970 00:00:00 UTC
* - if the value is a string, it represents a date which should be recognized by `Date.parse()` method
*
* max value check for other types is not supported.
*
* examples:
* ```typescript
* @Model()
* export class Foo {
* @In({
* max: 50
* })
* age: number;
*
* @In({
* max: '2020'
* })
* year: string;
*
* @In({
* max: '2017-09-30'
* })
* endDate: Date;
*
* @In({
* max: new Date('2017-09-30')
* })
* endDate2: Date;
* }
* ```
*/
max?: number | string | Date;
/**
* define the minimal length of a string, affects only on string types
*/
minLen?: number;
/**
* define the maximal length of a string, affects only on string types
*/
maxLen?: number;
/**
* limit input string with special length, example:
* ```typescript
* @In({
* len: 2
* })
* state: string; // state length is restricted to 2
* ```
*/
len?: number;
/**
* test input string with special pattern, example:
*
* ```typescript
* @In({
* pattern: /^[a-z\d_\.\-]+@[a-z\d_\.\-]+\.[a-z]{2,}$/i
* })
* email: string; // check email with the pattern
* ```
*/
pattern?: RegExp;
/**
* filter input value with given function, return value is used as
* new value for this field, example:
*
* ```typescript
* @In({
* filter: function(state: string) {
* return state ? state.toUpperCase() : '';
* }
* })
* state: string; // conver input state to upper case
* ```
*/
filter?: (input: any) => any;
/**
* group property checking, tells Kite this field is connected
* with other fields, at least one of grouped fields is required at input.
* The following example means: either "phone" or "email" is required,
* if both "phone" and "email" are emitted an error will be thrown:
*
* ```typescript
* @Model()
* class ExampleParam {
* @In({
* group: 'email' // "phone" will group with "email"
* })
* phone: string;
*
* @In()
* email: string;
* }
* ```
*/
group?: string | string[];
/**
* accept empty values, the following values are considered as empty:
* + `""` - an empty string
* + `" "` - whitespace string
* + `null` - null in JSON object
* + `[]` - an empty array
*
* default is `false`, disallow empty values
*
* `true` - allow empty values as input value
* `false` - disallow empty values as input value
*
* By default, Kite filters out a string if it's empty; therefore controllers always
* receives nonempty strings from "String" typed parameters.
*
* In most cases, people do not want "required" fields are empty strings, just like
* HTML `<input>` tag, if attribute "required" is set but nothing inputed, browser will warn you.
*
* But sometimes people do, for example empty string is frequently used in database design,
* empty fields do mean something in some cases, so set this field to `true` if you want
* Kite accept empty strings.
*
*/
allowEmpty?: boolean;
/**
* disable trim action from strings
*
* Kite trim benginning & ending spaces from string defaultly:
*
* `true` - do not trim
* `false` - trim
*/
trim?: boolean;
/**
* element type of property, used when source type is Array. if this filter is omitted, Kite will
* pass the whole input array to the controller.
*
* example:
* ```typescript
* class Foo {
* @In({
* arrayType: {
* template: 'Array<Number>',
* elementType: Number
* }
* })
* arr: Array<Number>;
* }
* ```
*/
arrayType?: ArrayType;
}
/**
* Decorate a model property as input
*
* Kite will retrive data for this property from client request, and filter the raw data with filter rules if given.
*
* Note this decorator must be used in a Kite model, or the rules won't take affect
*
* ```typescript
* @Model()
* export class User {
* // Filter input parameter "name": it's a required value, and min length is 3
* @In({
* required: true,
* minLength: 3
* }) name: string;
*
* @In({
* required: true,
* minLength: 6
* }) password: string;
* }
*
* ```
*/
export declare function In(rules?: FilterRule): (target: Object, property: string) => void;
/**
* Check if a class / prototype is a Kite model
* @param cls target to check
*/
export declare function isKiteModel(cls: any): boolean;
/**
* Check if a class / prototype has Kite input mappings
* @param cls target to check
*/
export declare function hasModelInputs(cls: any): boolean;