pailingual-odata
Version:
TypeScript client for OData v4 services
291 lines (249 loc) • 15.3 kB
text/typescript
import * as metadata from "./metadata";
import { ApiContextImpl } from "./apiContext";
import { Options, ExtendOptions } from "./options";
import { _extends } from "./utils";
import { CollectionSource } from "./collectionSource";
import { SingleSource } from "./singleSource";
import { Query } from "./query";
import * as serialization from "./serialization";
export { loadMetadata } from "./metadata";
export { metadata, serialization, Options, ExtendOptions, CollectionSource, SingleSource, Query };
export class Pailingual {
static use(plugin: { register: () => ExtendOptions | void }) {
if (plugin) {
const ext = plugin.register();
if (ext) {
ext.apiContextFn && _extends(ApiContextImpl, ext.apiContextFn);
ext.collectionSourceFn && _extends(CollectionSource, ext.collectionSourceFn);
ext.singleSourceFn && _extends(SingleSource, ext.singleSourceFn);
ext.queryFn && _extends(Query, ext.queryFn);
}
}
}
/*
* Factories
*/
static createApiContext<T extends IApiContextBase>(metadata: metadata.ApiMetadata, options?: Options): ApiContext<T>;
static createApiContext<T extends IApiContextBase>(apiRoot: string, options?: Options): Promise<ApiContext<T>>;
static createApiContext<T extends IApiContextBase>(api: metadata.ApiMetadata | string, options?: Options): Promise<ApiContext<T>> | ApiContext<T> {
if (typeof api == "string") {
return metadata.loadMetadata(api)
.then(m => new ApiContextImpl(m, options) as any as ApiContext<T>);
}
else if (api instanceof metadata.ApiMetadata)
return new ApiContextImpl(api, options) as any as ApiContext<T>;
throw new Error("First parameter must be api url or metadata object");
}
static loadMetadata(apiRoot: string, options?: Options, cache?: boolean) {
return metadata.loadMetadata(apiRoot, options, cache);
}
}
export default Pailingual;
/** @deprecated Use Palingual.createApiContext function */
export var ApiContextFactory = Pailingual.createApiContext;
/*
* Base interfaces
*/
export interface IEntityBase extends IActionsSupport, IFunctionsSupport {
$$EntityBaseMarker: never;
$$EntitySetActions: {}
$$EntitySetFunctions: {}
}
export interface IComplexBase {
$$ComplexBaseMarker: never;
}
export interface IApiContextBase extends IActionsSupport, IFunctionsSupport { }
type PrimitiveTypes = string | number | Date | boolean;
export type EntityArray<T extends IEntityBase> = Array<T>;
export type ComplexArray<T extends IComplexBase> = Array<T>;
type Unwrap<T> = T extends Array<infer A> ? A : T;
type Markers = keyof IEntityBase | keyof IComplexBase
type Result<T, R={}> =
R extends Array<infer A> ? {} extends A ? Entity<T>[] : R :
{} extends R ? Entity<T> & R : R;
export interface IExecutable<T, R={}> {
$exec(opt?: Options): Promise<Result<T, R>>;
$url(opt?: Options & { queryParams?: boolean }): string;
}
export interface IExecutableWithCount<T, R> extends IExecutable<T, R>{
$execWithCount(opt?: Options): Promise<{ count: number, value: Result<T, R> }>;
$urlWithCount(opt?: Options): string;
}
export type Entity<T> =
{ [P in (PrimitiveProps<T> | ComplexProps<T>) & RequiredProps<T>]: T[P] extends IComplexBase ? Entity<T[P]> : T[P] } &
{ [P in (PrimitiveProps<T> | ComplexProps<T>) & OptionalProps<T>]?: T[P] extends IComplexBase | undefined ? Entity<Exclude<T[P], undefined>> : T[P] };
/*
* ApiContext
*/
export type ApiContext<T extends IApiContextBase> =
NavigationSource<T>&
Actions<T> &
Functions<T>;
export type EntitySet<T> =
IEntitySetSource<T> &
EntitySetActions<T> &
EntitySetFunctions<T> &
{
$byKey(key: PrimitiveTypes | Pick<Partial<T>, PrimitiveProps<T>>): Singleton<T>;
$cast<T2 extends T>(fullTypeName: string): EntitySet<T2>;
$insert(insert: InsertParameter<T>): IExecutable<T>;
$delete(key: PrimitiveTypes | Partial<Pick<T, PrimitiveProps<T>>>): IExecutable<void, void>
$update(key: PrimitiveTypes | Partial<Pick<T, PrimitiveProps<T>>>, obj: UpdateParameter<T>): IExecutable<void, void>;
$patch(key: PrimitiveTypes | Partial<Pick<T, PrimitiveProps<T>>>, obj: PatchParameter<T>): IExecutable<void, void>;
};
export type Singleton<T> = { $cast<T2 extends T>(fullTypeName: string): EntitySet<T2>; } &
ISingleEntitySource<T> &
Actions<T> &
Functions<T> &
NavigationSource<T>;
//Property name selectors
type ExtractPropertyNames<T, U> = { [P in keyof T]-?: T[P] extends U | undefined ? P extends Markers ? never : P : never }[keyof T];
/** List of primitive properties names*/
export type PrimitiveProps<T> = ExtractPropertyNames<T, PrimitiveTypes>;
/** List of complex properties names*/
export type ComplexProps<T> = ExtractPropertyNames<T, IComplexBase>
/** List of navigation single-value properties names*/
export type NavigationEntityProps<T> = T extends IEntityBase | IApiContextBase ? ExtractPropertyNames<T, IEntityBase> : never;
/** List of navigation multiple-value properties names*/
export type NavigationSetProps<T> = T extends IEntityBase | IApiContextBase ? ExtractPropertyNames<T, IEntityBase[]> : never;
/** List of navigation properties names*/
export type NavigationProps<T> = NavigationEntityProps<T> | NavigationSetProps<T>;
/** List of all entity properties names*/
export type AllProps<T> = Exclude<keyof T, Markers>;
type OptionalProps<T> = {[K in keyof T]-?: undefined extends T[K] ? K : never}[keyof T]
type RequiredProps<T> = { [K in keyof T]-?: undefined extends T[K] ? never : K }[keyof T]
type NavigationSource<T> = { [P in NavigationProps<T>]:
T[P] extends EntityArray<infer E> | undefined ? EntitySet<E> :
T[P] extends IEntityBase | undefined ? Singleton<Exclude<T[P], undefined>> :
never };
export interface IEntitySetSource<T, R={}> extends IEntitySetFunctionSourceBase<T>, IExecutableWithCount<T, R[]> {
$count(): IExecutable<number>;
$unsafeExpand(exp: string): this;
$expand<P extends NavigationProps<T>>(navProp: P): IEntitySetSource<T, R & ExpandedProperty<T, P>>
$expand<P extends NavigationEntityProps<T>, E extends T[P], ER>(navProp: P, exp?: ExpandSingleExpression<E, ER>): IEntitySetSource<T, R & ExpandedProperty<T, P, ER>>
$expand<P extends NavigationSetProps<T>, E extends T[P], ER>(navProp: P, exp: ExpandSetExpression<E, ER>): IEntitySetSource<T, R & ExpandedProperty<T, P, ER[]>>
$select(): IExecutableWithCount<T, (Entity<T> & R)[]>;
$select<F extends PrimitiveProps<T> | ComplexProps<T> | (keyof R & keyof T)>(...props: F[]): IExecutableWithCount<T, (R & Pick<T, F>)[]>;
}
export interface ICastableEntitySetFunctionSource<T, R={}> extends IEntitySetFunctionSource<T, R> {
$cast<T2 extends T>(fullTypeName: string): ICastableEntitySetFunctionSource<T2>;
}
export interface IEntitySetFunctionSource<T, R={}> extends IEntitySetFunctionSourceBase<T>, IExecutableWithCount<T, R[]> {
$expand<P extends NavigationProps<T>>(navProp: P): IEntitySetFunctionSource<T, R & ExpandedProperty<T, P>>
$expand<P extends NavigationEntityProps<T>, E extends T[P], ER>(navProp: P, exp?: ExpandSingleExpression<E, ER>): IEntitySetFunctionSource<T, R & ExpandedProperty<T, P, ER>>
$expand<P extends NavigationSetProps<T>, E extends T[P], ER>(navProp: P, exp: ExpandSetExpression<E, ER>): IEntitySetFunctionSource<T, R & ExpandedProperty<T, P, ER[]>>
$select(): IExecutableWithCount<T, Entity<T> & R>;
$select<F extends PrimitiveProps<T> | ComplexProps<T> | (keyof R & keyof T)>(...props: F[]): IExecutableWithCount<T, R & Pick<T, F>>;
}
export interface IEntitySetFunctionSourceBase<T> {
$filter(expression: string): this;
$orderBy(...key: (PrimitiveProps<T> | ((e: OrderbySource<T>) => PrimitiveTypes))[]): this;
$orderByDesc(...key: (PrimitiveProps<T> | ((e: OrderbySource<T>) => PrimitiveTypes))[]): this;
$skip(num: number): this;
$search(expr: string):this;
$top(num: number): this;
}
export interface IComplexSetFunctionSource<T, R={}> extends IEntitySetFunctionSourceBase<T>, IExecutableWithCount<T, R[]> { }
export interface ISingleEntitySource<T, R={}> extends IExecutable<T, R> {
$unsafeExpand(exp: string): this;
$expand<P extends NavigationProps<T>>(navProp: P): ISingleEntitySource<T, R & ExpandedProperty<T, P>>
//Expand entity navigation property with query options
$expand<P extends NavigationEntityProps<T>, E extends T[P], ER>(navProp: P, exp?: ExpandSingleExpression<E, ER>): ISingleEntitySource<T, R & ExpandedProperty<T, P, ER>>
$expand<P extends NavigationSetProps<T>, E extends T[P], ER>(navProp: P, exp: ExpandSetExpression<E, ER>): ISingleEntitySource<T, R & ExpandedProperty<T, P, ER[]>>
$select(): IExecutable<T, Entity<T> & R>;
$select<F extends PrimitiveProps<T> | ComplexProps<T> | (keyof R & keyof T)>(...props: F[]): IExecutable<T, R & Pick<T, F>>;
$delete(): IExecutable<void, void>
$update(obj: UpdateParameter<T>): IExecutable<void, void>;
$patch(obj: PatchParameter<T>): IExecutable<void, void>;
}
export interface ISingleEntityFunctionSource<T, R={}> extends IExecutable<T, R> {
$expand<P extends NavigationProps<T>>(navProp: P): ISingleEntityFunctionSource<T, R & ExpandedProperty<T, P>>
//Expand entity navigation property with query options
$expand<P extends NavigationEntityProps<T>, E extends T[P], ER>(navProp: P, exp?: ExpandSingleExpression<E, ER>): ISingleEntityFunctionSource<T, R & ExpandedProperty<T, P, ER>>
$expand<P extends NavigationSetProps<T>, E extends T[P], ER>(navProp: P, exp: ExpandSetExpression<E, ER>): ISingleEntityFunctionSource<T, R & ExpandedProperty<T, P, ER[]>>
$select(): IExecutable<T, Entity<T> & R>;
$select<F extends PrimitiveProps<T> | ComplexProps<T> | (keyof R & keyof T)>(...props: F[]): IExecutable<T, R & Pick<T, F>>;
}
type ExpandedPropertyResult<T> = T extends Array<infer A> ? Entity<A>[] : Entity<T>
/** Represent expanded property */
export type ExpandedProperty<T, P extends NavigationProps<T>, R=ExpandedPropertyResult<Exclude<T[P], undefined>>> = { [K in P]: Result<Unwrap<Exclude<T[P], undefined>>, R> }
export type ExpandSingleExpression<T, R> = (e: IExpandebleEntity<Exclude<T, undefined>, {}>) => IExpandSelectResult<R> | IExpandebleEntity<Exclude<T, undefined>, R>;
export type ExpandSetExpression<T, R> =(e: IExpandebleSet<Unwrap<Exclude<T, undefined>>, {}>) => IExpandSelectResult<R> | IExpandebleSet<Unwrap<Exclude<T, undefined>>,R>;
/** Functions for expand expressions of navigation property return single entity*/
export interface IExpandebleEntity<T,R> {
$expand<P extends NavigationProps<T>>(navProp: P): IExpandebleEntity<T, R & ExpandedProperty<T, P>>
$expand<P extends NavigationSetProps<T>, E extends T[P], ER>(navProp: P, exp: ExpandSingleExpression<E, ER>): IExpandebleEntity<T, R & ExpandedProperty<T, P, ER[]>>
$expand<P extends NavigationEntityProps<T>, E extends T[P], ER>(navProp: P, exp?: ExpandSetExpression<E, ER>): IExpandebleEntity<T, R & ExpandedProperty<T, P, ER>>
$select<F extends PrimitiveProps<T> | ComplexProps<T> | (keyof R & keyof T)>(...props: F[]): IExpandSelectResult<R & Pick<T, F>>;
}
/** Functions for expand expressions of navigation property return entity set*/
export interface IExpandebleSet<T, R> extends IEntitySetFunctionSourceBase<T>{
$expand<P extends NavigationProps<T>>(navProp: P): IExpandebleSet<T, R & ExpandedProperty<T, P>>
$expand<P extends NavigationSetProps<T>, E extends T[P], ER>(navProp: P, exp: ExpandSingleExpression<E, ER>): IExpandebleSet<T, R & ExpandedProperty<T, P, ER[]>>
$expand<P extends NavigationEntityProps<T>, E extends T[P], ER>(navProp: P, exp?: ExpandSetExpression<E, ER>): IExpandebleSet<T, R & ExpandedProperty<T, P, ER>>
$select<F extends PrimitiveProps<T> | ComplexProps<T> | (keyof R & keyof T)>(...props: F[]): IExpandSelectResult< R & Pick<T, F>>;
}
interface IExpandSelectResult<R> {interfaceMarker:never }
export type OrderbySource<T> = {
[P in PrimitiveProps<T> | ComplexProps<T> | NavigationEntityProps<T>]-?:
T[P] extends IEntityBase ? OrderbySource<T[P]> : Exclude<T[P], undefined>
}
export type InsertParameter<T> = { //optional props
[P in (PrimitiveProps<T> | ComplexProps<T>) & OptionalProps<T>]?:
undefined extends T[P] ?
T[P] extends IComplexBase | undefined ? InsertParameter<Exclude<T[P], undefined>> :
T[P] :
never
} & { //required props
[P in (PrimitiveProps<T> | ComplexProps<T>) & RequiredProps<T>]:
undefined extends T[P] ? never :
T[P] extends IComplexBase ? InsertParameter<T[P]> :
T[P]
} & { //navigation props
[P in NavigationProps<T>]?:
T[P] extends EntityArray<infer U> ? Array<InsertParameter<U>> :
T[P] extends IEntityBase | IComplexBase ? InsertParameter<T[P]> :
never
};
export type UpdateParameter<T> = InsertParameter<T>;
export type PatchParameter<T> = {
[P in PrimitiveProps<T> | ComplexProps<T>]?:
T[P] extends EntityArray<infer U> ? Array<PatchParameter<U>> :
T[P] extends IEntityBase | IComplexBase ? PatchParameter<T[P]> :
T[P]
}
/*
* Opertations
*/
interface IActionsSupport {
$$Actions: {}
}
interface IFunctionsSupport {
$$Functions: {}
}
type EntitySetActions<T> = T extends IEntityBase ? { [P in keyof T["$$EntitySetActions"]]: ActionDelegate<T["$$EntitySetActions"][P]> } : {};
type EntitySetFunctions<T> = T extends IEntityBase ? { [P in keyof T["$$EntitySetFunctions"]]: FuncDelegate<T["$$EntitySetFunctions"][P]> } : {};
type Actions<T> = T extends IActionsSupport ? { [P in keyof T["$$Actions"]]: ActionDelegate<T["$$Actions"][P]> } : {};
type Functions<T> = T extends IFunctionsSupport ? { [P in keyof T["$$Functions"]]: FuncDelegate<T["$$Functions"][P]> } : {};
export type FuncRetType<T> =
T extends PrimitiveTypes | PrimitiveTypes[] ? IExecutable<T, T> :
T extends IComplexBase ? IExecutable<T, T> :
T extends ComplexArray<infer U> ? IComplexSetFunctionSource<U>:
T extends IEntityBase ? ISingleEntityFunctionSource<T> :
T extends EntityArray<infer U> ? ICastableEntitySetFunctionSource<U> :
never;
type FuncDelegate<T> =
T extends () => (infer R) ? () => FuncRetType<R> :
T extends (...a: infer A) => (infer R) ? (...a: A) => FuncRetType<R> :
T;
export type ActionRetType<T> =
T extends PrimitiveTypes | PrimitiveTypes[] ? IExecutable<T,T> :
T extends IComplexBase ? IExecutable<T,T> :
T extends ComplexArray<infer U> ? IExecutable<T, U[]> :
T extends IEntityBase ? IExecutable<T, T> :
T extends EntityArray<infer U> ? IExecutable<T, U[]> :
IExecutable<T,void>;
type ActionDelegate<T> =
T extends () => (infer R) ? () => ActionRetType<R> :
T extends (...a: infer A) => (infer R) ? (...a: A) => ActionRetType<R> :
T;