Differentiate business and non-business logic - javascript

I'm trying to implement oauth process through google.
I have multiple scenarios of using it, so I have multiple endpoints where google-oauth auth process should be inited.
It looks like:
#Controller('auth/google')
export class GoogleAuthController {
#Get('register')
async register(
#Res() res: Response,
) {
res.redirect("oautgoogleurl.com?state=register")
}
#Get('login')
async login(
#Res() res: Response,
) {
res.redirect("oautgoogleurl.com?state=login")
}
#Get('link')
async link(
#Res() res: Response,
) {
res.redirect("oautgoogleurl.com?state=link")
}
}
And in every case I have almost the same url for redirection except the "state" parameter. I want to move this logic of forming url to some other place, where it should be?
Can it be private method inside controller (without get/post/etc decorators)?
#Controller('auth/google')
export class GoogleAuthController {
// ... all above code ...
private getRedirectUrl(state: string) {
return 'oautgoogleurl.com?state=' + state;
}
}
Or do I have to move into another place?
I also have GoogleAuthService with the same methods names as controller (register/login/link). Should I place "getRedirectUrl" here?
Or maybe create some additional GoogleAuthHelper class and place this logic here?

Related

How to cancel previews incomplete HTTP requests of the same type in a service in Angular 2?

I'm using Angular 9 in my web app. I'm using a lot of services to connect to a web service. Sometimes a lot of identical requests are sent to a service. Maybe when a user click on a button repeatedly.
I want to cancel previews incomplete requests for all of my services. What's the best solution? (Maybe using RXJS)
This is one of my service functions:
constructor(private http: HttpClient) {}
getList(options?: ApiListOptions) {
return this.http.post<ApiResponse<UserGet[]>>(
environment.apiRoot + 'user/list',
options,
{ headers: this.headers() }
);
}
Thanks
Please try below with takeUntil rxjs operator you will be able to do the same.
export class YourComponent {
protected ngUnsubscribe: Subject<void> = new Subject<void>();
[...]
public httpGet(): void {
this.http.get()
.takeUntil(this.ngUnsubscribe)
.subscribe( (data) => { ... })
}
public ngOnDestroy(): void {
this.ngUnsubscribe.next();
this.ngUnsubscribe.complete();
}
}
For your specific case, like frequent button clicks, you can use switchMap() combine with debounceTime() to limit the frequency of action and http calls. You can use Subject for action trigger instead of component method
<button click="getList.next()">click</button>
getList=new Subject()
getList.pipe(debounceTime(2000),switchMap(()=>
this.http.post<ApiResponse<UserGet[]>>(
environment.apiRoot + 'user/list',
options,
{ headers: this.headers() }
);
)).subscribe()

Nest.js - skip middleware based on header value

I have an application which uses nestjs and MiddlewareConsumer.
I would like to know if there's a way to skip a middleware based on a header value?
I checked documentation and saw that I can only use path or method (as I do now) but maybe there's something I'm missing?
Sample of my code:
export class AuthorizationModule implements NestModule {
configure(consumer: MiddlewareConsumer): void {
consumer.apply(DiscriminatorValidator).with(common.USERS).forRoutes(
{path: RELATIVE_RESOURCE_PATH, method: RequestMethod.POST},{path: RELATIVE_RESOURCE_PATH, method: RequestMethod.PUT});
consumer.apply(validate).forRoutes(AuthorizationController);
consumer.apply(HeadersValidator).with().forRoutes(AuthorizationController);
consumer.apply(ContextAndHeadersMiddleware).forRoutes(AuthorizationController);
}
}
This is not possible with the MiddlewareConsumer.
However, the middleware itself can check if its applicable or should be skipped:
#Injectable()
export class ContextAndHeadersMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: Function) {
if (req.get('my-header') === 'SKIP') {
// skip this middleware if header value is set
return next();
}
// middleware logic
}
}

How do I ensure operators on an observable occur after a HTTP Interceptor?

