Angular 10 get webservice status with Await? - angular10

Just before login in the user, I need to test if the WebServce respond then if the system is in maintenance. On the WebService part (core3 .net) I got 2 functions:
HeartBeat that return: return Ok("OK");
MaintenanceInfo that return Return OK("No");
I display in real time the result of the 2 tests and if no problems, I display the login panel.
I need to do these tests in sequence, I was thinking doing it with await operator.
I got a TestHelperServie class with 2 functions that return bool. But I can't find how to pass from a HTTP subscribe function to a bool result. With true if I got the OK response and false if I got a timeout or another fail HTTP.
For now I do this:
async TestHeartBeat()
{
let Response:Boolean = false;
const headers = new HttpHeaders()
.set('Content-Type','application/json');
const options = {
headers: headers,
observe: "response" as const,
responseType: "json" as const
};
await this.http.get(`${environment.apiUrl}/TestController/HeartBeat`, options)
.subscribe(res=>{
Response = true;
},
error=>{
Response = false;
})
return Respone;
}
But the function does not wait for the http response.
How can I fix this?
Sorry for my newbies question, but I came from the c# world and we use await/async a lot. I don't think I can do that the same way in angular 10.

You can not await on Observables, you have to convert it to promise using toPromise()
return await this.http.get(....).toPromise().then(res=> Response = true).catch(err => Response = false);

Use rxjs pipe, map and catchError.
async TestHeartBeat()
{
const headers = new HttpHeaders()
.set('Content-Type','application/json');
const options = {
headers: headers,
observe: "response" as const,
responseType: "json" as const
};
return await this.http.get(`${environment.apiUrl}/TestController/HeartBeat`, options)
.pipe(
map(res => true),
catchError(error => false)
).toPromise();
}
Now you can call it like the following:
if (await TestHeartBeat()) {
// Display your panel
} else {
// Service is in maintenance
}

Related

Cloudflare worker scheduled response API KV

So previously I tried to store KV when CRON was processed and it works.
Now I tried to store KV based on API responses, and tried the following: https://gist.github.com/viggy28/522c4ed05e2bec051d3838ebaff27258 and Forward body from request to another url for Scheduled (CRON workers) but doesn't seems to work.
What I attempt to do is save response in KV when CRON triggers for access_token and use it in front-end code later on.
Attached is my current attempt:
addEventListener("scheduled", (event) => {
console.log("cron trigger processed..", event);
event.waitUntil(handleRequest());
});
async function handleRequest() {
const url = "<request_url>";
const body = { <credential> };
const init = {
body: JSON.stringify(body),
method: "POST",
headers: {
"content-type": "application/json;charset=UTF-8"
}
};
const response = await fetch(url, init);
const results = await gatherResponse(response);
//#ts-ignore
AUTH.put("attempt1", results);
//#ts-ignore
AUTH.put("attempt2", new Response(results, init));
return new Response(results, init);
}
/**
* gatherResponse awaits and returns a response body as a string.
* Use await gatherResponse(..) in an async function to get the response body
* #param {Response} response
*/
async function gatherResponse(response) {
const { headers } = response
const contentType = headers.get("content-type") || ""
if (contentType.includes("application/json")) {
return JSON.stringify(await response.json())
}
else if (contentType.includes("application/text")) {
return await response.text()
}
else if (contentType.includes("text/html")) {
return await response.text()
}
else {
return await response.text()
}
}
Both attempt doesn't seems to work (KV not saved):
//#ts-ignore
AUTH.put("attempt1", results);
//#ts-ignore
AUTH.put("attempt2", new Response(results, init));
Is there anything I'm missing?
The calls to NAMESPACE.put() return a Promise that should be awaited - I'd assume that since handleRequest() can return before those are completed (since you're missing the await), they're just being cancelled.
https://developers.cloudflare.com/workers/runtime-apis/kv/#writing-key-value-pairs

How to get total member count of any Discord server?

