Capture Angular API response from service to component - javascript

In my Angular 12 application, I am making an API call in the service file and want to capture the response of the API in my component.
But since the API response is async, the console below always returns undefined. I have tried async await as well. Here is what I have tried:
INSIDE SERVICE:
public checkEmail(emailToVerify: any) {
this.myService.emailValidate({ email: emailToVerify }).subscribe({
next: (data: { result: any) => {
console.log('updateEmail-res', data);
return data;
},
error: (err: any) => {
console.log('updateEmail-err', err);
}
});
}
INSIDE COMPONENT:
this.apiResponse = await this.myService.checkEmail(customerEmail);
console.log("this.apiResponse", this.apiResponse)

In Component
validateEmail = async () => {
const params = {
emailAddress: 'test#gmail.com',
};
const apiResponse: boolean = await this.emailService.validateEmail(params);
console.log(`apiResponse===`, apiResponse);
};
In Service
export class EmailService {
constructor() {}
validateEmail = async (params: {
emailAddress: string;
}): Promise<boolean> => {
return new Promise((resolve) => {
const isValid = /^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(
params.emailAddress
);
resolve(isValid);
});
};
}

The checkemail method does nothing. If you already have emailValidate, why put it in another method?
just one emailValidate instead of checkEmail
this.apiResponse = await this.myService.emailValidate({ email: customerEmail});
console.log("this.apiResponse", this.apiResponse)
o
in you component:
this.myService.emailValidate({ email: customerEmail}).subscribe({
next: (data: { result: any) => {
console.log('updateEmail-res', data);
this.apiResponse = data;
},
error: (err: any) => {
console.log('updateEmail-err', err);
}
});

Related

Having issue with typing a return type of fetching function

I made an async custom function wrapper that takes care of response returning 401 unauthorized.
How do i properly type return type of my fetching function to make my data from useQuery infer that type?
// ASYNC WRAPPER
type HandlerType = (args?: any) => Promise<any>;
export const asyncWrapper =
(handler: HandlerType) =>
async (args?: any): Promise<any> => {
try {
const result = await handler(args);
return result;
} catch (err: any) {
if (err.response.status === 401) {
// refresh token then again call handler
await sessionService.refreshToken();
const result = await handler(args);
return result;
}
}
};
//FETCHING REQUEST
export type QuestionsType = {
answerOptions: {
_id: string;
answerText: string;
isCorrect: boolean;
};
questionText: string;
};
const getQuestions = asyncWrapper(
async (difficulty: string): Promise<QuestionsType[]> //type not working => {
const token = localStorage.getItem("accessToken");
try {
const response = await axios.get("/questions", {
headers: {
Authorization: token,
},
});
return response.data;
} catch (e) {
throw new Error("Custom");
}
}
);
const { data } = useQuery(["quiz"], quizService.getQuestions); // data type is "any"
Use generics to type it, here is a playground
export const asyncWrapper =
<A, R>(handler: (args: A) => Promise<R>) =>
async (args: A): Promise<R> => {
try {
return handler(args);
} catch (err: any) {
if (err.response.status === 401) {
// refresh token then again call handler
return handler(args);
}
}
throw new Error("Handle this")
};
//FETCHING REQUEST
export type QuestionsType = {
answerOptions: {
_id: string;
answerText: string;
isCorrect: boolean;
};
questionText: string;
};
const getQuestions = asyncWrapper(
async (difficulty: string): Promise<QuestionsType[]> => {
const token = localStorage.getItem("accessToken");
try {
return [];
} catch (e) {
throw new Error("Custom");
}
}
);

Error: function uses multiple asynchronous interfaces: callback and promise

Error: function uses multiple asynchronous interfaces: callback and
promise
to use the callback interface: do not return a promise
to use the promise interface: remove the last argument to the function
I'm trying to write a cucumber test to one of my GET node API, and keep getting the above, looked at few GitHub and stack-overflow posts, and could not understand the issue, below are my test method details.
App.ts
async getSsoId(refId: any): Promise<string> {
let ssoId = '';
const secrets = await this.vaultService.getClientSecrets();
this.decrypt(refId, secrets.encryption_secret, secrets.encryption_Id).then((value: any) => {
ssoId = value;
});
return ssoId;
}
api.get('/status', async (req, res) => {
let id;
const statusCode = 200;
try {
id = await this.getId(String('123456'));
} catch (err: any) {
throw new ApiError('Error fetching id');
}
try {
const item = await dbService.getItem(id);
if (item) {
statusCode = 201;
} else {
statusCode = 202;
}
} catch (err: any) {
throw new ApiError(
'The API encountered an error while attempting to communicate with the DB service.'
);
}
res.status(statusCode).send(statusCode);
});
Step Definition:
Given('a valid customer', function () {});
When("I call the get-id api", { timeout: 2 * 5000 }, async function (val) {
util.callGetIdAPI().then((response) => {
this.setApiResponseStatus(response.status);
this.setResponseBody(JSON.stringify(response.body));
});
});
Then("the apiResponseStatus should be <apiResponseStatus>", function (status) {
assert.strictEqual(status, this.apiResponseStatus);
});
Then("the responseBody should be {string}", function (responseBody) {
assert.equal(responseBody, this.responseBody);
});
Util Function
callGetIdAPI = async () => {
const headers = {
'Content-Type': 'application/json;v=1',
Accept: 'application/json;v=1'
}
const client = await getClient('url');
const options = {
method: 'GET',
headers: headers,
version: 3
};
let response;
try {
response = await client.get('/status', options);
return {
status: response.statusCode,
body: response.body
};
} catch(error) {
return {
status: error.statusCode,
body: {
error: {
id: error.id,
message: error.message
}
}
}
}
};
I'm new to this and trying to understand how multiple Premisses and Callbacks works in parallel, any thoughts or inputs on what possibly cause the error, or am I missing anything ??

In angular 12 I need to call an api which will give token and then I need to pass that token to another api to call but my code is not working

Below is my code having two methods getToken and validateuser, I am
taking the token from getToken and passing it to the parameter of validateuser. But before getting the token my second api calls is getting executed.
ngOnInit(): void {
this.login()
}
getToken(){
const authParam = {
username: "abc",
password: "abc"
};
this.apiService.getToken(authParam)
.subscribe(
response => {
console.log("here1");
this.token = response;
console.log(this.token);
},
error => {
console.log(error);
});
}
vlidateUser(){
this.getToken();
const loginParam ={
userId:"abc",
serviceType:"UV",
auth_token:this.token
}
this.apiService.validateUser(loginParam)
.subscribe(
response => {
console.log("here2");
console.log(response);
},
error => {
console.log(error);
});
}
login(){
this.vlidateUser();
}
}
Use async before vlidateUser()
Use await before this.getToken()
ngOnInit(): void {
this.login()
}
getToken(){
const authParam = {
username: "abc",
password: "abc"
};
this.apiService.getToken(authParam)
.subscribe(
response => {
console.log("here1");
this.token = response;
console.log(this.token);
},
error => {
console.log(error);
});
}
async vlidateUser(){
await this.getToken();
const loginParam ={
userId:"abc",
serviceType:"UV",
auth_token:this.token
}
this.apiService.validateUser(loginParam)
.subscribe(
response => {
console.log("here2");
console.log(response);
},
error => {
console.log(error);
});
}
login(){
this.vlidateUser();
}
}
Use async await
ngOnInit(): void {
this.login()
}
getToken(){
const authParam = {
username: "abc",
password: "abc"
};
this.apiService.getToken(authParam)
.subscribe(
response => {
console.log("here1");
this.token = response;
console.log(this.token);
},
error => {
console.log(error);
});
}
async vlidateUser(){
await this.getToken();
const loginParam ={
userId:"abc",
serviceType:"UV",
auth_token:this.token
}
this.apiService.validateUser(loginParam)
.subscribe(
response => {
console.log("here2");
console.log(response);
},
error => {
console.log(error);
});
}
login(){
this.vlidateUser();
}
}
A better way to tackle this problem is to use mergeMap, switchMap operators instead of using async.
switchMap allows you to use the response of one observable (getToken() API in your case) in the next observable (validateUser() API)
login() {
const authParam = {
username: 'abc',
password: 'abc',
};
this.apiService
.getToken(authParam)
.pipe(
switchMap(response => {
const token = response;
const loginParam = {
userId: 'abc',
serviceType: 'UV',
auth_token: token,
};
return this.apiService.validateUser(loginParam);
})
)
.subscribe(
(resp) => {
console.log('here2');
console.log(resp);
},
error => {
console.log(error);
}
);
}
Your validateUser API will run only after getToken returns a value. I've used switchMap to use the latest getToken value (if there are multiple API calls)

