RxJs BehaviourSubject doesn't give its current value to forkJoin.pipe - rxjs

import { UserManager, UserManagerSettings, User } from 'oidc-client';
export class AuthService {
private manager = new UserManager(getClientSettings(this.appSettingsService));
private userSubject: BehaviorSubject<User>;
private get user(): Observable<User> { return this.userSubject.asObservable();}
constructor(private appSettingsService: AppSettingsService) {
this.userSubject = new BehaviorSubject<User>(null);
this.manager.getUser().then(user => {
if (user) {
this.userSubject.next(user);
localStorage.setItem('access_token', user.access_token);
}
});
}
isAuthenticated(): Observable<boolean> {
return this.user.pipe(map((user: User) => {
return (user != null && !user.expired) || !!localStorage.getItem('access_token');
}));
get access_token(): Observable<string> {
return this.user.pipe(map((u: User) => {
if (u && u.access_token) {
return u.access_token;
}
if (!!localStorage.getItem('access_token')) {
return localStorage.getItem('access_token');
}
return undefined;
}));
}
isInRole(roleName: string): Observable<boolean> {
console.log(`AuthService.isInRole ${roleName}`);
return forkJoin({ isAuthenticated: this.isAuthenticated(), access_token: this.access_token })
.pipe(map(fj => {
console.log(`AuthService.isInRole.pipe ${roleName}`);
if (!fj.isAuthenticated || !fj.access_token) {
return false;
}
var claims: object = jwt_decode(fj.access_token);
if ('role' in claims && claims['role'].indexOf(roleName) !== -1) {
console.log(`AuthService.isInRole ${roleName} YES`);
return true;
}
if ('http://schemas.microsoft.com/ws/2008/06/identity/claims/role' in claims
&& claims['http://schemas.microsoft.com/ws/2008/06/identity/claims/role'].indexOf(roleName) !== -1
) {
console.warn('Role claim type is ...microsoft...!');
return true;
}
return false;
}
));
}
The message AuthService.isInRole.pipe never appears.
The BehaviourSubject was supposed to give its latest value when referenced, but this is not happening. Why?

Related

mqttjs keep disconnecting and reconnect in main thread, but stable in worker thread

I am using mqttjs to connect to the aws iot mqtt by wss presigned URL
In my application, there are 2 mqtt connection, the first one is from the main thread, the second one is from a web worker thread. Both are created from a same class (MqttServcie), they have the same logic, same everything.
But the one from the main thread keep disconnecting and reconnecting.
The one from the worker is very stable, the connection is never die, it never has to be reconnected.
Do you have any idea why the connection from the main thread keep disconnecting?
And what make a connection ends? (other than connection timeout and lost wifi connection)
In the image below, I end the connection and create a new one after 5 times retrying fails, so the number of request is several, nevermind.
The client id is always random, so it never be kicked off by another client.
The MqttService class
/* eslint-disable no-useless-constructor, no-empty-function */
import mqtt, { MqttClient } from 'mqtt';
import { NuxtAxiosInstance } from '#nuxtjs/axios';
import RestService from './RestService';
import { Mqtt } from '~/types/Mqtt';
import { MILISECS_PER_SEC } from '~/configs';
import { logWithTimestamp } from '~/utils';
export type MqttServiceEventHandlers = {
close?: Array<() => void>;
disconnectd?: Array<() => void>;
connected?: Array<() => void>;
reconnect?: Array<() => void>;
reconnected?: Array<() => void>;
beforeReconect?: Array<() => void>;
};
export type MqttServiceEvent = keyof MqttServiceEventHandlers;
export interface IMqttService {
httpClient?: NuxtAxiosInstance;
presignedUrl?: string;
}
export class MqttService {
public client: MqttClient | null = null;
public closing = false;
public reconnecting = false;
// Example: "wss://abcdef123-ats.iot.us-east-2.amazonaws.com/mqtt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXXXXX%2F20230206%2Fus-east-2%2Fiotdevicegateway%2Faws4_request&X-Amz-Date=20230206T104907Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Signature=abcxyz123"
public presignedUrl = '';
public httpClient = null as null | NuxtAxiosInstance;
public retryCount = 0;
public retryLimits = 5;
public handlers: MqttServiceEventHandlers = {};
constructor(
{ httpClient, presignedUrl }: IMqttService,
public caller = 'main'
) {
if (httpClient) {
this.httpClient = httpClient;
} else if (presignedUrl) {
this.presignedUrl = presignedUrl;
} else {
throw new Error(
'[MqttService] a httpClient or presigned URL must be provided'
);
}
}
async connect() {
await this.updatePresignedUrl();
this.client = mqtt.connect(this.presignedUrl, {
clientId: this.generateClientId(),
reconnectPeriod: 5000,
connectTimeout: 30000,
resubscribe: true,
keepalive: 15,
transformWsUrl: (_url, _options, client) => {
// eslint-disable-next-line no-param-reassign
client.options.clientId = this.generateClientId();
logWithTimestamp(
`[MQTT] [${this.caller}] transformWsUrl()`,
client.options.clientId,
this.signature
);
return this.presignedUrl;
},
});
return this.setupHandlers();
}
protected setupHandlers() {
return new Promise<MqttClient>((resolve, reject) => {
this.client!.on('close', async () => {
if (this.closing) return;
if (this.retryCount >= this.retryLimits) {
(this.handlers.close || []).forEach((handler) => handler());
await this.disconnect();
logWithTimestamp(`[MQTT] [${this.caller}] connection has closed!`);
reject(new Error(`All retry attempts were failed (${this.caller})`));
return;
}
if (this.retryCount === 0) {
(this.handlers.beforeReconect || []).forEach((handler) => handler());
logWithTimestamp(
`[MQTT] [${this.caller}] connection lost`,
this.presignedUrl
);
}
// Re-generate new presigned URL at the 3rd attempt, or if the URL is expired
if (this.retryCount === 2 || this.isExpired) {
await this.updatePresignedUrl().catch(async () => {
await this.disconnect();
(this.handlers.close || []).forEach((handler) => handler());
logWithTimestamp(
`[MQTT] [${this.caller}] connection has closed! (Unable to get new presigned url)`
);
});
}
});
this.client!.on('reconnect', () => {
this.retryCount += 1;
this.reconnecting = true;
(this.handlers.reconnect || []).forEach((handler) => handler());
logWithTimestamp(
`[MQTT] [${this.caller}] reconnect (#${this.retryCount})`
);
});
this.client!.on('connect', () => {
if (this.reconnecting) {
(this.handlers.reconnected || []).forEach((handler) => handler());
}
this.retryCount = 0;
this.reconnecting = false;
(this.handlers.connected || []).forEach((handler) => handler());
logWithTimestamp(`[MQTT] [${this.caller}] connected`);
resolve(this.client!);
});
});
}
disconnect({ force = true, silent = false, ...options } = {}) {
this.closing = true;
return new Promise<void>((resolve) => {
if (this.client && this.isOnline()) {
this.client.end(force, options, () => {
this.reset(silent, '(fully)');
resolve();
});
} else {
this.client?.end(force);
this.reset(silent, '(client-side only)');
resolve();
}
});
}
reset(silent = false, debug?: any) {
this.client = null;
this.retryCount = 0;
this.reconnecting = false;
this.presignedUrl = '';
this.closing = false;
if (!silent) {
(this.handlers.disconnectd || []).forEach((handler) => handler());
}
logWithTimestamp(`[MQTT] [${this.caller}] disconnected!`, {
silent,
debug,
});
}
async destroy() {
await this.disconnect({ silent: true });
this.handlers = {};
}
// Get or assign a new wss url
async updatePresignedUrl(url?: string) {
if (this.httpClient) {
const service = new RestService<Mqtt>(this.httpClient, '/mqtts');
const { data } = await service.show('wss_link');
this.presignedUrl = data!.wss_link;
} else if (url) {
this.presignedUrl = url;
}
return this.presignedUrl;
}
on(event: MqttServiceEvent, handler: () => void) {
const { [event]: eventHanlders = [] } = this.handlers;
eventHanlders.push(handler);
this.handlers[event] = eventHanlders;
}
off(event: MqttServiceEvent, handler: () => void) {
const { [event]: eventHanlders = [] } = this.handlers;
const index = eventHanlders.findIndex((_handler) => _handler === handler);
eventHanlders.splice(index, 1);
}
get date() {
const matched = this.presignedUrl.match(/(X-Amz-Date=)(\w+)/);
if (!matched) return null;
return new Date(
String(matched[2]).replace(
/^(\d{4})(\d{2})(\d{2})(T\d{2})(\d{2})(\d{2}Z)$/,
(__, YYYY, MM, DD, HH, mm, ss) => `${YYYY}-${MM}-${DD}${HH}:${mm}:${ss}`
)
);
}
get expires() {
const matched = this.presignedUrl.match(/(X-Amz-Expires=)(\d+)/);
return matched ? Number(matched[2]) : null;
}
get signature() {
const matched = this.presignedUrl.match(/(X-Amz-Signature=)(\w+)/);
return matched ? matched[2] : null;
}
get expiresDate() {
if (!(this.date && this.expires)) return null;
return new Date(this.date.getTime() + this.expires * MILISECS_PER_SEC);
}
get isExpired() {
return !this.expiresDate || this.expiresDate <= new Date();
}
private generateClientId() {
return `mqttjs_[${this.caller}]_${Math.random()
.toString(16)
.substring(2, 10)}`.toUpperCase();
}
private isOnline() {
return typeof window !== 'undefined' && window?.$nuxt?.isOnline;
}
}

Angular 11 wait until subscribe finishes getting data

I have a auth.service and data.service. auth.service getting data from data.service but it checks before data arrives. So it returns undefined.
auth.service getting data like this;
get isLoggedIn(): boolean {
const user = JSON.parse(localStorage.getItem('user'));
const emailVerify = this.dataservice.userStatService(user.uid);
console.warn(emailVerify)
return (user !== null && emailVerify !== false && emailVerify !== undefined ) ? true : false;
}
data.service check user status function like this;
userStatService(uid: any): any{
console.error(uid)
this.get(uid)
.subscribe(
data => {
console.warn('status set', data.status)
this.statData = data.status;
},
error => {
console.log(error);
});
return this.statData;
}
and this code works like this now;
See console logs
I'm waiting for your code examples, thank you.
Update:
auth.guard code;
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
if (this.authService.isLoggedIn() !== true) {
this.router.navigate(['/auth/login'], { queryParams: { returnUrl: 'dashboard' } })
.then(() => {
this.authService.SignOut();
});
}else{
return true;
}
}
observables execute asynchronously, you need to return the observable and subscribe in the consumer to use it correctly:
// return observable
userStatService(uid: any): any{
console.error(uid)
return this.get(uid)
}
isLoggedIn() {
const user = JSON.parse(localStorage.getItem('user'));
this.dataservice.userStatService(user.uid).subscribe(emailVerify => {
console.warn(emailVerify)
})
// really can't do this while working with async execution. doesn't work.
//return (user !== null && emailVerify !== false && emailVerify !== undefined ) ? true : false;
}
if this is for a guard, use the map operator and return the whole observable, angular expects either a boolean or an observable:
isLoggedIn(): Observable<boolean> {
const user = JSON.parse(localStorage.getItem('user'));
if (!user)
return of(false); // null guard and slight performance improvement
return this.dataservice.userStatService(user.uid).pipe(map(emailVerify => {
console.warn(emailVerify)
return (emailVerify !== false && emailVerify !== undefined ) ? true : false;
}))
}
and in your guard you need to again, RETURN THE OBSERVABLE:
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
return this.authService.isLoggedIn().pipe(
map(isLoggedIn => {
if (!isLoggedIn) {
this.router.navigate(['/auth/login'], { queryParams: { returnUrl: 'dashboard' } }).then(() => {
this.authService.SignOut();
});
}
return isLoggedIn;
})
)
}
angular guards will handle the subscribing, but you must return the observable to the guard.

