Is it safe to (can I) use `HttpError` from 'admin-on-rest/lib/util/HttpError'? - admin-on-rest

My API provider is sending error messages in the response.body as a ReadableStream as plain text and I need to get admin-on-rest to display those messages to the end user.
I'm planning to do so like this:
import HttpError from 'admin-on-rest/lib/util/HttpError'
import _ from 'lodash';
function handleError(response) {
if (_.isFunction(response.text)) {
const statusText = response.statusText;
const status = response.status;
return response.text().then(text => { // get the text from the ReadableStream
return Promise.reject(new HttpError( text || statusText, status));
});
} else { throw new Error(response.statusText); }
}
And in my custom REST Client
//...
if (response.status < 200 || response.status >= 300) {
return handleError(response);
}
//...
But I feel that accessing to HttpError directly through 'admin-on-rest/lib/util/HttpError' intead of 'admin-on-rest' is insecure because if the internal implementation changes (or there is a refactor of the internal classes of the framework, etc) my implementation will fail.
I cannot do import HttpError from 'admin-on-rest/lib/util/HttpError' because it doesn't work that way. I get __WEBPACK_IMPORTED_MODULE_0_admin_on_rest___default.a is not a constructor
So, would be safe to import HttpError like this or not: import HttpError from 'admin-on-rest/lib/util/HttpError'?
If no, would it be any other alternative?

A simple(er) and safe(er) alternative can be just not to use HttpError at all.
Instead do Promise.reject with a simple object as argument, which contains a message and status properties like this:
//...
return response.text().then(text => {
return Promise.reject({message: text || statusText, status: status});
});
//...

Related

ThrottlerGuard not working on Websocket in Nestjs

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)

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.

Sending appropriate error responses on web actions

I have some web-enabled actions that are exposed through API Connect in IBM Cloud Serverless Functions.
Some of my actions use request-promises to call external REST services and I need to be able to catch an error and respond with an appropriate status-code to the caller.
Since the actions are web-enabled, the documentation indicates that I can use an annotated JSON to set the headers, status-code and body of the response. But it seems that, seems the API expects to always get a Content-Type=application/json, the response processor is failing to understand my annotations in the case of an error.
I tried the following without success:
let rp = require('request-promise');
function main(params){
//setup options
return rp(options).then(
res => {
return res;
}
).catch(
err => {
return { error: { statusCode:err.statusCode } }
}
);
}
Another variation:
let rp = require('request-promise');
function main(params){
//setup options
return rp(options).then(
res => {
return res;
}
).catch(
err => {
return { statusCode:err.statusCode }
}
);
}
The problem is that the status-code I always get is 200... I also tried to change the runtime to node8.0 without success.
Thanks!
I found the answer myself :)
In order to get the status-code and headers, one must set the field Response Content Type to `Use "Content-Type" header from action", while setting up the mapping between the API call and the action....

Is using 500 response code for normal app flow viable? Designing popup messages with Angular7+Spring

From Java standpoint using Exceptions to handle EXPECTED outcomes is wrong (Exceptions should be what they are called).
For all my services I've created wrapper mechanism that basically gives details on failure if such happens (all returns are arbitrary Result<?>). Now I need to display this message on client browser in some popup.
With Angular there is HttpClient that actually supports catching http response errors:
https://angular.io/guide/http#error-handling
Is this a viable way of reporting error from server to client?
Is there a way in Angular to define some extractor that would split responses from backend API?
Say I'd make my whole REST API return bodies:
{
messageType: "", // Success, Failure, Warning
message: "Message",
content: {
...
}
}
And that way I could strip message, messageType in interceptor, display them in popup, and pass only content further as body?
A good way to capture all exceptions at service side using #ControllerAdvice and throw the user/feature specific exceptions with the expected exception message and status code in standard structure so that front end can have an exception controller to evaluate this exception message on a fly in popup message dynamic to any message from service.
Since I came to web from pure Java backends - Exceptions in JAVA are bad (generally speaking), why follow same bad practices (using errors as info) when using HTTP?
After some more reading I can say - don't unless its actually an error, e.g:
404 if client tries to access entity with ID that is not there.
500 when server ACTUALLY can't handle something (actual Exception).
Here is Angular part for my Result<?> system.
Interceptor:
import { Injectable } from '#angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse } from '#angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { Globals } from '../globals.service';
#Injectable()
export class PopupInterceptor implements HttpInterceptor {
constructor(private globals: Globals) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.url.startsWith(this.globals.apiHost)) {
return next.handle(req)
.pipe(
map(e => {
if (e instanceof HttpResponse) {
const response = <Response>e.body;
// use response to do whatever - I am injecting popup service and pushing response.message there for display in component.
return e.clone({ body: response.result });
}
})
);
}
return next.handle(req);
}
}
interface Response {
type: string;
message: string;
result: any;
}
globals.service:
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class Globals {
apiHost = '/api/';
}
app.module:
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: PopupInterceptor, multi: true }
],

angular 2 ajax call getting error "EXCEPTION: SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data"

I'm creating app using angular2 and i need to get weather from yahoo weather. I try to do it using http get method but it's give me a error.
import {Component,OnInit,} from "angular2/core";
import {Http} from "angular2/http";
import 'rxjs/add/operator/map';
#Component({
templateUrl:'app/contact/contact.html',
})
export class ContactComponent{
constructor (private http: Http) {}
public url:string= "http://weather.yahooapis.com/forecastjson?w=2295424";
ngOnInit(url:string) {
return this.http.get(url).map(res => {
return res.json();
}).subscribe((response) => { console.log(response) });
}
}
Error I get is
EXCEPTION: SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
can anyone help me with this?.
You need to append Accept headers to your get request in order for Firefox to render the json that comes back. I found this after searching quite a bit and came across this https://brockallen.com/2012/04/27/change-firefoxs-default-accept-header-to-prefer-json-over-xml/ which led me down the path of adding the headers
ngOnInit() {
let headers = new Headers();
headers.append('Accept', 'q=0.8;application/json;q=0.9');
return this.http.get(this.url, { headers: headers } ).map(res => {
return res.json();
}).subscribe((response) => { console.log(response) });
}
I think the payload of your response isn't actually JSON. That's why Angular can't parse it. You could have a look within the Network tab of the Chrome developer tools for example for more hints.
I try your request (http://weather.yahooapis.com/forecastjson?w=2295424) and I got a 404 response with an HTML payload (and not a JSON one).
A strange thing in your code is the parameter you provide to the ngOnInit method. You should try something like that:
ngOnInit() {
return this.http.get(this.url).map(res => {
return res.json();
}).subscribe((response) => { console.log(response) });
}
Hope it helps you,
Thierry

Resources