Nest.JS and Observables

I am new to Nest.JS and apparently don't understand how to use observables so hopefully ya'll can help.
Basically I have a method that needs to:
first: login to hashicorp vault and return a client_token via an http call.
second: if we got a token back from vault, we then check that the request contained a certification-id, if not we have to request a new certification to be generated. Which requires the client_token from vault.
The problem I am having is that when I call vault to get the client_token, it does not get returned in time for me to be able to use it to generate a new cert via a second api call.
What can I do in order to be able to use the client_token in the next step?
Here is the code for my latest attempt:
Controller:
#Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}
#Post('getUserCert')
async getUserCert(#Body() loginDto: vaultLoginReqDto) {
return this.userService.getCertificate(loginDto);
}
}
Controller calls the getCertificate method:
getCertificate(loginDto: vaultLoginReqDto) {
this.loginToVault(loginDto);
if (this.vault_token) {
if (loginDto.cert_id) {
this.checkForExistingCert(loginDto);
} else {
this.generateNewCert(this.vault_token);
}
} else {
throw new Error('User is not authorized to access Vault.');
}
}
The logon method:
loginToVault(loginDto: vaultLoginReqDto) {
const url = 'http://vault:8200/v1/auth/jwt/login';
const payload: vaultLoginReqDto = {
jwt: loginDto.jwt,
role: loginDto.role,
};
try {
this.httpService
.post(url, payload)
.subscribe((res: AxiosResponse<vaultLoginResDto>) => {
this.vault_token = res.data.auth.client_token;
});
} catch (e) {
this.throwError(e, url, 'Unable to login to vault');
}
}
the problem method is the generateNewCert method. It is not getting the vault_token in time.
generateNewCert(vault_token: string): Observable<string> {
const url = `http://127.0.0.1:8200/v1/xxxx/xxxx/issue/reader`;
const payload = {
common_name: 'id.xxxx.com',
};
const headers = {
'X-Vault-Token': vault_token,
};
try {
return this.httpService.post(url, payload, { headers: headers }).pipe(
map((res: AxiosResponse<vaultGetCertResDto>) => {
return res.data.data.certificate;
}),
);
} catch (e) {
this.throwError(e, url);
}
}
I appreciate the help!
The easiest way to make it work is the convert to a Promise so you can wait for the result.
loginToVault(loginDto: vaultLoginReqDto) {
const url = 'http://vault:8200/v1/auth/jwt/login';
const payload = {
jwt: loginDto.jwt,
role: loginDto.role,
};
return this.httpService
.post(url, payload)
.pipe(
catchError(() => {/** ...handleError **/}),
map((res) => {
this.vault_token = res.data.auth.client_token;
return this.vault_token;
}),
)
.toPromise()
}
Now, you can use async / await at getCertificate
async getCertificate(loginDto: vaultLoginReqDto) {
await this.loginToVault(loginDto);
// or const vault_token = await this.loginToVault(loginDto)
if (this.vault_token) {
if (loginDto.cert_id) {
this.checkForExistingCert(loginDto);
} else {
this.generateNewCert(this.vault_token);
}
} else {
throw new Error('User is not authorized to access Vault.');
}
}
If you decide to stick with the observables, you can return an observable from the loginToVault method as opposed to subscribing to it
loginToVault(loginDto: vaultLoginReqDto): Observable<string> {
const url = 'http://vault:8200/v1/auth/jwt/login';
const payload = {
jwt: loginDto.jwt,
role: loginDto.role,
};
return this.httpService
.post(url, payload)
.pipe(
catchError(() => { /* handle errors */ }),
map((res) => res.data.auth.client_token)
)
}
Then in getCertificate method, you subscribe to loginToVault and handle the logic
getCertificate(loginDto: vaultLoginReqDto) {
this.loginToVault(loginDto)
.pipe(
tap(vault_token => {
if (!vault_token) {
throw new Error('User is not authorized to access Vault.');
}
})
)
.subscribe(vault_token => loginDto.cert_id ?
this.checkForExistingCert(loginDto) :
this.generateNewCert(vault_token)
)
}
The vault_token is passed from one service to another and thus will be accessible in the generateNewCert method. You do not need to declare it globally

mongodb : get all users

how do i edit this function of mine to get all users ? I have just started learning async await and i am having hard time learning how to get the request body.
here is my function :
export const get: Operation = async (
req: express.Request,
res: express.Response
) => {
commonUtility.showRequestParam(req);
let users: db.IUserDocument[] = [];
try {
// Describe data acquisition and registration from mongoDB here.
users = await UserModel.find()
.then(data => {
return data;
})
.catch(err => {
throw err;
});
} catch (err) {
// Error.
api.responseError(res, err);
}
if (users.length < 1) {
// this case is 404 ???
api.responseJSON(res, 200, []);
}
};
here is my user model:
export const usersSchema = new Schema({
username: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
BaseFields
});
export const UserModel = mongoose.model<db.IUserDocument>('Users', usersSchema);
You don't need to use .then when using async and await
export const get: Operation = async (
req: express.Request,
res: express.Response
) => {
commonUtility.showRequestParam(req);
let users: db.IUserDocument[] = [];
try {
users = await UserModel.find();
api.responseJSON(res, 200,users);
} catch (err) {
// Error.
api.responseError(res, err);
}
};
Read more about async await here -> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

Categories