GraphQL relay connectionFromArraySlice

There isn't any documentation for how the array meta info (arrayLength and sliceStart) should be implemented using facebook's graphql-relay-js helper library.
https://github.com/graphql/graphql-relay-js/issues/199
I managed to get it to work using the following implemention however I am guessing there is an easier/more correct way to do this.
Retrieve rows and row count from database
function transformRole(role: Role) {
return { ...role, roleId: role.id };
}
async function getRolesSlice({ roleId, after, first, last, before }: any): Promise<[Role[], number]> {
const queryBuilder = repository.createQueryBuilder();
if (roleId !== undefined) {
queryBuilder.where('id = :roleId', { roleId });
}
if (before) {
const beforeId = cursorToOffset(before);
queryBuilder.where('id < :id', { id: beforeId });
}
if (after) {
const afterId = cursorToOffset(after);
queryBuilder.where({
id: MoreThan(Number(afterId))
});
}
if (first === undefined && last === undefined) {
queryBuilder.orderBy('id', 'ASC');
}
if (first) {
queryBuilder.orderBy('id', 'ASC').limit(first);
}
if (last) {
queryBuilder.orderBy('id', 'DESC').limit(last);
}
return Promise.all([
queryBuilder.getMany()
.then(roles => roles.map(transformRole)),
repository.count() // Total number of roles
]);
}
Roles resolver
resolve: (_, args) =>
getRolesSlice(args)
.then(([results, count]) => {
const firstId = results[0] && results[0].roleId;
let sliceStart = 0;
if (args.first) {
sliceStart = firstId;
}
if (args.last) {
sliceStart = Math.max(firstId - args.last, 0);
}
if (args.after && args.last) {
sliceStart += 1;
}
return connectionFromArraySlice(
results,
args,
{
arrayLength: count + 1,
sliceStart
}
);
})
},
Edit:
This is what I came up with which is a little cleaner and seems to be working correctly.
const initialize = () => {
repository = getConnection().getRepository(Role);
}
function transformRole(role: Role) {
return { ...role, roleId: role.id };
}
function getRolesSlice(args: any):
Promise<[
Role[],
any,
{ arrayLength: number; sliceStart: number; }
]> {
if (!repository) initialize();
const { roleId, after, first, last, before } = args;
const queryBuilder = repository.createQueryBuilder();
if (roleId !== undefined) {
queryBuilder.where('id = :roleId', { roleId });
}
if (before !== undefined) {
const beforeId = cursorToOffset(before);
queryBuilder.where({
id: LessThan(beforeId)
});
}
if (after !== undefined) {
const afterId = cursorToOffset(after);
queryBuilder.where({
id: MoreThan(Number(afterId))
});
}
if (first !== undefined) {
queryBuilder.orderBy('id', 'ASC').limit(first);
} else if (last !== undefined) {
queryBuilder.orderBy('id', 'DESC').limit(last);
} else {
queryBuilder.orderBy('id', 'ASC');
}
return Promise.all([
queryBuilder.getMany()
.then(roles => roles.map(transformRole))
.then(roles => last !== undefined ? roles.slice().reverse() : roles),
repository.count()
]).then(([roles, totalCount]) =>
[
roles,
args,
{
arrayLength: totalCount + 1,
sliceStart: roles[0] && roles[0].roleId
}
]
);
}
// Resolver
roles: {
type: rolesConnection,
args: {
...connectionArgs,
roleId: {
type: GraphQLString
}
},
resolve: (_, args) =>
getRolesSlice(args)
.then((slice) => connectionFromArraySlice(...slice))
},

