-
Notifications
You must be signed in to change notification settings - Fork 69
Emitter Should Optionally Be Static #314
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I see where you are coming from with the desire for a static function. However, it's not the case that you are required to create a new const emitter = new Emitter(( { url: 'https://primary.system.com' });
emitter.send(new CloudEvent({ source: '/event-generator', type: 'system:info' });
// then later, maybe we send only error events to another URL
emitter.send(new CloudEvent({ source: '/event-generator', type: 'system:error'}, { url: 'https://error.system.com' }); This was designed intentionally so that the user isn't required to provide a URL for each send. In many cases, a system will have a single endpoint, and having the URL in the constructor enables this. I would argue that this is preferable to a static method if the static method always requires the same URL. |
I just used javascript-sdk marginally, so correct me if I am wrong. I would expect emitter to contain some http-client object inside. Sometimes such an object my store some state, e.g. keep http-cookies. For that reason I would avoid having single static(global) emitter. |
Creating a managed Emitter object is fine, but there are many use-cases where I don't want to manage state and figure out which file contains my emitter. An alternative would be to have something like this: const url = 'https://primary.system.com';
Emitter.send(new CloudEvent({ source: '/event-generator', type: 'system:info' }, { url }));
// then later, maybe we send only error events to another URL
Emitter.send(new CloudEvent({ source: '/event-generator', type: 'system:info' }, { url: 'https://error.system.com' }) Maybe we can we have both if we want to have a non-static emitter class? I see value in setting up configs for the emitter once. |
trying to do both would be very confusing for the user. |
OK. Can we just have 1 function to send a HTTP request? Like: Emitter.send(url, options) This format is more idiomatic and is similar to the format for what other npm modules do:
They don't require to create a class/object first. I don't see exactly what useful state we're trying to preserve for the user in |
@grant Seems you are right that the JS/TS HTTP clients are mostly stateless (which is really good thing). I had different experience in different languages. |
The current design is meant for simplicity once an It's easy enough to default to one format or the other, but let's say the default is // For example, if the default is structured, every call to a single endpoint would look like this
Emitter.emit( event, { protocol: Protocol.HTTPBinary, url: 'https://my-static.receiver.com' }); For me, it makes the most sense to have a class which contains the state of the endpoint (url, transport protocol, and an actual emitter implementation function), rather than having to specify this in the call to Let's say I have some codebase that has several different branches where an event needs to be emitted to a broker that reads structured events. I'd say that doing this: // specifiy these things once in the ctor
const emitter = new Emitter({ url: 'https://my.system.broker', protocol: Protocol.HTTPStructured });
// now everywhere in my code that this emitter is available just has to call
emitter.emit(event); With your suggestion, this Emitter.emit( event, { url: 'https://my.system.broker', protocol: Protocol.HTTPStructured }); In my mind that's a lot more room for error, a lot more typing, and generally not as "nice" for a developer. I think that having an object that maintains this state, with the option to override it in subsequent calls to |
I hear where you're coming from. The difference is a stateless functional vs stateful object-oriented design. I'm suggesting to be more functional like Node: Currentconst emitter = new Emitter({ url: 'https://my.system.broker', protocol: Protocol.HTTPStructured });
emitter.emit(event); Proposedconst options = { url: 'https://my.system.broker', protocol: Protocol.HTTPStructured };
Emitter.emit(event, options); There are benefits for both styles of programming. If you wanted to have a stateful endpoint, you can do this: Currentconst emitter = new Emitter({ url: 'https://my.system.broker', protocol: Protocol.HTTPStructured });
export emitter;
// ... other file
const emitter = require('./emitter');
emitter.emit(event); vs const emitterOptions = { url: 'https://my.system.broker', protocol: Protocol.HTTPStructured };
export emitterOptions;
// ... other file
const emitterOptions = require('./emitter');
Emitter.emit(event, emitterOptions); If we need to change the For example, let's say we want to now emit binary CEs. It's easy in the 2nd example: Emitter.emit(event, {...emitterOptions, protocol: Protocol.HTTPBinary}); If we're really using |
I think that instance of Emitter is supposed to be used only locally in single file (or even function) and not being exported. |
I like stateless immutable things, but it would force user to repeat code like const send = (e: CloudEvent, opts: TransportOptions = {}) => Emitter.send(e, { url: receiver, ...opts }); or maybe it could be extracted: |
You wouldn't need to repeat the code, you'd just need to export that stateful I'm also not saying that they have to be mutually exclusive, if we want to keep state, we could have that too. |
This is not a hill I feel like fighting on, so if you would like to propose an implementation in a PR that would be welcome. And we can discuss the merits of it there. I can see your point but do still have reasons to prefer the implementation as it is. Regarding idiomatic JS, there is a lot of room for interpretation around that and maintaining state is not inherently wrong in JS - note the recent addition of |
What about adding some default immutable instance as static member: class Emitter {
// ...
static readonly default = Object.freeze(new Emitter({ url: "http://localhost:8080", protocol: Protocol.HTTPBinary }));
// ...
} Then user could call: Emitter.default.send(cloudEvent, { url: "example.com" }) |
@grant i would suggest to send a PR with the proposed changes. Maybe seeing the code in context might help |
Thanks for the concrete suggestion @matejvasek. I'll look at changing the PR to something like that. |
This issue is stale because it has been open 30 days with no activity. |
Not sure of the status of this or what 4.x brings. |
@grant the proposal was that @lholmquist are you good with holding off a bit on 4.x? I'm noodling something like an |
@lance sounds good. i don't think we are in any rush for the 4.0 release |
Now that #342 has been merged, should we close this? |
Well, there's no way to send a CloudEvent without creating a factory or instance of an Emitter, so not really. sdk-javascript/src/transport/emitter.ts Line 97 in e334b6e
|
Didn't we just go through all of this last week? There will be no |
Hey, I was asked if we should close this issue. #342 creating a factory is useful but I thought it was a bit tangential. Really the goal was to not have to maintain an emitter across multiple files with a static option and just call emit anywhere (passing a transport). The current interface is: emitterFor(fn, transportOptions) // new
new Emitter(transportOptions) // standard I guess I was expecting a Emitter.send(event, transportOptions) //static so maybe a function like this here: static send(event: CloudEvent, options: TransportOptions): Promise<unknown> {
...
} with required options. To be clear, I'm not expecting anybody to work on this. If we do have an emitter, a static option would be nice. |
Or if we're removing the |
Yes, the plan is to remove
I don't understand how this would work, since we will be removing axios when we remove |
I guess I don't understand the best way to send an event currently. The thinking I was going with was to pass a function (which can use axios or something else) to actually send the event, but that's a bit overkill. Really, it would be nice to have a sample in the README like this: const request = require('request');
import {toHTTPRequest} from 'cloudevents';
const ce = { id: 1234, .... };
const [headers, body] = toHTTPRequest(ce, { mode: Mode.BINARY }); // or Mode.STRUCTURED
request.post(url, headers, body); This is similar to how python allows you to send requests: https://github.com/cloudevents/sdk-python#binary-http-cloudevent |
Outside of the const axios = require('axios').default;
const ce = { ... }
const message = HTTP.binary(ce); // Or HTTP.structured(ce)
axios({
method: 'post',
url: '...',
data: message.body,
headers: message.headers,
}); And the other is what you suggested with passing a function. This just landed in #342.
function sendViaSuperagent(message) {
// send the message.headers and message.body across the wire
}
const emit = emitterFor(sendViaSuperagent);
const ce = { ... }
emit(ce); |
Looks great, will close this issue |
Uh oh!
There was an error while loading. Please reload this page.
Describe the Bug
The HTTP Emitter should have static methods such that we don't need to construct multiple Emitters before sending our CE.
Currently we must instantiate the Emitter with a URL and then emit. See README.
Expected Behavior
Like the static
Receiver.accept(...)
, I'd imagine anEmitter.send(...)
.This format is more idiomatic and is similar to the format for what other npm modules do:
http
Standard library – (http.request(url, options)
)axios
- (axios.post(url, options)
)request
- (request.post({ url, ...options })
)superagent
- (superagent.post(url).send(body).end((err, res) => {})
)The text was updated successfully, but these errors were encountered: