type-arango
Version:
ArangoDB Foxx decorators and utilities for TypeScript
370 lines (320 loc) • 11.4 kB
text/typescript
import * as Joi from 'joi'
import {
AlternativesSchema,
AnySchema,
ArraySchema,
BinarySchema,
BooleanSchema,
DateSchema,
FunctionSchema,
LazySchema,
NumberSchema,
ObjectSchema,
Schema,
StringSchema,
ValidationOptions
} from 'joi'
import {enjoi} from './utils'
import {Entity} from './models'
// https://stackoverflow.com/questions/45306782/typescript-declaration-for-polymorphic-decorator
export interface ClassAndMethodDecorator {
// Class decorator overload
<TFunction extends Function>(target: TFunction): TFunction
// Property decorator overload
<T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T>
}
export interface ClassAndPropertyDecorator {
// Class decorator overload
<TFunction extends Function>(target: TFunction): TFunction
(target: Object, propertyKey: string | symbol): void
}
// declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void
export type Related<T = any> = T | any
export type Abstract<T> = Function & {prototype: T}
export type Constructor<T> = new (...args: any[]) => T
export type Class<T = {}> = Abstract<T> | Constructor<T>
export type CollectionName = string
export interface CreateCollectionOptions extends ArangoDB.CreateCollectionOptions, Partial<RouteGroups> {
name?: CollectionName
of?: typeof Entity
auth?: RouteAuth[] | RouteAuth
roles?: RouteRoles[] | RouteRoles
routes?: Array<RouteDecorator | CollectionRoute | CollectionRouteArray>
relations?: string[] | true
cache?: number | string
}
export type CollectionRouteArray = [RouteDecorator, string | CollectionRoute | SchemaFn, ...any[]]
export interface CollectionRoute extends RouteOpt {
method: RouteDecorator
}
export interface IndexOptions {
type?: ArangoDB.IndexType
additionalFields?: string[]
sparse?: boolean
unique?: boolean
deduplicate?: boolean
}
export interface DocumentData {
[key: string]: any
}
export interface DocumentMap {
forClient?: (doc: DocumentData, opt?: any) => DocumentData
fromClient?: (doc: DocumentData, opt?: any) => DocumentData
}
export interface DocumentOptions extends DocumentMap {}
export enum LogLevel {
Error = 1,
Warn,
Info,
Debug
}
export interface Config {
logLevel: LogLevel
requiredRolesFallback: Roles
requiredWriterRolesFallback: Roles
providedRolesDefault: Roles
getUserRoles: (req: Foxx.Request) => Roles
getAuthorizedRoles: (userRoles: Roles, accessRoles: Roles) => Roles
throwForbidden: ArangoDB.HttpStatus
throwUnauthorized: ArangoDB.HttpStatus
unregisterAQLFunctionEntityGroup: boolean
header: any
dasherizeRoutes: boolean
paramOperatorSeparator: string
disableCache: boolean
defaultLocale: string
defaultCurrency: string
defaultListLimit: number
defaultListLimitMax: number
prefixCollectionName: boolean
exposeRouteFunctionsToSwagger: boolean
addAttributeWritersToReaders: boolean
stripDocumentId: boolean
stripDocumentKey: boolean
stripDocumentRev: boolean
forClient: null | DocumentForClient
fromClient: null | DocumentFromClient
}
export type RoutePreset = '*' | 'ALL' | 'ALL+' | 'CRUD' | 'CRUD+'
export type RouteDecorator = 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE' | 'LIST'
export type RouteMethod = 'get' | 'post' | 'patch' | 'put' | 'delete'
export type RouteAction = 'create' | 'read' | 'update' | 'delete' | 'list'
// export type MetadataId = 'attribute' | 'index' | 'route'
// export type MetadataTypes = AttributeMetadata | IndexMetadata | RouteMetadata
export type Roles = string[]
export type RolesFn = (returns?: void) => Roles
// export interface Metadata<T> {
// id: MetadataId,
// attribute: string
// data: T
// }
export interface DocumentAttribute {
[key: string]: AttributeObject
}
export interface AttributeObject {
attribute: string
roles?: AttributeRoles
schema?: Schema
metadata?: any
}
export interface AttributeRoles {
readers: Roles
writers: Roles
}
export interface RoleAttributes {
read: string[]
write: string[]
}
export interface RoleObject {
[key: string]: RoleAttributes
}
export type SchemaFn = (enjoi: (type?: any) => typeof Joi | any, joi?: any) => typeof Joi | boolean | Object
export interface SchemaStructure {
[key: string]: Schema
}
export type DecoratorId = 'Attribute' | 'Authorized' | 'Description' | 'Index' | 'Collection' | 'Document' | 'Nested' | 'Route'
| 'Route.auth' | 'Route.roles' | RelationType | EventDecorator | 'Task' | 'Function'
export type DecoratorStorage = {
[key in DecoratorId]?: DecoratorObject[]
}
export interface DecoratorObject {
decorator: DecoratorId
prototype: any
attribute?: string
[key: string]: any
}
export type RelationType = 'OneToOne' | 'OneToMany'// | 'ManyToOne' | 'ManyToMany'
export interface Relation<T=any> {
type: RelationType
document: T
attribute: string
}
export interface RelationStructure<T> {
[key: string]: Relation<T>
}
export interface JoiContainer {
schema: Schema
opt: ValidationOptions
}
// https://github.com/RienNeVaPlus/type-arango/issues/2
export type AnyJoiSchema = AnySchema
& ArraySchema
& AlternativesSchema
& BinarySchema
& BooleanSchema
& DateSchema
& FunctionSchema
& NumberSchema
& ObjectSchema
& StringSchema
& LazySchema
export type Enjoi = typeof enjoi
export type ValidateSchema = Schema | JoiContainer
export type ValidateSchemaFn = (returns: AnyJoiSchema, enjoi: Enjoi) => AnyJoiSchema
export interface RouteBaseOpt {
deprecated?: boolean
tags?: string[]
summary?: string
description?: string
}
export interface RouteOpt extends RouteBaseOpt {
action?: 'list'
body?: RouteBody
pathParams?: RoutePathParam[]
queryParams?: RouteQueryParam[]
response?: Partial<RouteResponse>
errors?: RouteError[]
path?: string
process?: boolean
handlerName?: string
handler?: (arg: RouteArg) => any
cache?: string | number
roles?: Roles
schema?: Schema | SchemaFn
relations?: string[] | true
}
interface TemplateStringsArray extends ReadonlyArray<string> {
readonly raw: ReadonlyArray<string>
}
interface Aql {
(strings: TemplateStringsArray, ...args: any[]): ArangoDB.Query
literal(value: any): ArangoDB.AqlLiteral
}
type RouteArgFetch = (strings: TemplateStringsArray, ...args: any[]) => any[]
export interface RouteRolesArg {
// @deprecated
$: (
strings: TemplateStringsArray,
...args: any[]
) => ArangoDB.Cursor
_: RouteArgFetch
_key: string
action: RouteAction
aql: Aql
auth: RouteAuthorize
collection: ArangoDB.Collection
db: ArangoDB.Database
document: (selector?: string | ArangoDB.DocumentLike) => DocumentData
error: (status: ArangoDB.HttpStatus, reason?: string) => Foxx.Response
exists: (selector: string) => boolean
fetch: RouteArgFetch
hasAuth: boolean
insert: (data: DocumentData, options?: ArangoDB.InsertOptions) => ArangoDB.InsertResult
method: RouteMethod
name: string
param: {[key: string]: any}
path: string
query: (query: ArangoDB.Query, options?: ArangoDB.QueryOptions) => ArangoDB.Cursor
relations: (data: DocumentData) => DocumentData
remove: (selector: string | ArangoDB.DocumentLike, options?: ArangoDB.RemoveOptions) => ArangoDB.RemoveResult
replace: (selector: string | ArangoDB.DocumentLike, data: DocumentData, options?: ArangoDB.ReplaceOptions) => ArangoDB.UpdateResult
req: Foxx.Request
requestedAttributes: string[]
res: Foxx.Response
roles?: Roles
session: (set?: Partial<Foxx.Session>) => Foxx.Session
update: (selector: string | ArangoDB.DocumentLike | DocumentData, data?: DocumentData, options?: ArangoDB.UpdateOptions) => ArangoDB.UpdateResult
validParams: string[]
}
export type RouteRoles = (arg: RouteRolesArg) => Roles
export type RouteAuth = (arg: RouteAuthArg) => boolean
export interface RouteGroups {
creators: Roles
readers: Roles
updaters: Roles
deleters: Roles
}
export interface RouteArg extends RouteRolesArg, RouteBaseOpt {
userRoles: string[]
json: (omitUnwritableAttributes?: boolean) => DocumentData // read role specific body
send: (data: DocumentData | any, omitUnreadableAttributes?: boolean) => Foxx.Response // send role specific response
}
// const inp = {session:req.session,method,doc,req,res}
export type TypeFn = (returns?: void) => Class
type RouteStatus = number | ArangoDB.HttpStatus
export type RouteError = [RouteStatus, string]
export type RouteParam = [string, Schema, string?]
export type RouteQueryParam = RouteParam
export type RoutePathParam = RouteParam
export type RouteBody = [Schema, string?]
export type RouteHandler = (args: RouteArg) => any
export interface RouteResponse {
status: RouteStatus
schema: Foxx.Schema | Foxx.Model
mime: string[]
description?: string
}
export interface RouteAuthArg {
req: Foxx.Request
res: Foxx.Response
session: Foxx.Session
method?: RouteMethod
action?: RouteAction
roles: Roles
document: DocumentData
doc: DocumentData // alias for document
}
// export type AuthorizeMethod = 'read' | 'insert' | 'update' | 'delete'
export type RouteAuthorize = (doc: DocumentData, method?: RouteMethod, action?: RouteAction) => false | DocumentData
export type DocumentForClient = (doc: DocumentData, opt: any) => DocumentData
export type DocumentFromClient = (doc: DocumentData, opt: any) => DocumentData
type QueryFilterLogicalOperator = '&&' | '||' | 'AND' | 'OR'
type QueryFilterComparisonOperator = '==' | '!=' | '<' | '<=' | '>' | '>=' | 'IN' | 'NOT IN' | 'LIKE' | '=~' | '!~' | 'HAS'
type QueryFilterValue = undefined | string | number | boolean | [QueryFilterComparisonOperator, ...(string | number | boolean)[]]
export interface QueryFilter {
$?: QueryFilterLogicalOperator
[key: string]: QueryFilterValue
}
export interface QueryOpt {
collection?: string
filter?: QueryFilter | QueryFilter[]
sort?: string[]
limit?: number | [number, number]
keep?: string[]
unset?: string[]
aggregate?: string | boolean
}
export type EventDecorator =
'After.document.class' | 'After.document.prop' |
'Before.document.class' | 'Before.document.prop' |
'After.insert.class' | 'After.insert.prop' |
'Before.insert.class' | 'Before.insert.prop' |
'After.update.class' | 'After.update.prop' |
'Before.update.class' | 'Before.update.prop' |
'After.replace.class' | 'After.replace.prop' |
'Before.replace.class' | 'Before.replace.prop' |
'After.modify.class' | 'After.modify.prop' |
'Before.modify.class' | 'Before.modify.prop' |
'After.write.class' | 'After.write.prop' |
'Before.write.class' | 'Before.write.prop' |
'After.remove.class' | 'After.remove.prop' |
'Before.remove.class' | 'Before.remove.prop'
export type EventType =
'After.document' | 'Before.document' |
'After.insert' | 'Before.insert' |
'After.update' | 'Before.update' |
'After.modify' | 'Before.modify' |
'After.write' | 'Before.write' |
'After.replace' | 'Before.replace' |
'After.remove' | 'Before.remove'
export type EventMethod = 'document' | 'insert' | 'update' | 'replace' | 'modify' | 'write' | 'remove'