How to use a specific port while Testing web service clients - scalatest

Im following the play framework 2.5 documentation to write tests using web service clients
https://www.playframework.com/documentation/2.5.x/ScalaTestingWebServiceClients
The code below is extracted from above link which works. However as mentioned in the documentation, a random available port is assigned when using implicit port.
Is it possible to give a specific port instead of random port?
import play.core.server.Server
object GitHubClientSpec extends Specification with NoTimeConversions {
"GitHubClient" should {
"get all repositories" in {
Server.withRouter() {
case GET(p"/repositories") => Action {
Results.Ok(Json.arr(Json.obj("full_name" ->
"octocat/HelloWorld")))
}
} { implicit port =>
WsTestClient.withClient { client =>
val result = Await.result(
new GitHubClient(client, "").repositories(), 10.seconds)
result must_== Seq("octocat/Hello-World")
}
}
}
}
}

If you try to get into the definition of withRouter, you'll see it takes ServerConfig in which you can provide the port and run mode.
import play.core.server.Server
object GitHubClientSpec extends Specification with NoTimeConversions {
"GitHubClient" should {
"get all repositories" in {
//here 8888 is the port which you have defined.
Server.withRouter(ServerConfig(port = Some(8888), mode = Mode.Test)) {
case GET(p"/repositories") => Action {
Results.Ok(Json.arr(Json.obj("full_name" ->
"octocat/HelloWorld")))
}
} { implicit port =>
//The port will 8888 in this block
WsTestClient.withClient { client =>
val result = Await.result(
new GitHubClient(client, "").repositories(), 10.seconds)
result must_== Seq("octocat/Hello-World")
}
}
}
}
}
Hope that helps, happy coding :)

Adding this to build.sbt worked
PlayKeys.devSettings := Seq("play.server.http.port" -> "9100")

Related

Winston logger's CustomTransport's callback is called only once

I am trying to add a new websocket connection to my app (I already have a few and none of them had this issue).
The idea is to send logs to my spring boot application from my reactjs frontend.
Here's my client's code:
connectToLoggingWebsocket = ip => {
if (this.props.loggingWebsocketConnected === false) {
loggingStompClient = Stomp.client(`${this.props.environment.webSocketUrl}/logging`);
loggingStompClient.connect({ name: ip }, frame => this.loggingStompSuccessCallBack(frame, loggingStompClient), err => this.loggingStompClientFailureCallBack(err, ip));
}
}
loggingStompSuccessCallBack = (frame, stompClient) => {
console.log(`Connected: ${frame}`);
this.props.setLoggingWebsocketConnected(true);
stompClient.subscribe(LOGGING_NODE, data => {
console.log(data);
});
}
loggingStompClientFailureCallBack = (error, ip) => {
console.log('Exception: ', error);
console.log('Trying to reconnect in 10 seconds');
this.props.setLoggingWebsocketConnected(false);
setTimeout(() => this.connectToLoggingWebsocket(ip), 10000);
}
/* THIS IS OUTSIDE THE CLASS SCOPE */
export const sendLog = log => {
console.log("TRYING TO SEND")
console.log(loggingStompClient);
if (loggingStompClient) {
loggingStompClient.send("/app/logging", log.message);
}
};
And I am using winston -> it calls the sendLog method via a custom Transport
class CustomTransport extends Transport {
log = (info) => {
console.log("SENDING...")
sendToWebsocket(info);
}
}
// Using transport
const transport = new CustomTransport({});
// Create a logger and consume an instance of your transport
export const logger = winston.createLogger({
level: 'info',
transports: [ transport,
new winston.transports.Console() ]
});
const sendToWebsocket = message => {
console.log(message);
sendLog(message);
}
My sendLog method is NOT in my React class because I have to export it for winston. loggingStompClient is also not in my class since sendLog is calling send off of it.
var loggingStompClient = null;
class Key extends React.Component {
componentDidMount = () => { ... }
...
}
My service looks like this - I am basically sending something from the browser that invokes receivedLog -> it then sends a message to the browser - this works exactly ONCE
#MessageMapping("/logging")
public void receivedLog(String log) {
this.template.convertAndSend("/topic/logging", "logging");
logger.info("Received: {}", log);
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/logging").setAllowedOrigins("*");
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableStompBrokerRelay(MESSAGE_PREFIX, "queue", "/amq/").setRelayHost(rabbitmqUri)
.setSystemLogin(rabbitmqUsername).setClientLogin(rabbitmqUsername).setClientPasscode(rabbitmqPassword)
.setSystemPasscode(rabbitmqPassword).setAutoStartup(true)
.setSystemHeartbeatReceiveInterval(10000)
.setSystemHeartbeatSendInterval(10000);
registry.setApplicationDestinationPrefixes("/app");
}
Here's my browser's output:
CONNECT
name:127.0.0.1
accept-version:1.0
heart-beat:10000,10000
----------------------
CONNECTED
server:RabbitMQ/3.7.13
session:session-9l1yyKAGsRZlWwKfOFAqKg
heart-beat:10000,10000
version:1.0
user-name:127.0.0.1
----------------------
SUBSCRIBE
id:sub-1661872200224-169
destination:/topic/logging
----------------------
SEND
destination:/app/logging
content-length:63
ADDING TO ITEM: 6a5dd06d-7a66-4048-91db-a74525f87252
----------------------
MESSAGE
subscription:sub-1661872200224-169
destination:/topic/logging
message-id:T_sub-1661872200224-169##session-9l1yyKAGsRZlWwKfOFAqKg##1
redelivered:false
content-type:text/plain;charset=UTF-8
content-length:7
logging
-----------------------
ERROR
message:Connection to broker closed.
content-length:0
enter image description here
It also sends a bunch of PONG messages and never receives a PING
I've been sitting on this all day and I can't figure it out. Also turned everything off and on again.
Thanks!
EDIT
if I set the following the websocket doesn't disconnect. I am also able to continuously send messages to my frontend, but I still can only send ONCE from my frontend to the service.
loggingStompClient.heartbeat.outgoing = 0;
loggingStompClient.heartbeat.incoming = 0;
This method somehow gets called only ONCE.
export const sendLog = log => {
console.log("TRYING TO SEND")
console.log(loggingStompClient);
if (loggingStompClient) {
loggingStompClient.send("/app/logging", log.message);
}
};
Could it be that my winston logger works only once? I tried binding my websocket send method to a button and spring boot does register the messages.
So am I using winston incorrectly? It does log to console but my custom transport's callback is not being called.
const sendToWebsocket = message => {
console.error('SEND TO WEBSOCKET')
sendLog(message);
};
class CustomTransport extends Transport {
log = info => sendToWebsocket(info);
}
// Using transport
const transport = new CustomTransport({
name: 'custom-transport',
handleExceptions: true
});
// Create a logger and consume an instance of your transport
export const logger = createLogger({
exitOnError: false,
level: 'info',
transports: [ transport,
new transports.Console({
name: 'console',
handleExceptions: true
}) ]
});
In the documentation there is this callback() method which I thought was a placeholder for my own callback. I removed it and added my callback -> this was the problem.
Do NOT omit callback()
Now my code looks like this and it works.