I'm trying to build a scraping script to get a bunch of Discord server's total members. I actually did that with Puppeteer like below but I think my IP address has been banned because I'm getting "Invite Invalid" error from Discord even though invite links are working.
My question is that does Discord have APIs to get any server's total member count? Or is there any 3rd party library for that purpose? Or any other method?
const puppeteer = require('puppeteer')
const discordMembers = async ({ server, browser }) => {
if (!server) return
let totalMembers
const page = await browser.newPage()
try {
await page.goto(`https://discord.com/invite/${server}`, {
timeout: 3000
})
const selector = '.pill-qMtBTq'
await page.waitForSelector(selector, {
timeout: 3000
})
const totalMembersContent = await page.evaluate(selector => {
return document.querySelectorAll(selector)[1].textContent
}, selector)
if (totalMembersContent) {
totalMembers = totalMembersContent
.replace(/ Members/, '')
.replace(/,/g, '')
totalMembers = parseInt(totalMembers)
}
} catch (err) {
console.log(err.message)
}
await page.close()
if (totalMembers) return totalMembers
}
const asyncForEach = async (array, callback) => {
for (let i = 0; i < array.length; i++) {
await callback(array[i], i, array)
}
}
const run = async () => {
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox']
})
const servers = ['tQp4pSE', '3P5K3dzgdB']
await asyncForEach(servers, async server => {
const members = await discordMembers({ server, browser })
console.log({ server, members })
// result
// { server: 'tQp4pSE', members: 57600 }
// { server: '3P5K3dzgdB', members: 159106 }
})
await browser.close()
}
run()
Update: Mar 22, 2022
Thanks for #Vaviloff's answer we can actually access Discord's private APIs but the problem is it's only accessible over browser. I'm getting Request failed with status code 400 issue from Axios. Is it a CORS issue? How do we get the results in a Node.js app?
const axios = require('axios')
const discordMembers = async ({ server }) => {
try {
const apiResult = await axios({
data: {},
method: 'get',
url: `https://discord.com/api/v9/invites/${server}?with_counts=true&with_expiration=true`
})
console.log(apiResult)
} catch (err) {
console.log(err)
}
}
discordMembers({ server: 'tQp4pSE' })
A lot of modern web applications have their own internal APIs. Oftentimes you can spot frontend making requests to it, by using Networking tab in Devtools (filter by Fetch/XHR type):
Such API endpoints can change any time of course, but usually the last for a long time and is a rather convenient way of scraping
Currently Discord uses this URL for basic instance description:
https://discord.com/api/v9/invites/tQp4pSE?with_counts=true&with_expiration=true
By accessing it you get the desired data:
Update
To make your code work don't send any data in the request:
const apiResult = await axios({
method: 'get',
url: `https://discord.com/api/v9/invites/${server}?with_counts=true&with_expiration=true`
})

Trying to set a cookie established on a web session as a header back to API

I am trying to login via the webfront end and trying to intercept a cookie and then using that in the subsequent API request. I am having trouble getting the cookie back into the GET request. Code posted below.
import https from 'https';
import { bitbucketUser } from "../userRole.js"
import { ClientFunction } from 'testcafe';
fixture `Request/Response API`
// .page `https://myurl.company.com/login`
.beforeEach(async t => {
await t.useRole(bitbucketUser)
});
test('test', async t => {
const getCookie = ClientFunction(() => {
return document.cookie;
});
var mycookie = await getCookie()
const setCookie = ClientFunction(mycookie => {
document.cookie = mycookie;
});
var validatecookie = await getCookie()
console.log(validatecookie)
const executeRequest = () => {
return new Promise(resolve => {
const options = {
hostname: 'myurl.company.com',
path: '/v1/api/policy',
method: 'GET',
headers: {
'accept': 'application/json;charset=UTF-8',
'content-type': 'application/json'
}
};
const req = https.request(options, res => {
console.log('statusCode:', res.statusCode);
console.log('headers:', res.headers);
let body = "";
res.on("data", data => {
body += data;
});
res.on("end", () => {
body = JSON.parse(body);
console.log(body);
});
resolve();
});
req.on('error', e => {
console.error(e);
});
req.end();
});
};
await setCookie(mycookie)
await executeRequest();
});
I have tried several examples but am quite not able to figure what is it that I am missing.
When you call the setCookie method, you modify cookies in your browser using the ClientFunction.
However, when you call your executeRequest method, you run it on the server side using the nodejs library. When you set cookies on the client, this will not affect your request sent from the server side. You need to add cookie information directly to your options object as described in the following thread: How do I create a HTTP Client Request with a cookie?.
In TestCafe v1.20.0 and later, you can send HTTP requests in your tests using the t.request method. You can also use the withCredentials option to attach all cookies to a request.
Please also note that TestCafe also offers a cookie management API to set/get/delete cookies including HTTPOnly.

Axios interceptors don't send data to API in production Heroku app

