Can somebody explain what is wrong with the following?
void main() {
var intercept = new HttpInterceptor();
intercept.response = (HttpResponse response) => print('we have a response');
Injector injector = ngBootstrap(module: new MyModule());
var interceptors = injector.get(HttpInterceptors);
interceptors.add(intercept);
}
Thanks in advance
EDIT:
And here is how I do an http request (from inside a directive):
#NgDirective(
selector: '.my-selector')
class MyDirective implements NgAttachAware {
#NgOneWay('id')
String id;
Element self;
MyDirective(Element el) {
self = el;
}
void attach() {
final String _urlBase = 'http://www.some-site.com/';
HttpRequest req = new HttpRequest();
req.open('GET', _urlBase + id);
req.overrideMimeType('text\/plain; charset=x-user-defined');
req.onLoadEnd.listen((e) {
if (req.status == 200) {
// do something useful
} else {
print('download failed');
}
});
req.send();
}
}
Requests return successfully but the interceptor never fires. Why?
You must use the AngularDart built-in service "Http" and not the dart:html HttpRequest class.
The HttpInterceptors only work on when using that service which wraps the HttpRequest class.
In other words, inject Http in the directive´s constructor:
MyDirective(Element el, Http http) {
self = el;
this._http = http;
}
Related
I call the constructor of OAuth2Strategy in app.controller.ts, i send it the 2 params it needs: options with clientID, callbackURL etc, and a verify callback function. But i have this error, looks like i don't send a verify callback function to the constructor, but i did. The error happens in node_modules/passport-oauth2/lib/strategy.js.
app.controller.ts:
import { Body, Controller, Get, Post, Query, Res, Req, Next, UnauthorizedException, UseGuards } from '#nestjs/common';
import { PartieService, JoueurService, CanalService } from './app.service';
import { Joueur, Partie, Canal } from './app.entity';
import { AuthGuard } from '#nestjs/passport';
import passport = require("passport");
import OAuth2Strategy = require("passport-oauth2");
//var OAuth2Strategy = require('passport-oauth2').OAuth2Strategy;
//import OAuth2Strategy from 'passport-oauth2';
#Controller()
export class AppController {
constructor(private readonly partieService: PartieService, private readonly joueurService: JoueurService,
private readonly canalService: CanalService) {}
#Get('/oauth2')
async oauth2(#Req() req, #Res() res, #Next() next) {
passport.use('oauth2', new OAuth2Strategy(
{
clientID: 'my_clientID',
clientSecret: 'my_clientSecret',
authorizationURL: 'https://api.abc.com/oauth/authorize',
tokenURL: 'https://api.abc.fr/oauth/token',
callbackURL: 'http://localhost:3000/Connection'
},
async (accessToken: string, refreshToken: string, profile: any, done: Function) => {
//console.log(profile);
try {
if (!accessToken || !refreshToken || !profile) {
return done(new UnauthorizedException(), false);
}
const user: Joueur = await this.joueurService.findOrCreate(profile);
return done(null, user);
} catch (err) {
return done(new UnauthorizedException(), false);
}
} ));
passport.authenticate('oauth2');
return res.sendFile("/home/user42/Documents/Projets/ft_transcendence/services/pong/pong/public/connection.html");
}
#Get('/Connection')
//#UseGuards(AuthGuard('oauth2'))
async Callback(#Req() req, #Res() res, #Next() next) {
passport.authenticate('oauth2', (err, user, info) => {
if (err || !user) {
req.session.destroy();
return res.status(401).json({
message: 'Unauthorized',
});
}
req.user = user;
return next();
})(req, res, next);
const utilisateur: Joueur = req.user;
console.log(utilisateur);
}
[...]
}
node_modules/passport-oauth2/lib/strategy.js:
function OAuth2Strategy(options, verify) {
if (typeof options == 'function') {
verify = options;
options = undefined;
}
options = options || {};
if (!verify) { throw new TypeError('OAuth2Strategy requires a verify callback'); }
[...]
}
Error:
[Nest] 6550 - 05/02/2023, 18:34:54 ERROR [ExceptionHandler] OAuth2Strategy requires a verify callback
TypeError: OAuth2Strategy requires a verify callback
at new OAuth2Strategy (/home/user42/Documents/Projets/ft_transcendence/services/pong/pong/node_modules/passport-oauth2/lib/strategy.js:84:24)
at Injector.instantiateClass (/home/user42/Documents/Projets/ft_transcendence/services/pong/pong/node_modules/#nestjs/core/injector/injector.js:340:19)
at callback (/home/user42/Documents/Projets/ft_transcendence/services/pong/pong/node_modules/#nestjs/core/injector/injector.js:53:45)
at Injector.resolveConstructorParams (/home/user42/Documents/Projets/ft_transcendence/services/pong/pong/node_modules/#nestjs/core/injector/injector.js:132:24)
at Injector.loadInstance (/home/user42/Documents/Projets/ft_transcendence/services/pong/pong/node_modules/#nestjs/core/injector/injector.js:57:13)
at Injector.loadProvider (/home/user42/Documents/Projets/ft_transcendence/services/pong/pong/node_modules/#nestjs/core/injector/injector.js:84:9)
at async Promise.all (index 3)
at InstanceLoader.createInstancesOfProviders (/home/user42/Documents/Projets/ft_transcendence/services/pong/pong/node_modules/#nestjs/core/injector/instance-loader.js:47:9)
at /home/user42/Documents/Projets/ft_transcendence/services/pong/pong/node_modules/#nestjs/core/injector/instance-loader.js:32:13
at async Promise.all (index 1)
`
I tried to change the import OAuth2Strategy many times (like the commented lines). I tried a lot of things but i cannot remember all. I found no answer in internet, and no more with the not-so-well-chatgpt.
I am new in nestjs and this error is weird for me, how can it ask me a parameter that i send ? Can someone help me to resolve this pls ? :)
Sorry if the answer is obvious :/ , it's my first API with nestjs
The error was coming from another file, my bad, it's fixed.
From the example given at this place :
https://pub.dev/packages/shelf_router/example
I have written the router part like this :
class Service {
Handler get handler {
final router = Router();
router.get('/say-hi/<name>', (Request request, String name) {
return Response.ok('hi $name');
});
router.get('/user/<userId|[0-9]+>', (Request request, String userId) {
return Response.ok('User has the user-number: $userId');
});
router.get('/wave', (Request request) async {
await Future.delayed(Duration(milliseconds: 100));
return Response.ok('_o/');
});
router.mount('/api/', Api().router);
router.all('/<ignored|.*>', (Request request) {
return Response.notFound('Page not found');
});
return router;
}
}
class Api {
Future<Response> _messages(Request request) async {
return Response.ok('[]');
}
Router get router {
final router = Router();
router.get('/messages', _messages);
router.get('/messages/', _messages);
uter.all('/<ignored|.*>', (Request request) => Response.notFound('null'));
return router;
}
}
and from the main method it tells to use it like this:
void main() async {
final service = Service();
final server = await shelf_io.serve(service.handler, 'localhost', 8080);
print('Server running on localhost:${server.port}');
}
but in web-only project we don't do : final server = await shelf_io.serve(service.handler, 'localhost', 8080);
I was thinking of creating single page application using a shelf router, I saw from the documentation it says that it is compatible with the dart web js platform
My expectation was :
if I write this in web :
router.get('/say-hi/<name>', (Request request, String name) {
return Response.ok('hi $name');
});
then when I will hit /say-hi/ram, then it should have returned "hi ram" in the browser
I am using springboot to setup my websocket endpoint and angularJs to connect to my endpoint. which i have done before and it worked fine that time. but now when i do it in new project. its giving me hand shake error. below is mycode:
Websocket config
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chatService").setAllowedOrigins("*").addInterceptors(new HttpSessionHandshakeInterceptor());
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.enableSimpleBroker("/chat");
}
}
angularJs code:
var socket = new WebSocket('ws://192.168.225.133:9191/chatService');
// console.log(socket);
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
if(frame.command=="CONNECTED"){
//alert(frame);
//showLoader(false);
stompClient.subscribe('http://localhost:9191/chat/messages', function(response) {
var data = JSON.parse(response.body);
chat.gif=false;
console.log("data_json...");
if(data.to==$scope.admin.id)
$scope.getMessagesById(data.from,1);
else
$scope.getMessagesById(data.to,1);
console.log(data);
$scope.myImgs=null;
});
}
else{
var r = confirm("Could not connect! Retry?");
if (r == true) {
connect();
}
}
});
}
in my project there was a client forward controller. disabling that fixed the issue.
I've set a custom unique validator decorator on my TypeORM entity field email. NestJS has dependency injection, but the service is not injected.
The error is:
TypeError: Cannot read property 'findByEmail' of undefined
Any help on implementing a custom email validator?
user.entity.ts:
#Column()
#Validate(CustomEmail, {
message: "Title is too short or long!"
})
#IsEmail()
email: string;
My CustomEmail validator is
import {ValidatorConstraint, ValidatorConstraintInterface,
ValidationArguments} from "class-validator";
import {UserService} from "./user.service";
#ValidatorConstraint({ name: "customText", async: true })
export class CustomEmail implements ValidatorConstraintInterface {
constructor(private userService: UserService) {}
async validate(text: string, args: ValidationArguments) {
const user = await this.userService.findByEmail(text);
return !user;
}
defaultMessage(args: ValidationArguments) {
return "Text ($value) is too short or too long!";
}
}
I know I could set unique in the Column options
#Column({
unique: true
})
but this throws a mysql error and the ExceptionsHandler that crashes my app, so I can't handle it myself...
Thankx!
I can propose 2 different approaches here, the first one catches the constraint violation error locally without additional request, and the second one uses a global error filter, catching such errors in the entire application. I personally use the latter.
Local no-db request solution
No need to make additional database request. You can catch the error violating the unique constraint and throw any HttpException you want to the client. In users.service.ts:
public create(newUser: Partial<UserEntity>): Promise<UserEntity> {
return this.usersRepository.save(newUser).catch((e) => {
if (/(email)[\s\S]+(already exists)/.test(e.detail)) {
throw new BadRequestException(
'Account with this email already exists.',
);
}
return e;
});
}
Which will return:
Global error filter solution
Or even create a global QueryErrorFilter:
#Catch(QueryFailedError)
export class QueryErrorFilter extends BaseExceptionFilter {
public catch(exception: any, host: ArgumentsHost): any {
const detail = exception.detail;
if (typeof detail === 'string' && detail.includes('already exists')) {
const messageStart = exception.table.split('_').join(' ') + ' with';
throw new BadRequestException(
exception.detail.replace('Key', messageStart),
);
}
return super.catch(exception, host);
}
}
Then in main.ts:
async function bootstrap() {
const app = await NestFactory.create(/**/);
/* ... */
const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new QueryErrorFilter(httpAdapter));
/* ... */
await app.listen(3000);
}
bootstrap();
This will give generic $table entity with ($field)=($value) already exists. error message. Example:
I have modified my code. I am checking the uniqueness of username/email in the user service (instead of a custom validator) and return an HttpExcetion in case the user is already inserted in the DB.
The easiest solution!
#Entity()
export class MyEntity extends BaseEntity{
#Column({unique:true}) name:string;
}
export abstract class BaseDataService<T> {
constructor(protected readonly repo: Repository<T>) {}
private async isUnique(t: any) {
const uniqueColumns = this.repo.metadata.uniques.map(
(e) => e.givenColumnNames[0]
);
for (const u of uniqueColumns) {
const count = await this.repo.count({ where: { [u]: ILike(t[u]) } });
if (count > 0) {
throw new UnprocessableEntityException(`${u} must be unique!`);
}
}
}
async save(body: DeepPartial<T>) {
await this.isUnique(body);
try {
return await this.repo.save(body);
} catch (err) {
throw new UnprocessableEntityException(err.message);
}
}
async update(id: number, updated: QueryDeepPartialEntity<T>) {
await this.isUnique(updated)
try {
return await this.repo.update(id, updated);
} catch (err) {
throw new UnprocessableEntityException(err.message);
}
}
}
An approach that works for modern version of NestJS which is based in Daniel Kucal's answer and actually returns the error to the frontend when calling the JSON API is the following:
import {
Catch,
ArgumentsHost,
BadRequestException,
HttpException,
} from '#nestjs/common';
import { BaseExceptionFilter } from '#nestjs/core';
import { QueryFailedError } from 'typeorm';
type ExceptionType = { detail: string; table: string };
#Catch(QueryFailedError)
export class QueryErrorFilter extends BaseExceptionFilter<
HttpException | ExceptionType
> {
public catch(exception: ExceptionType, host: ArgumentsHost): void {
const { detail = null } = exception || {};
if (
!detail ||
typeof detail !== 'string' ||
// deepcode ignore AttrAccessOnNull: <False positive>
!detail.includes('already exists')
) {
return super.catch(exception, host);
} // else
/**
* this regex transform the message `(phone)=(123)` to a more intuitive `with phone: "123"` one,
* the regex is long to prevent mistakes if the value itself is ()=(), for example, (phone)=(()=())
*/
const extractMessageRegex =
/\((.*?)(?:(?:\)=\()(?!.*(\))(?!.*\))=\()(.*?)\)(?!.*\)))(?!.*(?:\)=\()(?!.*\)=\()((.*?)\))(?!.*\)))/;
const messageStart = `${exception.table.split('_').join(' ')} with`;
/** prevent Regex DoS, doesn't treat messages longer than 200 characters */
const exceptionDetail =
exception.detail.length <= 200
? exception.detail.replace(extractMessageRegex, 'with $1: "$3"')
: exception.detail;
super.catch(
new BadRequestException(exceptionDetail.replace('Key', messageStart)),
host,
);
}
}
Also, not forgetting main.ts:
async function bootstrap() {
const app = await NestFactory.create(/**/);
/* ... */
const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new QueryErrorFilter(httpAdapter));
/* ... */
await app.listen(3000);
}
bootstrap();
I want to do a HTTPRequest based upon a url, which is set in a component attribute. I tried it like shown below, but the dataUrl is always none. It seems as the constructor of the component is executed before the attributes, which are set in the html, are available to the component.
How can I tell the HTTPRequest to wait until the dataUrl variable is available?
component.dart
class TableData {
static List data = [];
TableData() {}
//GETTERS
List get getData => data;
}
#NgComponent(
selector: 'jstable',
templateUrl: 'jstable/jstable_component.html',
cssUrl: 'jstable/jstable_component.css',
publishAs: 'cmp'
)
class JSTableComponent {
#NgAttr('name')
String name;
#NgAttr('data-url')
String dataUrl;
TableData _table_data = new TableData();
final Http _http;
bool dataLoaded = false;
JSTableComponent(this._http) {
_loadData().then((_) {
dataLoaded = true;
}, onError: (_) {
dataLoaded = false;
});
}
//GETTERS
List get data => _table_data.getData;
//HTTP
Future _loadData() {
print("data url is");
print(dataUrl);
return _http.get(dataUrl).then((HttpResponse response) {
TableData.data = response.data['data'];
});
}
}
.html
<jstable name="myjstablename" data-url="table-data.json"></jstable>
Implement NgAttachAware and put your code in the attach method. The attributes are already evaluated when attach is called.