Skip to content
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

feat: add RabbitMQ publisher #137

Merged
merged 21 commits into from
Apr 12, 2022

Conversation

anandsunderraman
Copy link
Collaborator

@anandsunderraman anandsunderraman commented Mar 8, 2022

Description

Creating a amqp publisher from asyncapi file

Related issue(s)
#132

Summary of Changes

  • Added functionality for amqp publishers under components/Handlers.js
  • Added functionality to create amqpPublisher under components/Publisher.js
  • Added util functions under components/common.js
  • Added a util to convert any model to watermill message under template/asyncapi/payload.js
  • Refactored code to produce the main.go file in template/index.js
  • Added a bunch of unit tests

@anandsunderraman anandsunderraman changed the title feat:rabbit-publisher feat: rabbit-publisher Mar 11, 2022
@@ -15,28 +15,81 @@ func ${operation}(msg *message.Message) error {
}
`;

function SubscriptionHandlers({ channels }) {
export function SubscriptionHandlers({ channels }) {
return Object.entries(channels)
.map(([channelName, channel]) => {
if (channel.hasPublish()) {
const operation = pascalCase(channel.publish().id());
Copy link
Member

@smoya smoya Mar 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

operationId is not mandatory so can be empty. I didn't realize the fact we were using it for generating the method names.

I think we should generate one if it's empty. Maybe channel name + operation type.

Copy link

@dxps dxps Mar 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly! Either:

  • Some method (handler function) name is auto generated (and have it documented). Currently, nothing is included, and ofc, we have a syntax error in two (declaration and usage) places.
  • Or the docs of this generator should explicit that operationId should be used to properly define your handler name.
    • For a good UX, I would prefer to output the fact that some generated names where included, since no explicit operationId exists in the spec.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And there is also $id property of payload, based on which the proper Go struct naming is done.
That's another useful and optional thing (without it you get a name, but it's something like AnonymousSchema which is not that not that great. It's ok that it is generated, no syntax errors, but the naming can be improved).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@smoya @dxps thanks for noticing this.
I am tempted to generate a method name based if operationId is not present.
But as @dxps mentions we would have to make sure there are no duplicate names.
Another thing I notice is that channel names can have special characters. So we would have to remove all special characters.
I like @dxps's suggestion to mandate an operationId for this spec. Meanwhile I can also check other generators on how they handle this scenario

Copy link
Member

@smoya smoya Apr 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@anandsunderraman I reviewed the code again and I noticed you didn't add a guard checking if the operationId is present, forcing the user to set it or throwing an error otherwise. You have an example in nodejs-ws-template.

Also, I would mention this in the README file as for example java-spring-template does.

Copy link

@dxps dxps Apr 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Forgot about operationId, although I mentioned it initially.

The other part that could get improved is related to the $id property of payload, based on which the proper Go struct naming is done. That's another useful and optional thing, without specifying it you get a name like AnonymousSchema which is not that great or useful.

components/Handlers.js Outdated Show resolved Hide resolved
components/Handlers.js Outdated Show resolved Hide resolved
@dxps
Copy link

dxps commented Mar 24, 2022

@anandsunderraman Could you please react on the provided feedback?
Meanwhile, I will play with this new contribution and get back with feedback and contribute.
Thanks!

@anandsunderraman
Copy link
Collaborator Author

@anandsunderraman Could you please react on the provided feedback? Meanwhile, I will play with this new part as well. Thanks!

@dxps I was waiting for all reviewers to provide feedback, so that I can make my changes all at once, preventing multiple back and forth

@dxps
Copy link

dxps commented Mar 26, 2022

@anandsunderraman While just testing it with the spec mentioned here, which the spec does not yet declare any subscribe operation at the channel level, there are two (small) issues:

  1. In handlers.go, "context" is imported but not used.
  2. In subscribers.go, there is no GetAMQPPublisher generated function, thus in main.go there is the corresponding error.

Used ag --debug /path/to/spec.yaml https://github.com/anandsunderraman/go-watermill-template/tree/rabbit-publisher -o gen_go for testing it.

template/index.js Outdated Show resolved Hide resolved
Copy link

@dxps dxps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a heads-up for now (for the 1st one, I'll look into it myself as well, to see how harmful it can be):

  1. In payloads.go, there is this warning:
    image

  2. In main.go, there is this warning:
    image
    This can easily be solved by checking for that error, by introducing these after that call:
     if err != nil {
     	return err
     }

  3. In handlers.go, context package is being used, but not included in the import section.
    This is another easy to solve one.

Besides these, I tested the generated code and is functionally working.
So, thank you for doing this! 🙏

@dxps
Copy link

dxps commented Mar 26, 2022

Ok, I understand the 1st warning (copylocks) and it's quite problematic.

To solve it, the code should be slightly updated as follows:

  1. In payloads.go file, payloadToMessage function should return a pointer to the message instead of a copy
    (that does a copy of an included Mutex, that's why the warning). Basically, it should look like this:
    func PayloadToMessage(i interface{}) (*message.Message, error) {
         var m message.Message
    
         b, err := json.Marshal(i)
         if err != nil {
                 return &m, nil
         }
         m.Payload = b
    
         return &m, nil
    }
  2. And just adapt the handlers in handlers.go file, to call the Publish(...) method accordingly, that is:
    return a.Publish("{topicName}", m)

@dxps
Copy link

dxps commented Mar 26, 2022

Last but not least, some small improvements that can be included in main.go:

  1. Close the connection at the end.
    Although it's a generated code, it should guide others how to do it properly.
    Ofc, without it, the sample still works, and you could just see RabbitMQ's log complaining about it
    (with client unexpectedly closed TCP connection). The code to include right before the last return nil line:
    if err := amqpPub.Close(); err != nil {
    	log.Printf("Error while closing AMQP connection: %v\n", err)
    }
  2. I might be too picky here, but startAMQPPublishers(_) function is not actually starting something (that should eventually be stopped), but actually it showcases how publishing should be done. Therefore, I'd rename it as doAMQPPublishing() or something like that. And ofc, the error message being logged in main(_) can be error publishing: ....

Copy link
Member

@derberg derberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not look into logic code as I see others already did it.

I only left one comment about one script name. Also please change PR title to feat: add RabbitMQ publisher. I also think it would be worth extending the readme overview with some more info on what users can generate with this template. Also good to add development section and explain the dev script.

One curiosity question: why did you add dependency related to babel?

package.json Outdated Show resolved Hide resolved
@anandsunderraman
Copy link
Collaborator Author

here

@dxps again thanks for finding this issue I will look to address it

@anandsunderraman
Copy link
Collaborator Author

  1. Close the connection at the end.
    Although it's a generated code, it should guide others how to do it properly.
    Ofc, without it, the sample still works, and you could just see RabbitMQ's log complaining about it
    (with client unexpectedly closed TCP connection). The code to include right before the last return nil line:

@dxps Completely agree with this suggestion. However I feel this needs more investigation to be done in the watermill.io fashion. I will create a separate issue to handle this

@anandsunderraman anandsunderraman changed the title feat: rabbit-publisher feat: add RabbitMQ publisher Apr 8, 2022
@anandsunderraman
Copy link
Collaborator Author

One curiosity question: why did you add dependency related to babel?
@derberg I introduced a change in this PR at: https://github.com/asyncapi/go-watermill-template/pull/137/files#diff-4bab0aa7cca57d1ac8ae38749f9f50f6c2c91b81eb46e28ad46ebcf0e375d1ceR41

const result = render(<SubscriptionHandlers channels={doc.channels()} />)

If I did not add babel I was getting the error:

Add @babel/preset-react (https://git.io/JfeDR) to the 'presets' section of your Babel config to enable transformation.
    If you want to leave it as-is, add @babel/plugin-syntax-jsx (https://git.io/vb4yA) to the 'plugins' section to enable parsing.

@dxps
Copy link

dxps commented Apr 10, 2022

With these recently introduced updates, the generated code for the publisher looks good and it works.
LGTM 👍 Thanks!

test/asyncapi.yaml Outdated Show resolved Hide resolved
derberg
derberg previously approved these changes Apr 11, 2022
Copy link
Member

@smoya smoya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pasting here as the conversation was marked as solved (now I unresolved).

https://github.com/asyncapi/go-watermill-template/pull/137/files#r847178655

@anandsunderraman I reviewed the code again and I noticed you didn't add a guard checking if the operationId is present, forcing the user to set it or throwing an error otherwise. You have an example in nodejs-ws-template.

Also, I would mention this in the README file as for example java-spring-template does.

@anandsunderraman
Copy link
Collaborator Author

Pasting here as the conversation was marked as solved (now I unresolved).

https://github.com/asyncapi/go-watermill-template/pull/137/files#r847178655

@anandsunderraman I reviewed the code again and I noticed you didn't add a guard checking if the operationId is present, forcing the user to set it or throwing an error otherwise. You have an example in nodejs-ws-template.

Also, I would mention this in the README file as for example java-spring-template does.

@smoya apologies. Thanks for pointing out to the other generator examples.
I have addressed them now and added unit tests.

Copy link
Member

@smoya smoya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Superb work @anandsunderraman! Thanks for such an effort; It has been a pleasure to review this PR :)

LGTM!🚀 🌔

@anandsunderraman
Copy link
Collaborator Author

thanks @smoya !!

@anandsunderraman anandsunderraman merged commit 7d9587d into asyncapi:master Apr 12, 2022
@asyncapi-bot
Copy link
Contributor

🎉 This PR is included in version 0.2.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants