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

Создание скриптов для консольной команды tadmin Script

Создание скриптов для консольной команды tadmin Script

Начиная со сборки платформы 3.6 добавлена возможность создавать скрипты tadmin - специальные классы, которые запускает команда tadmin Script для выполнения кода .NET. Классы располагаются в сборках расширений, мы рекомендуем использовать сборку Tessa.Extensions.Console. Скрипты могут получать именованные аргументы, которые используются в классе для параметризации его действий.

Синтаксис команды tadmin Script доступен в руководстве администратора.

Вместо написания скрипта можно создать собственную команду с произвольными параметрами, как было описано выше, но скрипты значительно проще писать - обычно это класс с единственным методом, в котором разработчик автоматически получает контейнер Unity со всеми зависимостями, способ установки соединения с БД, и открытую сессию с веб-сервисом (опционально). Также скрипты удобны, когда необходимо выполнить некоторые действия при установке обновлений, т.е. для написания миграций.

Клиентские скрипты

Клиентские скрипты выполняют логин (открытие сессии) на сервер приложений в соответствии с параметрами, передаваемыми в командной строке. По завершении работы скрипта выполняется закрытие сессии.

Классу скрипта доступны следующие свойства:

  1. Result - код возврата, который получает сценарий, вызывающий команду tadmin. По умолчанию равен 0.

  2. Logger - объект, используемый для логирования с использованием консоли.

  3. ConfigurationManager - объект, предоставляющий доступ к конфигурационному файлу утилиты.

  4. Container - контейнер Unity, содержащий клиентские зависимости.

В сборке расширений Tessa.Extensions.Console создадим папку Scripts, в которой будут располагаться классы со скриптами. Добавим в неё класс с именем NewCard, который будет содержать скрипт создания структуры карточки с выводом её в окно консоли (или на стандартный вывод, который может быть перенаправлен в файл).

Tip

Вы можете использовать любую структуру папок в любых проектах расширений для определения классов-скриптов. Требования к классу-скрипту в том, чтобы он использовал атрибут [ConsoleScript] и реализовывал интерфейс IConsoleScript (а лучше - наследовался от одного из наследников базового класса ConsoleScriptBase). Использование проекта Tessa.Extensions.Console и папки Scripts - это рекомендация.

Класс клиентского скрипта должен быть унаследован от базового класса ClientConsoleScriptBase. Атрибут [ConsoleScript] является обязательным для того, чтобы утилита tadmin определила этот класс как класс скрипта. Также в атрибуте опционально указывается имя скрипта, по которому он будет вызван (когда имя не указано, то используется краткое имя типа - без пространства имён).

using System; using System.Threading; using System.Threading.Tasks; using Tessa.Cards; using Tessa.Platform.ConsoleApps; using Unity;

namespace Tessa.Extensions.Console.Scripts { [ConsoleScript] public class NewCard : ClientConsoleScriptBase { protected override async ValueTask ExecuteCoreAsync(CancellationToken cancellationToken) { string typeName = this.TryGetParameter("TypeName"); if (string.IsNullOrEmpty(typeName)) { await this.Logger.ErrorAsync("Pass 'TypeName' parameter for card type name to create."); this.Result = -1; return; }

var request = new CardNewRequest { CardTypeName = typeName }; var response = await this.Container.Resolve<ICardRepository>().NewAsync(request, cancellationToken);

var result = response.ValidationResult.Build(); await this.Logger.LogResultAsync(result);

if (!result.IsSuccessful) { this.Result = -2; return; }

response.Card.ID = Guid.NewGuid();

await this.Logger.WriteLineAsync(response.Card.ToTypedJson(indented: true)); }

protected override async ValueTask ShowHelpCoreAsync(CancellationToken cancellationToken = default) { await this.Logger.WriteLineAsync( $"Example: {Assembly.GetEntryAssembly()?.GetName().Name} Script {nameof(NewCard)}" + " -pp:TypeName=Partner"); } } }

Скрипт будет вызываться по имени NewCard. Имена всех скриптов должны быть уникальными, поэтому рассмотрите добавление префикса к имени, например, AbNewCard. Если вы не укажите имя в атрибуте [ConsoleScript], то скрипт можно будет вызвать, указав краткое имя типа, а именно NewCard.

