Options
All
  • Public
  • Public/Protected
  • All
Menu

Module ngx-customapp-pattern-auth-before-socket

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

yarn add ngx-customapp-pattern-auth-before-socket

Install the customapp-rxjs-websocket package (it is a peer dependency).

yarn add customapp-rxjs-websocket

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.

import {socketsFeatureKey, SocketsRootState} from 'ngx-customapp-pattern-auth-before-socket';

export interface AppState {
// ... 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'
})
export class BaseSocketService {
constructor(
// Injecting a service with initialized sockets
private sockets: WebSocketsOwnerService<api.Request.AsObject, api.Response.AsObject, ArrayBuffer, UserInfo, UserCredentials, AuthResponse.AsObject>,
private errorsService: ErrorsService,
) {
console.log('sockets', this.sockets.sockets)
}

// For now, we assume, that there is only one socket.
get socket() {
return this.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.
private request(req: Omit<api.Request.AsObject, 'id'>): Observable<api.Response.AsObject> {
return this.socket
.request({id: 0, ...req})
.pipe(
// Handling errors, according to conventions from ngx-customapp-errors
catchError(this.errorsService.reportError),
catchError(this.errorsService.toUserError),
)
}

// Helper method for handling subscription notifications.
get sub$(): Observable<SubData.AsObject> {
return this.socket.messages$.pipe(
map(message => message.sub),
filter(Boolean),
)
}

// 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).
get error$(): Observable<any> {
return this.socket.error$;
}

// helper functions for making subscriptions
subscribe(kind: SubKindMap[keyof SubKindMap]): Observable<void> {
return this.request({sub: {kind}}).pipe(
map(() => void 0)
)
}

unsubscribe(kind: SubKindMap[keyof SubKindMap]): Observable<void> {
return this.request({unsub: {kind}}).pipe(
map(() => void 0)
)
}

// example of a request function
addMenuItem(menuAdd: MenuAddRequest.AsObject): Observable<MenuAddResponse.AsObject> {
return this.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()
export class CategoriesEffects {
handleSub$ = createEffect(() => this.baseSocketService.sub$.pipe(
mergeMap((sub) => {
if (sub.categories) {
return of(
// an action, indicating that some entities must be added or replaced
menuCategoriesUpdated({
updateType: UpdateType.list,
data: sub.categories,
})
)
} else if (sub.categoryUpdate) {
// an action, indicating that one entity must be updated
return of(
menuCategoriesUpdated({
updateType: UpdateType.entity,
data: sub.categoryUpdate,
})
)
} else if (sub.categoryDelete) {
// an action, indicating that one entity must be deleted
return of(
menuCategoriesUpdated({
updateType: UpdateType.delete,
data: sub.categoryDelete
})
)
} else {
return EMPTY
}
})
))
}

Index

Type Aliases

CommonWebSocketConfig<RequestType, ResponseType, UnderlyingDataType>: Pick<WebSocketControllerConfig<RequestType, ResponseType, UnderlyingDataType>, "serializer" | "deserializer" | "setRequestId" | "getResponseId" | "isErrorResponse" | "requestTimeout" | "WebSocketCtor" | "binaryType" | "buffer">

Type Parameters

  • RequestType

  • ResponseType

  • UnderlyingDataType extends string | ArrayBufferLike | Blob | ArrayBufferView

IndividualWebSocketConfig<RequestType, ResponseType, UserInfo>: Pick<WebSocketControllerConfig<RequestType, ResponseType, any>, "url" | "protocol"> & { authorize?: { createRequest: any; isResponseSuccessful?: any }; socketId: SocketId; subscribe?: { createRequests: any; isResponseSuccessful?: any } }

Type Parameters

  • RequestType

  • ResponseType

  • UserInfo

SocketId: string

Unique name, used to reference an instance of the socket.

Variables

closeSockets: ActionCreator<"[ngx-customapp-pattern-auth-before-socket] close sockets", (() => TypedAction<"[ngx-customapp-pattern-auth-before-socket] close sockets">)> = ...
closeSocketsFinished: ActionCreator<"[ngx-customapp-pattern-auth-before-socket close sockets finished", (() => TypedAction<"[ngx-customapp-pattern-auth-before-socket close sockets finished">)> = ...
closeTimeout: number = ...
initSockets: ActionCreator<"[ngx-customapp-pattern-auth-before-socket] init sockets", (() => TypedAction<"[ngx-customapp-pattern-auth-before-socket] init sockets">)> = ...
initSocketsErrored: ActionCreator<"[ngx-customapp-pattern-auth-before-socket] init sockets errored", ((props: { error: string }) => { error: string } & TypedAction<"[ngx-customapp-pattern-auth-before-socket] init sockets errored">)> = ...
initSocketsSucceed: ActionCreator<"[ngx-customapp-pattern-auth-before-socket] init sockets succeed", (() => TypedAction<"[ngx-customapp-pattern-auth-before-socket] init sockets succeed">)> = ...
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.

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.

loginAndInitSocketsErrored: ActionCreator<"[ngx-customapp-pattern-auth-before-socket] login and init sockets errored", ((props: { error: string }) => { error: string } & TypedAction<"[ngx-customapp-pattern-auth-before-socket] login and init sockets errored">)> = ...

Used to make an authorization and a socket initialization an atomic action. Is dispatched when either the login action OR the initSockets action errored.

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.

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.

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.

metaReducers: MetaReducer<SocketsRootState>[] = []
reducers: ActionReducerMap<SocketsRootState> = ...
selectCloseInProcess: MemoizedSelector<object, boolean, DefaultProjectorFn<boolean>> = ...
selectSocketsInitInProcess: MemoizedSelector<object, boolean, DefaultProjectorFn<boolean>> = ...
selectSocketsState: MemoizedSelector<object, SocketsRootState, DefaultProjectorFn<SocketsRootState>> = ...
socketsFeatureKey: "ngx-customapp-pattern-auth-before-socket" = packageName

Generated using TypeDoc