connectors 0.1.0 → 0.1.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/CONNECTORS_FRAMEWORK.md +54 -99
- data/MCP_CLIENT.md +5 -5
- data/README.md +5 -3
- data/app/connectors/gmail/api.rb +6 -14
- data/app/connectors/gmail/connector.rb +11 -24
- data/app/connectors/gmail/mime_builder.rb +0 -3
- data/app/connectors/gmail/mime_parser.rb +1 -2
- data/app/connectors/gmail/polling.rb +7 -14
- data/app/connectors/resend/connector.rb +2 -4
- data/app/controllers/connectors/actions_controller.rb +0 -6
- data/app/controllers/connectors/credentials_controller.rb +10 -23
- data/app/controllers/connectors/grants_controller.rb +6 -11
- data/app/controllers/connectors/types_controller.rb +18 -45
- data/app/controllers/connectors/webhooks_controller.rb +2 -4
- data/app/jobs/connectors/deliver_webhook_job.rb +2 -4
- data/app/models/connectors/credential_share.rb +3 -10
- data/app/models/connectors/grant.rb +2 -4
- data/config/routes.rb +6 -13
- data/db/migrate/20260518210324_create_connectors_grants.rb +4 -11
- data/db/migrate/20260521140000_create_connectors_credential_shares.rb +2 -8
- data/docs/releasing.md +4 -4
- data/lib/connectors/action.rb +4 -7
- data/lib/connectors/action_builder.rb +5 -12
- data/lib/connectors/action_runner.rb +5 -11
- data/lib/connectors/auth_injection.rb +7 -18
- data/lib/connectors/client_builder.rb +4 -10
- data/lib/connectors/configuration.rb +9 -39
- data/lib/connectors/connector.rb +31 -83
- data/lib/connectors/credential_schema.rb +12 -29
- data/lib/connectors/credential_tester.rb +3 -4
- data/lib/connectors/credential_type_registry.rb +9 -20
- data/lib/connectors/credential_types/http_auth.rb +18 -55
- data/lib/connectors/engine.rb +3 -6
- data/lib/connectors/errors.rb +2 -5
- data/lib/connectors/middleware/authenticate_generic.rb +4 -14
- data/lib/connectors/middleware/pre_authentication.rb +2 -13
- data/lib/connectors/oauth/authorize_url.rb +3 -9
- data/lib/connectors/oauth/client_credentials.rb +0 -4
- data/lib/connectors/oauth/pkce.rb +3 -9
- data/lib/connectors/oauth/revoke.rb +2 -6
- data/lib/connectors/oauth1.rb +4 -8
- data/lib/connectors/permission_check.rb +2 -4
- data/lib/connectors/poll_runner.rb +1 -5
- data/lib/connectors/pre_authentication_helpers.rb +4 -9
- data/lib/connectors/version.rb +1 -1
- data/lib/connectors/webhook_context.rb +8 -18
- data/lib/connectors/webhook_lifecycle.rb +1 -8
- data/lib/connectors/webhook_methods.rb +2 -6
- data/openapi.yaml +16 -16
- metadata +21 -1
data/lib/connectors/engine.rb
CHANGED
|
@@ -32,11 +32,9 @@ module Connectors
|
|
|
32
32
|
initializer :connectors_register_credential_types do
|
|
33
33
|
reg = ::Connectors::CredentialTypeRegistry
|
|
34
34
|
reg.register(:oauth2, reg::OAUTH2)
|
|
35
|
-
#
|
|
36
|
-
# (packages/nodes-base/credentials/OAuth1Api.credentials.ts:1-72).
|
|
35
|
+
# Register the OAuth1 base credential schema.
|
|
37
36
|
reg.register(:oauth1_api, reg::OAUTH1)
|
|
38
|
-
#
|
|
39
|
-
# (packages/nodes-base/credentials/Http*Auth.credentials.ts).
|
|
37
|
+
# Register reusable HTTP authentication schemas.
|
|
40
38
|
reg.register(:http_basic_auth, ::Connectors::CredentialTypes::HTTP_BASIC_AUTH)
|
|
41
39
|
reg.register(:http_bearer_auth, ::Connectors::CredentialTypes::HTTP_BEARER_AUTH)
|
|
42
40
|
reg.register(:http_header_auth, ::Connectors::CredentialTypes::HTTP_HEADER_AUTH)
|
|
@@ -54,8 +52,7 @@ module Connectors
|
|
|
54
52
|
#
|
|
55
53
|
# Walks every Engine root (including the host's main app) and globs each
|
|
56
54
|
# one's `app/connectors/` — the union covers shipped connectors here in
|
|
57
|
-
# the engine
|
|
58
|
-
# `app/connectors/` (if/when the host decides to ship its own).
|
|
55
|
+
# the engine and connectors defined in the host's app/connectors directory.
|
|
59
56
|
config.to_prepare do
|
|
60
57
|
::Rails::Engine.subclasses.map(&:instance).push(::Rails.application).uniq.each do |engine|
|
|
61
58
|
dir = engine.root.join("app/connectors")
|
data/lib/connectors/errors.rb
CHANGED
|
@@ -32,11 +32,8 @@ module Connectors
|
|
|
32
32
|
class AuthenticationFailed < ApiError; end
|
|
33
33
|
class Forbidden < ApiError; end
|
|
34
34
|
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
# is a non-HTTP-Request node. Mirrors the visibility gate n8n applies via
|
|
38
|
-
# the credential's `supportedNodes` field (interfaces.ts:381) and
|
|
39
|
-
# `genericAuth` flag (interfaces.ts:379).
|
|
35
|
+
# Raised when a credential does not permit the requested node type
|
|
36
|
+
# under its supported_nodes and generic_auth settings.
|
|
40
37
|
class CredentialNotPermitted < Error
|
|
41
38
|
def initialize(connector_key, node_type, reason: nil)
|
|
42
39
|
msg = "credential #{connector_key.inspect} is not permitted for node #{node_type.inspect}"
|
|
@@ -2,17 +2,9 @@ require "base64"
|
|
|
2
2
|
|
|
3
3
|
module Connectors
|
|
4
4
|
module Middleware
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# (n8n source: packages/workflow/src/interfaces.ts:278-288 + the
|
|
9
|
-
# request-helpers consumer at
|
|
10
|
-
# packages/core/src/node-execute-functions.ts).
|
|
11
|
-
#
|
|
12
|
-
# The Grant's credentials hash supplies values for the
|
|
13
|
-
# `={{$credentials.x}}` templates inside `properties`. Resolution runs
|
|
14
|
-
# per-request so credentials that rotate between requests (e.g. after a
|
|
15
|
-
# `preAuthentication` hook lands in Phase 3) are reflected immediately.
|
|
5
|
+
# Apply resolved authentication properties to each outgoing request.
|
|
6
|
+
# Credential templates are evaluated per request so rotated values,
|
|
7
|
+
# including values from pre_authentication, take effect immediately.
|
|
16
8
|
class AuthenticateGeneric < Faraday::Middleware
|
|
17
9
|
def initialize(app, grant:, authenticate_config:)
|
|
18
10
|
super(app)
|
|
@@ -63,9 +55,7 @@ module Connectors
|
|
|
63
55
|
body.each { |k, v| env.body[k.to_s] = v }
|
|
64
56
|
end
|
|
65
57
|
|
|
66
|
-
#
|
|
67
|
-
# (interfaces.ts:198-202). Sets the `Authorization: Basic ...` header
|
|
68
|
-
# directly so we don't double-handle.
|
|
58
|
+
# Set the HTTP Basic Authorization header from username and password.
|
|
69
59
|
def apply_basic_auth(env, auth)
|
|
70
60
|
return if auth.nil?
|
|
71
61
|
username = auth[:username] || auth["username"]
|
|
@@ -1,18 +1,7 @@
|
|
|
1
1
|
module Connectors
|
|
2
2
|
module Middleware
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
# Mirrors n8n's contract at packages/workflow/src/interfaces.ts:374-377;
|
|
6
|
-
# n8n's runtime gates re-runs on `credentialsExpired: boolean` (see
|
|
7
|
-
# `ICredentialsHelper.runPreAuthentication` at :232-238). We use the
|
|
8
|
-
# same conceptual signal: if the credentials hash carries an
|
|
9
|
-
# `expires_at` (unix seconds) and it's in the past — OR no expires_at
|
|
10
|
-
# has ever been set — the hook fires and its return value is merged
|
|
11
|
-
# into the grant's credentials before the AuthenticateGeneric step.
|
|
12
|
-
#
|
|
13
|
-
# Reference impl: CrowdStrikeOAuth2Api.credentials.ts:62-76 — fetches
|
|
14
|
-
# an access token from `/oauth2/token` using client_id + client_secret,
|
|
15
|
-
# returns `{ sessionToken }` which feeds `authenticate.headers.Authorization`.
|
|
3
|
+
# Run pre_authentication when expires_at is absent or in the past.
|
|
4
|
+
# Merge returned credentials before AuthenticateGeneric injects them.
|
|
16
5
|
class PreAuthentication < Faraday::Middleware
|
|
17
6
|
def initialize(app, grant:, pre_auth_block:)
|
|
18
7
|
super(app)
|
|
@@ -16,11 +16,8 @@ module Connectors
|
|
|
16
16
|
@secrets = Connectors.configuration.oauth_credentials_for(connector_class.connector_key)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
# The
|
|
20
|
-
# per request
|
|
21
|
-
# need a wider or narrower scope than the connector's baseline).
|
|
22
|
-
# n8n's `Gmail OAuth2 API.credentials.ts` does the same thing — the
|
|
23
|
-
# node ships defaults, the workflow editor can ask for more.
|
|
19
|
+
# The declared scopes are defaults; callers can override them
|
|
20
|
+
# per authorization request through the scope argument.
|
|
24
21
|
def build(owner:, return_to: nil, scope: nil, name: nil, grant: nil)
|
|
25
22
|
pkce = pkce_params if @config[:grant_type].to_s == "pkce"
|
|
26
23
|
|
|
@@ -66,10 +63,7 @@ module Connectors
|
|
|
66
63
|
Connectors::OAuth::Pkce.generate
|
|
67
64
|
end
|
|
68
65
|
|
|
69
|
-
#
|
|
70
|
-
# has wired a separate frontend callback page (Activepieces-style split
|
|
71
|
-
# frontend/backend); otherwise falls back to the engine's own
|
|
72
|
-
# `/<connector>/callback` route (n8n-style monolithic).
|
|
66
|
+
# Use a configured frontend callback or the mounted engine callback.
|
|
73
67
|
def redirect_uri
|
|
74
68
|
Connectors.configuration.resolved_app_callback_url(@connector_class.connector_key)
|
|
75
69
|
end
|
|
@@ -7,10 +7,6 @@ module Connectors
|
|
|
7
7
|
# `grant_type=client_credentials` + `client_id`/`client_secret` to the
|
|
8
8
|
# provider's token endpoint and returns a normalized token hash that the
|
|
9
9
|
# OAuthController writes to a new or explicitly selected Grant.
|
|
10
|
-
#
|
|
11
|
-
# n8n parity: `OAuth2Api.credentials.ts:1-45` declares the `grantType`
|
|
12
|
-
# field with `clientCredentials` as one option; the runtime branch lives
|
|
13
|
-
# at `oauth.service.ts:783-789`.
|
|
14
10
|
class ClientCredentials
|
|
15
11
|
def self.exchange(connector_class)
|
|
16
12
|
new(connector_class).exchange
|
|
@@ -4,15 +4,9 @@ require "base64"
|
|
|
4
4
|
|
|
5
5
|
module Connectors
|
|
6
6
|
module OAuth
|
|
7
|
-
# RFC 7636
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# token so the callback can include it in the token exchange.
|
|
11
|
-
#
|
|
12
|
-
# n8n parity: `oauth.service.ts:578-586` — same `S256` derivation,
|
|
13
|
-
# verifier stashed in encrypted credential data until callback. We use
|
|
14
|
-
# the signed state token instead of a DB write so the flow stays
|
|
15
|
-
# stateless on the engine side.
|
|
7
|
+
# RFC 7636 PKCE using S256. Generate a random verifier and derive
|
|
8
|
+
# BASE64URL(SHA256(verifier)). Store the verifier in authenticated, encrypted
|
|
9
|
+
# OAuth state for use during the callback token exchange.
|
|
16
10
|
module Pkce
|
|
17
11
|
module_function
|
|
18
12
|
|
|
@@ -9,13 +9,9 @@ module Connectors
|
|
|
9
9
|
# request shape (Google's revoke is `?token=`, etc.), so a connector
|
|
10
10
|
# can also override the whole call with a block:
|
|
11
11
|
#
|
|
12
|
-
# revoke_token do |
|
|
13
|
-
#
|
|
12
|
+
# revoke_token do |grant|
|
|
13
|
+
# client.delete("oauth/tokens/#{grant.credentials_hash['access_token']}")
|
|
14
14
|
# end
|
|
15
|
-
#
|
|
16
|
-
# n8n parity: per-provider, no centralized contract — n8n's oauth
|
|
17
|
-
# service implements it ad-hoc on a per-credential basis. We give
|
|
18
|
-
# connectors a single uniform DSL.
|
|
19
15
|
class Revoke
|
|
20
16
|
def self.call(connector_class, grant)
|
|
21
17
|
new(connector_class, grant).call
|
data/lib/connectors/oauth1.rb
CHANGED
|
@@ -5,12 +5,8 @@ require "uri"
|
|
|
5
5
|
require "faraday"
|
|
6
6
|
|
|
7
7
|
module Connectors
|
|
8
|
-
# OAuth1.0a
|
|
9
|
-
# HMAC-SHA1
|
|
10
|
-
# `oauth.service.ts:601-700` (request-token leg) +
|
|
11
|
-
# `oauth1-credential.controller.ts:43-101` (callback / access-token leg).
|
|
12
|
-
# n8n uses the `oauth-1.0a` npm package; we implement the signing inline
|
|
13
|
-
# to keep the engine dependency-free.
|
|
8
|
+
# OAuth1.0a (RFC 5849) request-token and access-token exchanges
|
|
9
|
+
# with HMAC-SHA1, HMAC-SHA256 or HMAC-SHA512 signing.
|
|
14
10
|
module OAuth1
|
|
15
11
|
module_function
|
|
16
12
|
|
|
@@ -20,7 +16,7 @@ module Connectors
|
|
|
20
16
|
"HMAC-SHA512" => "SHA512"
|
|
21
17
|
}.freeze
|
|
22
18
|
|
|
23
|
-
#
|
|
19
|
+
# Request-token step: request the unauthorized request token. Sends an
|
|
24
20
|
# OAuth1-signed POST to `requestTokenUrl`. Provider returns
|
|
25
21
|
# `oauth_token` + `oauth_token_secret` (URL-encoded form). We need both
|
|
26
22
|
# to (a) build the authorize URL the owner is redirected to and (b)
|
|
@@ -60,7 +56,7 @@ module Connectors
|
|
|
60
56
|
}
|
|
61
57
|
end
|
|
62
58
|
|
|
63
|
-
#
|
|
59
|
+
# Exchange the verifier and request token for an access token.
|
|
64
60
|
def exchange_access_token(connector_class, oauth_token:, oauth_verifier:, oauth_token_secret:)
|
|
65
61
|
config = connector_class.oauth1_config or
|
|
66
62
|
raise Connectors::Error.new("#{connector_class}: oauth1 ... DSL not declared")
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
module Connectors
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
# HTTP-Request node use any credential, and `supportedNodes: [...]`
|
|
5
|
-
# restricts a credential to a specific node allowlist when set.
|
|
2
|
+
# Enforce supported_nodes restrictions, including generic_auth permission
|
|
3
|
+
# for :http_request. An empty allowlist permits every node type.
|
|
6
4
|
#
|
|
7
5
|
# Call from inside a node's execution wrapper before it touches the grant:
|
|
8
6
|
#
|
|
@@ -3,11 +3,7 @@ module Connectors
|
|
|
3
3
|
# and filters even when consumers share credentials. Omitting the key keeps
|
|
4
4
|
# the legacy, per-grant cursor for existing single-consumer integrations.
|
|
5
5
|
#
|
|
6
|
-
#
|
|
7
|
-
# cursor persistence via `getWorkflowStaticData('node')`
|
|
8
|
-
# (interfaces.ts:1257-1274). The scheduler that decides *when* to fire
|
|
9
|
-
# this is a workflow-roadmap concern; this primitive just exposes the
|
|
10
|
-
# one-shot contract.
|
|
6
|
+
# The host schedules calls and delivers returned items.
|
|
11
7
|
class PollRunner
|
|
12
8
|
GROUP = "polling".freeze
|
|
13
9
|
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
module Connectors
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
# packages/workflow/src/interfaces.ts:210-212). The block uses this to
|
|
5
|
-
# fetch a fresh token from an arbitrary endpoint without needing the
|
|
6
|
-
# full connector client (which itself depends on credentials that may
|
|
7
|
-
# not exist yet on first auth).
|
|
2
|
+
# HTTP helper for pre_authentication. Fetches credentials independently
|
|
3
|
+
# of the connector client, whose authentication may not yet be available.
|
|
8
4
|
#
|
|
9
5
|
# pre_authentication do |credentials, helpers|
|
|
10
6
|
# response = helpers.http_request(
|
|
@@ -44,9 +40,8 @@ module Connectors
|
|
|
44
40
|
req.body = body if body
|
|
45
41
|
end
|
|
46
42
|
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
# exploded" failures visible instead of silently merging a nil token.
|
|
43
|
+
# Raise on non-2xx responses so the hook cannot silently merge
|
|
44
|
+
# an unsuccessful token response into credentials.
|
|
50
45
|
if response.status >= 400
|
|
51
46
|
body_preview = response.body.is_a?(String) ? response.body.byteslice(0, 200) : response.body.inspect
|
|
52
47
|
raise Connectors::ApiError.new(
|
data/lib/connectors/version.rb
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
module Connectors
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
# `webhook_name` for multi-webhook providers, without having to thread
|
|
6
|
-
# `event.request_object.foo` through everywhere.
|
|
7
|
-
#
|
|
8
|
-
# Forwards a small set of methods to the underlying `WebhookEvent` so
|
|
9
|
-
# legacy handlers written against `event.payload_hash` keep working — see
|
|
10
|
-
# `Connector#handle_webhook` arity detection in `DeliverWebhookJob`.
|
|
2
|
+
# Request context passed to Connector#handle_webhook. Exposes parsed and
|
|
3
|
+
# raw body, headers, query and webhook group. Delegates payload aliases
|
|
4
|
+
# and exposes the persisted WebhookEvent for existing handlers.
|
|
11
5
|
class WebhookContext
|
|
12
6
|
attr_reader :event
|
|
13
7
|
|
|
@@ -15,33 +9,29 @@ module Connectors
|
|
|
15
9
|
@event = event
|
|
16
10
|
end
|
|
17
11
|
|
|
18
|
-
# Parsed body
|
|
12
|
+
# Parsed body, with an empty hash fallback.
|
|
19
13
|
def body
|
|
20
14
|
@event.payload_hash
|
|
21
15
|
end
|
|
22
16
|
alias payload body
|
|
23
17
|
alias payload_hash body
|
|
24
18
|
|
|
25
|
-
# Raw
|
|
26
|
-
# capture it (form-encoded payloads where Rails consumed the stream).
|
|
27
|
-
# n8n: `getRequestObject().rawBody`.
|
|
19
|
+
# Raw request body captured on the persisted event, when available.
|
|
28
20
|
def raw_body
|
|
29
21
|
@event.raw_body
|
|
30
22
|
end
|
|
31
23
|
|
|
32
|
-
#
|
|
33
|
-
# normalization n8n applies via `req.headers`.
|
|
24
|
+
# Request headers with lowercase keys.
|
|
34
25
|
def headers
|
|
35
26
|
@event.headers || {}
|
|
36
27
|
end
|
|
37
28
|
|
|
38
|
-
# Query string parameters
|
|
29
|
+
# Query string parameters.
|
|
39
30
|
def query
|
|
40
31
|
@event.query || {}
|
|
41
32
|
end
|
|
42
33
|
|
|
43
|
-
#
|
|
44
|
-
# n8n: `getWebhookName()`.
|
|
34
|
+
# Named webhook group, defaulting to :default.
|
|
45
35
|
def webhook_name
|
|
46
36
|
(@event.webhook_name || "default").to_sym
|
|
47
37
|
end
|
|
@@ -2,12 +2,6 @@ module Connectors
|
|
|
2
2
|
# Runs a connector's webhook_methods callbacks: subscribe = run create
|
|
3
3
|
# (skipping when check_exists already returns true), unsubscribe = run
|
|
4
4
|
# delete. State is persisted into `grant.static_data[group_name]`.
|
|
5
|
-
#
|
|
6
|
-
# n8n parity: this is the per-trigger activation orchestration that
|
|
7
|
-
# `cli/src/active-workflow-runner.ts` performs around each
|
|
8
|
-
# `INodeType#webhookMethods.default.*` call. We provide it as a connector-
|
|
9
|
-
# side primitive so the eventual workflow-side activation manager can
|
|
10
|
-
# call into it without re-implementing the gating.
|
|
11
5
|
class WebhookLifecycle
|
|
12
6
|
def self.subscribe(grant, hook_url:, webhook_name: :default)
|
|
13
7
|
new(grant, webhook_name).subscribe(hook_url: hook_url)
|
|
@@ -27,8 +21,7 @@ module Connectors
|
|
|
27
21
|
)
|
|
28
22
|
end
|
|
29
23
|
|
|
30
|
-
#
|
|
31
|
-
# already has an equivalent subscription. Otherwise run create.
|
|
24
|
+
# Skip creation when check_exists finds an existing subscription.
|
|
32
25
|
def subscribe(hook_url:)
|
|
33
26
|
result = nil
|
|
34
27
|
@grant.update_static_data!(@webhook_name) do |sub|
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
module Connectors
|
|
2
|
-
# Declarative webhook subscription
|
|
3
|
-
# `INodeType.webhookMethods.default.{checkExists, create, delete}`
|
|
4
|
-
# (interfaces.ts:2017, 2091-2095; reference impl in
|
|
5
|
-
# `packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts:114-247`).
|
|
2
|
+
# Declarative webhook subscription callbacks.
|
|
6
3
|
#
|
|
7
4
|
# Provider-specific contract:
|
|
8
5
|
#
|
|
@@ -28,8 +25,7 @@ module Connectors
|
|
|
28
25
|
# end
|
|
29
26
|
#
|
|
30
27
|
# Multi-webhook providers (Slack has separate `default` event-delivery
|
|
31
|
-
# group + `setup` URL-verification group
|
|
32
|
-
# at interfaces.ts:2600) declare additional named groups:
|
|
28
|
+
# group + `setup` URL-verification group) declare additional named groups:
|
|
33
29
|
#
|
|
34
30
|
# webhook_methods :setup do ... end
|
|
35
31
|
# webhook_methods :default do ... end
|
data/openapi.yaml
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
openapi: 3.1.0
|
|
2
2
|
info:
|
|
3
3
|
title: Connectors Engine API
|
|
4
|
-
version: "0.1.
|
|
4
|
+
version: "0.1.1"
|
|
5
5
|
description: |
|
|
6
|
-
Rails engine
|
|
6
|
+
Rails engine for encrypted credentials, provider authorization, actions, webhooks, polling and remote MCP tools.
|
|
7
7
|
Every endpoint is namespaced under the engine's mount path (default `/connectors`).
|
|
8
8
|
|
|
9
9
|
**Auth model.** The engine itself doesn't authenticate requests — the host wires
|
|
10
10
|
`Connectors.configuration.current_owner_resolver` to extract the requester from the
|
|
11
11
|
controller (session, JWT, API key — host's choice). Endpoints that mutate
|
|
12
12
|
credentials require an `Owner` to be resolvable; those that read enforce
|
|
13
|
-
role-based visibility via `principal_resolver
|
|
13
|
+
role-based visibility via `principal_resolver`.
|
|
14
14
|
|
|
15
15
|
**Source of truth.** This document is hand-maintained alongside the engine.
|
|
16
|
-
Cross-check against `
|
|
16
|
+
Cross-check against `config/routes.rb` and the controller files —
|
|
17
17
|
they are the implementation.
|
|
18
18
|
|
|
19
|
-
**Mount path.** The host
|
|
20
|
-
|
|
19
|
+
**Mount path.** The host chooses the engine mount, such as `/connectors`
|
|
20
|
+
or `/api/v1/connectors`. The engine introspects its actual
|
|
21
21
|
mount via `Connectors::Engine.mount_path`, so OAuth callback URLs and
|
|
22
22
|
webhook subscribe URLs in responses always reflect reality.
|
|
23
23
|
contact:
|
|
@@ -32,7 +32,7 @@ servers:
|
|
|
32
32
|
default: "http://localhost:3000"
|
|
33
33
|
description: Scheme + host of the host application
|
|
34
34
|
mount:
|
|
35
|
-
default: "/
|
|
35
|
+
default: "/connectors"
|
|
36
36
|
description: Path where the host mounts Connectors::Engine
|
|
37
37
|
|
|
38
38
|
tags:
|
|
@@ -281,7 +281,7 @@ paths:
|
|
|
281
281
|
type: { type: string, description: "connector key" }
|
|
282
282
|
name: { type: string }
|
|
283
283
|
data: { type: object, additionalProperties: true, description: "credential values" }
|
|
284
|
-
external_ref: { type: string, description: "vault reference
|
|
284
|
+
external_ref: { type: string, description: "vault reference" }
|
|
285
285
|
is_managed: { type: boolean, default: false }
|
|
286
286
|
responses:
|
|
287
287
|
"201":
|
|
@@ -804,7 +804,7 @@ paths:
|
|
|
804
804
|
- { name: webhook_name, in: path, required: true, schema: { type: string } }
|
|
805
805
|
post:
|
|
806
806
|
tags: [Webhooks]
|
|
807
|
-
summary: App-level named-group webhook
|
|
807
|
+
summary: App-level named-group webhook
|
|
808
808
|
operationId: receiveAppWebhookNamed
|
|
809
809
|
requestBody:
|
|
810
810
|
required: true
|
|
@@ -888,12 +888,12 @@ components:
|
|
|
888
888
|
extends: { type: array, items: { type: string }, description: "base credential types inherited from" }
|
|
889
889
|
properties:
|
|
890
890
|
type: array
|
|
891
|
-
description: "
|
|
891
|
+
description: "Credential form field descriptors"
|
|
892
892
|
items: { $ref: "#/components/schemas/Field" }
|
|
893
893
|
authenticate:
|
|
894
894
|
type: object
|
|
895
895
|
nullable: true
|
|
896
|
-
description: "
|
|
896
|
+
description: "Declarative credential injection properties"
|
|
897
897
|
properties:
|
|
898
898
|
type: { type: string, enum: [generic] }
|
|
899
899
|
properties: { type: object, additionalProperties: true }
|
|
@@ -960,7 +960,7 @@ components:
|
|
|
960
960
|
|
|
961
961
|
Action:
|
|
962
962
|
type: object
|
|
963
|
-
description: "
|
|
963
|
+
description: "A provider operation exposed by a connector"
|
|
964
964
|
properties:
|
|
965
965
|
name: { type: string, description: "machine key, e.g. send_email" }
|
|
966
966
|
display_name: { type: string }
|
|
@@ -973,12 +973,12 @@ components:
|
|
|
973
973
|
items: { $ref: "#/components/schemas/Field" }
|
|
974
974
|
output:
|
|
975
975
|
type: array
|
|
976
|
-
description: "
|
|
976
|
+
description: "Schema of the action result"
|
|
977
977
|
items: { $ref: "#/components/schemas/Field" }
|
|
978
978
|
|
|
979
979
|
Field:
|
|
980
980
|
type: object
|
|
981
|
-
description: "
|
|
981
|
+
description: "Input field descriptor used to render a form"
|
|
982
982
|
properties:
|
|
983
983
|
name: { type: string }
|
|
984
984
|
displayName: { type: string }
|
|
@@ -1011,7 +1011,7 @@ components:
|
|
|
1011
1011
|
displayOptions:
|
|
1012
1012
|
type: object
|
|
1013
1013
|
additionalProperties: true
|
|
1014
|
-
description: "
|
|
1014
|
+
description: "Field visibility rules with _cnd predicates"
|
|
1015
1015
|
options:
|
|
1016
1016
|
type: array
|
|
1017
1017
|
items: { type: object, additionalProperties: true }
|
|
@@ -1020,7 +1020,7 @@ components:
|
|
|
1020
1020
|
type: object
|
|
1021
1021
|
properties:
|
|
1022
1022
|
id: { type: string, format: uuid }
|
|
1023
|
-
type: { type: string, description: "
|
|
1023
|
+
type: { type: string, description: "Connector key" }
|
|
1024
1024
|
connector_key: { type: string, deprecated: true }
|
|
1025
1025
|
name: { type: string, description: "display name" }
|
|
1026
1026
|
external_account_id: { type: string, nullable: true }
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: connectors
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jackson Helio
|
|
@@ -23,6 +23,26 @@ dependencies:
|
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: 8.1.3
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: json
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '2.3'
|
|
33
|
+
- - "<"
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: '3'
|
|
36
|
+
type: :runtime
|
|
37
|
+
prerelease: false
|
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
- - ">="
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '2.3'
|
|
43
|
+
- - "<"
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '3'
|
|
26
46
|
- !ruby/object:Gem::Dependency
|
|
27
47
|
name: faraday
|
|
28
48
|
requirement: !ruby/object:Gem::Requirement
|