Notification when ReactiveCommand completes - system.reactive

I'm trying to use ReactiveUI ReactiveCommands to switch on and off a gRPC stream that I've converted into an observable.
The code shown below works to some extent - the connect button will cause the stream to connect, and I start receiving data in the onNext handler of the subscribe. The disconnect button does also disconnect the stream via the cancellation token.
However, once the disconnect command is executed, I would also like to be notified so I can clear up some other state in the application. I understand that the onCompleted of the ReactiveCommand never gets called, because at any point it could be executed again, so my question is - how can I know when the stream has been switched off?
View
this.WhenActivated(d =>
{
d(this.BindCommand(ViewModel, x => x.ConnectCommand, x => x.Connect));
d(this.BindCommand(ViewModel, x => x.DisconnectCommand, x => x.Disconnect));
});
ViewModel
ConnectCommand = ReactiveCommand.CreateFromObservable(
() => appService.ApplicationStream(request)
.TakeUntil(DisconnectCommand));
ConnectCommand.Subscribe(
resp =>
{
_logger.Debug(resp);
},
() =>
{
// Ideally I could do something useful here, but https://stackoverflow.com/a/26599880/57215
_logger.Debug("Never called, ReactiveCommands never OnComplete");
});
ConnectCommand.IsExecuting.Subscribe(x => _logger.Debug($"is executing: {x}"));
ConnectCommand.CanExecute.Subscribe(x => _logger.Debug($"can execute: {x}"));
ConnectCommand.ThrownExceptions.Subscribe(ex =>
throw new Exception($"Could not get data from server: {ex}"));
DisconnectCommand = ReactiveCommand.Create(
() => { },
ConnectCommand.IsExecuting);
Service
public IObservable<ApplicationStreamResponse> ApplicationStream(ApplicationStreamRequest request)
{
return Observable.Create<ApplicationStreamResponse>(async (observer, token) =>
{
try
{
using (var call = _client.ApplicationStream(request, cancellationToken: token))
{
while (await call.ResponseStream.MoveNext())
{
if (token.IsCancellationRequested) return;
observer.OnNext(call.ResponseStream.Current);
}
observer.OnCompleted();
}
}
catch (RpcException e)
{
if (e.Status.StatusCode == StatusCode.Cancelled)
{
_logger.Debug($"Application stream was disconnected: {e.Status.Detail}");
observer.OnCompleted();
}
observer.OnError(e);
}
});
}

Subscribe to the command:
d(this.BindCommand(ViewModel, x => x.DisconnectCommand, x => x.Disconnect));
this.ViewModel.DisconnectCommand
.Subscribe(_ => { /* command finished */});
Or create a bool reative property, set it to true at the end of the DisconnectCommand code, and check the value in the view.

Related

I am developing VS Code extension and I need to capture the call stack records and log the result