Invalid value for transfer with ipcRenderer.postMessage in Electron

I'm getting this error message : Invalid value for transfer when trying to use, for the very first time, the message-ports-reply-streams.
In preload.js I defined this api:
contextBridge.exposeInMainWorld(
"api", {
electronIpcPostMessage: (channel: string, message: any, transfer?: MessagePort[]) => {
ipcRenderer.postMessage(channel, message, transfer)
},
}
)
declare global {
interface Window {
api: {
electronIpcPostMessage: (channel: string, message: any, transfer?: MessagePort[]) => void;
}
}
And , following the example found here: https://www.electronjs.org/docs/latest/tutorial/message-ports#reply-streams , in the renderer React component I defined the streaming request as follows:
const Layers = () => {
const componentIsMounted = React.useRef(true)
React.useEffect(() => {
const cb = (event, args) => {
try {
if (componentIsMounted.current) {
console.log("react-leaflet-layers-args: ", args)
}
} catch (err) {
console.log("err: ", err)
}
}
const makeStreamingRequest = (element, cb) => {
// MessageChannels are lightweight--it's cheap to create a new one for each request.
const { port1, port2 } = new MessageChannel()
// We send one end of the port to the main process ...
window.api.electronIpcPostMessage(
'give-me-a-stream',
{ element, count: 10 },
[port2]
)
// ... and we hang on to the other end.
// The main process will send messages to its end of the port,
// and close it when it's finished.
port1.onmessage = (event) => {
cb(event.data)
}
port1.onclose = () => {
console.log('stream ended')
}
}
makeStreamingRequest(42, (data) => {
console.log('got response data:', event.data)
})
// We will see "got response data: 42" 10 times.
return () => { // clean-up function
componentIsMounted.current = false
window.api.electronIpcRemoveListener(
"give-me-a-stream",
cb,
)
}
}, [])
As said, when running Electron-React app the error message I get when accessing the page rendered by that component, is : Invalid value for transfer .
From this StackOverflow question : Invalid value for transfer while using ipcRenderer.postMessage of electron, it seems that I'm not the only one stumbling on this type of error, but I didn't find any solutions yet.
What am I doing wrongly or missing? How to solve the problem?
My objective is to send, better in a streaming fashion, a very big geojson file from the main process to the renderer process. That's why I thought to try to use ipcRenderer.postMessage.
By the way, any other working solutions that accomplish this goal, are welcomed.
Other info:
electron: v. 16
node: v. 16.13.0
O.S: Ubuntu 20.04
Looking forward to hints and help
I also encountered the same problem. In https://www.electronjs.org/docs/latest/api/context-bridge, it is mentioned that the types of parameters, errors and return values in functions bound with contextBridge are restricted, and MessagePort is one of the types that cannot be transported, so it doesn't recognize the MessagePort you passed in and throw this error.
If you want to use MessageChannel for communication, you can provide some proxy functions through contextBridge in preload.js, call these functions in renderer.js and pass in copyable parameters.
Hope my answer helps you.

Why does the websocket close and open another one?

I'm having a problem with the ws . In principle I should only have one ws running in the application. But in certain situations (which I don't understand) two things happen:
More than one ws are started , which have no messages ( only header ) and the last one is the one that connects / subscribes.
The ws starts normally ( only 1 ) but suddenly ( without any clear trigger ) unsubscribes from everything and opens another ws . Which by the logic of my application generates me problems.
Here is the code, this would be the one that creates the Ws with rxjs :
import { Observable } from "rxjs";
/**
* Create a stomp connection to a given url
*
* #param url base url to connect.
* #param debug (Optional) flag to set debug traces
*/
export function createStompConnection(url: string, debug = false) {
// create the main connection and return and observable
const observerStomp: Observable<{ connected: boolean }> = Observable.create(
(observer: Observer<any>) => {
// define callbacks for the stomp connection
const _connectCallback = () => {
observer.next({ connected: true });
};
const _errorCallback = () => {
observer.next({ connected: false });
};
const _closeEventCallback = () => {
observer.next({ connected: false });
};
connectCallback.setCallback(_connectCallback);
errorCallback.setCallback(_errorCallback);
closeEventCallback.setCallback(_closeEventCallback);
// connect to client
connect();
// set reconnect delay
client.reconnect_delay = 1000;
// return disconnection handle, should be client.disconnect()
return () => console.warn("client disconnected");
}
);
and here is where you call and pass the subscribers ( in the redux store ) .
const stomp = createStompConnection(proxyWs);
I am not very clear about the management that makes the ws in the front end, so ask me and I will post any part of code that is necessary and I have missed.
Any idea why this behaviour is the expected one? Thx !

React native, get value from url browser

How do i get the value of url from the browser? using the react native
If you can make callbacks from the gateway website, then I recommend to use deep linking to handle flow between app and browser. Basically, your app will open the gateway website for payment, and depending on payment result, the website will make a callback to the app using its deep link. App then will listen to the link, take out necessary information and continue to proceed.
What you need to do is:
Set up deep linking in your app. You should follow the guide from official website (here) to enable it. Let pick a random URL here for linking, e.g. gatewaylistener
Set the necessary callbacks from gateway to your app. In your case, since you need to handle successful payment and failed payment, you can add 2 callbacks, e.g. gatewaylistener://success?id={paymentId} and gatewaylistener://error?id={paymentId}
Finally, you need to listen to web browser from the app. One way to do that is add listener right inside the component opening the gateway.
// setup
componentDidMount() {
Linking.getInitialURL().then((url) => {
if (url) {
this.handleOpenURL(url)
}
}).catch(err => {})
Linking.addEventListener('url', this.handleOpenURL)
}
componentWillUnmount() {
Linking.removeEventListener('url', this.handleOpenURL)
}
// open your gateway
async openGateWay = () => {
const { addNewOrderGatewayToken } = this.props
const url = `${BASEURL}${addNewOrderGatewayToken}`
const canOpen = await Linking.canOpenURL(url)
if (canOpen) {
this.props.dispatch(setPaymentStatus('checked'))
Linking.openURL(url)
}
}
// handle gateway callbacks
handleOpenURL = (url) => {
if (isSucceedPayment(url)) { // your condition
// handle success payment
} else {
// handle failure
}
}

RTCPeerConnection and createDataChannel not working for Edge

I have been working on an application where when a person logs into an account, the IP address of the device is stored in the backend and local storage. Then when a person logs into the same account from another browser or so, it will show a popup with the last login IP address. It seems to work on chrome and Mozilla but not working in edge browser. It always returns null because it's not entering the code after pc.createDataChannel.
The code snippet is as below.
const getIpAddress = state => {
window.RTCPeerConnection = window.RTCPeerConnection ||
window.mozRTCPeerConnection || window.webkitRTCPeerConnection || false;
let ip = false;
if (window.RTCPeerConnection) {
ip = [];
var pc = new RTCPeerConnection({ iceServers: [] }), noop = function
() {
};
pc.createDataChannel('');
pc.createOffer(pc.setLocalDescription.bind(pc), noop);
pc.onicecandidate = function (event) {
if (event && event.candidate && event.candidate.candidate) {
var s = event.candidate.candidate.split('\n');
ip.push(s[0].split(' ')[4]);
localStorage.setItem('ipV4Address', ip[0]);
}
};
}
return state;
};
I also tried using adapter js but not sure how to exactly use it.
WebRTC Peer-to-peer connections not yet supported fully on Edge v18 browser yet. You can check your browser compatible at Can I use RTCPeerConnection ?
It seems that there is no way to actually get the private ip in edge browser, so had to use a third party api to get public ip and use that.

Resources