開發環境

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 忽略 webpack 組態的變更,方法是使用 git update-index --assume-unchanged webpack.config.js

依預設,我們在開發模式中排除 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,您也可以搭配 yarn test 使用 FS_CACHE=true

前端偵錯

依預設,我們使用針對速度最佳化的簡單來源對應選項。

如果您在斷點方面遇到問題,尤其是在 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> 模式。

應該要有 multimethod 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)

執行檢查器

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

持續整合

所有前端和後端檢查器與測試都可以使用下列指令執行:

$ yarn ci

也可以分別執行前端和後端檢查

$ yarn ci-frontend
$ yarn ci-backend

閱讀 Metabase 其他版本的文件