This gem adds all of the required plumbing for your SaaS Rails application to integrate with the new add-ons marketplace at DigitalOcean. It is designed to be flexible and adds integration points, but does not specify how your SaaS creates accounts or handles other specific things. Integrating with the add-ons marketplae involves 4 touch points:
- Resources: Account provisioning, deprovisioning, and plan updates,
- SSO: Allows DigitalOcean customers to sign into their account from within DigitalOcean
- Notifications: Web-hook notifications about specific resources
- Configuration Variables: Allows your application to push data back into the customers DigitalOcean interface
Official integration docs here: https://marketplace.digitalocean.com/vendors/saas-api-docs
If you use Devise to authenticate Users
, this should work mostly out of the box. Each customer of your add-on will be mapped to a single User
in your SaaS app. With some customization (see below) you could easily adapt this to work with some other approach, like Account
or Tenant
mapping.
This connector attempts to be flexible and work with most account/user designs by allowing you to customize what happens after a resource has been provisioned or changed. You can do this through custom logic inserted through Rails Concerns.
Add to your Gemfile
.
gem 'do_addon_connector', git: 'https://github.com/scott/do_addon_connector'
bundle install
Next install and migrate the database, then run the installation script:
bin/rails do_addon_connector:install:migrations
rails db:migrate
bin/rails g do_addon_connector:install
Finally, mount the connector in routes.rb
mount DoAddonConnector::Engine => '/connectors'
The install script will add an initializer and some Concerns
which give you complete flexibility over how your application reacts to a provisioning or SSO request.
The initializer do_addon_connector.rb
defines configuration values as follows:
# config/initializers/do_addon_connector.rb
DoAddonConnector.setup do |config|
#
# Service Name
# ======================
# This is the name of the parent service. It is used in
# messages sent back to the user
config.service_name = "Application"
# App Slug
# ======================
# This is the slug used by your app.
config.slug = "acme"
# Password
# ======================
# This is the password assigned to your resource.
config.password = "password"
# Salt
# ======================
# This is the salt assigned to your resource.
config.salt = "sso_salt"
# Secret
# ======================
# This is the client secret assigned to your resource
config.secret = "do_secret".
# SSO Token Expiration
# ======================
# This is how long in seconds before the authentication token
# is expired.
config.token_expires_in = 120
# Source
# ======================
# This represents the source of the user, and can be used
# to customize your app experience for this type of account.
config.source = 'digitalocean'
# SSO Redirect
# ======================
# This determines where the user should be taken after a successful SSO.
# This can also be handled in the `Concern` file.
config.redirect_to = 'https://scrubl.com/dashboard'
# Debug
# ======================
# Logs additional information and stores SSO tokens for later inspection
config.debug = false
end
The installation script adds several Concerns
to your app. These are used to define app behavior in response to the different actions provided by the provisioning service:
Resource Provisioning - when the service sends a resource creation request, your app should add an account and/or create a login for the user who has provisioned the app. You can configure how the account/user gets created using: https://github.com/scott/do_addon_connector/blob/master/lib/generators/do_addon_connector/controllers/concerns/resources_controller_extension.rb
Important: The resource creation concern must set an @account
instance variable. Failure to do this will cause your provisioning to fail.
After the customer has been added on your app, you can use the customer_extensions
concern to add context from metadata and set the resources initial plan subscription. https://github.com/scott/do_addon_connector/blob/master/lib/generators/do_addon_connector/models/concerns/customer_extensions.rb
Plan upgrade/downgrade - The above file also includes an action for upgrading and downgrading plans in response to a request from the provisioning service.
Resource destruction - When DO sends a resource deletion request, your app will need to handle that. You can specify what else will happen in this Concern
.
When a DO user sends a SSO request, you will need to log them into your app. How you authenticate a user is up to your app, but you can use the following to respond to the SSO login request: https://github.com/scott/do_addon_connector/blob/master/lib/generators/do_addon_connector/controllers/concerns/sso_login_extension.rb
DigitalOcean marketplace will send notification webhooks to your app when certain events happen. If a customer fails to pay their bill, a webhook indicating this will be sent to your app. Likewise, if the same customer becomes current again, another notification indicating this will be sent.
You can customize how your app behaves in response to any notification within the notifications_concern
.
Your SaaS can send Key-Value variables back to DigitalOcean where they will be provided to customers within the marketplace UI. This can be useful if you want to communicate an API key, password or URL. To do this, use something like the following:
DoAddonConnetor::Customer.find_by(owner_id: <your-id>).update_config(vars = {"FOO":"BAR})
The owner_id
is is the account ID or customer ID on your SaaS.
Please submit issues or PRs for bugs or improvement ideas.
The gem is available as open source under the terms of the MIT License. Copyright 2022 Scott Miller, Helpy.io, Inc.
This integration is used in production at Helpy.io where it was extracted and turned into a gem. At Helpy we were fortunate enough to gain early access to the add-ons marketplace as a beta partner, and felt it was only appropriate to contribute this work back to accelerate the integration process at other companies.