EvGen — это инструмент для генерации кода событий аналитики для различных платформ. Он позволяет создать единое описание событий в YAML-формате и автоматически генерировать код на разных языках программирования, а также документацию в различных форматах.
Для работы нужны Node.js и pnpm.
pnpm install
pnpm evgen -e events -c evgen.yaml- Kotlin
- Java
- Swift
- TypeScript
- Dart
- C#
- Основные концепции
- Конфигурационный файл evgen.yaml
- Файл событий events.yaml
- Типы данных
- Глобальные типы (.GlobalTypes)
- Интерфейсы
- Платформозависимые параметры
- Шаблоны
- Примеры использования
- Логика работы
EvGen работает с двумя основными параметрами:
- events(или events.yaml) - yaml-файл или папка с yaml-файлами с событиями
- evgen.yaml - конфигурация генерации кода и документации
Инструмент генерирует:
- Код для различных платформ
- Документацию в форматах Markdown, TXT, YAML
- Проверяет совместимость событий с интерфейсами
code:
# Конфигурации для генерации кода
ConfigName1:
platform: string # Название платформы
output_dir: string # Папка для генерируемого кода
language: string # Язык программирования
class_name: string # Имя генерируемого класса
only_last_version: boolean # Генерировать только последние версии (опционально)
param_name_case: string # Стиль именования параметров (опционально)
template_dir: string # Путь к пользовательским шаблонам (опционально)
disable_sending_meta: boolean # Отключить _meta для этой платформы (опционально)
meta_to_send: array # Какие поля включить в _meta: ['event', 'interfaces'] (опционально)
doc:
# Конфигурации для генерации документации
DocName1:
extension: string # Расширение файлов (md, txt, yaml)
output_dir: string # Папка для документации
template_dir: string # Путь к пользовательским шаблонам (опционально)
tag: string # Фильтр по тегам (опционально)
options:
keepParametersOrder: boolean # Сохранять порядок параметров в событиях согласно YAML
disable_sending_meta: boolean # Отключить добавление _meta атрибута в событиях
meta_to_send: array # Какие поля включить в _meta: ['event', 'interfaces']kotlin- Kotlin для Androidjava- Java для Androidswift- Swift для iOStype_script- TypeScript для веб-платформdart- Dart для Flutterc_sharp- C# для Unity
code:
AndroidKotlin:
platform: 'Android'
output_dir: 'android_kotlin'
language: 'kotlin'
class_name: 'EvgenAnalytics'
only_last_version: true
param_name_case: 'camelCase'
iOSSwift:
platform: 'iOS'
output_dir: 'ios_swift'
language: 'swift'
class_name: 'EvgenAnalytics'
doc:
Markdown:
extension: 'md'
output_dir: 'md_doc'
Txt:
extension: 'txt'
output_dir: 'txt_doc'
options:
keepParametersOrder: trueСохраняет порядок параметров в событиях согласно YAML-спецификации.
false(по умолчанию) — параметры, включённые через_included, располагаются перед остальными (для совместимости с предыдущими версиями)true— порядок параметров соответствует порядку в YAML
Отключает добавление атрибута _meta и генерацию функции makeMeta в сгенерированном коде.
false(по умолчанию) — атрибут_metaдобавляется к каждому событию с информацией о версии и интерфейсахtrue— атрибут_metaи функцияmakeMetaне генерируются
Эту опцию можно задать глобально в options, а также переопределить для конкретной платформы в конфигурации code. Настройка платформы имеет приоритет над глобальной.
Пример с глобальным отключением:
options:
disable_sending_meta: true # Отключено для всех платформ
code:
Android:
platform: 'Android'
output_dir: 'android'
language: 'kotlin'Пример с переопределением для платформы:
options:
disable_sending_meta: false # Включено глобально
code:
Android:
platform: 'Android'
output_dir: 'android'
language: 'kotlin'
disable_sending_meta: true # Но отключено для Android
iOS:
platform: 'iOS'
output_dir: 'ios'
language: 'swift'
# Использует глобальную настройку (false)Позволяет выборочно указать, какие поля включать в атрибут _meta. Работает только если disable_sending_meta не установлен в true.
- Массив, который может содержать значения:
'event','interfaces' - По умолчанию (не указано) — включаются все поля:
['event', 'interfaces']
Доступные значения:
'event'— включает информацию о версии события ({ event: { version: N } })'interfaces'— включает информацию об интерфейсах ({ interfaces: {...} })
Эту опцию можно задать глобально в options, а также переопределить для конкретной платформы.
Важно:
disable_sending_meta: trueимеет больший приоритет — если он установлен,meta_to_sendигнорируется.
Пример — отправлять только версию события:
options:
meta_to_send: ['event'] # Только версия события, без интерфейсов
code:
Android:
platform: 'Android'
output_dir: 'android'
language: 'kotlin'Пример — разная конфигурация для разных платформ:
options:
meta_to_send: ['event', 'interfaces'] # Полная мета для всех
code:
Android:
platform: 'Android'
output_dir: 'android'
language: 'kotlin'
meta_to_send: ['event'] # Только версия для Android
iOS:
platform: 'iOS'
output_dir: 'ios'
language: 'swift'
disable_sending_meta: true # Полностью отключить _meta для iOS# Глобальные параметры (добавляются ко всем событиям)
.GlobalParams:
parameters:
globalParam:
type: String
description: "Глобальный параметр"
description: "Глобальные параметры"
# Платформозависимые параметры (добавляются ко всем событиям платформы)
.PlatformParams:
Android:
description: "Параметры для Android"
parameters:
appVersion:
type: String
description: "Версия приложения"
# События
Events:
Namespace:
EventName:
v1:
description: "Описание события"
parameters:
param1:
type: String
description: "Описание параметра"
platforms:
Android:
app_versions: "1.0.0"
ticket: "https://tracker.com/TICKET-123"
# Интерфейсы
Interfaces:
InterfaceName:
v1:
description: "Описание интерфейса"
parameters:
requiredParam:
type: String
description: "Обязательный параметр"| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
description |
string | Да | Описание события |
parameters |
object | Нет | Параметры события |
platforms |
object | Да | Поддерживаемые платформы |
comment |
string | Нет | Дополнительный комментарий |
interface |
string/array | Нет | Интерфейсы, которым должно соответствовать событие |
namespaces |
array | Нет | Дополнительные пространства имен |
force_event_name |
string | Нет | Принудительное имя события |
tags |
string/array | Нет | Теги для фильтрации |
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
type |
string/object | Да | Тип данных параметра |
description |
string | Нет | Описание параметра |
default_value |
any | Нет | Значение по умолчанию |
abstract |
boolean | Нет | Абстрактный параметр |
optional |
boolean | Нет | Опциональный параметр |
element_type |
string/object | Нет | Тип элементов для списков |
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
app_versions |
string/number | Да | Версия приложения или статус |
ticket |
string | Нет | Ссылка на задачу в трекере |
Специальные значения для app_versions:
in_progress- в разработкеnot_supported- не поддерживается- Конкретная версия (например,
"1.0.0",42)
parameters:
stringParam:
type: String
description: "Строковый параметр"
intParam:
type: Int
description: "Целочисленный параметр"
longIntParam:
type: "Long Int"
description: "Длинное целое число"
boolParam:
type: Bool
description: "Булевый параметр"
doubleParam:
type: Double
description: "Параметр с плавающей точкой"parameters:
# Простое перечисление
enumParam:
type:
Enum:
name: MyEnum # Опциональное имя
values:
- option1
- option2
- option3
description: "Перечисление"
default_value: option1
# Перечисление с описаниями
enumWithDescriptions:
type:
Enum:
name: PagesEnum
values:
- value: screen_1
description: "Первый экран"
- value: screen_2
description: "Второй экран"
# Числовое перечисление
enumInt:
type:
Enum:
values: [1, 2, 3]parameters:
# Простой словарь (String -> element_type)
dictParam:
type: Dict
description: "Простой словарь"
element_type: String # (optional)
# Словарь с типом Enum
dictWithElementType:
type: Dict
element_type:
Enum:
name: DictValueType
values: [option1, option2]
# Типизированный словарь с разными типами значений { stringField: String, intField: Int, boolField: Bool }
typedDict:
type:
Dict:
stringField: String
intField: Int
boolField: Bool
parameters:
# Простой список
listParam:
type: List
element_type: String
default_value: empty_list
# Типизированный список словарей [{ itemName: String, itemId: Int}]
typedList:
type:
List:
itemName: String
itemId: Int
# Список перечислений
enumList:
type: List
element_type:
Enum:
values: [val1, val2, val3]parameters:
# Простая константа
constParam:
type:
Const: "FIXED_VALUE"
description: "Константа"
# Платформозависимая константа
platformConst:
type:
PlatformConst:
Android: "AndroidValue"
iOS: "iOSValue"
Flutter: "FlutterValue"Глобальные типы позволяют определить переиспользуемые типы данных, на которые можно ссылаться в параметрах событий через !ref.
.GlobalTypes:
# Простые типы-алиасы
UserId: String
ContentId: String
Timestamp: Long Int
Price: Double
# Enum типы
PageId:
Enum:
name: PageId
values:
- home
- catalog
- movie_card
# Типизированный Dict
Metadata:
Dict:
source: String
timestamp: Int
tags: List
# Типизированный List
ContentItems:
List:
id: String
title: String
rating: DoubleДля ссылки на глобальный тип используйте тег !ref:
Events:
MyNamespace:
PageView:
v1:
description: "Просмотр страницы"
parameters:
pageId:
type: !ref PageId
description: "ID страницы"
userId:
type: !ref UserId
description: "ID пользователя"
metadata:
type: !ref Metadata
description: "Метаданные"
platforms:
Android:
app_versions: "1.0.0"Глобальные типы поддерживают вложенность и версионирование:
.GlobalTypes:
# Вложенные типы (namespace.type)
Content:
Type:
Enum:
name: ContentType
values: [movie, series]
Category:
Enum:
name: ContentCategory
values: [action, comedy, drama]
# Версионированные типы
Metadata:
v1:
Dict:
source: String
timestamp: Int
v2:
Dict:
source: String
timestamp: Int
tags: List
version: IntИспользование:
parameters:
contentType:
type: !ref Content.Type
description: "Тип контента"
metadataV1:
type: !ref Metadata.v1
description: "Метаданные v1"
metadataV2:
type: !ref Metadata.v2
description: "Метаданные v2"Интерфейсы позволяют определить общие требования к событиям:
Interfaces:
Page:
v1:
description: "Протокол показа страницы"
parameters:
page:
type: String
description: "Название страницы"
pageId:
type: Int
description: "ID страницы"
Movie:
v1:
description: "Протокол фильма"
parameters:
movieName:
type: String
description: "Название фильма"
v2:
description: "Расширенный протокол фильма"
parameters:
movieId:
type: Int
description: "ID фильма"
movieName:
type: String
description: "Название фильма"
Events:
Shop:
Showed:
v1:
description: "Показ магазина"
interface: Page # Должно содержать параметры интерфейса Page
parameters:
page:
type: String
description: "Название страницы"
pageId:
type: Int
description: "ID страницы"
MoviePage:
Showed:
v1:
description: "Показ страницы фильма"
interface:
- Page
- Movie # Несколько интерфейсов
parameters:
page:
type: String
pageId:
type: Int
movieId:
type: Int
movieName:
type: StringПлатформозависимые параметры добавляются автоматически ко всем событиям конкретной платформы:
.PlatformParams:
Android:
description: "Параметры для Android"
parameters:
appVersion:
type: String
description: "Версия приложения"
deviceModel:
type: String
description: "Модель устройства"
iOS:
description: "Параметры для iOS"
parameters:
hasSubscription:
type: Bool
description: "Наличие подписки"
Events:
MyEvent:
v1:
description: "Мое событие"
parameters:
customParam:
type: String
platforms:
Android:
app_versions: "1.0.0"
iOS:
app_versions: "1.0.0"В результате для Android будут параметры: customParam, appVersion, deviceModel
Для iOS: customParam, hasSubscription
EvGen использует шаблоны Handlebars для генерации кода. Встроенные шаблоны находятся в папке src/templates/.
Доступные хелперы в шаблонах:
- Все из категорий 'comparison', 'array', 'string', 'object', 'collection', 'math' из Handlebars-helpers
- Кастомные хелперы:
- изменение кейса (эти были сделаны в основном для сохранения поведения кодогенерации для внутренних проектах по сравнению с предыдущим генератором, но также можно использовать другие хелперы из Handlebars-helpers):
camelcase-{{camelcase "userName"}}→userNamepascalcase-{{pascalcase "userName"}}→UserNamesnakecase-{{snakeCase userName}}→user_nameuppersnakecase-{{upperSnakeCase "userName"}}→USER_NAMEupperCase-{{upperCase "userName"}}→USERNAMElowerFirstLetter-{{lowerFirstLetter "Hello"}}→helloupperFirstLetter-{{upperFirstLetter "hello"}}→Hello
- проверка типа:
isNumber-{{#if (isNumber value)}}число{{/if}}isString-{{#if (isString value)}}строка{{/if}}isEnum-{{#if (isEnum parameter.type)}}Enum {{parameter.name}}{{/if}}isTypedList-{{#if (isTypedList parameter.type)}}список{{/if}}isTypedDict-{{#if (isTypedDict parameter.type)}}словарь{{/if}}isConst-{{#if (isConst parameter.type)}}"{{parameter.type.Const}}"{{/if}}isPlatformConst-{{#if (isPlatformConst parameter.type)}}платформенная константа{{/if}}isNamedEnum-{{#if (isNamedEnum parameter.type)}}именованный enum{{/if}}isCustomParameter-{{#if (isCustomParameter parameter.type)}}пользовательский тип{{/if}}isNamedCustomType-{{#if (isNamedCustomType parameter.type)}}именованный тип{{/if}}isRef-{{#if (isRef parameter.type)}}ссылка на глобальный тип{{/if}}extractRef-{{extractRef parameter.type}}→ извлекает имя типа из!ref_TypeName
- фильтрация:
filterTruthy-{{#filterTruthy events "isActive"}}{{#each this}}{{name}} is truthy{{/each}}{{/filterTruthy}}filterFalsy-{{#filterFalsy parameters "isOptional"}}{{#each}}{{name}} is required{{/each}}{{/filterFalsy}}filterDefined-{{#filterDefined parameters "defaultValue"}}{{#each}}{{name}} с дефолтным значением{{/each}}{{/filterDefined}}filterUndefined-{{#filterUndefined parameters "defaultValue"}}{{#each}}{{name}} без дефолтного значения{{/each}}{{/filterUndefined}}
- другие полезные хелперы:
isDefined-{{#if (isDefined value)}}определено{{/if}}toArray-{{#contains (toArray 'Int' 'Double') 'Int'}}Int{{/contains}}splitByNewLine-{{splitByNewLine "line1\nline2"}}→["line1", "line2"]escape-{{#escape newLine="\n"}}контент{{/escape}}repeat-{{repeat "-" 3}}→"---"entries-{{#each (entries object)}}{{key}}: {{value}}{{/each}}groupBy-{{#each (groupBy events "namespace")}}группа{{/each}}
- изменение кейса (эти были сделаны в основном для сохранения поведения кодогенерации для внутренних проектах по сравнению с предыдущим генератором, но также можно использовать другие хелперы из Handlebars-helpers):
Можно создать собственные шаблоны и указать путь в конфигурации:
code:
CustomAndroid:
platform: 'Android'
language: 'kotlin'
template_dir: 'my-templates/kotlin'
# ... остальная конфигурацияclassname(string) - имя генерируемого класса из конфигурацииonlyLastVersion(boolean) - флаг генерации только последних версий событийdisableSendingMeta(boolean) - флаг полного отключения генерации_metaатрибутаsendMetaEvent(boolean) - включать ли версию события в_meta.eventsendMetaInterfaces(boolean) - включать ли интерфейсы в_meta.interfaces
-
globalParameters(GlobalParameters) - глобальные параметрыparameters: EventParameter[]- массив параметровdescription: string- описание глобальных параметровcomment?: string- дополнительный комментарий
-
platformParameters(Record<string, PlatformParameters>) - параметры для каждой платформыparameters: EventParameter[]- массив параметров платформыdescription: string- описание параметров платформыcomment?: string- дополнительный комментарий
-
globalTypes(Record<string, GlobalType>) - глобальные типыname: string- имя типа (может содержать точки для вложенных типов, напримерMetadata.v1)type: ParameterType- определение типа
-
eventNamespaces(EventNamespace[]) - массив пространств имен событийname: string- название пространства именevents: Event[]- массив событий в пространстве именdocumentationDir?: string- директория для документацииallVersions: EventVersion[]- все версии событий (добавляется при обработке)actualVersions: EventVersion[]- актуальные версии событийdeprecatedVersions: EventVersion[]- устаревшие версии событийallPlatforms: string[]- все поддерживаемые платформыnamedEnums: EventParameter[]- именованные перечисления в пространстве именnamedCustomParameters: EventParameter[]- именованные пользовательские типы
-
allVersionsByEvent(Record<string, EventVersion[]>) - все версии событий, сгруппированные по имени события
-
namedEnums(EventParameter[]) - глобальные именованные перечисления- Каждый элемент содержит
type.Enum.nameдля именованных enum'ов
- Каждый элемент содержит
-
namedCustomParameters(EventParameter[]) - глобальные именованные пользовательские типы- Элементы с пользовательскими типами данных
interfaces(Record<string, InterfaceVersion[]>) - интерфейсы, доступные только в шаблонах документацииname: string- название интерфейсаnamespace: string- пространство именversion: number- версия интерфейсаparameters: EventParameter[]- параметры интерфейсаdescription: string- описание интерфейса
shared(произвольной структуры) - общие данные из блокаSharedв YAML файлах
interface EventParameter {
name: string; // Имя параметра
namespace: string; // Пространство имен
version: number; // Версия
type: ParameterType; // Тип параметра (см. раздел "Типы данных")
description: string; // Описание параметра
abstract: boolean; // Абстрактный параметр
optional: boolean; // Опциональный параметр
elementType?: PrimitiveType; // Тип элементов для List/Dict
defaultValue?: string; // Значение по умолчанию
}interface EventVersion {
event: string; // Полное имя события
name: string; // Короткое имя события
namespace: string; // Пространство имен
additionalNamespaces: string[]; // Дополнительные пространства имен
version: number; // Версия события
description: string; // Описание события
interfaces: Record<string, number>; // Используемые интерфейсы
comment?: string; // Комментарий
interfaceNames?: string[]; // Имена интерфейсов
tags: string[]; // Теги события
platforms?: Record<string, Platform>; // Поддерживаемые платформы
parameters: EventParameter[]; // Параметры события
}Events:
User:
Login:
v1:
description: "Вход пользователя в систему"
parameters:
userId:
type: Int
description: "ID пользователя"
loginMethod:
type:
Enum:
name: LoginMethod
values: [email, phone, social]
description: "Способ входа"
platforms:
Android:
app_versions: "2.1.0"
ticket: "https://tracker.com/USER-123"
iOS:
app_versions: "2.0.5"Interfaces:
Trackable:
v1:
parameters:
eventId:
type: String
timestamp:
type: "Long Int"
Events:
Product:
Purchase:
v1:
description: "Покупка товара"
interface: Trackable
parameters:
eventId:
type: String
timestamp:
type: "Long Int"
productId:
type: Int
amount:
type: DoubleМожно разделить события на модули:
events/user.yaml:
Events:
User:
Login:
v1:
# ... определение событияevents/product.yaml:
Events:
Product:
Purchase:
v1:
# ... определение событияevents/interfaces.yaml:
Interfaces:
Trackable:
v1:
# ... определение интерфейса- Парсинг конфигурации - чтение
evgen.yaml - Парсинг событий - чтение
events.yamlили папки с модулями - Валидация - проверка корректности данных и совместимости с интерфейсами
- Обработка для каждой платформы:
- Фильтрация событий по платформе
- Добавление глобальных параметров
- Добавление платформозависимых параметров
- Применение версионности
- Генерация кода - использование шаблонов для создания файлов
- Генерация документации - создание документации в указанных форматах
События именуются по принципу конкатенации неймспейсов через точку:
Events:
Namespace1:
Namespace2:
EventName:
v1:
# Итоговое имя: "Namespace1.Namespace2.EventName"Можно принудительно задать имя:
Events:
Namespace1:
EventName:
v1:
force_event_name: "custom-event-name"
# Итоговое имя: "custom-event-name"События и интерфейсы поддерживают версионирование:
Events:
MyEvent:
v1:
# Первая версия
v2:
# Вторая версия
Interfaces:
MyInterface:
v1:
# Первая версия интерфейса
v2:
# Вторая версияПри указании only_last_version: true генерируются только последние версии.
EvGen проверяет, что события соответствуют указанным интерфейсам:
- Все параметры интерфейса должны присутствовать в событии
- Типы параметров должны совпадать
- Поддерживается проверка совместимости с любой версией интерфейса
EvGen при парсинге полностью поддерживает YAML-якоря (&anchor) и ссылки (*reference):
Constants:
string: &string String
description: &description description
Events:
MyEvent:
v1:
parameters:
param1:
type: *string
*description: "Параметр 1"YAML поддерживает специальный синтаксис <<: для слияния содержимого якоря в текущий объект:
Events:
MyNamespace:
# Определение переиспользуемых параметров в блоке inheritance
inheritance:
reused_params: &reused_params
userId:
type: Int
description: "ID пользователя"
timestamp:
type: "Long Int"
description: "Временная метка"
Event1:
v1:
description: "Первое событие"
parameters:
<<: *reused_params # Вставка всех параметров из якоря
customParam:
type: String
description: "Дополнительный параметр"
platforms:
Android:
app_versions: "1.0.0"
Event2:
v1:
description: "Второе событие"
parameters:
<<: *reused_params # Те же параметры переиспользуются
anotherParam:
type: Bool
platforms:
Android:
app_versions: "1.0.0"В результате оба события (Event1 и Event2) будут содержать параметры userId и timestamp, а также свои уникальные параметры.
EvGen поддерживает специальную логику для подключения данных из других файлов:
**1. Для подключения значений as is ** используйте тег !include с синтаксисом filename:key.nested_key:
# params.yaml
reused_scalar_values:
shop_page: shop_page
default_timeout: 5000# events.yaml
Events:
Shop:
PageShowed:
v1:
parameters:
pageName:
type:
Const: !include params:reused_scalar_values.shop_page**2. Для мержа объекта из другого файла в объект текущего файла (аналог merge key <<: но между файлами) ** используйте префикс _included к ключу:
# params.yaml
reused_params:
single_param:
paramFromAnotherFile:
type: String
description: "Параметр из другого файла"
several_params:
batchParam1:
type: String
description: "Первый параметр"
batchParam2:
type: Int
description: "Второй параметр"# events.yaml
Events:
MyEvent:
v1:
description: "Событие с переиспользованием параметров"
parameters:
customParam:
type: String
description: "Кастомный параметр"
# Включение одного параметра
_included_single: !include params:reused_params.single_param
# Включение нескольких параметров
_included_batch: !include params:reused_params.several_paramsПримечания:
- Путь к файлу указывается без расширения
.yaml - Можно использовать вложенные ключи через точку:
file:key.nested_key.deep_key - Префикс
_includedк ключу говорит парсеру, что нужно включить содержимое объекта из другого файла
Эта документация охватывает все основные возможности EvGen. Для более детального изучения рекомендуется изучить примеры в папке tutorial/ пакета.