From abb6e53cd684d850c58d3ae2678f9151c443a5f0 Mon Sep 17 00:00:00 2001 From: jurien Date: Tue, 3 Jan 2017 14:26:10 +0100 Subject: [PATCH 1/7] added interval import from rxjs to the imports for angular2 --- lib/angular2/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/angular2/index.js b/lib/angular2/index.js index 1a8d552e..73529f50 100644 --- a/lib/angular2/index.js +++ b/lib/angular2/index.js @@ -429,6 +429,7 @@ module.exports = function generate(ctx) { { module: 'Subject', from: 'rxjs/Subject'}, { module: 'Observable', from: 'rxjs/Observable'}, { module: 'rxjs/add/operator/map' }, + { module: 'rxjs/add/operator/interval' }, { module: modelName, from: `../../models/${modelName}`}, ]; if (isIo === 'enabled') { From 5689e43f603a339c03216534d4243508a1b58897 Mon Sep 17 00:00:00 2001 From: jurien Date: Tue, 3 Jan 2017 14:45:57 +0100 Subject: [PATCH 2/7] fixed sloppy copy and pase --- lib/angular2/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/angular2/index.js b/lib/angular2/index.js index 73529f50..851b36d9 100644 --- a/lib/angular2/index.js +++ b/lib/angular2/index.js @@ -429,7 +429,7 @@ module.exports = function generate(ctx) { { module: 'Subject', from: 'rxjs/Subject'}, { module: 'Observable', from: 'rxjs/Observable'}, { module: 'rxjs/add/operator/map' }, - { module: 'rxjs/add/operator/interval' }, + { module: 'rxjs/add/observable/interval' }, { module: modelName, from: `../../models/${modelName}`}, ]; if (isIo === 'enabled') { From 7ef25651be21ae61b2130a0247b72c10b9a00ed3 Mon Sep 17 00:00:00 2001 From: jurien Date: Tue, 3 Jan 2017 16:16:46 +0100 Subject: [PATCH 3/7] changed import to --- lib/angular2/index.js | 4 +--- tests/ng2web/src/app/shared/sdk/services/custom/Account.ts | 3 +-- .../app/shared/sdk/services/custom/ApplicationCredential.ts | 3 +-- tests/ng2web/src/app/shared/sdk/services/custom/Category.ts | 3 +-- tests/ng2web/src/app/shared/sdk/services/custom/Core.ts | 3 +-- tests/ng2web/src/app/shared/sdk/services/custom/Like.ts | 3 +-- tests/ng2web/src/app/shared/sdk/services/custom/Message.ts | 3 +-- tests/ng2web/src/app/shared/sdk/services/custom/Room.ts | 3 +-- .../ng2web/src/app/shared/sdk/services/custom/RoomAccount.ts | 3 +-- tests/ng2web/src/app/shared/sdk/services/custom/RoomAdmin.ts | 3 +-- tests/ng2web/src/app/shared/sdk/services/custom/Storage.ts | 3 +-- tests/ng2web/src/app/shared/sdk/services/custom/User.ts | 3 +-- .../src/app/shared/sdk/services/custom/UserCredential.ts | 3 +-- .../ng2web/src/app/shared/sdk/services/custom/UserIdentity.ts | 3 +-- 14 files changed, 14 insertions(+), 29 deletions(-) diff --git a/lib/angular2/index.js b/lib/angular2/index.js index 851b36d9..48c6dfaf 100644 --- a/lib/angular2/index.js +++ b/lib/angular2/index.js @@ -427,9 +427,7 @@ module.exports = function generate(ctx) { { module: 'JSONSearchParams', from: '../core/search.params'}, { module: 'ErrorHandler', from: '../core/error.service'}, { module: 'Subject', from: 'rxjs/Subject'}, - { module: 'Observable', from: 'rxjs/Observable'}, - { module: 'rxjs/add/operator/map' }, - { module: 'rxjs/add/observable/interval' }, + { module: 'Observable', from: 'rxjs/rx'}, { module: modelName, from: `../../models/${modelName}`}, ]; if (isIo === 'enabled') { diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/Account.ts b/tests/ng2web/src/app/shared/sdk/services/custom/Account.ts index 3f1e1296..4d43ff0b 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/Account.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/Account.ts @@ -9,8 +9,7 @@ import { LoopBackFilter, SDKToken, AccessToken } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/Observable'; -import 'rxjs/add/operator/map'; +import { Observable } from 'rxjs/rx'; import { Account } from '../../models/Account'; import { SocketConnections } from '../../sockets/socket.connections'; import { RoomAccount } from '../../models/RoomAccount'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/ApplicationCredential.ts b/tests/ng2web/src/app/shared/sdk/services/custom/ApplicationCredential.ts index 9ad1b7ee..3a2f9b12 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/ApplicationCredential.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/ApplicationCredential.ts @@ -9,8 +9,7 @@ import { LoopBackFilter, } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/Observable'; -import 'rxjs/add/operator/map'; +import { Observable } from 'rxjs/rx'; import { ApplicationCredential } from '../../models/ApplicationCredential'; import { SocketConnections } from '../../sockets/socket.connections'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/Category.ts b/tests/ng2web/src/app/shared/sdk/services/custom/Category.ts index 9355f31b..f7aa54dd 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/Category.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/Category.ts @@ -9,8 +9,7 @@ import { LoopBackFilter, } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/Observable'; -import 'rxjs/add/operator/map'; +import { Observable } from 'rxjs/rx'; import { Category } from '../../models/Category'; import { SocketConnections } from '../../sockets/socket.connections'; import { Room } from '../../models/Room'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/Core.ts b/tests/ng2web/src/app/shared/sdk/services/custom/Core.ts index 3ddafc7c..9a386fda 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/Core.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/Core.ts @@ -9,8 +9,7 @@ import { LoopBackFilter, } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/Observable'; -import 'rxjs/add/operator/map'; +import { Observable } from 'rxjs/rx'; import { Core } from '../../models/Core'; import { SocketConnections } from '../../sockets/socket.connections'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/Like.ts b/tests/ng2web/src/app/shared/sdk/services/custom/Like.ts index f48cccdf..62799305 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/Like.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/Like.ts @@ -9,8 +9,7 @@ import { LoopBackFilter, } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/Observable'; -import 'rxjs/add/operator/map'; +import { Observable } from 'rxjs/rx'; import { Like } from '../../models/Like'; import { SocketConnections } from '../../sockets/socket.connections'; import { Message } from '../../models/Message'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/Message.ts b/tests/ng2web/src/app/shared/sdk/services/custom/Message.ts index d01e9fa4..1b00ddce 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/Message.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/Message.ts @@ -9,8 +9,7 @@ import { LoopBackFilter, } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/Observable'; -import 'rxjs/add/operator/map'; +import { Observable } from 'rxjs/rx'; import { Message } from '../../models/Message'; import { SocketConnections } from '../../sockets/socket.connections'; import { Like } from '../../models/Like'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/Room.ts b/tests/ng2web/src/app/shared/sdk/services/custom/Room.ts index cdc8b75f..68e4c057 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/Room.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/Room.ts @@ -9,8 +9,7 @@ import { LoopBackFilter, } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/Observable'; -import 'rxjs/add/operator/map'; +import { Observable } from 'rxjs/rx'; import { Room } from '../../models/Room'; import { SocketConnections } from '../../sockets/socket.connections'; import { Message } from '../../models/Message'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/RoomAccount.ts b/tests/ng2web/src/app/shared/sdk/services/custom/RoomAccount.ts index 99d4e17f..bd20ee9e 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/RoomAccount.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/RoomAccount.ts @@ -9,8 +9,7 @@ import { LoopBackFilter, } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/Observable'; -import 'rxjs/add/operator/map'; +import { Observable } from 'rxjs/rx'; import { RoomAccount } from '../../models/RoomAccount'; import { SocketConnections } from '../../sockets/socket.connections'; import { Account } from '../../models/Account'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/RoomAdmin.ts b/tests/ng2web/src/app/shared/sdk/services/custom/RoomAdmin.ts index 87ef873f..15e64021 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/RoomAdmin.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/RoomAdmin.ts @@ -9,8 +9,7 @@ import { LoopBackFilter, } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/Observable'; -import 'rxjs/add/operator/map'; +import { Observable } from 'rxjs/rx'; import { RoomAdmin } from '../../models/RoomAdmin'; import { SocketConnections } from '../../sockets/socket.connections'; import { Account } from '../../models/Account'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/Storage.ts b/tests/ng2web/src/app/shared/sdk/services/custom/Storage.ts index c4106d02..be78ba3d 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/Storage.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/Storage.ts @@ -9,8 +9,7 @@ import { LoopBackFilter, } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/Observable'; -import 'rxjs/add/operator/map'; +import { Observable } from 'rxjs/rx'; import { Storage } from '../../models/Storage'; import { SocketConnections } from '../../sockets/socket.connections'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/User.ts b/tests/ng2web/src/app/shared/sdk/services/custom/User.ts index 49b23bcd..688298c6 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/User.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/User.ts @@ -9,8 +9,7 @@ import { LoopBackFilter, SDKToken, AccessToken } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/Observable'; -import 'rxjs/add/operator/map'; +import { Observable } from 'rxjs/rx'; import { User } from '../../models/User'; import { SocketConnections } from '../../sockets/socket.connections'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/UserCredential.ts b/tests/ng2web/src/app/shared/sdk/services/custom/UserCredential.ts index 16d7df69..dd8d2ce7 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/UserCredential.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/UserCredential.ts @@ -9,8 +9,7 @@ import { LoopBackFilter, } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/Observable'; -import 'rxjs/add/operator/map'; +import { Observable } from 'rxjs/rx'; import { UserCredential } from '../../models/UserCredential'; import { SocketConnections } from '../../sockets/socket.connections'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/UserIdentity.ts b/tests/ng2web/src/app/shared/sdk/services/custom/UserIdentity.ts index c8a28579..aa04cc06 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/UserIdentity.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/UserIdentity.ts @@ -9,8 +9,7 @@ import { LoopBackFilter, } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/Observable'; -import 'rxjs/add/operator/map'; +import { Observable } from 'rxjs/rx'; import { UserIdentity } from '../../models/UserIdentity'; import { SocketConnections } from '../../sockets/socket.connections'; import { User } from '../../models/User'; From d708e1212c361158ef3f7571b10f952c2efdf53d Mon Sep 17 00:00:00 2001 From: jurien Date: Tue, 3 Jan 2017 16:33:21 +0100 Subject: [PATCH 4/7] Capitalized R of rx --- lib/angular2/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/angular2/index.js b/lib/angular2/index.js index 48c6dfaf..45b22f9b 100644 --- a/lib/angular2/index.js +++ b/lib/angular2/index.js @@ -427,7 +427,7 @@ module.exports = function generate(ctx) { { module: 'JSONSearchParams', from: '../core/search.params'}, { module: 'ErrorHandler', from: '../core/error.service'}, { module: 'Subject', from: 'rxjs/Subject'}, - { module: 'Observable', from: 'rxjs/rx'}, + { module: 'Observable', from: 'rxjs/Rx'}, { module: modelName, from: `../../models/${modelName}`}, ]; if (isIo === 'enabled') { From ae00ee7e4c5d522c2e49322ca2ad2d439c7ac469 Mon Sep 17 00:00:00 2001 From: Jonathan Casarrubias Date: Fri, 6 Jan 2017 13:52:01 -0600 Subject: [PATCH 5/7] RC 8 WIP --- lib/angular2/index.js | 25 ++- lib/angular2/shared/models/fireloop.ejs | 1 - lib/angular2/shared/models/flref.ejs | 82 +++++++- lib/angular2/shared/services/core/base.ejs | 35 +++- .../shared/services/core/realtime.ejs | 104 +++++++--- .../shared/services/custom/service.ejs | 2 +- lib/angular2/shared/sockets/connections.ts | 189 ++++++++++++------ tests/fireloop/package.json | 2 +- tests/fireloop/server/component-config.json | 2 +- tests/ng2web/src/app/app.component.ts | 54 ++--- tests/ng2web/src/app/room/room.component.html | 1 + tests/ng2web/src/app/room/room.component.ts | 22 +- .../src/app/room/room.list.component.html | 7 +- .../src/app/room/room.list.component.ts | 33 +-- tests/ng2web/src/app/shared/auth.guard.ts | 4 +- tests/ng2web/src/app/shared/sdk/index.ts | 4 +- .../src/app/shared/sdk/models/FireLoop.ts | 1 - .../src/app/shared/sdk/models/FireLoopRef.ts | 82 +++++++- .../shared/sdk/services/core/base.service.ts | 8 +- .../app/shared/sdk/services/core/real.time.ts | 104 +++++++--- .../app/shared/sdk/services/custom/Account.ts | 12 +- .../services/custom/ApplicationCredential.ts | 6 +- .../shared/sdk/services/custom/Category.ts | 8 +- .../app/shared/sdk/services/custom/Core.ts | 6 +- .../app/shared/sdk/services/custom/Like.ts | 6 +- .../app/shared/sdk/services/custom/Message.ts | 10 +- .../app/shared/sdk/services/custom/Room.ts | 16 +- .../shared/sdk/services/custom/RoomAccount.ts | 6 +- .../shared/sdk/services/custom/RoomAdmin.ts | 6 +- .../app/shared/sdk/services/custom/Storage.ts | 6 +- .../app/shared/sdk/services/custom/User.ts | 8 +- .../sdk/services/custom/UserCredential.ts | 6 +- .../sdk/services/custom/UserIdentity.ts | 6 +- .../shared/sdk/sockets/socket.connections.ts | 189 ++++++++++++------ 34 files changed, 712 insertions(+), 341 deletions(-) diff --git a/lib/angular2/index.js b/lib/angular2/index.js index 1a8d552e..efee8aca 100644 --- a/lib/angular2/index.js +++ b/lib/angular2/index.js @@ -265,8 +265,6 @@ module.exports = function generate(ctx) { modelName: modelName, plural: ctx.models[modelName].sharedClass.ctor.settings.plural || ejs.filters.pluralize(modelName), - buildModelImports: buildModelImports, - buildModelProperties: buildModelProperties, buildPropertyType: buildPropertyType, buildPropertyDefaultValue: buildPropertyDefaultValue, buildRelationType: buildRelationType, @@ -432,7 +430,7 @@ module.exports = function generate(ctx) { { module: modelName, from: `../../models/${modelName}`}, ]; if (isIo === 'enabled') { - imports.push({ module: 'SocketConnections', from: '../../sockets/socket.connections' }); + imports.push({ module: 'SocketConnection', from: '../../sockets/socket.connections' }); } let loaded = {}; loaded[model.name] = true; getModelRelations(model).forEach((relationName, i) => { @@ -510,7 +508,7 @@ module.exports = function generate(ctx) { if (isIo === 'enabled') { imports = imports.concat([ { module: 'SocketDriver', from: './sockets/socket.driver'}, - { module: 'SocketConnections', from: './sockets/socket.connections'}, + { module: 'SocketConnection', from: './sockets/socket.connections'}, { module: 'RealTime', from: './services/core/real.time'} ]); } @@ -568,7 +566,7 @@ module.exports = function generate(ctx) { */ function buildNgProviders(isIo) { let imports = ['ErrorHandler']; - if (isIo === 'enabled') { imports.push('SocketConnections'); } + if (isIo === 'enabled') { imports.push('SocketConnection'); } return imports.join(',\n '); } /** @@ -579,7 +577,7 @@ module.exports = function generate(ctx) { function buildServiceDI(isIo) { let dependencies = ['@Inject(Http) protected http: Http']; if (isIo === 'enabled') { - dependencies.push('@Inject(SocketConnections) protected connections: SocketConnections'); + dependencies.push('@Inject(SocketConnection) protected connection: SocketConnection'); } dependencies = dependencies.concat([ '@Inject(SDKModels) protected models: SDKModels', @@ -613,7 +611,7 @@ module.exports = function generate(ctx) { ]; if (isIo === 'enabled') { - imports.push({ module: 'SocketConnections', from: '../../sockets/socket.connections'}); + imports.push({ module: 'SocketConnection', from: '../../sockets/socket.connections'}); } return buildImports(imports); @@ -671,31 +669,36 @@ module.exports = function generate(ctx) { } ); if (isIo) params = params.filter(param => !param.arg.match(/(fk|data|options)/)); + console.log('CLASSES: ', availableClasses); params.forEach((param, i, arr) => { - let type; + let type, isArray = false; if (param.type === 'object') { type = param.arg === 'filter' ? 'LoopBackFilter' : 'any'; } else { type = param.type !== 'AccessToken' && param.type !== 'any' ? capitalize(param.type) : 'any'; } + console.log('THE ACTUAL TYPE: ', type); if (!type.match(/(^any$|LoopBackFilter)/) && availableClasses.indexOf(type) < 0) { type = 'any'; } let value = ''; // Accept Array on createMany method. if (methodName.match(/createMany/) && param.arg === 'data') { - type = `Array<${type}>`; + isArray = true; } + console.log('RESULT TYPE: ', type); // Set default value, usually will be {}, but on login we include user // Should not be undefined or will create request issues if (!param.required && methodName === 'login' && param.arg === 'include') { type = 'any'; value = " = 'user'"; + } else if (type.match(/(any|LoopBackFilter)/)) { + value = !param.required ? ` = ${ isArray ? '[]' : '{}' }`: ''; } else { - value = !param.required ? ` = ${ type.match(/Array/) ? '[]' : '{}' }`: ''; + value = !param.required ? ` = ${ isArray ? `new Array<${type}>()` : `new ${type}()` }`: ''; } - output.push(`${param.arg}: ${type}${value}`); + output.push(`${param.arg}: ${type}${ isArray ? '[]' : '' }${value}`); }); // When login, there is a property not coming from LoopBack that is needed. diff --git a/lib/angular2/shared/models/fireloop.ejs b/lib/angular2/shared/models/fireloop.ejs index f19d4ade..1883a32b 100644 --- a/lib/angular2/shared/models/fireloop.ejs +++ b/lib/angular2/shared/models/fireloop.ejs @@ -8,7 +8,6 @@ export class FireLoop { public ref(model: any): FireLoopRef { let name: string = model.getModelName(); - if (this.references[name]) { return this.references[name]; } model.models = this.models; this.references[name] = new FireLoopRef(model, this.socket); return this.references[name]; diff --git a/lib/angular2/shared/models/flref.ejs b/lib/angular2/shared/models/flref.ejs index 5a59ecc4..44604f8d 100644 --- a/lib/angular2/shared/models/flref.ejs +++ b/lib/angular2/shared/models/flref.ejs @@ -1,6 +1,7 @@ import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Rx'; import { LoopBackFilter, StatFilter } from './index'; +import { SocketConnection } from '../sockets/socket.connections'; /** * @class FireLoopRef * @author Jonathan Casarrubias @@ -31,7 +32,7 @@ export class FireLoopRef { **/ constructor( private model: any, - private socket: any, + private socket: SocketConnection, private parent: any = null, private relationship: any = null ) { @@ -42,6 +43,21 @@ export class FireLoopRef { return this; } /** + * @method dispose + * @description + * This method is super important to avoid memory leaks in the server. + * This method requires to be called on components destroy + * + * ngOnDestroy() { + * this.someRef.dispose() + * } + **/ + public dispose() { + this.operation('dispose', {}) + .subscribe() + .unsubscribe(); + } + /** * @method upsert * @param data * @description @@ -51,7 +67,7 @@ export class FireLoopRef { return this.operation('upsert', data); } /** - * @method upsert + * @method create * @param data * @description * Operation wrapper for create function. @@ -60,7 +76,7 @@ export class FireLoopRef { return this.operation('create', data); } /** - * @method upsert + * @method remove * @param data * @description * Operation wrapper for remove function. @@ -69,6 +85,37 @@ export class FireLoopRef { return this.operation('remove', data); } /** + * @method remote + * @param method + * @param params + * @description + * This method calls for any remote method. It is flexible enough to + * allow you call either built-in or custom remote methods. + * + * FireLoop provides this interface to enable calling remote methods + * but also to optionally send any defined accept params that will be + * applied within the server. + **/ + public remote(method: string, params?: any[], broadcast: Boolean = false): Observable { + return this.operation('remote', { method, params, broadcast }); + } + /** + * @method onRemote + * @param method + * @param params + * @description + * This method listen for public . + **/ + public onRemote(method: string): Observable { + let event: string = 'remote'; + if (!this.relationship) { + event = `${ this.model.getModelName() }.${event}`; + } else { + event = `${ this.parent.model.getModelName() }.${ this.relationship }.${event}`; + } + return this.broadcasts(event, {}); + } + /** * @method on * @param event * @param filter @@ -81,6 +128,9 @@ export class FireLoopRef { * - child_removed (Triggers when a child is removed) **/ public on(event: string, filter: LoopBackFilter = { limit: 100, order: 'id DESC' }): Observable { + if (event === 'remote') { + throw new Error('The "remote" event is not allowed using "on()" method, use "onRemote()" instead'); + } let request: any; if (!this.relationship) { event = `${ this.model.getModelName() }.${event}`; @@ -113,8 +163,12 @@ export class FireLoopRef { * @method make * @param instance * @description - * This method will set a model instance into this current FireLoop Reference. + * This method will set a model instance into this a new FireLoop Reference. * This allows to persiste parentship when creating related instances. + * + * It also allows to have multiple different persisted instance references to same model. + * otherwise if using singleton will replace a previous instance for a new instance, when + * we actually want to have more than 1 instance of same model. **/ public make(instance: any): FireLoopRef { let reference: FireLoopRef = new FireLoopRef(this.model, this.socket); @@ -165,7 +219,7 @@ export class FireLoopRef { } sbj.next(data); }; - this.socket.onZone(nowEvent, pullNow); + this.socket.on(nowEvent, pullNow); return sbj.asObservable(); } /** @@ -176,12 +230,12 @@ export class FireLoopRef { **/ private broadcasts(event: string, request: any): Observable { let sbj: Subject = new Subject(); - this.socket.onZone( + this.socket.on( `${event}.broadcast.announce.${ this.id }`, (res: T) => this.socket.emit(`${event}.broadcast.request.${ this.id }`, request) ); - this.socket.onZone(`${ event }.broadcast.${ this.id }`, (data: any) => sbj.next(data)); + this.socket.on(`${ event }.broadcast.${ this.id }`, (data: any) => sbj.next(data)); return sbj.asObservable(); } /** @@ -203,10 +257,16 @@ export class FireLoopRef { parent: this.parent && this.parent.instance ? this.parent.instance : null }; this.socket.emit(event, config); - this.socket.onZone(`${ this.model.getModelName() }.value.result.${ this.id }`, (res: any) => - subject.next(res.error ? Observable.throw(res.error) : res) - ); - return subject.asObservable(); + this.socket.on(`${ this.model.getModelName() }.value.result.${ this.id }`, (res: any) => { + if (res.error) { + subject.error(res); + } else { + subject.next(res); + } + }); + // This event listener will be wiped within socket.connections + this.socket.sharedObservables.sharedOnDisconnect.subscribe(() => subject.complete()); + return subject.asObservable().catch((error: any) => Observable.throw(error)); } /** * @method buildId diff --git a/lib/angular2/shared/services/core/base.ejs b/lib/angular2/shared/services/core/base.ejs index 6544205d..b95c9a05 100644 --- a/lib/angular2/shared/services/core/base.ejs +++ b/lib/angular2/shared/services/core/base.ejs @@ -1,5 +1,19 @@ /* tslint:disable */ -<%- buildBaseServiceImports(isIo) %> +import { Injectable, Inject, Optional } from '@angular/core'; +import { Http, Headers, Request } from '@angular/http'; +import { NgModule, ModuleWithProviders } from '@angular/core'; +import { JSONSearchParams } from './search.params'; +import { ErrorHandler } from './error.service'; +import { LoopBackAuth } from './auth.service'; +import { LoopBackConfig } from '../../lb.config'; +import { LoopBackFilter, AccessToken } from '../../models/BaseModels'; +import { SDKModels } from '../custom/SDKModels'; +import { Observable } from 'rxjs/Observable'; +import { Subject } from 'rxjs/Subject'; +import { ErrorObservable } from 'rxjs/observable/ErrorObservable'; +import 'rxjs/add/operator/catch'; +import 'rxjs/add/operator/map'; +import { SocketConnection } from '../../sockets/socket.connections'; // Making Sure EventSource Type is available to avoid compilation issues. declare var EventSource: any; /** @@ -20,7 +34,12 @@ export abstract class BaseLoopBackApi { protected model: any; constructor( - <%- buildServiceDI(isIo) %> + @Inject(Http) protected http: Http, + @Inject(SocketConnection) protected connection: SocketConnection, + @Inject(SDKModels) protected models: SDKModels, + @Inject(LoopBackAuth) protected auth: LoopBackAuth, + @Inject(JSONSearchParams) protected searchParams: JSONSearchParams, + @Optional() @Inject(ErrorHandler) protected errorHandler: ErrorHandler ) { this.model = this.models.get(this.getModelName()); } @@ -39,8 +58,8 @@ export abstract class BaseLoopBackApi { url : string, routeParams : any = {}, urlParams : any = {}, - postBody : any = {}<% if (isIo === 'enabled') { %>, - isio : boolean = false<% } %> + postBody : any = {}, + isio : boolean = false ): Observable { let headers = new Headers(); @@ -61,7 +80,7 @@ export abstract class BaseLoopBackApi { routeParams[key] + "$1" ); } -<% if (isIo === 'enabled') { %> + if (isio) { if (requestUrl.match(/fk/)) { let arr = requestUrl.split('/'); arr.pop(); @@ -72,11 +91,11 @@ export abstract class BaseLoopBackApi { let token: AccessToken = new AccessToken(); token.id = this.auth.getAccessTokenId(); token.userId = this.auth.getCurrentUserId(); - let socket: any = this.connections.getHandler(LoopBackConfig.getPath(), token); - socket.on(event, (res: any) => subject.next(res)); + this.connection.connect(token); + this.connection.on(event, (res: any) => subject.next(res)); return subject.asObservable(); } -<% } %> + // Body fix for built in remote methods using "data", "options" or "credentials // that are the actual body, Custom remote method properties are different and need // to be wrapped into a body object diff --git a/lib/angular2/shared/services/core/realtime.ejs b/lib/angular2/shared/services/core/realtime.ejs index d04106d3..aeaee0a2 100644 --- a/lib/angular2/shared/services/core/realtime.ejs +++ b/lib/angular2/shared/services/core/realtime.ejs @@ -4,58 +4,98 @@ import { JSONSearchParams } from './search.params'; import { LoopBackAuth } from './auth.service'; import { LoopBackConfig } from '../../lb.config'; import { FireLoop } from '../../models/FireLoop'; -import { SocketConnections } from '../../sockets/socket.connections'; +import { SocketConnection } from '../../sockets/socket.connections'; import { SDKModels } from '../custom/SDKModels'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs/Rx'; import { Subject } from 'rxjs/Subject'; - +import { Subscription } from 'rxjs/Subscription'; +/** +* @author Jonathan Casarrubias +* @module RealTime +* @license MIT +* @description +* This module is a real-time interface for using socket connections, its main purpose +* is to make sure that when there is a valid connection, it will create instances +* of the different real-time functionalities like FireLoop, PubSub and IO. +**/ @Injectable() export class RealTime { public IO: IO; public FireLoop: FireLoop; - private connected: boolean = false; - private socket: any; + private connecting: boolean = false; + private onReadySubject: Subject = new Subject(); + private sharedOnReady: Observable = this.onReadySubject.asObservable().share(); constructor( - @Inject(SocketConnections) protected connections: SocketConnections, + @Inject(SocketConnection) public connection: SocketConnection, @Inject(SDKModels) protected models: SDKModels, @Inject(LoopBackAuth) protected auth: LoopBackAuth, @Inject(JSONSearchParams) protected searchParams: JSONSearchParams - ) {} - - disconnect(): void { - this.connected = false; - this.IO = null; - this.FireLoop = null; - this.connections.disconnect(); + ) { + this.sharedOnReady.subscribe(); } - - getConnection(): void { - return this.connections.getHandler(LoopBackConfig.getPath(), this.auth.getToken()); + /** + * @method onDisconnect + * @description + * Will trigger when Real-Time Service is disconnected from server. + **/ + onDisconnect(): Observable { + return this.connection.sharedObservables.sharedOnDisconnect; + } + /** + * @method onAuthenticated + * @description + * Will trigger when Real-Time Service is not authorized from server. + **/ + onAuthenticated(): Observable { + return this.connection.sharedObservables.sharedOnAuthenticated; + } + /** + * @method onUnAuthorized + * @description + * Will trigger when Real-Time Service is not authorized from server. + **/ + onUnAuthorized(): Observable { + return this.connection.sharedObservables.sharedOnUnAuthorized; } /** * @method onReady * @description - * Will trigger when FireLoop is Ready for broadcasting. + * Will trigger when Real-Time Service is Ready for broadcasting. + * and will register connection flow events to notify subscribers. **/ - public onReady(): Observable { - let subject: Subject = new Subject(); - if (this.connected) { - setTimeout(() => subject.next()); - } else { - this.socket = this.getConnection(); - this.IO = new IO(this.socket); - this.FireLoop = new FireLoop(this.socket, this.models); - this.socket.on('connect', () => { - this.connected = true; - subject.next(); + public onReady(): Observable { + if (this.connection.isConnected()) { + // Send back to the event loop so it executes after subscription + let to = setTimeout(() => { + this.onReadySubject.next(); + clearTimeout(to); }); - this.socket.on('disconnect', () => { - subject.complete(); - this.disconnect(); + } else if (this.connecting) { + let ti = setInterval(() => { + if (this.connection.isConnected()) { + this.onReadySubject.next(); + clearInterval(ti); + } + }, 500); + + } else { + this.connecting = true; + this.connection.connect(this.auth.getToken()); + this.IO = new IO(this.connection); + this.FireLoop = new FireLoop(this.connection, this.models); + // Fire event for those subscribed + let s1: Subscription = this.connection.sharedObservables.sharedOnConnect.subscribe(() => { + console.log('Real-Time connection has been established'); + this.connecting = false; + this.onReadySubject.next('connected'); + let s2: Subscription = this.connection.sharedObservables.sharedOnDisconnect.subscribe(() => { + s1.unsubscribe(); + s2.unsubscribe(); + }); }); } - return subject.asObservable(); + return this.sharedOnReady; } } diff --git a/lib/angular2/shared/services/custom/service.ejs b/lib/angular2/shared/services/custom/service.ejs index 1ee5d644..5a98c5b0 100644 --- a/lib/angular2/shared/services/custom/service.ejs +++ b/lib/angular2/shared/services/custom/service.ejs @@ -17,7 +17,7 @@ export class <%-: modelName %>Api extends BaseLoopBackApi { constructor( <%- buildServiceDI(isIo) %> ) { - super(http, <% if (isIo === 'enabled') { %> connections, <% } %> models, auth, searchParams, errorHandler); + super(http, <% if (isIo === 'enabled') { %> connection, <% } %> models, auth, searchParams, errorHandler); } <% model.methods.forEach(function(action) { diff --git a/lib/angular2/shared/sockets/connections.ts b/lib/angular2/shared/sockets/connections.ts index 24163ec4..b41e7b3c 100644 --- a/lib/angular2/shared/sockets/connections.ts +++ b/lib/angular2/shared/sockets/connections.ts @@ -2,9 +2,12 @@ import { Injectable, Inject, NgZone } from '@angular/core'; import { SocketDriver } from './socket.driver'; import { AccessToken } from '../models'; +import { Subject } from 'rxjs/Subject'; +import { Observable } from 'rxjs/Observable'; +import { LoopBackConfig } from '../lb.config'; /** * @author Jonathan Casarrubias -* @module SocketConnections +* @module SocketConnection * @license MIT * @description * This module handle socket connections and return singleton instances for each @@ -12,75 +15,143 @@ import { AccessToken } from '../models'; * Angular 2 for web and NativeScript 2. **/ @Injectable() -export class SocketConnections { - private connections: any = {}; - private configured: boolean = false; +export class SocketConnection { + private socket: any; + private subjects: { + onConnect: Subject<{}>, + onDisconnect: Subject<{}>, + onAuthenticated: Subject<{}>, + onUnAuthorized: Subject<{}> + } = { + onConnect: new Subject(), + onDisconnect: new Subject(), + onAuthenticated: new Subject(), + onUnAuthorized: new Subject() + }; + public sharedObservables: { + sharedOnConnect?: Observable<{}>, + sharedOnDisconnect?: Observable<{}>, + sharedOnAuthenticated?: Observable<{}>, + sharedOnUnAuthorized?: Observable<{}> + } = {}; + private unauthenticated: boolean = true; + /** + * @method constructor + * @param driver + * @param zone + **/ constructor( @Inject(SocketDriver) private driver: SocketDriver, @Inject(NgZone) private zone: NgZone - ) { } - getHandler(url: string, token: AccessToken) { - console.log('Getting handler for socket connections'); - if (!this.connections[url]) { - console.log('Creating a new connection with: ', url); - let config: any = { log: false, secure: false, forceNew: true, forceWebsockets: true }; - this.connections[url] = this.driver.connect(url, config); - this.connections[url].onZone = ((event: string, handler: Function) => { - this.connections[url].on(event, (data: any) => { - this.zone.run(() => handler(data)); - }); + ) { + this.sharedObservables = { + sharedOnConnect: this.subjects.onConnect.asObservable().share(), + sharedOnDisconnect: this.subjects.onDisconnect.asObservable().share(), + sharedOnAuthenticated: this.subjects.onAuthenticated.asObservable().share(), + sharedOnUnAuthorized: this.subjects.onUnAuthorized.asObservable().share() + }; + // This is needed to create the first channel, subsequents will share the connection + // We are using Hot Observables to avoid duplicating connection status events. + this.sharedObservables.sharedOnConnect.subscribe(); + this.sharedObservables.sharedOnDisconnect.subscribe(); + this.sharedObservables.sharedOnAuthenticated.subscribe(); + this.sharedObservables.sharedOnUnAuthorized.subscribe(); + } + /** + * @method connect + * @param url string + * @param token AccessToken + * @description + * This method will return a socket socket connection + **/ + public connect(token: AccessToken = null): void { + if (!this.socket) { + console.info('Creating a new connection with: ', LoopBackConfig.getPath()); + // Create new socket connection + this.socket = this.driver.connect(LoopBackConfig.getPath(), { + log: false, + secure: false, + forceNew: true, + forceWebsockets: true }); - this.connections[url].on('connect', () => { - if (!this.configured) - this.setupConnection(url, token, config); + // Listen for connection + this.on('connect', () => { + this.subjects.onConnect.next('connected'); + // Authenticate or start heartbeat now + this.emit('authentication', token); }); - let forceConfig: any = setInterval(() => { - if (!this.configured && this.connections[url] && this.connections[url].connected) { - console.info('Forcing IO Configuration'); - this.setupConnection(url, token, config); - clearInterval(forceConfig); - } else if (this.configured) { - clearInterval(forceConfig); - } - }, 1000) - } else { - console.log('Reusing existing connection: ', url); + // Listen for authentication + this.on('authenticated', () => { + this.subjects.onAuthenticated.next(); + this.heartbeater(); + }) + // Listen for authentication + this.on('unauthorized', (err: any) => { + this.subjects.onUnAuthorized.next(err); + }) + // Listen for disconnections + this.on('disconnect', (status: any) => this.subjects.onDisconnect.next(status)); + } else if (this.socket && !this.socket.connected){ + this.socket.off(); + this.socket.destroy(); + delete this.socket; + this.connect(token); } - return this.connections[url]; } - - public disconnect() { - Object.keys(this.connections).forEach((connKey) => { - if (this.connections[connKey] && this.connections[connKey].connected) { - this.connections[connKey].disconnect(); - } - if (this.connections[connKey].off) { - this.connections[connKey].off(); - } - }); - this.connections = {}; - this.configured = false; + /** + * @method isConnected + * @description + * This method will return true or false depending on established connections + **/ + public isConnected(): boolean { + return (this.socket && this.socket.connected); } - - private setupConnection(url: string, token: AccessToken, config: any): void { - this.configured = true; - console.log('Connected to %s', url); - if (token.id) { - console.log('Emitting authentication', token.id); - this.connections[url].emit('authentication', token); - } - this.connections[url].on('unauthorized', (res: any) => console.error('Unauthenticated', res)); + /** + * @method on + * @description + * This method will wrap the original "on" method and run it within the Angular Zone + **/ + public on(event: string, handler: Function): void { + this.socket.on(event, (data: any) => this.zone.run(() => handler(data))); + } + /** + * @method emit + * @description + * This method will wrap the original "on" method and run it within the Angular Zone + **/ + public emit(event: string, data: any): void { + this.socket.emit(event, data); + } + /** + * @method removeListener + * @description + * This method will wrap the original "on" method and run it within the Angular Zone + **/ + public removeListener(event: string, handler: Function): void { + this.socket.removeListener(event, handler); + } + /** + * @method disconnect + * @description + * This will disconnect the client from the server + **/ + public disconnect(): void { + this.socket.disconnect(); + } + /** + * @method heartbeater + * @description + * This will keep the connection as active, even when users are not sending + * data, this avoids disconnection because of a connection not being used. + **/ + private heartbeater(): void { let heartbeater: any = setInterval(() => { - if (this.connections[url]) { - this.connections[url].emit('lb-ping'); + if (this.socket && this.socket.connected) { + this.socket.emit('lb-ping'); } else { - clearInterval(heartbeater); + clearInterval(heartbeater); } }, 15000); - this.connections[url].on('lb-pong', (data: any) => console.info('Heartbeat: ', data)); - this.connections[url].on('disconnect', (data: any) => { - this.disconnect(); - console.info('Disconnected from WebSocket server'); - }); + this.socket.on('lb-pong', (data: any) => console.info('Heartbeat: ', data)); } } diff --git a/tests/fireloop/package.json b/tests/fireloop/package.json index b7f80ee5..8f577fa0 100644 --- a/tests/fireloop/package.json +++ b/tests/fireloop/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@mean-expert/boot-script": "^1.0.0", - "@mean-expert/loopback-component-realtime": "^1.0.0-rc.2", + "@mean-expert/loopback-component-realtime": "file:///Volumes/HD710M/development/www/mean.expert/@mean-expert/loopback-component-realtime", "@mean-expert/loopback-stats-mixin": "^1.2.0", "@mean-expert/model": "^1.0.9", "chai": "^3.5.0", diff --git a/tests/fireloop/server/component-config.json b/tests/fireloop/server/component-config.json index 436fbe0b..aaedc3f6 100644 --- a/tests/fireloop/server/component-config.json +++ b/tests/fireloop/server/component-config.json @@ -4,6 +4,6 @@ }, "@mean-expert/loopback-component-realtime": { "auth": true, - "debug": false + "debug": true } } diff --git a/tests/ng2web/src/app/app.component.ts b/tests/ng2web/src/app/app.component.ts index eaa76000..479b7bb2 100644 --- a/tests/ng2web/src/app/app.component.ts +++ b/tests/ng2web/src/app/app.component.ts @@ -1,11 +1,7 @@ import { Component } from '@angular/core'; +import { Router } from '@angular/router'; import { LoggerService } from './shared/sdk/services'; -// import { RealTime } from './shared/sdk/services'; -// import { Room, FireLoopRef } from './shared/sdk/models'; -// Hardcoded Message interface, this because Messge models -// Is private, this is only for testing purposes, in real life -// the Message should not be private -if needed in front end- -interface Message { text: string; } +import { RealTime } from './shared/sdk/services'; @Component({ selector: 'app-root', @@ -18,38 +14,22 @@ export class AppComponent { title = 'LoopBack SDK Builder Test Application'; constructor( - private logger: LoggerService + private logger: LoggerService, + private realTime: RealTime, + private router: Router ) { this.logger.info('LoopBack SDK Builder - Test Application'); - // Simple IO Test - // this.realTime.IO.emit('hello', 'world'); - // this.realTime.IO.on('hello').subscribe((msg: any) => this.logger.info('REALTIME: ', msg)); - // Simple FireLoop set and get examples. - /*let RoomReference: FireLoopRef = this.realTime.FireLoop.ref(Room); - // This will get the list of results and fire every time there is new data. - RoomReference.on('changes').subscribe((rooms: Array) => this.logger.info(rooms)); - RoomReference.on('child_added', { - limit: 2, - order: 'id DESC' - }).subscribe((room: Room) => this.logger.info(room.name)); - RoomReference.on('child_changed', { - limit: 2, - order: 'id DESC' - }).subscribe((room: Room) => this.logger.info(room.name)); - // This will set a new value into the reference - [ - new Room({ name: 'Room 1' }), - new Room({ name: 'Room 2' }), - new Room({ name: 'Room 3' }), - new Room({ name: 'Room 4' }) - ].forEach((room: Room) => RoomReference.create(room).subscribe((instance: Room) => { - - // Child Feature - let MessageReference: FireLoopRef = RoomReference.make(instance).child('messages'); - MessageReference.on('changes').subscribe( - (messages: Array) => this.logger.info(messages) - ); - MessageReference.upsert({ text : 'Hello Child Reference' }).subscribe((res: Message) => console.log(res.text)); - }));*/ + this.realTime.onReady().subscribe(() => { + console.log('NgApp has been connected'); + }); + this.realTime.onDisconnect().subscribe((error: string) => { + console.log('Disconnected', error); + }); + this.realTime.onUnAuthorized().subscribe((error: string) => { + this.router.navigate(['/access']); + }); + this.realTime.onAuthenticated().subscribe((error: string) => { + console.log('I am authorized'); + }); } } diff --git a/tests/ng2web/src/app/room/room.component.html b/tests/ng2web/src/app/room/room.component.html index f6c6f925..66b4a86d 100644 --- a/tests/ng2web/src/app/room/room.component.html +++ b/tests/ng2web/src/app/room/room.component.html @@ -5,6 +5,7 @@

Messages

+
  • Message: {{ _message.text }} diff --git a/tests/ng2web/src/app/room/room.component.ts b/tests/ng2web/src/app/room/room.component.ts index 08d9999d..5701589d 100644 --- a/tests/ng2web/src/app/room/room.component.ts +++ b/tests/ng2web/src/app/room/room.component.ts @@ -40,14 +40,32 @@ export class RoomComponent implements OnInit, OnDestroy { this.listenMessages(); }, err => alert(err.message))); }); + this.roomRef.onRemote('findByRoom').subscribe((room: Room) => { + this.logger.info(`OnRemote Method Result`); + this.logger.info(room); + }); + }, (error: any) => { + console.log('ERROR: ', error); }); } + findByRoom(): void { + let subscription: Subscription = this.roomRef + .remote('findByRoom', [this.room], true) + .subscribe((room: Room) => { + this.logger.info(`Remote Method Result`); + this.logger.info(room); + subscription.unsubscribe(); + }); + } + ngOnDestroy() { this.subscriptions.forEach((subscription: Subscription) => subscription.unsubscribe()); - this.roomRef = null; + this.roomRef.dispose(); + this.messageRef.dispose(); + this.roomRef = null; this.messageRef = null; - this.replyRefs = {}; + this.replyRefs = {}; } send(): void { diff --git a/tests/ng2web/src/app/room/room.list.component.html b/tests/ng2web/src/app/room/room.list.component.html index 39aab7f7..b446b47b 100644 --- a/tests/ng2web/src/app/room/room.list.component.html +++ b/tests/ng2web/src/app/room/room.list.component.html @@ -1,9 +1,10 @@ - -

    Update Email

    + + +

    Room Name {{ room.name }}

    diff --git a/tests/ng2web/src/app/room/room.list.component.ts b/tests/ng2web/src/app/room/room.list.component.ts index ee36ad5f..60d107c9 100644 --- a/tests/ng2web/src/app/room/room.list.component.ts +++ b/tests/ng2web/src/app/room/room.list.component.ts @@ -6,6 +6,11 @@ import { Router } from '@angular/router'; import { Subscription } from 'rxjs'; import { LoopBackConfig } from '../shared/sdk/lb.config'; LoopBackConfig.setBaseURL('http://127.0.0.1:3002'); + +import * as io from 'socket.io-client'; + +var i = 0; + @Component({ selector: 'app-room', templateUrl: './room.list.component.html' @@ -18,7 +23,9 @@ export class RoomListComponent implements OnInit, OnDestroy { private roomRef: FireLoopRef; private room: Room = new Room(); private rooms: Room[]; + private connected: boolean = false; private subscriptions: Subscription[] = new Array(); + //private socket: any = io.connect(LoopBackConfig.getPath(), { secure: false }); constructor( private accountApi: AccountApi, @@ -29,21 +36,28 @@ export class RoomListComponent implements OnInit, OnDestroy { private models: SDKModels ) { this.logger.info('Room Module Loaded'); - this.logged = this.accountApi.getCachedCurrent(); + this.logged = this.accountApi.getCachedCurrent(); } ngOnInit() { - this.realTime.onReady().subscribe(() => { + this.subscriptions.push(this.realTime.onReady().subscribe((status: string) => { + console.log('CONNECT STATUS: ', status); + this.connected = true; this.accountRef = this.realTime.FireLoop.ref(Account); - this.roomRef = this.realTime.FireLoop.ref(Room); + this.roomRef = this.realTime.FireLoop.ref(Room); this.subscriptions.push( this.roomRef.on('change').subscribe((rooms: Room[]) => this.rooms = rooms) ); this.subscriptions.push( - this.roomRef.on('child_added').subscribe((child: Room) => { - console.log('CHILD ADDED: ', child); - } - )); + this.roomRef.on('child_added').subscribe((child: Room) => {}) + ); + })); + } + + logout(): void { + this.accountApi.logout().subscribe(() => { + this.realTime.connection.disconnect(); + this.router.navigate(['/access']); }); } @@ -51,11 +65,6 @@ export class RoomListComponent implements OnInit, OnDestroy { this.subscriptions.forEach((subscription: Subscription) => subscription.unsubscribe()); } - logout(): void { - this.realTime.disconnect(); - this.accountApi.logout().subscribe(res => this.router.navigate(['/access'])); - } - update(): void { this.accountRef.upsert(this.logged).subscribe(res => console.log(res)); } diff --git a/tests/ng2web/src/app/shared/auth.guard.ts b/tests/ng2web/src/app/shared/auth.guard.ts index 1da0477b..623b92f2 100644 --- a/tests/ng2web/src/app/shared/auth.guard.ts +++ b/tests/ng2web/src/app/shared/auth.guard.ts @@ -13,8 +13,8 @@ export class AuthGuard implements CanActivate { if (this.accountApi.isAuthenticated()) { return true; } else { - this.router.navigate(['/access']); - return false; + //this.router.navigate(['/access']); + return true; } } } diff --git a/tests/ng2web/src/app/shared/sdk/index.ts b/tests/ng2web/src/app/shared/sdk/index.ts index 2c1710de..22aa29f2 100644 --- a/tests/ng2web/src/app/shared/sdk/index.ts +++ b/tests/ng2web/src/app/shared/sdk/index.ts @@ -46,7 +46,7 @@ import { CookieBrowser } from './storage/cookie.browser'; import { StorageBrowser } from './storage/storage.browser'; import { SocketBrowser } from './sockets/socket.browser'; import { SocketDriver } from './sockets/socket.driver'; -import { SocketConnections } from './sockets/socket.connections'; +import { SocketConnection } from './sockets/socket.connections'; import { RealTime } from './services/core/real.time'; import { UserApi } from './services/custom/User'; import { AccountApi } from './services/custom/Account'; @@ -76,7 +76,7 @@ import { UserIdentityApi } from './services/custom/UserIdentity'; exports: [ ], providers: [ ErrorHandler, - SocketConnections + SocketConnection ] }) export class SDKBrowserModule { diff --git a/tests/ng2web/src/app/shared/sdk/models/FireLoop.ts b/tests/ng2web/src/app/shared/sdk/models/FireLoop.ts index f19d4ade..1883a32b 100644 --- a/tests/ng2web/src/app/shared/sdk/models/FireLoop.ts +++ b/tests/ng2web/src/app/shared/sdk/models/FireLoop.ts @@ -8,7 +8,6 @@ export class FireLoop { public ref(model: any): FireLoopRef { let name: string = model.getModelName(); - if (this.references[name]) { return this.references[name]; } model.models = this.models; this.references[name] = new FireLoopRef(model, this.socket); return this.references[name]; diff --git a/tests/ng2web/src/app/shared/sdk/models/FireLoopRef.ts b/tests/ng2web/src/app/shared/sdk/models/FireLoopRef.ts index 5a59ecc4..44604f8d 100644 --- a/tests/ng2web/src/app/shared/sdk/models/FireLoopRef.ts +++ b/tests/ng2web/src/app/shared/sdk/models/FireLoopRef.ts @@ -1,6 +1,7 @@ import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Rx'; import { LoopBackFilter, StatFilter } from './index'; +import { SocketConnection } from '../sockets/socket.connections'; /** * @class FireLoopRef * @author Jonathan Casarrubias @@ -31,7 +32,7 @@ export class FireLoopRef { **/ constructor( private model: any, - private socket: any, + private socket: SocketConnection, private parent: any = null, private relationship: any = null ) { @@ -42,6 +43,21 @@ export class FireLoopRef { return this; } /** + * @method dispose + * @description + * This method is super important to avoid memory leaks in the server. + * This method requires to be called on components destroy + * + * ngOnDestroy() { + * this.someRef.dispose() + * } + **/ + public dispose() { + this.operation('dispose', {}) + .subscribe() + .unsubscribe(); + } + /** * @method upsert * @param data * @description @@ -51,7 +67,7 @@ export class FireLoopRef { return this.operation('upsert', data); } /** - * @method upsert + * @method create * @param data * @description * Operation wrapper for create function. @@ -60,7 +76,7 @@ export class FireLoopRef { return this.operation('create', data); } /** - * @method upsert + * @method remove * @param data * @description * Operation wrapper for remove function. @@ -69,6 +85,37 @@ export class FireLoopRef { return this.operation('remove', data); } /** + * @method remote + * @param method + * @param params + * @description + * This method calls for any remote method. It is flexible enough to + * allow you call either built-in or custom remote methods. + * + * FireLoop provides this interface to enable calling remote methods + * but also to optionally send any defined accept params that will be + * applied within the server. + **/ + public remote(method: string, params?: any[], broadcast: Boolean = false): Observable { + return this.operation('remote', { method, params, broadcast }); + } + /** + * @method onRemote + * @param method + * @param params + * @description + * This method listen for public . + **/ + public onRemote(method: string): Observable { + let event: string = 'remote'; + if (!this.relationship) { + event = `${ this.model.getModelName() }.${event}`; + } else { + event = `${ this.parent.model.getModelName() }.${ this.relationship }.${event}`; + } + return this.broadcasts(event, {}); + } + /** * @method on * @param event * @param filter @@ -81,6 +128,9 @@ export class FireLoopRef { * - child_removed (Triggers when a child is removed) **/ public on(event: string, filter: LoopBackFilter = { limit: 100, order: 'id DESC' }): Observable { + if (event === 'remote') { + throw new Error('The "remote" event is not allowed using "on()" method, use "onRemote()" instead'); + } let request: any; if (!this.relationship) { event = `${ this.model.getModelName() }.${event}`; @@ -113,8 +163,12 @@ export class FireLoopRef { * @method make * @param instance * @description - * This method will set a model instance into this current FireLoop Reference. + * This method will set a model instance into this a new FireLoop Reference. * This allows to persiste parentship when creating related instances. + * + * It also allows to have multiple different persisted instance references to same model. + * otherwise if using singleton will replace a previous instance for a new instance, when + * we actually want to have more than 1 instance of same model. **/ public make(instance: any): FireLoopRef { let reference: FireLoopRef = new FireLoopRef(this.model, this.socket); @@ -165,7 +219,7 @@ export class FireLoopRef { } sbj.next(data); }; - this.socket.onZone(nowEvent, pullNow); + this.socket.on(nowEvent, pullNow); return sbj.asObservable(); } /** @@ -176,12 +230,12 @@ export class FireLoopRef { **/ private broadcasts(event: string, request: any): Observable { let sbj: Subject = new Subject(); - this.socket.onZone( + this.socket.on( `${event}.broadcast.announce.${ this.id }`, (res: T) => this.socket.emit(`${event}.broadcast.request.${ this.id }`, request) ); - this.socket.onZone(`${ event }.broadcast.${ this.id }`, (data: any) => sbj.next(data)); + this.socket.on(`${ event }.broadcast.${ this.id }`, (data: any) => sbj.next(data)); return sbj.asObservable(); } /** @@ -203,10 +257,16 @@ export class FireLoopRef { parent: this.parent && this.parent.instance ? this.parent.instance : null }; this.socket.emit(event, config); - this.socket.onZone(`${ this.model.getModelName() }.value.result.${ this.id }`, (res: any) => - subject.next(res.error ? Observable.throw(res.error) : res) - ); - return subject.asObservable(); + this.socket.on(`${ this.model.getModelName() }.value.result.${ this.id }`, (res: any) => { + if (res.error) { + subject.error(res); + } else { + subject.next(res); + } + }); + // This event listener will be wiped within socket.connections + this.socket.sharedObservables.sharedOnDisconnect.subscribe(() => subject.complete()); + return subject.asObservable().catch((error: any) => Observable.throw(error)); } /** * @method buildId diff --git a/tests/ng2web/src/app/shared/sdk/services/core/base.service.ts b/tests/ng2web/src/app/shared/sdk/services/core/base.service.ts index 52490828..b95c9a05 100644 --- a/tests/ng2web/src/app/shared/sdk/services/core/base.service.ts +++ b/tests/ng2web/src/app/shared/sdk/services/core/base.service.ts @@ -13,7 +13,7 @@ import { Subject } from 'rxjs/Subject'; import { ErrorObservable } from 'rxjs/observable/ErrorObservable'; import 'rxjs/add/operator/catch'; import 'rxjs/add/operator/map'; -import { SocketConnections } from '../../sockets/socket.connections'; +import { SocketConnection } from '../../sockets/socket.connections'; // Making Sure EventSource Type is available to avoid compilation issues. declare var EventSource: any; /** @@ -35,7 +35,7 @@ export abstract class BaseLoopBackApi { constructor( @Inject(Http) protected http: Http, - @Inject(SocketConnections) protected connections: SocketConnections, + @Inject(SocketConnection) protected connection: SocketConnection, @Inject(SDKModels) protected models: SDKModels, @Inject(LoopBackAuth) protected auth: LoopBackAuth, @Inject(JSONSearchParams) protected searchParams: JSONSearchParams, @@ -91,8 +91,8 @@ export abstract class BaseLoopBackApi { let token: AccessToken = new AccessToken(); token.id = this.auth.getAccessTokenId(); token.userId = this.auth.getCurrentUserId(); - let socket: any = this.connections.getHandler(LoopBackConfig.getPath(), token); - socket.on(event, (res: any) => subject.next(res)); + this.connection.connect(token); + this.connection.on(event, (res: any) => subject.next(res)); return subject.asObservable(); } diff --git a/tests/ng2web/src/app/shared/sdk/services/core/real.time.ts b/tests/ng2web/src/app/shared/sdk/services/core/real.time.ts index d04106d3..aeaee0a2 100644 --- a/tests/ng2web/src/app/shared/sdk/services/core/real.time.ts +++ b/tests/ng2web/src/app/shared/sdk/services/core/real.time.ts @@ -4,58 +4,98 @@ import { JSONSearchParams } from './search.params'; import { LoopBackAuth } from './auth.service'; import { LoopBackConfig } from '../../lb.config'; import { FireLoop } from '../../models/FireLoop'; -import { SocketConnections } from '../../sockets/socket.connections'; +import { SocketConnection } from '../../sockets/socket.connections'; import { SDKModels } from '../custom/SDKModels'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs/Rx'; import { Subject } from 'rxjs/Subject'; - +import { Subscription } from 'rxjs/Subscription'; +/** +* @author Jonathan Casarrubias +* @module RealTime +* @license MIT +* @description +* This module is a real-time interface for using socket connections, its main purpose +* is to make sure that when there is a valid connection, it will create instances +* of the different real-time functionalities like FireLoop, PubSub and IO. +**/ @Injectable() export class RealTime { public IO: IO; public FireLoop: FireLoop; - private connected: boolean = false; - private socket: any; + private connecting: boolean = false; + private onReadySubject: Subject = new Subject(); + private sharedOnReady: Observable = this.onReadySubject.asObservable().share(); constructor( - @Inject(SocketConnections) protected connections: SocketConnections, + @Inject(SocketConnection) public connection: SocketConnection, @Inject(SDKModels) protected models: SDKModels, @Inject(LoopBackAuth) protected auth: LoopBackAuth, @Inject(JSONSearchParams) protected searchParams: JSONSearchParams - ) {} - - disconnect(): void { - this.connected = false; - this.IO = null; - this.FireLoop = null; - this.connections.disconnect(); + ) { + this.sharedOnReady.subscribe(); } - - getConnection(): void { - return this.connections.getHandler(LoopBackConfig.getPath(), this.auth.getToken()); + /** + * @method onDisconnect + * @description + * Will trigger when Real-Time Service is disconnected from server. + **/ + onDisconnect(): Observable { + return this.connection.sharedObservables.sharedOnDisconnect; + } + /** + * @method onAuthenticated + * @description + * Will trigger when Real-Time Service is not authorized from server. + **/ + onAuthenticated(): Observable { + return this.connection.sharedObservables.sharedOnAuthenticated; + } + /** + * @method onUnAuthorized + * @description + * Will trigger when Real-Time Service is not authorized from server. + **/ + onUnAuthorized(): Observable { + return this.connection.sharedObservables.sharedOnUnAuthorized; } /** * @method onReady * @description - * Will trigger when FireLoop is Ready for broadcasting. + * Will trigger when Real-Time Service is Ready for broadcasting. + * and will register connection flow events to notify subscribers. **/ - public onReady(): Observable { - let subject: Subject = new Subject(); - if (this.connected) { - setTimeout(() => subject.next()); - } else { - this.socket = this.getConnection(); - this.IO = new IO(this.socket); - this.FireLoop = new FireLoop(this.socket, this.models); - this.socket.on('connect', () => { - this.connected = true; - subject.next(); + public onReady(): Observable { + if (this.connection.isConnected()) { + // Send back to the event loop so it executes after subscription + let to = setTimeout(() => { + this.onReadySubject.next(); + clearTimeout(to); }); - this.socket.on('disconnect', () => { - subject.complete(); - this.disconnect(); + } else if (this.connecting) { + let ti = setInterval(() => { + if (this.connection.isConnected()) { + this.onReadySubject.next(); + clearInterval(ti); + } + }, 500); + + } else { + this.connecting = true; + this.connection.connect(this.auth.getToken()); + this.IO = new IO(this.connection); + this.FireLoop = new FireLoop(this.connection, this.models); + // Fire event for those subscribed + let s1: Subscription = this.connection.sharedObservables.sharedOnConnect.subscribe(() => { + console.log('Real-Time connection has been established'); + this.connecting = false; + this.onReadySubject.next('connected'); + let s2: Subscription = this.connection.sharedObservables.sharedOnDisconnect.subscribe(() => { + s1.unsubscribe(); + s2.unsubscribe(); + }); }); } - return subject.asObservable(); + return this.sharedOnReady; } } diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/Account.ts b/tests/ng2web/src/app/shared/sdk/services/custom/Account.ts index 3f1e1296..68815cf8 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/Account.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/Account.ts @@ -12,7 +12,7 @@ import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import { Account } from '../../models/Account'; -import { SocketConnections } from '../../sockets/socket.connections'; +import { SocketConnection } from '../../sockets/socket.connections'; import { RoomAccount } from '../../models/RoomAccount'; import { Room } from '../../models/Room'; import { RoomAdmin } from '../../models/RoomAdmin'; @@ -26,13 +26,13 @@ export class AccountApi extends BaseLoopBackApi { constructor( @Inject(Http) protected http: Http, - @Inject(SocketConnections) protected connections: SocketConnections, + @Inject(SocketConnection) protected connection: SocketConnection, @Inject(SDKModels) protected models: SDKModels, @Inject(LoopBackAuth) protected auth: LoopBackAuth, @Inject(JSONSearchParams) protected searchParams: JSONSearchParams, @Optional() @Inject(ErrorHandler) protected errorHandler: ErrorHandler ) { - super(http, connections, models, auth, searchParams, errorHandler); + super(http, connection, models, auth, searchParams, errorHandler); } /** @@ -1055,7 +1055,7 @@ export class AccountApi extends BaseLoopBackApi { * This usually means the response is a `Account` object.) * */ - public createManyAccessTokens(id: any, data: Array = []): Observable { + public createManyAccessTokens(id: any, data: any[] = []): Observable { let _method: string = "POST"; let _url: string = LoopBackConfig.getPath() + "/" + LoopBackConfig.getApiVersion() + "/accounts/:id/accessTokens"; @@ -1088,7 +1088,7 @@ export class AccountApi extends BaseLoopBackApi { * This usually means the response is a `Account` object.) * */ - public createManyRooms(id: any, data: Array = []): Observable { + public createManyRooms(id: any, data: any[] = []): Observable { let _method: string = "POST"; let _url: string = LoopBackConfig.getPath() + "/" + LoopBackConfig.getApiVersion() + "/accounts/:id/rooms"; @@ -1121,7 +1121,7 @@ export class AccountApi extends BaseLoopBackApi { * This usually means the response is a `Account` object.) * */ - public createManyAdministrations(id: any, data: Array = []): Observable { + public createManyAdministrations(id: any, data: any[] = []): Observable { let _method: string = "POST"; let _url: string = LoopBackConfig.getPath() + "/" + LoopBackConfig.getApiVersion() + "/accounts/:id/administrations"; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/ApplicationCredential.ts b/tests/ng2web/src/app/shared/sdk/services/custom/ApplicationCredential.ts index 9ad1b7ee..03cc248a 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/ApplicationCredential.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/ApplicationCredential.ts @@ -12,7 +12,7 @@ import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import { ApplicationCredential } from '../../models/ApplicationCredential'; -import { SocketConnections } from '../../sockets/socket.connections'; +import { SocketConnection } from '../../sockets/socket.connections'; /** @@ -23,13 +23,13 @@ export class ApplicationCredentialApi extends BaseLoopBackApi { constructor( @Inject(Http) protected http: Http, - @Inject(SocketConnections) protected connections: SocketConnections, + @Inject(SocketConnection) protected connection: SocketConnection, @Inject(SDKModels) protected models: SDKModels, @Inject(LoopBackAuth) protected auth: LoopBackAuth, @Inject(JSONSearchParams) protected searchParams: JSONSearchParams, @Optional() @Inject(ErrorHandler) protected errorHandler: ErrorHandler ) { - super(http, connections, models, auth, searchParams, errorHandler); + super(http, connection, models, auth, searchParams, errorHandler); } /** diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/Category.ts b/tests/ng2web/src/app/shared/sdk/services/custom/Category.ts index 9355f31b..8b8f5030 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/Category.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/Category.ts @@ -12,7 +12,7 @@ import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import { Category } from '../../models/Category'; -import { SocketConnections } from '../../sockets/socket.connections'; +import { SocketConnection } from '../../sockets/socket.connections'; import { Room } from '../../models/Room'; @@ -24,13 +24,13 @@ export class CategoryApi extends BaseLoopBackApi { constructor( @Inject(Http) protected http: Http, - @Inject(SocketConnections) protected connections: SocketConnections, + @Inject(SocketConnection) protected connection: SocketConnection, @Inject(SDKModels) protected models: SDKModels, @Inject(LoopBackAuth) protected auth: LoopBackAuth, @Inject(JSONSearchParams) protected searchParams: JSONSearchParams, @Optional() @Inject(ErrorHandler) protected errorHandler: ErrorHandler ) { - super(http, connections, models, auth, searchParams, errorHandler); + super(http, connection, models, auth, searchParams, errorHandler); } /** @@ -412,7 +412,7 @@ export class CategoryApi extends BaseLoopBackApi { * This usually means the response is a `Category` object.) * */ - public createManyRooms(id: any, data: Array = []): Observable { + public createManyRooms(id: any, data: any[] = []): Observable { let _method: string = "POST"; let _url: string = LoopBackConfig.getPath() + "/" + LoopBackConfig.getApiVersion() + "/categories/:id/rooms"; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/Core.ts b/tests/ng2web/src/app/shared/sdk/services/custom/Core.ts index 3ddafc7c..40041099 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/Core.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/Core.ts @@ -12,7 +12,7 @@ import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import { Core } from '../../models/Core'; -import { SocketConnections } from '../../sockets/socket.connections'; +import { SocketConnection } from '../../sockets/socket.connections'; /** @@ -23,13 +23,13 @@ export class CoreApi extends BaseLoopBackApi { constructor( @Inject(Http) protected http: Http, - @Inject(SocketConnections) protected connections: SocketConnections, + @Inject(SocketConnection) protected connection: SocketConnection, @Inject(SDKModels) protected models: SDKModels, @Inject(LoopBackAuth) protected auth: LoopBackAuth, @Inject(JSONSearchParams) protected searchParams: JSONSearchParams, @Optional() @Inject(ErrorHandler) protected errorHandler: ErrorHandler ) { - super(http, connections, models, auth, searchParams, errorHandler); + super(http, connection, models, auth, searchParams, errorHandler); } /** diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/Like.ts b/tests/ng2web/src/app/shared/sdk/services/custom/Like.ts index f48cccdf..4395058d 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/Like.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/Like.ts @@ -12,7 +12,7 @@ import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import { Like } from '../../models/Like'; -import { SocketConnections } from '../../sockets/socket.connections'; +import { SocketConnection } from '../../sockets/socket.connections'; import { Message } from '../../models/Message'; import { Room } from '../../models/Room'; @@ -25,13 +25,13 @@ export class LikeApi extends BaseLoopBackApi { constructor( @Inject(Http) protected http: Http, - @Inject(SocketConnections) protected connections: SocketConnections, + @Inject(SocketConnection) protected connection: SocketConnection, @Inject(SDKModels) protected models: SDKModels, @Inject(LoopBackAuth) protected auth: LoopBackAuth, @Inject(JSONSearchParams) protected searchParams: JSONSearchParams, @Optional() @Inject(ErrorHandler) protected errorHandler: ErrorHandler ) { - super(http, connections, models, auth, searchParams, errorHandler); + super(http, connection, models, auth, searchParams, errorHandler); } /** diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/Message.ts b/tests/ng2web/src/app/shared/sdk/services/custom/Message.ts index d01e9fa4..c0a679e8 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/Message.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/Message.ts @@ -12,7 +12,7 @@ import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import { Message } from '../../models/Message'; -import { SocketConnections } from '../../sockets/socket.connections'; +import { SocketConnection } from '../../sockets/socket.connections'; import { Like } from '../../models/Like'; import { Room } from '../../models/Room'; @@ -25,13 +25,13 @@ export class MessageApi extends BaseLoopBackApi { constructor( @Inject(Http) protected http: Http, - @Inject(SocketConnections) protected connections: SocketConnections, + @Inject(SocketConnection) protected connection: SocketConnection, @Inject(SDKModels) protected models: SDKModels, @Inject(LoopBackAuth) protected auth: LoopBackAuth, @Inject(JSONSearchParams) protected searchParams: JSONSearchParams, @Optional() @Inject(ErrorHandler) protected errorHandler: ErrorHandler ) { - super(http, connections, models, auth, searchParams, errorHandler); + super(http, connection, models, auth, searchParams, errorHandler); } /** @@ -589,7 +589,7 @@ export class MessageApi extends BaseLoopBackApi { * This usually means the response is a `Message` object.) * */ - public createManyLikes(id: any, data: Array = []): Observable { + public createManyLikes(id: any, data: any[] = []): Observable { let _method: string = "POST"; let _url: string = LoopBackConfig.getPath() + "/" + LoopBackConfig.getApiVersion() + "/messages/:id/likes"; @@ -622,7 +622,7 @@ export class MessageApi extends BaseLoopBackApi { * This usually means the response is a `Message` object.) * */ - public createManyReplies(id: any, data: Array = []): Observable { + public createManyReplies(id: any, data: any[] = []): Observable { let _method: string = "POST"; let _url: string = LoopBackConfig.getPath() + "/" + LoopBackConfig.getApiVersion() + "/messages/:id/replies"; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/Room.ts b/tests/ng2web/src/app/shared/sdk/services/custom/Room.ts index cdc8b75f..462501fe 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/Room.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/Room.ts @@ -12,7 +12,7 @@ import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import { Room } from '../../models/Room'; -import { SocketConnections } from '../../sockets/socket.connections'; +import { SocketConnection } from '../../sockets/socket.connections'; import { Message } from '../../models/Message'; import { Like } from '../../models/Like'; import { Category } from '../../models/Category'; @@ -29,13 +29,13 @@ export class RoomApi extends BaseLoopBackApi { constructor( @Inject(Http) protected http: Http, - @Inject(SocketConnections) protected connections: SocketConnections, + @Inject(SocketConnection) protected connection: SocketConnection, @Inject(SDKModels) protected models: SDKModels, @Inject(LoopBackAuth) protected auth: LoopBackAuth, @Inject(JSONSearchParams) protected searchParams: JSONSearchParams, @Optional() @Inject(ErrorHandler) protected errorHandler: ErrorHandler ) { - super(http, connections, models, auth, searchParams, errorHandler); + super(http, connection, models, auth, searchParams, errorHandler); } /** @@ -1640,7 +1640,7 @@ export class RoomApi extends BaseLoopBackApi { * This usually means the response is a `Room` object.) * */ - public createManyMessages(id: any, data: Array = []): Observable { + public createManyMessages(id: any, data: any[] = []): Observable { let _method: string = "POST"; let _url: string = LoopBackConfig.getPath() + "/" + LoopBackConfig.getApiVersion() + "/rooms/:id/messages"; @@ -1673,7 +1673,7 @@ export class RoomApi extends BaseLoopBackApi { * This usually means the response is a `Room` object.) * */ - public createManyLikes(id: any, data: Array = []): Observable { + public createManyLikes(id: any, data: any[] = []): Observable { let _method: string = "POST"; let _url: string = LoopBackConfig.getPath() + "/" + LoopBackConfig.getApiVersion() + "/rooms/:id/likes"; @@ -1706,7 +1706,7 @@ export class RoomApi extends BaseLoopBackApi { * This usually means the response is a `Room` object.) * */ - public createManyCategories(id: any, data: Array = []): Observable { + public createManyCategories(id: any, data: any[] = []): Observable { let _method: string = "POST"; let _url: string = LoopBackConfig.getPath() + "/" + LoopBackConfig.getApiVersion() + "/rooms/:id/categories"; @@ -1739,7 +1739,7 @@ export class RoomApi extends BaseLoopBackApi { * This usually means the response is a `Room` object.) * */ - public createManyAccounts(id: any, data: Array = []): Observable { + public createManyAccounts(id: any, data: any[] = []): Observable { let _method: string = "POST"; let _url: string = LoopBackConfig.getPath() + "/" + LoopBackConfig.getApiVersion() + "/rooms/:id/accounts"; @@ -1772,7 +1772,7 @@ export class RoomApi extends BaseLoopBackApi { * This usually means the response is a `Room` object.) * */ - public createManyAdmins(id: any, data: Array = []): Observable { + public createManyAdmins(id: any, data: any[] = []): Observable { let _method: string = "POST"; let _url: string = LoopBackConfig.getPath() + "/" + LoopBackConfig.getApiVersion() + "/rooms/:id/admins"; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/RoomAccount.ts b/tests/ng2web/src/app/shared/sdk/services/custom/RoomAccount.ts index 99d4e17f..92825783 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/RoomAccount.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/RoomAccount.ts @@ -12,7 +12,7 @@ import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import { RoomAccount } from '../../models/RoomAccount'; -import { SocketConnections } from '../../sockets/socket.connections'; +import { SocketConnection } from '../../sockets/socket.connections'; import { Account } from '../../models/Account'; import { Room } from '../../models/Room'; @@ -25,13 +25,13 @@ export class RoomAccountApi extends BaseLoopBackApi { constructor( @Inject(Http) protected http: Http, - @Inject(SocketConnections) protected connections: SocketConnections, + @Inject(SocketConnection) protected connection: SocketConnection, @Inject(SDKModels) protected models: SDKModels, @Inject(LoopBackAuth) protected auth: LoopBackAuth, @Inject(JSONSearchParams) protected searchParams: JSONSearchParams, @Optional() @Inject(ErrorHandler) protected errorHandler: ErrorHandler ) { - super(http, connections, models, auth, searchParams, errorHandler); + super(http, connection, models, auth, searchParams, errorHandler); } /** diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/RoomAdmin.ts b/tests/ng2web/src/app/shared/sdk/services/custom/RoomAdmin.ts index 87ef873f..d78218b9 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/RoomAdmin.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/RoomAdmin.ts @@ -12,7 +12,7 @@ import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import { RoomAdmin } from '../../models/RoomAdmin'; -import { SocketConnections } from '../../sockets/socket.connections'; +import { SocketConnection } from '../../sockets/socket.connections'; import { Account } from '../../models/Account'; import { Room } from '../../models/Room'; @@ -25,13 +25,13 @@ export class RoomAdminApi extends BaseLoopBackApi { constructor( @Inject(Http) protected http: Http, - @Inject(SocketConnections) protected connections: SocketConnections, + @Inject(SocketConnection) protected connection: SocketConnection, @Inject(SDKModels) protected models: SDKModels, @Inject(LoopBackAuth) protected auth: LoopBackAuth, @Inject(JSONSearchParams) protected searchParams: JSONSearchParams, @Optional() @Inject(ErrorHandler) protected errorHandler: ErrorHandler ) { - super(http, connections, models, auth, searchParams, errorHandler); + super(http, connection, models, auth, searchParams, errorHandler); } /** diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/Storage.ts b/tests/ng2web/src/app/shared/sdk/services/custom/Storage.ts index c4106d02..0cc71794 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/Storage.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/Storage.ts @@ -12,7 +12,7 @@ import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import { Storage } from '../../models/Storage'; -import { SocketConnections } from '../../sockets/socket.connections'; +import { SocketConnection } from '../../sockets/socket.connections'; /** @@ -23,13 +23,13 @@ export class StorageApi extends BaseLoopBackApi { constructor( @Inject(Http) protected http: Http, - @Inject(SocketConnections) protected connections: SocketConnections, + @Inject(SocketConnection) protected connection: SocketConnection, @Inject(SDKModels) protected models: SDKModels, @Inject(LoopBackAuth) protected auth: LoopBackAuth, @Inject(JSONSearchParams) protected searchParams: JSONSearchParams, @Optional() @Inject(ErrorHandler) protected errorHandler: ErrorHandler ) { - super(http, connections, models, auth, searchParams, errorHandler); + super(http, connection, models, auth, searchParams, errorHandler); } /** diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/User.ts b/tests/ng2web/src/app/shared/sdk/services/custom/User.ts index 49b23bcd..540126f5 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/User.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/User.ts @@ -12,7 +12,7 @@ import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import { User } from '../../models/User'; -import { SocketConnections } from '../../sockets/socket.connections'; +import { SocketConnection } from '../../sockets/socket.connections'; /** @@ -23,13 +23,13 @@ export class UserApi extends BaseLoopBackApi { constructor( @Inject(Http) protected http: Http, - @Inject(SocketConnections) protected connections: SocketConnections, + @Inject(SocketConnection) protected connection: SocketConnection, @Inject(SDKModels) protected models: SDKModels, @Inject(LoopBackAuth) protected auth: LoopBackAuth, @Inject(JSONSearchParams) protected searchParams: JSONSearchParams, @Optional() @Inject(ErrorHandler) protected errorHandler: ErrorHandler ) { - super(http, connections, models, auth, searchParams, errorHandler); + super(http, connection, models, auth, searchParams, errorHandler); } /** @@ -389,7 +389,7 @@ export class UserApi extends BaseLoopBackApi { * This usually means the response is a `User` object.) * */ - public createManyAccessTokens(id: any, data: Array = []): Observable { + public createManyAccessTokens(id: any, data: any[] = []): Observable { let _method: string = "POST"; let _url: string = LoopBackConfig.getPath() + "/" + LoopBackConfig.getApiVersion() + "/Users/:id/accessTokens"; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/UserCredential.ts b/tests/ng2web/src/app/shared/sdk/services/custom/UserCredential.ts index 16d7df69..277371ad 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/UserCredential.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/UserCredential.ts @@ -12,7 +12,7 @@ import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import { UserCredential } from '../../models/UserCredential'; -import { SocketConnections } from '../../sockets/socket.connections'; +import { SocketConnection } from '../../sockets/socket.connections'; /** @@ -23,13 +23,13 @@ export class UserCredentialApi extends BaseLoopBackApi { constructor( @Inject(Http) protected http: Http, - @Inject(SocketConnections) protected connections: SocketConnections, + @Inject(SocketConnection) protected connection: SocketConnection, @Inject(SDKModels) protected models: SDKModels, @Inject(LoopBackAuth) protected auth: LoopBackAuth, @Inject(JSONSearchParams) protected searchParams: JSONSearchParams, @Optional() @Inject(ErrorHandler) protected errorHandler: ErrorHandler ) { - super(http, connections, models, auth, searchParams, errorHandler); + super(http, connection, models, auth, searchParams, errorHandler); } /** diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/UserIdentity.ts b/tests/ng2web/src/app/shared/sdk/services/custom/UserIdentity.ts index c8a28579..2b8c3e81 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/UserIdentity.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/UserIdentity.ts @@ -12,7 +12,7 @@ import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import { UserIdentity } from '../../models/UserIdentity'; -import { SocketConnections } from '../../sockets/socket.connections'; +import { SocketConnection } from '../../sockets/socket.connections'; import { User } from '../../models/User'; @@ -24,13 +24,13 @@ export class UserIdentityApi extends BaseLoopBackApi { constructor( @Inject(Http) protected http: Http, - @Inject(SocketConnections) protected connections: SocketConnections, + @Inject(SocketConnection) protected connection: SocketConnection, @Inject(SDKModels) protected models: SDKModels, @Inject(LoopBackAuth) protected auth: LoopBackAuth, @Inject(JSONSearchParams) protected searchParams: JSONSearchParams, @Optional() @Inject(ErrorHandler) protected errorHandler: ErrorHandler ) { - super(http, connections, models, auth, searchParams, errorHandler); + super(http, connection, models, auth, searchParams, errorHandler); } /** diff --git a/tests/ng2web/src/app/shared/sdk/sockets/socket.connections.ts b/tests/ng2web/src/app/shared/sdk/sockets/socket.connections.ts index 24163ec4..b41e7b3c 100644 --- a/tests/ng2web/src/app/shared/sdk/sockets/socket.connections.ts +++ b/tests/ng2web/src/app/shared/sdk/sockets/socket.connections.ts @@ -2,9 +2,12 @@ import { Injectable, Inject, NgZone } from '@angular/core'; import { SocketDriver } from './socket.driver'; import { AccessToken } from '../models'; +import { Subject } from 'rxjs/Subject'; +import { Observable } from 'rxjs/Observable'; +import { LoopBackConfig } from '../lb.config'; /** * @author Jonathan Casarrubias -* @module SocketConnections +* @module SocketConnection * @license MIT * @description * This module handle socket connections and return singleton instances for each @@ -12,75 +15,143 @@ import { AccessToken } from '../models'; * Angular 2 for web and NativeScript 2. **/ @Injectable() -export class SocketConnections { - private connections: any = {}; - private configured: boolean = false; +export class SocketConnection { + private socket: any; + private subjects: { + onConnect: Subject<{}>, + onDisconnect: Subject<{}>, + onAuthenticated: Subject<{}>, + onUnAuthorized: Subject<{}> + } = { + onConnect: new Subject(), + onDisconnect: new Subject(), + onAuthenticated: new Subject(), + onUnAuthorized: new Subject() + }; + public sharedObservables: { + sharedOnConnect?: Observable<{}>, + sharedOnDisconnect?: Observable<{}>, + sharedOnAuthenticated?: Observable<{}>, + sharedOnUnAuthorized?: Observable<{}> + } = {}; + private unauthenticated: boolean = true; + /** + * @method constructor + * @param driver + * @param zone + **/ constructor( @Inject(SocketDriver) private driver: SocketDriver, @Inject(NgZone) private zone: NgZone - ) { } - getHandler(url: string, token: AccessToken) { - console.log('Getting handler for socket connections'); - if (!this.connections[url]) { - console.log('Creating a new connection with: ', url); - let config: any = { log: false, secure: false, forceNew: true, forceWebsockets: true }; - this.connections[url] = this.driver.connect(url, config); - this.connections[url].onZone = ((event: string, handler: Function) => { - this.connections[url].on(event, (data: any) => { - this.zone.run(() => handler(data)); - }); + ) { + this.sharedObservables = { + sharedOnConnect: this.subjects.onConnect.asObservable().share(), + sharedOnDisconnect: this.subjects.onDisconnect.asObservable().share(), + sharedOnAuthenticated: this.subjects.onAuthenticated.asObservable().share(), + sharedOnUnAuthorized: this.subjects.onUnAuthorized.asObservable().share() + }; + // This is needed to create the first channel, subsequents will share the connection + // We are using Hot Observables to avoid duplicating connection status events. + this.sharedObservables.sharedOnConnect.subscribe(); + this.sharedObservables.sharedOnDisconnect.subscribe(); + this.sharedObservables.sharedOnAuthenticated.subscribe(); + this.sharedObservables.sharedOnUnAuthorized.subscribe(); + } + /** + * @method connect + * @param url string + * @param token AccessToken + * @description + * This method will return a socket socket connection + **/ + public connect(token: AccessToken = null): void { + if (!this.socket) { + console.info('Creating a new connection with: ', LoopBackConfig.getPath()); + // Create new socket connection + this.socket = this.driver.connect(LoopBackConfig.getPath(), { + log: false, + secure: false, + forceNew: true, + forceWebsockets: true }); - this.connections[url].on('connect', () => { - if (!this.configured) - this.setupConnection(url, token, config); + // Listen for connection + this.on('connect', () => { + this.subjects.onConnect.next('connected'); + // Authenticate or start heartbeat now + this.emit('authentication', token); }); - let forceConfig: any = setInterval(() => { - if (!this.configured && this.connections[url] && this.connections[url].connected) { - console.info('Forcing IO Configuration'); - this.setupConnection(url, token, config); - clearInterval(forceConfig); - } else if (this.configured) { - clearInterval(forceConfig); - } - }, 1000) - } else { - console.log('Reusing existing connection: ', url); + // Listen for authentication + this.on('authenticated', () => { + this.subjects.onAuthenticated.next(); + this.heartbeater(); + }) + // Listen for authentication + this.on('unauthorized', (err: any) => { + this.subjects.onUnAuthorized.next(err); + }) + // Listen for disconnections + this.on('disconnect', (status: any) => this.subjects.onDisconnect.next(status)); + } else if (this.socket && !this.socket.connected){ + this.socket.off(); + this.socket.destroy(); + delete this.socket; + this.connect(token); } - return this.connections[url]; } - - public disconnect() { - Object.keys(this.connections).forEach((connKey) => { - if (this.connections[connKey] && this.connections[connKey].connected) { - this.connections[connKey].disconnect(); - } - if (this.connections[connKey].off) { - this.connections[connKey].off(); - } - }); - this.connections = {}; - this.configured = false; + /** + * @method isConnected + * @description + * This method will return true or false depending on established connections + **/ + public isConnected(): boolean { + return (this.socket && this.socket.connected); } - - private setupConnection(url: string, token: AccessToken, config: any): void { - this.configured = true; - console.log('Connected to %s', url); - if (token.id) { - console.log('Emitting authentication', token.id); - this.connections[url].emit('authentication', token); - } - this.connections[url].on('unauthorized', (res: any) => console.error('Unauthenticated', res)); + /** + * @method on + * @description + * This method will wrap the original "on" method and run it within the Angular Zone + **/ + public on(event: string, handler: Function): void { + this.socket.on(event, (data: any) => this.zone.run(() => handler(data))); + } + /** + * @method emit + * @description + * This method will wrap the original "on" method and run it within the Angular Zone + **/ + public emit(event: string, data: any): void { + this.socket.emit(event, data); + } + /** + * @method removeListener + * @description + * This method will wrap the original "on" method and run it within the Angular Zone + **/ + public removeListener(event: string, handler: Function): void { + this.socket.removeListener(event, handler); + } + /** + * @method disconnect + * @description + * This will disconnect the client from the server + **/ + public disconnect(): void { + this.socket.disconnect(); + } + /** + * @method heartbeater + * @description + * This will keep the connection as active, even when users are not sending + * data, this avoids disconnection because of a connection not being used. + **/ + private heartbeater(): void { let heartbeater: any = setInterval(() => { - if (this.connections[url]) { - this.connections[url].emit('lb-ping'); + if (this.socket && this.socket.connected) { + this.socket.emit('lb-ping'); } else { - clearInterval(heartbeater); + clearInterval(heartbeater); } }, 15000); - this.connections[url].on('lb-pong', (data: any) => console.info('Heartbeat: ', data)); - this.connections[url].on('disconnect', (data: any) => { - this.disconnect(); - console.info('Disconnected from WebSocket server'); - }); + this.socket.on('lb-pong', (data: any) => console.info('Heartbeat: ', data)); } } From 2884868a7dd31c13d0125ccc19ae79c05c02a827 Mon Sep 17 00:00:00 2001 From: Jonathan Casarrubias Date: Fri, 6 Jan 2017 14:59:26 -0600 Subject: [PATCH 6/7] Fix CI Issue --- lib/angular2/shared/services/core/base.ejs | 28 ++++--------------- tests/fireloop/package.json | 3 +- .../shared/sdk/services/core/base.service.ts | 5 ++-- .../app/shared/sdk/services/custom/Account.ts | 2 +- .../services/custom/ApplicationCredential.ts | 2 +- .../shared/sdk/services/custom/Category.ts | 2 +- .../app/shared/sdk/services/custom/Core.ts | 2 +- .../app/shared/sdk/services/custom/Like.ts | 2 +- .../app/shared/sdk/services/custom/Message.ts | 2 +- .../app/shared/sdk/services/custom/Room.ts | 2 +- .../shared/sdk/services/custom/RoomAccount.ts | 2 +- .../shared/sdk/services/custom/RoomAdmin.ts | 2 +- .../app/shared/sdk/services/custom/Storage.ts | 2 +- .../app/shared/sdk/services/custom/User.ts | 2 +- .../sdk/services/custom/UserCredential.ts | 2 +- .../sdk/services/custom/UserIdentity.ts | 2 +- 16 files changed, 22 insertions(+), 40 deletions(-) diff --git a/lib/angular2/shared/services/core/base.ejs b/lib/angular2/shared/services/core/base.ejs index b95c9a05..7a6b5e16 100644 --- a/lib/angular2/shared/services/core/base.ejs +++ b/lib/angular2/shared/services/core/base.ejs @@ -1,19 +1,5 @@ /* tslint:disable */ -import { Injectable, Inject, Optional } from '@angular/core'; -import { Http, Headers, Request } from '@angular/http'; -import { NgModule, ModuleWithProviders } from '@angular/core'; -import { JSONSearchParams } from './search.params'; -import { ErrorHandler } from './error.service'; -import { LoopBackAuth } from './auth.service'; -import { LoopBackConfig } from '../../lb.config'; -import { LoopBackFilter, AccessToken } from '../../models/BaseModels'; -import { SDKModels } from '../custom/SDKModels'; -import { Observable } from 'rxjs/Observable'; -import { Subject } from 'rxjs/Subject'; -import { ErrorObservable } from 'rxjs/observable/ErrorObservable'; -import 'rxjs/add/operator/catch'; -import 'rxjs/add/operator/map'; -import { SocketConnection } from '../../sockets/socket.connections'; +<%- buildBaseServiceImports(isIo) %> // Making Sure EventSource Type is available to avoid compilation issues. declare var EventSource: any; /** @@ -34,12 +20,7 @@ export abstract class BaseLoopBackApi { protected model: any; constructor( - @Inject(Http) protected http: Http, - @Inject(SocketConnection) protected connection: SocketConnection, - @Inject(SDKModels) protected models: SDKModels, - @Inject(LoopBackAuth) protected auth: LoopBackAuth, - @Inject(JSONSearchParams) protected searchParams: JSONSearchParams, - @Optional() @Inject(ErrorHandler) protected errorHandler: ErrorHandler + <%- buildServiceDI(isIo) %> ) { this.model = this.models.get(this.getModelName()); } @@ -80,7 +61,7 @@ export abstract class BaseLoopBackApi { routeParams[key] + "$1" ); } - +<% if (isIo === 'enabled') { %> if (isio) { if (requestUrl.match(/fk/)) { let arr = requestUrl.split('/'); arr.pop(); @@ -95,7 +76,8 @@ export abstract class BaseLoopBackApi { this.connection.on(event, (res: any) => subject.next(res)); return subject.asObservable(); } - +<% } %> + // Body fix for built in remote methods using "data", "options" or "credentials // that are the actual body, Custom remote method properties are different and need // to be wrapped into a body object diff --git a/tests/fireloop/package.json b/tests/fireloop/package.json index 8f577fa0..edf6c320 100644 --- a/tests/fireloop/package.json +++ b/tests/fireloop/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@mean-expert/boot-script": "^1.0.0", - "@mean-expert/loopback-component-realtime": "file:///Volumes/HD710M/development/www/mean.expert/@mean-expert/loopback-component-realtime", + "@mean-expert/loopback-component-realtime": "^1.0.0-rc.3", "@mean-expert/loopback-stats-mixin": "^1.2.0", "@mean-expert/model": "^1.0.9", "chai": "^3.5.0", @@ -34,7 +34,6 @@ "supertest": "^2.0.1" }, "devDependencies": { - "@mean-expert/loopback-sdk-builder": "^2.1.0-rc.6", "@types/mocha": "^2.2.34", "@types/node": "^6.0.52", "eslint": "^2.13.1", diff --git a/tests/ng2web/src/app/shared/sdk/services/core/base.service.ts b/tests/ng2web/src/app/shared/sdk/services/core/base.service.ts index b95c9a05..6c86d1b4 100644 --- a/tests/ng2web/src/app/shared/sdk/services/core/base.service.ts +++ b/tests/ng2web/src/app/shared/sdk/services/core/base.service.ts @@ -34,7 +34,7 @@ export abstract class BaseLoopBackApi { protected model: any; constructor( - @Inject(Http) protected http: Http, + @Inject(Http) protected http: Http, @Inject(SocketConnection) protected connection: SocketConnection, @Inject(SDKModels) protected models: SDKModels, @Inject(LoopBackAuth) protected auth: LoopBackAuth, @@ -95,7 +95,8 @@ export abstract class BaseLoopBackApi { this.connection.on(event, (res: any) => subject.next(res)); return subject.asObservable(); } - + + // Body fix for built in remote methods using "data", "options" or "credentials // that are the actual body, Custom remote method properties are different and need // to be wrapped into a body object diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/Account.ts b/tests/ng2web/src/app/shared/sdk/services/custom/Account.ts index f759b015..401c715e 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/Account.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/Account.ts @@ -9,7 +9,7 @@ import { LoopBackFilter, SDKToken, AccessToken } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/rx'; +import { Observable } from 'rxjs/Rx'; import { Account } from '../../models/Account'; import { SocketConnection } from '../../sockets/socket.connections'; import { RoomAccount } from '../../models/RoomAccount'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/ApplicationCredential.ts b/tests/ng2web/src/app/shared/sdk/services/custom/ApplicationCredential.ts index 8c27a465..9f2b17d2 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/ApplicationCredential.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/ApplicationCredential.ts @@ -9,7 +9,7 @@ import { LoopBackFilter, } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/rx'; +import { Observable } from 'rxjs/Rx'; import { ApplicationCredential } from '../../models/ApplicationCredential'; import { SocketConnection } from '../../sockets/socket.connections'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/Category.ts b/tests/ng2web/src/app/shared/sdk/services/custom/Category.ts index 6b419364..35d04837 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/Category.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/Category.ts @@ -9,7 +9,7 @@ import { LoopBackFilter, } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/rx'; +import { Observable } from 'rxjs/Rx'; import { Category } from '../../models/Category'; import { SocketConnection } from '../../sockets/socket.connections'; import { Room } from '../../models/Room'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/Core.ts b/tests/ng2web/src/app/shared/sdk/services/custom/Core.ts index b5a88fda..5e6748f7 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/Core.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/Core.ts @@ -9,7 +9,7 @@ import { LoopBackFilter, } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/rx'; +import { Observable } from 'rxjs/Rx'; import { Core } from '../../models/Core'; import { SocketConnection } from '../../sockets/socket.connections'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/Like.ts b/tests/ng2web/src/app/shared/sdk/services/custom/Like.ts index c7c3cecb..7272688b 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/Like.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/Like.ts @@ -9,7 +9,7 @@ import { LoopBackFilter, } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/rx'; +import { Observable } from 'rxjs/Rx'; import { Like } from '../../models/Like'; import { SocketConnection } from '../../sockets/socket.connections'; import { Message } from '../../models/Message'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/Message.ts b/tests/ng2web/src/app/shared/sdk/services/custom/Message.ts index 631076a8..6133a137 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/Message.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/Message.ts @@ -9,7 +9,7 @@ import { LoopBackFilter, } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/rx'; +import { Observable } from 'rxjs/Rx'; import { Message } from '../../models/Message'; import { SocketConnection } from '../../sockets/socket.connections'; import { Like } from '../../models/Like'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/Room.ts b/tests/ng2web/src/app/shared/sdk/services/custom/Room.ts index d4f6aac1..6f3c9510 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/Room.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/Room.ts @@ -9,7 +9,7 @@ import { LoopBackFilter, } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/rx'; +import { Observable } from 'rxjs/Rx'; import { Room } from '../../models/Room'; import { SocketConnection } from '../../sockets/socket.connections'; import { Message } from '../../models/Message'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/RoomAccount.ts b/tests/ng2web/src/app/shared/sdk/services/custom/RoomAccount.ts index da5409a1..55c2a89d 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/RoomAccount.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/RoomAccount.ts @@ -9,7 +9,7 @@ import { LoopBackFilter, } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/rx'; +import { Observable } from 'rxjs/Rx'; import { RoomAccount } from '../../models/RoomAccount'; import { SocketConnection } from '../../sockets/socket.connections'; import { Account } from '../../models/Account'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/RoomAdmin.ts b/tests/ng2web/src/app/shared/sdk/services/custom/RoomAdmin.ts index 931c1359..07c47d16 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/RoomAdmin.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/RoomAdmin.ts @@ -9,7 +9,7 @@ import { LoopBackFilter, } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/rx'; +import { Observable } from 'rxjs/Rx'; import { RoomAdmin } from '../../models/RoomAdmin'; import { SocketConnection } from '../../sockets/socket.connections'; import { Account } from '../../models/Account'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/Storage.ts b/tests/ng2web/src/app/shared/sdk/services/custom/Storage.ts index 43e7ba21..8cbfc25d 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/Storage.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/Storage.ts @@ -9,7 +9,7 @@ import { LoopBackFilter, } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/rx'; +import { Observable } from 'rxjs/Rx'; import { Storage } from '../../models/Storage'; import { SocketConnection } from '../../sockets/socket.connections'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/User.ts b/tests/ng2web/src/app/shared/sdk/services/custom/User.ts index eaa4ae0c..914b90aa 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/User.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/User.ts @@ -9,7 +9,7 @@ import { LoopBackFilter, SDKToken, AccessToken } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/rx'; +import { Observable } from 'rxjs/Rx'; import { User } from '../../models/User'; import { SocketConnection } from '../../sockets/socket.connections'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/UserCredential.ts b/tests/ng2web/src/app/shared/sdk/services/custom/UserCredential.ts index 48ad337e..4226efaf 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/UserCredential.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/UserCredential.ts @@ -9,7 +9,7 @@ import { LoopBackFilter, } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/rx'; +import { Observable } from 'rxjs/Rx'; import { UserCredential } from '../../models/UserCredential'; import { SocketConnection } from '../../sockets/socket.connections'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/UserIdentity.ts b/tests/ng2web/src/app/shared/sdk/services/custom/UserIdentity.ts index 490d8568..7a71957c 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/UserIdentity.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/UserIdentity.ts @@ -9,7 +9,7 @@ import { LoopBackFilter, } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; -import { Observable } from 'rxjs/rx'; +import { Observable } from 'rxjs/Rx'; import { UserIdentity } from '../../models/UserIdentity'; import { SocketConnection } from '../../sockets/socket.connections'; import { User } from '../../models/User'; From eeb3b780401d5bb0916d3fe196bf4d27ddbe310c Mon Sep 17 00:00:00 2001 From: Jonathan Casarrubias Date: Fri, 6 Jan 2017 17:11:44 -0600 Subject: [PATCH 7/7] Release 2.1.0-rc.8 : rocket: - Milestone Details: https://github.com/mean-expert-official/loopback-sdk-builder/milestone/30?closed=1 - Fix: https://github.com/mean-expert-official/loopback-sdk-builder/issues/297 - Fix: https://github.com/mean-expert-official/loopback-sdk-builder/issues/296 - Fix: https://github.com/mean-expert-official/loopback-sdk-builder/issues/295 - Fix: https://github.com/mean-expert-official/loopback-sdk-builder/issues/292 - Fix: https://github.com/mean-expert-official/loopback-sdk-builder/issues/290 - Fix: https://github.com/mean-expert-official/loopback-sdk-builder/issues/288 - Fix: https://github.com/mean-expert-official/loopback-sdk-builder/issues/285 - Fix: https://github.com/mean-expert-official/loopback-sdk-builder/issues/284 --- CHANGELOG.md | 13 +++ lib/angular2/index.js | 3 - lib/angular2/shared/models/base.ejs | 87 +++++++++++++++---- lib/angular2/shared/services/core/base.ejs | 69 ++++++--------- .../shared/services/custom/models.ejs | 12 ++- package.json | 2 +- tests/fireloop/server/model-config.json | 2 +- tests/ng2web/src/app/shared/sdk/index.ts | 2 + .../src/app/shared/sdk/models/AccessToken.ts | 80 +++++++++++++++++ .../src/app/shared/sdk/models/Account.ts | 9 +- .../src/app/shared/sdk/models/BaseModels.ts | 35 ++------ .../ng2web/src/app/shared/sdk/models/User.ts | 11 ++- .../ng2web/src/app/shared/sdk/models/index.ts | 1 + .../shared/sdk/services/core/base.service.ts | 71 ++++++--------- .../shared/sdk/services/custom/AccessToken.ts | 72 +++++++++++++++ .../app/shared/sdk/services/custom/Account.ts | 3 +- .../shared/sdk/services/custom/SDKModels.ts | 14 ++- .../app/shared/sdk/services/custom/User.ts | 3 +- .../app/shared/sdk/services/custom/index.ts | 1 + 19 files changed, 345 insertions(+), 145 deletions(-) create mode 100644 tests/ng2web/src/app/shared/sdk/models/AccessToken.ts create mode 100644 tests/ng2web/src/app/shared/sdk/services/custom/AccessToken.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index dbbf36dc..f359ab0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ This file is created to keep history of the LoopBack SDK Builder, it does not consider or keeps any history of its parent module `loopback-sdk-angular`. +## Release 2.1.0-rc.8 + +- Milestone Details: https://github.com/mean-expert-official/loopback-sdk-builder/milestone/30?closed=1 + +- Fix: https://github.com/mean-expert-official/loopback-sdk-builder/issues/297 +- Fix: https://github.com/mean-expert-official/loopback-sdk-builder/issues/296 +- Fix: https://github.com/mean-expert-official/loopback-sdk-builder/issues/295 +- Fix: https://github.com/mean-expert-official/loopback-sdk-builder/issues/292 +- Fix: https://github.com/mean-expert-official/loopback-sdk-builder/issues/290 +- Fix: https://github.com/mean-expert-official/loopback-sdk-builder/issues/288 +- Fix: https://github.com/mean-expert-official/loopback-sdk-builder/issues/285 +- Fix: https://github.com/mean-expert-official/loopback-sdk-builder/issues/284 + ## Release 2.1.0-rc.7 - Milestone Details: https://github.com/mean-expert-official/loopback-sdk-builder/milestone/29?closed=1 diff --git a/lib/angular2/index.js b/lib/angular2/index.js index fcdf52de..3a367419 100644 --- a/lib/angular2/index.js +++ b/lib/angular2/index.js @@ -668,7 +668,6 @@ module.exports = function generate(ctx) { } ); if (isIo) params = params.filter(param => !param.arg.match(/(fk|data|options)/)); - console.log('CLASSES: ', availableClasses); params.forEach((param, i, arr) => { let type, isArray = false; if (param.type === 'object') { @@ -677,7 +676,6 @@ module.exports = function generate(ctx) { type = param.type !== 'AccessToken' && param.type !== 'any' ? capitalize(param.type) : 'any'; } - console.log('THE ACTUAL TYPE: ', type); if (!type.match(/(^any$|LoopBackFilter)/) && availableClasses.indexOf(type) < 0) { type = 'any'; } @@ -686,7 +684,6 @@ module.exports = function generate(ctx) { if (methodName.match(/createMany/) && param.arg === 'data') { isArray = true; } - console.log('RESULT TYPE: ', type); // Set default value, usually will be {}, but on login we include user // Should not be undefined or will create request issues if (!param.required && methodName === 'login' && param.arg === 'include') { diff --git a/lib/angular2/shared/models/base.ejs b/lib/angular2/shared/models/base.ejs index c449db69..5f41d503 100644 --- a/lib/angular2/shared/models/base.ejs +++ b/lib/angular2/shared/models/base.ejs @@ -1,6 +1,7 @@ /* tslint:disable */ <% if (!loadAccessToken) { %> -import { AccessToken } from './AccessToken'; +import { AccessToken, AccessTokenInterface } from './AccessToken'; +export * from './AccessToken'; <% } %> declare var Object: any; export interface LoopBackFilter { @@ -17,34 +18,88 @@ export interface AccessTokenInterface { id?: string; ttl?: number; issuedAt?: any; - created?: any; + created: any; userId?: string; rememberMe?: boolean; } export class AccessToken implements AccessTokenInterface { - id:string; - ttl: number; - issuedAt?: any; - created?: any; - userId: string; - user: any; - rememberMe: boolean = null; - constructor(instance?: AccessToken) { - Object.assign(this, instance); + id: string = ''; + ttl: number = 1209600; + created: Date = new Date(0); + userId: number = 0; + user: User = null; + constructor(data?: AccessTokenInterface) { + Object.assign(this, data); + } + /** + * The name of the model represented by this $resource, + * i.e. `AccessToken`. + */ + public static getModelName() { + return "AccessToken"; + } + /** + * @method factory + * @author Jonathan Casarrubias + * @license MIT + * This method creates an instance of AccessToken for dynamic purposes. + **/ + public static factory(data: AccessTokenInterface): AccessToken{ + return new AccessToken(data); + } + /** + * @method getModelDefinition + * @author Julien Ledun + * @license MIT + * This method returns an object that represents some of the model + * definitions. + **/ + public static getModelDefinition() { + return { + name: 'AccessToken', + plural: 'AccessTokens', + properties: { + id: { + name: 'id', + type: 'string' + }, + ttl: { + name: 'ttl', + type: 'number', + default: 1209600 + }, + created: { + name: 'created', + type: 'Date', + default: new Date(0) + }, + userId: { + name: 'userId', + type: 'number' + }, + }, + relations: { + user: { + name: 'user', + type: 'User', + model: 'User' + }, + } } + } } <% } %> -export class SDKToken extends AccessToken { +export class SDKToken implements AccessTokenInterface { id: any = null; ttl: number = null; - issuedAt?: any = null; - created?: any = null; + issuedAt: any = null; + created: any = null; userId: any = null; user: any = null; rememberMe: boolean = null; - constructor(instance?: AccessToken) { - super(instance); + constructor(data?: AccessTokenInterface) { + Object.assign(this, data); } } diff --git a/lib/angular2/shared/services/core/base.ejs b/lib/angular2/shared/services/core/base.ejs index 7a6b5e16..58c61852 100644 --- a/lib/angular2/shared/services/core/base.ejs +++ b/lib/angular2/shared/services/core/base.ejs @@ -24,7 +24,6 @@ export abstract class BaseLoopBackApi { ) { this.model = this.models.get(this.getModelName()); } - /** * Process request * @param string method Request method (GET, POST, PUT) @@ -39,66 +38,37 @@ export abstract class BaseLoopBackApi { url : string, routeParams : any = {}, urlParams : any = {}, - postBody : any = {}, - isio : boolean = false + postBody : any = {} ): Observable { - - let headers = new Headers(); + // Headers to be sent + let headers: Headers = new Headers(); headers.append('Content-Type', 'application/json'); - - if (this.auth.getAccessTokenId()) { - headers.append( - 'Authorization', - LoopBackConfig.getAuthPrefix() + this.auth.getAccessTokenId() - ); - } - - let requestUrl = url; - let key: string; - for (key in routeParams) { - requestUrl = requestUrl.replace( - new RegExp(":" + key + "(\/|$)", "g"), - routeParams[key] + "$1" - ); - } -<% if (isIo === 'enabled') { %> - if (isio) { - if (requestUrl.match(/fk/)) { - let arr = requestUrl.split('/'); arr.pop(); - requestUrl = arr.join('/'); - } - let event: string = (`[${method}]${requestUrl}`).replace(/\?/, ''); - let subject: Subject = new Subject(); - let token: AccessToken = new AccessToken(); - token.id = this.auth.getAccessTokenId(); - token.userId = this.auth.getCurrentUserId(); - this.connection.connect(token); - this.connection.on(event, (res: any) => subject.next(res)); - return subject.asObservable(); - } -<% } %> - + // Authenticate request + this.authenticate(url, headers); + // Transpile route variables to the actual request Values + Object.keys(routeParams).forEach((key: string) => { + url = url.replace(new RegExp(":" + key + "(\/|$)", "g"), routeParams[key] + "$1") + }); // Body fix for built in remote methods using "data", "options" or "credentials // that are the actual body, Custom remote method properties are different and need // to be wrapped into a body object let body: any; let postBodyKeys = typeof postBody === 'object' ? Object.keys(postBody) : [] if (postBodyKeys.length === 1) { - body = postBody[postBodyKeys[0]] + body = postBody[postBodyKeys[0]]; } else { body = postBody; } - // Separate filter object from url params + // Separate filter object from url params and add to search query if (urlParams.filter) { headers.append('filter', JSON.stringify(urlParams.filter)); delete urlParams.filter; } - this.searchParams.setJSON(urlParams); let request: Request = new Request({ headers : headers, method : method, - url : requestUrl, + url : url, search : Object.keys(urlParams).length > 0 ? this.searchParams.getURLSearchParams() : null, body : body ? JSON.stringify(body) : undefined @@ -107,6 +77,21 @@ export abstract class BaseLoopBackApi { .map((res: any) => (res.text() != "" ? res.json() : {})) .catch((e) => this.errorHandler.handleError(e)); } + /** + * @method authenticate + * @author Jonathan Casarrubias + * @license MIT + * @description + * This method will try to authenticate using either an access_token or basic http auth + */ + public authenticate(url: string, headers: Headers): void { + if (this.auth.getAccessTokenId()) { + headers.append( + 'Authorization', + LoopBackConfig.getAuthPrefix() + this.auth.getAccessTokenId() + ); + } + } /** * @method create * @author Jonathan Casarrubias diff --git a/lib/angular2/shared/services/custom/models.ejs b/lib/angular2/shared/services/custom/models.ejs index 6eb009c4..e8b7c599 100644 --- a/lib/angular2/shared/services/custom/models.ejs +++ b/lib/angular2/shared/services/custom/models.ejs @@ -8,10 +8,12 @@ import { Injectable } from '@angular/core'; import { <%- modelName %> } from '../../models/<%- modelName %>'; <% } // for modelName in models -%> +interface Models { [name: string]: any } + @Injectable() export class SDKModels { - private models: { [name: string]: any } = { + private models: Models = { <% for ( modelName in models ) { %><%= modelName %>: <%= modelName %>, <% } %> }; @@ -19,4 +21,12 @@ export class SDKModels { public get(modelName: string): any { return this.models[modelName]; } + + public getAll(): Models { + return this.models; + } + + public getModelNames(): string[] { + return Object.keys(this.models); + } } diff --git a/package.json b/package.json index 4f98d3bc..977395ab 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mean-expert/loopback-sdk-builder", - "version": "2.1.0-rc.7", + "version": "2.1.0-rc.8", "description": "Tool for auto-generating Software Development Kits (SDKs) for LoopBack", "bin": { "lb-sdk": "bin/lb-sdk" diff --git a/tests/fireloop/server/model-config.json b/tests/fireloop/server/model-config.json index ae1e053d..06cae616 100644 --- a/tests/fireloop/server/model-config.json +++ b/tests/fireloop/server/model-config.json @@ -21,7 +21,7 @@ }, "AccessToken": { "dataSource": "db", - "public": false + "public": true }, "ACL": { "dataSource": "db", diff --git a/tests/ng2web/src/app/shared/sdk/index.ts b/tests/ng2web/src/app/shared/sdk/index.ts index 22aa29f2..0bd444cc 100644 --- a/tests/ng2web/src/app/shared/sdk/index.ts +++ b/tests/ng2web/src/app/shared/sdk/index.ts @@ -49,6 +49,7 @@ import { SocketDriver } from './sockets/socket.driver'; import { SocketConnection } from './sockets/socket.connections'; import { RealTime } from './services/core/real.time'; import { UserApi } from './services/custom/User'; +import { AccessTokenApi } from './services/custom/AccessToken'; import { AccountApi } from './services/custom/Account'; import { ApplicationCredentialApi } from './services/custom/ApplicationCredential'; import { CategoryApi } from './services/custom/Category'; @@ -90,6 +91,7 @@ export class SDKBrowserModule { SDKModels, RealTime, UserApi, + AccessTokenApi, AccountApi, ApplicationCredentialApi, CategoryApi, diff --git a/tests/ng2web/src/app/shared/sdk/models/AccessToken.ts b/tests/ng2web/src/app/shared/sdk/models/AccessToken.ts new file mode 100644 index 00000000..e22d9305 --- /dev/null +++ b/tests/ng2web/src/app/shared/sdk/models/AccessToken.ts @@ -0,0 +1,80 @@ +/* tslint:disable */ +import { + User +} from '../index'; + +declare var Object: any; +export interface AccessTokenInterface { + id?: string; + ttl?: number; + created?: Date; + userId?: number; + user?: User; +} + +export class AccessToken implements AccessTokenInterface { + id: string = ''; + ttl: number = 1209600; + created: Date = new Date(0); + userId: number = 0; + user: User = null; + constructor(data?: AccessTokenInterface) { + Object.assign(this, data); + } + /** + * The name of the model represented by this $resource, + * i.e. `AccessToken`. + */ + public static getModelName() { + return "AccessToken"; + } + /** + * @method factory + * @author Jonathan Casarrubias + * @license MIT + * This method creates an instance of AccessToken for dynamic purposes. + **/ + public static factory(data: AccessTokenInterface): AccessToken{ + return new AccessToken(data); + } + /** + * @method getModelDefinition + * @author Julien Ledun + * @license MIT + * This method returns an object that represents some of the model + * definitions. + **/ + public static getModelDefinition() { + return { + name: 'AccessToken', + plural: 'AccessTokens', + properties: { + id: { + name: 'id', + type: 'string' + }, + ttl: { + name: 'ttl', + type: 'number', + default: 1209600 + }, + created: { + name: 'created', + type: 'Date', + default: new Date(0) + }, + userId: { + name: 'userId', + type: 'number' + }, + }, + relations: { + user: { + name: 'user', + type: 'User', + model: 'User' + }, + } + } + } +} diff --git a/tests/ng2web/src/app/shared/sdk/models/Account.ts b/tests/ng2web/src/app/shared/sdk/models/Account.ts index 55eaf6d7..da64b0b0 100644 --- a/tests/ng2web/src/app/shared/sdk/models/Account.ts +++ b/tests/ng2web/src/app/shared/sdk/models/Account.ts @@ -1,5 +1,6 @@ /* tslint:disable */ import { + AccessToken, Room } from '../index'; @@ -18,7 +19,7 @@ export interface AccountInterface { id?: number; createdAt: Date; updatedAt: Date; - accessTokens?: Array; + accessTokens?: Array; rooms?: Array; administrations?: Array; } @@ -37,7 +38,7 @@ export class Account implements AccountInterface { id: number = 0; createdAt: Date = new Date(0); updatedAt: Date = new Date(0); - accessTokens: Array = []; + accessTokens: Array = []; rooms: Array = []; administrations: Array = []; constructor(data?: AccountInterface) { @@ -131,8 +132,8 @@ export class Account implements AccountInterface { relations: { accessTokens: { name: 'accessTokens', - type: 'Array', - model: '' + type: 'Array', + model: 'AccessToken' }, rooms: { name: 'rooms', diff --git a/tests/ng2web/src/app/shared/sdk/models/BaseModels.ts b/tests/ng2web/src/app/shared/sdk/models/BaseModels.ts index 29605fe4..6ea39666 100644 --- a/tests/ng2web/src/app/shared/sdk/models/BaseModels.ts +++ b/tests/ng2web/src/app/shared/sdk/models/BaseModels.ts @@ -1,5 +1,8 @@ /* tslint:disable */ +import { AccessToken, AccessTokenInterface } from './AccessToken'; +export * from './AccessToken'; + declare var Object: any; export interface LoopBackFilter { fields?: any; @@ -11,38 +14,16 @@ export interface LoopBackFilter { where?: any; } -export interface AccessTokenInterface { - id?: string; - ttl?: number; - issuedAt?: any; - created?: any; - userId?: string; - rememberMe?: boolean; -} - -export class AccessToken implements AccessTokenInterface { - id:string; - ttl: number; - issuedAt?: any; - created?: any; - userId: string; - user: any; - rememberMe: boolean = null; - constructor(instance?: AccessToken) { - Object.assign(this, instance); - } -} - -export class SDKToken extends AccessToken { +export class SDKToken implements AccessTokenInterface { id: any = null; ttl: number = null; - issuedAt?: any = null; - created?: any = null; + issuedAt: any = null; + created: any = null; userId: any = null; user: any = null; rememberMe: boolean = null; - constructor(instance?: AccessToken) { - super(instance); + constructor(data?: AccessTokenInterface) { + Object.assign(this, data); } } diff --git a/tests/ng2web/src/app/shared/sdk/models/User.ts b/tests/ng2web/src/app/shared/sdk/models/User.ts index 2c8486b3..9817e0d2 100644 --- a/tests/ng2web/src/app/shared/sdk/models/User.ts +++ b/tests/ng2web/src/app/shared/sdk/models/User.ts @@ -1,4 +1,7 @@ /* tslint:disable */ +import { + AccessToken +} from '../index'; declare var Object: any; export interface UserInterface { @@ -13,7 +16,7 @@ export interface UserInterface { created?: Date; lastUpdated?: Date; id?: number; - accessTokens?: Array; + accessTokens?: Array; } export class User implements UserInterface { @@ -28,7 +31,7 @@ export class User implements UserInterface { created: Date = new Date(0); lastUpdated: Date = new Date(0); id: number = 0; - accessTokens: Array = []; + accessTokens: Array = []; constructor(data?: UserInterface) { Object.assign(this, data); } @@ -112,8 +115,8 @@ export class User implements UserInterface { relations: { accessTokens: { name: 'accessTokens', - type: 'Array', - model: '' + type: 'Array', + model: 'AccessToken' }, } } diff --git a/tests/ng2web/src/app/shared/sdk/models/index.ts b/tests/ng2web/src/app/shared/sdk/models/index.ts index 43cd38ff..0151a90d 100644 --- a/tests/ng2web/src/app/shared/sdk/models/index.ts +++ b/tests/ng2web/src/app/shared/sdk/models/index.ts @@ -1,5 +1,6 @@ /* tslint:disable */ export * from './User'; +export * from './AccessToken'; export * from './Account'; export * from './ApplicationCredential'; export * from './Category'; diff --git a/tests/ng2web/src/app/shared/sdk/services/core/base.service.ts b/tests/ng2web/src/app/shared/sdk/services/core/base.service.ts index 6c86d1b4..6ca686c1 100644 --- a/tests/ng2web/src/app/shared/sdk/services/core/base.service.ts +++ b/tests/ng2web/src/app/shared/sdk/services/core/base.service.ts @@ -34,7 +34,7 @@ export abstract class BaseLoopBackApi { protected model: any; constructor( - @Inject(Http) protected http: Http, + @Inject(Http) protected http: Http, @Inject(SocketConnection) protected connection: SocketConnection, @Inject(SDKModels) protected models: SDKModels, @Inject(LoopBackAuth) protected auth: LoopBackAuth, @@ -43,7 +43,6 @@ export abstract class BaseLoopBackApi { ) { this.model = this.models.get(this.getModelName()); } - /** * Process request * @param string method Request method (GET, POST, PUT) @@ -58,66 +57,37 @@ export abstract class BaseLoopBackApi { url : string, routeParams : any = {}, urlParams : any = {}, - postBody : any = {}, - isio : boolean = false + postBody : any = {} ): Observable { - - let headers = new Headers(); + // Headers to be sent + let headers: Headers = new Headers(); headers.append('Content-Type', 'application/json'); - - if (this.auth.getAccessTokenId()) { - headers.append( - 'Authorization', - LoopBackConfig.getAuthPrefix() + this.auth.getAccessTokenId() - ); - } - - let requestUrl = url; - let key: string; - for (key in routeParams) { - requestUrl = requestUrl.replace( - new RegExp(":" + key + "(\/|$)", "g"), - routeParams[key] + "$1" - ); - } - - if (isio) { - if (requestUrl.match(/fk/)) { - let arr = requestUrl.split('/'); arr.pop(); - requestUrl = arr.join('/'); - } - let event: string = (`[${method}]${requestUrl}`).replace(/\?/, ''); - let subject: Subject = new Subject(); - let token: AccessToken = new AccessToken(); - token.id = this.auth.getAccessTokenId(); - token.userId = this.auth.getCurrentUserId(); - this.connection.connect(token); - this.connection.on(event, (res: any) => subject.next(res)); - return subject.asObservable(); - } - - + // Authenticate request + this.authenticate(url, headers); + // Transpile route variables to the actual request Values + Object.keys(routeParams).forEach((key: string) => { + url = url.replace(new RegExp(":" + key + "(\/|$)", "g"), routeParams[key] + "$1") + }); // Body fix for built in remote methods using "data", "options" or "credentials // that are the actual body, Custom remote method properties are different and need // to be wrapped into a body object let body: any; let postBodyKeys = typeof postBody === 'object' ? Object.keys(postBody) : [] if (postBodyKeys.length === 1) { - body = postBody[postBodyKeys[0]] + body = postBody[postBodyKeys[0]]; } else { body = postBody; } - // Separate filter object from url params + // Separate filter object from url params and add to search query if (urlParams.filter) { headers.append('filter', JSON.stringify(urlParams.filter)); delete urlParams.filter; } - this.searchParams.setJSON(urlParams); let request: Request = new Request({ headers : headers, method : method, - url : requestUrl, + url : url, search : Object.keys(urlParams).length > 0 ? this.searchParams.getURLSearchParams() : null, body : body ? JSON.stringify(body) : undefined @@ -126,6 +96,21 @@ export abstract class BaseLoopBackApi { .map((res: any) => (res.text() != "" ? res.json() : {})) .catch((e) => this.errorHandler.handleError(e)); } + /** + * @method authenticate + * @author Jonathan Casarrubias + * @license MIT + * @description + * This method will try to authenticate using either an access_token or basic http auth + */ + public authenticate(url: string, headers: Headers): void { + if (this.auth.getAccessTokenId()) { + headers.append( + 'Authorization', + LoopBackConfig.getAuthPrefix() + this.auth.getAccessTokenId() + ); + } + } /** * @method create * @author Jonathan Casarrubias diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/AccessToken.ts b/tests/ng2web/src/app/shared/sdk/services/custom/AccessToken.ts new file mode 100644 index 00000000..cdf56af9 --- /dev/null +++ b/tests/ng2web/src/app/shared/sdk/services/custom/AccessToken.ts @@ -0,0 +1,72 @@ +/* tslint:disable */ +import { Injectable, Inject, Optional } from '@angular/core'; +import { Http, Response } from '@angular/http'; +import { SDKModels } from './SDKModels'; +import { BaseLoopBackApi } from '../core/base.service'; +import { LoopBackConfig } from '../../lb.config'; +import { LoopBackAuth } from '../core/auth.service'; +import { LoopBackFilter, } from '../../models/BaseModels'; +import { JSONSearchParams } from '../core/search.params'; +import { ErrorHandler } from '../core/error.service'; +import { Subject } from 'rxjs/Subject'; +import { Observable } from 'rxjs/Rx'; +import { AccessToken } from '../../models/AccessToken'; +import { SocketConnection } from '../../sockets/socket.connections'; +import { User } from '../../models/User'; + + +/** + * Api services for the `AccessToken` model. + */ +@Injectable() +export class AccessTokenApi extends BaseLoopBackApi { + + constructor( + @Inject(Http) protected http: Http, + @Inject(SocketConnection) protected connection: SocketConnection, + @Inject(SDKModels) protected models: SDKModels, + @Inject(LoopBackAuth) protected auth: LoopBackAuth, + @Inject(JSONSearchParams) protected searchParams: JSONSearchParams, + @Optional() @Inject(ErrorHandler) protected errorHandler: ErrorHandler + ) { + super(http, connection, models, auth, searchParams, errorHandler); + } + + /** + * Fetches belongsTo relation user. + * + * @param any id PersistedModel id + * + * @param boolean refresh + * + * @returns object An empty reference that will be + * populated with the actual data once the response is returned + * from the server. + * + * + * (The remote method definition does not provide any description. + * This usually means the response is a `AccessToken` object.) + * + */ + public getUser(id: any, refresh: any = {}): Observable { + let _method: string = "GET"; + let _url: string = LoopBackConfig.getPath() + "/" + LoopBackConfig.getApiVersion() + + "/AccessTokens/:id/user"; + let _routeParams: any = { + id: id + }; + let _postBody: any = {}; + let _urlParams: any = {}; + if (refresh) _urlParams.refresh = refresh; + let result = this.request(_method, _url, _routeParams, _urlParams, _postBody); + return result; + } + + /** + * The name of the model represented by this $resource, + * i.e. `AccessToken`. + */ + public getModelName() { + return "AccessToken"; + } +} diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/Account.ts b/tests/ng2web/src/app/shared/sdk/services/custom/Account.ts index 401c715e..1ddad85d 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/Account.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/Account.ts @@ -5,13 +5,14 @@ import { SDKModels } from './SDKModels'; import { BaseLoopBackApi } from '../core/base.service'; import { LoopBackConfig } from '../../lb.config'; import { LoopBackAuth } from '../core/auth.service'; -import { LoopBackFilter, SDKToken, AccessToken } from '../../models/BaseModels'; +import { LoopBackFilter, SDKToken } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Rx'; import { Account } from '../../models/Account'; import { SocketConnection } from '../../sockets/socket.connections'; +import { AccessToken } from '../../models/AccessToken'; import { RoomAccount } from '../../models/RoomAccount'; import { Room } from '../../models/Room'; import { RoomAdmin } from '../../models/RoomAdmin'; diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/SDKModels.ts b/tests/ng2web/src/app/shared/sdk/services/custom/SDKModels.ts index a94ed4f5..2ae99dec 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/SDKModels.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/SDKModels.ts @@ -1,6 +1,7 @@ /* tslint:disable */ import { Injectable } from '@angular/core'; import { User } from '../../models/User'; +import { AccessToken } from '../../models/AccessToken'; import { Account } from '../../models/Account'; import { ApplicationCredential } from '../../models/ApplicationCredential'; import { Category } from '../../models/Category'; @@ -14,11 +15,14 @@ import { Storage } from '../../models/Storage'; import { UserCredential } from '../../models/UserCredential'; import { UserIdentity } from '../../models/UserIdentity'; +interface Models { [name: string]: any } + @Injectable() export class SDKModels { - private models: { [name: string]: any } = { + private models: Models = { User: User, + AccessToken: AccessToken, Account: Account, ApplicationCredential: ApplicationCredential, Category: Category, @@ -37,4 +41,12 @@ export class SDKModels { public get(modelName: string): any { return this.models[modelName]; } + + public getAll(): Models { + return this.models; + } + + public getModelNames(): string[] { + return Object.keys(this.models); + } } diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/User.ts b/tests/ng2web/src/app/shared/sdk/services/custom/User.ts index 914b90aa..a12c2e2a 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/User.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/User.ts @@ -5,13 +5,14 @@ import { SDKModels } from './SDKModels'; import { BaseLoopBackApi } from '../core/base.service'; import { LoopBackConfig } from '../../lb.config'; import { LoopBackAuth } from '../core/auth.service'; -import { LoopBackFilter, SDKToken, AccessToken } from '../../models/BaseModels'; +import { LoopBackFilter, SDKToken } from '../../models/BaseModels'; import { JSONSearchParams } from '../core/search.params'; import { ErrorHandler } from '../core/error.service'; import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Rx'; import { User } from '../../models/User'; import { SocketConnection } from '../../sockets/socket.connections'; +import { AccessToken } from '../../models/AccessToken'; /** diff --git a/tests/ng2web/src/app/shared/sdk/services/custom/index.ts b/tests/ng2web/src/app/shared/sdk/services/custom/index.ts index e25e5fd3..4b93f923 100644 --- a/tests/ng2web/src/app/shared/sdk/services/custom/index.ts +++ b/tests/ng2web/src/app/shared/sdk/services/custom/index.ts @@ -1,5 +1,6 @@ /* tslint:disable */ export * from './User'; +export * from './AccessToken'; export * from './Account'; export * from './ApplicationCredential'; export * from './Category';