In NestJS, how to get execution context or request instance in custom method decorator? - decorator

I have a custom method decorator like this.
export function CustomDecorator() {
return applyDecorators(
UseGuards(JwtAuthGuard)
);
}
Inside the Custom Decorator, I want to get the Request Header but not sure how to get the Request Instance?

You won't be able to get the ExectuionContext object or the Request object in a class or method decorator, because these decorators are run immediately at the moment of import. What should be done instead is to make a SuperGuard that does have the ExecutionContext available to it. This SuperGuard should have all of the other guards injected into it via the constructor and depending on the header you should call/return the result from the guard called. Something like this:
#Injectable()
export class SuperGuard implements CanActivate {
constructor(
private readonly jwtAuthGuard: JwtAuthGuard,
private readonly googleAuthGuard: GoogleAuthGuard,
) {}
canActivate(context: ExecutionContext) {
const req = context.switchToHttp().getRequest();
if (req.headers['whatever'] === 'google') {
return this.googleAuthGuard.canActivate(context);
} else {
return this.jwtAuthGuard.canActivate(context);
}
}
}

I managed to access the execution context within decorator using Inject inside decorator's factory.
Here is my decorator that swallows errors produced by method and returns predefined value in case of exception.
import { Injectable, Scope, Inject, ExecutionContext } from '#nestjs/common';
import { CONTEXT } from '#nestjs/graphql';
#Injectable({ scope: Scope.REQUEST })
export class ExceptionsHandler {
public constructor(#Inject(CONTEXT) private readonly context: ExecutionContext) {}
private integrationsRequestErrors: unknown[] = [];
public handle(error: unknown): void {
// ADD error to context if necessary
this.integrationsRequestErrors.push(error);
}
}
export const ErrorSwallower = (options: {
serviceImplementation: string;
defaultValue: unknown;
errorMessage?: string;
}): MethodDecorator => {
const { defaultValue, integration } = options;
const Injector = Inject(ExceptionsHandler);
return (target: object, _propertyKey: string, descriptor: PropertyDescriptor) => {
Injector(target, 'exceptionsHandler');
const originalMethod = descriptor.value;
descriptor.value = function (...args: unknown[]) {
const exceptionHandler = this.experiment as ExceptionsHandler;
try {
const result = originalMethod.apply(this, args);
if (result && result instanceof Promise) {
return result.catch((error: unknown) => {
exceptionHandler.handle({ error, integration });
return defaultValue;
});
}
return result;
} catch (error) {
exceptionHandler.handle({ error, integration });
return defaultValue;
}
};
};
};
and here is the code above put into action:
#Injectable()
export class ExampleService {
#ErrorSwallower({ serviceImplementation: 'ExampleClass', defaultValue: [] })
private async getSomeData(args: IGetSomeDataArgs): Promise<ISomeData[]> {
throw new Error('Oops');
}
}

Related

Http POST response undefined - how to get an object from controller by using Ok(object) method

I think I have got a newbie question, but I searched over the Internet - no result.
So I'm calling a controller with a POST method with given parameters (weight and height) and I expect to receive a status code Ok(result) with an object inside it.
The method is called properly, I receive sth from the method, but the result is "undefined". I tried to tell the POST method to expect JSON results, by giving a header, but no result. I mean, I receive an Object, but I don't know why it's not mapped correctly and thus, the result is not shown as it should.
I was expecting, that response will be type Result, like in the class defined and I can freely read from it, but no.
That's the response I get
{"bmiClassification":0,"result":4.03,"summary":"To be done"}
Here is controller class I'm calling BMICalculatorController.cs
[ApiController]
[Route("[controller]")]
public class BMICalculatorController : ControllerBase
{
private readonly IBMICalculatorLogic _calculator;
private readonly ITest _test;
public BMICalculatorController(IBMICalculatorLogic calculator)
{
_calculator = calculator;
}
[HttpPost]
[Route("calc")]
public IActionResult Calculate([FromBody] ParametersDto parameters)
{
var result = _calculator.GetResult(parameters.Weight, parameters.Height);
return Ok(result);
}
}
}
Here is typescript component I'm working on:
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Component, Inject, OnInit } from '#angular/core';
import { ParametersDto } from '../models/ParametersDto';
import { Results } from '../models/Results';
#Component({
selector: 'app-bmicalculator',
templateUrl: './bmicalculator.component.html',
styleUrls: ['./bmicalculator.component.css']
})
export class BmicalculatorComponent implements OnInit {
public parameters: ParametersDto = new ParametersDto;
public result: number = 0.0;
public text: string = "Default text";
public results: Results = new Results();
constructor(private http: HttpClient, #Inject('BASE_URL') baseUrl: string) {}
ngOnInit(): void {
}
sendRequest() {
this.http.post<Results>('https://localhost:44431/' + 'bmicalculator' + '/calc', this.parameters,
{ headers: new HttpHeaders().set('Content-Tye', 'application/json') }).
subscribe(response => {
this.results = response;
this.result = this.results.Result;
}, error => console.error(error));
}
}
Below is a class Result I expect to receive:
export class Results {
public Classification: BMIClassification = 1;
public Result: number = 0.0;
public Summary: string = "";
}
enum BMIClassification {
Underweight,
Normal,
Overweight,
Obesity,
ExtremeObesity
}
Here is class of result that controller returns:
public class BMIResult
{
public BMIClassification? BMIClassification { get; set; }
public double Result { get; set; }
public string? Summary { get; set; }
}
and here is enum used in the class above
public enum BMIClassification
{
Underweight,
Normal,
Overweight,
Obesity,
ExtremeObesity
}
Most probably, I messed up sth with in the Typescript, but I don't know where... Please give me any hint ! :)
[SOLVED]
I changed slightly the way how I read result and it worked :) Code below
sendRequest() {
this.http.post<Results>('https://localhost:44431/' + 'bmicalculator' + '/calc', this.parameters).
subscribe(response => {
this.result = (response as any).result;
}, error => console.error(error));
}
}