Скрипт должен получить обязательный параметр TypeName с алиасом типа карточки, который создаётся. Если параметр отсутствует, то будет возвращён код возврата ErrorLevel = -1 (по логике в самом скрипте), что можно будет отследить из файла скрипта .bat или .sh.

Пример использования скрипта выводится на экран в методе ShowHelpCoreAsync, который вызывается при передаче параметра --help или -help.

.\tadmin Script NewCard --help

Скомпилируйте и замените файл Tessa.Extensions.Console в подпапке extensions внутри папки с tadmin. Запустите скрипт следующей командой (параметры подключения укажите в соответствии с вашим сервером приложений):

.\tadmin Script NewCard -pp:TypeName=Partner -a:https://localhost/tessa -u:admin -p:admin -q >partner.txt

Tip

Параметр -q блокирует вывод информационных сообщений, оставляя только ошибки и вывод методов Logger.WriteLineAsync.

Tip

Имя скрипта NewCard и имя параметров -pp:Parameter=Value могут указываться без учёта регистра, в т.ч. при выполнении на Linux.

Если команда выполняется без ошибок, то в текущей папке создаётся файл partner.txt, в котором указана структура новой карточки “Контрагент”. Откройте файл в блокноте, в секции “Partners” для поля “Name” укажите любую строку (в кавычках), содержащую краткого имени контрагента - это обязательное поле в карточке “Контрагент”. Вы также можете указать другие поля по желанию.

Мы используем этот файл в новом скрипте CreateCard, создайте класс с таким именем в папке Scripts.

using System.Threading; using System.Threading.Tasks; using Tessa.Cards; using Tessa.Platform.ConsoleApps; using Unity;

namespace Tessa.Extensions.Console.Scripts { [ConsoleScript] public class CreateCard : ClientConsoleScriptBase { protected override async ValueTask ExecuteCoreAsync(CancellationToken cancellationToken) { var json = await this.Options.Input.ReadToEndAsync(); var card = new Card().FromTypedJson(json);

var request = new CardStoreRequest { Card = card }; var response = await this.Container.Resolve<ICardRepository>().StoreAsync(request, cancellationToken);

var result = response.ValidationResult.Build(); await this.Logger.LogResultAsync(result);

this.Result = result.IsSuccessful ? 0 : -1; } } }

Команда считывает карточку из стандартного ввода и сохраняет карточку в веб-сервисе от имени указанного пользователя. Посредством вызова type partner.txt мы выводим карточку из файла partner.txt на стандартный вывод, который перенаправляется на стандартный ввод команды tadmin Script.

type partner.txt|.\tadmin Script CreateCard -a:https://localhost/tessa -u:admin -p:admin

Клиентские скрипты с подключением к БД

Помимо прямого подключения к веб-сервису та же самая команда может использовать строку подключения в app.json для прямого подключения к базе данных, чтобы выполнить на БД запросы в обход веб-сервиса.

В проекте Tessa.Extensions.Console в папке Scripts создайте класс GetCurrency. Запрос к базе данных будет выполнять поиск идентификатора карточки валюты по имени, указанному в параметре “Name”. Карточка по найденному идентификатору загружается и выводится на консоль в виде json-структуры.

using System; using System.Threading; using System.Threading.Tasks; using Tessa.Cards; using Tessa.Platform.ConsoleApps; using Tessa.Platform.Data; using Unity;

namespace Tessa.Extensions.Console.Scripts { [ConsoleScript] public class GetCurrency : ClientConsoleScriptBase { protected override async ValueTask ExecuteCoreAsync(CancellationToken cancellationToken) { string name = this.TryGetParameter("name"); if (string.IsNullOrEmpty(name)) { await this.Logger.ErrorAsync("Pass 'name' parameter for currency name."); this.Result = -1; return; }

Guid? cardID;

(DbManager db, IQueryBuilderFactory builder) = await this.ConnectDbAsync(cancellationToken); await using (db) { cardID = await db .SetCommand( builder .Select().C("ID") .From("Currencies").NoLock() .Where().LowerC("Name").Equals().LowerP("Name") .Build(), db.Parameter("Name", name)) .LogCommand() .ExecuteAsync<Guid?>(cancellationToken); }

if (!cardID.HasValue) { await this.Logger.ErrorAsync("Can't find currency by name: '{0}'", name); this.Result = -2; return; }

var cardRepository = this.Container.Resolve<ICardRepository>(); var response = await cardRepository.GetAsync(new CardGetRequest { CardID = cardID }, cancellationToken);

var result = response.ValidationResult.Build(); await this.Logger.LogResultAsync(result);

if (!result.IsSuccessful) { this.Result = -3; return; }

await this.Logger.WriteLineAsync(response.Card.ToTypedJson(indented: true)); } } }