In my Angular 8 application, I have a basic caching interceptor:
export class CacheInterceptor implements HttpInterceptor {
constructor(private cache: CacheService) {}
public intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
if (req.method !== 'GET') {
return next.handle(req);
}
const cachedResponse = this.cache.get(req);
if (cachedResponse) {
console.log(cachedResponse);
return of(cachedResponse);
}
return next.handle(req).pipe(
filter(event => event instanceof HttpResponse),
map((response: HttpResponse<any>) => {
this.cache.addToCache(req, response);
return response;
})
);
}
}
I also have a service which retrieves data from an external API:
public getCases(options: ModuleArguments): Observable<CaseResponse> {
return this.http
.get<CaseResponse>(this.URL_BASE, {
params: options as HttpParams
})
.pipe(map(this.cleanData, this));
}
The 'cleanData' method just loops through the received data and amends some of the values to make them more human friendly (e.g. turns 'support_request' to 'Support Request').
What appears to be happening is the response is being added to the cache by the CacheInterceptor after the data has been 'cleaned' within the service. Therefore, when the same request is made again, and received from the cache, the service is attempting to clean data which has already been cleaned.
How do I ensure that the the HTTP Response has been intercepted and added to the cache before it has been amended by the service?
How about you approach this by moving the pipe(map(this.cleanData, this)) operation to the point when the Observable has completed and returned your CaseResponse. Likely, this will mean that the HttpInterceptor has been applied first.
i.e. In the place where you invoke getCases you could try something like this:
service.getCases(options).subscribe(resolvedData => {
// assuming cleanData(data: CaseResponse) signature
const cleanedData = this.cleanData(resolvedData);
// .. do something with cleanedData
});
Also, from a design perspective, you wouldn't want getCases to do more than exactly what it's supposed to - It's a service method that performs an HTTP request and returns the cases in the format they are sent to you. The reformatting of the data could be ideally done at the consumer of that service function - as it's very likely the consumer that needs it cleaned/reshaped.

Angular - Display server error in component

I'm currently working on a register page and therefore, I need to post my data to the server. The client-side validation and server-validation works. I know I can handle the client-side errors like *ngIf="(emailAddress.errors?.required || emailAddress.errors?.email) && emailAddress.touched". But how to handle the server-side errors?
In my service, if an error occurs, I simply give it into the component by return throwError(error);
But how can I know to display the specific error in my component if for example there is already someone with this email address? And how do distinguish between email/password validation errors server side?
Depends on the complexity and modules you have.
If you use any kind of state management library such as ngrx or ngxs, I suggest you do as follow:
Define State with a property 'error' which keeps track of the latest server-error.
Api call executed via actions and error is caught and stored to state also via actions. (Do whatever error mapping before saving the error to your State)
Use selectors for component to receive error stream from State.
If you don't have any state management library, you can create a BehaviorSubject within singleton service, and use it to publish server-error as soon as you got into any catchError context.
This way you can write your own http interceptor and have your response inside your component. Also in your component you know your http response is success or error.
You can implement other Http calls(GET, PUT, etc) and also handle your general errors in handleError() function.
app.module.ts
export class AppModule {
constructor() {}
imports: [
HttpClientModule,
],
providers: [
{
provide: Http,
useFactory: httpFactory,
deps: [HttpHandler]
},
],
}
export function httpFactory(httpHandler: HttpHandler) {
return new Http(httpHandler);
}
http.service.ts
export class Http extends HttpClient {
constructor(handler: HttpHandler) {
super(handler);
}
postCall(url: string, body, options?): Observable<any> {
return super.post(url, body, options)
.pipe(
tap(response => { console.log(response) }),
catchError(this.handleError(error)));
}
private handleError<T>(result?: T) {
return (error: any): Observable<T> => {
console.log(error);
return of(result as T);
};
}
}
your.component.ts
export class YourComponent {
constructor(private http: Http) {}
this.http.postCall('URL', {}).subscribe(response => {
if (response instanceof HttpErrorResponse) {
// Your Response is error
} else {
// Your Response is your desired result
}
}

Angular 2 subject is not a function

Trying to communicate with 2 components
I thought I'd be able to make a http call, then maybe mergeMap or switchMap to a subject?
Something like
import {Subject} from 'rxjs/Subject';
constructor(private _http: HttpClient) {
this.populateList = new Subject<Blog[]>();
}
getBlogs(){
return this._http.get(this.blogsURL+'blogs')
.map((result: Response ) => {
this.blogs = result['blogs'];
return this.blogs;
}).switchMap((blogs)=>this.populateList.next(blogs))
}
But I get:
You provided 'undefined' where a stream was expected. You can provide
an Observable, Promise, Array
I'm getting errors just trying to subscribe to the subject:
this.blogsService.populateList()
.subscribe((res)=>{
console.log(res)
})
this.blogsService.populateList is not a function
What I'm looking for is a way to update views after http calls
You need to subscribe like this without (). Cause its not a function. surprise
this.blogsService.populateList.subscribe()
and rewrite first function like this cause you dont need switch map you just need to do is side effect to populate list.
getBlogs(){
return this._http.get(this.blogsURL+'blogs')
.map((result: Response ) => {
this.blogs = result['blogs'];
return this.blogs;
}).do((blogs)=>this.populateList.next(blogs))
}

Categories