The user logs in.
An http request with user credentials is sent to the backend.
An http response with JWT and maybe user info is received.
JWT tokens being saved and included in headers of every request (except explicitly stated).
The socket opened.
An optional auth request is sent in the socket.
An auth response received from the socket.
Optional Subscription requests are sent in the socket.
Subscription responses received from the socket.
Optionally more sockets are opened and more auth and subscription messages are sent.
The user can log out, logout from every device, maybe log in as another user.
After logout all the sockets are closed.
This package plugs to the application NgRx store as a separate substore.
Import PatternAuthBeforeSocketModule.forRoot(config) in your app. This will enable package's store and provide
services.
exportinterfaceAppState { // ... other fields of the state [socketsFeatureKey]: SocketsRootState, }
The sockets will open after ngx-customapp-jwt loginSucceed or loginAsSucceed actions and closed after logoutSucceed action.
If you want to listen for sockets being initialized before navigating into the app, you shouldn't.
If the app written properly, sockets will be successfully initialized after successful login, loginAs or loginAgain,
cos you are supposed to use the same credentials. If you still want to handle such errors
(when the user is logged in but socket is not available), react to the initSocketsErrored
action by suggesting the user to log in again.
If you still want to react to the sockets being successfully/unsuccessfully initialized,
you should listen for loginAndInitSocketsSucceed, loginAndInitSocketsErrored,
loginAsAndInitSocketsSucceed, loginAsAndInitSocketsErrored, loginAgainAndInitSocketsSucceed,
loginAgainAndInitSocketsErrored. But use them carefully, cos all actions in general need
different handler.
To send the request into the socket and handle subscriptions, make a service.
@Injectable({ providedIn:'root' }) exportclassBaseSocketService { constructor( // Injecting a service with initialized sockets privatesockets: WebSocketsOwnerService<api.Request.AsObject, api.Response.AsObject, ArrayBuffer, UserInfo, UserCredentials, AuthResponse.AsObject>, privateerrorsService: ErrorsService, ) { console.log('sockets', this.sockets.sockets) }
// For now, we assume, that there is only one socket. getsocket() { returnthis.sockets.sockets[socketId] }
// The id is set by the setRequestId function from <a href="../interfaces/ngx_customapp_pattern_auth_before_socket.WebSocketChain.html#commonConfig">WebSocketChain.commonConfig</a>, // so it is omitted from the argument of the function below. privaterequest(req: Omit<api.Request.AsObject, 'id'>): Observable<api.Response.AsObject> { returnthis.socket .request({id:0, ...req}) .pipe( // Handling errors, according to conventions from ngx-customapp-errors catchError(this.errorsService.reportError), catchError(this.errorsService.toUserError), ) }
// Helper function, to listen for errors. Generally, there is no way to handle this errors, // other than just report them and reopen the socket (which is made internally). geterror$(): Observable<any> { returnthis.socket.error$; }
// helper functions for making subscriptions subscribe(kind: SubKindMap[keyofSubKindMap]): Observable<void> { returnthis.request({sub: {kind}}).pipe( map(() =>void0) ) }
// example of a request function addMenuItem(menuAdd: MenuAddRequest.AsObject): Observable<MenuAddResponse.AsObject> { returnthis.request({menuAdd}).pipe( map(response=>response.menuAdd!) ) } }
To handle the subscription, map subscription messages into actions according to your app logic.
// an effect to work with entity, called Category @Injectable() exportclassCategoriesEffects { handleSub$ = createEffect(() =>this.baseSocketService.sub$.pipe( mergeMap((sub) => { if (sub.categories) { returnof( // an action, indicating that some entities must be added or replaced menuCategoriesUpdated({ updateType:UpdateType.list, data:sub.categories, }) ) } elseif (sub.categoryUpdate) { // an action, indicating that one entity must be updated returnof( menuCategoriesUpdated({ updateType:UpdateType.entity, data:sub.categoryUpdate, }) ) } elseif (sub.categoryDelete) { // an action, indicating that one entity must be deleted returnof( menuCategoriesUpdated({ updateType:UpdateType.delete, data:sub.categoryDelete }) ) } else { returnEMPTY } }) )) }
Unique name, used to reference an instance of the socket.
Variables
Const closeSockets
closeSockets:ActionCreator<"[ngx-customapp-pattern-auth-before-socket] close sockets", (() => TypedAction<"[ngx-customapp-pattern-auth-before-socket] close sockets">)> = ...
Const closeSocketsFinished
closeSocketsFinished:ActionCreator<"[ngx-customapp-pattern-auth-before-socket close sockets finished", (() => TypedAction<"[ngx-customapp-pattern-auth-before-socket close sockets finished">)> = ...
loginAgainAndInitSocketsErrored:ActionCreator<"[ngx-customapp-pattern-auth-before-socket] login again and init sockets errored", ((props: { error: string }) => { error: string } & TypedAction<"[ngx-customapp-pattern-auth-before-socket] login again and init sockets errored">)> = ...
Used to make an authorization and a socket initialization an atomic action.
is dispatched when either the loginAgain action OR the initSockets action errored.
Const loginAgainAndInitSocketsSucceed
loginAgainAndInitSocketsSucceed:ActionCreator<"[ngx-customapp-pattern-auth-before-socket] login again and init sockets succeed", (() => TypedAction<"[ngx-customapp-pattern-auth-before-socket] login again and init sockets succeed">)> = ...
Used to make an authorization and a socket initialization an atomic action.
Is dispatched when BOTH the loginAgain action and the initSockets action succeed.
Used to make an authorization and a socket initialization an atomic action.
Is dispatched when either the login action OR the initSockets action errored.
Const loginAndInitSocketsSucceed
loginAndInitSocketsSucceed:ActionCreator<"[ngx-customapp-pattern-auth-before-socket] login and init sockets succeed", (() => TypedAction<"[ngx-customapp-pattern-auth-before-socket] login and init sockets succeed">)> = ...
Used to make an authorization and a socket initialization an atomic action.
Is dispatched when BOTH the login action and the initSockets action succeed.
Const loginAsAndInitSocketsErrored
loginAsAndInitSocketsErrored:ActionCreator<"[ngx-customapp-pattern-auth-before-socket] login as and init sockets errored", ((props: { error: string }) => { error: string } & TypedAction<"[ngx-customapp-pattern-auth-before-socket] login as and init sockets errored">)> = ...
Used to make an authorization and a socket initialization an atomic action.
is dispatched when either the loginAs action OR the initSockets action errored.
Const loginAsAndInitSocketsSucceed
loginAsAndInitSocketsSucceed:ActionCreator<"[ngx-customapp-pattern-auth-before-socket] login as and init sockets succeed", (() => TypedAction<"[ngx-customapp-pattern-auth-before-socket] login as and init sockets succeed">)> = ...
Used to make an authorization and a socket initialization an atomic action.
Is dispatched when BOTH the loginAs action and the initSockets action succeed.
Implemented flow:
The user logs in. An http request with user credentials is sent to the backend. An http response with JWT and maybe user info is received. JWT tokens being saved and included in headers of every request (except explicitly stated). The socket opened. An optional auth request is sent in the socket. An auth response received from the socket. Optional Subscription requests are sent in the socket. Subscription responses received from the socket. Optionally more sockets are opened and more auth and subscription messages are sent. The user can log out, logout from every device, maybe log in as another user. After logout all the sockets are closed.
Usage
Install
Install the customapp-rxjs-websocket package (it is a peer dependency).
Install and configure ngx-customapp-errors and ngx-customapp-jwt packages. You may want to use ngx-customapp-proto-http during the configuration.
This package plugs to the application NgRx store as a separate substore. Import
PatternAuthBeforeSocketModule.forRoot(config)
in your app. This will enable package's store and provide services.Add types to the NgRx AppState.
The sockets will open after ngx-customapp-jwt loginSucceed or loginAsSucceed actions and closed after logoutSucceed action.
If you want to listen for sockets being initialized before navigating into the app, you shouldn't. If the app written properly, sockets will be successfully initialized after successful
login
,loginAs
orloginAgain
, cos you are supposed to use the same credentials. If you still want to handle such errors (when the user is logged in but socket is not available), react to theinitSocketsErrored
action by suggesting the user to log in again.If you still want to react to the sockets being successfully/unsuccessfully initialized, you should listen for
loginAndInitSocketsSucceed
,loginAndInitSocketsErrored
,loginAsAndInitSocketsSucceed
,loginAsAndInitSocketsErrored
,loginAgainAndInitSocketsSucceed
,loginAgainAndInitSocketsErrored
. But use them carefully, cos all actions in general need different handler.To send the request into the socket and handle subscriptions, make a service.
To handle the subscription, map subscription messages into actions according to your app logic.