Note

Рекомендуемым способом решать такую задачу было бы выполнение представления через IViewService, но для примера выполняется прямой запрос к БД.

Скомпилируйте и замените файл Tessa.Extensions.Console.dll для замены подпапки extensions. Выполните следующую команду, чтобы вывести структуру карточки “Валюта” для валюты “RUB” (рубли, поиск без учёта регистра).

.\tadmin Script GetCurrency -pp:Name=RUB -a:https://localhost/tessa -u:admin -p:admin -q

Tip

Укажите параметр командной строки -cs:connectionString для использования строки подключения с указанным именем вместо строки по умолчанию. Параметр -db:databaseName может быть указан для изменения имени базы данных в строке подключения.

Если вы укажете несуществующую валюту, то скрипт выведет ошибку и вернёт ненулевой код возврата (в соответствии с бизнес-логикой метода ExecuteCoreAsync):

.\tadmin Script GetCurrency -pp:Name=RUB111 -a:https://localhost/tessa -u:admin -p:admin -q

Can't find currency by name: 'RUB111'

Серверные скрипты

Серверные скрипты имеют полностью инициализированный серверный контейнер IUnityContainer, который по умолчанию определяет сессию пользователя System (её можно заменить с использованием метода SessionContext.Create). При этом не выполняется логина к веб-сервису, и все параметры для логина игнорируются.

Warning

Серверный контейнер содержит только те расширения, которые подключены в extensions.xml. Для стандартной конфигурации команды tadmin это платформенные расширения (Server/Shared), серверные Shared-расширения типового решения из сборки Tessa.Extensions.Default.Shared (как правило, это расширения на метаинформацию) и серверные Shared-расширения проектного решения Tessa.Extensions.Shared. Для полноценной серверной работы, включая все расширения типового решения (правила доступа, маршруты и др.) мы рекомендуем использовать клиентские скрипты, или же подключать сборки с Server-расширениями в extensions.xml.

Классу скрипта доступны следующие свойства:

  1. Result - код возврата, который получает сценарий, вызывающий команду tadmin. По умолчанию равен 0.

  2. Logger - объект, используемый для логирования с использованием консоли.

  3. ConfigurationManager - объект, предоставляющий доступ к конфигурационному файлу утилиты.

  4. Container - контейнер Unity, содержащий серверные зависимости.

В проекте Tessa.Extensions.Console в папке Scripts добавьте файл GetCurrencySql, который будет содержать скрипт, аналогичный GetCurrency, но выполняемый на прямом подключении к базе данных с ограниченным набором расширений. Серверный скрипт должен наследоваться от базового класса ServerConsoleScriptBase в дополнение к атрибуту [ConsoleScript] с опционального именем скрипта.

using System; using System.Threading; using System.Threading.Tasks; using Tessa.Cards; using Tessa.Platform.ConsoleApps; using Tessa.Platform.Data; using Unity;

namespace Tessa.Extensions.Console.Scripts { [ConsoleScript] public class GetCurrencySql : ServerConsoleScriptBase { protected override async ValueTask ExecuteCoreAsync(CancellationToken cancellationToken) { string name = this.TryGetParameter("name"); if (string.IsNullOrEmpty(name)) { await this.Logger.ErrorAsync("Pass 'name' parameter for currency name."); this.Result = -1; return; }

Guid? cardID;

var dbScope = this.Container.Resolve<IDbScope>(); await using (dbScope.Create()) { cardID = await dbScope.Db .SetCommand( dbScope.BuilderFactory .Select().C("ID") .From("Currencies").NoLock() .Where().LowerC("Name").Equals().LowerP("Name") .Build(), dbScope.Db.Parameter("Name", name)) .LogCommand() .ExecuteAsync<Guid?>(cancellationToken); }

if (!cardID.HasValue) { await this.Logger.ErrorAsync("Can't find currency by name: '{0}'", name); this.Result = -2; return; }

var cardRepository = this.Container.Resolve<ICardRepository>(); var response = await cardRepository.GetAsync(new CardGetRequest { CardID = cardID }, cancellationToken);

var result = response.ValidationResult.Build(); await this.Logger.LogResultAsync(result);

if (!result.IsSuccessful) { this.Result = -3; return; }

await this.Logger.WriteLineAsync(response.Card.ToTypedJson(indented: true)); } } }

