melaya 0.1.2 → 0.2.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.
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Melaya
4
+ # Accounts API — GDPR data export, API key management, and profile updates.
5
+ #
6
+ # Maps to /api/v1/private/accounts/* and /api/v1/private/keys/*.
7
+ #
8
+ # Note: credit balance endpoints live in BillingAPI to keep billing concerns
9
+ # co-located. Key listing is in AccountAPI (trading plane) for backward
10
+ # compatibility; this module covers the write/management side.
11
+ class AccountsAPI
12
+ def initialize(http)
13
+ @http = http
14
+ end
15
+
16
+ # POST /api/v1/private/accounts/export
17
+ # GDPR Art 15/20 data export; returns JSON blob of all user data.
18
+ def export_data
19
+ @http.post("/api/v1/private/accounts/export")
20
+ end
21
+
22
+ # DELETE /api/v1/private/keys/:keyId
23
+ # Remove a stored CEX API key.
24
+ # @param key_id [String]
25
+ def remove_key(key_id)
26
+ @http.delete("/api/v1/private/keys/#{enc(key_id)}")
27
+ end
28
+
29
+ # PATCH /api/v1/private/accounts/profile
30
+ # Update user display name, avatar, or settings.
31
+ # @param body [Hash]
32
+ def update_profile(body = {})
33
+ @http.patch("/api/v1/private/accounts/profile", body)
34
+ end
35
+
36
+ private
37
+
38
+ def enc(s)
39
+ URI.encode_www_form_component(s.to_s)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Melaya
4
+ # Assistant API — get and save the caller's onboarding / persona profile.
5
+ #
6
+ # The profile is stored envelope-encrypted (service=assistant_profile) and
7
+ # used to personalise the in-app assistant experience.
8
+ #
9
+ # Maps to /api/v1/private/assistant/profile.
10
+ #
11
+ # @example
12
+ # profile = melaya.assistant.get_profile
13
+ # melaya.assistant.set_profile(name: "Antoine", goals: ["grow my trading edge"])
14
+ class AssistantAPI
15
+ def initialize(http)
16
+ @http = http
17
+ end
18
+
19
+ # GET /api/v1/private/assistant/profile
20
+ # Get the caller's assistant onboarding profile.
21
+ def get_profile
22
+ @http.get("/api/v1/private/assistant/profile")
23
+ end
24
+
25
+ # PUT /api/v1/private/assistant/profile
26
+ # Save the caller's assistant onboarding profile.
27
+ # @param name [String, nil]
28
+ # @param goals [Array<String>, nil]
29
+ # @param context [String, nil]
30
+ # @param preferences [Hash, nil]
31
+ # @param extra [Hash] any additional profile fields
32
+ def set_profile(name: nil, goals: nil, context: nil, preferences: nil, **extra)
33
+ profile = extra.transform_keys(&:to_s)
34
+ profile["name"] = name unless name.nil?
35
+ profile["goals"] = goals unless goals.nil?
36
+ profile["context"] = context unless context.nil?
37
+ profile["preferences"] = preferences unless preferences.nil?
38
+ @http.put("/api/v1/private/assistant/profile", profile)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Melaya
4
+ # Auth API — login, MFA, registration, password management, and session tokens.
5
+ #
6
+ # Most endpoints are public (no Bearer token needed for login/register).
7
+ # However, the SDK HttpClient always sends the mk_* API key which is fine —
8
+ # the server simply ignores it for public routes.
9
+ #
10
+ # Maps to /api/v1/private/auth/* and /api/v1/auth/*.
11
+ class AuthAPI
12
+ def initialize(http)
13
+ @http = http
14
+ end
15
+
16
+ # POST /api/v1/private/auth/login
17
+ # Password + username login; returns session JWT + optional MFA challenge token.
18
+ # @param username [String]
19
+ # @param password [String]
20
+ def login(username:, password:)
21
+ @http.post("/api/v1/private/auth/login",
22
+ "username" => username,
23
+ "password" => password)
24
+ end
25
+
26
+ # POST /api/v1/private/auth/mfa/verify
27
+ # Resolve MFA challenge after login.
28
+ # @param challenge_token [String]
29
+ # @param code [String] TOTP or recovery code
30
+ def verify_mfa(challenge_token:, code:)
31
+ @http.post("/api/v1/private/auth/mfa/verify",
32
+ "challengeToken" => challenge_token,
33
+ "code" => code)
34
+ end
35
+
36
+ # POST /api/v1/private/auth/register
37
+ # Create a new user account; sends email verification.
38
+ def register(username:, email:, password:)
39
+ @http.post("/api/v1/private/auth/register",
40
+ "username" => username,
41
+ "email" => email,
42
+ "password" => password)
43
+ end
44
+
45
+ # POST /api/v1/private/auth/verify-signup
46
+ # Confirm email address from signup link token.
47
+ # @param token [String]
48
+ def verify_signup(token:)
49
+ @http.post("/api/v1/private/auth/verify-signup", "token" => token)
50
+ end
51
+
52
+ # POST /api/v1/private/auth/resend-verification
53
+ # Re-send email verification message.
54
+ # @param email [String]
55
+ def resend_verification(email:)
56
+ @http.post("/api/v1/private/auth/resend-verification", "email" => email)
57
+ end
58
+
59
+ # GET /api/v1/private/auth/me
60
+ # Return current authenticated user profile.
61
+ def me
62
+ @http.get("/api/v1/private/auth/me")
63
+ end
64
+
65
+ # GET /api/v1/private/auth/check
66
+ # Lightweight session validity check; returns ok: true.
67
+ def check
68
+ @http.get("/api/v1/private/auth/check")
69
+ end
70
+
71
+ # POST /api/v1/private/auth/change-password
72
+ # Change password (current + new).
73
+ # @param current_password [String]
74
+ # @param new_password [String]
75
+ def change_password(current_password:, new_password:)
76
+ @http.post("/api/v1/private/auth/change-password",
77
+ "currentPassword" => current_password,
78
+ "newPassword" => new_password)
79
+ end
80
+
81
+ # POST /api/v1/private/auth/forgot-password
82
+ # Initiate password reset flow; sends email with reset token.
83
+ # @param email [String]
84
+ def forgot_password(email:)
85
+ @http.post("/api/v1/private/auth/forgot-password", "email" => email)
86
+ end
87
+
88
+ # POST /api/v1/private/auth/reset-password
89
+ # Complete password reset using token from email.
90
+ # @param token [String]
91
+ # @param new_password [String]
92
+ def reset_password(token:, new_password:)
93
+ @http.post("/api/v1/private/auth/reset-password",
94
+ "token" => token,
95
+ "newPassword" => new_password)
96
+ end
97
+
98
+ # POST /api/v1/private/auth/mobile-handoff
99
+ # Create a short-lived handoff token for mobile app deep-link auth.
100
+ def mobile_handoff
101
+ @http.post("/api/v1/private/auth/mobile-handoff")
102
+ end
103
+
104
+ # GET /api/v1/private/auth/permissions
105
+ # Return caller's permission flags (capabilities, tier, feature gates).
106
+ def permissions
107
+ @http.get("/api/v1/private/auth/permissions")
108
+ end
109
+
110
+ # POST /api/v1/private/auth/refresh
111
+ # Rotate session JWT (sliding expiry); returns new token.
112
+ def refresh
113
+ @http.post("/api/v1/private/auth/refresh")
114
+ end
115
+
116
+ # ── MFA ───────────────────────────────────────────────────────────────────
117
+
118
+ # GET /api/v1/private/mfa/status
119
+ # Return MFA enrollment status for the caller.
120
+ def mfa_status
121
+ @http.get("/api/v1/private/mfa/status")
122
+ end
123
+
124
+ # POST /api/v1/private/mfa/setup
125
+ # Initiate TOTP setup; returns QR/secret.
126
+ def mfa_setup
127
+ @http.post("/api/v1/private/mfa/setup")
128
+ end
129
+
130
+ # POST /api/v1/private/mfa/confirm
131
+ # Confirm TOTP setup with first valid code; activates MFA.
132
+ # @param code [String]
133
+ def mfa_confirm(code:)
134
+ @http.post("/api/v1/private/mfa/confirm", "code" => code)
135
+ end
136
+ end
137
+ end
@@ -1,101 +1,139 @@
1
- # frozen_string_literal: true
2
-
3
- module Melaya
4
- # Backtest API — run strategies against historical data on the Rust engine.
5
- #
6
- # Start a single run or a parameter sweep (grid / random), poll the job,
7
- # then pull metrics, the equity curve, and the trade list. All backtests run
8
- # natively on Melaya's in-house engine — no per-venue SDK in the loop.
9
- #
10
- # Maps to https://api.melaya.org/api/v1/private/backtest/* on the private plane.
11
- class BacktestAPI
12
- def initialize(http)
13
- @http = http
14
- end
15
-
16
- # Start a backtest. +mode+ defaults to a single run; pass "grid_sweep" /
17
- # "random_sweep" with +param_ranges+ to fan out a parameter search.
18
- # Returns a hash with "job_id" (and optionally "count").
19
- #
20
- # @param body [Hash] see BacktestStart type in the reference SDKs
21
- def start(body)
22
- @http.post("/api/v1/private/backtest/start", body)
23
- end
24
-
25
- # Job status + progress (+status+, +progress_pct+, ...).
26
- # @param job_id [String]
27
- def job(job_id)
28
- @http.get("/api/v1/private/backtest/jobs/#{job_id}")
29
- end
30
-
31
- # Metrics, equity curve, and OHLCV for a completed job.
32
- # @param job_id [String]
33
- def results(job_id)
34
- @http.get("/api/v1/private/backtest/results/#{job_id}")["result"]
35
- end
36
-
37
- # The trade list for a completed job (default 500, max 5000 per call).
38
- # @param job_id [String]
39
- # @param limit [Integer, nil]
40
- # @param offset [Integer, nil]
41
- def trades(job_id, limit: nil, offset: nil)
42
- @http.get("/api/v1/private/backtest/trades/#{job_id}",
43
- "limit" => limit, "offset" => offset
44
- )["trades"]
45
- end
46
-
47
- # Ranked children of a sweep parent (default objective: sharpe DESC).
48
- # @param parent_id [String]
49
- # @param objective [String, nil]
50
- # @param limit [Integer, nil]
51
- def sweep(parent_id, objective: nil, limit: nil)
52
- @http.get("/api/v1/private/backtest/sweep/#{parent_id}",
53
- "objective" => objective, "limit" => limit
54
- )
55
- end
56
-
57
- # Your backtest jobs, newest first.
58
- # @param limit [Integer, nil]
59
- # @param offset [Integer, nil]
60
- def list(limit: nil, offset: nil)
61
- @http.get("/api/v1/private/backtest",
62
- "limit" => limit, "offset" => offset
63
- ).dig("data", "jobs")
64
- end
65
-
66
- # Your favorited backtest jobs (Forge tier and above).
67
- # @param limit [Integer, nil]
68
- # @param offset [Integer, nil]
69
- def favorites(limit: nil, offset: nil)
70
- @http.get("/api/v1/private/backtest/favorites",
71
- "limit" => limit, "offset" => offset
72
- ).dig("data", "jobs")
73
- end
74
-
75
- # Earliest funding-rate timestamp available for an exchange+symbol (ms, or nil).
76
- # @param exchange [String]
77
- # @param symbol [String]
78
- def funding_range(exchange:, symbol:)
79
- @http.get("/api/v1/private/backtest/funding-range",
80
- "exchange" => exchange, "symbol" => symbol
81
- )["earliest_ms"]
82
- end
83
-
84
- # Cancel an in-flight job.
85
- # @param job_id [String]
86
- def cancel(job_id)
87
- @http.post("/api/v1/private/backtest/#{job_id}/cancel")
88
- end
89
-
90
- # Soft-delete a single job.
91
- # @param job_id [String]
92
- def delete(job_id)
93
- @http.delete("/api/v1/private/backtest/#{job_id}")
94
- end
95
-
96
- # Soft-delete every non-favorited job. Returns a hash with "deleted" count.
97
- def delete_all
98
- @http.delete("/api/v1/private/backtest")
99
- end
100
- end
101
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Melaya
4
+ # Backtest API — run strategies against historical data on the Rust engine.
5
+ #
6
+ # Start a single run or a parameter sweep (grid / random), poll the job,
7
+ # then pull metrics, the equity curve, and the trade list. All backtests run
8
+ # natively on Melaya's in-house engine — no per-venue SDK in the loop.
9
+ #
10
+ # Maps to https://api.melaya.org/api/v1/private/backtest/* on the private plane.
11
+ class BacktestAPI
12
+ def initialize(http)
13
+ @http = http
14
+ end
15
+
16
+ # Start a backtest. +mode+ defaults to a single run; pass "grid_sweep" /
17
+ # "random_sweep" with +param_ranges+ to fan out a parameter search.
18
+ # Returns a hash with "job_id" (and optionally "count").
19
+ #
20
+ # @param body [Hash] see BacktestStart type in the reference SDKs
21
+ def start(body)
22
+ @http.post("/api/v1/private/backtest/start", body)
23
+ end
24
+
25
+ # Job status + progress (+status+, +progress_pct+, ...).
26
+ # @param job_id [String]
27
+ def job(job_id)
28
+ @http.get("/api/v1/private/backtest/jobs/#{job_id}")
29
+ end
30
+
31
+ # Metrics, equity curve, and OHLCV for a completed job.
32
+ # @param job_id [String]
33
+ def results(job_id)
34
+ @http.get("/api/v1/private/backtest/results/#{job_id}")["result"]
35
+ end
36
+
37
+ # The trade list for a completed job (default 500, max 5000 per call).
38
+ # @param job_id [String]
39
+ # @param limit [Integer, nil]
40
+ # @param offset [Integer, nil]
41
+ def trades(job_id, limit: nil, offset: nil)
42
+ @http.get("/api/v1/private/backtest/trades/#{job_id}",
43
+ "limit" => limit, "offset" => offset
44
+ )["trades"]
45
+ end
46
+
47
+ # Ranked children of a sweep parent (default objective: sharpe DESC).
48
+ # @param parent_id [String]
49
+ # @param objective [String, nil]
50
+ # @param limit [Integer, nil]
51
+ def sweep(parent_id, objective: nil, limit: nil)
52
+ @http.get("/api/v1/private/backtest/sweep/#{parent_id}",
53
+ "objective" => objective, "limit" => limit
54
+ )
55
+ end
56
+
57
+ # Your backtest jobs, newest first.
58
+ # @param limit [Integer, nil]
59
+ # @param offset [Integer, nil]
60
+ def list(limit: nil, offset: nil)
61
+ @http.get("/api/v1/private/backtest",
62
+ "limit" => limit, "offset" => offset
63
+ ).dig("data", "jobs")
64
+ end
65
+
66
+ # Your favorited backtest jobs (Forge tier and above).
67
+ # @param limit [Integer, nil]
68
+ # @param offset [Integer, nil]
69
+ def favorites(limit: nil, offset: nil)
70
+ @http.get("/api/v1/private/backtest/favorites",
71
+ "limit" => limit, "offset" => offset
72
+ ).dig("data", "jobs")
73
+ end
74
+
75
+ # Earliest funding-rate timestamp available for an exchange+symbol (ms, or nil).
76
+ # @param exchange [String]
77
+ # @param symbol [String]
78
+ def funding_range(exchange:, symbol:)
79
+ @http.get("/api/v1/private/backtest/funding-range",
80
+ "exchange" => exchange, "symbol" => symbol
81
+ )["earliest_ms"]
82
+ end
83
+
84
+ # Cancel an in-flight job.
85
+ # @param job_id [String]
86
+ def cancel(job_id)
87
+ @http.post("/api/v1/private/backtest/#{job_id}/cancel")
88
+ end
89
+
90
+ # Soft-delete a single job.
91
+ # @param job_id [String]
92
+ def delete(job_id)
93
+ @http.delete("/api/v1/private/backtest/#{job_id}")
94
+ end
95
+
96
+ # Soft-delete every non-favorited job. Returns a hash with "deleted" count.
97
+ def delete_all
98
+ @http.delete("/api/v1/private/backtest")
99
+ end
100
+
101
+ # ── Parameter sweep optimizer (restRoutes.ts /api/v1/private/backtest/optimize) ──
102
+
103
+ # POST /api/v1/private/backtest/optimize
104
+ # Start a parameter sweep optimization (genetic/grid) over a strategy config.
105
+ # @param body [Hash] strategy config + param ranges + objective
106
+ def optimize_start(body)
107
+ @http.post("/api/v1/private/backtest/optimize", body)
108
+ end
109
+
110
+ # GET /api/v1/private/backtest/optimize
111
+ # List optimization sweep runs.
112
+ # @param params [Hash]
113
+ def optimize_list(params = {})
114
+ @http.get("/api/v1/private/backtest/optimize", params)
115
+ end
116
+
117
+ # GET /api/v1/private/backtest/optimize/:optRunId/status
118
+ # Get the status/progress of an optimization sweep run.
119
+ # @param opt_run_id [String]
120
+ def optimize_status(opt_run_id)
121
+ @http.get("/api/v1/private/backtest/optimize/#{URI.encode_www_form_component(opt_run_id.to_s)}/status")
122
+ end
123
+
124
+ # POST /api/v1/private/backtest/optimize/:optRunId/cancel
125
+ # Cancel an in-progress optimization sweep.
126
+ # @param opt_run_id [String]
127
+ def optimize_cancel(opt_run_id)
128
+ @http.post("/api/v1/private/backtest/optimize/#{URI.encode_www_form_component(opt_run_id.to_s)}/cancel")
129
+ end
130
+
131
+ # POST /api/v1/private/backtest/optimize/:optRunId/apply
132
+ # Apply best params from a completed optimization sweep to a strategy.
133
+ # @param opt_run_id [String]
134
+ # @param body [Hash]
135
+ def optimize_apply(opt_run_id, body = {})
136
+ @http.post("/api/v1/private/backtest/optimize/#{URI.encode_www_form_component(opt_run_id.to_s)}/apply", body)
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Melaya
4
+ # Billing API — subscription status, Stripe checkout/portal sessions,
5
+ # public pricing plans, and credit balances.
6
+ #
7
+ # Maps to /api/v1/private/billing/* (authenticated) and
8
+ # /api/v1/billing/plans (public), and /api/v1/private/accounts/* for credits.
9
+ class BillingAPI
10
+ def initialize(http)
11
+ @http = http
12
+ end
13
+
14
+ # GET /api/v1/private/billing/subscription
15
+ # Get caller's current Stripe subscription status and tier.
16
+ def subscription
17
+ @http.get("/api/v1/private/billing/subscription")
18
+ end
19
+
20
+ # POST /api/v1/private/billing/checkout
21
+ # Create Stripe Checkout session for tier upgrade.
22
+ # Returns a URL to redirect the user to.
23
+ # @param price_id [String, nil] Stripe price ID
24
+ # @param tier [String, nil]
25
+ def create_checkout(price_id: nil, tier: nil)
26
+ body = compact("priceId" => price_id, "tier" => tier)
27
+ @http.post("/api/v1/private/billing/checkout", body)
28
+ end
29
+
30
+ # POST /api/v1/private/billing/portal
31
+ # Create Stripe Customer Portal session for subscription management.
32
+ # Returns a URL to redirect the user to.
33
+ def create_portal
34
+ @http.post("/api/v1/private/billing/portal")
35
+ end
36
+
37
+ # GET /api/v1/billing/plans (public)
38
+ # Return public pricing plan details (price IDs for forge/bastion/citadel tiers).
39
+ def plans
40
+ @http.get("/api/v1/billing/plans")
41
+ end
42
+
43
+ # ── Credits ───────────────────────────────────────────────────────────────
44
+
45
+ # GET /api/v1/private/accounts/credits
46
+ # Return current credit balance and transaction history.
47
+ def credits
48
+ @http.get("/api/v1/private/accounts/credits")
49
+ end
50
+
51
+ # GET /api/v1/private/accounts/credits/ai
52
+ # Return AI/LLM credit balance.
53
+ def ai_credits
54
+ @http.get("/api/v1/private/accounts/credits/ai")
55
+ end
56
+
57
+ # GET /api/v1/private/accounts/credits/portfolio-ideas
58
+ # Return portfolio-ideas feature credit balance.
59
+ def portfolio_ideas_credits
60
+ @http.get("/api/v1/private/accounts/credits/portfolio-ideas")
61
+ end
62
+
63
+ # GET /api/v1/private/accounts/credits/risk-monitoring
64
+ # Return risk-monitoring feature credit balance.
65
+ def risk_monitoring_credits
66
+ @http.get("/api/v1/private/accounts/credits/risk-monitoring")
67
+ end
68
+
69
+ private
70
+
71
+ def compact(hash)
72
+ hash.reject { |_, v| v.nil? }
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Melaya
4
+ # Bugs API — submit and track bug reports.
5
+ #
6
+ # Maps to /api/v1/private/bugs/*.
7
+ #
8
+ # @example
9
+ # melaya.bugs.create(title: "UI crash", description: "...")
10
+ # reports = melaya.bugs.list_mine
11
+ class BugsAPI
12
+ def initialize(http)
13
+ @http = http
14
+ end
15
+
16
+ # POST /api/v1/private/bugs
17
+ # Submit a bug report (user-facing feedback form).
18
+ # @param title [String]
19
+ # @param description [String]
20
+ # @param extra [Hash] any additional fields
21
+ def create(title:, description: nil, **extra)
22
+ body = extra.transform_keys(&:to_s)
23
+ body["title"] = title
24
+ body["description"] = description unless description.nil?
25
+ @http.post("/api/v1/private/bugs", body)
26
+ end
27
+
28
+ # GET /api/v1/private/bugs/mine
29
+ # List bug reports submitted by the caller.
30
+ def list_mine
31
+ @http.get("/api/v1/private/bugs/mine")
32
+ end
33
+
34
+ # GET /api/v1/private/bugs/:bugId
35
+ # Get a single bug report by ID.
36
+ # @param bug_id [String]
37
+ def get(bug_id)
38
+ @http.get("/api/v1/private/bugs/#{enc(bug_id)}")
39
+ end
40
+
41
+ # POST /api/v1/private/bugs/:bugId/comments
42
+ # Add a comment to a bug report.
43
+ # @param bug_id [String]
44
+ # @param comment [String]
45
+ def add_comment(bug_id, comment:)
46
+ @http.post("/api/v1/private/bugs/#{enc(bug_id)}/comments", "comment" => comment)
47
+ end
48
+
49
+ # GET /api/v1/private/bugs/notifications
50
+ # List unread bug-related notifications for the caller.
51
+ def list_notifications
52
+ @http.get("/api/v1/private/bugs/notifications")
53
+ end
54
+
55
+ # POST /api/v1/private/bugs/notifications/read
56
+ # Mark bug notifications as read.
57
+ def mark_notifications_read(body = {})
58
+ @http.post("/api/v1/private/bugs/notifications/read", body)
59
+ end
60
+
61
+ private
62
+
63
+ def enc(s)
64
+ URI.encode_www_form_component(s.to_s)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Melaya
4
+ # Project Connectors API — manage credentials at project scope.
5
+ #
6
+ # Project-scoped connectors are isolated per project, letting separate
7
+ # projects use different API keys for the same service.
8
+ #
9
+ # Maps to /api/v1/private/projects/:project/connectors/*.
10
+ #
11
+ # @example
12
+ # melaya.connectors.set("my-project", "openai", value: "sk-...")
13
+ # services = melaya.connectors.connected_services("my-project")
14
+ class ConnectorsAPI
15
+ def initialize(http)
16
+ @http = http
17
+ end
18
+
19
+ # GET /api/v1/private/projects/:project/connectors/services
20
+ # List connected services for a project.
21
+ # @param project [String] project name
22
+ def connected_services(project)
23
+ @http.get("/api/v1/private/projects/#{enc(project)}/connectors/services")
24
+ end
25
+
26
+ # PUT /api/v1/private/projects/:project/connectors/:service
27
+ # Store a connector credential at project scope.
28
+ # @param project [String]
29
+ # @param service [String]
30
+ # @param value [String]
31
+ # @param key [String, nil]
32
+ # @param label [String, nil]
33
+ def set(project, service, value:, key: nil, label: nil)
34
+ body = compact("value" => value, "key" => key, "label" => label)
35
+ @http.put("/api/v1/private/projects/#{enc(project)}/connectors/#{enc(service)}", body)
36
+ end
37
+
38
+ # DELETE /api/v1/private/projects/:project/connectors/:service
39
+ # Delete a project-scoped connector credential.
40
+ # @param project [String]
41
+ # @param service [String]
42
+ def delete(project, service)
43
+ @http.delete("/api/v1/private/projects/#{enc(project)}/connectors/#{enc(service)}")
44
+ end
45
+
46
+ # POST /api/v1/private/projects/:project/connectors/env-handle
47
+ # Get a short-lived env-handle token for project-scoped credentials.
48
+ # @param project [String]
49
+ def env_handle(project)
50
+ @http.post("/api/v1/private/projects/#{enc(project)}/connectors/env-handle")
51
+ end
52
+
53
+ # POST /api/v1/private/projects/:project/connectors/google/oauth
54
+ # Start Google OAuth flow for project-scoped connector.
55
+ # @param project [String]
56
+ # @param body [Hash]
57
+ def google_oauth_start(project, body = {})
58
+ @http.post("/api/v1/private/projects/#{enc(project)}/connectors/google/oauth", body)
59
+ end
60
+
61
+ private
62
+
63
+ def enc(s)
64
+ URI.encode_www_form_component(s.to_s)
65
+ end
66
+
67
+ def compact(hash)
68
+ hash.reject { |_, v| v.nil? }
69
+ end
70
+ end
71
+ end