I am writing a simple VS Code extension that suppose to just log the call stack in the console at specific point while debugging a code.
I was able to write a code to retrieve the current session of debugging, the break points and things like this, but I failed to find any property or method to allow me retrieve the call stack records.
This is the code I wrote:
export function activate(context: vscode.ExtensionContext) {
console.log('Congratulations, your extension "sampleextension1" is now active!');
let disposable = vscode.commands.registerCommand('sampleextension1.hello', () => {
vscode.window.showInformationMessage('Hello World from sampleextension1!');
vscode.commands.executeCommand('editor.action.addCommentLine');
vscode.debug.onDidStartDebugSession(x => {
});
vscode.debug.onDidChangeActiveDebugSession(c => {
var b = vscode.debug.breakpoints[0];
});
});
context.subscriptions.push(disposable);
}
As you see in the code, there is an event handler for onDidChangeActiveDebugSession which enables me to capture the session of the debugging but no chance to find how to capture the stack trace.
I went through the documentation but it's not helpful though.
I was able to achieve what I want by sending a CutomRequest to the debugging session to retrieve the stack frames.
More information could be found in the DAP page here
The code is as shown below:
x.customRequest('stackTrace', { threadId: 1 }).then(reply => {
const frameId = reply.stackFrames[0].id;
}, error => {
vscode.window.showInformationMessage(`error: ${error.message}`);
});
or more efficient is to register tracker as shown below:
vscode.debug.registerDebugAdapterTrackerFactory('*', {
createDebugAdapterTracker(session: vscode.DebugSession) {
return {
onWillReceiveMessage: m => console.log(`> ${JSON.stringify(m, undefined, 2)}`),
onDidSendMessage: m => console.log(`< ${JSON.stringify(m, undefined, 2)}`)
};
}
});
The full example is shown here:
export function activate(context: vscode.ExtensionContext) {
console.log('Congratulations, your extension "sampleextension1" is now active!');
let disposable = vscode.commands.registerCommand('sampleextension1.hello', () => {
vscode.window.showInformationMessage('Hello World from sampleextension1!');
vscode.commands.executeCommand('editor.action.addCommentLine');
vscode.debug.onDidStartDebugSession(x => {
// x.customRequest("evaluate", {
// "expression": "Math.sqrt(10)"
// }).then(reply => {
// vscode.window.showInformationMessage(`result: ${reply.result}`);
// }, error => {
// vscode.window.showInformationMessage(`error: ${error.message}`);
// });
x.customRequest('stackTrace', { threadId: 1 }).then(reply => {
const frameId = reply.stackFrames[0].id;
}, error => {
vscode.window.showInformationMessage(`error: ${error.message}`);
});
});
vscode.debug.onDidChangeActiveDebugSession(c => {
var b = vscode.debug.breakpoints[0];
});
vscode.debug.registerDebugAdapterTrackerFactory('*', {
createDebugAdapterTracker(session: vscode.DebugSession) {
return {
onWillReceiveMessage: m => console.log(`> ${JSON.stringify(m, undefined, 2)}`),
onDidSendMessage: m => console.log(`< ${JSON.stringify(m, undefined, 2)}`)
};
}
});
});
Steps to run:
F5 to run the Extension Dev Environment.
Ctl+Shift+P then write your cmd, in my case it was Hello
Then F5 to start the debugging in the Dev Environment then you will be able to see the result.
Hope it helps

While testing error responses, the test fails with the expected error (React/Jest/ReactQuery/Axios/MSW)

I am trying to test error states of the following MSW rest endpoint:
import { rest } from 'msw'
export const exceptionHandlers = [
rest.post(config.accountApiUrl + '/login', (req, res, ctx) => {
return res(
ctx.status(500),
ctx.json({ data: { message: 'Mock Error Message' } })
)
})
]
This endpoint is called in a custom hook return function thats using React Query's mutateAsync:
const { mutateAsync } = useMutation(AuthApi.login)
const handleLogin = async (props): Promise<void> => {
await mutateAsync(props, {
onSuccess: async () => {
// this block tests fine
}
onError: async () => {
console.log('!!!')
// it reaches this block, '!!!' is logged to the console,
// but the test still fails with `Request failed with status code 500`
}
})
}
return handleLogin
In a test file:
it('handles network errors', async () => {
mswServer.use(...exceptionHandlers)
const user = userEvent.setup()
const screen = render(<LoginForm />)
const submitButton = screen.getByTestId('Login.Submit')
// Complete form
await user.click(submitButton)
})
It doesnt matter what comes after that, the test always fails with
Request failed with status code 500
at createError (node_modules/axios/lib/core/createError.js:16:15)
at settle (node_modules/axios/lib/core/settle.js:17:12)
at XMLHttpRequestOverride.onloadend (node_modules/axios/lib/adapters/xhr.js:54:7)
at XMLHttpRequestOverride.trigger (node_modules/#mswjs/interceptors/src/interceptors/XMLHttpRequest/XMLHttpRequestOverride.ts:176:17)
at node_modules/#mswjs/interceptors/src/interceptors/XMLHttpRequest/XMLHttpRequestOverride.ts:354:16
But its supposed to fail with status 500. That's the whole point. If I change the handler to return another error, ie ctx.status(404), then the test just fails with that error code.
I've tried wrapping the assertion in a try/catch block but the same thing results. I see examples online of people doing (apparently) exactly this and it works fine, so I'm quite confused what's causing this. All other tests that check success states work as expected.
i've had the same problem.
As far as i could understand, the problem is that in test environment there is no handler for the rejected promise.
https://github.com/TanStack/query/issues/4109

socket.io-client Jest testing inconsistent results

I am writing some end-to-end test cases to test socket connections in my app. I expect receiving socket events after specific rest API requests. For instance, after hitting: /api/v1/[createTag], I expect receiving createTag event to be captured by socket.io-client. The issue is that, it is very inconsistently passing, and sometimes failing, with good rest API requests. The reason to fail is that done() event inside socket.on('createTag' ... is never called, so it gets timeout. On browser, currently all the API endpoints and sockets seem to be working fine. Is there a specific configuration that I might be missing in order to test socket.io-client within Node.js environment and Jest?
Below is my test cases, and thanks a lot in advance:
describe('Socket integration tests: ', () => {
beforeAll(async done => {
await apiInit();
const result = await requests.userSignIn(TEST_MAIL, TEST_PASSWORD);
TEST_USER = result.user;
SESSION = result.user.session;
console.log('Test user authenticated succesfully.');
done();
});
beforeEach(done => {
socket = io(config.socket_host, { forceNew: true })
socket.on('connect', () => {
console.log('Socket connection succesful.');
socket.emit('session', { data: SESSION }, (r) => {
console.log('Socket session successful.');
done();
});
});
})
test('Receiving createTag socket event?', async(done) => {
console.log('API request on createTag');
const response = await Requester.post(...);
console.log('API response on createTag', response);
socket.on('createTag', result => {
console.log('createTag socket event succesful.');
createdTagXid = result.data.xid;
done();
})
});
afterEach(done => {
if(socket.connected) {
console.log('disconnecting.');
socket.disconnect();
} else {
console.log('no connection to break');
}
done();
})
}
Basically, setting event handles after async API calls seems to be the issue. So I should have first set the socket.on( ... and then call rest API.

Unable to get Moxios stubRequest to work

I'm having issues getting stubRequest to work properly. Here's my code:
it('should stub my request', (done) => {
moxios.stubRequest('/authenticate', {
status: 200
})
//here a call to /authenticate is being made
SessionService.login('foo', 'bar')
moxios.wait(() => {
expect(something).toHaveHappened()
done()
})
})
This works fine:
it('should stub my request', (done) => {
SessionService.login('foo', 'bar')
moxios.wait(async () => {
let request = moxios.requests.mostRecent()
await request.respondWith({
status: 200
})
expect(something).toHaveHappened()
done()
})
})
The second method just get's the last call though, and I'd really like to be able to explicitely stub certain requests.
I'm running Jest with Vue.
I landed here with a similar goal and eventually solved it using a different approach that may be helpful to others:
moxios.requests has a method .get() (source code) that lets you grab a specific request from moxios.requests based on the url. This way, if you have multiple requests, your tests don't require the requests to occur in a specific order to work.
Here's what it looks like:
moxios.wait(() => {
// Grab a specific API request based on the URL
const request = moxios.requests.get('get', 'endpoint/to/stub');
// Stub the response with whatever you would like
request.respondWith(yourStubbedResponseHere)
.then(() => {
// Your assertions go here
done();
});
});
NOTE:
The name of the method .get() is a bit misleading. It can handle different types of HTTP requests. The type is passed as the first parameter like: moxios.requests.get(requestType, url)
it would be nice if you show us the service. Service call must be inside the moxios wait func and outside must be the axios call alone. I have pasted a simplified with stubRequest
describe('Fetch a product action', () => {
let onFulfilled;
let onRejected;
beforeEach(() => {
moxios.install();
store = mockStore({});
onFulfilled = sinon.spy();
onRejected = sinon.spy();
});
afterEach(() => {
moxios.uninstall();
});
it('can fetch the product successfully', done => {
const API_URL = `http://localhost:3000/products/`;
moxios.stubRequest(API_URL, {
status: 200,
response: mockDataSingleProduct
});
axios.get(API_URL, mockDataSingleProduct).then(onFulfilled);
const expectedActions = [
{
type: ACTION.FETCH_PRODUCT,
payload: mockDataSingleProduct
}
];
moxios.wait(function() {
const response = onFulfilled.getCall(0).args[0];
expect(onFulfilled.calledOnce).toBe(true);
expect(response.status).toBe(200);
expect(response.data).toEqual(mockDataSingleProduct);
return store.dispatch(fetchProduct(mockDataSingleProduct.id))
.then(() => {
var actions = store.getActions();
expect(actions.length).toBe(1);
expect(actions[0].type).toBe(ACTION.FETCH_PRODUCT);
expect(actions[0].payload).not.toBe(null || undefined);
expect(actions[0].payload).toEqual(mockDataSingleProduct);
expect(actions).toEqual(expectedActions);
done();
});
});
});
})

Unable to run a POST action in play framework 2.2

I have a similar problem to this person and I am unsure why.
my post action is called from a form submission and the controller code
def processFreeDoc = UserAwareAction {
implicit request => {
userEmailForm.bindFromRequest.fold(
formWithErrors => {
Logger.error("Error processing the form %s "format formWithErrors.errors)
Redirect(routes.Application.index)
},
data => {
val sessionProduct = getSessionUnProcessedProduct(data.sessionId, data.documentName)
if(sessionProduct != null){
Logger.info("Found")
sessionProduct.updateProcessing(data.emailAddress, data.sessionId)
Redirect(routes.Application.index)
}
else
Logger.info("Nothing found")
Redirect(routes.Application.checkoutFree(data.sessionId))
}
)
}
}
is skipped entirely. There are no errors in the IDE(IDEA) console and the breakpoint at the entry of the method is not reached so none of the log messages are seen.
EDIT :
The relevant route in the routes file - POST /processFreeDoc controllers.Application.processFreeDoc