Recursion using Flux

I have a scenario, where I am using the State pattern. Every Start has an Action defined. We start with an initial state and an initial Action and then keep going until the Action becomes null. If the action becomes null, then we stop the execution. Also, the transition between the states is not sequential. We execute the Action on the State that is returned by the previous state.
To replicate the same, I have implemented the below recursion -
SelectionMachine
private Flux<SelectionState> run(OfferSelectionRequest request) {
Flux<SelectionStateResult> initialSelectionStateResult = Flux.just(new SelectionStateResult(selectionRequested, ACTION_START, new SelectionContext(request)));
return go(initialSelectionStateResult);
}
private Flux<SelectionState> go(Flux<SelectionStateResult> resultFlux) {
val result = resultFlux.flatMap(selectionStateResult -> {
if (selectionStateResult.getAction().isEmpty())
return Flux.just(selectionStateResult);
else {
selectionStateResult.getAction().get();
return go(move(selectionStateResult.getState(), selectionStateResult.getAction().orElse(null), selectionStateResult.getContext().getRequest()));
}
});
return result;
}
private Flux<SelectionStateResult> move(SelectionState state, Action action, OfferSelectionRequest request) {
Flux<SelectionStateResult> result = Flux.empty();
if (ACTION_START.equals(action)) {
result = state.start(request);
} else if (ACTION_FETCH_OFFERS.equals(action)) {
state.fetchOffers(request);
} else {
result = Flux.just(new SelectionStateResult(state));
}
return result;
}
SelectionStateResult
package com.paytmlabs.adtech.adtechdecisionengine.domain.selection;
import com.paytmlabs.adtech.adtechdecisionengine.domain.result.OfferResult;
import com.paytmlabs.adtech.adtechdecisionengine.exception.SelectionError;
import com.paytmlabs.adtech.adtechdecisionengine.selection.machine.InternalSelectionStateMachine;
import com.paytmlabs.adtech.adtechdecisionengine.selection.state.SelectionState;
import lombok.Data;
import java.util.List;
import java.util.Optional;
#Data
public class SelectionStateResult {
private SelectionState state;
private Optional<InternalSelectionStateMachine.Action> action;
private SelectionContext context;
private SelectionError error;
private List<OfferResult> selectedOffers; // TODO: OfferResult needs to be created
public SelectionStateResult(SelectionState state) {
this.state = state;
this.action = Optional.empty();
}
public SelectionStateResult(SelectionState state, InternalSelectionStateMachine.Action action) {
this.state = state;
this.action = Optional.ofNullable(action);
}
public SelectionStateResult(SelectionState state, InternalSelectionStateMachine.Action action, SelectionContext context) {
this.state = state;
this.action = Optional.ofNullable(action);
this.context = context;
}
public SelectionStateResult(SelectionState state, SelectionError error) {
this.state = state;
this.action = Optional.empty();
this.context = null;
this.error = error;
}
public SelectionStateResult(SelectionState state, SelectionContext context) {
this.state = state;
this.action = Optional.empty();
this.context = context;
this.error = null;
}
}
When I try to do this, I am getting an error in the go method-
no instance(s) of type variable(s) R exist so that SelectionState conforms to Publisher<? extends R>
Need some guidance on how can I implement the following use case -
We start from an initial state and action.
After the state terminates, it returns a state and an action.
If action and state are not null, we execute the action of the returned state.
If the action is null, we just return the state and that is the end of the execution.

How to implement class in TypeScript?

Could you help me to create class 'MyClass'
class M should be Newable and implement IMyInterface
export interface IMyInterface<A>
{
SomeData : A;
}
export class MyClass<T,M inherits IMyInterface<T> and new() >
{
list = new Array<M>();
privete Creator()
{
const obj = new M();
obj.SameData = 'Hello data';
list.push( obj );
}
}
Think you're looking for something like this...
export interface IMyInterface<A> {
SomeData: A;
}
export class MyClass<T, M extends IMyInterface<T>> {
private list: M[] = [];
constructor(private constructorFunction: {new(): M; }) {
}
public add(item: T): void {
const obj = new this.constructorFunction();
obj.SomeData = item;
this.list.push(obj);
}
}
export class MyItem implements IMyInterface<string> {
public SomeData: string = '';
}
const collection = new MyClass<string, MyItem>(MyItem);
collection.add('Hello data');
I've tweaked your psuedocode so that it actually compiles and does what I think you were aiming for in the question. The important thing to note is that the types in TypeScript have no representation at runtime so you can't do new T(). Instead you need to pass in the constructor function for your class which has a type of { new(): M; }. You can then do a new X() with this value to get an object which extends your interface.

