NestJS factory pattern for use service - javascript

I have 2 types of login. I need to pass the param in the request and use this param in auth service to choose what service for login I should use.
Does mb exist better way?
Body in POST request.
{
"type": "Customer",
"store": "store24",
"email": "test333333#email.com",
"password": "testpass2"
}
authFactory.ts
const loginFactoryTypes = {
[LOGIN_TYPE.Customer]: CustomerAuthService,
[LOGIN_TYPE.User]: UserAuthService,
};
export const createLoginFactory = (type: string) => {
return loginFactoryTypes[type];
};
auth.controller.ts
#Post('/signin')
signInFactory(#Body() signInDto: SignInUserDto & SignInCustomerDto): Promise<unknown> {
const { type } = signInDto;
const loginFactory: AuthInterface = createLoginFactory(type);
return loginFactory.signin(signInDto);
}
If I pass param type - Customer then I get this service CustomerAuthService.
async signin(signInDto: SignInCustomerDto) {
const customer = await this.customersService.findCustomerByEmail(signInDto);
const token = this.jwtService.sign({
id: customer.id,
email: customer.email,
});
return { token, customer };
}
But the controller gets me an error on the string.
loginFactory.signin(signInDto);
I get the error loginFactory.signin is not a function.

as you have this:
const loginFactoryTypes = {
[LOGIN_TYPE.Customer]: CustomerAuthService,
[LOGIN_TYPE.User]: UserAuthService,
};
loginFactoryTypes[type] will return a class, not an instance like you've expected.
At the consumer side of createLoginFactory, you could leverage on moduleRef to retrieve an instance of that class using Nest's DI system.

Related

Passing query param to api in Angular