This is part 2 of me debugging my application in production
In part 1, I managed to at least see what was causing my problem and managed to solve that.
When I send a request to my API which is hosted on Heroku using axios interceptor, every single request object looks like this in the API
{ 'object Object': '' }
Before sending out data to the API, I console.log() the transformRequest in axios and I can see that the data I am sending is actually there.
Note: I have tested this process simply using
axios.<HTTP_METHOD>('my/path', myData)
// ACTUAL EXAMPLE
await axios.post(
`${process.env.VUE_APP_BASE_URL}/auth/login`,
userToLogin
);
and everything works and I get data back from the server.
While that is great and all, I would like to abstract my request implementation into a separate class like I did below.
Does anyone know why the interceptor is causing this issue? Am I misusing it?
request.ts
import axios from "axios";
import { Message } from "element-ui";
import logger from "#/plugins/logger";
import { UsersModule } from "#/store/modules/users";
const DEBUG = process.env.NODE_ENV === "development";
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_URL,
timeout: 5000,
transformRequest: [function (data) {
console.log('data', data)
return data;
}],
});
service.interceptors.request.use(
config => {
if (DEBUG) {
logger.request({
method: config.method,
url: config.url
});
}
return config;
},
error => {
return Promise.reject(error);
}
);
service.interceptors.response.use(
response => {
console.log('axios interception response', response)
return response.data;
},
error => {
const { response } = error;
console.error('axios interception error', error)
if (DEBUG) {
logger.error(response.data.message, response);
}
Message({
message: `Error: ${response.data.message}`,
type: "error",
duration: 5 * 1000
});
return Promise.reject({ ...error });
}
);
export default service;
Login.vue
/**
* Sign user in
*/
async onClickLogin() {
const userToLogin = {
username: this.loginForm.username,
password: this.loginForm.password
};
try {
const res = await UsersModule.LOGIN_USER(userToLogin);
console.log("res", res);
this.onClickLoginSuccess();
} catch (error) {
throw new Error(error);
}
}
UsersModule (VUEX Store)
#Action({ rawError: true })
async [LOGIN_USER](params: UserSubmitLogin) {
const response: any = await login(params);
console.log('response in VUEX', response)
if (typeof response !== "undefined") {
const { accessToken, username, name, uid } = response;
setToken(accessToken);
this.SET_UID(uid);
this.SET_TOKEN(accessToken);
this.SET_USERNAME(username);
this.SET_NAME(name);
}
}
users api class
export const login = async (data: UserSubmitLogin) => {
return await request({
url: "/auth/login",
method: "post",
data
});
};
I'm not sure what you're trying to do with transformRequest but that probably isn't what you want.
A quote from the documentation, https://github.com/axios/axios#request-config:
The last function in the array must return a string or an instance of Buffer, ArrayBuffer, FormData or Stream
If you just return a normal JavaScript object instead it will be mangled in the way you've observed.
transformRequest is responsible for taking the data value and converting it into something that can actually be sent over the wire. The default implementation does quite a lot of work manipulating the data and setting relevant headers, in particular Content-Type. See:
https://github.com/axios/axios/blob/885ada6d9b87801a57fe1d19f57304c315703079/lib/defaults.js#L31
If you specify your own transformRequest then you are replacing that default, so none of that stuff will happen automatically.
Without knowing what you're trying to do it's difficult to advise further but you should probably use a request interceptor rather than transformRequest for whatever it is you're trying to do.

Testcafe multiple requests to endpoint synchronously not working

Currently I am setting up testcafe and have hit a hurdle when making a request to the same endpoint.
This is an authentication request. The steps are currently:
Initial request is sent to server which responds with a body. This is awaited
Once step is retrieved from step 1 this is mutated and sent back to the server.
We then the same endpoint and pass in the body generated in step 2
But I am having issues in the testcafe tests as it always using the first request and returns that twice.
I have referred to this issue on testcafe https://github.com/DevExpress/testcafe/issues/2477 however this did not work.
const mock = (mockResponse, status) => {
return RequestMock()
.onRequestTo(/\/apitest\/authenticate\)
.respond(mockResponse, status, {
"Access-Control-Allow-Origin": "http://localhost:3000",
"Access-Control-Allow-Credentials": "true"
});
};
const mock1 = mock(RESPONSE1, 200);
const mock2 = mock(RESPONSE2, 200);
test.requestHooks(mock1)(
"Should redirect to error if user has been suspended after submitting",
async t => {
const usernameInput = Selector("#username");
const mobileDigitOne = Selector("#mobile-digit-0");
const mobileDigitTwo = Selector("#mobile-digit-1");
const mobileDigitThree = Selector("#mobile-digit-2");
const submitButton = Selector("#submit-button");
await t
.typeText(usernameInput, "username123456")
.click(digits)
.typeText(digits, "123456")
.click(submitButton);
await t.removeRequestHooks(mock1);
await t.addRequestHooks(mock2);
Any ideas on how to request same endpoint multiple times?
You can distinguish different requests to the same endpoint using the request body. You can pass different kinds of filter to the mock's onRequestTo method, including a predicate function. Refer to the Filter with a Predicate article for more details.
Thus, you can use a single mock for both requests, like this:
function isTheSecondRequest(requestBody) {
// This should return `true` for the mutated request body
// and `false` for the original one.
//
// For example:
return requestBody.indexOf('{ foo: bar }') !== -1;
}
const mock = RequstMock()
.onRequestTo(request => {
request.url === '/apitest/authenticate' &&
!isTheSecondRequest(request.body)
})
.respond(RESPONSE1, 200, HEADERS)
.onRequestTo(request => {
request.url === '/apitest/authenticate' &&
isTheSecondRequest(request.body)
})
.respond(RESPONSE2, 200, HEADERS);