Среда разработки

Приложение GlarusBI состоит из двух основных компонентов:

  1. Бэкэнд, написанный на Clojure, который содержит REST API, а также весь соответствующий код для общения с базами данных и обработки запросов.

  2. Внешний веб интерфейс, написанный как одностраничное приложение Javascript.

Оба компонента создаются и собираются вместе в один файл JAR. В каталоге, где вы запускаете JAR, вы можете создать файл JAR (если GlarusBI еще не создала его) и добавить туда драйверы (драйверы также являются JAR).

Быстрый старт

Чтобы запустить среду разработки, запустите:

yarn dev

This runs both the frontend and backend. Alternatively, you can run them separately in two terminal sessions below.

Фронтенд

GlarusBI зависит от запуска сторонних библиотек, поэтому вам необходимо поддерживать их в актуальном состоянии. 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

В некоторых системах могут возникнуть проблемы с обнаружением изменений в файлах фронтенда. Вы можете включить опрос файловой системы, раскомментировав предложение watchOptions в файле webpack.config.js. Если вы сделаете это, возможно, стоит заставить git игнорировать изменения в конфигурации веб-пакета, используя 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, вы также можете использовать FS_CACHE=true с yarn test.

Отладка интерфейса

По умолчанию мы используем простой вариант сопоставления источников, оптимизированный для скорости.

Если у вас возникнут проблемы с точками останова, особенно внутри jsx, перед запуском сервера установите для переменной env 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

You can also start a REPL another way (e.g., through your editor) and then call:

(do (dev) (start!))

To start the server (at localhost:3000). This will also set up or migrate your application database. To actually use Metabase, don’t forget to start the frontend as well (e.g. with yarn build-hot).

The application database

By default, Metabase uses H2 for its application database, but we recommend using Postgres. This is configured with several properties that can be set as environment variables or in a deps.edn. One approach is:

;; ~/.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="]}}}    

You could also pass a full conection string in as the mb.db.connection.uri:

"-Dmb.db.connection.uri=postgres://<user>:<password>@localhost:5432/<dbname>"

Помимо использования переменных окружения, есть опция прямого взаимодействия с библиотекой конфигурации среда.

Данный подход требует создания файла .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 чтобы вы случайно не закоммитили этот файл.

Сборка драйверов

Большинство драйверов, которые GlarusBI использует для подключения к внешним базам данных хранилища, представляют собой отдельные проекты в рамках подкаталог modules/. При запуске GlarusBI через clojure вам нужно будет собрать эти драйверы, чтобы иметь доступ к ним. Вы можете собрать драйверы следующим образом:

# Build the 'mongo' driver
./bin/build-driver.sh mongo

(или)

# Build all drivers
./bin/build-drivers.sh

Включая исходные пути драйверов для разработки или других задач

Для разработки при выполнении различных задач Clojure вы можете добавить псевдонимы drivers и drivers-dev, чтобы объединить зависимости драйверов и исходные пути в проект GlarusBI:

# 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"'

As in any clojure.test project, you can also run unit tests from the REPL. Some examples of useful ways to run tests are:

;; 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). Тесты завершатся ошибкой при запуске и дадут вам знать, какие параметры указать, если это необходимо.

If running tests from the REPL, you can call something like:

(mt/set-test-drivers! #{:postgres :mysql :h2})

Most drivers need to be able to load some data (a few use static datasets) and all drivers need to be able to connect to an instance of that database. You can find out what is needed in each’s drivers test data namespace which follows that pattern metabase.test.data.<driver>.

There should be an implementation of a multimethod tx/dbdef->connection-details which must produce a way to connect to a database. You can see what is required.

Here’s the one for postgres in metabase.test.data.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})))

You can see that this looks in the environment for:

  • host (defaults to “localhost”)

  • port (defaults to 5432)

  • user

  • password

The function names indicate if they throw or not (although in this instance the ones that would throw are also supplied default values).

The (tx/db-test-env-var :postgresql :password) will look in the env/env map for :mb-postgres-test-password which will be set by the environmental variable 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