Add ObserverMixin members to ModelBase typings
Signed-off-by: Miroslav Bajtoš <mbajtoss@gmail.com>
This commit is contained in:
parent
65a358656f
commit
c7b88e9b8e
|
@ -1 +1,2 @@
|
||||||
coverage
|
coverage
|
||||||
|
dist
|
||||||
|
|
|
@ -12,3 +12,4 @@ npm-debug.log
|
||||||
.travis.yml
|
.travis.yml
|
||||||
.nyc_output
|
.nyc_output
|
||||||
dist
|
dist
|
||||||
|
types/__test__.ts
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright IBM Corp. 2020. All Rights Reserved.
|
||||||
|
// Node module: loopback-datasource-juggler
|
||||||
|
// This file is licensed under the MIT License.
|
||||||
|
// License text available at https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
// A test file to verify types described by our .d.ts files.
|
||||||
|
// The code in this file is only compiled, we don't run it via Mocha.
|
||||||
|
|
||||||
|
import {
|
||||||
|
DataSource,
|
||||||
|
KeyValueModel,
|
||||||
|
ModelBase,
|
||||||
|
ModelBaseClass,
|
||||||
|
PersistedModel,
|
||||||
|
PersistedModelClass,
|
||||||
|
} from '..';
|
||||||
|
|
||||||
|
const db = new DataSource('db', {connector: 'memory'});
|
||||||
|
|
||||||
|
//-------
|
||||||
|
// ModelBase should provide ObserverMixin APIs as static methods
|
||||||
|
//-------
|
||||||
|
//
|
||||||
|
(function() {
|
||||||
|
const Data = db.createModel('Data');
|
||||||
|
|
||||||
|
// An operation hook can be installed
|
||||||
|
Data.observe('before save', async ctx => {});
|
||||||
|
|
||||||
|
// Context is typed and provides `Model` property
|
||||||
|
Data.observe('before save', async ctx => {
|
||||||
|
console.log(ctx.Model.modelName);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ModelBaseClass can be assigned to `typeof ModelBase`
|
||||||
|
// Please note that both `ModelBaseClass` and typeof ModelBase`
|
||||||
|
// are different ways how to describe a class constructor of a model.
|
||||||
|
// In this test we are verifying that the value returned by `createModel`
|
||||||
|
// can be assigned to both types.
|
||||||
|
const modelTypeof: typeof ModelBase = Data;
|
||||||
|
const modelCls: ModelBaseClass = modelTypeof;
|
||||||
|
});
|
||||||
|
|
||||||
|
//-------
|
||||||
|
// PersistedModel should provide ObserverMixin APIs as static methods
|
||||||
|
//-------
|
||||||
|
(function () {
|
||||||
|
const Product = db.createModel<PersistedModelClass>(
|
||||||
|
'Product',
|
||||||
|
{name: String},
|
||||||
|
{strict: true}
|
||||||
|
);
|
||||||
|
|
||||||
|
// It accepts async function
|
||||||
|
Product.observe('before save', async ctx => {});
|
||||||
|
|
||||||
|
// It accepts callback-based function
|
||||||
|
Product.observe('before save', (ctx, next) => {
|
||||||
|
next(new Error('test error'));
|
||||||
|
});
|
||||||
|
|
||||||
|
// ctx.Model is a PersistedModel class constructor
|
||||||
|
Product.observe('before save', async ctx => {
|
||||||
|
await ctx.Model.findOne();
|
||||||
|
});
|
||||||
|
|
||||||
|
// PersistedModelClass can be assigned to `typeof PersistedModel`
|
||||||
|
// Please note that both `PersistedModelClass` and typeof PersistedModel`
|
||||||
|
// are different ways how to describe a class constructor of a model.
|
||||||
|
// In this test we are verifying that the value returned by `createModel`
|
||||||
|
// can be assigned to both types.
|
||||||
|
const modelTypeof: typeof PersistedModel = Product;
|
||||||
|
const modelCls: PersistedModelClass = modelTypeof;
|
||||||
|
});
|
||||||
|
|
||||||
|
//-------
|
||||||
|
// KeyValueModel should provide ObserverMixin APIs as static methods
|
||||||
|
//-------
|
||||||
|
(function () {
|
||||||
|
const kvdb = new DataSource({connector: 'kv-memory'});
|
||||||
|
const CacheItem = kvdb.createModel<typeof KeyValueModel>('CacheItem');
|
||||||
|
|
||||||
|
// An operation hook can be installed
|
||||||
|
CacheItem.observe('before save', async ctx => {});
|
||||||
|
|
||||||
|
// ctx.Model is a KeyValueModel class constructor
|
||||||
|
CacheItem.observe('before save', async ctx => {
|
||||||
|
await ctx.Model.expire('key', 100);
|
||||||
|
});
|
||||||
|
});
|
|
@ -6,6 +6,7 @@
|
||||||
import {EventEmitter} from 'events';
|
import {EventEmitter} from 'events';
|
||||||
import {AnyObject, Options} from './common';
|
import {AnyObject, Options} from './common';
|
||||||
import {DataSource} from './datasource';
|
import {DataSource} from './datasource';
|
||||||
|
import {Listener, OperationHookContext} from './observer-mixin';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Property types
|
* Property types
|
||||||
|
@ -243,6 +244,85 @@ export declare class ModelBase {
|
||||||
anotherClass: string | ModelBaseClass | object,
|
anotherClass: string | ModelBaseClass | object,
|
||||||
options?: Options,
|
options?: Options,
|
||||||
): ModelBaseClass;
|
): ModelBaseClass;
|
||||||
|
|
||||||
|
// ObserverMixin members are added as static methods, this is difficult to
|
||||||
|
// describe in TypeScript in a way that's easy to use by consumers.
|
||||||
|
// As a workaround, we include a copy of ObserverMixin members here.
|
||||||
|
//
|
||||||
|
// See also https://github.com/microsoft/TypeScript/issues/5863#issuecomment-410887254
|
||||||
|
// for more information about using `this` in static members.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an asynchronous observer for the given operation (event).
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* Registers a `before save` observer for a given model.
|
||||||
|
*
|
||||||
|
* ```javascript
|
||||||
|
* MyModel.observe('before save', function filterProperties(ctx, next) {
|
||||||
|
* if (ctx.options && ctx.options.skipPropertyFilter) return next();
|
||||||
|
* if (ctx.instance) {
|
||||||
|
* FILTERED_PROPERTIES.forEach(function(p) {
|
||||||
|
* ctx.instance.unsetAttribute(p);
|
||||||
|
* });
|
||||||
|
* } else {
|
||||||
|
* FILTERED_PROPERTIES.forEach(function(p) {
|
||||||
|
* delete ctx.data[p];
|
||||||
|
* });
|
||||||
|
* }
|
||||||
|
* next();
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param {String} operation The operation name.
|
||||||
|
* @callback {function} listener The listener function. It will be invoked with
|
||||||
|
* `this` set to the model constructor, e.g. `User`.
|
||||||
|
* @end
|
||||||
|
*/
|
||||||
|
static observe<T extends typeof ModelBase>(
|
||||||
|
this: T,
|
||||||
|
operation: string,
|
||||||
|
listener: Listener<OperationHookContext<T>>,
|
||||||
|
): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister an asynchronous observer for the given operation (event).
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* ```javascript
|
||||||
|
* MyModel.removeObserver('before save', function removedObserver(ctx, next) {
|
||||||
|
* // some logic user want to apply to the removed observer...
|
||||||
|
* next();
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param {String} operation The operation name.
|
||||||
|
* @callback {function} listener The listener function.
|
||||||
|
* @end
|
||||||
|
*/
|
||||||
|
static removeObserver<T extends typeof ModelBase>(
|
||||||
|
this: T,
|
||||||
|
operation: string,
|
||||||
|
listener: Listener<OperationHookContext<T>>,
|
||||||
|
): Listener<OperationHookContext<T>> | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister all asynchronous observers for the given operation (event).
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* Remove all observers connected to the `before save` operation.
|
||||||
|
*
|
||||||
|
* ```javascript
|
||||||
|
* MyModel.clearObservers('before save');
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param {String} operation The operation name.
|
||||||
|
* @end
|
||||||
|
*/
|
||||||
|
static clearObservers(operation: string): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ModelBaseClass = typeof ModelBase;
|
export type ModelBaseClass = typeof ModelBase;
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
// License text available at https://opensource.org/licenses/MIT
|
// License text available at https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
import {Callback, PromiseOrVoid} from './common';
|
import {Callback, PromiseOrVoid} from './common';
|
||||||
import {PersistedModel, PersistedModelClass} from './persisted-model';
|
import {ModelBase} from './model';
|
||||||
|
|
||||||
export interface OperationHookContext<T extends typeof PersistedModel> {
|
export interface OperationHookContext<T extends typeof ModelBase> {
|
||||||
/**
|
/**
|
||||||
* The constructor of the model that triggered the operation.
|
* The constructor of the model that triggered the operation.
|
||||||
*/
|
*/
|
||||||
|
@ -19,7 +19,7 @@ export interface OperationHookContext<T extends typeof PersistedModel> {
|
||||||
[property: string]: any;
|
[property: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Listener<Ctx = OperationHookContext<PersistedModelClass>> = (
|
export type Listener<Ctx = OperationHookContext<typeof ModelBase>> = (
|
||||||
ctx: Ctx, next: (err?: any) => void
|
ctx: Ctx, next: (err?: any) => void
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
|
@ -52,7 +52,11 @@ export interface ObserverMixin {
|
||||||
* `this` set to the model constructor, e.g. `User`.
|
* `this` set to the model constructor, e.g. `User`.
|
||||||
* @end
|
* @end
|
||||||
*/
|
*/
|
||||||
observe(operation: string, listener: Listener<OperationHookContext<PersistedModelClass>>): void;
|
observe<T extends typeof ModelBase>(
|
||||||
|
this: T,
|
||||||
|
operation: string,
|
||||||
|
listener: Listener<OperationHookContext<T>>,
|
||||||
|
): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregister an asynchronous observer for the given operation (event).
|
* Unregister an asynchronous observer for the given operation (event).
|
||||||
|
@ -70,7 +74,11 @@ export interface ObserverMixin {
|
||||||
* @callback {function} listener The listener function.
|
* @callback {function} listener The listener function.
|
||||||
* @end
|
* @end
|
||||||
*/
|
*/
|
||||||
removeObserver(operation: string, listener: Listener<OperationHookContext<PersistedModelClass>>): Listener<OperationHookContext<PersistedModelClass>> | undefined;
|
removeObserver<T extends typeof ModelBase>(
|
||||||
|
this: T,
|
||||||
|
operation: string,
|
||||||
|
listener: Listener<OperationHookContext<T>>,
|
||||||
|
): Listener<OperationHookContext<T>> | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregister all asynchronous observers for the given operation (event).
|
* Unregister all asynchronous observers for the given operation (event).
|
||||||
|
|
Loading…
Reference in New Issue