ThrottlerGuard not working on Websocket in Nestjs - websocket

I'm creating an application that is using Nestjs with websockets, but now I need to add rate limit on the sockets, but analyzing the documentation documentation link and implementing what it says in it, when I use #UseGuards(MyGuard) an error occurs in the application.
My Guard:
#Injectable()
export class NewThrottlerGuard extends ThrottlerGuard {
protected async handleRequest(
context: ExecutionContext,
limit: number,
ttl: number,
): Promise<boolean> {
console.log('Request');
const client = context.switchToWs().getClient();
const ip = client.conn.remoteAddress;
const key = this.generateKey(context, ip);
const ttls = await this.storageService.getRecord(key);
if (ttls.length >= limit) {
throw new ThrottlerException();
}
await this.storageService.addRecord(key, ttl);
return true;
}
}
Websocket:
#UseGuards(NewThrottlerGuard)
#SubscribeMessage('sendMessage')
sendMessage(
#ConnectedSocket() client: Socket,
#MessageBody() message: string,
) {
client.rooms.forEach((room) => {
if (room !== client.id) {
client.broadcast.to(room).emit('message', message);
}
});
}
Error in console:
/node_modules/#nestjs/common/utils/validate-each.util.js:22
throw new InvalidDecoratorItemException(decorator, item, context.name);
^
Error: Invalid guard passed to #UseGuards() decorator (ChatGateway).
at validateEach
The file in: #nestjs/common/utils/validate-each.util.js:22
function validateEach(context, arr, predicate, decorator, item) {
if (!context || !context.name) {
return true;
}
console.log(context, arr)
const errors = arr.some(str => !predicate(str));
if (errors) {
throw new InvalidDecoratorItemException(decorator, item, context.name);
}
return true;
}
i put some console.log then in the terminal it show:
[Function: ChatGateway] [ undefined ]
In Github Throttler documentation they say: You cannot bind the guard with APP_GUARD or app.useGlobalGuards() due to how Nest binds global guards.
So, im using #UseGuards()

The guard itself was written correctly, but it was put in a location that importing it made a circular reference between files, so when #UseGuards() was used it became #UseGuards(undefined) which caused the cryptic error message. Moving the guard to a dedicated file will fix the error

I follow your github reference settings and it doesn't work,The following is my code, where is my setting wrong, and the request to ws is not intercepted(In the handleRequest method)

Related

Terminate a graphql request from Apollo Link

I have an ApolloLink setup for the AWSAppSyncClient in my React project to check if the user is authorized to make the graphql request. If the user is not authorized then, link should not invoke the forward method rather just return an Error for unauthorized access (making it a terminating link).
I tried returning the plain Error response (also tried it with Observable.of), but the request doesn't resolves but keeps on to retry the request on an endless loop.
Need a better solution to implement it.
const authLink = new ApolloLink((operation, forward) => {
const { operationName } = operation;
if (checkIfNotAuthorized(operationName)) {
const error = new Error('Access denied');
return { errors: [error], data: null };
}
return forward(operation);
});

NestJS Interceptor not enriching Exception handlers

