开发环境

Metabase应用程序有两个基本组件

  1. 一个使用Clojure编写的后端,其中包含REST API以及与数据库通信和查询处理的全部相关代码。
  2. 一个作为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来忽略webpack配置的更改。

默认情况下,我们通过排除dev模式下的ESLint加载器,使初始构建速度提高七倍。您可以通过导出环境变量来启用它

$ USE_ESLINT=true yarn build-hot

默认情况下,这些构建过程依赖于内存缓存。启用 ESLint 加载器的构建过程会消耗大量内存,并且可能需要较长时间才能启动(1-2分钟或更长时间)。前端开发者(或频繁重启前端构建的任何人)建议使用 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,您还可以使用 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任务进行开发时,您可以添加driversdrivers-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版本的文档。

想要改进这些文档吗? 提出更改。