edgebase_admin 0.1.4
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 +7 -0
- data/LICENSE +21 -0
- data/README.md +176 -0
- data/lib/edgebase_admin/admin_auth.rb +98 -0
- data/lib/edgebase_admin/admin_client.rb +135 -0
- data/lib/edgebase_admin/analytics_client.rb +61 -0
- data/lib/edgebase_admin/d1_client.rb +26 -0
- data/lib/edgebase_admin/functions_client.rb +46 -0
- data/lib/edgebase_admin/generated/admin_api_core.rb +503 -0
- data/lib/edgebase_admin/kv_client.rb +44 -0
- data/lib/edgebase_admin/push_client.rb +58 -0
- data/lib/edgebase_admin/vectorize_client.rb +58 -0
- data/lib/edgebase_admin.rb +5 -0
- data/llms.txt +117 -0
- metadata +75 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 7788c6699a8440a552520aaaf8f7374d52b8fbfe41888b119686f56ddff67e91
|
|
4
|
+
data.tar.gz: 1006a04397e2ff7b73049156f853f7b6a566e2b5cd01a14a57154dab2fb3d7bc
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 16c9a66d72b3c0b8440efba908b01b8d9a99aa5bd44f53cbe5efd48fb5acf5d610f6f0b67d611c6bc020885461b46f15d24af652bab7d6d5d466a85c0b78f284
|
|
7
|
+
data.tar.gz: dc86005559bc4a81e7553d02aea3a501b556bacc1751051d50f4d091184f7c9e4a8c501138851b3e07b367d8dbe4301bdaa55955e6f178d0b00e05eb1b7d1c2f
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 melodysdreamj
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# EdgeBase Ruby Admin SDK
|
|
2
|
+
|
|
3
|
+
Trusted server-side Ruby SDK for EdgeBase.
|
|
4
|
+
|
|
5
|
+
Use `edgebase_admin` from backend apps, scripts, cron jobs, and other trusted Ruby runtimes that hold a Service Key. It exposes admin auth, database access, raw SQL, storage, push, analytics, functions, and native edge resources.
|
|
6
|
+
|
|
7
|
+
Ruby uses a synchronous, object-oriented API. Most methods return values directly rather than promises or futures.
|
|
8
|
+
|
|
9
|
+
## Documentation Map
|
|
10
|
+
|
|
11
|
+
Use this README for a fast overview, then jump into the docs when you need depth:
|
|
12
|
+
|
|
13
|
+
- [SDK Overview](https://edgebase.fun/docs/sdks)
|
|
14
|
+
Install commands and the public SDK matrix
|
|
15
|
+
- [Admin SDK](https://edgebase.fun/docs/sdks/client-vs-server)
|
|
16
|
+
Trusted-server boundaries and admin-only capabilities
|
|
17
|
+
- [Admin SDK Reference](https://edgebase.fun/docs/admin-sdk/reference)
|
|
18
|
+
Cross-language examples for auth, database, storage, functions, push, and analytics
|
|
19
|
+
- [Admin User Management](https://edgebase.fun/docs/authentication/admin-users)
|
|
20
|
+
Create, update, delete, and manage users with a Service Key
|
|
21
|
+
- [Database Admin SDK](https://edgebase.fun/docs/database/admin-sdk)
|
|
22
|
+
Table queries, filters, pagination, batch writes, and raw SQL
|
|
23
|
+
- [Storage](https://edgebase.fun/docs/storage/upload-download)
|
|
24
|
+
Uploads, downloads, metadata, and signed URLs
|
|
25
|
+
- [Analytics Admin SDK](https://edgebase.fun/docs/analytics/admin-sdk)
|
|
26
|
+
Request metrics, event tracking, and event queries
|
|
27
|
+
- [Push Admin SDK](https://edgebase.fun/docs/push/admin-sdk)
|
|
28
|
+
Push send, topic broadcast, token inspection, and logs
|
|
29
|
+
- [Native Resources](https://edgebase.fun/docs/server/native-resources)
|
|
30
|
+
KV, D1, Vectorize, and other trusted edge-native resources
|
|
31
|
+
|
|
32
|
+
## For AI Coding Assistants
|
|
33
|
+
|
|
34
|
+
This package includes an `llms.txt` file for AI-assisted development.
|
|
35
|
+
|
|
36
|
+
Use it when you want an agent or code assistant to:
|
|
37
|
+
|
|
38
|
+
- keep Service Keys on trusted servers
|
|
39
|
+
- use the real Ruby method names and keyword arguments
|
|
40
|
+
- avoid copying JS promise-based examples into Ruby
|
|
41
|
+
- know when to use `admin_auth` versus the `auth` alias
|
|
42
|
+
|
|
43
|
+
You can find it:
|
|
44
|
+
|
|
45
|
+
- in this repository: [llms.txt](https://github.com/edge-base/edgebase/blob/main/packages/sdk/ruby/packages/admin/llms.txt)
|
|
46
|
+
- in your environment after install, inside the `edgebase_admin` package directory as `llms.txt`
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
gem install edgebase_admin
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
If you use Bundler, add `edgebase_admin` to your Gemfile and run `bundle install`.
|
|
55
|
+
|
|
56
|
+
## Quick Start
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
require "edgebase_admin"
|
|
60
|
+
|
|
61
|
+
admin = EdgebaseAdmin::AdminClient.new(
|
|
62
|
+
"https://your-project.edgebase.fun",
|
|
63
|
+
service_key: ENV.fetch("EDGEBASE_SERVICE_KEY")
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
users = admin.admin_auth.list_users(limit: 20)
|
|
67
|
+
|
|
68
|
+
rows = admin.sql(
|
|
69
|
+
"shared",
|
|
70
|
+
"SELECT id, title FROM posts WHERE status = ?",
|
|
71
|
+
["published"]
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
bucket = admin.storage.bucket("avatars")
|
|
75
|
+
bucket.upload("user-1.jpg", "binary-data", content_type: "image/jpeg")
|
|
76
|
+
|
|
77
|
+
admin.push.send("user-1", {
|
|
78
|
+
"title" => "Deployment finished",
|
|
79
|
+
"body" => "Your content is live."
|
|
80
|
+
})
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Core API
|
|
84
|
+
|
|
85
|
+
Once you create an admin client, these are the main surfaces you will use:
|
|
86
|
+
|
|
87
|
+
- `admin.db(namespace = "shared", instance_id = nil)`
|
|
88
|
+
Server-side database access
|
|
89
|
+
- `admin.admin_auth`
|
|
90
|
+
Admin user management
|
|
91
|
+
- `admin.auth`
|
|
92
|
+
Alias for `admin.admin_auth`
|
|
93
|
+
- `admin.sql(namespace = "shared", query, params = nil, instance_id: nil)`
|
|
94
|
+
Raw SQL execution
|
|
95
|
+
- `admin.storage`
|
|
96
|
+
Server-side storage access
|
|
97
|
+
- `admin.functions`
|
|
98
|
+
Call app functions from trusted code
|
|
99
|
+
- `admin.push`
|
|
100
|
+
Send push notifications
|
|
101
|
+
- `admin.analytics`
|
|
102
|
+
Query analytics and track server-side events
|
|
103
|
+
- `admin.kv(namespace)`, `admin.d1(database)`, `admin.vector(index)`
|
|
104
|
+
Access platform resources from trusted code
|
|
105
|
+
- `admin.broadcast(channel, event, payload = {})`
|
|
106
|
+
Server-side database-live broadcast
|
|
107
|
+
- `admin.destroy`
|
|
108
|
+
No-op cleanup hook
|
|
109
|
+
|
|
110
|
+
## Database Access
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
posts = admin.db("app").table("posts")
|
|
114
|
+
rows = posts.where("status", "==", "published").get
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
For instance databases, pass the instance id as the second argument:
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
admin.db("workspace", "ws-123")
|
|
121
|
+
admin.db("user", "user-123")
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Admin Users
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
created = admin.admin_auth.create_user(
|
|
128
|
+
email: "admin@example.com",
|
|
129
|
+
password: "secure-pass-123",
|
|
130
|
+
data: { "displayName" => "June" }
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
admin.admin_auth.set_custom_claims(created["id"], {
|
|
134
|
+
"role" => "moderator"
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
users = admin.admin_auth.list_users(limit: 20)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Raw SQL
|
|
141
|
+
|
|
142
|
+
```ruby
|
|
143
|
+
shared_rows = admin.sql(
|
|
144
|
+
"shared",
|
|
145
|
+
"SELECT 1 AS ok"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
workspace_rows = admin.sql(
|
|
149
|
+
"workspace",
|
|
150
|
+
"SELECT * FROM documents WHERE status = ?",
|
|
151
|
+
["published"],
|
|
152
|
+
instance_id: "ws-123"
|
|
153
|
+
)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Push And Analytics
|
|
157
|
+
|
|
158
|
+
```ruby
|
|
159
|
+
admin.push.send("user-123", {
|
|
160
|
+
"title" => "Hello",
|
|
161
|
+
"body" => "From the admin SDK"
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
overview = admin.analytics.overview("range" => "7d")
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Choose The Right Package
|
|
168
|
+
|
|
169
|
+
| Package | Use it for |
|
|
170
|
+
| --- | --- |
|
|
171
|
+
| `edgebase_admin` | Trusted server-side Ruby code with Service Key access |
|
|
172
|
+
| `edgebase_core` | Lower-level primitives for custom integrations |
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
MIT
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "edgebase_core"
|
|
4
|
+
|
|
5
|
+
module EdgebaseAdmin
|
|
6
|
+
# Admin auth — server-side user management via Service Key.
|
|
7
|
+
#
|
|
8
|
+
# user = admin.admin_auth.get_user("user-id")
|
|
9
|
+
# new_user = admin.admin_auth.create_user(email: "admin@example.com", password: "secure")
|
|
10
|
+
# admin.admin_auth.set_custom_claims("user-id", { "role" => "pro" })
|
|
11
|
+
# admin.admin_auth.revoke_all_sessions("user-id")
|
|
12
|
+
class AdminAuthClient
|
|
13
|
+
def initialize(client)
|
|
14
|
+
@client = client
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def get_user(user_id)
|
|
18
|
+
require_service_key!
|
|
19
|
+
unwrap_user(@client.get("/auth/admin/users/#{user_id}"))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def create_user(email = nil, password = nil, data: nil, **kwargs)
|
|
23
|
+
require_service_key!
|
|
24
|
+
body = normalize_create_user_payload(email, password, data, kwargs)
|
|
25
|
+
unwrap_user(@client.post("/auth/admin/users", body))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def update_user(user_id, data)
|
|
29
|
+
require_service_key!
|
|
30
|
+
unwrap_user(@client.patch("/auth/admin/users/#{user_id}", data))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def delete_user(user_id)
|
|
34
|
+
require_service_key!
|
|
35
|
+
@client.delete("/auth/admin/users/#{user_id}")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def list_users(limit: 20, cursor: nil)
|
|
39
|
+
require_service_key!
|
|
40
|
+
params = { "limit" => limit.to_s }
|
|
41
|
+
params["cursor"] = cursor if cursor
|
|
42
|
+
result = @client.get("/auth/admin/users", params: params)
|
|
43
|
+
result.is_a?(Hash) ? result : { "users" => [], "cursor" => nil }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def set_custom_claims(user_id, claims)
|
|
47
|
+
require_service_key!
|
|
48
|
+
unwrap_user(@client.put("/auth/admin/users/#{user_id}/claims", claims))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def revoke_all_sessions(user_id)
|
|
52
|
+
require_service_key!
|
|
53
|
+
@client.post("/auth/admin/users/#{user_id}/revoke")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def disable_mfa(user_id)
|
|
57
|
+
require_service_key!
|
|
58
|
+
@client.delete("/auth/admin/users/#{user_id}/mfa")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def normalize_create_user_payload(email, password, data, kwargs)
|
|
64
|
+
if email.is_a?(Hash) && password.nil? && data.nil? && kwargs.empty?
|
|
65
|
+
return stringify_hash(email)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
body = stringify_hash(kwargs)
|
|
69
|
+
body["email"] = email if email
|
|
70
|
+
body["password"] = password if password
|
|
71
|
+
body["data"] = data if data
|
|
72
|
+
body
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def stringify_hash(value)
|
|
76
|
+
value.each_with_object({}) do |(key, val), result|
|
|
77
|
+
result[key.to_s] = val
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def unwrap_user(value)
|
|
82
|
+
return value["user"] if value.is_a?(Hash) && value["user"].is_a?(Hash)
|
|
83
|
+
|
|
84
|
+
value
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def require_service_key!
|
|
88
|
+
sk = @client.instance_variable_get(:@service_key)
|
|
89
|
+
return if sk && !sk.empty?
|
|
90
|
+
|
|
91
|
+
raise EdgebaseCore::EdgeBaseError.new(
|
|
92
|
+
403,
|
|
93
|
+
"Service Key required for admin operations. " \
|
|
94
|
+
"Pass service_key: when constructing AdminClient."
|
|
95
|
+
)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "edgebase_core"
|
|
4
|
+
require_relative "admin_auth"
|
|
5
|
+
require_relative "kv_client"
|
|
6
|
+
require_relative "d1_client"
|
|
7
|
+
require_relative "vectorize_client"
|
|
8
|
+
require_relative "push_client"
|
|
9
|
+
require_relative "functions_client"
|
|
10
|
+
require_relative "analytics_client"
|
|
11
|
+
|
|
12
|
+
module EdgebaseAdmin
|
|
13
|
+
# DB namespace block reference for table access.
|
|
14
|
+
#
|
|
15
|
+
# Obtained via `admin.db("shared")`.
|
|
16
|
+
class DbRef
|
|
17
|
+
attr_reader :_namespace, :_instance_id
|
|
18
|
+
|
|
19
|
+
def initialize(core, namespace, instance_id = nil)
|
|
20
|
+
@core = core
|
|
21
|
+
@_namespace = namespace
|
|
22
|
+
@_instance_id = instance_id
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Get a TableRef for the named table.
|
|
26
|
+
def table(name)
|
|
27
|
+
EdgebaseCore::TableRef.new(
|
|
28
|
+
@core, name,
|
|
29
|
+
namespace: @_namespace,
|
|
30
|
+
instance_id: @_instance_id
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Unified admin client — db, storage, auth access via Service Key.
|
|
36
|
+
#
|
|
37
|
+
# admin = EdgebaseAdmin::AdminClient.new("http://localhost:8688", service_key: ENV.fetch("EDGEBASE_SERVICE_KEY"))
|
|
38
|
+
# table = admin.db("shared").table("posts")
|
|
39
|
+
# record = table.insert({ "title" => "Hello" })
|
|
40
|
+
#
|
|
41
|
+
# bucket = admin.storage.bucket("documents")
|
|
42
|
+
# bucket.upload("file.txt", "Hello", content_type: "text/plain")
|
|
43
|
+
class AdminClient
|
|
44
|
+
attr_reader :admin_auth
|
|
45
|
+
|
|
46
|
+
def initialize(base_url, service_key:)
|
|
47
|
+
@http = EdgebaseCore::HttpClient.new(base_url, service_key: service_key)
|
|
48
|
+
@core = EdgebaseCore::GeneratedDbApi.new(@http)
|
|
49
|
+
@admin_auth = AdminAuthClient.new(@http)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Get a DbRef for the given namespace.
|
|
53
|
+
def db(namespace = "shared", instance_id: nil)
|
|
54
|
+
DbRef.new(@core, namespace, instance_id)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Get the StorageClient for file operations.
|
|
58
|
+
def storage
|
|
59
|
+
EdgebaseCore::StorageClient.new(@http)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Get a KvClient for the named KV namespace.
|
|
63
|
+
def kv(namespace)
|
|
64
|
+
KvClient.new(@http, namespace)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Get a D1Client for the named D1 database.
|
|
68
|
+
def d1(database)
|
|
69
|
+
D1Client.new(@http, database)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Get a VectorizeClient for the named Vectorize index.
|
|
73
|
+
def vector(index)
|
|
74
|
+
VectorizeClient.new(@http, index)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Get a PushClient for push notification operations.
|
|
78
|
+
def push
|
|
79
|
+
PushClient.new(@http)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Get a FunctionsClient for calling app functions.
|
|
83
|
+
def functions
|
|
84
|
+
FunctionsClient.new(@http)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Get an AnalyticsClient for metrics and custom event tracking.
|
|
88
|
+
def analytics
|
|
89
|
+
AnalyticsClient.new(@core, EdgebaseAdmin::GeneratedAdminApi.new(@http))
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Execute raw SQL via DatabaseDO.
|
|
93
|
+
#
|
|
94
|
+
# rows = admin.sql("posts",
|
|
95
|
+
# "SELECT authorId, COUNT(*) as cnt FROM posts GROUP BY authorId ORDER BY cnt DESC LIMIT ?",
|
|
96
|
+
# [10])
|
|
97
|
+
def sql(namespace = "shared", query = nil, params = nil, instance_id: nil)
|
|
98
|
+
if !query.is_a?(String) || query.strip.empty?
|
|
99
|
+
raise ArgumentError, "Invalid sql() signature: query must be a non-empty string"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
body = {
|
|
103
|
+
"namespace" => namespace,
|
|
104
|
+
"sql" => query,
|
|
105
|
+
"params" => params || [],
|
|
106
|
+
}
|
|
107
|
+
body["id"] = instance_id if instance_id
|
|
108
|
+
admin_core = EdgebaseAdmin::GeneratedAdminApi.new(@http)
|
|
109
|
+
result = admin_core.execute_sql(body)
|
|
110
|
+
return result["rows"] if result.is_a?(Hash) && result["rows"].is_a?(Array)
|
|
111
|
+
return result["items"] if result.is_a?(Hash) && result["items"].is_a?(Array)
|
|
112
|
+
return result["results"] if result.is_a?(Hash) && result["results"].is_a?(Array)
|
|
113
|
+
|
|
114
|
+
result
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Send a broadcast message to a database-live channel.
|
|
118
|
+
#
|
|
119
|
+
# admin.broadcast("notifications", "alert", { "message" => "Maintenance in 5 min" })
|
|
120
|
+
def broadcast(channel, event, payload = {})
|
|
121
|
+
admin_core = EdgebaseAdmin::GeneratedAdminApi.new(@http)
|
|
122
|
+
admin_core.database_live_broadcast({
|
|
123
|
+
"channel" => channel,
|
|
124
|
+
"event" => event,
|
|
125
|
+
"payload" => payload
|
|
126
|
+
})
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Stateless HTTP client; nothing to tear down, but final-suite runners
|
|
130
|
+
# expect every admin SDK to expose a destroy/cleanup hook.
|
|
131
|
+
def destroy
|
|
132
|
+
nil
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EdgebaseAdmin
|
|
4
|
+
class AnalyticsClient
|
|
5
|
+
def initialize(core, admin_core)
|
|
6
|
+
@methods = EdgebaseCore::GeneratedAnalyticsMethods.new(core)
|
|
7
|
+
@admin_core = admin_core
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def overview(options = {})
|
|
11
|
+
result = @admin_core.query_analytics(query: build_query("overview", options))
|
|
12
|
+
result.is_a?(Hash) ? result : {}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def time_series(options = {})
|
|
16
|
+
result = @admin_core.query_analytics(query: build_query("timeSeries", options))
|
|
17
|
+
result.is_a?(Hash) ? Array(result["timeSeries"]) : []
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def breakdown(options = {})
|
|
21
|
+
result = @admin_core.query_analytics(query: build_query("breakdown", options))
|
|
22
|
+
result.is_a?(Hash) ? Array(result["breakdown"]) : []
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def top_endpoints(options = {})
|
|
26
|
+
result = @admin_core.query_analytics(query: build_query("topEndpoints", options))
|
|
27
|
+
result.is_a?(Hash) ? Array(result["topItems"]) : []
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def track(name, properties = {}, user_id: nil)
|
|
31
|
+
event = {
|
|
32
|
+
"name" => name,
|
|
33
|
+
"timestamp" => (Time.now.to_f * 1000).to_i
|
|
34
|
+
}
|
|
35
|
+
event["properties"] = properties unless properties.nil? || properties.empty?
|
|
36
|
+
event["userId"] = user_id if user_id
|
|
37
|
+
track_batch([event])
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def track_batch(events)
|
|
41
|
+
normalized = Array(events).map do |event|
|
|
42
|
+
payload = event.dup
|
|
43
|
+
payload["timestamp"] ||= (Time.now.to_f * 1000).to_i
|
|
44
|
+
payload
|
|
45
|
+
end
|
|
46
|
+
return if normalized.empty?
|
|
47
|
+
|
|
48
|
+
@methods.track({ "events" => normalized })
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def query_events(options = {})
|
|
52
|
+
@admin_core.query_custom_events(query: options)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def build_query(metric, options)
|
|
58
|
+
{ "metric" => metric }.merge(options || {})
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "edgebase_core"
|
|
4
|
+
|
|
5
|
+
module EdgebaseAdmin
|
|
6
|
+
# Client for a user-defined D1 database.
|
|
7
|
+
#
|
|
8
|
+
# rows = admin.d1("analytics").exec("SELECT * FROM events WHERE type = ?", ["pageview"])
|
|
9
|
+
class D1Client
|
|
10
|
+
def initialize(http_client, database)
|
|
11
|
+
@http = http_client
|
|
12
|
+
@admin_core = EdgebaseAdmin::GeneratedAdminApi.new(http_client)
|
|
13
|
+
@database = database
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Execute a SQL query. Use ? placeholders for bind parameters.
|
|
17
|
+
def exec(query, params = nil)
|
|
18
|
+
body = { "query" => query }
|
|
19
|
+
body["params"] = params if params
|
|
20
|
+
res = @admin_core.execute_d1_query(@database, body)
|
|
21
|
+
res["results"] || []
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
alias query exec
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EdgebaseAdmin
|
|
4
|
+
class FunctionsClient
|
|
5
|
+
def initialize(http_client)
|
|
6
|
+
@http = http_client
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def call(path, method: "POST", body: nil, query: nil)
|
|
10
|
+
normalized_path = "/functions/#{path.sub(%r{^/}, "")}"
|
|
11
|
+
|
|
12
|
+
case method.to_s.upcase
|
|
13
|
+
when "GET"
|
|
14
|
+
@http.get(normalized_path, params: query)
|
|
15
|
+
when "PUT"
|
|
16
|
+
@http.put(normalized_path, body)
|
|
17
|
+
when "PATCH"
|
|
18
|
+
@http.patch(normalized_path, body)
|
|
19
|
+
when "DELETE"
|
|
20
|
+
@http.delete(normalized_path)
|
|
21
|
+
else
|
|
22
|
+
@http.post(normalized_path, body)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def get(path, query: nil)
|
|
27
|
+
call(path, method: "GET", query: query)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def post(path, body = nil)
|
|
31
|
+
call(path, method: "POST", body: body)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def put(path, body = nil)
|
|
35
|
+
call(path, method: "PUT", body: body)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def patch(path, body = nil)
|
|
39
|
+
call(path, method: "PATCH", body: body)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def delete(path)
|
|
43
|
+
call(path, method: "DELETE")
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|