README.md in hephaestus-0.6.4 vs README.md in hephaestus-0.7.0

- old
+ new

@@ -1,32 +1,177 @@ # Hephaestus -A plug template for Yetto. Use this to quickly spin up new plugs with a set of defaults. +A plug template for Yetto. It serves two purposes: +1. Use this to quickly spin up new plugs with a set of defaults. +2. Hephaestus also exists as an engine, and is responsible for the majority of a plug's Yetto communication logic. + ## Usage +First, install Hephaestus _globally_, on your system: + ``` gem install hephaestus +``` +Then, in the parent directory where you want your plug to be created, run: + +``` hephaestus plug-app ``` -Where `app` represents the name of the platform you'd like to interact with `jira`, `notion`, `slack`, etc. +Where `app` represents the name of the platform you'd like to interact with, like `jira`, `notion`, `slack`, etc. -If you're working on updating/testing this gem locally, you may also want: +### Testing locally +If you're working on updating/testing this gem locally, you may also try: + ``` rm -rf plug-app && DEBUG=1 hephaestus/bin/hephaestus plug-app ``` This way you can wipe the dir and quickly iterate on new changes. -## Building upon it +## Building upon the base +Most of the files which Hephaestus creates for you shouldn't need much modification. The information below is part-informative, part-guidelines on how to extend the generated code for your specific plug. + +### Controllers + +#### `ApplicationController` + +Your application controller inherits from `Hephaestus::ApplicationController`, and handles the basics of request/response cycles. Two common callbacks to add here are: + +- `before_action :set_request_span_context`, which lets you add data to the `OpenTelemetry` trace before a request occurs +- `after_action :set_response_span_context`, similarly, lets you add to the trace after the response is sent + +For examples on how to integrate with these, check out `plug-email`. + +#### `SettingsController` + +Typically, your settings inherit directly from Hephaestus. We'll discuss more about how this works in `routes.rb`. Either way, you'll define two files: + +- `app/views/settings/new.json.jbuilder` +- `app/views/settings/edit.json.jbuilder` + +Define the settings UI in these files. See `plug-email` for an example of a settings page with no customizations. + +However, for multi-step plugs, it's common to expect customized steps. If you'd like to extend the settings controller, create a file with one of two methods: + +- `new` +- `edit` + +You only need to define these methods if you're actually overriding the action—often, this only means `edit`. If you do define methods in this controller, remember to add `before_action :ensure_json_request` at the top. For examples on settings pages which have _some_ customization, see `plug-github` and `plug-linear`. For an example of a totally custom settings page, see `plug-zendesk`. + +#### `AppController` + +The logic to communicate with your platform sits in `AppController`. Every plug is unique, so the requirements for your platform varies! + +At a minimum, though, you should place all the logic which authenticates requests in the `Authable` concern. See `plug-github` and `plug-linear` for practical examples. + +#### `YettoController` + +Place the logic which receives webhooks from Yetto in the `YettoController`. This controller must `include Hephaestus::ValidatesFromYetto`, and must define `before_action :from_yetto?`. Beyond that, you can respond to events as they come in, and fire jobs to perform the actions your platform needs to integrate with Yetto. + +### Jobs + +Jobs are at the heart of plugs, because they perform time-sensitive work asynchronously. Since Hephaestus already creates an `ApplicationJob` which inherits from `Hephaestus::ApplicationJob`, there isn't much else you need to do, other than define your jobs. + +Whenever you need to integrate with Yetto, just pass the appropriate arguments to `Hephaestus::UpdateYettoJob`. + +The bulk of your plug logic should exist within these jobs. Keep the controllers as place to authenticate incoming requests and respond with appropriate status codes. + +### Constants + +Place any and all constants in the file called `lib/constants.rb`. + +Note that Hepheastus provides several convenience methods and constants for you. These are: + +- `plug_shortname`: the downcased version of the plug (e.g., `Slack => slack`, `GitHub => github`) +- `plug_name`: The proper name of the plug (e.g., `Slack`, `GitHub`) +- `plug_url`: The plug's URL (e.g., `email{.plugs.yetto.app,.plugs.yetto.dev,.ngrok.io}`) + +### Application Configuration + +The typical Rails' environment configs (`config/environments/{development.rb,test.rb,production.rb}`) are managed by Hephaestus. However, any plug can define their own environment files as well. These are added after Hephaestus' default configuration. + +The `test.rb` file in `plug-email` provides an example of this. + +#### Upgrading Rails + +Upgrading Rails always introduces breaking changes, even between minor releases. To ensure that a plug still works after a Rails upgrade, first, add + +```ruby +rails "x" +``` + +to your plug's Gemfile (where `"x"` is the new Rails version.) Note that this means plugs can run Rails versions independently of what Hephaestus provides (although variance is not recommended, it can help during the upgrade process). + +Next, call `rails app:update:configs`. You can pretty much ignore every changed file, except the one that starts with `new_framework_defaults_`. Use these values to slowly reintroduce the new configuration changes, as described in [the Rails documentation](https://guides.rubyonrails.org/v8.0/upgrading_ruby_on_rails.html#configure-framework-defaults). + +### Integrating the Plug with the Engine + +There are certain times where the child application needs to "inject" data into Hephaestus. These are typically customizations that are unique to the plug. Place these customizations in `config/application.rb`, inside of an `initializer :engines_blank_point do` block. + +At a minimum, you'll need to define your plug's API version, and the version of the Yetto API it's communicating with: + +```ruby +initializer :engines_blank_point do + config.yetto_api_version = "2023-03-06" + config.plug_api_version = "2023-03-06" +end +``` + +You can also define several other customizations: + +- `config.tracing_body_filters`: use this to scrub out any sensitive data coming in from your platform, to prevent it from leaking in our metrics aggregator. See the override in `plug-email` for an example. The default is to simply log everything (after running it through [Rails' `ParameterFilter`](https://api.rubyonrails.org/v7.1/classes/ActiveSupport/ParameterFilter.html), of course), which may not be a good idea! +- `config.enhance_update_yetto_job`: use this to include any plug-specific logic which needs to occur as part of issuing calls to the Yetto API + +For an example of both these customizations, see `plug-email`. + +There's also middleware you can use to protect any endpoint with an OpenAPI schema. To do so, add: + +```ruby +require "hephaestus/middleware/openapi_validation" +config.middleware.use(Hephaestus::Middleware::OpenapiValidation, match_path: "/app/2023-03-06/path/", limit_methods_to: ["POST"], spec: Rails.root.join("lib/schemas/path/2023-03-06/openapi.json")) +``` + +In this example, any `POST` to `match_path` is protected by the OpenAPI `spec` provided. + +### Services + +Place any HTTP communication necessary to communicate with your platform in this directory. All other code should which communicates to your plug should rely on methods defined in this file. + +See any of the plugs for an example of how to define this interaction, particularly with `httpsensible`. + +### Routes + +If you do not have any custom actions to perform in the settings controller, define your settings pages as: + +```ruby +# you must always include this line when referring to `hephaestus_settings` +HephaestusSettingsController = Hephaestus::SettingsController + +get "/api/#{Rails.application.config.plug_api_version}/settings", to: "hephaestus_settings#new" +get "/api/#{Rails.application.config.plug_api_version}/settings/:organization_id/:inbox_id/:plug_installation_id/edit", to: "hephaestus_settings#edit" +``` + +Otherwise, be sure to refer to `hephaestus_settings` only for any default settings related actions: + +```ruby +# you must always include this line when referring to `hephaestus_settings` +HephaestusSettingsController = Hephaestus::SettingsController + +get "/api/#{Rails.application.config.plug_api_version}/settings", to: "hephaestus_settings#new" +get "/api/#{Rails.application.config.plug_api_version}/settings/:organization_id/:inbox_id/:plug_installation_id/edit", to: "settings#edit" +``` + +Otherwise, you can add any other routes your plug needs to `config/routes.rb`. + ### Launching the server -First, you'll note that you have a `script/ngrok` file, which launches an ngrok server at `https://plug-app.ngrok.io`, which maps locally to `http://localhost:6661`. This can be essential when testing the platform locally for the first time. (Keep in mind that you still need to run `script/server` to actually start the local server—this is just to help facilitate communication with the platform.) +First, you'll note that you have a `script/ngrok` file, which launches an ngrok server at `https://${hostname}-plug-app.ngrok.io`, which maps locally to `http://localhost:6661`. Setting up ngrok can be essential when testing the platform locally for the first time. (Keep in mind that you still need to run `script/server` to actually start the local server—this is just to help facilitate communication with the platform.) ### Setting up routes You should probably open up config/routes.rb to make modifications to any incoming (from the platform) or outgoing (for Yetto) HTTP flows. @@ -40,8 +185,26 @@ ### Creating services Any code which communicates with the third party should be placed in the `app/services` directory. A generic HTTP service is included. +### Writing tests + +In addition to writing tests for your plug interacting with its platform, many of your tests will also need to cover your plug's interaction with Yetto. + +Writing these tests well comes with time; it's not an easy thing to cover in a pithy paragraph. Regardless, here's a bit of hopefully helpful advice. + +Be sure to tests requests before _and_ after they occur. Typically, this takes the form of a `stub_request` and `assert_requested`. Similarly, be sure to: + +- `include Hephaestus::Webmocks::SlackWebmock` whenever a plug action is expected to error out to Slack +- `include Hephaestus::Webmocks::YettoWebmock` whenever a plug needs to issue a request to Yetto + +Use `include Hephaestus::API::TestHelpers` whenever you need to write a test which either + +- match `"/#{plug_shortname}` routes (by issuing calls to `plug`). These are routes which your external server is calling into. +- match `/api` routes (by issuing calls to `api`). These are routes which Yetto is calling into. + +For examples on when and how to use these methods, see `plug-github`. + ## Acknowledgements -This project was heavily based on [thoughtbot/suspenders](https://github.com/thoughtbot/suspenders). +The template generation for this project was heavily based on [thoughtbot/suspenders](https://github.com/thoughtbot/suspenders).