Skip to content

Commit

Permalink
Add ObserverMixin to PersistedModelClass typings
Browse files Browse the repository at this point in the history
Signed-off-by: Miroslav Bajtoš <mbajtoss@gmail.com>
  • Loading branch information
bajtos committed Feb 28, 2020
1 parent 36303b3 commit 1a8af55
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 0 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
coverage
dist
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ npm-debug.log
.travis.yml
.nyc_output
dist
types/__test__.ts
67 changes: 67 additions & 0 deletions types/__test__.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// 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 => {});

// ModelBaseClass can be assigned to `typeof ModelBase`
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'));
});

// PersistedModelClass can be assigned to `typeof PersistedModel`
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 => {});
});
80 changes: 80 additions & 0 deletions types/model.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import {EventEmitter} from 'events';
import {AnyObject, Options} from './common';
import {DataSource} from './datasource';
import {Listener} from './observer-mixin';

/**
* Property types
Expand Down Expand Up @@ -243,6 +244,85 @@ export declare class ModelBase {
anotherClass: string | ModelBaseClass | object,
options?: Options,
): 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
*/
observe<T extends ModelBase>(
this: T,
operation: string,
listener: Listener
): 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
*/
removeObserver<T extends ModelBase>(
this: T,
operation: string,
listener: Listener
): Listener | 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
*/
clearObservers(operation: string): void;
}

export type ModelBaseClass = typeof ModelBase;
Expand Down

0 comments on commit 1a8af55

Please sign in to comment.