Issues mapping a HTTP response to a JSON and then mapping the JSON to a model - rest

I am trying to map a http response to a JSON and then map part of that JSON to a Ticket interface as it has many values I don't need in my ticket interface. My app compiles without any issues but when I test the REST function I get the below runtime error, I know the issue isn't with the backend as I'm able to successfully console log the response. Any idea what I'm doing wrong here?
My error:
vendor.bundle.js:8137 Angular is running in the development mode. Call enableProdMode() to enable the production mode.
main.bundle.js:553 TypeError: res.json(...).map is not a function
at MapSubscriber.project (main.bundle.js:1330)
at MapSubscriber._next (vendor.bundle.js:42853)
at MapSubscriber.Subscriber.next (vendor.bundle.js:4709)
at XMLHttpRequest.onLoad (vendor.bundle.js:47289)
at ZoneDelegate.webpackJsonp.749.ZoneDelegate.invokeTask (polyfills.bundle.js:2478)
at Object.onInvokeTask (vendor.bundle.js:9228)
at ZoneDelegate.webpackJsonp.749.ZoneDelegate.invokeTask (polyfills.bundle.js:2477)
at Zone.webpackJsonp.749.Zone.runTask (polyfills.bundle.js:2245)
at XMLHttpRequest.ZoneTask.invoke (polyfills.bundle.js:2540)
My code:
retrieveTicket(barcode: string) : Observable<any> {
return this.http.get(`${this.API_URL}POS/RetrieveTicket/${barcode}`, this.options)
.map((res: Response) => res.json().map(ticket => {
Object.assign({
ResponseCode: ticket.ResponseCode,
CustomError: ticket.CustomError,
ticketDate: ticket.POSTicket.Date,
ticketTime: ticket.POSTicket.EndTime,
cashierName: ticket.POSBarCode.POSCashier_Name,
tranNo: ticket.POSTicket.TranNo,
tranValue: ticket.POSTicket.ScanValue,
securityChecked: ticket.POSBarCode.SecurityChecked
}) as ITicket})
)
.catch((error: any) => Observable.throw(error || 'server error'));
}
my interface:
import { BaseRequestInterface } from './base-request.interface';
export interface ITicket extends IBaseRequest {
ticketDate: string;
ticketTime: string;
cashierName: string;
tranNo: string;
tranValue: string;
timeSincePurchase: string;
securityChecked: boolean;
export function setTicket(obj?: any) {
super();
this.ResponseCode = obj && obj.ResponseCode || null;
this.CustomError = obj && obj.CustomError || null;
this.ticketDate = obj && obj.ticketDate || null;
this.ticketTime = obj && obj.ticketTime || null;
this.cashierName = obj && obj.cashierName || null;
this.tranNo = obj && obj.tranNo || null;
this.tranValue = obj && obj.tranValue || null;
this.timeSincePurchase = obj && obj.timeSincePurchase || null;
this.securityChecked = obj && obj.securityChecked || null;
}
}
my BaseRequest Interface:
// Base request class that returns from BRMService API
export interface IBaseRequest {
// Public properties available
BaseURI?: string;
CustomError?: string;
ProviderName?: string;
RequestFormData?: string;
RequestURI?: string;
ResponseCode?: number;
}

