doorkeeper icon indicating copy to clipboard operation
doorkeeper copied to clipboard

[WIP][PoC] Doorkeeper Plugins

Open nbulaj opened this issue 4 years ago • 2 comments

PoC for Doorkeeper plugins system.

Allows to extend Doorkeeper without the need to patch internals or create PRs to main repository.

Something similar (but not inspired by) to official AWS Ruby SDK.

TODO

  • [x] Implement plugins registry
  • [x] Implement plugins invocation with context
  • [ ] Collect namespaces & places where plugins can be used
    • [ ] Extract some existing hooks (after/before auth) into plugins ?
  • [ ] Add specs
  • [ ] Add documentation for plugins

Example of usage:

class Plugin
  def initialize(options = {})
    @options = options
  end

  def run(**context)
    puts "#{self.class.name} invoked"
  end
end

TestPlugin1 = Class.new(Plugin)
TestPlugin2 = Class.new(Plugin)
TestPlugin3 = Class.new(Plugin)
TestPlugin4 = Class.new(Plugin)

PluginWithException = Class.new(Plugin) do
  def run(**context)
    raise StandardError, "test exception"
  end
end

InvalidPlugin = Class.new

Doorkeeper::Plugin.register(TestPlugin1, namespace: "access_token")
Doorkeeper::Plugin.register(TestPlugin2, namespace: "access_token", options: { key: "123" })
Doorkeeper::Plugin.register(TestPlugin3, namespace: "access_token", after: TestPlugin2)
Doorkeeper::Plugin.register(TestPlugin4, namespace: "access_token", before: TestPlugin1)

Doorkeeper::Plugin.register(PluginWithException, namespace: "access_token", shallow_exceptions: false)

Doorkeeper::Plugin.run_for("access_token")

# => TestPlugin4
# TestPlugin1
# TestPlugin2
# TestPlugin3

Doorkeeper::Plugin.register(InvalidPlugin, namespace: "access_token")
# => ArgumentError ('InvalidPlugin' must respond to #run method!)

Doorkeeper::Plugin.register(TestPlugin2, namespace: "access_token")
# => ArgumentError ('TestPlugin3' already registered for access_a)

Doorkeeper::Plugin.register(TestPlugin3, namespace: "0123_access_token")
Doorkeeper::Plugin.register(TestPlugin3, namespace: "")
Doorkeeper::Plugin.register(TestPlugin3, namespace: "access-token")
Doorkeeper::Plugin.register(TestPlugin3, namespace: "access_token.")
Doorkeeper::Plugin.register(TestPlugin3, namespace: "access_token11")
# => ArgumentError (namespace must contain only letters, digits and underscore)

Real-life example

class FakeAccessToken
  def destroy
    puts "Token destroyed"
  end 
end

class AccessTokenPlugin
  def initialize(options = {})
    @options = options
  end

  def run(**context)
    puts "#{self.class.name} invoked"
    access_token = context.fetch(:token)
    access_token.destroy
  end
end

Doorkeeper::Plugin.register(AccessTokenPlugin, namespace: "access_token")

# app/controllers/tokens_controller.rb
token = FakeAccessToken.new

Doorkeeper::Plugin.run_for("access_token", token: token)
#=> AccessTokenPlugin invoked
#=> Token destroyed

nbulaj avatar Oct 10 '19 10:10 nbulaj

1 Warning
:warning: Please squash all your commits to a single one

Generated by :no_entry_sign: Danger

doorkeeper-bot avatar Oct 10 '19 15:10 doorkeeper-bot

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Dec 26 '19 09:12 stale[bot]