I have the following code:
#OnEvent("**")
public handleEverything(parentId: number): void {
// #ts-expect-error this.envent
console.log(this.event)
}
I tried to get the event name by using the this.event but it returns undefined. Is there a way to get the event name inside the handler using Nestjs and EventEmitter?
I think that is not possible. You can instead add a parameter to the payload when dispatching event and use it in the handler:
Dispatching:
this.eventEmitter.emit('eventName', { type: 'eventType', others: 'others' });
Listening:
#OnEvent("**")
public handleEverything(payload: any): void {
if (payload.type === 'eventType') {
// do something
}
}
Related
Hello I have a command bus, a query bus, which basically has a keypair with the name of the command or query and the handler and then I execute the command that should publish my event.
But I have some doubts about how I could do my event-bus.
is the command-bus part of an event-bus?
how could I do an event-bus with the handlers
command-bus:
export interface ICommand {
}
export interface ICommandHandler<
TCommand extends ICommand = any,
TResult = any
> {
execute(command: TCommand): Promise<TResult>
}
export interface ICommandBus<CommandBase extends ICommand = ICommand> {
execute<T extends CommandBase>(command: T): Promise<any>
register(data:{commandHandler: ICommandHandler, command: ICommand}[]): void
}
command-bus implementation:
export class CommandBus<Command extends ICommand = ICommand>
implements ICommandBus<Command> {
private handlers = new Map<string, ICommandHandler<Command>>()
public execute<T extends Command>(command: T): Promise<any> {
const commandName = this.getCommandName(command as any)
const handler = this.handlers.get(commandName)
if (!handler) throw new Error(``)
return handler.execute(command)
}
public register(
data: { commandHandler: ICommandHandler; command: ICommand }[],
): void {
data.forEach(({command,commandHandler}) => {
this.bind(commandHandler, this.getCommandName(command as any))
})
}
private bind<T extends Command>(handler: ICommandHandler<T>, name: string) {
this.handlers.set(name, handler)
}
private getCommandName(command: Function): string {
const { constructor } = Object.getPrototypeOf(command)
return constructor.name as string
}
}
Here another question arose, who should have the responsibility to publish the events in my event db or read a stream of my event db is my class event-store?
event-store class:
export class EventStoreClient {
[x: string]: any;
/**
* #constructor
*/
constructor(private readonly config: TCPConfig) {
this.type = 'event-store';
this.eventFactory = new EventFactory();
this.connect();
}
connect() {
this.client = new TCPClient(this.config);
return this;
}
getClient() {
return this.client;
}
newEvent(name: any, payload: any) {
return this.eventFactory.newEvent(name, payload);
}
close() {
this.client.close();
return this;
}
}
And then I have doubts about how to implement my event-bus, with my event handlers and my events.
I would be happy if someone could help me ..
event-interface:
export interface IEvent {
readonly aggregrateVersion: number
readonly aggregateId: string
}
export interface IEventHandler<T extends IEvent = any> {
handle(event: T): any
}
maybe usage:
commandBus.execute(new Command())
class commandHandler {
constructor(repository: IRepository, eventBus ????){}
execute(){
//how i can publish an event with after command handler logic with event bus her
}
}
I see there's some confusion between the various Buses and the Event Store. Before attempting to implement an Event Bus, you need to answer one important question that lies at the foundation of any Event Sourcing implementation:
How to preserve the Event Store as the Single Source of Truth?
That is, your Event Store contains the complete state of the domain. This also means that the consumers of the Event Bus (whatever it ends up being - a message queue, a streaming platform, Redis, etc.) should only get the events that are persisted. Therefore, the goals become:
Only deliver events on the Bus that are persisted to the Store (so if you get an error writing to the Store, or maybe a Concurrency Exception, do not deliver via bus!)
Deliver all events to all interested consumers, without losing any events
These two goals intuitively translate to "I want atomic commit between the Event Store and the Event Bus". This is simplest to achieve when they're the same thing!
So, instead of thinking about how to connect an "Event Bus" to command handlers and send events back and forth, think about how to retrieve already persisted events from the Event Store and subscribe to that. This also removes any dependency between command handlers and event subscribers - they live on different sides of the Event Store (writer vs. reader), and could be in different processes, on different machines.
I try to create OWIN(IIS Hosted) middleware that will ensure that all log4net events will have a particular property (CorrelationId) assigned per-request.
I tried to:
Use following middleware and use IOwinContext.
It works only when appender batching size is to 1. Otherwise, the whole batch of events is assigned the same CorrelationId.
public class CorrelationIdMiddleware : OwinMiddleware
{
public CorrelationIdMiddleware(OwinMiddleware next): base(next){}
public override async Task Invoke(IOwinContext context)
{
correlationId = Guid.NewGuid().ToString();
context.Set("CorrelationId", correlationId);
await Next.Invoke(context);
}
}
Middleware was paired with log4net active property:
public class CorrelationIdActiveLog4NetValue
{
public override string ToString()
{
var context = HttpContext.Current.GetOwinContext();
if (context != null)
{
var value = context.Get<string>("CorrelationId");
if (!string.IsNullOrEmpty(value))
{
return value;
}
}
return "N/A";
}
}
Use LogicalCallContext.
var stack = log4net.LogicalThreadContext.Stacks["CorrelationId"]
using (stack.Push(correlationId))
{
log4net.LogManager.GetLogger(typeof(CorrelationIdMiddleware))
.Info("TEST MESSAGE");
await Next.Invoke(context);
}
It worked for the message I produced in the middleware itself, but not when I logged from controllers.
For comparison, in Serilog such middleware code works universally in every case(ASP.NET core):
using (LogContext.PushProperty("BayCorrelationId", context.TraceIdentifier))
{
await next(context);
}
I would like the ASPNetCore2.0 webapp I'm working on to send a notification to specific users using SignalR. I would like to call the hub's method from another controller's action (as opposed to a client's JS call).
I have learned that this is not how SignalR is intended to be used, but I've found many users who had the same 'desire' and also some solutions.
I have checked several proposed solutions, but the simplest and cleaner seemed to be the accepted answer to this post: Get Hub Context in SignalR Core from within another object.
So I gave it a go, and I get no errors at all. The server's output is error-free, and so are the browser's console and network tabs (I'm using Chrome). When debugging, the flow is smooth and the program does exactly what it should do... except the users don't get any notification...
Do any of you spot the problem?
I created a class that contains the shared methods for the hub:
using Microsoft.AspNetCore.SignalR;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace WebApp.Hubs
{
public class HubMethods
{
private readonly IHubContext<PostsHub> _hubContext;
public HubMethods(IHubContext<PostsHub> hubContext)
{
_hubContext = hubContext;
}
public async Task Notify(string postId, string sender, List<string> users)
{
await _hubContext.Clients.Users(users).SendAsync("Notify", sender, postId);
}
}
}
Then I created a hub:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace WebApp.Hubs
{
[Authorize]
public class PostsHub : Hub
{
private HubMethods _hubMethods;
public PostsHub(HubMethods hubMethods)
{
_hubMethods = hubMethods;
}
public async Task Notify(string postId, string sender, List<string> users)
{
await _hubMethods.Notify(postId, sender, users);
}
}
}
Added these bits to Startup's ConfigureServices method:
[...]// Services before these...
services.AddSignalR();
services.AddScoped<HubMethods>();
services.AddMvc();
And Startup's Configure method:
app.UseSignalR(routes =>
{
routes.MapHub<PostsHub>("/postshub");
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
Then these lines to the view:
<script src="~/lib/signalr/signalr.js"></script>
#await Html.PartialAsync("_NotifyScriptsPartial")
And this is "_NotifyScriptsPartial.cshtml":
<script>
var connection = new signalR.HubConnectionBuilder().withUrl('/PostsHub').build();
connection.on('Notify', function (sender, postId) {
var post = postId;
var sentBy = sender;
var content = '<a href=\'#\' class=\'close\' data-dismiss=\'alert\' aria-label=\'close\'>×</a>' +
'You just received a new comment from <strong>' +
sentBy + '</strong>. Click <a href = \'#\' class=\'alert-link\' >here</a> to view the post.'
var alert = document.createElement('div');
alert.classList.add('alert', 'alert-info', 'alert-dismissible');
alert.html(content);
document.getElementById('pageContent').appendChild(alert);
});
</script>
Finally, in the controller that is supposed to send the notification, I added these:
public class PostsController : Controller
{
private readonly HubMethods _hubMethods;
public PostsController(HubMethods hubMethods)
{
_hubMethods = hubMethods;
}
// POST: Create a new post
[Authorize]
[HttpPost]
public async Task<IActionResult> Create(DetailsModel model, List<string> readers)
{
if (ModelState.IsValid)
{
// Do stuff here... including creating the newPostId, userId and receipients variables used below
await _hubMethods.Notify(newPostId, userId, receipients);
// Do more stuff and eventually...
return View();
}
}
}
Any idea?
In Asp.Net Core 2.1 I can use hub like this, It solves my problem, also You used like this in your controller. Hope it helps.
public class SomeController : Controller
{
private readonly IHubContext<MyHub> _myHub;
public SomeController (IHubContext<MyHub> myHub)
{
_myHub = myHub;
}
public void SomeAction()
{
//for your example
_myHub.Clients.All.SendAsync("Notify", "data");
}
}
I can get the "data" text from browser's console. If you use jQuery in your project, add those codes between jQuery(document).ready(function () { }); because you tried to load a partial html and I think your code needs to run after ready() event. Sorry If I misunderstood you.
In ServiceStack 3 I had a custom handler decorating the result DTO in case of exceptions:
ServiceExceptionHandler = (request, exception) =>
{
var ret = DtoUtils.HandleException(this, request, exception);
var error = ret as HttpError;
if ( error == null )
return ret;
// ...
error.Response = new MyErrorResponse
{
ResponseStatus = responseStatus,
// ...
};
return ret;
};
After migrating to ServiceStack 4 I tried different hooks:
ServiceExceptionHandlers.Add
OnExceptionTypeFilter
Own ServiceRunner with overridden HandleException
Neither of them is been called when exceptions occur. What am I missing?
I'm using the new Task based services, if this is relevant.
Edit: A simple test service included in my solution triggers the hooks:
[Route("/test")]
public class TestRequest : IReturn<int>
{
}
public class TestService : Service
{
public Task<int> Get(TestRequest request)
{
throw new Exception("Ha!");
}
}
Edit2: Seems to be a bug in the handling of asynchronous services. If I move the exception from the synchronous to the asynchronous part of the handler, none of the hooks are called:
public class TestService : Service
{
public async Task<int> Get(TestRequest request)
{
await Task.Yield();
throw new Exception("Ha!");
}
}
I've been thrashing for a bit and having difficulty figuring out how to pass server error messages to a client.
On the server I have (simplified):
export function get(req: express.ExpressServerRequest, res: express.ExpressServerResponse) {
res.statusCode = 500;
res.send('CUSTOM ERROR MESSAGE');
}
On the client:
public fetchObject(successF: Function, failF: Function): void {
this.myObj = new MyObj();
this.myObj.fetch({ success: successF, error: failF });
}
private failF(model, xhr, options): void {
// Want to get access to "CUSTOM ERROR MESSAGE"
}
The xhr object responseText is empty and the statusText is always "error".
Any suggestions? Thanks!
Found a solution. Define a class variable and capture the return from the fetch call:
private xhr: XMLHttpRequest = null;
Then:
public fetchObject(successF: Function, failF: Function): void {
this.myObj = new MyObj();
this.xhr = this.myObj.fetch({ success: successF, error: failF });
}
Finally:
private failF(model, xhr, options): void {
doSomething(this.xhr.responseText);
}
this.xhr will contain the reponseText (i.e. 'CUSTOM ERROR MESSAGE'). The local xhr will still be a blank string.
I'm still not sure why this is the case, and if anyone has some insight I'd appreciate it.