Using Provider Inside firebase.auth().onAuthStateChanged return undefined

I try to call to a function that it a part from Provider, but I noticed that when
the call is inside firebase.auth().onAuthStateChanged function all the providers are undefined.
#Ionic3.9.2
#cordova-8.1.2 (cordova-lib#8.1.1)
That's my Code :
constructor(public global:global,public navCtrl: NavController, public navParams: NavParams, public app: App, public dataProvider: DataProvider,
public loadingProvider: LoadingProvider, public alertProvider: AlertProvider, public alertCtrl: AlertController, public firebaseProvider: FirebaseProvider, public modalCtrl: ModalController) { }
ionViewDidLoad() {
console.log(this.dataProvider);
firebase.auth().onAuthStateChanged(function (user) {
if (user) {
// User is signed in.
console.log(user);
this.currentUser=user;
this.tab = "friends";
this.title = "Friends";
this.searchFriend = '';
console.log(this.dataProvider); /* -> undefined*/
if (this.dataProvider.getRequests(this.currentUser != null)) { /* the console return to me an error :TypeError: Cannot read property 'getRequests' of undefined */
console.log(this.dataProvider.getRequests(this.currentUser));
// console.log(this.dataProvider.getRequests(firebase.auth().currentUser.uid));
console.log("if ");
// console.log(this.currentUser.uid);
console.log(this.currentUser);
this.dataProvider.getRequests(this.currentUser.uid).snapshotChanges().subscribe((requestsRes) => {
let requests = requestsRes.payload.val();
console.log(requests);
if (requests != null) {
if (requests.friendRequests != null && requests.friendRequests != undefined)
this.friendRequestCount = requests.friendRequests.length;
else this.friendRequestCount = 0
}
else this.friendRequestCount = 0;
console.log(this.friendRequestCount);
});
this.getFriends();
}
} else {
// No user is signed in.
console.log('no User');
}
});
I am sure that I miss something and be grateful for some help Thank you!
Ok I found the solution
Explanation:
It's all about "this" keyword
this.dataProvider in the callback is not the same this.dataProvider outside the callback function.
To refer to the same this.dataProvider inside and outside the callback function, you just need to create a function and pass the dataProvider as a parameter.
constructor(public global:global,public navCtrl: NavController, public navParams: NavParams, public app: App, public dataProvider: DataProvider,
public loadingProvider: LoadingProvider, public alertProvider: AlertProvider, public alertCtrl: AlertController, public firebaseProvider: FirebaseProvider, public modalCtrl: ModalController) {
this.ionViewDidLoad(this.dataProvider); // <--- with this (mandatory)
}
ionViewDidLoad(dataProvider: DataProvider) {
console.log(dataProvider); // <-- defined with or without this
firebase.auth().onAuthStateChanged(function (user) {
.........
console.log(dataProvider); /* without this -> NOT undefined anymore :) */
})

HTTP : Detect changes in child component when update/save/delete

I'm learning HTTP as I go. Unsure how to move forward. I'm doing http calls only in my service.
Then I have a class, Teacher, here's some of the methods:
export class Teacher {
public addStudent(value: Student): void {
this.students.push(value);
}
}
I have a list component that lists teachers, and in that component, the user can click a teacher and move to a detail-page, where it takes user input and adds students to the teacher.
export class TeacherDetailComponent implements OnActivate {
teacher: Teacher;
constructor(public _service: Service, public _router: Router) { }
routerOnActivate(curr: RouteSegment): void {
let id = curr.getParam('id');
this._service.getById(id)
.subscribe(teacher => {
this.teacher = teacher;
});
}
addStudent() {
this.teacher.getStudents().push(new Student());
//what code here?
}
}
There is my headscratcher, how and where do I tell Angular that to update the data for the teacher when a new student is added!
In fact your question is related to component communication. I would create a shared service for this.
See this doc for more details:
https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service
So I would create a service to notify the list component that a student is added or remove, so it can update the list accordingly. Here is a sample:
#Injectable()
export class StudentService {
userAdded:Subject<Student> = new Subject();
userDeleted:Subject<Student> = new Subject();
constructor(private http:Http) {
}
addStudent(student:Student) {
return this.http.post('/users', ...)
(...)
.do((addedStudent) => {
this.userAdded.next(addedStudent);
});
}
deleteStudent(student:Student) {
return this.http.post('/users', ...)
(...)
.do((removedStudent) => {
this.userRemoved.next(removedStudent);
});
}
}
So you can update your details component:
addStudent() {
let newStudent = new Student();
this.studentService.addStudent(newStudent).subscribe(addedStudent => {
this.teacher.getStudents().push(addedStudent);
});
}
In the list component:
this.studentService.addedStudent.subscribe(addedStudent => {
// do something
});

Resources