开发环境
Metabase 应用程序有两个基本组件
- 后端使用 Clojure 编写,其中包含 REST API 以及用于与数据库对话和处理查询的所有相关代码。
- 前端编写为 Javascript 单页应用程序,提供 Web UI。
这两个组件构建并组装成一个 JAR 文件。在运行 JAR 的目录中,您可以创建一个 JAR 文件(如果 Metabase 尚未创建),并在其中添加驱动程序(驱动程序也是 JAR)。
快速入门
要启动开发环境,请运行
yarn dev
这将同时运行前端和后端。或者,您可以分别在下面的两个终端会话中运行它们。
前端
Metabase 依赖于第三方库来运行,因此您需要保持这些库为最新。Clojure CLI 将在需要时自动获取依赖项。但是,对于 JavaScript 依赖项,您需要手动启动安装过程。
# javascript dependencies
$ yarn
使用以下命令启动前端构建过程
yarn build-hot
请参阅前端开发。
后端
使用以下命令运行后端开发服务器
clojure -M:run
请参阅后端开发。
前端开发
我们使用这些技术进行 FE 构建过程,以便我们能够使用模块、es6 语法和 css 变量。
- webpack
- babel
- cssnext
前端任务使用 yarn
执行。所有可用的任务都可以在 package.json
的 *scripts* 下找到。
要构建前端客户端而不监视更改,您可以使用
$ yarn build
如果您直接在前端工作,您很可能希望在保存时重新加载更改,并且在 React 组件的情况下,在保持状态的同时执行此操作。要启动热重载构建,请使用
$ yarn build-hot
请注意,此时如果您更改 CSS 变量,则只有在重新启动构建时才会拾取这些更改。
如果您喜欢,也可以选择在保存时重新加载更改,而无需热重载。
$ yarn build-watch
某些系统可能无法检测到前端文件的更改。您可以通过取消注释 webpack.config.js
中的 watchOptions
子句来启用文件系统轮询。如果您这样做,则可能值得使用 git update-index --assume-unchanged webpack.config.js
使 git 忽略对 webpack 配置的更改
默认情况下,我们在开发模式下排除 ESLint 加载器,以实现七倍更快的初始构建。您可以通过导出环境变量来启用它
$ USE_ESLINT=true yarn build-hot
默认情况下,这些构建过程依赖于内存缓存。启用 ESLint 加载器的构建过程使用大量内存,并且可能需要相当长的时间才能启动(1-2 分钟或更长时间)。鼓励 FE 开发人员(或任何其他经常重新启动 FE 构建的人员)使用 webpack 的文件系统缓存选项,以获得更好的启动性能
$ FS_CACHE=true yarn build-hot
当使用 FS_CACHE=true
时,您可能需要删除 node_modules/.cache
目录,以修复构建可能被不正确缓存的情况,并且当在代码库的开源版本和企业版本之间交替时,您必须运行 rm -rf node_modules/.cache
才能使构建正常工作。
前端测试
使用以下命令运行所有单元测试和 Cypress 端到端测试
yarn test
Cypress 测试和一些单元测试位于 frontend/test
目录中。新的单元测试文件添加到它们测试的文件旁边。
如果您正在使用 FS_CACHE=true
,您也可以将 FS_CACHE=true
与 yarn test
一起使用。
前端调试
默认情况下,我们使用针对速度优化的简单源映射选项。
如果您在断点处遇到问题,尤其是在 jsx 内部,请在运行服务器之前将环境变量 BETTER_SOURCE_MAPS
设置为 true。
示例
BETTER_SOURCE_MAPS=true yarn dev
Cypress 端到端测试
端到端测试模拟用户交互的真实序列。阅读更多关于我们如何使用 Cypress 进行端到端测试。
Cypress 端到端测试使用强制文件命名约定 <test-suite-name>.cy.spec.js
将它们与单元测试分开。
Jest 单元测试
单元测试侧重于业务逻辑的隔离部分。
单元测试使用强制文件命名约定 <test-suite-name>.unit.spec.js
将它们与端到端测试分开。
yarn test-unit # Run all tests at once
yarn test-unit-watch # Watch for file changes
后端开发
Clojure REPL 是后端的主要开发工具。下面介绍如何设置 REPL 以简化开发。
当然,您的 Jetty 开发服务器也可以通过以下方式获得
clojure -M:run
您也可以通过另一种方式(例如,通过您的编辑器)启动 REPL,然后调用
(do (dev) (start!))
启动服务器(位于 localhost:3000
)。这也将设置或迁移您的应用程序数据库。要实际使用 Metabase,请不要忘记也启动前端(例如使用 yarn build-hot
)。
应用程序数据库
默认情况下,Metabase 使用 H2 作为其应用程序数据库,但我们建议使用 Postgres。这通过几个属性配置,这些属性可以设置为环境变量或在 deps.edn
中设置。一种方法是
;; ~/.clojure/deps.edn
{:aliases
{:user
{:jvm-opts
["-Dmb.db.host=localhost"
"-Dmb.db.type=postgres"
"-Dmb.db.user=<username>"
"-Dmb.db.dbname=<dbname>"
"-Dmb.db.pass="]}}}
您也可以将完整的连接字符串作为 mb.db.connection.uri
传入
"-Dmb.db.connection.uri=postgres://<user>:<password>@localhost:5432/<dbname>"
除了使用环境变量外,还可以选择直接与配置库 environ 接口。
这种方法需要您的项目目录中创建一个 .lein-env
文件
{:mb-db-type "postgres"
:mb-db-host "localhost"
:mb-db-user "<username>"
:mb-db-dbname "<dbname>"
:mb-db-pass ""}
尽管名称如此,此文件仍可与 deps.edn
项目正常工作。与全局 deps.edn
方法相比,此方法的优势在于它仅限于此项目。
仅将其用于开发,生产用途不支持。 .gitignore
中已经有条目,以防止您意外提交此文件。
构建驱动程序
Metabase 用于连接到外部数据仓库数据库的大多数驱动程序都是 modules/
子目录下的单独项目。通过 clojure
运行 Metabase 时,您需要构建这些驱动程序才能访问它们。您可以按如下方式构建驱动程序
# Build the 'mongo' driver
./bin/build-driver.sh mongo
(或)
# Build all drivers
./bin/build-drivers.sh
包含驱动程序源路径以进行开发或其他任务
对于开发,当运行各种 Clojure 任务时,您可以添加 drivers
和 drivers-dev
别名,以将驱动程序的依赖项和源路径合并到 Metabase 项目中
# Install dependencies, including for drivers
clojure -P -X:dev:ci:drivers:drivers-dev
运行单元测试
使用以下命令运行单元测试
# OSS tests only
clojure -X:dev:test
# OSS + EE tests
clojure -X:dev:ee:ee-dev:test
或使用以下命令运行特定测试(或测试命名空间)
# run tests in only one namespace (pass in a symbol)
clojure -X:dev:test :only metabase.api.session-test
# run one specific test (pass in a qualified symbol)
clojure -X:dev:test :only metabase.api.session-test/my-test
# run tests in one specific folder (test/metabase/util in this example)
# pass arg in double-quotes so Clojure CLI interprets it as a string;
# our test runner treats strings as directories
clojure -X:dev:test :only '"test/metabase/util"'
与任何 clojure.test 项目一样,您也可以从 REPL 运行单元测试。以下是一些运行测试的有用方法示例
;; run a single test with clojure.test
some-ns=> (clojure.test/run-test metabase.util-test/add-period-test)
Testing metabase.util-test
Ran 1 tests containing 4 assertions.
0 failures, 0 errors.
{:test 1, :pass 4, :fail 0, :error 0, :type :summary}
;; run all tests in the namespace
some-ns=> (clojure.test/run-tests 'metabase.util-test)
Testing metabase.util-test
{:result true, :num-tests 100, :seed 1696600311261, :time-elapsed-ms 45, :test-var "pick-first-test"}
Ran 33 tests containing 195 assertions.
0 failures, 0 errors.
{:test 33, :pass 195, :fail 0, :error 0, :type :summary}
;; run tests for a set of namespaces related to a feature you are working on (eg pulses)
some-ns=> (let [namespaces '[metabase.pulse.markdown-test metabase.pulse.parameters-test]]
(apply require namespaces) ;; make sure the test namespaces are loaded
(apply clojure.test/run-tests namespaces))
Testing metabase.pulse.markdown-test
Testing metabase.pulse.parameters-test
Ran 5 tests containing 147 assertions.
0 failures, 0 errors.
{:test 5, :pass 147, :fail 0, :error 0, :type :summary}
;; but we also have a lovely test runner with lots of cool options
some-ns=> (metabase.test-runner/find-and-run-tests-repl {:namespace-pattern ".*pulse.*"})
Running tests with options {:mode :repl, :namespace-pattern ".*pulse.*", :exclude-directories ["classes" "dev" "enterprise/backend/src" "local" "resources" "resources-ee" "src" "target" "test_config" "test_resources"], :test-warn-time 3000}
Excluding directory "dev/src"
Excluding directory "local/src"
Looking for test namespaces in directory test
Finding tests took 1.6 s.
Excluding directory "test_resources"
Excluding directory "enterprise/backend/src"
Looking for test namespaces in directory enterprise/backend/test
Excluding directory "src"
Excluding directory "resources"
Running 159 tests
...
;; you can even specify a directory if you're working on a subfeature like that
some-ns=> (metabase.test-runner/find-and-run-tests-repl {:only "test/metabase/pulse/"})
Running tests with options {:mode :repl, :namespace-pattern #"^metabase.*", :exclude-directories ["classes" "dev" "enterprise/backend/src" "local" "resources" "resources-ee" "src" "target" "test_config" "test_resources"], :test-warn-time 3000, :only "test/metabase/pulse/"}
Running tests in "test/metabase/pulse/"
Looking for test namespaces in directory test/metabase/pulse
Finding tests took 37.0 ms.
Running 65 tests
...
测试驱动程序
默认情况下,测试仅针对 h2
驱动程序运行。您可以使用环境变量 DRIVERS
指定要针对哪些驱动程序运行测试
DRIVERS=h2,postgres,mysql,mongo clojure -X:dev:drivers:drivers-dev:test
某些驱动程序在测试时需要额外的环境变量,因为它们无法在本地运行(例如 Redshift 和 Bigquery)。测试将在启动时失败,并告知您如果需要提供哪些参数。
如果从 REPL 运行测试,您可以调用类似
(mt/set-test-drivers! #{:postgres :mysql :h2})
大多数驱动程序需要能够加载一些数据(一些使用静态数据集),并且所有驱动程序都需要能够连接到该数据库的实例。您可以在每个驱动程序的测试数据命名空间中找到所需的内容,该命名空间遵循模式 metabase.test.data.<driver>
。
应该有一个多方法 tx/dbdef->connection-details 的实现,它必须生成一种连接到数据库的方法。您可以看到需要什么。
这是 metabase.test.data.postgres
中 postgres 的一个
(defmethod tx/dbdef->connection-details :postgres
[_ context {:keys [database-name]}]
(merge
{:host (tx/db-test-env-var-or-throw :postgresql :host "localhost")
:port (tx/db-test-env-var-or-throw :postgresql :port 5432)
:timezone :America/Los_Angeles}
(when-let [user (tx/db-test-env-var :postgresql :user)]
{:user user})
(when-let [password (tx/db-test-env-var :postgresql :password)]
{:password password})
(when (= context :db)
{:db database-name})))
您可以看到这在环境中查找
- 主机(默认为“localhost”)
- 端口(默认为 5432)
- 用户
- 密码
函数名称指示它们是否抛出异常(尽管在此实例中,也会为那些会抛出异常的函数提供默认值)。
(tx/db-test-env-var :postgresql :password)
将在 env/env 映射中查找 :mb-postgresql-test-password
,它将由环境变量 MB_POSTGRESQL_TEST_PASSWORD
设置。
some-ns=> (take 10 (keys environ.core/env))
(:mb-redshift-test-password
:java-class-path
:path
:mb-athena-test-s3-staging-dir
:iterm-profile
:mb-snowflake-test-warehouse
:mb-bigquery-cloud-sdk-test-service-account-json
:tmpdir
:mb-oracle-test-service-name
:sun-management-compiler)
运行 linters
clj-kondo
必须单独安装。
# Run Eastwood
clojure -X:dev:ee:ee-dev:drivers:drivers-dev:eastwood
# Run the namespace checker
clojure -X:dev:ee:ee-dev:drivers:drivers-dev:test:namespace-checker
# Run clj-kondo
./bin/kondo.sh
# Lint the migrations file (if you've written a database migration):
./bin/lint-migrations-file.sh
持续集成
可以使用以下命令执行所有前端和后端 linters 和测试
$ yarn ci
也可以分别执行前端和后端检查
$ yarn ci-frontend
$ yarn ci-backend
阅读关于 其他版本的 Metabase 的文档。