Реализация OpenFeature - открытого стандарта CNCF для оценки фича-флагов - на OneScript.
Прикладной код работает с флагами через единый API и ничего не знает о том, где они хранятся. Систему управления флагами можно заменить, поменяв одну строку регистрации провайдера: локальный JSON на старте, свой сервер в проде, набор в памяти в тестах.
opm install openfeature
#Использовать openfeature
// Один раз при старте приложения
OpenFeature.УстановитьПровайдер(Новый JsonFileProvider("flags.json"));
// Дальше везде
Клиент = OpenFeature.ПолучитьКлиента();
Если Клиент.ПолучитьЛогическое("new-checkout", Ложь) Тогда
ПоказатьНовуюКорзину();
КонецЕсли;Оценка никогда не выбрасывает исключений. Недоступный провайдер, отсутствующий флаг, неверный тип значения - во всех случаях вернётся переданное значение по умолчанию. Флаги не должны ронять приложение.
Контекст = Новый EvaluationContext("user-42", Новый Структура("plan", "pro"));
Тема = Клиент.ПолучитьСтроку("тема", "light", Контекст);Ключ таргетинга - идентификатор субъекта оценки: по нему провайдеры закрепляют пользователя за вариантом, чтобы процентные выкатки были стабильными.
Контекст уровня приложения задаётся один раз и объединяется с контекстом вызова, причём контекст вызова имеет приоритет:
OpenFeature.УстановитьГлобальныйКонтекст(Новый EvaluationContext("", Новый Структура("окружение", "prod")));Детали = Клиент.ПолучитьСтрокуСДеталями("тема", "light", Контекст);
Сообщить(Детали.Значение); // dark
Сообщить(Детали.Вариант); // тёмная
Сообщить(Детали.Причина); // TARGETING_MATCH
Сообщить(Детали.КодОшибки); // пусто при успехеПровайдеры InMemoryProvider и JsonFileProvider понимают два вида описания. Простое - сразу значение:
{
"new-checkout": true,
"максимум-позиций": 25
}Развёрнутое - с вариантами и правилами таргетинга:
{
"тема": {
"Варианты": { "светлая": "light", "тёмная": "dark" },
"ВариантПоУмолчанию": "светлая",
"Правила": [
{ "Атрибут": "plan", "Значения": ["pro", "enterprise"], "Вариант": "тёмная" }
]
},
"выключенный": { "Значение": true, "Включен": false }
}Правила проверяются по порядку, побеждает первое совпавшее; если ни одно не сработало, берётся вариант по умолчанию. Выключенный флаг всегда отдаёт значение по умолчанию с причиной DISABLED.
В коде набор флагов удобнее задавать Соответствием: ключи флагов обычно содержат дефис, а Структура такие имена не допускает.
Флаги = Новый Соответствие();
Флаги.Вставить("new-checkout", Истина);
OpenFeature.УстановитьПровайдер(Новый InMemoryProvider(Флаги));Хук - объект с методами До, После, ПриОшибке, Финально; реализовать можно любое подмножество.
Функция До(Знач Данные, Знач Подсказки) Экспорт
// Можно дополнить контекст - возвращённые атрибуты имеют приоритет
Возврат Новый EvaluationContext("", Новый Структура("окружение", "prod"));
КонецФункции
Процедура После(Знач Данные, Знач Детали, Знач Подсказки) Экспорт
Метрики.Учесть(Данные.КлючФлага, Детали.Вариант);
КонецПроцедурыOpenFeature.ДобавитьХук(Новый ХукМетрик()); // для всех клиентов
Клиент.ДобавитьХук(Новый ХукАудита()); // для одного клиентаСтадия До выполняется от глобальных хуков к локальным, а После, ПриОшибке и Финально - в обратном порядке. Сбой хука не роняет оценку: она завершается штатным отказом со значением по умолчанию.
| Метод | Возвращает | Описание |
|---|---|---|
УстановитьПровайдер(Провайдер) |
- | Регистрирует провайдера, инициализирует его и завершает предыдущего |
ПолучитьКлиента(Домен = "") |
FeatureClient |
Клиент оценки |
ДобавитьХук(Хук) |
- | Хук для всех клиентов |
УстановитьГлобальныйКонтекст(Контекст) |
- | Контекст уровня приложения |
ГлобальныйКонтекст() |
EvaluationContext |
Текущий глобальный контекст |
Провайдер() / МетаданныеПровайдера() / СостояниеПровайдера() |
Сведения о провайдере | |
Завершить() |
- | Сброс всего состояния API |
СоздатьИзолированныйЭкземпляр() |
OpenFeatureApi |
Независимый экземпляр API |
ПолучитьЛогическое, ПолучитьСтроку, ПолучитьЧисло, ПолучитьОбъект и их варианты …СДеталями. Сигнатура: (Ключ, ЗначениеПоУмолчанию, Контекст = Неопределено, Параметры = Неопределено), где Параметры - структура с полями Хуки и ПодсказкиХуков.
Также ДобавитьХук(Хук), Метаданные(), СостояниеПровайдера().
КлючТаргетинга(), УстановитьКлючТаргетинга(Значение), Установить(Имя, Значение), Получить(Имя, ЗначениеПоУмолчанию), Содержит(Имя), Атрибуты(), Объединить(Другой), Количество().
Свойства КлючФлага, Значение, Вариант, Причина, КодОшибки, СообщениеОбОшибке, МетаданныеФлага; методы ЭтоОшибка(), Метаданное(Имя, ЗначениеПоУмолчанию).
Константы вместо строковых литералов: OpenFeatureReason.СовпадениеТаргетинга() → TARGETING_MATCH, OpenFeatureErrorCode.ФлагНеНайден() → FLAG_NOT_FOUND и так далее.
Провайдер - объект со следующими методами:
Функция Метаданные() Экспорт // Структура("Имя", "МойПровайдер")
Процедура Инициализировать(Знач Контекст) Экспорт
Процедура Завершить() Экспорт
Функция Состояние() Экспорт // NOT_READY, READY, ERROR, STALE, FATAL
Функция ВычислитьЛогическое(Знач Ключ, Знач ЗначениеПоУмолчанию, Знач Контекст) Экспорт
Функция ВычислитьСтроку(Знач Ключ, Знач ЗначениеПоУмолчанию, Знач Контекст) Экспорт
Функция ВычислитьЧисло(Знач Ключ, Знач ЗначениеПоУмолчанию, Знач Контекст) Экспорт
Функция ВычислитьОбъект(Знач Ключ, Знач ЗначениеПоУмолчанию, Знач Контекст) ЭкспортКаждый метод оценки возвращает ResolutionDetails:
Функция ВычислитьЛогическое(Знач Ключ, Знач ЗначениеПоУмолчанию, Знач Контекст) Экспорт
Если НеНашли(Ключ) Тогда
Результат = Новый ResolutionDetails(ЗначениеПоУмолчанию);
Возврат Результат.ЗаполнитьОшибку(OpenFeatureErrorCode.ФлагНеНайден(),
"Флаг не найден", ЗначениеПоУмолчанию);
КонецЕсли;
Возврат Новый ResolutionDetails(Значение, OpenFeatureReason.СовпадениеТаргетинга(), "вариант-а");
КонецФункцииРеализованы обязательные требования разделов Flag Evaluation, Providers, Evaluation Context и Hooks:
- API - глобальный синглтон; есть и фабрика изолированных экземпляров (раздел 1.8).
- Регистрация провайдера вызывает
Инициализироватьу нового иЗавершитьу предыдущего. - До регистрации провайдера работает заглушка, возвращающая значения по умолчанию с причиной
DEFAULT. - Клиент не выбрасывает исключений и гарантирует тип: значение неожиданного типа заменяется значением по умолчанию с кодом
TYPE_MISMATCH. EvaluationDetailsсодержит ключ флага, значение, вариант, причину, код и сообщение об ошибке; поле метаданных при отсутствии данных - пустая запись, а неНеопределено.- Причины и коды ошибок соответствуют перечням спецификации.
- Порядок стадий хуков и слияние контекстов - по спецификации; ошибка в хуке приводит к штатному отказу оценки.
Не реализовано в этой версии: события провайдера (PROVIDER_READY, PROVIDER_CONFIGURATION_CHANGED и прочие), транзакционный контекст, доменные провайдеры (ПолучитьКлиента("домен") возвращает клиент с метаданными домена, но привязка отдельного провайдера к домену не поддерживается), tracking API.
opm install -l
oneunit execute -d ./tests