Call multiple ajax and wait for result Angular2

I have problem with my Angular. I have this functions:
private callUserInfo(): any {
this.isLoading = true;
return this._ajaxService.getService('/system/ping')
.map(
result => {
this.userId =
result.participant.substring(result.participant.indexOf('#'));
this.isLoading = false;
}
)
.catch(error => {
return Observable.throw(error);
});
}
public loadUserData(userName: string): any {
this.isLoading = true;
return this._ajaxService.getService('/User/' + userName)
.map(
result => {
const data = result[0];
this.user = new User(
data.id,
data.contacts[0].email,
data.name,
data.surname,
data.address.street,
data.address.city,
data.address.state,
data.address.country,
data.address.postCode,
data.address.timeZone);
this.isLoading = false;
})
.catch(error => {
return Observable.throw(error);
});
}
public getUser(): any {
if (this.user == null) {
this.callUserInfo().subscribe(() => {
this.loadUserData(this.userId).subscribe(() => {
return this.user;
});
});
} else {
return this.user;
}
}
In my component I call this service functions like this (auth service is service with functions defined up):
constructor(private _auth: AuthService) {
this.user = _auth.getUser();
}
But it stills return null (because Ajax calls are not finished?) Can someone explain me, how to call this two calls (first is system/ping service and based on return (userId) I need to call second ajax call (/user/id). After this two calls I have defined user in my service and I can return it to other components. Can someone expllain me, what am i doing wrong, or how I can do it better? I´m using newest version of angular.
P.S. Get service is from my wrapper service:
getService(url: string): Observable<any> {
return this.http
.get(this.base + url, this.options)
.map(this.extractData)
.catch(this.handleError);
}
You are not returning anything in case this.user==null
Change your function as following:
userObservabel=new BehaviourSubject(null);
public getUser(): any {
if (this.user == null) {
this.callUserInfo().subscribe(() => {
this.loadUserData(this.userId).subscribe(() => {
this.userObservabel.next(this.user);
});
});
return this.userObservabel.asObservable();
} else {
return this.userObservabel.asObservable();
}
}
and then you need to subscribe it
constructor(private _auth: AuthService) {
_auth.getUser().subscribe(user => this.user = user);
}
You need to call the second service in the subscribe or in the map method i.e. the Observable has returned a promise and that is resolved. Once that is resolved u should call your chained service.
A sample snipped from my POC might help you
this._accountListService.getAccountsFromBE().subscribe(
response => {
this.response = response;
this._accountListService.getAccountSorting().subscribe(
response => {
this.acctSort = response;
if (response.prodCode) {
this._accountListService.getAccountOrder().subscribe(
response => {
this.acctOrder = response;
this.response = this.setAccountOrder(this.response);
this.response.sort(this.myComparator);
this.acctFlag = true;
if (this.prodDesc) {
this.loader = false;
this.accountDetl = this.response[0];
this.accountDetl.entCdeDesc = this.prodDesc[this.accountDetl.entProdCatCde];
}
},
err => console.log(err)
);
}
},
err => console.log(err)
);
},
err => console.log(err)
);

Not all code paths return a value in Typescript Promise

I have a method that returns a Typescript promise. I am tightening up my code by enabling TSLint and it says that "Not all code paths return a value" for this method.
Please critique my code - I cannot work out what path it is referring to:
public getExtendedProfile(): Promise<any> {
//only do this if they are authenticated
if (this.authenticated()) {
if (typeof this.userProfile.user_id !== 'undefined') {
if (typeof this.user == 'undefined') {
this.profileService.getExtendedUserProfile(this.userProfile.user_id)
.then(data => {
return Promise.resolve(data);
})
.catch((error: any) => {
return Promise.reject(error)
});
}
else {
return Promise.resolve(this.user);
}
}
else {
return Promise.reject("No user stored");
}
}
else {
return Promise.reject("Not Authenticated");
}
}
// una alternativa podria ser
:Promise<any>
// cambiar a
:Promise<any|void>
docs typescript funciones: https://www.typescriptlang.org/docs/handbook/functions.html

Resources