I have a NestJS application that uses passport, and I'd like to add a #User param decorator to get the user in the request more easily, similarly to this example.
However, I also have a global validation pipe that I apply for all incoming requests (bodies, headers, query string), and apply different validation depending on the declared body/query/header type. For some reason, when using the createParamDecorator() function, NestJS applies the validator to the req.user object. That is wrong however. The passport user is not part of the request contents, so it shouldn't be validated like that.
Short of copy&pasting createParamDecorator()'s source, and stripping out the line that applies the pipes, is there a way to turn off pipes, or at least make my validation pipe ignore non-request related types?
My validation pipe, for reference:
import { ArgumentMetadata, BadRequestException, Injectable, PipeTransform } from '#nestjs/common';
import { ValidationService } from './validation.service';
#Injectable()
export class ValidationPipe<T extends unknown> implements PipeTransform {
public constructor(private readonly service: ValidationService) {}
public async transform(value: T, metadata: ArgumentMetadata): Promise<T> {
const className: string | undefined = metadata.metatype && metadata.metatype.name;
if (className) {
const errors = await this.service.validate(value, className);
if (errors && errors.length > 0) {
throw new BadRequestException(errors);
}
}
return value;
}
}
The one thing I don't want is to modify the above (or the service) to blacklist/whitelist types, as those will be changing rapidly as the application evolves, so it would be big pain point to add them to such lists.
After making a PR with a change that was supposed to fix this, it seems for my particular problem, I could use metadata.type, like so:
import { ArgumentMetadata, BadRequestException, Injectable, PipeTransform } from '#nestjs/common';
import { ValidationService } from './validation.service';
#Injectable()
export class ValidationPipe<T extends unknown> implements PipeTransform {
public constructor(private readonly service: ValidationService) {}
public async transform(value: T, metadata: ArgumentMetadata): Promise<T> {
if (metadata.type !== 'custom') {
const className: string | undefined = metadata.metatype && metadata.metatype.name;
if (className) {
const errors = await this.service.validate(value, className);
if (errors && errors.length > 0) {
throw new BadRequestException(errors);
}
}
}
return value;
}
}
And the PR in question doesn't even do what I thought it does, so it doesn't really work either.
Related
I'm trying to write test cases around a component that contains my FullCalendar component in Angular.
I've written karma tests using a MockFullCalendar that extends FullCalendar and have been successfully able to mock the getApi().view calls for getting fake active start and active end dates. But when I use the same method for getApi().getResources() and getApi.getResourceById(id) I get this error when running tests
error TS2339: Property 'getResourceById' does not exist on type 'Calendar'.
const calendarResource = this.calendar.getApi().getResourceById(resourceIdAssignedToEvent);
Here are my mock classes
export class MockFullCalendarComponent extends FullCalendarComponent {
getApi(): any {
return {};
}
}
export class MockCalendarApi extends CalendarApi {
getResources(): any {
return [];
}
getResourceById(resourceId: any): any {
return {};
}
}
export class MockCalendar extends MockCalendarApi {
get view(): any {
return {
get activeStart(): any {
return new Date();
},
get activeEnd(): any {
return new Date();
}
};
}
}
The following is how I was able to successfully mock the getApi().view
export class MockFullCalendarComponent extends FullCalendarComponent {
getApi(): any {
return {
view: {
activeStart: new Date(),
activeEnd: new Date()
}
};
}
}
Using the same pattern somehow does not work for the Calendar and CalendarApi classes. I'm not sure if this is because there is a class and an interface of the same name (CalendarApi) in FullCalendar common and only the interface contains these two methods.
I'm using Angular 12 and FullCalendar V5.
I've tried mocking as described above, tried different ways to provide my mock classes to the TestBed for injection.
My test case does not run at all, because these methods aren't detected somehow, none of the existing tests run at all, its almost like the whole initialization of the test case fails at this and I'm not sure how else I can force the spec to use my mock classes that contains mock implementations of those methods.
Its possible to use Hoc with context api inside a next page?
I have a next page generated by SSR, and a HOC privateRoute to validate authorization on this page. But for every access, we have a authorization request and its sound's me like a problem.
My idea is to use contexApi to get data one time, and reuse that on auth private route.
Anyone has a minimal exemple about?
Thanks.
I found a solution.
The problem was to use Context inside react class component.
mport React, { Component } from 'react'
import UserContext from './UserContext'
class HomePage extends Component {
static contextType = UserContext
componentDidMount() {
const user = this.context
console.log(user) // { name: 'Tania', loggedIn: true }
}
for more, see https://www.taniarascia.com/using-context-api-in-react/
render() {
return <div>{user.name}</div>
}
}
I currently have this code:
package sage.sys;
import com.dongxiguo.continuation.Async;
#if flash
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.URLLoaderDataFormat;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;
#end
class FileSystem implements Async
{
public static function fetchText(url:String, callback:String->Void) : Void
{
var urlLoader = new URLLoader();
var onLoaderError = function(e : Event) : Void {
callback(e.type);
};
urlLoader.addEventListener(Event.COMPLETE, function(_) : Void {
callback(Std.string(urlLoader.data));
});
urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onLoaderError);
urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onLoaderError);
try {
urlLoader.dataFormat = URLLoaderDataFormat.TEXT;
urlLoader.load(new URLRequest(url));
}
catch (e : Dynamic)
{
callback(Std.string(e));
}
}
#async
public static function fetch(url:String):String
{
var result = #await fetchText(url);
return result;
}
}
When I try to run it, it doesn't wait at the await, and returns from the function. How can I make the #await actually enforce itself and and stop running outside the function till the value is resolved from the async call?
Lib used: https://github.com/proletariatgames/haxe-continuation
You can't really. The function, in which you call fetchText() will never "wait" in that sense.
However according to the documentation of "haxe-continuation" it will put everything after your #await FileSystem.fetchText() expression into a new function, which will be passed as the callback parameter of fetchText(). So in code it kind of "looks" like it is waiting.
According to the documentation you have to make sure that you put #async in front of the function which uses fetchText(). According to that something like this should work (untested):
#async function someFunction(): Void {
var result1 = #await FileSystem.fetchText(someUrl);
var result2 = #await FileSystem.fetchText(anotherUrl);
trace(result1);
trace(result2);
}
The 2 traces at the end of the function should happen after result1 and result2 are fetched, even though someFunction() actually returned before the traces already.
It might be helpful to see your code which calls fetchText().
How did you use the fetch function?
I suppose you should put a callback to it:
FileSystem.fetch(url, function(result) trace(result));
The fetch call itself should return immediately, because it is async and the result will be passed to the callback.
I don't think it is possible to make flash block at the fetch call.
In my app I have 2 components (Page and Home).
Home extends Page like so:
export default class Home extends Page
and Page just extends Component.
In Page I have a method to get user data from AsyncStorage
async getUser() {
// get from storage
this.setState({user});
}
The above is called on the constructor of Page.
The problem I have is that Home has a method on componentWillMount that relies on this.state.user. Obviously this isn't going to work since the getUser method is async.
Is there a way I can get the user information and only call specific methods once I have that info?
export default class Page extends Component {
async getUser() {
// get from AsyncStorage
this.setState({user});
}
componentWillMount() {
this.getUser();
}
}
export default class Home extends Page {
async foo(user_id) {
this.setState({something});
}
componentWillMount() {
this.foo(this.state.user);
}
render() {
return <Text>{this.state.something}</Text>;
}
}
Is there a way I can get the user information and only call specific
methods once I have that info?
Not in a general sense, but of course there are some solutions. What this comes down to is that you have an async data dependency. You need to write the code in such a way that a call to dependent functions is only made after the data becomes available.
Perhaps the easiest way to do this is to use componentWillUpdate instead of componentDidMount and check if you are receiving the required data:
componentWillUpdate(nextProps, nextState) {
if (nextState.user != null && this.state.user !== nextState.user) {
this.prepareForUser(nextState.user);
}
}
(Note that you can't setState directly in componentWillUpdate, but since your method is async it won't happen until later. You can use componentDidUpdate to chain another setState call.)
Another option (which I like to use) is to use composition instead of inheritance. This makes the life-cycle easier to control through rendering: the parent can only render a child when all the child's dependent data is loaded, then the child does not need to worry about any timing issues related to the initial loading of data and can do whatever it wants from its componentDidMount:
class UserContainer extends Component {
state = {};
componentDidMount() {
this.getUser();
}
async getUser() {
// get user from async store
this.setState({user});
}
render() {
return (
<div>
{ this.state.user ? <UserView user={this.state.user} /> : <Spinner /> }
</div>
);
}
}
class UserView extends Component {
componentDidMount() {
this.props.user != null
}
}
This is pretty much the "container component" pattern.
In my Angular 2 (beta 14) application I need to keep track of user login status in order to hide/display certain elements.
The issue I'm getting is that property binding is not working the way I did as follows.
I created a class to store and update global variables:
app-global.ts
import {Injectable} from "angular2/core";
#Injectable() export class AppGlobals {
// use this property for property binding
public isUserLoggedIn: boolean = false;
setLoginStatus(isLoggedIn){
this.isUserLoggedIn = isLoggedIn;
}
getLoginStatus(){
return this.isUserLoggedIn;
} }
In the login component I import AppGlobals
export class LoginComponent {
constructor(private _appGlobals: AppGlobals) { }
and set login state by
this._appGlobals.setLoginStatus(true);
In another component I inject AppGlobals as I do in LoginComponent
I define a class (component)'s property
isLoggedIn: boolean = this._appGlobals.isUserLoggedIn; // I also tried by using the getter instead of the public property (see above)
which I then use in the component's template to show/hide a certain element:
<!-- here I also tried with {{!isLoggedIn}} but results in a syntax error whereas using [(hidden)] instead of [hidden] changes nothing -->
<div id="some-element" [hidden] = "!isLoggedIn">
Finally, the binding works but there is no update (this component is part of AppComponent template and shown in every page) when another component (e.g. LoginComponent) sets the login status.
EDIT I tried to apply Gunter's answer but I get the following errors:
app/app-globals.ts(10,54): error TS2346: Supplied parameters do not match any signature of call target.
app/app-globals.ts(13,29): error TS2339: Property 'emit' does not exist on type 'BehaviorSubject<boolean>'.
Error at line 10 comes from [SOLVED]
public isUserLoggedIn:BehaviorSubject = new BehaviorSubject().startWith(false);
and it's apparently caused by BehaviorSubject expecting 1 parameter
Error at line 13 comes from
this.isUserLoggedIn.emit(isLoggedIn);
and it's apparently cause by a non-existing emit method.
Also, I don't understand how shall I use AppGlobals so that the property binding auto updates in another component (see last example before EDIT)
Further, in LoginComponent I replaced isLoggedIn boolean type with BehaviorSubject because isUserLoggedIn has type BehaviorSubject in AppGlobals
but
this._appGlobals.isUserLoggedIn.subscribe(value => this.isLoggedIn = value);
returns a TypeError:
Assigned expression type boolean is not assignable to type BehaviorSubject
isLoggedIn: boolean = this._appGlobals.isUserLoggedIn;
is a one-time action that copies the value at the time when this line is executed. If you want subsequent changes to be propagated use observables
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/share';
import 'rxjs/add/operator/startWith';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
#Injectable()
export class AppGlobals {
// use this property for property binding
public isUserLoggedIn:BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
setLoginStatus(isLoggedIn){
this.isUserLoggedIn.next(isLoggedIn);
}
}
and use it like:
export class LoginComponent {
constructor(private _appGlobals: AppGlobals) {
this._appGlobals.isUserLoggedIn.subscribe(value => this.isLoggedIn = value);
}
See also https://stackoverflow.com/a/35568924/217408
had a similar need, ended up implementing this via simple getter - and it binds the property in the template (--> changes propagate). less code.
you can set the global var either directly or implement setters/getters in the Globals class.
globals.ts
export class Globals{
public static server_call_in_progress:boolean = true;
}
app.component.ts
import {Globals} from "./shared/globals";
export class AppComponent{
constructor(){}
ngOnInit(){}
get server_call_in_progress(){
return Globals.server_call_in_progress;
}
}
app.component.html
<div *ngIf="server_call_in_progress">
<div class="loader"></div>
</div>