Después de mucho tiempo volvemos recargado,
La Necesidad: Hacer una llamada encadenada para crear un objeto de tokens, cada token se genera con apis distintas.
Objetivo: Optimizar llamados al servidor y tener siempre a la mano todos los token necesarios
Solución: switchmap para la llamada principal
SwitchMap es un operador muy útil en Angular para manejar flujos de datos de forma reactiva y eficiente. Al cancelar suscripciones anteriores y suscribirse a nuevas, garantiza que siempre estés trabajando con los datos más recientes.
Pero para llegar a esa solución pase por varias ideas hasta que encontre cual es la forma más eficiente de hacer llamadas encadenadas con angular.
Primera opción para la llamada encadenadas de vario tokens
tokenSuperApp() {
const headers = new HttpHeaders({
'Content-Type': 'application/x-www-form-urlencoded'
});
const requestOptions = { headers: headers };
let params = new HttpParams()
.set('client_id', environment.apic.clientidSAAP)
.set('client_secret', environment.apic.clientSecretSAAP)
.set('grant_type', environment.apic.grant_typeSAAP)
.set('scope', 'default');
this.http.post(environment.apic.host + environment.apic.OAuthSAPP, params, requestOptions).pipe(take(1)).subscribe({
next: (oauth: any) => {
this.token.token = oauth.access_token;
}, error: () => {
return null
},
complete: () => {
const headers2 = new HttpHeaders({
'Content-Type': 'application/vnd.api+json',
'Authorization': 'Bearer ' + this.token.token!,
'aud-claim': 'aud',
'iss-claim': this._userInfo.id,
'sub-claim': 'sub'
});
const requestOptions2 = { headers: headers2 };
this.http.post(environment.apic.host + environment.apic.JWTSAPP, {}, requestOptions2).pipe(take(1)).subscribe({
next: (tk: any) => {
this.token.JWT = tk.meta.token;
},
error: () => {
return null;
},
complete: () => {
const headers3 = new HttpHeaders({
'Content-Type': 'application/vnd.api+json',
'Authorization': 'Bearer ' + this.token.token,
'X-Auth-JWT': this.token.JWT!
});
const requestOptions3 = { headers: headers3 };
this.http.get(environment.apic.host + environment.apic.tokenparaAPP, requestOptions3).pipe(take(1)).subscribe({
next: (tksaleforce: any) => {
this.token.tokenparaAPP = tksaleforce.access_token;
},
error: () => {
return null
},
complete: () => {
return this.token
}
})
}
})
}
})
}
Técnicamente hacemos algo que está bien pero no es eficiente; estamos encadenando llamadas al finalizar todo. Esto hace que no podamos tener un buen control en la entrega de los resultados y, aparte, es un poco más difícil de leer.
Opción 2 -Usando SwitchMap para hacer llamadas encadenadas como observable
tokenSuperApp2(): Observable<any> {
const headers = new HttpHeaders({
'Content-Type': 'application/x-www-form-urlencoded'
});
const requestOptions = { headers: headers };
let params = new HttpParams()
.set('client_id', environment.apic.clientidSAAP)
.set('client_secret', environment.apic.clientSecretSAAP)
.set('grant_type', environment.apic.grant_typeSAAP)
.set('scope', 'default');
return this.http.post<any>(environment.apic.host + environment.apic.OAuthSAPP, params, requestOptions)
.pipe(
switchMap(oauth => {
this.token.token = oauth.access_token;
const headers2 = new HttpHeaders({
'Content-Type': 'application/vnd.api+json',
'Authorization': 'Bearer ' + this.token.token!,
'aud-claim': 'aud',
'iss-claim': this._userInfo.cn,
'sub-claim': 'sub'
});
const requestOptions2 = { headers: headers2 };
return this.http.post<any>(environment.apic.host + environment.apic.JWTSAPP, {}, requestOptions2);
}),
switchMap(jwt => {
this.token.JWT = jwt.meta.token;
const headers3 = new HttpHeaders({
'Content-Type': 'application/vnd.api+json',
'Authorization': 'Bearer ' + this.token.token,
'X-Auth-JWT': this.token.JWT!
});
const requestOptions3 = { headers: headers3 };
return this.http.get<any>(environment.apic.host + environment.apic.tokenSaleforce, requestOptions3);
}),
map(tksaleforce => {
this.token.tokenSaleforce = tksaleforce.access_token;
return this.token;
}),
catchError(error => {
// Manejo de errores personalizado
console.error('Error al obtener tokens:', error);
return error;
})
);
}
Y bueno entonces:
¿Qué es SwitchMap?
Imagina que tienes un río (un Observable) y cada vez que cae una gota de lluvia (un valor emitido), quieres construir un nuevo canal (un nuevo Observable) para esa gota. SwitchMap es como un ingeniero que se encarga de construir y destruir estos canales de manera eficiente.
¿Cómo funciona?
- Suscripción: Te suscribes a un Observable fuente.
- Emisión: Cada vez que el Observable fuente emite un valor, se aplica una función a ese valor.
- Nuevo Observable: Esa función retorna un nuevo Observable.
- Suscripción al nuevo: SwitchMap se suscribe automáticamente a este nuevo Observable.
- Cancelación: Si el Observable fuente emite otro valor antes de que el Observable actual complete, SwitchMap cancela la suscripción al Observable actual y se suscribe al nuevo.
Opción 3 – Usar promise para mejorar la espera en la obtención de la llamada encadenada
tokenSuperApp2Promesa(): Promise<any> {
const headers = new HttpHeaders({
'Content-Type': 'application/x-www-form-urlencoded'
});
const requestOptions = { headers: headers };
let params = new HttpParams()
.set('client_id', environment.apic.clientidSAAP)
.set('client_secret', environment.apic.clientSecretSAAP)
.set('grant_type', environment.apic.grant_typeSAAP)
.set('scope', 'default');
return new Promise((resolve, reject) => {
this.http.post<any>(environment.apic.host + environment.apic.OAuthSAPP, params, requestOptions)
.pipe(
switchMap(oauth => {
this.token.token = oauth.access_token;
const headers2 = new HttpHeaders({
'Content-Type': 'application/vnd.api+json',
'Authorization': 'Bearer ' + this.token.token!,
'aud-claim': 'aud',
'iss-claim': this._userInfo.cn,
'sub-claim': 'sub'
});
const requestOptions2 = { headers: headers2 };
return this.http.post<any>(environment.apic.host + environment.apic.JWTSAPP, {}, requestOptions2);
}),
switchMap(jwt => {
this.token.JWT = jwt.meta.token;
const headers3 = new HttpHeaders({
'Content-Type': 'application/vnd.api+json',
'Authorization': 'Bearer ' + this.token.token,
'X-Auth-JWT': this.token.JWT!
});
const requestOptions3 = { headers: headers3 };
return this.http.get<any>(environment.apic.host + environment.apic.tokenSaleforce, requestOptions3);
}),
map(tksaleforce => {
this.token.tokenSaleforce = tksaleforce.access_token;
return this.token;
}),
catchError(error => {
// Manejo de errores personalizado
console.error('Error al obtener tokens:', error);
return error;
})
)
.subscribe(
{
next: token => resolve(token),
error: error => reject(error)
}
);
});
}
y Al final fue como solucione la problemática para generar un token entrando a mi aplicación de micro front, igual proximamente estare publicando como hacer un guard y algo sobre micro front.