使用MetabaseAPI
MetabaseAPI简介。
本文介绍如何使用Metabase的API。我们自己使用该API连接前端和后端,因此您可以编写Metabase几乎可以执行的所有操作。
警告:MetabaseAPI可能会更改
开始之前有两个注意事项:
- API可能会更改。API与前端紧密耦合,在不同版本之间可能会发生变化。端点可能不会有那么大的变化(现有的API端点很少被更改,也很少被删除),但是如果您编写代码来使用API,您可能需要在将来更新它。
- API没有版本。意思是:它可以在不同的版本之间进行更改,因此不要期望为了使用“稳定”的API而停留在Metabase的特定版本上。
MetabaseAPI入门
为了简单起见,我们将使用古老的命令行实用程序curl对于我们的API调用示例;您还可以考虑使用一个专用工具来开发API请求(比如Postman). 要跟上,你可以启动一个新的Metabase本地实例到处玩。
使用session token验证您的请求
你需要一个session token以验证您的请求,否则Metabase将拒绝与您交谈。若要获取session token,请向/api/session具有用户名和密码的终结点:
curl -X POST \
-H "Content-Type: application/json" \
-d '{"username": "person@metabase.com", "password": "fakepassword"}' \
http://localhost:3000/api/session
如果使用的是远程服务器,则需要替换localhost:3000你的服务器地址。此请求将返回一个JSON对象,该对象的键名为id
以及令牌作为密钥的值,例如:
{“id”
:“38f4939c-ad7f-4cbe-ae54-30946daf8593”}
您将需要在后续请求的标头中包括该session token,如下所示:
"X-Metabase-Session: 38f4939c-ad7f-4cbe-ae54-30946daf8593"
关于会话的一些注意事项:
- 默认情况下,会话有效期为14天。您可以通过设置环境变量来配置此会话持续时间MB_SESSION_AGE(值以分钟为单位)。
- 你应该缓存凭据重复使用它们直到它们过期,因为登录是安全的速率限制的。
- 无效和过期的session token返回401(未授权)状态代码。
- 优雅地处理401状态码。我们建议您编写代码以获取新的session token,并在API返回401 status code.
- 有些端点要求用户是管理员,也称为超级用户。需要管理员或超级用户状态(admin=superuser)的终结点通常在其文档中这样说。他们将返回一个403 (Forbidden) status code 如果当前用户不是管理员。
- 如果您想使用替代身份验证机制请随意投票功能请求.
获取请求示例
下面是一个示例API请求(注意session token),它将/api/user/current端点,它返回有关当前用户的信息:
curl -X GET \
-H "Content-Type: application/json" \
-H "X-Metabase-Session: 38f4939c-ad7f-4cbe-ae54-30946daf8593" \
http://localhost:3000/api/user/current
上面的请求返回一个JSON对象(格式化为可读性):
{
"email": "person@metabase.com",
"ldap_auth": false,
"first_name": "Human",
"locale": null,
"last_login": "2020-08-31T13:08:50.203",
"is_active": true,
"is_qbnewb": false,
"updated_at": "2020-08-31T13:08:50.203",
"group_ids": [
1,
2
],
"is_superuser": true,
"login_attributes": null,
"id": 1,
"last_name": "Person",
"date_joined": "2020-08-19T10:50:46.547",
"personal_collection_id": 1,
"common_name": "Human Person",
"google_auth": false
}
POST请求示例
您还可以使用一个文件来存储POST请求的JSON负载。这使得您可以很容易地对API发出一组预定义的请求。
curl -H @header_file.txt -d @payload.json http://localhost/api/card
下面是一个JSON文件的示例@payload.json在上面的命令中)创建一个问题:
{
"visualization_settings": {
"table.pivot_column": "QUANTITY",
"table.cell_column": "SUBTOTAL"
},
"description value": "A card generated by the API",
"collection_position": null,
"result_metadata": null,
"metadata_checksum": null,
"collection_id": null,
"name": "API-generated question",
"dataset_query": {
"database": 1,
"query": {
"source-table": 2
},
"type": "query"
},
"display": "table"
}
该请求生成了如图1所示的问题。
使用开发人员工具查看Metabase如何发出请求
如果自动生成的API文档不清楚,可以使用Firefox、Chrome和Edge等浏览器附带的开发工具来查看Metabase的请求和响应(图2)。
在Metabase应用程序中,执行要编写脚本的操作,例如添加用户或创建仪表板。然后使用浏览器中的开发人员工具查看执行该操作时向服务器发出的请求Metabase。
使用MetabaseAPI可以做的一些事情
设置Metabase实例
除了使用环境变量,您可以使用MetabaseAPI来设置Metabase的实例。一旦您使用首选方法,并且Metabase服务器已启动并正在运行,您可以通过发布到特定端点来创建第一个用户(作为管理员),/api/setup.这个/api/setup终结点:
- 将第一个用户创建为管理员(超级用户)。
- 让他们登录。
- 返回会话ID。
然后可以使用/api/setup端点,使用/api/emai端点,并使用/api/setup/admin_checklist验证安装进度的终结点。有关管理面板中检查表的图形表示,请参见图3。
添加数据源
您可以使用POST /api/database/,并使用/api/setup/validate终结点。将数据库连接到Metabase实例后,可以重新扫描数据库并更新架构元数据。你甚至可以加上我们的信任示例数据库作为实例的新数据库POST /api/database/sample_database.
下面是一个示例数据库创建调用红移数据库。
curl -s -X POST \
-H "Content-type: application/json" \
-H "X-Metabase-Session: ${MB_TOKEN}" \
http://localhost:3000/api/database \
-d '{
"engine": "redshift",
"name": "Redshift",
"details": {
"host": "redshift.aws.com",
"port": "5432",
"db": "dev",
"user": "root",
"password": "password"
}
}'
设置用户、组和权限
你可以使用/api/user要创建、更新和禁用用户的终结点,或/api/permissions要设置组或的终结点向其添加用户。以下是创建用户的curl命令示例:
curl -s "http://localhost:3000/api/user" \
-H 'Content-Type: application/json' \
-H "X-Metabase-Session: ${MB_TOKEN}" \
-d '{
"first_name":"Basic",
"last_name":"User",
"email":"basic@somewhere.com",
"password":"Sup3rS3cure_:}"
}'
生成报告
在Metabase中,“报告”被称为仪表板。您可以使用/api/dashboard终结点。你可以创建新仪表板具有POST /api/dashboard/,和将保存的问题添加到仪表板与[POST/api/dashboard/:id/cards].
有用的端点
下面“端点”列中的链接将带您转到该端点可用的第一个操作,该操作通常按字母顺序为“删除”操作。您可以在API文档中向下滚动查看该端点的操作和url的完整列表,并查看每个操作和url的描述。
域 | 说明 | 终结点 |
集合 | 集合是组织仪表盘、保存的问题和定时器的好方法。 | /api/collection |
仪表板 | 仪表板是由一组问题和文本卡组成的报告。 | /api/dashboard |
数据库 | 获取数据库、字段、模式、主(实体)键、表列表等。 | /api/database |
电子邮件 | 更新电子邮件设置并发送测试电子邮件。 | /api/email |
嵌入 | 使用签名的jwt获取嵌入式卡和仪表板上的信息。 | /api/embed |
指标 | 指标是节省的计算(如收入)。创建和更新指标,返回相关实体,恢复到以前的版本,等等。 | /api/metric |
权限 | Metabase使用组管理对数据库和集合的权限。创建权限组、向组添加和删除用户、检索所有权限组的图形,等等。 | /api/permissions |
搜索 | 在卡片(问题)、仪表板、集合和脉冲中搜索子字符串。 | /api/search |
分段 | 分段是过滤器的命名集(如“活动用户”)。创建和更新段,恢复到以前的版本,等等。 | /api/segment |
会话 | 使用令牌重置密码,使用Google Auth登录,发送密码重置电子邮件,等等。 | /api/sessions |
设置 | 创建/更新全局应用程序设置。 | /api/setting |
查询 | 使用API执行查询并以指定格式返回查询结果。 | /api/dataset |
问题 | 问题(API中称为卡片)是查询及其可视化结果。 | /api/card |
还有一些很酷的端点需要检查,比如api/database/:virtual-db/metadata,这是用来“愚弄”前端,以便它可以治疗保存的问题就好像它们是虚拟数据库中的表一样。这就是Metabase如何让您将保存的问题当作数据源使用。
文件包含API端点的完整列表以及每个端点的文档,所以仔细研究一下,看看您能找到哪些其他很酷的端点。
端点引用会使用Metabase的新版本定期更新。也可以通过运行以下命令生成引用:
java -jar metabase.jar api-documentation
运行自定义查询
用我们的查询编辑器保存在我们自定义的基于JSON的查询语言MBQL中。您可以查看MBQL的限定语法,以及一个(不完整)MBQL参考文件了解MBQL背后的一些设计理念。
为了熟悉MBQL,我们建议使用Metabase应用程序来构建GUI问题,然后使用浏览器的开发人员工具查看Metabase如何使用查询格式化请求正文。
三种语言示例
Curl是探索api的一个方便工具,但是如果您要将Metabase集成到一个大型数据生态系统中,您可能会使用其他工具。演示如何使用Python、R和Node访问API。js,让我们提出两个问题。第一个(如图4所示)按类别查找超过100美元的订单的平均税前价值。它是公开共享的-本教程解释如何做到这一点。第二个问题,如图5所示,统计数据库中的人数。它是不共享的:我们把它包括进来,以展示如何区分共享问题和非共享问题。
Python
我们的第一个示例是用Python编写的。像大多数数据科学项目一样,它使用请求用于发送HTTP请求和Pandas为了管理表格数据,我们从导入这些数据开始:
import requests
import pandas as pd
下一步是获取一个session token来验证将来的所有请求。(该示例使用我的凭据您可以用您的用户名和密码替换它们,但请确保别这样将这些值提交到版本控制存储库。)为了从结果中获取令牌,我们将后者转换为JSON并查找ID。我们将其存储在字典中,并使用正确的密钥以备将来使用:
response = requests.post('http://localhost:3000/api/session',
json={'username': 'greg@metabase.com',
'password': 'database1'})
session_id = response.json()['id']
headers = {'X-Metabase-Session': session_id}
我们现在可以询问Metabase哪些问题具有公共id,也就是说,哪些问题已经被共享,以便我们可以远程调用它们。正如下面的代码所示,当我们要求所有的卡片时,我们会得到一个包含所有问题信息的列表;只有那些有public_uuid字段可调用:
response = requests.get('http://localhost:3000/api/card',
headers=headers).json()
questions = [q for q in response if q['public_uuid']]
print(f'{len(questions)} public of {len(response)} questions')
果然,输出告诉我们有两个问题,但只有一个是公开的:
1 public of 2 questions
让我们获取一些关于公共问题的信息并打印标题:
uuid=questions[0]['public_uuid']
response=
requests.get(f'http://localhost:3000/api/public/card/{uuid}',
headers=headers)
print(f'First title: {response.json()["name"]}')
First title: Average value of orders over $100 grouped by category
最后,我们可以从列表中的第一个问题中提取数据。这个'
数据'
key在JSON响应中有很多信息;我们最感兴趣的是子关键字下的值'
行'
,它将结果表存储在通常的列表列表表单中。让我们将其转换为Pandas数据帧并打印它:
response
=
requests.get(f'http://localhost:3000/api/public/card/{uuid}/query',
headers=headers)
rows
=
response.json()['data']['rows']
data
=
pd.DataFrame(rows,
columns=['Category',
'Average'])
print('First data')
print(data)
First data
Category Average
0 Doohickey 114.679742
1 Gadget 123.530916
2 Gizmo 120.897286
3 Widget 122.078721
和潮人在一起
我们示例的R版本与Python版本具有相同的结构。像大多数数据科学家一样,我们使用tidyverse库族,让我们一起加载这些库httr对于管理HTTP请求,jsonlite用于解析JSON,以及glue对于字符串格式:
library(tidyverse)
library(httr)
library(jsonlite)
library(glue)
我们再次获得会话ID并将其保存以备将来使用:
data <- POST(
'http://localhost:3000/api/session',
body = list(username = 'greg@metabase.com', password = 'database1'),
encode = 'json'
) %>%
content(as = 'text', encoding = 'UTF-8') %>%
fromJSON()
session_id <- data$id
headers <- add_headers('X-Metabase-Session' = session_id)
然后我们得到所有问题的信息,并询问哪些问题是公开的:
data <- GET('http://localhost:3000/api/card', headers) %>%
content(as = 'text', encoding = 'UTF-8') %>%
fromJSON()
num_questions <- data %>%
nrow()
num_public <- data %>%
pull(public_uuid) %>%
discard(is.na) %>%
length()
glue('{num_public} public of {num_questions} questions')
1 public of 2 questions
显示第一张公共卡的标题会产生与Python相同的结果,这令人放心:
uuid <- data %>%
pull(public_uuid) %>%
discard(is.na) %>%
first()
data <- glue('http://localhost:3000/api/public/card/{uuid}') %>%
GET(headers) %>%
content(as = 'text', encoding = 'UTF-8') %>%
fromJSON()
glue('First title: {data$name}')
First title: Average value of orders over $100 grouped by category
当我们将卡转换为tibble时,与该卡相关联的数据也是相同的,尽管R的默认显示没有提供足够多的小数位数:
data <- glue('http://localhost:3000/api/public/card/{uuid}/query') %>%
GET(headers) %>%
content(as = 'text', encoding = 'UTF-8') %>%
fromJSON()
rows <- data$data$rows
colnames(rows) <- c('Category', 'Average')
rows <- rows %>% as_tibble()
rows$Average <- as.numeric(rows$Average)
glue('First data')
rows
First data
# A tibble: 4 x 2
Category Average
<chr> <dbl>
1 Doohickey 115.
2 Gadget 124.
3 Gizmo 121.
4 Widget 122.
Node.js
JavaScript是一种越来越流行的服务器端脚本语言,但与Python和R不同,JavaScript缺少一个用于数据表的单一主流库。对于我们喜欢的大型项目data-forge,但对于小例子,我们坚持Dataframe-js我们也使用 got对于HTTP请求而不是旧的request包,因为后者现在已被弃用。最后,因为我们发现async
/await
语法比承诺或回调更容易阅读,我们将所有代码放在async然后立即调用的函数:
const got = require("got");
const DataFrame = require("dataframe-js").DataFrame;
const main = async () => {
// ...program goes here...
};
main();
我们再次从自我认证开始:
// Get a session token to authenticate all future requests.
let response = await got.post("http://localhost:3000/api/session", {
json: { username: "greg@metabase.com", password: "database1" },
responseType: "json",
});
session_id = response.body.id;
headers = { "X-Metabase-Session": session_id };
然后,我们会询问完整的问题列表,并对其进行筛选以选择公共问题:
response
=
await
got.get("http://localhost:3000/api/card",
{
responseType:
"json",
headers:
headers,
});
// filter for public questions
questions
=
response.body.filter((q)
=>
q.public_uuid);
console.log(`${questions.length} public of ${response.body.length} questions`);
1 public of 2 questions
第一张公共卡仍然有我们以前见过的标题:
const
uuid
=
questions[0].public_uuid;
response
=
await
got.get(`http://localhost:3000/api/public/card/${uuid}`,
{
responseType:
"json",
headers:
headers,
});
console.log(`First title: ${response.body.name}`);
First title: Average value of orders over $100 grouped by category
当我们把它的数据拉下来时,我们得到的是相同的值,尽管数字的显示方式略有不同:
response
=
await
got.get(
`http://localhost:3000/api/public/card/${uuid}/query`,
{
responseType:
"json",
headers:
headers,
},
);
const
rows
=
response.body.data.rows;
const
df
=
new
DataFrame(rows,
["Category",
"Average"]);
df.show();
| Category | Average |
------------------------
| Doohickey | 114.67... |
| Gadget | 123.53... |
| Gizmo | 120.89... |
| Widget | 122.07... |
玩得高兴
如果您觉得本教程很有趣,您可以启动Metabase的本地实例,尝试API,玩得开心!如果你被卡住了,看看我们的论坛看看是否有人遇到了类似的问题,或张贴新的问题。
Metabase中文社区