Скрипт во много похож на предыдущий, но для выполнения прямых запросов к БД используется объект IDbScope.

Скомпилируйте и замените файл Tessa.Extensions.Console.dll в подпапке extensions. Выполните команду:

.\tadmin Script GetCurrencySql -pp:Name=RUB -q

В окне консоли будет выведена структура карточки “Валюта”.

Скрипты с настройкой контейнера Unity

Вы можете унаследовать класс скрипта от базового класса BasicConsoleScriptBase, чтобы либо не выполнялось никаких действий по настройке контейнера Unity, либо эти действия можно было полностью определить в методе RegisterCoreAsync. Используйте этот класс, если необходимо по-особому инициализировать объект контейнера Unity.

Классу скрипта доступны следующие свойства:

  1. Result - код возврата, который получает сценарий, вызывающий команду tadmin. По умолчанию равен 0.

  2. Logger - объект, используемый для логирования с использованием консоли.

  3. ConfigurationManager - объект, предоставляющий доступ к конфигурационному файлу утилиты.

  4. Container - контейнер Unity, содержащий определяемые вами зависимости в методе RegisterCoreAsync, или содержащий минимальные зависимости, если этот метод не переопределён.

Скрипт по-прежнему использует атрибут [ConsoleScript] для указания на то, что это скрипт, и для опционального задания его имени.

В проекте Tessa.Extensions.Console в папке Scripts создайте класс GetCurrencyId. В этом примере по имени валюты, переданной в параметре -pp:Name=..., определяется идентификатор карточки запросом к БД.

using System; using System.Threading; using System.Threading.Tasks; using Tessa.Platform.ConsoleApps; using Tessa.Platform.Data; using Unity;

namespace Tessa.Extensions.Console.Scripts { [ConsoleScript] public class GetCurrencyId : BasicConsoleScriptBase { protected override ValueTask RegisterCoreAsync(IUnityContainer container, CancellationToken cancellationToken) => container.RegisterDatabaseForConsoleAsync(cancellationToken: cancellationToken);

protected override async ValueTask ExecuteCoreAsync(CancellationToken cancellationToken) { var name = this.TryGetParameter("name"); if (string.IsNullOrEmpty(name)) { await this.Logger.ErrorAsync("Pass 'name' parameter for currency name."); this.Result = -1; return; }

var dbScope = this.Container.Resolve<IDbScope>();

Guid? cardID; await using (dbScope.Create()) { var db = dbScope.Db;

cardID = await db .SetCommand( dbScope.BuilderFactory .Select().C("ID") .From("Currencies").NoLock() .Where().LowerC("Name").Equals().LowerP("Name") .Build(), db.Parameter("Name", name)) .LogCommand() .ExecuteAsync<Guid?>(cancellationToken); }

if (!cardID.HasValue) { await this.Logger.ErrorAsync("Can't find currency by name: '{0}'", name); this.Result = -2; return; }

await this.Logger.WriteLineAsync(cardID.Value.ToString()); } } }

  1. В методе RegisterCoreAsync регистрируются зависимости для контейнера, связанные с базой данных.

  2. Из свойства с контейнером this.Container резолвится зарегистрированная зависимость IDbScope, посредством которой выполняется обращение к БД.

Note

Описанный скрипт взаимодействует с БД, поэтому для его реализации лучше подходит базовый класс ServerConsoleScriptBase. Наследование от BasicConsoleScriptBase реализовано для примера. Оно будет актуально для тех скриптов, в которых взаимодействие с БД не требуется.

Скомпилируйте и замените файл Tessa.Extensions.Console.dll в подпапке extensions в папке tadmin. Выполните команду:

.\tadmin Script GetCurrencyId -pp:Name=RUB -q

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

.\tadmin Script GetCurrencyId -pp:Name=RUB -cs:migration -db:tessa_new -q

Back to top