I am using both a Exception Filter, Interceptor and Validation Pipe. All of these work as expected on their own. I set these globally, as that is all I need at this time. The AllExceptionsFilter enriches the default exception from NestJS to include the URL, Http method, etc. The AddProgramVersionToResponseHeaderInterceptor is used to add a custom header to the response, which contains my application and version. The AppGlobalValidationPipeOptions is used to convert the Http Status code from 400 on a validation error, to be 422 Unprocessable Entity.
main.ts
...
const AppHttpAdapter = app.get(HttpAdapterHost);
app.useGlobalFilters(new AllExceptionsFilter(AppHttpAdapter));
app.useGlobalInterceptors(new AddProgramVersionToResponseHeaderInterceptor());
app.useGlobalPipes(new ValidationPipe(AppGlobalValidationPipeOptions));
await app.listen(IpPort);
All of these work fine, until I get an exception in the code, such as a bad route.
GET /doesnotexist will return the enriched 404 result, which has what I want, but the Http Header field from the AddProgramVersionToResponseHeaderInterceptor does not update the header.
I have defined the global settings in different orders to see if that would allow the interceptor to add the Http header, but that did not work.
AddProgramVersionToResponseHeaderInterceptor
export class AddProgramVersionToResponseHeaderInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const AppInfo: ApplicationInformationService = new ApplicationInformationService();
const EmulatorInfo: string = `${AppInfo.Name}/${AppInfo.Version}`;
const ResponseObj: ExpressResponse = context.switchToHttp().getResponse();
ResponseObj.setHeader('my-custom-header', EmulatorInfo);
return next.handle().pipe();
}
}
AllExceptionsFilter
export class AllExceptionsFilter implements ExceptionFilter {
constructor(private readonly httpAdapterHost: HttpAdapterHost) {}
catch(exception: unknown, host: ArgumentsHost): void {
// In certain situations `httpAdapter` might not be available in the
// constructor method, thus we should resolve it here.
const { httpAdapter } = this.httpAdapterHost;
const ctx = host.switchToHttp();
const HttpStatusCode: number = exception instanceof HttpException ? exception.getStatus() : HttpStatus.EXPECTATION_FAILED;
const RequestData: string = httpAdapter.getRequestUrl(ctx.getRequest());
const RequestURLInfo: string[] = RequestData.split('?');
const ResponseBody: IBackendException = {
Hostname: httpAdapter.getRequestHostname(ctx.getRequest()),
Message: exception instanceof HttpException ? (exception.getResponse() as INestHttpException).message : [(exception as Error).message.toString()],
Method: httpAdapter.getRequestMethod(ctx.getRequest()),
StackTrace: exception instanceof HttpException ? '' : (exception as Error).stack,
StatusCode: HttpStatusCode,
Timestamp: new Date().toISOString(),
URL: RequestURLInfo[0],
Parameters: RequestURLInfo[1],
};
httpAdapter.reply(ctx.getResponse(), ResponseBody, HttpStatusCode);
};
AppGlobalValidationPipeOptions
export const AppGlobalValidationPipeOptions: ValidationPipeOptions = {
errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY,
transform: true,
whitelist: true,
};
Interceptors bind to route handlers (class methods) that are marked with an HTTP verb decorator. If there is no route handler (like a 404) then then interceptor cannot be called. I have the same problem with my OgmaInterceptor. You can either make an #All('*') handler that throws a NotFoundException or just take care of it in the filter as you already are.

How do I blend a promise with an observable?

I'm having trouble promises and observables. I have a handful of http requests which are defined in a package using promises. In the rest of my code I am using observables for various things, including other http calls. In one particular section I am checking to see if the user's bearer token is expired and if so then I get a new token and then proceed with the rest of the call.
if (!token || token.exp < Math.round((new Date()).getTime() / 1000)) {
from(this._store.refreshBearerToken())
.pipe(flatMap(resp => {
let newToken = resp.data;
newToken.exp = (new Date()).getTime() / 1000 + newToken.expires_in;
localStorage.setItem('token', JSON.stringify(newToken))
options = options || {};
options.headers = new HttpHeaders({
"Authorization": `${newToken.token_type} ${newToken.access_token}`,
"Content-Type": "application/json"
});
return this._http$.request<T>(method, url, options as Object).pipe(share());
}));
}
Bearer Token method:
async refreshBearerToken() {
const response = await this._q2.sources.requestExtensionData({
route: "refreshBearerToken"
});
console.log(response);
return response;
}
Since this._store.refreshBearerToken returns a promise I wrapped the call in a from to convert it to an observable. This compiles but when it runs I get "Cannot read property 'pipe' of undefined".
How can I convert this promise to an observable so that I can refresh the token and then continue with the rest of the call?
Edit:
I am importing from via import { Observable, from } from "rxjs";.
So, I thought the error was coming from the line .pipe(flatMap(resp =>... but I was wrong. The error is coming from the method which is calling this.
GetInitialLinkList(): Observable<Institution[]>
{
let base = { 'MemberId': localStorage.getItem('memberId') };
let ins = localStorage.getItem("initialInstitutionList");
if (ins)
{
return of(JSON.parse(ins));
}
return this._settingsService.get().pipe(
flatMap(settings =>
{
this._settings = settings;
return this._api.request<Institution[]>("Post", `${this._settings.mea}/GetInitialLinkList`, { body: base })
.pipe(
retry(1),
catchError(this.handleError)
)
.pipe(flatMap(instList =>
{
localStorage.setItem("initialInstitutionList", JSON.stringify(instList));
return of(instList);
}))
}));
}
and that is being subscribed to inside my component:
private GetLinkList()
{
this.showWaiting.emit(true);
this._data.GetInitialLinkList().subscribe((result) =>
{
this.initialList = result;
this.showWaiting.emit(false);
});
}
From what Brandon said (I forgot to return /facepalm...) I added the return so I have return from(this._store.refreshBearerToken()) which changed my error to
ERROR Error Code: undefined
Message: You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
defaultErrorLogger # core.js:6014
Can you show the actual error and the line in the code that the error occurs on? Also show where and how you import from.
I notice your code snippet does not return the observable it builds up via from(...).pipe(...) nor does it subscribe to it. It might help to show how your code actually uses this observable.

Connect Lambda to mLab

I would like to read a collection in mLab(mongoDB) and get result document based on the request from AWS LAMBDA function.
I could write a nodeJS function code snippet and whatever timeout I set it results in
Task timed out after *** seconds
Any solution, link or thoughts will be helpful. Either JAVA or NODE
'use strict';
const MongoClient = require('mongodb').MongoClient;
exports.handler = (event, context, callback) => {
console.log('=> connect to database');
MongoClient.connect('mongodb://test:test123#ds.xyx.fleet.mlab.com:1234', function (err, client) {
if (err) {
console.log("ERR ",err );
throw err;
}
var db = client.db('user');
db.collection('sessions').findOne({}, function (findErr, result) {
if (findErr){
console.log("findErr ",findErr);
throw findErr;
} else {
console.log("#",result);
console.log("##",result.name);
context.succeed(result);
}
client.close();
});
});
};
P.S : Referred all related stack questions.
Lambda function returned success after adding db name in
MongoClient.connect('mongodb://test:test123#ds.xyx.fleet.mlab.com:1234/dbNAME')
Apart from declaring db name in
var db = client.db('dbNAME');
It should also be added in mLab connection URI.

Invalid Response Error with Alexa SDK v2

For 2 days now, I have the issue that my lambda function using the ask-sdk-core v2.0.2 returns invalid responses.
A very simple setup:
HelloIntent is handled by HelloIntentHandler:
const HelloIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'HelloIntent';
},
handle(handlerInput) {
const speechText = 'Hello back';
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText)
.getResponse();
}
};
When I call this intent, the simulator goes straight to:
There was a problem with the requested skill's response
Using the ErrorHandler, I checked and the results of handlerInput.requestEnvelope.request.error are:
{ type: 'INVALID_RESPONSE',
message: 'An exception occurred while dispatching the request to the skill.' }
Question: What is the problem here? When I open the skill using the invocation name, the LaunchRequestHandler gets called and responds properly, but any other intent does not work.
Okay I found the problem, it is sort of difficult to deduct from the above error:
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'HelloIntent';
},
In the canHandle function, you need to check for the request type and intent name.

Resources