connectors 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +35 -0
  3. data/CONNECTORS_FRAMEWORK.md +799 -0
  4. data/CONTRIBUTING.md +60 -0
  5. data/MCP_CLIENT.md +168 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +146 -0
  8. data/app/connectors/clickup/connector.rb +13 -0
  9. data/app/connectors/gmail/api.rb +114 -0
  10. data/app/connectors/gmail/connector.rb +921 -0
  11. data/app/connectors/gmail/mime_builder.rb +261 -0
  12. data/app/connectors/gmail/mime_parser.rb +106 -0
  13. data/app/connectors/gmail/polling.rb +154 -0
  14. data/app/connectors/remote_mcp/connector.rb +18 -0
  15. data/app/connectors/resend/connector.rb +218 -0
  16. data/app/controllers/concerns/connectors/grant_access.rb +43 -0
  17. data/app/controllers/connectors/actions_controller.rb +66 -0
  18. data/app/controllers/connectors/application_controller.rb +5 -0
  19. data/app/controllers/connectors/credentials_controller.rb +245 -0
  20. data/app/controllers/connectors/grants_controller.rb +123 -0
  21. data/app/controllers/connectors/mcp_controller.rb +88 -0
  22. data/app/controllers/connectors/oauth_controller.rb +134 -0
  23. data/app/controllers/connectors/types_controller.rb +144 -0
  24. data/app/controllers/connectors/webhooks_controller.rb +105 -0
  25. data/app/jobs/connectors/application_job.rb +4 -0
  26. data/app/jobs/connectors/deliver_webhook_job.rb +27 -0
  27. data/app/jobs/connectors/poll_job.rb +49 -0
  28. data/app/models/connectors/application_record.rb +5 -0
  29. data/app/models/connectors/credential_share.rb +31 -0
  30. data/app/models/connectors/grant.rb +103 -0
  31. data/app/models/connectors/mcp_authorization.rb +6 -0
  32. data/app/models/connectors/mcp_interaction.rb +6 -0
  33. data/app/models/connectors/poll_state.rb +17 -0
  34. data/app/models/connectors/webhook_event.rb +19 -0
  35. data/config/routes.rb +68 -0
  36. data/db/migrate/20260518210324_create_connectors_grants.rb +49 -0
  37. data/db/migrate/20260518214609_create_connectors_webhook_events.rb +35 -0
  38. data/db/migrate/20260521140000_create_connectors_credential_shares.rb +26 -0
  39. data/db/migrate/20260922120000_create_connectors_poll_states.rb +12 -0
  40. data/db/migrate/20260922130000_create_connectors_mcp_transactions.rb +18 -0
  41. data/docs/adding-connectors.md +92 -0
  42. data/docs/architecture.md +71 -0
  43. data/docs/releasing.md +60 -0
  44. data/lib/connectors/action.rb +90 -0
  45. data/lib/connectors/action_builder.rb +125 -0
  46. data/lib/connectors/action_runner.rb +99 -0
  47. data/lib/connectors/auth/scheme/api_key.rb +51 -0
  48. data/lib/connectors/auth/scheme/oauth2.rb +23 -0
  49. data/lib/connectors/auth/scheme.rb +35 -0
  50. data/lib/connectors/auth.rb +4 -0
  51. data/lib/connectors/auth_injection.rb +65 -0
  52. data/lib/connectors/client_builder.rb +87 -0
  53. data/lib/connectors/configuration.rb +138 -0
  54. data/lib/connectors/connector.rb +565 -0
  55. data/lib/connectors/credential_schema.rb +219 -0
  56. data/lib/connectors/credential_tester.rb +85 -0
  57. data/lib/connectors/credential_type_registry.rb +162 -0
  58. data/lib/connectors/credential_types/http_auth.rb +171 -0
  59. data/lib/connectors/engine.rb +123 -0
  60. data/lib/connectors/errors.rb +88 -0
  61. data/lib/connectors/grant_policy.rb +13 -0
  62. data/lib/connectors/mcp/access.rb +50 -0
  63. data/lib/connectors/mcp/authorization.rb +177 -0
  64. data/lib/connectors/mcp/authorization_context.rb +25 -0
  65. data/lib/connectors/mcp/authorization_discovery.rb +42 -0
  66. data/lib/connectors/mcp/cancellation.rb +36 -0
  67. data/lib/connectors/mcp/client.rb +137 -0
  68. data/lib/connectors/mcp/connection_config.rb +48 -0
  69. data/lib/connectors/mcp/http.rb +108 -0
  70. data/lib/connectors/mcp/interaction.rb +82 -0
  71. data/lib/connectors/mcp/pending_transaction.rb +26 -0
  72. data/lib/connectors/mcp/protocol/2026-07-28.json +3963 -0
  73. data/lib/connectors/mcp/protocol/LICENSE +216 -0
  74. data/lib/connectors/mcp/protocol/README.md +8 -0
  75. data/lib/connectors/mcp/protocol_schema.rb +30 -0
  76. data/lib/connectors/mcp/schema.rb +45 -0
  77. data/lib/connectors/mcp/settings.rb +27 -0
  78. data/lib/connectors/mcp/token_endpoint.rb +48 -0
  79. data/lib/connectors/mcp/transport.rb +105 -0
  80. data/lib/connectors/mcp.rb +69 -0
  81. data/lib/connectors/middleware/authenticate_generic.rb +79 -0
  82. data/lib/connectors/middleware/auto_refresh.rb +71 -0
  83. data/lib/connectors/middleware/error_normalization.rb +45 -0
  84. data/lib/connectors/middleware/grant_status.rb +19 -0
  85. data/lib/connectors/middleware/pre_authentication.rb +54 -0
  86. data/lib/connectors/middleware/rate_limit.rb +40 -0
  87. data/lib/connectors/oauth/authorize_url.rb +78 -0
  88. data/lib/connectors/oauth/client_authentication.rb +24 -0
  89. data/lib/connectors/oauth/client_credentials.rb +43 -0
  90. data/lib/connectors/oauth/grant_writer.rb +73 -0
  91. data/lib/connectors/oauth/pkce.rb +32 -0
  92. data/lib/connectors/oauth/revoke.rb +72 -0
  93. data/lib/connectors/oauth/state.rb +41 -0
  94. data/lib/connectors/oauth/token_exchange.rb +69 -0
  95. data/lib/connectors/oauth/token_response.rb +40 -0
  96. data/lib/connectors/oauth.rb +4 -0
  97. data/lib/connectors/oauth1.rb +151 -0
  98. data/lib/connectors/permission_check.rb +33 -0
  99. data/lib/connectors/poll_runner.rb +50 -0
  100. data/lib/connectors/pre_authentication_helpers.rb +76 -0
  101. data/lib/connectors/registry.rb +38 -0
  102. data/lib/connectors/version.rb +3 -0
  103. data/lib/connectors/webhook_context.rb +62 -0
  104. data/lib/connectors/webhook_lifecycle.rb +69 -0
  105. data/lib/connectors/webhook_methods.rb +48 -0
  106. data/lib/connectors/webhooks/verifier.rb +31 -0
  107. data/lib/connectors/webhooks.rb +5 -0
  108. data/lib/connectors.rb +50 -0
  109. data/lib/tasks/connectors_mcp.rake +9 -0
  110. data/lib/tasks/connectors_tasks.rake +4 -0
  111. data/openapi.yaml +1099 -0
  112. metadata +263 -0
