I'm creating a link to remove user auth token on a 401 response from server. The problem here, is that I need to remove tokesn from storage in my case AsyncStorage, which, as its name suggests, performs asynchronous operations, but if I try to pass an asynchronous function to onError callback from #apollo/client/link/error, the code editor immediately throws the following error: Type 'Promise<void>' is not assignable to type 'void | Observable<FetchResult<{ [key: string]: any; },.
So there is a way to pass an async callback to onError?
Invalidate token link
import {ErrorResponse, onError} from '#apollo/client/link/error';
import {isJwtError, JWTError} from './errors';
// async function to remove auth token from asyncstorage
import {removeTokens} from './utils';
interface ResponseError extends ErrorResponse {
networkError?: Error & {
statusCode?: number;
bodyText?: string;
};
}
export const invalidateTokenLink = onError((error: ResponseError) => {
if (
(error.networkError && error.networkError.statusCode === 401) ||
error.graphQLErrors?.some(isJwtError)
) {
if (error.graphQLErrors[0].extensions.code !== JWTError.expired) {
// this is where I run an asynchronous function to remove the token
removeTokens();
}
}
});
The error message mentions that you can return an Observable<FetchResult<{ [key: string]: any; }. It seems that apollo-client uses the zen-observable library.
You can build an observable from your promise. Or use the promiseToObservable function from observable-helpers.
Related
I have a react-native mobile app making a call to a graphql backend. Queries are working without issue. I just coded a mutation however, and everytime I try to make a call, the client is raising this issue:
Error: Could not find a default resolver for travelPassPasswordResetEmail. Please supply a resolver for this mutation.: {"response":{"errors":[{"message":"Could not find a default resolver for travelPassPasswordResetEmail. Please supply a resolver for this mutation.","locations":[{"line":2,"column":3}],"path":["travelPassPasswordResetEmail"]}],"data":null,"status":201,"headers":{"map":{"content-type":"application/json"}}},"request":{"query":"mutation travelPassPasswordResetEmail($email: String) {\n travelPassPasswordResetEmail(email: $email) {\n message\n status\n }\n}","variables":{"email":"test#test.com"}}}
We are using graphql-codegen.
The mutation in the schema:
type Mutation {
travelPassPasswordResetEmail(email: String): SetResetPasswordMutation!
}
The operations graphql file:
mutation travelPassPasswordResetEmail($email: String) {
travelPassPasswordResetEmail(email: $email) {
message
status
}
}
In the types files we get the following generated for the mutation:
export const TravelPassPasswordResetEmail = gql`
mutation travelPassPasswordResetEmail($email: String) {
travelPassPasswordResetEmail(email: $email) {
message
status
}
}
`;
export const TravelPassPasswordResetEmailDocument = gql`
mutation travelPassPasswordResetEmail($email: String) {
travelPassPasswordResetEmail(email: $email) {
message
status
}
}
`;
export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper = defaultWrapper) {
return {
travelPassPasswordResetEmail(variables?: TravelPassPasswordResetEmailMutationVariables, requestHeaders?: Dom.RequestInit["headers"]): Promise<TravelPassPasswordResetEmailMutation> {
return withWrapper((wrappedRequestHeaders) => client.request<TravelPassPasswordResetEmailMutation>(TravelPassPasswordResetEmailDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'travelPassPasswordResetEmail');
}
};
}
Does anyone know why this is asking for a resolver and it cannot be found? I have tested the graphql on the backend with postman and everything is ok.
I'm planning to use my authentication part as a microservice so I user nest js TCP transporter but I don't know how to execute passport local strategy in my microservice I used the below code
#MessagePattern('login')
#UseGuards(LocalAuthGuard)
localLogin( loginDto: LoginDto) {
console.log('awa')
return loginDto
// return this.authService.localLogin(req.user, loginDto.email);
}
but it doesn't work any idea how can I authorized local strategy in microservice my local strategy looks like below
import { Strategy } from 'passport-local';
import { PassportStrategy } from '#nestjs/passport';
import { Injectable, UnauthorizedException } from '#nestjs/common';
import { AuthService } from './auth.service';
import { Request } from 'express';
#Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true,
});
}
async validate(
email: string,
password: string,
): Promise<{ id: string; isVerified: boolean }> {
try {
const user = await this.authService.validateLocalUser(email, password);
if (!user) {
//throw new UnauthorizedException();
}
return { id: user.id,isVerified: user.isVerified };
} catch (error) {
console.log('error')
}
}
}
This is the error I got
node:71975) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'reply' of null
at AppExceptionFilter.catch (/Users/imanthaatapattu/apps/beta-identity-service/node_modules/#nestjs/core/exceptions/base-exception-filter.js:26:24)
at AppExceptionFilter.catch (/Users/imanthaatapattu/apps/beta-identity-service/dist/common/exception-filters/app-exception.filter.js:29:20)
at RpcExceptionsHandler.invokeCustomFilters (/Users/imanthaatapattu/apps/beta-identity-service/node_modules/#nestjs/microservices/exceptions/rpc-exceptions-handler.js:34:32)
at RpcExceptionsHandler.handle (/Users/imanthaatapattu/apps/beta-identity-service/node_modules/#nestjs/microservices/exceptions/rpc-exceptions-handler.js:13:36)
at RpcProxy.handleError (/Users/imanthaatapattu/apps/beta-identity-service/node_modules/#nestjs/microservices/context/rpc-proxy.js:24:34)
at /Users/imanthaatapattu/apps/beta-identity-service/node_modules/#nestjs/microservices/context/rpc-proxy.js:17:29
at processTicksAndRejections (internal/process/task_queues.js:93:5)
(node:71975) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 3)
(node:71975) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Passport is an Express middleware, meant for use over HTTP. You're using it with a middleware, which isn't quite expected. If you want to continue using passport with a microservice, you need to provide the same values it expects under the HTTP context. You can see Nest's implementation of the AuthGuard here. The most important part of this, is making sure that getRequest returns an object with the property body and with body having the properties email and password. It would most likely be easier to write your own microservice specific guard that can determine if the request is valid or not (via a JWT or similar)
I'm trying to get user profile information from Auth0. It works in the frontend with the pipe Async
<pre *ngIf="auth.userProfile$ | async as profile">
<code>{{ profile | json }}</code>
</pre>
But in the backend If I want to read the nickname and do something with it. I'm lost.
constructor(public auth: AuthService)
ngOnInit() {
if (this.auth.isAuthenticated$) {
const result = this.auth.userProfile$;
}
}
I know that my varibale "result" is an Observable.
But I'm new with this stuff of Observable. And I try to get the value.
If I use the debug console, I can see the value with this line :
this.auth.userProfile$.source.value.nickname
But If I wrote it in my code, I have this error: Typescript error: Property 'value' does not exist on type 'Observable'
ngOnInit() {
if (this.auth.isAuthenticated$) {
const result = this.auth.userProfile$;
console.log(this.auth.userProfile$.source.value.nickname); // error here
}
}
So someone can help me with this ?
thanks
You access data within an observable by subscribing to it (this is what the async pipe does under the hood).
Here's an example for how to subscribe to your observable:
// import { tap } from 'rxjs/operators';
// tap is one of many rxjs operators
// operators allow you to perform operations on data within observables
this.auth.userProfile$.pipe(
tap(profile => {
console.log(profile);
})
)
.subscribe(); // this is what allows you access the data within the observable
Observables: https://angular.io/guide/observables
RXJS Operators: https://rxjs-dev.firebaseapp.com/guide/operators
I finally find a solution:
in the class I enter this code:
export class getInfo implements OnInit {
private login_info: any;
ngOnInit() {
this.auth.getUser$().subscribe(val => {
this.login_info = val;
console.log('print info', val.nickname);
});
}
}
public useInfo(status: string) {
console.log('print info', this.login_info.nickname);
}
So In the useInfo method, I can use the information that I grabbed from the user profile.
In angular 5.2.x for http get and post I had this code:
post(url: string, model: any): Observable<boolean> {
return this.http.post(url, model)
.map(response => response)
.do(data => console.log(url + ': ' + JSON.stringify(data)))
.catch(err => this.handleError(err));
}
get(url: string): Observable<any> {
return this.http.get(url)
.map(response => response)
.do(data =>
console.log(url + ': ' + JSON.stringify(data))
)
.catch((error: any) => Observable.throw(this.handleError(error)));
}
In angular 6 it doesn't work.
How can we make an HTTP post or get request?
Update :
In angular 7, they are the same as 6
In angular 6
the complete answer found in live example
/** POST: add a new hero to the database */
addHero (hero: Hero): Observable<Hero> {
return this.http.post<Hero>(this.heroesUrl, hero, httpOptions)
.pipe(
catchError(this.handleError('addHero', hero))
);
}
/** GET heroes from the server */
getHeroes (): Observable<Hero[]> {
return this.http.get<Hero[]>(this.heroesUrl)
.pipe(
catchError(this.handleError('getHeroes', []))
);
}
it's because of pipeable/lettable operators which now angular is able to use tree-shakable and remove unused imports and optimize the app
some rxjs functions are changed
do -> tap
catch -> catchError
switch -> switchAll
finally -> finalize
more in MIGRATION
and Import paths
For JavaScript developers, the general rule is as follows:
rxjs: Creation methods, types, schedulers and utilities
import { Observable, Subject, asapScheduler, pipe, of, from, interval, merge, fromEvent } from 'rxjs';
rxjs/operators: All pipeable operators:
import { map, filter, scan } from 'rxjs/operators';
rxjs/webSocket: The web socket subject implementation
import { webSocket } from 'rxjs/webSocket';
rxjs/ajax: The Rx ajax implementation
import { ajax } from 'rxjs/ajax';
rxjs/testing: The testing utilities
import { TestScheduler } from 'rxjs/testing';
and for backward compatability you can use rxjs-compat
You can do a post/get using a library which allows you to use HttpClient with strongly-typed callbacks.
The data and the error are available directly via these callbacks.
The library is called angular-extended-http-client.
angular-extended-http-client library on GitHub
angular-extended-http-client library on NPM
Very easy to use.
Traditional approach
In the traditional approach you return Observable<HttpResponse<T>> from Service API. This is tied to HttpResponse.
With this approach you have to use .subscribe(x => ...) in the rest of your code.
This creates a tight coupling between the http layer and the rest of your code.
Strongly-typed callback approach
You only deal with your Models in these strongly-typed callbacks.
Hence, The rest of your code only knows about your Models.
Sample usage
The strongly-typed callbacks are
Success:
IObservable<T>
IObservableHttpResponse
IObservableHttpCustomResponse<T>
Failure:
IObservableError<TError>
IObservableHttpError
IObservableHttpCustomError<TError>
Add package to your project and in your app module
import { HttpClientExtModule } from 'angular-extended-http-client';
and in the #NgModule imports
imports: [
.
.
.
HttpClientExtModule
],
Your Models
export class SearchModel {
code: string;
}
//Normal response returned by the API.
export class RacingResponse {
result: RacingItem[];
}
//Custom exception thrown by the API.
export class APIException {
className: string;
}
Your Service
In your Service, you just create params with these callback types.
Then, pass them on to the HttpClientExt's get method.
import { Injectable, Inject } from '#angular/core'
import { SearchModel, RacingResponse, APIException } from '../models/models'
import { HttpClientExt, IObservable, IObservableError, ResponseType, ErrorType } from 'angular-extended-http-client';
.
.
#Injectable()
export class RacingService {
//Inject HttpClientExt component.
constructor(private client: HttpClientExt, #Inject(APP_CONFIG) private config: AppConfig) {
}
//Declare params of type IObservable<T> and IObservableError<TError>.
//These are the success and failure callbacks.
//The success callback will return the response objects returned by the underlying HttpClient call.
//The failure callback will return the error objects returned by the underlying HttpClient call.
searchRaceInfo(model: SearchModel, success: IObservable<RacingResponse>, failure?: IObservableError<APIException>) {
let url = this.config.apiEndpoint;
this.client.post<SearchModel, RacingResponse>(url, model,
ResponseType.IObservable, success,
ErrorType.IObservableError, failure);
}
}
Your Component
In your Component, your Service is injected and the searchRaceInfo API called as shown below.
search() {
this.service.searchRaceInfo(this.searchModel, response => this.result = response.result,
error => this.errorMsg = error.className);
}
Both, response and error returned in the callbacks are strongly typed. Eg. response is type RacingResponse and error is APIException.
For reading full response in Angular you should add the observe option:
{ observe: 'response' }
return this.http.get(`${environment.serverUrl}/api/posts/${postId}/comments/?page=${page}&size=${size}`, { observe: 'response' });
I am trying to use the Auth0 for social login but I keep getting an exception of an undefined reference.
This is the authentication service
import { Injectable } from '#angular/core';
import { tokenNotExpired } from 'angular2-jwt';
// Avoid name not found warnings
declare var Auth0Lock: any;
#Injectable()
export class AuthService {
// Configure Auth0
lock = new Auth0Lock('I have set the ID correctly here', 'and the domain as well', {});
constructor() {
// Add callback for lock `authenticated` event
this.lock.on("authenticated", (authResult) => {
localStorage.setItem('id_token', authResult.idToken);
});
}
public login() {
// Call the show method to display the widget.
this.lock.show();
};
public authenticated() {
// Check if there's an unexpired JWT
// This searches for an item in localStorage with key == 'id_token'
return tokenNotExpired();
};
public logout() {
// Remove token from localStorage
localStorage.removeItem('id_token');
};
}
I injected the services and configured providers. Everything is wired correctly, but it just won't find Auth0Lock even though defined.
Each time it reaches lock = new Auth0Lock('ID', 'DOMAIN', {}); it bombs out.
I replaced declare var Auth0Lock: any; with const Auth0Lock = require('auth0-lock').default; and that fixed the problem.
The accepted answer is good. I did get a Cannot find name 'require' error.
Rather than using 'declare const require', I imported like so:
import Auth0Lock from 'auth0-lock';
I needed to add to index.html:
<script src="https://cdn.auth0.com/js/lock/10.8/lock.min.js"></script>
via https://github.com/auth0/lock/issues/588