Перейти к содержанию

Конфигурационные файлы и переменные окружения

Файлы app.json

Различные настройки сервисов и приложений определяются в конфигурационных файлах app.json.

Note

Файл app.json объявлен в формате JSON. Он должен быть в кодировке UTF-8 (с опциональным BOM).

Файлы app.json используются для компонентов:

  • сервисов chronos, jinni, monitor, web, webbi;
  • консольной утилиты tadmin;
  • проектов с тестами NUnit (Tessa.Test.*);
  • desktop-приложений TessaClient, TessaAdmin, TessaAppManager, TessaHost;
  • сервисов и приложений .NET, разработанных в проектных решениях с использованием API TESSA.

Комментарии

В конфигурационных файлах app.json можно использовать комментарии - строки, начинающиеся с //, которые игнорируются при чтении файла:

{ "Settings": { // mail sending mode: SMTP, Exchange, Disabled // ExchangeVersion specify as 'Exchange2013', if you have Exchange 2013 or newer "NoticeMailer.Mode": "Disabled", "NoticeMailer.ExchangeOAuthToken": "" } }

Ключи верхнего уровня

На верхнем уровне JSON-объекта в файле могут располагаться следующие ключи:

  • ключи-директивы, начинающиеся с символа точки . - определяют специальные действия, выполняемые при загрузке конфигурационного файла. В каждом файле может быть не более одной директивы каждого типа на верхнем уровне, но директивы, позволяющие несколько объявлений или действий, задаются как массивы [ ... ]. После обработки действий, определённых в директиве, ключ с директивой удаляется из объекта конфигурации, т.е. он недоступен приложению;

  • Settings - ключи с произвольными настройками приложения;

    { "Settings": { "ServerCode": "platform", "LicenseFile": "@*.tlic" } }

  • ConnectionSettings - строки подключения. По дочернему ключу указывается имя строки подключения (default для строки по умолчанию). Значение - либо строка подключения MS SQL Server, либо массив, в котором первый элемент - строка подключения, второй элемент - имя провайдера базы данных из блока DataProviders (для PostgreSQL укажите "Npgsql");

    { "ConnectionStrings": { "default": [ "Host=localhost; Database=tessa; User ID=postgres; Password=Master1234; Pooling=true; MaxPoolSize=100; MaxAutoPrepare=50; AutoPrepareMinUsages=20", "Npgsql" ], "migration": "Server=.\\SQLEXPRESS; Database=tessa; Integrated Security=false; User ID=sa; Password=Master1234; Connect Timeout=200; Pooling=true; Max Pool Size=200; Trust Server Certificate=true" } }

  • DataProviders - провайдеры данных для указания в ConnectionSettings. Дочерний ключ - алиас провайдера, который можно передать в ConnectionSettings вторым элементом массива; значение - имя класса .NET для провайдера данных с указанием сборки через запятую.

    { "DataProviders": { "Npgsql": "Npgsql.NpgsqlFactory, Npgsql" } }

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

Tip

Используйте команду tadmin PrintJson app.json -i, чтобы вывести на консоль содержимое конфигурации в JSON-форме после подстановки всех символов и обработки всех директив, в т.ч. после включения содержимого других файлов директивой .include. Здесь app.json - полный или относительный путь до выводимого файла.

Директива .include

Директива .include (ключ .include на верхнем уровне конфигурационного файла) позволяет к уже обработанному содержимому включить содержимое из другого файла, т.е. добавить или объединить значения ключей.

  • Для указания единственного значения достаточно задать по ключу директивы строку, а для указания нескольких значений - использовать массив строк.
  • Если передан относительный путь, то он рассчитывается от папки с текущим конфигурационным файлом.
  • Можно указывать маскированный путь, где символ * определяет ноль или более любых символов, а символ ? - ровно один любой символ. При поиске файлов полные пути к ним (включая имена и названия папок) сортируются по алфавиту с учётом регистра.
  • Разделители путей / и \ приводятся в соответствии с текущей ОС. Так, на ОС Windows все символы / в пути заменяются на \, а на ОС Linux - \ заменяются на /. Следует учитывать, что в соответствии с синтаксисом JSON для указания символа \ его необходимо дублировать \\.
  • Если по указанному пути (маскированному или не маскированному) файл не найден, то он пропускается без генерации ошибок.
  • Если пути к файлам повторяются, то повторное включение файла не выполняется.
  • Файлы включаются в порядке указания.

Включить файлы, подходящие по маске app*.json в текущей папке:

{ ".include": "app*.json" }

Note

Если текущий файл называется app.json, или один из ранее обработанных файлов также подходил по маске app*.json, то его повторное включение не выполняется.

Другой пример:

{ ".include": [ "localization.json", "patch*.json", "app-*.json", "config/app-*.json" ] }

Здесь формируется следующий список файлов на включение:

  1. Файл с именем localization.json в папке с текущим файлом.
  2. Файлы, подходящие по маске patch*.json в папке с текущим файлом. Например, patch.json, patch-crm.json и др.
  3. Файлы, подходящие по маске app-*.json в папке с текущим файлом. Например, app-db.json, app-ext.json и др.
  4. Файлы в подпапке config, подходящие по маске app-*.json. Например, config/app-web.json, config/app-dev.json и др.

Процесс обработки каждого конфигурационного файла выполняется следующим образом:

  1. Обрабатываются все директивы в файле - известные ключи верхнего уровня, начинающиеся с ..
    • В процессе обработки директивы .include формируется список файлов на включение, которые в этом порядке добавляются в конец очереди на включение.
  2. Ранее полученное (при обработке предыдущих файлов) JSON-содержимое объединяется с содержимым текущего файла.
    • Для объектов { ... } объединяются все значения по текущим и новым ключам.
    • При совпадении по ключу атомарного значения (строки, числа или логического значения) - значение из включаемого файла заменяет значение исходного файла.
    • При совпадении по ключу значений-массивов [ ... ], значения из включаемого файла добавляются в конец массива из ранее полученного объекта.
    • При совпадении по ключу значений-объектов они объединяются по таким же правилам.
  3. Из очереди файлов на включение удаляется первый файл, и далее для него повторяется процесс с п.1, в т.ч. с добавлением новых включаемых файлов в конец очереди.
    • Если же очередь пуста, то обработка завершается.

Для первого обрабатываемого файла (с именем app.json в папке приложения, или по пути, указанному в переменной окружения TESSA_APP_JSON) ранее полученное JSON-содержимое пустое { } (т.к. предыдущего файла не было), и очередь на включение также пустая. После обработки этого файла наполняется JSON-содержимое, которое передаётся в следующий по очереди файл на включение (если он есть). И так для всех файлов.

Например, есть файл app.json:

{ ".include": "app2.json", "Settings": { "ServerCode": "platform", "Numbers": [ 1, 2 ], "WebServer": { "HttpsRedirect": "Disabled", "Http2Disabled": true } } }

В той же папке расположен файл app2.json:

{ "Settings": { "ServerCode": "tessa", "WinAuthIsEnabled": false, "Numbers": [ 3 ], "WebServer": { "Http2Disabled": false } } }

После обработки файла app.json выполняется включение файла app2.json. В результате объединённый объект выглядит так:

{ ".include": "app*.json", "Settings": { "ServerCode": "tessa", "WinAuthIsEnabled": false, "Numbers": [ 1, 2, 3 ], "WebServer": { "HttpsRedirect": "Disabled", "Http2Disabled": false } } }

В процессе объединения были произведены действия:

  1. Значение по совпадающему ключу Settings является объектом { ... }, поэтому оно объединяется.
    1. Значение по совпадающему ключу ServerCode изменено на "tessa".
    2. Добавлено значение по ключу WinAuthIsEnabled, которое отсутствовало в ранее обработанном файле.
    3. Значение по совпадающему ключу Numbers является массивом [ ... ], поэтому число 3 добавлено в конец массива, т.е. после чисел 1, 2.
    4. Значение по совпадающему ключу WebServer является объектом { ... }, поэтому оно объединяется.
      1. Значение по совпадающему ключу Http2Disabled изменено на false.

Файлы app.json и app2.json оба подходят под маскированный путь app*.json, но они уже были обработаны, поэтому их включение повторно не выполняется (и не приводит к зацикливанию алгоритма).

Переопределение ключей при включении

Для того, чтобы по совпадающим ключам значения-массивы [ ... ] и значения-объекты { ... } переопределялись (полностью заменялись) из включаемого директивой .include файла, допишите к имени ключа два восклицательных знака key!!.

  • В результирующем объекте будет ключ key без восклицательных знаков.
  • Наличие или отсутствие в ранее полученном объекте (перед объединением) ключа key не играет роли. В результате всё так же будет ключ key с содержимым из включаемого файла.
  • Если значение по ключу key!! является атомарным (не массивом или объектом, а строкой, числом или логическим значением), то он также заменяет содержимое по ключу key, как если бы восклицательные знаки !! отсутствовали в его имени.

Пусть в папке расположен файл app3.json, который подходит под маскированный путь app*.json, поэтому будет включён после обработки файла app2.json.

{ "Settings": { "Numbers!!": [ 4, 5, 6 ], "WebServer!!": { "HttpsRedirect": "Enabled" } } }

В результате объединённый объект выглядит так:

{ "Settings": { "ServerCode": "tessa", "WinAuthIsEnabled": false, "Numbers": [ 4, 5, 6 ], "WebServer": { "HttpsRedirect": "Enabled" } } }

В процессе объединения были произведены действия:

  1. Значение по совпадающему ключу Settings является объектом { ... }, поэтому оно объединяется.
    1. Значение по совпадающему ключу Numbers!! является массивом [ ... ], но ключ завершается на !!, поэтому его содержимое 4, 5, 6 заменяет полученный ранее массив 1, 2, 3.
    2. Значение по совпадающему ключу WebServer!! является объектом { ... }, поэтому его значение заменяет все ранее указанные ключи. Http2Disabled отсутствует в переопределяемом объекте, поэтому он удалён.

Tip

Для переопределения строк подключения к базе данных используйте переопределение имени строки, если она указана как массив (для PostgreSQL). Иначе, если ранее указанное значение также было массивом, то новое значение будет добавлено в конец массива, как третий и четвёртый элементы, которые игнорируются при фактическом подключении к базе данных.

{ "ConnectionSettings": { "default!!": [ "Host=localhost; Database=tessa; User ID=postgres; Password=Master1234", "Npgsql" ] } }

Директива .define

Директива .define (ключ .define на верхнем уровне конфигурационного файла) позволяет определить один или несколько символов, которые могут использоваться в проверках .if или при вставке переменных %symbol% для строковых значений.

Символ имеет имя и значение (строка), а также может быть не определён. При ссылках на символы для переопределения ранее определённого символа, а также удаления или использования в других директивах, проверка имени выполняется без учёта регистра.

Значение по ключу может быть строкой для определения одного символа, или массивом для определения нескольких:

Определить символ symbol1 со значением "true":

{ ".define": "symbol1" }

Определить символы symbol1 и symbol2 со значениями "true":

{ ".define": [ "symbol1", "symbol2" ] }

Определить символы SERVER_CODE и MAGIC_NUMBER с указанными значениями через знак равенства =:

{ ".define": [ "SERVERCODE=tessa", "MAGIC_NUMBER=42" ] }

При указании значения символа как пустой строки ему назначается значение по умолчанию "true" (что аналогично указанию имени без знака равенства =):

{ ".define": "symbol1=" }

Возможно удалить символ, предварив его имя восклицательным знаком. Что полезно, если символ был определён в другом файле, из которого текущий файл включается по директиве .include:

{ ".define": "!symbol1" }

В системе определены стандартные символы, которые либо не определены, либо определены со значением "true", в зависимости от среды запуска. Ниже перечислены такие символы и условия, при которых они определены:

  • windows - приложение запущено на ОС Windows;
  • linux - приложение запущено на ОС Linux;
  • wine - приложение запущено на ОС Linux в окружении Wine, которое имитирует Windows;
  • x64 - приложение запущено в 64-битном процессе на 64-битной ОС;
  • x86 - приложение запущено в 32-битном процессе на 32-битной или 64-битной ОС.

Также система определяет для каждой переменной окружения символ с соответствующим именем и значением. Так, если определена переменная окружения ASPNETCORE_ENVIRONMENT со значением DevelopmentHot, то это аналогично объявлению символа:

{ ".define": "ASPNETCORE_ENVIRONMENT=DevelopmentHot" }

Note

Если в конфигуционном файле объявлен символ с тем же именем, что и переменная окружения (без учёта регистра), то при его обработке используется значение объявленного символа, а не значение переменной.

Символы для переменных окружения можно удалить, как и любые другие символы:

{ ".define": [ "!ASPNETCORE_ENVIRONMENT", "!CID_FILE_PATH", "SERVERCODE=tessa" ] }

Директива .if

Директива .if (ключ .if на верхнем уровне конфигурационного файла) позволяет включить при выполнении или невыполнении условия объединить содержимое указанного объекта с содержимым файла верхнего уровня по тем же правилам, что и включение других файлов директивой .include.

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

  1. Одна или несколько строк или массивов с условиями, объединяемыми по “ИЛИ” (т.н. блок “condition”).
  2. Объект { ... }, который объединяется с содержимым конфигурационного файла при выполнении условия (т.н. блок “then”).
  3. Опционально объект { ... }, который объединяется с содержимым конфигурационного файла при невыполнении условия (т.н. блок “else”).
  4. Далее алгоритм переходит к п.1, где перечисляются одна или несколько строк или массивов с условиями для следующей проверки, и т.д. до завершения массива .if.

Условие в массиве if может быть строкой или массивом строк. Проверки внутри массива объединяются по “И”.

Строка условия (или одна из строк в массиве условия) может быть:

  1. именем символа Symbol - проверяет, что символ с указанным именем Symbol объявлен (в соответствии с директивой .define, переменными окружения и объявленными по умолчанию символами, такими как linux);
  2. именем символа с предваряющим восклицательным знаком !Symbol - проверяет, что символ с указанным именем Symbol не объявлен;
  3. именем символа со значением через знак равенства Symbol=value - проверяет, что символ с указанным именем Symbol объявлен, и его значение равно строке value без учёта регистра;
  4. именем символа с предваряющим восклицательным знаком со значением через знак равенства !Symbol=value - проверяет, что или символ с указанным именем Symbol не объявлен, или его значение не равно строке value без учёта регистра.

Таким образом, в блоке “condition” возможно определять логические выражение в форме СДНФ (совершенная дизъюнктивная нормальная форма).


Рассмотрим конфигурационный файл:

{ ".if": [ [ "A", "B" ], [ "C", "D" ], { "Settings": { "Result": "then" } }, { "Settings": { "Result": "else" } } ] }

При его обработки проверяется, что если либо объявлены символы A и B, либо объявлены символы C и D, то в блоке "Settings" конфигурационного файла определяется ключ "Result" со значением "then. После обработки директивы .if объект конфигурации выглядит так:

{ "Settings": { "Result": "then" } }

Иначе, если условие не выполняется, то производится аналогичное определение ключа со значением "else". В результате объект конфигурации выглядит так:

{ "Settings": { "Result": "else" } }

Если четвёртый элемент массива с блоком “else” отсутствовал бы, то при невыполнении условия содержимое конфигурационного файла бы не изменялось (в этом примере оно осталось бы пустым).

Также блок “then” можно указать как пустой объект { }, тогда при выполнении условия не будет изменений, а при невыполнении - выполнится объединение с блоком “else”.


Рассмотрим пример файла app.json, где если символ windows определён, то значение по ключу "WinAuthIsEnabled" меняется на true.

{ ".if": [ "windows", { "Settings": { "WinAuthIsEnabled": true } } ], "Settings": { "ServerCode": "tessa", "WinAuthIsEnabled": false } }

Если символ windows объявлен (этот символ автоматически объявляется при выполнении на ОС Windows), то содержимое блока “then” объединяется с тем содержимым, что указано вне директивы .if. Значение по совпадающему ключу "WinAuthIsEnabled" заменяется, а другой ключ ServerCode остаётся без изменений. В результате обработки будет следующий объект конфигурации:

{ "Settings": { "ServerCode": "tessa", "WinAuthIsEnabled": true } }

Если символ windows не объявлен (например, при выполнении на ОС Linux), то объект конфигурации не изменяется:

{ "Settings": { "ServerCode": "tessa", "WinAuthIsEnabled": false } }


Рассмотрим пример с проверкой значения символа ASPNETCORE_ENVIRONMENT (напоминаем, что символ может быть объявлен как переменная окружения):

{ ".if": [ "ASPNETCORE_ENVIRONMENT=Development", "ASPNETCORE_ENVIRONMENT=DevelopmentHot", "ASPNETCORE_ENVIRONMENT=SdkHot", { "Settings": { "GuyFawkesAuth": "", "WinAuthIsEnabled": false, "SessionsSyncIsLocal": true, "Discovery.MonitorIntegrationEnabled": false,

"WebServer": { "HttpsRedirect": "Disabled", "Http2Disabled": true } } } ] }

Если символ с именем ASPNETCORE_ENVIRONMENT (без учёта регистра) объявлен, и его значение равно (без учёта регистра) либо строке Development, либо DevelopmentHot, либо SdkHot, то в результирующем объекте конфигурации будут настройки из блока “then”:

{ "Settings": { "GuyFawkesAuth": "", "WinAuthIsEnabled": false, "SessionsSyncIsLocal": true, "Discovery.MonitorIntegrationEnabled": false,

"WebServer": { "HttpsRedirect": "Disabled", "Http2Disabled": true } } }

Если же символ не объявлен или его значение не совпадает ни с одним из перечисленных, то результирующий объект конфигурации пуст (т.к. блок “else” отсутствует, и снаружи директивы .if нет других объявлений):

{ }


Рассмотрим пример с несколькими условиями в файле app.json:

{ "ConnectionStrings": { "default": [ "Host=prod.server.com; Database=tessa_prod; User ID=tessa_user; Password=jfdH321", "Npgsql" ], "migration": "Server=.\\SQLEXPRESS; Database=tessa; Integrated Security=true" },

".if": [ "LOCAL_DATABASE", { "ConnectionStrings": { "default!!": [ "Host=localhost; Database=tessa; User ID=postgres; Password=Master1234", "Npgsql" ] } }, "linux", { "Settings": { "Webbi": { "MaintenanceConfig": { "Mode": "nginx" } } } }, { "Settings": { "Webbi": { "MaintenanceConfig": { "Mode": "iis" } } } } ],

"Settings": { "Webbi": { "Port": 19857 } } }

Note

Как и для любых файлов формата JSON, порядок объявления ключей внутри объектов не играет роли. Поэтому директива .if может быть расположена между других ключей верхнего уровня. Местоположение директивы не влияет на её выполнение, пока она объявлена на верхнем уровне.

В файле определено следующее:

  1. Ключ верхнего уровня ConnectionStrings объявляет строку подключения к базе данных.
  2. Ключ верхнего уровня Settings включает группу настроек Webbi, в которой указана настройка Port.
  3. Директива .if содержит два условия:
    1. Если объявлен символ LOCAL_DATABASE (например, как переменная окружения LOCAL_DATABASE со значением 1 или любым непустым значением), то строка подключения "default" будет изменена. Поскольку для подключения к PostgreSQL строка указывается как массив, то чтобы происходило не объединение с массивом из ключа верхнего уровня ConnectionSettings, а его замена, то ключ для массива указан с двумя восклицательными знаками "default!!". Строка "migration" отсутствует в блоке “then”, и весь блок со строками не переопределяется двумя восклицательными знаками ConnectionSettings!!, поэтому она остаётся без изменений.
    2. Если объявлен символ linux (обычно, если выполнение производится на ОС Linux), то в блок Webbi (внутри блока верхнего уровня Settings) добавляется вложенный объект MaintenanceConfig с ключом Mode, равным nginx. Если символ linux не объявлен (например, выполнение производится на ОС Windows), то добавляется аналогичный объект с ключом Mode, равным iis.

Рассмотрим содержимое объекта конфигурации в ситуации, когда объявлен символ LOCAL_DATABASE и объявлен символ linux.

{ "ConnectionStrings": { "default": [ "Host=localhost; Database=tessa; User ID=postgres; Password=Master1234", "Npgsql" ], "migration": "Server=.\\SQLEXPRESS; Database=tessa; Integrated Security=true" },

"Settings": { "Webbi": { "MaintenanceConfig": { "Mode": "nginx" }, "Port": 19857 } } }

Покажем содержимое объекта конфигурации, когда не объявлен символ LOCAL_DATABASE и не объявлен символ linux:

{ "ConnectionStrings": { "default": [ "Host=prod.server.com; Database=tessa_prod; User ID=tessa_user; Password=jfdH321", "Npgsql" ], "migration": "Server=.\\SQLEXPRESS; Database=tessa; Integrated Security=true" },

"Settings": { "Webbi": { "MaintenanceConfig": { "Mode": "iis" }, "Port": 19857 } } }

Сочетание директив в файле

Для директивы .if в блоках “then” и “else” возможно указание любых вложенных директив .if, .define, .include (без ограничения вложенности .if друг в друга).

Алгоритм обработки единственного конфигурационного файла с учётом всех директив следующий:

  1. Обрабатывается директива .include, при этом добавляются указанные в ней конфигурационные файлы в очередь на включение. Фактическое их включение в результирующий объект конфигурации производится только после полной обработки текущего файла. После обработки ключ с именем директивы удаляется.
  2. Обрабатывается директива .define, при этом добавляются, изменяются или удаляются значения для указанных символов. После обработки ключ с именем директивы удаляется.
  3. Обрабатывается директива .if, причём перед обработкой ключ с именем директивы удаляется. Выполнение производится по описанному выше алгоритму, где сначала для первого условия в массиве блок “then” или “else” объединяется с текущий объектом конфигурации (начиная ключей верхнего уровня), далее для второго условия и т.д., пока все условия не обработаны.
  4. Если в результате обработки директивы .if на верхнем уровне объекта конфигурации содержится хотя бы одна директива, то алгоритм переходит к п.1, и заново обрабатывает оставшиеся директивы (пополняя очередь файлов на включение, изменяя символы и/или обрабатывая условия).

В результате обработки файла определяется объект конфигурации, с которым объединяются файлы из очереди на включение, сформированной директивами .include, где каждый из файлов перед включением обрабатывается по алгоритму выше (т.е. он может содержать свои директивы).

Рассмотрим пример файла app.json для демонстрации работы алгоритма:

{ ".if": [ "EMULATE_WINE", { ".include": "app-wine.json", ".define": [ "!linux", "windows", "wine" ], "Settings": { "PlatformDependencies": "Tessa.Platform.WineTessaPlatformDependencies, Tessa", "DefaultUILanguage": "ru" }, ".if": [ "RENDERING_PROBLEMS", { "Settings": { "FadeAllowed": false, "MaxPreviewInstances": 1, "SoftwareRendering": true } }, "TESSA_LANGUAGE=English", { "Settings": { "DefaultUILanguage": "en" } } ] } ] }

Содержимое объекта конфигурации после обработки файла определяется следующим образом:

  1. Если объявлен символ EMULATE_WINE, то:
    1. Содержимое Settings из блока .if переносится на верхний уровень объекта конфигурации.
    2. Добавляется файл с именем app-wine.json в очередь на включение;
    3. Удаляется определение символа с именем linux, и добавляется объявление символов windows и wine со значением true.
    4. Если объявлен символ RENDERING_PROBLEMS, то включаются указанные в блоке “then” настройки FadeAllowed и др.
    5. Если символ TESSA_LANGUAGE объявлен со значением English (без учёта регистра), то добавляется настройка DefaultUILanguage со значением en.
    6. Иначе, если символ TESSA_LANGUAGE не объявлен или объявлен с отличающимся от English значением, то настройка DefaultUILanguage не изменяется, а значит имеет значение ru.
  2. Иначе, если символ EMULATE_WINE не объявлен, то никаких действий не производится, объект конфигурации будет пустым, очередь файлов на включение также не дополняется.

Пусть объявлен символ linux (т.к. выполнение производится на ОС Linux), а также символы EMULATE_WINE, RENDERING_PROBLEMS, и символ TESSA_LANGUAGE равен строке "english". Тогда объект конфигурации после обработки выглядит так:

{ "Settings": { "PlatformDependencies": "Tessa.Platform.WineTessaPlatformDependencies, Tessa", "DefaultUILanguage": "en", "FadeAllowed": false, "MaxPreviewInstances": 1, "SoftwareRendering": true } }

Также после обработки объявленными считаются символы windows, wine, EMULATE_WINE, RENDERING_PROBLEMS, TESSA_LANGUAGE.

Далее будет включено содержимое файла app-wine.json по аналогичному алгоритму (если файл существует). В этом файле могут быть проверки, связанные с символами, объявление которых изменено в текущем файле.

Подстановка символов в строку

Символы, как объявленные в конфигурационном файле, так и полученные из переменных окружения, могут подставляться в строковые значения JSON на любых уровнях вложенности (для значений-объектов { ... } и массивов [ ... ]), используя синтаксис %ИмяСимвола%.

{ "ConnectionStrings": { "default": [ "Host=localhost; Database=%DATABASE_NAME%; User ID=postgres; Password=%DATABASE_PASSWORD%", "Npgsql" ] }, "Settings": { "ServerCode": "%TESSA_SERVER_CODE%" } }

Здесь в строку подключения default подставляется символ DATABASE_NAME как имя базы данных, символ DATABASE_PASSWORD как пароль, и TESSA_SERVER_CODE как код сервера.

Если подставляемый символ не объявлен (или он был удалён объявлением "!ИмяСимвола"), то на эту позицию подставляется пустая строка.

Для указания символа процента % (т.е. для эскейпинга) его необходимо задвоить:

{ "Settings": { "RecognitionAccuracy": "50%%" } }

Здесь значением настройки RecognitionAccuracy будет строка "50%".

Подстановка текущей папки в строку

Для того, чтобы подставить полный путь до папки с текущим обрабатываемым конфигурационным файлом в начало любого строкового ключа, начните эту строку с символа @:

{ "Settings": { "LicenseFile": "@*.tlic", "WebServer": { "CertificateFile": "@server_name.cer", } } }

Если обработка выполняется для ОС Linux и текущий конфигурационный файл расположен по пути /home/tessa/configs, то в результате его обработки приложение получит следующие значения ключей:

{ "Settings": { "LicenseFile": "/home/tessa/configs/*.tlic", "WebServer": { "CertificateFile": "/home/tessa/configs/server_name.cer", } } }

Если обработка выполняется для ОС Windows и текущий конфигурационный файл расположен по пути C:\Tessa\Configs, то в результате будет:

{ "Settings": { "LicenseFile": "C:\\Tessa\\Configs\\*.tlic", "WebServer": { "CertificateFile": "C:\\Tessa\\Configs\\server_name.cer", } } }

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

Note

Символ \ дублирован в соответствии с синтаксисом JSON-файлов. Действительное значение, которое используется приложение, будет содержать символы \ без дублирования.

При необходимости указать строку, действительно начинающуюся с символа @, его необходимо дублировать. Например:

{ "Settings": { "SecretPassword": "@@K*&123" } }

Значение по ключу SecretPassword будет прочитано как @K*&123.

Если строка равна одиночному символу "@", то его можно как дублировать, так и оставить без изменений - он не будет заменён.

При подстановке символа (в т.ч. из переменной окружения) через синтаксис %symbol% сначала выполняется его подстановка, а потом применение синтаксиса @.

{ "Settings": { "LicenseFile": "@%LICENSE_FILE%" } }

Если символ LICENSE_FILE равен ../Partner.tlic, то сначала выполняется замена @../Partner.tlic, а потом, поскольку значение начинается с @, то вставляется путь до папки с текущим конфигурационным файлом вида /home/tessa/configs/../Partner.tlic, что в свою очередь соответствует пути /home/tessa/Partner.tlic.

Important

Если строка начинается с @ и далее уже содержит полный путь к файлу или папке (в соответствии с синтаксисом на ОС Linux или ОС Windows), то начальный символ удаляется без замены на путь к папке с конфигурационным файлом.

Например, символ LICENSE_FILE равен /var/license/*.tlic, и обработка выполняется на ОС Linux, где это - полный путь (который может быть или не быть маскированным путём). Тогда значение "@%LICENSE_FILE%" будет заменено на /var/license/*.tlic.

Примеры

Пусть в app.json указан ключ ServerCode со включением символа:

{ ".include": "app-*.json", "Settings": { "ServerCode": "%TESSA_SERVER_CODE%" } }

Вместо указания этого значения через переменную среды, рядом возможно создать такой файл app-vars.json (имя файла любое, которое будет включаться директивами .include):

{ ".define": [ "TESSA_SERVER_CODE=platform123" ] }


Значения по умолчанию можно добавить через директиву .if в файле app.json:

{ ".if": [ "!TESSA_SERVER_CODE", { "Settings": { "ServerCode": "platform" } } ], "Settings": { "ServerCode": "%TESSA_SERVER_CODE%" } }

Или пойти от обратного: только когда объявлена переменная - подставляем её, заменяя значение по умолчанию.

{ ".if": [ "TESSA_SERVER_CODE", { "Settings": { "ServerCode": "%TESSA_SERVER_CODE%" } } ], "Settings": { "ServerCode": "platform" } }


Другой пример: если объявлены обе переменные (или оба символа), то изменяем строку подключения с их учётом:

{ ".if": [ [ "DATABASE_NAME", "DATABASE_PASSWORD" ], { "ConnectionStrings": { "default!!": [ "Host=localhost; Database=%DATABASE_NAME%; User ID=postgres; Password=%DATABASE_PASSWORD%", "Npgsql" ] } } ], ".include": "app-*.json" }

Аналогично вместо переменных возможно использовать символы из отдельного файла app-vars.json:

{ ".define": [ "DATABASE_NAME=tessa", "DATABASE_PASSWORD=Master1234" ] }

Файлы extensions.xml

Поиск и загрузка файлов сборок .dll с регистраторами расширений .NET конфигурируется в файлах extensions.xml.

Note

Файл extensions.xml объявлен в формате XML. Он должен быть в кодировке UTF-8 (с опциональным BOM).

<?xml version="1.0" encoding="utf-8" ?> <extensions>

...

</extensions>

Типы регистраторов расширений, загрузка которых определяется этим конфигурационным файлом:

  • Стандартные расширения: регистраторы с атрибутом [Registrator].
  • Консольные команды: регистраторы с атрибутом [ConsoleRegistrator].
  • Консольные скрипты, вызываемые командой tadmin Script: регистраторы с атрибутом [ConsoleScript].
  • Регистраторы ASP.NET Core для веб-сервисов: регистраторы с атрибутом [WebRegistrator].

Тег reference

Загружает сборку .NET по указанному пути. Других действий со сборкой не выполняется.

<reference file="LibraryName.dll" />

  • file - путь до файла сборки .dll, который загружается. Относительные пути рассчитываются от расположения текущего файла extensions.xml.
  • clientOnly="true" - сборка загружается только для клиентских приложений (в т.ч. клиентские команды tadmin и клиентские тесты).
  • serverOnly="true" - сборка загружается только для серверных приложений (в т.ч. серверные команды tadmin, тесты, сервисы chronos, web).

Tip

Используйте, чтобы загрузить зависимость (стороннюю библиотеку), используемую в регистраторах расширений (в т.ч. опосредованно через статические конструкторы), перед тем, как будет загружена сборка с расширениями, но только если она расположена в папке, не добавленной в настройке ProbingPath.

Тег include

Загружает сборку .NET по указанному пути и выполняет сканирование в ней классов регистраторов для поиска расширений.

<include file="ExtensionsLibrary.dll" />

  • file - путь до файла сборки .dll, в которой выполняется сканирование классов регистраторов для поиска расширений. Относительные пути рассчитываются от расположения текущего файла extensions.xml.
  • clientOnly="true" - сборка загружается и сканируется только для клиентских приложений (в т.ч. клиентские команды tadmin и клиентские тесты).
  • serverOnly="true" - сборка загружается и сканируется только для серверных приложений (в т.ч. серверные команды tadmin, тесты, сервисы chronos, web).

Тег scan

Сканирует указанную папку (и опционально её подпапки) на наличие файлов extensions.xml, которые, аналогично текущему конфигурационному файлу, обрабатываются для загрузки сборок.

<scan path="folder/path" />

  • path - путь до папки, в которой выполняется поиск файлов extensions.xml для загрузки сборок. Если папка не существует или в ней отсутствует файл extensions.xml, то игнорируется. Относительные пути рассчитываются от расположения текущего файла extensions.xml.
  • subfolders="false" - сканировать только указанную в path папку. Если не указано (по умолчанию), то в дополнение к ней сканируются вложенные папки верхнего уровня в папку из path, и далее в каждой из таких подпапок, в которой есть файл extensions.xml, продолжается загрузка.

Important

Избегайте циклических зависимостей между папками, которые могут привести к зависанию приложения. Например, укажите <scan path=".." subfolders="false" />, чтобы сканировать папку уровнем выше, но не выполнять поиск extensions.xml в подпапках, где уже лежит текущий файл.

Примеры

<?xml version="1.0" encoding="utf-8" ?> <extensions>

<include file="Tessa.Extensions.PostgreSql.Server.dll" /> <scan path="platform" /> <scan path="extensions" />

</extensions>

  1. Загружаются расширения из файла Tessa.Extensions.PostgreSql.Server.dll в текущей папке.
  2. В подпапке platform, вложенной в папку с текущим файлом extensions.xml, выполняется поиск файла extensions.xml для загрузки расширений. Далее файл ищется в каждой из подпапок верхнего уровня, вложенных в platform.
  3. Аналогично сканируется подпапка extensions.

Пример для сборки с тестами, некоторые из которых могут быть серверными, а некоторые клиентскими (для клиентских тестов поднимается и клиентская, и серверная часть, поэтому выполняются все расширения, но для разных DI-контейнеров).

<?xml version="1.0" encoding="utf-8" ?> <extensions>

<include file="Tessa.Extensions.Default.Shared.dll" /> <include file="Tessa.Extensions.Shared.dll" />

<include file="Tessa.Extensions.Default.Client.dll" clientOnly="true" /> <include file="Tessa.Extensions.Client.dll" clientOnly="true" />

<include file="Tessa.Extensions.Default.Server.dll" serverOnly="true" /> <include file="Tessa.Extensions.Server.dll" serverOnly="true" />

<include file="Tessa.Extensions.Default.Server.Web.dll" serverOnly="true" /> <include file="Tessa.Extensions.Server.Web.dll" serverOnly="true" />

<include file="Tessa.Extensions.PostgreSql.Server.dll" serverOnly="true" />

</extensions>

Файлы plugins.xml

Файлы сборок .dll для загрузки плагинов Chronos определяются в конфигурационных файлах plugins.xml.

Note

Файл plugins.xml объявлен в формате XML. Он должен быть в кодировке UTF-8 (с опциональным BOM).

<?xml version="1.0" encoding="utf-8" ?> <plugins>

...

</plugins>

Тег include

<include file="PluginsLibrary.dll" />

  • file - путь до файла сборки .dll, в которой выполняется сканирование классов плагинов [Plugin]. Относительные пути рассчитываются от расположения текущего файла plugins.xml.

Примеры

<?xml version="1.0" encoding="utf-8" ?> <plugins> <include file="Tessa.Chronos.dll" /> <include file="Tessa.Chronos.DocumentsLoad.dll" /> <include file="Tessa.Extensions.Default.Chronos.dll" /> </plugins>

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

Файлы NLog.config

Настройки логирования определяются в конфигурационных файлах NLog.config.

Note

Файл NLog.config объявлен в формате XML. Он должен быть в кодировке UTF-8 (с опциональным BOM).

Файлы NLog.config используются для компонентов:

  • сервисов chronos, jinni, monitor, web;
  • консольной утилиты tadmin;
  • проектов с тестами NUnit (Tessa.Test.*);
  • desktop-приложений TessaAppLauncher, TessaClient, TessaAdmin, TessaAppManager, TessaHost;
  • сервисов и приложений .NET, разработанных в проектных решениях с использованием API TESSA.

Note

Логирование для сервиса webbi и приложения-ассистента web-клиента Deski настраивается отдельно. Обратитесь к соответствующим разделам по описанию этих компонентов.

Поиск файла NLog.config выполняется в папке приложения, или в папке с плагинами для плагинов Chronos (если в папке с плагинами нет файла, то логирование для плагина выполняется по правилам в папке сервиса chronos).

Также путь к файлу можно переопределить, используя переменную окружения TESSA_NLOG_CONFIG.

Подробная информация по формату файла NLog.config и доступным настройкам приведена в документации по библиотеке NLog.

Tip

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

Файлы тем интерфейса

Темы интерфейса для web-клиента и для desktop-клиента определяются файлами тем themeName.json (где themeName - любое имя, обычно соответствует имени определяемой темы).

Их подробное описание приведено в разделе Создание/Редактирование тем оформления и настройка фоновых изображений.

Note

Файл темы themeName.json объявлен в формате JSON. Он должен быть в кодировке UTF-8 (с опциональным BOM).

Tip

Используйте команду tadmin PrintJson Theme themeFile.json -i, чтобы вывести на консоль содержимое темы в JSON-форме после обработки всех директив, в т.ч. после включения содержимого других файлов директивами .include и .override. Здесь themeFile.json - полный или относительный путь до выводимого файла.

Переменные окружения

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

  • TESSA_CONFIG_ROOT - базовый путь к папке для всех прочих настроек, если для них не заданы абсолютные пути. Переменная не задана - используется текущая папка приложения.

  • TESSA_EXTENSIONS_ROOT - папка, начиная с которой выполняется сканирование файлов расширений extensions.xml (всех видов, включая консольные команды, скрипты, регистраторы ASP.NET Core и т.п). Переменная не задана - используется значение из TESSA_CONFIG_ROOT.

  • TESSA_APP_JSON - путь к файлу app.json, из которого загружается конфигурация. Если этот файл содержит включение других конфигурационных файлов по директивам .include, то они загружаются относительно его расположения. Действуют прежние правила:

    • если в конфигурационном файле значение строки начинается с @ (например, @localhost.cer), то путь считается от папки, в который лежит этот конфигурационный файл;
    • если путь относительный, но без символа @ (например, ./wwwroot/themes), то расчёт идёт от папки в переменной TESSA_CONFIG_ROOT (если не указана - от папки приложения).
  • TESSA_NLOG_CONFIG - путь к файлу NLog.config (по умолчанию - одноимённый файл из папки приложения, или из переменной TESSA_CONFIG_ROOT соответственно).

  • TESSA_CID - путь к файлу .cid, который будет создан приложением для записи туда имени компонента для подсистемы мониторинга и обнаружения компонентов, а также для его чтения при повторных запусках приложения.

  • TESSA_AUTHORIZED_KEYS - путь к папке authorized_keys, в которой лежат приватные ключи для подсистемы мониторинга и обнаружения компонентов.

Note

Всё это позволяет иметь, например, одну папку с бинарными файлами сервиса, и в юните-файле System D через переменные запустить несколько процессов с различными конфигурационными файлами. Причём в файлах app.json можно вставлять через директиву .include общие части из других конфигурационных файлов, и доопределять какие-то настройки в конфиге под экземпляр сервиса. В т.ч. настраивать отдельно логи, компоненты мониторинга и др.

Tip

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

Back to top