@@ -0,0 +1,138 @@
1
+ module Connectors
2
+ # Host-app integration surface. The engine never references host constants
3
+ # like `User` directly — it goes through this block, which the host wires up
4
+ # in an initializer:
5
+ #
6
+ # # config/initializers/connectors.rb
7
+ # Connectors.configure do |c|
8
+ # c.owner_class_name = "User"
9
+ # c.current_owner_resolver = ->(controller) { controller.current_user }
10
+ # c.host_base_url = ENV.fetch("APP_BASE_URL", "http://localhost:3000")
11
+ # c.oauth_credentials = {
12
+ # slack: { client_id: ENV["SLACK_CLIENT_ID"], client_secret: ENV["SLACK_CLIENT_SECRET"] },
13
+ # linear: { client_id: ENV["LINEAR_CLIENT_ID"], client_secret: ENV["LINEAR_CLIENT_SECRET"] }
14
+ # }
15
+ # c.on_webhook = ->(event) { Inbox::IngestJob.perform_later(event.id) }
16
+ # end
17
+ class Configuration
18
+ attr_accessor :owner_class_name,
19
+ :current_owner_resolver,
20
+ :host_base_url,
21
+ :app_callback_url,
22
+ :on_webhook,
23
+ :principal_resolver,
24
+ :secrets_resolver,
25
+ :secrets_managed_fields_for
26
+
27
+ attr_reader :oauth_credentials
28
+
29
+ def mcp
30
+ @mcp ||= MCP::Settings.new
31
+ end
32
+
33
+ def resolved_mcp_callback_url
34
+ mcp.callback_url || "#{host_base_url.to_s.chomp('/')}#{Connectors::Engine.mount_path}/mcp/oauth/callback"
35
+ end
36
+
37
+ def initialize
38
+ @oauth_credentials = {}
39
+ end
40
+
41
+ def oauth_credentials=(map)
42
+ @oauth_credentials = (map || {}).transform_keys(&:to_sym)
43
+ end
44
+
45
+ def owner_class
46
+ raise Error.new("Connectors.configuration.owner_class_name is not set") if owner_class_name.nil?
47
+ owner_class_name.constantize
48
+ end
49
+
50
+ def oauth_credentials_for(connector_key)
51
+ @oauth_credentials.fetch(connector_key.to_sym) do
52
+ raise Error.new("no oauth_credentials configured for #{connector_key.inspect}")
53
+ end
54
+ end
55
+
56
+ def resolve_owner(controller)
57
+ raise Error.new("Connectors.configuration.current_owner_resolver is not set") if current_owner_resolver.nil?
58
+ current_owner_resolver.call(controller)
59
+ end
60
+
61
+ # The host wires in a block that, given the controller (or any context
62
+ # with `current_user`/etc.), returns an array of `[principal_type,
63
+ # principal_id]` pairs representing every identity the requester has
64
+ # for sharing-visibility purposes.
65
+ #
66
+ # Default: just the owner — `[["<owner_class_name>", owner.id]]`. EE
67
+ # hosts override to include teams / projects:
68
+ #
69
+ # c.principal_resolver = ->(ctrl) {
70
+ # [
71
+ # ["User", ctrl.current_user.id],
72
+ # *ctrl.current_user.team_ids.map { |id| ["Team", id] },
73
+ # ]
74
+ # }
75
+ # n8n parity: external secrets EE module
76
+ # (`external-secrets.controller.ee.ts`). Host wires a block that takes a
77
+ # Grant (one whose `is_managed?` is true) and returns the hash to merge
78
+ # into the credentials at request time. Returned values overwrite the
79
+ # DB-stored values for the lifetime of that Grant instance.
80
+ #
81
+ # c.secrets_resolver = ->(grant) {
82
+ # vault_client.read("kv/data/connectors/#{grant.connector_key}/#{grant.external_ref}")
83
+ # }
84
+ def resolve_secrets(grant)
85
+ return {} if secrets_resolver.nil?
86
+ secrets_resolver.call(grant).to_h.transform_keys(&:to_s)
87
+ end
88
+
89
+ # Per-connector list of field names sourced from the vault — surfaces
90
+ # in `GET /connectors/types/:name` as `__overwritten_properties` so the
91
+ # editor can hide / lock those fields. n8n parity:
92
+ # `frontend.service.ts:681-705` — same shape, just emitted server-side
93
+ # instead of recomputed in the editor.
94
+ def managed_fields_for(connector_key)
95
+ return [] if secrets_managed_fields_for.nil?
96
+ Array(secrets_managed_fields_for.call(connector_key)).map(&:to_s)
97
+ end
98
+
99
+ def resolve_principals(controller)
100
+ return principal_resolver.call(controller) if principal_resolver
101
+ owner = resolve_owner(controller)
102
+ return [] if owner.nil?
103
+ [ [ owner.class.name, owner.id ] ]
104
+ end
105
+
106
+ # The URL the OAuth provider redirects back to after consent. Defaults
107
+ # to the engine's own callback path (n8n-style: provider → backend →
108
+ # render HTML → close popup). For split frontend/backend setups
109
+ # (Activepieces-style: provider → frontend → POST exchange → backend),
110
+ # set this to the FRONTEND's callback page, e.g.
111
+ # `"http://localhost:3001/oauth/callback"`.
112
+ #
113
+ # Either way it must MATCH a value registered in the provider's app
114
+ # console; if it doesn't, Google/Slack/etc. respond with
115
+ # `redirect_uri_mismatch` before consent even renders.
116
+ def resolved_app_callback_url(connector_key)
117
+ return app_callback_url if app_callback_url.present?
118
+ base = host_base_url or
119
+ raise Error.new("Connectors.configuration.host_base_url is not set")
120
+ "#{base.chomp('/')}#{Connectors::Engine.mount_path}/#{connector_key}/callback"
121
+ end
122
+ end
123
+
124
+ class << self
125
+ def configure
126
+ yield configuration
127
+ end
128
+
129
+ def configuration
130
+ @configuration ||= Configuration.new
131
+ end
132
+
133
+ # For tests — reset between cases.
134
+ def reset_configuration!
135
+ @configuration = Configuration.new
136
+ end
137
+ end
138
+ end