# Среда разработки Приложение GlarusBI состоит из двух основных компонентов: 1. Бэкэнд, написанный на Clojure, который содержит REST API, а также весь соответствующий код для общения с базами данных и обработки запросов. 2. Внешний веб интерфейс, написанный как одностраничное приложение Javascript. Оба компонента создаются и собираются вместе в один файл JAR. В каталоге, где вы запускаете JAR, вы можете создать файл JAR (если GlarusBI еще не создала его) и добавить туда драйверы (драйверы также являются JAR). ## Быстрый старт Чтобы запустить среду разработки, запустите: ``` yarn dev ``` This runs both the [frontend](#frontend) and [backend](#backend). Alternatively, you can run them separately in two terminal sessions below. ### Фронтенд GlarusBI зависит от запуска сторонних библиотек, поэтому вам необходимо поддерживать их в актуальном состоянии. Clojure CLI автоматически извлечет зависимости, когда это необходимо. Однако с зависимостями JavaScript вам нужно будет запустить процесс установки вручную. ```sh # javascript dependencies $ yarn ``` Запустите процесс сборки фронтенда с помощью ``` yarn build-hot ``` См. [Разработка фронтенда](#frontend-development). ### Бэкенд Запустите свой локальный сервер с помощью ``` clojure -M:run ``` См. [бэкэнд-разработка](#backend-development). ## Фронтенд разработка Мы используем эти технологии для нашего процесса сборки FE, чтобы позволить нам использовать модули, синтаксис es6 и переменные css. - webpack - babel - cssnext Задачи внешнего интерфейса выполняются с использованием `yarn`. Все доступные задачи можно найти в `package.json` в разделе _scripts_. Чтобы запустить без горячей загрузки изменений, вы можете использовать: ```sh $ yarn build ``` Если вы работаете непосредственно с фронтендом, вы, скорее всего, захотите перезагрузить изменения при их внесении, а в случае с компонентами React сделать это, сохранив состояние. Чтобы запустить сборку с горячей перезагрузкой, используйте: ```sh $ yarn build-hot ``` Обратите внимание, что в настоящее время, если вы изменяете переменные CSS, эти изменения вступят в силу только при перезапуске сборки. Существует также возможность перезагрузить изменения при сохранении без горячей перезагрузки с помощью ```sh $ yarn build-watch ``` В некоторых системах могут возникнуть проблемы с обнаружением изменений в файлах фронтенда. Вы можете включить опрос файловой системы, раскомментировав предложение `watchOptions` в файле `webpack.config.js`. Если вы сделаете это, возможно, стоит заставить git игнорировать изменения в конфигурации веб-пакета, используя `git update-index --assume-unchanged webpack.config.js` Мы исключаем загрузчик ESLint в режиме разработки по умолчанию, чтобы в семь раз ускорить первоначальные сборки. Вы можете включить его, экспортировав переменную среды: ```sh $ USE_ESLINT=true yarn build-hot ``` По умолчанию эти процессы сборки полагаются на кеш памяти. Процесс сборки с включенным загрузчиком ESLint использует большой объем памяти и может занять значительное время для запуска (1-2 минуты и более). Разработчикам FE (или всем, кто часто перезапускает сборки FE) рекомендуется использовать параметр кеша файловой системы webpack для улучшения производительности при запуске: ```sh $ 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](./e2e-tests.md). Сквозные тесты Cypress используют принудительное соглашение об именах файлов `.cy.spec.js`, чтобы отделить их от модульных тестов. ### Модульные тесты Jest Модульные тесты обрабатывают изолированные части бизнес-логики. Модульные тесты используют принудительное соглашение об именах файлов `.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=" "-Dmb.db.dbname=" "-Dmb.db.pass="]}}} ``` You could also pass a full conection string in as the `mb.db.connection.uri`: ``` "-Dmb.db.connection.uri=postgres://:@localhost:5432/" ``` Помимо использования переменных окружения, есть опция прямого взаимодействия с библиотекой конфигурации [среда](https://github.com/weavejester/environ). Данный подход требует создания файла `.lein-env` внутри директории вашего проекта: ``` {:mb-db-type "postgres" :mb-db-host "localhost" :mb-db-user "" :mb-db-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: ```clojure ;; 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.`. 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`: ```clojure (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`. ```clojure 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` должен быть [установлен отдельно](https://github.com/clj-kondo/clj-kondo/blob/master/doc/install.md). ``` # 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 ``` ## Непрерывная интеграция Все внешние и внутренние линтеры и тесты могут быть выполнены с ```sh $ yarn ci ``` Также возможно выполнять проверки фронтенда и бэкенда отдельно. ```sh $ yarn ci-frontend $ yarn ci-backend ```