EDIT:
We learned that we are dealing with just one Object, rather than an array of object, so mapping the object to type TicketModel would be done like the following (shortened):
retrieveTicket(barcode: string) {
return this.http.get(...)
.map(res => res.json())
.map(res => ({ResponseCode:res.ResponseCode, CustomError:res.CustomError}) as TicketModel)
}
The interface:
export interface TicketModel {
ResponseCode: number;
CustomError:string
}
DEMO
ORIGINAL POST:
Unclear if you are using a class or interface for your TicketModel, anyway I suggest you use an interface ;) Then you can simply map your incoming data like (shortened version):
.map((res:Response) => res.json().map(x =>
Object.assign({ResponseCode: x.ResponseCode,
CustomError:x.CustomError}) as TicketModel)))
If your response is an object with an array containing inside items, just add the items in:
.... res.json().items.map( ...

Related

mongodb/mongoose: Save unique value if data is not null in from nestjs

I am trying to save data in MongoDB. I want to store unique data when data is not null. However, I want to allow multiple null values in the unique identifier.
My sample schema:
#Schema()
export class Contact extends Document {
#Prop({ unique: true, sparse: true, require: true })
email: string;
#Prop({ default: '+1' })
countryCode: string;
#Prop({ unique: true, sparse: true })
mobile: string;
}
In this case, a mobile number is not required. User can add their contact information with or without providing a mobile number. If the user sends their mobile number that should be unique. So, I need to allow multiple null values in the mobile field. However, that field should be unique when the user provides any mobile number.
Empty entries seem to get the value null so every entry without mobile crashes with the unique identifier.
Is there any way to solve this problem either from the database layer or the application layer?
I am using NestJS for developing my API.
A unique index still does not allow multiple docs with a field of null. You need to transform your data payload by dropping the null field before you save your docs in MongoDB. A transform pipe will help you to handle this issue. Here is a transform pipe that you can use for this purpose:
#Injectable()
export class NullValidationPipe implements PipeTransform {
private isObj(obj: any): boolean {
return typeof obj === 'object' && obj !== null;
}
private dropNull(values) {
Object.keys(values).forEach((key) => {
if (!(key === 'password' || key === '_id')) {
if (this.isObj(values[key])) {
values[key] = this.dropNull(values[key]);
} else if (Array.isArray(values[key]) && values[key].length > 0) {
values[key] = values[key].map((value) => {
if (this.isObj(value)) {
value = this.dropNull(value);
}
return value;
});
} else {
if (values[key] === null || values[key] === undefined) {
delete values[key];
}
}
}
});
return values;
}
transform(values: any, metadata: ArgumentMetadata) {
const { type } = metadata;
if (type === 'param' || type === 'custom') return values;
else if (this.isObj(values) && type === 'body') {
return this.dropNull(values);
}
throw new BadRequestException('Validation failed');
}
}
Use this pipe in the controller and this pipe will drop all incoming null fields which will come with the request payload.
You can also check nest pipe transform docs: https://docs.nestjs.com/techniques/validation

Angular 12 how to convert date object to string using http interceptor

I am trying to convert the date property to string object for all http request. Currently converting individual request body as below;
modifiedOn: modifiedOn?.map(x => moment(x).format('YYYY-MM-DD HH:mm:ss')),
But I would like this to apply for all http request in the interceptor. Can anyone help how to do this. my interceptor is as below:
#Injectable()
export class VerifyAuthorisationInterceptor implements HttpInterceptor {
constructor(#Inject(ENVIRONMENT) private environment: IEnvironment) {}
intercept(
request: HttpRequest<unknown>,
next: HttpHandler
): Observable<HttpEvent<unknown>> {
return next.handle(request).pipe(
map((event: HttpEvent<unknown>) => {
return event;
}),
catchError((error: HttpErrorResponse) => {
if (error.status === 401 && error.statusText === 'Unauthorized') {
const returnUrl = window.location.href;
window.location.href = radarWebUrl;
}
return throwError(error);
})
);
}
}
Add this to your intercept method before return. It will check if your request contains body and any Date elements and then converts those:
if(request?.body) {
Object.keys(request.body).forEach((key: any) => {
if(request.body[key] instanceof Date) {
request.body[key] = moment(request.body[key]).format('YYYY-MM-DD HH:mm:ss');
}
});
}

How to access Marshalling iOS [Nativescript]

I currently have a functional plugin, but I have a problem being able to get the object in the finishCheckout method
import { Options } from "./mercadopago-px.common";
import * as frameModule from "tns-core-modules/ui/frame";
export class LifeCycleProtocolImpl extends NSObject
implements PXLifeCycleProtocol {
public resolve: any;
public reject: any;
static ObjCProtocols = [PXLifeCycleProtocol]; // define our native protocalls
static new(): LifeCycleProtocolImpl {
return <LifeCycleProtocolImpl>super.new(); // calls new() on the NSObject
}
cancelCheckout(): () => void {
this.reject({
status: "cancel",
data: null,
error: "cancelCheckout"
});
return null;
}
changePaymentMethodTapped?(): () => void {
return null;
}
finishCheckout(): (result: PXResult) => void {
this.resolve({
status: "finishCheckout",
data: null,
error: null
});
return null;
}
}
export class MercadopagoPx {
public start(options: Options): Promise<any> {
return new Promise((resolve, reject) => {
let checkout = MercadoPagoCheckout.alloc().initWithBuilder(
MercadoPagoCheckoutBuilder.alloc()
.initWithPublicKeyPreferenceId(
options.publicKey,
options.preferenceId
)
.setLanguage(options.language)
);
let lifeCycleProtocolDelegate: LifeCycleProtocolImpl = LifeCycleProtocolImpl.new();
lifeCycleProtocolDelegate.resolve = resolve;
lifeCycleProtocolDelegate.reject = reject;
let pxLifeCycleProtocol: PXLifeCycleProtocol = lifeCycleProtocolDelegate;
checkout.startWithNavigationControllerLifeCycleProtocol(
frameModule.topmost().ios.controller,
pxLifeCycleProtocol
);
});
}
}
My custion is how to access here, actually not access because i have wrong with linter TS :
finishCheckout(): (result: PXResult) => void {
console.dir(result); <---- Here error, can't access to result
this.resolve({
status: "finishCheckout",
data: null,
error: null
});
return null;
}
Trying to access the object, I can not because it detects that the variable does not exist, when it is present, I do not know if it is the way to access the property, or if there is a way to access that object that returns the method to me which try to implement
It means you are returning a function from finishCheckout which has a parameter result.
finishCheckout(): (result: PXResult) => void {
console.dir(result); <---- Here error, can't access to result
this.resolve({
status: "finishCheckout",
data: null,
error: null
});
return null;
}
It suppose to be,
finishCheckout(result: PXResult) {
console.dir(result);
this.resolve({
status: "finishCheckout",
data: null,
error: null
});
return null;
}
if result should be a parameter for finishCheckout.
Edit:
As per the typings (finishCheckout(): (p1: PXResult) => void;), the method doesn't have a parameter but it returns a function that will have result as a parameter. Hence you can't access result in there.

Is it possible to create dynamic getters/setters in typescript?

I'm new in typescript, and I'm trying to rewrite our application from es2016 to TypeScript.
My task is to have a class with data property and make each element from data object available as class property.
I get stuck on this JavaScript code:
for(let key in this.data) {
Object.defineProperty(this, key, {
get: function(value:any) { return this.data[key]; },
set: function(value:any) {
if (this.data[key] !== value) {
this.data[key] = value;
this.updatedKeys.push(key);
}
},
});
}
It is pretty easy to use getter/setters for typescript, but i get confused if i can create them dynamically?
interface IData {
id: number;
[propName: string]: any;
}
class Model {
protected updatedKeys:string[] = [];
baseUrl:string = null;
data:IData;
fields:IData;
constructor(data:IData={id:null}, fields:IData={id:null}) {
super();
this.data = data;
this.fields = fields;
for(let key in this.data) {
Object.defineProperty(this, key, {
get: function(value:any) { return this.data[key]; },
set: function(value:any) {
if (this.data[key] !== value) {
this.data[key] = value;
this.updatedKeys.push(key);
}
},
});
}
}
}
tsc -t ES2016 --lib "es2016","dom" models.ts
will give this error:
models.ts(33,40): error TS2345: Argument of type '{ get: (value: any) => any; set: (value: any) => void; }' is not assignable to parameter of type 'PropertyDescriptor & ThisType<any>'.
Type '{ get: (value: any) => any; set: (value: any) => void; }' is not assignable to type 'PropertyDescriptor'.
Types of property 'get' are incompatible.
Type '(value: any) => any' is not assignable to type '() => any'.
And I don't know how to get rid of this problem.
thanks to the https://github.com/epicgirl1998, she helped me to find the solution. I'll post it here:
the error is that the getter has a value parameter even though getters
aren't passed any value
i replaced it with get: function() { return this.data[key]; }, and now
the only error is that there's a super call in the class which is only
needed if the class extends another class
also, this inside the accessors doesn't refer to the class instance,
but using arrow functions for them should fix it
try this:
interface IData {
id: number;
[propName: string]: any;
}
class Model {
protected updatedKeys:string[] = [];
baseUrl:string = null;
data:IData;
fields:IData;
constructor(data:IData={id:null}, fields:IData={id:null}) {
this.data = data;
this.fields = fields;
for(let key in this.data) {
Object.defineProperty(this, key, {
get: () => { return this.data[key]; },
set: (value:any) => {
if (this.data[key] !== value) {
this.data[key] = value;
this.updatedKeys.push(key);
}
},
});
}
}
}
In typescript, you generally don't need to create objects with methods and properties dynamically. You either create instances of classes, or you type your data using an interface.
If all you want is to convert loaded (json) data to typed data, you can use an interface that describes the structure of your json data.
interface describes the properties of actor data
interface Actor {
name: string;
height: number;
}
fetch generic json data from somewhere
let data : any = getSingleActorData();
type the actor to an interface and put it in an actor array
let actorData : Actor[] = [];
actorData.push(data as Actor);
Now your IDE will allow you to access the name and height of the actor variable:
console.log(actorData[0].name);
If you do want a complete 'object' with getters and setters you can create an Actor class and then instantiate it with the data you loaded:
class Actor {
private _name:string;
private _height:string;
get name {}
set name {}
get height {}
set height {}
constructor(name:string, height:number){
}
}
And then you can put your json data in an actor instance:
actorData.push(new Actor(jsondata.name, jsondata.height));

Convert MongoDB data to json on client side

I'm trying to convert mongoDB data passed by cookie on my client side from my server.
I'm using Express et React.js.
Cient:
export default class Profile extends React.Component {
constructor(){
super();
this.state = {}
}
componentWillMount(){
console.log(JSON.stringify(cookie.load('user')))
}
render(){
return (
<div>
<h1>Profile</h1>
</div>
)
}
The console.log return :
"j:{\"_id\":\"58e622ac7144862dbb5722f1\",\"password\":\"paswdtest\",\"email\":\"test#test.com\",\"pseudo\":\"testname\",\"__v\":0}"
Server:
const post = (req, res, next) => {
if(req.body.pseudo && req.body.password) {
User.authenticate(req.body.pseudo, req.body.password, (error, user) => {
if(error || !user) {
var error = new Error('Wrong email or password')
error.status = 401;
return next(error);
}
else {
req.session.user = user;
res.cookie('user', req.session.user)
return res.redirect('/profile');
}
})
}
else {
var error = new Error('Email and password are required');
error.status = 401;
return next(error);
}
}
I'm trying to convert with parse and stringlify but it's not working.
MongoDB doesn't return JSON but instead it returns extended JSON called BSON.
If your needs are simple then the quickest approach may be to just convert the BSON to JSON in your code.
For your example this would be
before
"j:{\"_id\":\"58e622ac7144862dbb5722f1\",\"password\":\"paswdtest\",\"email\":\"test#test.com\",\"pseudo\":\"testname\",\"__v\":0}"
after
{"j":{"_id":"58e622ac7144862dbb5722f1","password":"paswdtest","email":"test#test.com","pseudo":"testname","__v":0}}