I have a child component, in which, after button press it pass a form field to a father component. I have to pass this fields as query Params to an API GET /valuation/ that require more or less 20 optional parameters, in which the name differs from Input fields.
I created a temporary object res inside the father function accept in which I iterate the field . Still I can't figure how to pass this parameter inside the object res, using multiple if is dirty code.
for example
Function in father component
accept(form:any){
const req: any = {};
if(form.name)
req.user = form.name
if(form.address)
req.clusterAddress = form.address
... and so on x 20 times
this.requestService.getRequest(req).subscribe(
(response) => {
this.getRequest(response);
}, (error) => {
console.error(error);
}
}
As you can see this is not a viable option, what I can use to take dynamically the query param names?
You can use a map to store information about which property from the form maps to which property on your res object and then iterate through the keys, check for the presence of the value and assign:
const formToResMap: { [key: string]: string } = {
name: 'name',
address: 'clusterAddress',
// ...
};
accept(form: any) {
const req = Object.keys(formToResMap).reduce((acc, key) => {
if (form[key]) {
acc[formToResMap[key]] = form[key];
}
return acc;
}, {});
console.log(req);
}

Default string value after call the object in JavaScript

I have a js object in which I return my endpoint addresses from api. This is a very nice solution for me, it looks like this:
export const API_BASE_URL = 'http://localhost:3000';
export const USERS = '/Users';
export default {
users: {
checkEmail: (email) => `${API_BASE_URL}${USERS}/${email}/checkEmail`,
notifications: `${API_BASE_URL}${USERS}/notifications`,
messages: `${API_BASE_URL}${USERS}/messages`,
},
};
Now I can call this address in my redux-saga to execute the xhr query:
import { api } from 'utils';
const requestURL = api.users.notifications;
But I'm a bit stuck because now I have a problem - base path is missing here: '/users'.
Now when I call api.users, then I get a object. I would like to have a default value after calling the object like:
import { api } from 'utils';
const requestURL = api.users; // http://localhost:3000/Users
const requestURL2 = api.users.notifications; // http://localhost:3000/Users/notifications
I know that I could add a new string with the name 'base' to the object and add '/Users' there, but I don't like this solution and I think, there is a better solution.
You could do one of the following:
extend the String class
const API_BASE_URL = "http://localhost:3000"
const USERS = "/Users"
class UsersEndpoints extends String {
constructor(base) {
super(base)
}
// this is still a proposal at stage 3 to declare instance variables like this
// if u want a truly es6 way you can move them to the constructor
checkEmail = (email) => `${API_BASE_URL}${USERS}/${email}/checkEmail`
notifications = `${API_BASE_URL}${USERS}/notifications`
messages = `${API_BASE_URL}${USERS}/messages`
}
// you can use userEndpoints itself as a string everywhere a string is expected
const userEndpoints = new UsersEndpoints(API_BASE_URL)
export default {
users: userEndpoints
}
The previous is just actually equivalent to
...
const userEndpoints = new String(API_BASE_URL)
userEndpoints.notifications = `${API_BASE_URL}${USERS}/notifications`
...
Obviously this is not recommended: you should not extend native classes, there are many disadvantages to this approach.
An obvious example is that there could be a conflict between the properties you use and the properties that might be brought by the native class
override the toString method
...
export default {
users: {
checkEmail: (email) => `${API_BASE_URL}${USERS}/${email}/checkEmail`,
notifications: `${API_BASE_URL}${USERS}/notifications`,
messages: `${API_BASE_URL}${USERS}/messages`,
toString: () => API_BASE_URL
},
};
// this is actually not much different than the previous method, since a String is an objet with an overridden toString method.
// That said this method is also not recommended since toString is used in many places in native code, and overriding it just to substitute a string value will make information get lost in such places, error stacks for example
Achieve what u want using the language features intended for such a use case
What you are asking is to make the same variable to have different values in the same time, which is not possible in the language syntax, and it makes sense because it makes it hard to reason about code.
that being said i recommend something of the following nature
// it is also better to use named exports
export const getUsersEndpoint = ({
path = "",
dynamicEndpointPayload = {},
} = {}) => {
switch (path) {
case "notifications":
return `${API_BASE_URL}${USERS}/notifications`
case "messages":
return `${API_BASE_URL}${USERS}/messages`
case "checkEmail":
return `${API_BASE_URL}${USERS}/${dynamicEndpointPayload.email}/checkEmail`
// you still can do checkEmail like this, but the previous is more consistent
// case "checkEmail":
// return (email) => `${API_BASE_URL}${USERS}/${email}/checkEmail`
default:
return `${API_BASE_URL}`
}
}
// you can use it like this
getUsersEndpoint() // returns the base
getUsersEndpoint({path: 'notifications'})
You can extend prototype to achieve this behaviour:
export const API_BASE_URL = 'http://localhost:3000';
export const USERS = '/Users';
const users = `${API_BASE_URL}${USERS}`
const baseUrls = {
checkEmail: (email) => `${users}/${email}/checkEmail`,
notifications: `${users}/notifications`,
messages: `${users}/messages`,
}
Object.setPrototypeOf(users.__proto__, baseUrls);
export default {
users
};
Try having object will all user endpoint and a function that return a value of a end point
const user = {
default: '/users',
notification: '/notification',
profile: '/profile',
getEndPoint(prop) {
if(this[prop] === 'default' ){
return this[prop];
} else {
if(this[prop]) {
return this.default + this[prop];
}
}
}
}
So you can have more end points that come under user and you can simply call
const requestURL = api.user.getEndPoint('default'); // http://localhost:3000/Users
const requestURL2 = api.user.getEndPoint('notifications'); // http://localhost:3000/Users/notification

Can't I use getters and setters in an Angular's httpclient result?

In my angular project I have a method that returns a User class from the backend:
export class User {
id: string;
name: string;
my_list: Array<string>;
get otherNameForMyList() : Array<string> {
return this.my_list;
}
set otherNameForMyList(newList : Array<string>) {
this.my_list = newList;
}
}
so I call the backend this way:
export class UserService {
getUserFromBE(login: string): Observable<User> {
return this.http
.get<User>(`${environment.apiUrl}/user/${login}`)
.pipe(catchError(this.processHTTPMsgService.handleError));
}
}
But when I call this method and subscribe the result I can't use otherNameForMyList:
let user = new User();
userService.getUserFromBE('mylogin').subscribe(async (u : User)=>{
console.log(u.otherNameForMyList); // undefined
}
yet if I make it this way it works:
let user = new User();
userService.getUserFromBE('mylogin').subscribe(async (u : User)=>{
console.log(u.my_list); // ok result
}
also console.log(u) returns Object type, not User type as it should be.
Now, if I declared the ": Observable" as result type from the httpclient call in getUserFromBE, shouldn't u be of User type ? Can I force it to be ?
Obviously the api is returning just my_list field.
Can anyone please explain why u is Object and not User ?
Thanks
The generic methods on the HttpClient will use type assertion to on the deserialized json result. Example: .get<User> would still return an object but type assertion can now be used in the static code to treat that returned instance as a User. It does not mean the returned instance is of type User.
If you want an actual instance of User you should use map and create an instance of User to be returned to the caller.
Example:
export class UserService {
getUserFromBE(login: string): Observable<User> {
return this.http
.get<{id:string;name:string;my_list:Array<string>}>(`${environment.apiUrl}/user/${login}`)
.pipe(map(user => {
var result = new User();
result.id = user.id;
result.name = user.name;
result.my_list = user.my_list;
return result;
}),catchError(this.processHTTPMsgService.handleError));
}
}

Angular Unit Test: How to mock properties in a method?

Here is the service that I am trying to test:
#Injectable()
export class BomRevisiosnsService {
constructor(
private baseService: BaseService,
private appConstants: AppConstants,
private dmConstants: DMConstants
) { }
public getRevisionsData(): any {
var itemId = this.appConstants.userPreferences.modelData['basicDetails']['itemId'];
let url = this.dmConstants.URLs.GETBOMREVISIONS + itemId + "/GetRevisionsAsync";
let headers = {
"Content-Type": "application/json",
UserExecutionContext: JSON.stringify(this.appConstants.userPreferences.UserBasicDetails),
}
if (itemId != null || itemId != undefined) {
return this.baseService.getData(url, headers).map(response => {
return response;
});
}
}
}
spec file
describe('bom-revisions.service ',()=>{
let bomRevisiosnsService:BomRevisiosnsService;
let baseService: BaseService;
let appConstants: AppConstants;
let dmConstants: DMConstants;
beforeEach(()=>{
baseService=new BaseService(null,null);
appConstants=null;
dmConstants=null;
bomRevisiosnsService=new BomRevisiosnsService(baseService,appConstants,dmConstants);
});
it('getRevisionsData() call base service getData()',()=>{
let spy = spyOn(baseService, 'getData').and.returnValue(Observable.of())
bomRevisiosnsService.getRevisionsData();
expect(baseService.getData).toHaveBeenCalled();
});
})
Error: TypeError: Cannot read property 'userPreferences' of null
I believe I need to provide some mock value for this.appConstants.userPreferences.modelData['basicDetails']['itemId'];
and this.dmConstants.URLs.GETBOMREVISIONS + itemId + "/GetRevisionsAsync";
Yes, indeed, you need to provide a valid value for appConstants and dmConstants because the call to bomRevisiosnsService.getRevisionsData() uses that information internally.
So, instead of assigning null to appConstants and dmConstants, you could create an objects with some valid data, like this:
appConstants = {
userPreferences: {
modelData: {
basicDetails: {
itemId: 3 // some other valid value here is fine
}
},
UserBasicDetails: {
// some valid values here, maybe
}
}
};
dmConstants = {
URLs: {
GETBOMREVISIONS: 'revisions' // or just some valid value according to the use case
}
};
And the same goes to baseService.
In general, you need to create valid stub, mock, etc for all object, services, etc that are used internally by the service you're testing.

Unable to pass parameters to service hook through find function in featherjs

Featherjs find service unable to pass extra parameters through find function. In below find service passing extra params data to service.
but unable to receive the value at service hook.
Client code :
return this.app.service('userlist').find({
query: { usersIds: { "$in" : [this.user._id]} },
paginate: false,
params:{ name:'sam' }
}).then(response => {
}).catch(error => {
console.log(error);
});
Server code (Service hook ) :
module.exports = function (options = {}) {
return async function dogetUsr (context) {
const { data } = context;
console.log('Client Param data -->',context.params.name);
return context;
};
};
params data not receiving at server -->
params:{ name:'sam' }
Output at server/service hook :
Client Param data -->undefined
For security reasons, only params.query is passed between the client and the server. In general I wouldn't recommend letting the client disable pagination unless you are guaranteed to only get a few (less than 100) records. Otherwise requests with many records can cause major issues on both sides.
If it is still something you need, you can use the disablePagination hook which lets you set the $limit to -1 if you want to disable pagination:
const { disablePagination } = require('feathers-hooks-common');
module.exports = { before: {
find: disablePagination()
} };

Categories

Resources