mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-29 04:58:05 +00:00
feat: Feature flag for relational tables (#10599)
# Which Problems Are Solved This PR introduces a new feature flag `EnableRelationalTables` that will be used in following implementations to decide whether Zitadel should use the relational model or the event sourcing one. # TODO - [x] Implement flag at system level - [x] Display the flag on console: https://github.com/zitadel/zitadel/pull/10615 # How the Problems Are Solved - Implement loading the flag from config - Add persistence of the flag through gRPC endpoint (SetInstanceFeatures) - Implement reading of the flag through gRPC endpoint (GetInstanceFeatures) # Additional Changes Some minor refactoring to remove un-needed generics annotations # Additional Context - Closes #10574 --------- Co-authored-by: Silvan <27845747+adlerhurst@users.noreply.github.com>
This commit is contained in:
@@ -1146,6 +1146,7 @@ DefaultInstance:
|
||||
# BaseURI: "" # ZITADEL_DEFAULTINSTANCE_FEATURES_LOGINV2_BASEURI
|
||||
# PermissionCheckV2: false # ZITADEL_DEFAULTINSTANCE_FEATURES_PERMISSIONCHECKV2
|
||||
# ConsoleUseV2UserApi: false # ZITADEL_DEFAULTINSTANCE_FEATURES_CONSOLEUSEV2USERAPI
|
||||
# EnableRelationalTables: false # ZITADEL_DEFAULTINSTANCE_FEATURES_ENABLERELATIONALTABLES
|
||||
|
||||
Limits:
|
||||
# AuditLogRetention limits the number of events that can be queried via the events API by their age.
|
||||
|
||||
@@ -36,6 +36,7 @@ const FEATURE_KEYS = [
|
||||
'oidcTokenExchange',
|
||||
'permissionCheckV2',
|
||||
'userSchema',
|
||||
'enableRelationalTables',
|
||||
] as const;
|
||||
|
||||
export type ToggleState = { source: Source; enabled: boolean };
|
||||
|
||||
@@ -1653,7 +1653,9 @@
|
||||
"CONSOLEUSEV2USERAPI_DESCRIPTION": "Когато този флаг е активиран, конзолата използва V2 User API за създаване на нови потребители. С V2 API новосъздадените потребители започват без начален статус.",
|
||||
"LOGINV2": "Вход V2",
|
||||
"LOGINV2_DESCRIPTION": "Активирането на това включва новия потребителски интерфейс за вход, базиран на TypeScript, с подобрена сигурност, производителност и възможности за персонализиране.",
|
||||
"LOGINV2_BASEURI": "Базов URI"
|
||||
"LOGINV2_BASEURI": "Базов URI",
|
||||
"ENABLERELATIONALTABLES": "Релационни таблици",
|
||||
"ENABLERELATIONALTABLES_DESCRIPTION": "Активирайте това, за да използвате релационни таблици вместо проекции за съхранение на данни. В момента този превключвател не прави нищо."
|
||||
},
|
||||
"DIALOG": {
|
||||
"RESET": {
|
||||
|
||||
@@ -1654,7 +1654,9 @@
|
||||
"CONSOLEUSEV2USERAPI_DESCRIPTION": "Když je tato příznak povolen, konzole používá V2 User API k vytvoření nových uživatelů. S V2 API nově vytvoření uživatelé začínají bez počátečního stavu.",
|
||||
"LOGINV2": "Přihlášení V2",
|
||||
"LOGINV2_DESCRIPTION": "Povolením této možnosti se aktivuje nové přihlašovací rozhraní založené na TypeScriptu s vylepšeným zabezpečením, výkonem a přizpůsobitelností.",
|
||||
"LOGINV2_BASEURI": "Základní URI"
|
||||
"LOGINV2_BASEURI": "Základní URI",
|
||||
"ENABLERELATIONALTABLES": "Relační tabulky",
|
||||
"ENABLERELATIONALTABLES_DESCRIPTION": "Povolte toto pro použití relačních tabulek místo projekcí pro ukládání dat. Momentálně tento přepínač nic nedělá."
|
||||
},
|
||||
"DIALOG": {
|
||||
"RESET": {
|
||||
|
||||
@@ -1654,7 +1654,9 @@
|
||||
"CONSOLEUSEV2USERAPI_DESCRIPTION": "Wenn diese Option aktiviert ist, verwendet die Konsole die V2 User API, um neue Benutzer zu erstellen. Mit der V2 API starten neu erstellte Benutzer nicht im Initial Zustand.",
|
||||
"LOGINV2": "Login V2",
|
||||
"LOGINV2_DESCRIPTION": "Durch das Aktivieren wird das neue TypeScript-basierte Login-UI mit verbesserter Sicherheit, Leistung und Anpassbarkeit aktiviert.",
|
||||
"LOGINV2_BASEURI": "Basis-URI"
|
||||
"LOGINV2_BASEURI": "Basis-URI",
|
||||
"ENABLERELATIONALTABLES": "Relationale Tabellen",
|
||||
"ENABLERELATIONALTABLES_DESCRIPTION": "Aktivieren Sie diese Option, um relationale Tabellen anstelle von Projektionen zur Datenspeicherung zu verwenden. Diese Option hat derzeit keine Auswirkung."
|
||||
},
|
||||
"DIALOG": {
|
||||
"RESET": {
|
||||
|
||||
@@ -1658,7 +1658,9 @@
|
||||
"CONSOLEUSEV2USERAPI_DESCRIPTION": "When this flag is enabled, the console uses the V2 User API to create new users. With the V2 API, newly created users start without an initial state.",
|
||||
"LOGINV2": "Login V2",
|
||||
"LOGINV2_DESCRIPTION": "Enabling this activates the new TypeScript-based login UI with improved security, performance, and customization.",
|
||||
"LOGINV2_BASEURI": "Base URI"
|
||||
"LOGINV2_BASEURI": "Base URI",
|
||||
"ENABLERELATIONALTABLES": "Relational Tables",
|
||||
"ENABLERELATIONALTABLES_DESCRIPTION": "Enable this to use relational tables instead of projections for storing data. Currently this toggle does nothing."
|
||||
},
|
||||
"DIALOG": {
|
||||
"RESET": {
|
||||
|
||||
@@ -1655,7 +1655,9 @@
|
||||
"CONSOLEUSEV2USERAPI_DESCRIPTION": "Cuando esta opción está habilitada, la consola utiliza la API V2 de usuario para crear nuevos usuarios. Con la API V2, los usuarios recién creados comienzan sin un estado inicial.",
|
||||
"LOGINV2": "Inicio de sesión V2",
|
||||
"LOGINV2_DESCRIPTION": "Al habilitar esto, se activa la nueva interfaz de inicio de sesión basada en TypeScript con mejoras en seguridad, rendimiento y personalización.",
|
||||
"LOGINV2_BASEURI": "URI base"
|
||||
"LOGINV2_BASEURI": "URI base",
|
||||
"ENABLERELATIONALTABLES": "Tablas relacionales",
|
||||
"ENABLERELATIONALTABLES_DESCRIPTION": "Habilitar esto para usar tablas relacionales en lugar de proyecciones para almacenar datos. Actualmente este interruptor no hace nada."
|
||||
},
|
||||
"DIALOG": {
|
||||
"RESET": {
|
||||
|
||||
@@ -1654,7 +1654,9 @@
|
||||
"CONSOLEUSEV2USERAPI_DESCRIPTION": "Lorsque ce drapeau est activé, la console utilise l'API V2 User pour créer de nouveaux utilisateurs. Avec l'API V2, les nouveaux utilisateurs commencent sans état initial.",
|
||||
"LOGINV2": "Connexion V2",
|
||||
"LOGINV2_DESCRIPTION": "L’activation de cette option lance la nouvelle interface de connexion basée sur TypeScript, avec une sécurité, des performances et une personnalisation améliorées.",
|
||||
"LOGINV2_BASEURI": "URI de base"
|
||||
"LOGINV2_BASEURI": "URI de base",
|
||||
"ENABLERELATIONALTABLES": "Tables relationnelles",
|
||||
"ENABLERELATIONALTABLES_DESCRIPTION": "Activez ceci pour utiliser des tables relationnelles au lieu de projections pour stocker des données. Actuellement, ce commutateur ne fait rien."
|
||||
},
|
||||
"DIALOG": {
|
||||
"RESET": {
|
||||
|
||||
@@ -1652,7 +1652,9 @@
|
||||
"CONSOLEUSEV2USERAPI_DESCRIPTION": "Ha ez a jelző engedélyezve van, a konzol a V2 User API-t használja új felhasználók létrehozásához. A V2 API-val az újonnan létrehozott felhasználók kezdeti állapot nélkül indulnak.",
|
||||
"LOGINV2": "Bejelentkezés V2",
|
||||
"LOGINV2_DESCRIPTION": "Ennek engedélyezésével aktiválódik az új, TypeScript-alapú bejelentkezési felület, amely jobb biztonságot, teljesítményt és testreszabhatóságot nyújt.",
|
||||
"LOGINV2_BASEURI": "Alap URI"
|
||||
"LOGINV2_BASEURI": "Alap URI",
|
||||
"ENABLERELATIONALTABLES": "Kapcsolati táblák",
|
||||
"ENABLERELATIONALTABLES_DESCRIPTION": "Engedélyez ezt a relációs táblák használatához a projekciók helyett az adatok tárolásához. Jelenleg ez a kapcsoló semmit sem csinál."
|
||||
},
|
||||
"DIALOG": {
|
||||
"RESET": {
|
||||
|
||||
@@ -1521,7 +1521,9 @@
|
||||
"CONSOLEUSEV2USERAPI_DESCRIPTION": "Ketika flag ini diaktifkan, konsol menggunakan API Pengguna V2 untuk membuat pengguna baru. Dengan API V2, pengguna yang baru dibuat dimulai tanpa keadaan awal.",
|
||||
"LOGINV2": "Login V2",
|
||||
"LOGINV2_DESCRIPTION": "Mengaktifkan ini akan mengaktifkan antarmuka login baru berbasis TypeScript dengan keamanan, performa, dan kustomisasi yang lebih baik.",
|
||||
"LOGINV2_BASEURI": "URI dasar"
|
||||
"LOGINV2_BASEURI": "URI dasar",
|
||||
"ENABLERELATIONALTABLES": "Tabel Relasional",
|
||||
"ENABLERELATIONALTABLES_DESCRIPTION": "Aktifkan ini untuk menggunakan tabel relasional alih-alih proyeksi untuk menyimpan data. Saat ini, toggle ini tidak berfungsi."
|
||||
},
|
||||
"DIALOG": {
|
||||
"RESET": {
|
||||
|
||||
@@ -1654,7 +1654,9 @@
|
||||
"CONSOLEUSEV2USERAPI_DESCRIPTION": "Quando questa opzione è abilitata, la console utilizza l'API V2 User per creare nuovi utenti. Con l'API V2, i nuovi utenti creati iniziano senza uno stato iniziale.",
|
||||
"LOGINV2": "Accesso V2",
|
||||
"LOGINV2_DESCRIPTION": "Abilitando questa opzione si attiva la nuova interfaccia di login basata su TypeScript con sicurezza, prestazioni e personalizzazione migliorate.",
|
||||
"LOGINV2_BASEURI": "URI di base"
|
||||
"LOGINV2_BASEURI": "URI di base",
|
||||
"ENABLERELATIONALTABLES": "Tabelle relazionali",
|
||||
"ENABLERELATIONALTABLES_DESCRIPTION": "Abilita questo toggle per utilizzare le tabelle relazionali invece di proiezioni per memorizzare i dati. Attualmente questo toggle non fa nulla."
|
||||
},
|
||||
"DIALOG": {
|
||||
"RESET": {
|
||||
|
||||
@@ -1654,7 +1654,9 @@
|
||||
"CONSOLEUSEV2USERAPI_DESCRIPTION": "このフラグが有効化されると、コンソールはV2ユーザーAPIを使用して新しいユーザーを作成します。V2 APIでは、新しく作成されたユーザーは初期状態なしで開始します。",
|
||||
"LOGINV2": "ログイン V2",
|
||||
"LOGINV2_DESCRIPTION": "これを有効にすると、セキュリティ、パフォーマンス、およびカスタマイズ性が向上した、TypeScript ベースの新しいログイン UI が有効になります。",
|
||||
"LOGINV2_BASEURI": "ベースURI"
|
||||
"LOGINV2_BASEURI": "ベースURI",
|
||||
"ENABLERELATIONALTABLES": "リレーショナルテーブル",
|
||||
"ENABLERELATIONALTABLES_DESCRIPTION": "データを保存するためにプロジェクションの代わりにリレーショナルテーブルを使用するには、これを有効にします。現在、このトグルは何もしません。"
|
||||
},
|
||||
"DIALOG": {
|
||||
"RESET": {
|
||||
|
||||
@@ -1654,7 +1654,9 @@
|
||||
"CONSOLEUSEV2USERAPI_DESCRIPTION": "이 플래그가 활성화되면 콘솔은 V2 사용자 API를 사용하여 새 사용자를 생성합니다. V2 API를 사용하면 새로 생성된 사용자는 초기 상태 없이 시작합니다.",
|
||||
"LOGINV2": "로그인 V2",
|
||||
"LOGINV2_DESCRIPTION": "이 옵션을 활성화하면 보안, 성능 및 사용자 정의 기능이 향상된 새로운 TypeScript 기반 로그인 UI가 활성화됩니다.",
|
||||
"LOGINV2_BASEURI": "기본 URI"
|
||||
"LOGINV2_BASEURI": "기본 URI",
|
||||
"ENABLERELATIONALTABLES": "관계형 테이블",
|
||||
"ENABLERELATIONALTABLES_DESCRIPTION": "데이터 저장을 위해 프로젝션 대신 관계형 테이블을 사용하려면 이를 활성화하십시오. 현재 이 토글은 아무런 기능을 하지 않습니다."
|
||||
},
|
||||
"DIALOG": {
|
||||
"RESET": {
|
||||
|
||||
@@ -1655,7 +1655,9 @@
|
||||
"CONSOLEUSEV2USERAPI_DESCRIPTION": "Кога ова знаме е овозможено, конзолата го користи V2 User API за креирање на нови корисници. Со V2 API, новосоздадените корисници започнуваат без почетна состојба.",
|
||||
"LOGINV2": "Најава V2",
|
||||
"LOGINV2_DESCRIPTION": "Овозможувањето на ова ја активира новата TypeScript-базирана најава со подобрена безбедност, перформанси и прилагодливост.",
|
||||
"LOGINV2_BASEURI": "Основен URI"
|
||||
"LOGINV2_BASEURI": "Основен URI",
|
||||
"ENABLERELATIONALTABLES": "Релациски табели",
|
||||
"ENABLERELATIONALTABLES_DESCRIPTION": "Овозможете го ова за користење на релациски табели наместо проекции за складирање податоци. Во моментов, овој прекинувач не прави ништо."
|
||||
},
|
||||
"DIALOG": {
|
||||
"RESET": {
|
||||
|
||||
@@ -1654,7 +1654,9 @@
|
||||
"CONSOLEUSEV2USERAPI_DESCRIPTION": "Wanneer deze vlag is ingeschakeld, gebruikt de console de V2 User API om nieuwe gebruikers aan te maken. Met de V2 API beginnen nieuw aangemaakte gebruikers zonder een initiële status.",
|
||||
"LOGINV2": "Inloggen V2",
|
||||
"LOGINV2_DESCRIPTION": "Door dit in te schakelen wordt de nieuwe TypeScript-gebaseerde login-UI geactiveerd met verbeterde beveiliging, prestaties en aanpasbaarheid.",
|
||||
"LOGINV2_BASEURI": "Basis-URI"
|
||||
"LOGINV2_BASEURI": "Basis-URI",
|
||||
"ENABLERELATIONALTABLES": "Relationele tabellen",
|
||||
"ENABLERELATIONALTABLES_DESCRIPTION": "Schakel dit in om relationele tabellen te gebruiken in plaats van projecties voor het opslaan van gegevens. Momenteel doet deze schakelaar niets."
|
||||
},
|
||||
"DIALOG": {
|
||||
"RESET": {
|
||||
|
||||
@@ -1653,7 +1653,9 @@
|
||||
"CONSOLEUSEV2USERAPI_DESCRIPTION": "Gdy ta flaga jest włączona, konsola używa API V2 User do tworzenia nowych użytkowników. W przypadku API V2 nowo utworzeni użytkownicy rozpoczynają bez stanu początkowego.",
|
||||
"LOGINV2": "Logowanie V2",
|
||||
"LOGINV2_DESCRIPTION": "Włączenie tej opcji aktywuje nowy interfejs logowania oparty na TypeScript z ulepszonym bezpieczeństwem, wydajnością i możliwością dostosowania.",
|
||||
"LOGINV2_BASEURI": "Podstawowy URI"
|
||||
"LOGINV2_BASEURI": "Podstawowy URI",
|
||||
"ENABLERELATIONALTABLES": "Tabele relacyjne",
|
||||
"ENABLERELATIONALTABLES_DESCRIPTION": "Włącz to, aby używać tabel relacyjnych zamiast projekcji do przechowywania danych. Obecnie ten przełącznik nic nie robi."
|
||||
},
|
||||
"DIALOG": {
|
||||
"RESET": {
|
||||
|
||||
@@ -1655,7 +1655,9 @@
|
||||
"CONSOLEUSEV2USERAPI_DESCRIPTION": "Quando esta opção está ativada, o console utiliza a API V2 de Usuários para criar novos usuários. Com a API V2, os novos usuários criados começam sem um estado inicial.",
|
||||
"LOGINV2": "Login V2",
|
||||
"LOGINV2_DESCRIPTION": "Ativar esta opção ativa a nova interface de login baseada em TypeScript, com melhorias na segurança, desempenho e personalização.",
|
||||
"LOGINV2_BASEURI": "URI base"
|
||||
"LOGINV2_BASEURI": "URI base",
|
||||
"ENABLERELATIONALTABLES": "Tabelas relacionais",
|
||||
"ENABLERELATIONALTABLES_DESCRIPTION": "Ative isso para usar tabelas relacionais em vez de projeções para armazenar dados. Atualmente, este interruptor não faz nada."
|
||||
},
|
||||
"DIALOG": {
|
||||
"RESET": {
|
||||
|
||||
@@ -1652,7 +1652,9 @@
|
||||
"CONSOLEUSEV2USERAPI_DESCRIPTION": "Când acest indicator este activat, consola utilizează API-ul de utilizator V2 pentru a crea utilizatori noi. Cu API-ul V2, utilizatorii nou creați încep fără o stare inițială.",
|
||||
"LOGINV2": "Autentificare V2",
|
||||
"LOGINV2_DESCRIPTION": "Activarea acestei opțiuni pornește noua interfață de autentificare bazată pe TypeScript, cu securitate, performanță și personalizare îmbunătățite.",
|
||||
"LOGINV2_BASEURI": "URI de bază"
|
||||
"LOGINV2_BASEURI": "URI de bază",
|
||||
"ENABLERELATIONALTABLES": "Tabele relaționale",
|
||||
"ENABLERELATIONALTABLES_DESCRIPTION": "Activați acest lucru pentru a utiliza tabele relaționale în loc de proiecții pentru stocarea datelor. În prezent, acest comutator nu face nimic."
|
||||
},
|
||||
"DIALOG": {
|
||||
"RESET": {
|
||||
|
||||
@@ -1707,7 +1707,9 @@
|
||||
"CONSOLEUSEV2USERAPI_DESCRIPTION": "Когда этот флаг включен, консоль использует V2 User API для создания новых пользователей. С API V2 новые пользователи создаются без начального состояния.",
|
||||
"LOGINV2": "Вход V2",
|
||||
"LOGINV2_DESCRIPTION": "Включение этой опции активирует новый интерфейс входа на основе TypeScript с улучшенной безопасностью, производительностью и возможностью настройки.",
|
||||
"LOGINV2_BASEURI": "Базовый URI"
|
||||
"LOGINV2_BASEURI": "Базовый URI",
|
||||
"ENABLERELATIONALTABLES": "Реляционные таблицы",
|
||||
"ENABLERELATIONALTABLES_DESCRIPTION": "Включите это, чтобы использовать реляционные таблицы вместо проекций для хранения данных. В настоящее время этот переключатель ничего не делает."
|
||||
},
|
||||
"DIALOG": {
|
||||
"RESET": {
|
||||
|
||||
@@ -1658,7 +1658,9 @@
|
||||
"CONSOLEUSEV2USERAPI_DESCRIPTION": "När denna flagga är aktiverad använder konsolen V2 User API för att skapa nya användare. Med V2 API startar nyligen skapade användare utan ett initialt tillstånd.",
|
||||
"LOGINV2": "Inloggning V2",
|
||||
"LOGINV2_DESCRIPTION": "Att aktivera detta startar det nya inloggningsgränssnittet baserat på TypeScript med förbättrad säkerhet, prestanda och anpassning.",
|
||||
"LOGINV2_BASEURI": "Bas-URI"
|
||||
"LOGINV2_BASEURI": "Bas-URI",
|
||||
"ENABLERELATIONALTABLES": "Relationella tabeller",
|
||||
"ENABLERELATIONALTABLES_DESCRIPTION": "Aktivera detta för att använda relationella tabeller istället för projektioner för att lagra data. För närvarande gör denna växel ingenting."
|
||||
},
|
||||
"DIALOG": {
|
||||
"RESET": {
|
||||
|
||||
@@ -1657,7 +1657,9 @@
|
||||
"CONSOLEUSEV2USERAPI_DESCRIPTION": "Bu bayrak etkinleştirildiğinde, konsol yeni kullanıcılar oluşturmak için V2 Kullanıcı API'sini kullanır. V2 API ile yeni oluşturulan kullanıcılar başlangıç durumu olmadan başlar.",
|
||||
"LOGINV2": "Giriş V2",
|
||||
"LOGINV2_DESCRIPTION": "Bunu etkinleştirmek, gelişmiş güvenlik, performans ve özelleştirme ile yeni TypeScript tabanlı giriş UI'ını etkinleştirir.",
|
||||
"LOGINV2_BASEURI": "Temel URI"
|
||||
"LOGINV2_BASEURI": "Temel URI",
|
||||
"ENABLERELATIONALTABLES": "İlişkisel Tablolar",
|
||||
"ENABLERELATIONALTABLES_DESCRIPTION": "Veri depolamak için projeksiyonlar yerine ilişkisel tabloları kullanmak için bunu etkinleştirin. Şu anda bu anahtar hiçbir şey yapmıyor."
|
||||
},
|
||||
"DIALOG": {
|
||||
"RESET": {
|
||||
|
||||
@@ -1657,7 +1657,9 @@
|
||||
"CONSOLEUSEV2USERAPI_DESCRIPTION": "启用此标志时,控制台使用V2用户API创建新用户。使用V2 API,新创建的用户将以无初始状态开始。",
|
||||
"LOGINV2": "登录 V2",
|
||||
"LOGINV2_DESCRIPTION": "启用此选项将激活基于 TypeScript 的新登录界面,具有更高的安全性、性能和可定制性。",
|
||||
"LOGINV2_BASEURI": "基础 URI"
|
||||
"LOGINV2_BASEURI": "基础 URI",
|
||||
"ENABLERELATIONALTABLES": "关系表",
|
||||
"ENABLERELATIONALTABLES_DESCRIPTION": "启用此功能以使用关系表而不是投影来存储数据。目前此切换没有任何作用。"
|
||||
},
|
||||
"DIALOG": {
|
||||
"RESET": {
|
||||
|
||||
@@ -26,6 +26,7 @@ func systemFeaturesToCommand(req *feature_pb.SetSystemFeaturesRequest) (*command
|
||||
EnableBackChannelLogout: req.EnableBackChannelLogout,
|
||||
LoginV2: loginV2,
|
||||
PermissionCheckV2: req.PermissionCheckV2,
|
||||
EnableRelationalTables: req.EnableRelationalTables,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -40,6 +41,7 @@ func systemFeaturesToPb(f *query.SystemFeatures) *feature_pb.GetSystemFeaturesRe
|
||||
EnableBackChannelLogout: featureSourceToFlagPb(&f.EnableBackChannelLogout),
|
||||
LoginV2: loginV2ToLoginV2FlagPb(f.LoginV2),
|
||||
PermissionCheckV2: featureSourceToFlagPb(&f.PermissionCheckV2),
|
||||
EnableRelationalTables: featureSourceToFlagPb(&f.EnableRelationalTables),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +61,7 @@ func instanceFeaturesToCommand(req *feature_pb.SetInstanceFeaturesRequest) (*com
|
||||
LoginV2: loginV2,
|
||||
PermissionCheckV2: req.PermissionCheckV2,
|
||||
ConsoleUseV2UserApi: req.ConsoleUseV2UserApi,
|
||||
EnableRelationalTables: req.EnableRelationalTables,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -75,6 +78,7 @@ func instanceFeaturesToPb(f *query.InstanceFeatures) *feature_pb.GetInstanceFeat
|
||||
LoginV2: loginV2ToLoginV2FlagPb(f.LoginV2),
|
||||
PermissionCheckV2: featureSourceToFlagPb(&f.PermissionCheckV2),
|
||||
ConsoleUseV2UserApi: featureSourceToFlagPb(&f.ConsoleUseV2UserApi),
|
||||
EnableRelationalTables: featureSourceToFlagPb(&f.EnableRelationalTables),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ import (
|
||||
)
|
||||
|
||||
func Test_systemFeaturesToCommand(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Given
|
||||
arg := &feature_pb.SetSystemFeaturesRequest{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
UserSchema: gu.Ptr(true),
|
||||
@@ -28,6 +30,9 @@ func Test_systemFeaturesToCommand(t *testing.T) {
|
||||
Required: true,
|
||||
BaseUri: gu.Ptr("https://login.com"),
|
||||
},
|
||||
EnableRelationalTables: gu.Ptr(true),
|
||||
EnableBackChannelLogout: gu.Ptr(true),
|
||||
PermissionCheckV2: gu.Ptr(true),
|
||||
}
|
||||
want := &command.SystemFeatures{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
@@ -39,13 +44,23 @@ func Test_systemFeaturesToCommand(t *testing.T) {
|
||||
Required: true,
|
||||
BaseURI: &url.URL{Scheme: "https", Host: "login.com"},
|
||||
},
|
||||
EnableRelationalTables: gu.Ptr(true),
|
||||
EnableBackChannelLogout: gu.Ptr(true),
|
||||
PermissionCheckV2: gu.Ptr(true),
|
||||
}
|
||||
|
||||
// Test
|
||||
got, err := systemFeaturesToCommand(arg)
|
||||
|
||||
// Verify
|
||||
assert.Equal(t, want, got)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_systemFeaturesToPb(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Given
|
||||
arg := &query.SystemFeatures{
|
||||
Details: &domain.ObjectDetails{
|
||||
Sequence: 22,
|
||||
@@ -87,6 +102,10 @@ func Test_systemFeaturesToPb(t *testing.T) {
|
||||
Level: feature.LevelSystem,
|
||||
Value: true,
|
||||
},
|
||||
EnableRelationalTables: query.FeatureSource[bool]{
|
||||
Level: feature.LevelSystem,
|
||||
Value: true,
|
||||
},
|
||||
}
|
||||
want := &feature_pb.GetSystemFeaturesResponse{
|
||||
Details: &object.Details{
|
||||
@@ -127,12 +146,22 @@ func Test_systemFeaturesToPb(t *testing.T) {
|
||||
Enabled: true,
|
||||
Source: feature_pb.Source_SOURCE_SYSTEM,
|
||||
},
|
||||
EnableRelationalTables: &feature_pb.FeatureFlag{
|
||||
Enabled: true,
|
||||
Source: feature_pb.Source_SOURCE_SYSTEM,
|
||||
},
|
||||
}
|
||||
|
||||
// Test
|
||||
got := systemFeaturesToPb(arg)
|
||||
|
||||
// Verify
|
||||
assert.Equal(t, want, got)
|
||||
}
|
||||
|
||||
func Test_instanceFeaturesToCommand(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Given
|
||||
arg := &feature_pb.SetInstanceFeaturesRequest{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
UserSchema: gu.Ptr(true),
|
||||
@@ -145,7 +174,9 @@ func Test_instanceFeaturesToCommand(t *testing.T) {
|
||||
Required: true,
|
||||
BaseUri: gu.Ptr("https://login.com"),
|
||||
},
|
||||
ConsoleUseV2UserApi: gu.Ptr(true),
|
||||
ConsoleUseV2UserApi: gu.Ptr(true),
|
||||
PermissionCheckV2: gu.Ptr(false),
|
||||
EnableRelationalTables: gu.Ptr(true),
|
||||
}
|
||||
want := &command.InstanceFeatures{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
@@ -159,14 +190,22 @@ func Test_instanceFeaturesToCommand(t *testing.T) {
|
||||
Required: true,
|
||||
BaseURI: &url.URL{Scheme: "https", Host: "login.com"},
|
||||
},
|
||||
ConsoleUseV2UserApi: gu.Ptr(true),
|
||||
ConsoleUseV2UserApi: gu.Ptr(true),
|
||||
PermissionCheckV2: gu.Ptr(false),
|
||||
EnableRelationalTables: gu.Ptr(true),
|
||||
}
|
||||
|
||||
// Test
|
||||
got, err := instanceFeaturesToCommand(arg)
|
||||
|
||||
// Verify
|
||||
assert.Equal(t, want, got)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_instanceFeaturesToPb(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
arg := &query.InstanceFeatures{
|
||||
Details: &domain.ObjectDetails{
|
||||
Sequence: 22,
|
||||
@@ -212,6 +251,10 @@ func Test_instanceFeaturesToPb(t *testing.T) {
|
||||
Level: feature.LevelInstance,
|
||||
Value: true,
|
||||
},
|
||||
EnableRelationalTables: query.FeatureSource[bool]{
|
||||
Level: feature.LevelInstance,
|
||||
Value: true,
|
||||
},
|
||||
}
|
||||
want := &feature_pb.GetInstanceFeaturesResponse{
|
||||
Details: &object.Details{
|
||||
@@ -260,6 +303,10 @@ func Test_instanceFeaturesToPb(t *testing.T) {
|
||||
Enabled: true,
|
||||
Source: feature_pb.Source_SOURCE_INSTANCE,
|
||||
},
|
||||
EnableRelationalTables: &feature_pb.FeatureFlag{
|
||||
Enabled: true,
|
||||
Source: feature_pb.Source_SOURCE_INSTANCE,
|
||||
},
|
||||
}
|
||||
got := instanceFeaturesToPb(arg)
|
||||
assert.Equal(t, want, got)
|
||||
|
||||
@@ -76,7 +76,8 @@ func TestServer_SetSystemFeatures(t *testing.T) {
|
||||
args: args{
|
||||
ctx: SystemCTX,
|
||||
req: &feature.SetSystemFeaturesRequest{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
EnableRelationalTables: gu.Ptr(true),
|
||||
},
|
||||
},
|
||||
want: &feature.SetSystemFeaturesResponse{
|
||||
@@ -170,8 +171,9 @@ func TestServer_GetSystemFeatures(t *testing.T) {
|
||||
name: "some features",
|
||||
prepare: func(t *testing.T) {
|
||||
_, err := Client.SetSystemFeatures(SystemCTX, &feature.SetSystemFeaturesRequest{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
UserSchema: gu.Ptr(false),
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
UserSchema: gu.Ptr(false),
|
||||
EnableRelationalTables: gu.Ptr(true),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
},
|
||||
@@ -188,6 +190,10 @@ func TestServer_GetSystemFeatures(t *testing.T) {
|
||||
Enabled: false,
|
||||
Source: feature.Source_SOURCE_SYSTEM,
|
||||
},
|
||||
EnableRelationalTables: &feature.FeatureFlag{
|
||||
Enabled: true,
|
||||
Source: feature.Source_SOURCE_SYSTEM,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -209,6 +215,7 @@ func TestServer_GetSystemFeatures(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assertFeatureFlag(t, tt.want.LoginDefaultOrg, got.LoginDefaultOrg)
|
||||
assertFeatureFlag(t, tt.want.UserSchema, got.UserSchema)
|
||||
assertFeatureFlag(t, tt.want.EnableRelationalTables, got.EnableRelationalTables)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -247,7 +254,8 @@ func TestServer_SetInstanceFeatures(t *testing.T) {
|
||||
args: args{
|
||||
ctx: IamCTX,
|
||||
req: &feature.SetInstanceFeaturesRequest{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
EnableRelationalTables: gu.Ptr(true),
|
||||
},
|
||||
},
|
||||
want: &feature.SetInstanceFeaturesResponse{
|
||||
@@ -363,14 +371,19 @@ func TestServer_GetInstanceFeatures(t *testing.T) {
|
||||
Enabled: false,
|
||||
Source: feature.Source_SOURCE_UNSPECIFIED,
|
||||
},
|
||||
EnableRelationalTables: &feature.FeatureFlag{
|
||||
Enabled: false,
|
||||
Source: feature.Source_SOURCE_UNSPECIFIED,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "some features, no inheritance",
|
||||
prepare: func(t *testing.T) {
|
||||
_, err := Client.SetInstanceFeatures(IamCTX, &feature.SetInstanceFeaturesRequest{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
UserSchema: gu.Ptr(true),
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
UserSchema: gu.Ptr(true),
|
||||
EnableRelationalTables: gu.Ptr(true),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
},
|
||||
@@ -387,6 +400,10 @@ func TestServer_GetInstanceFeatures(t *testing.T) {
|
||||
Enabled: true,
|
||||
Source: feature.Source_SOURCE_INSTANCE,
|
||||
},
|
||||
EnableRelationalTables: &feature.FeatureFlag{
|
||||
Enabled: true,
|
||||
Source: feature.Source_SOURCE_INSTANCE,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -412,6 +429,10 @@ func TestServer_GetInstanceFeatures(t *testing.T) {
|
||||
Enabled: false,
|
||||
Source: feature.Source_SOURCE_UNSPECIFIED,
|
||||
},
|
||||
EnableRelationalTables: &feature.FeatureFlag{
|
||||
Enabled: false,
|
||||
Source: feature.Source_SOURCE_UNSPECIFIED,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -433,6 +454,7 @@ func TestServer_GetInstanceFeatures(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assertFeatureFlag(t, tt.want.LoginDefaultOrg, got.LoginDefaultOrg)
|
||||
assertFeatureFlag(t, tt.want.UserSchema, got.UserSchema)
|
||||
assertFeatureFlag(t, tt.want.EnableRelationalTables, got.EnableRelationalTables)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +141,7 @@ type InstanceSetupFeatures struct {
|
||||
LoginV2 *InstanceSetupFeatureLoginV2
|
||||
PermissionCheckV2 *bool
|
||||
ConsoleUseV2UserApi *bool
|
||||
EnableRelationalTables *bool
|
||||
}
|
||||
|
||||
type InstanceSetupFeatureLoginV2 struct {
|
||||
@@ -174,6 +175,7 @@ func (f *InstanceSetupFeatures) ToInstanceFeatures() (_ *InstanceFeatures, err e
|
||||
LoginV2: loginV2,
|
||||
PermissionCheckV2: f.PermissionCheckV2,
|
||||
ConsoleUseV2UserApi: f.ConsoleUseV2UserApi,
|
||||
EnableRelationalTables: f.EnableRelationalTables,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -23,10 +23,11 @@ type InstanceFeatures struct {
|
||||
LoginV2 *feature.LoginV2
|
||||
PermissionCheckV2 *bool
|
||||
ConsoleUseV2UserApi *bool
|
||||
EnableRelationalTables *bool
|
||||
}
|
||||
|
||||
func (m *InstanceFeatures) isEmpty() bool {
|
||||
return m.LoginDefaultOrg == nil &&
|
||||
return m == nil || (m.LoginDefaultOrg == nil &&
|
||||
m.UserSchema == nil &&
|
||||
m.TokenExchange == nil &&
|
||||
// nil check to allow unset improvements
|
||||
@@ -35,7 +36,9 @@ func (m *InstanceFeatures) isEmpty() bool {
|
||||
m.OIDCSingleV1SessionTermination == nil &&
|
||||
m.EnableBackChannelLogout == nil &&
|
||||
m.LoginV2 == nil &&
|
||||
m.PermissionCheckV2 == nil && m.ConsoleUseV2UserApi == nil
|
||||
m.PermissionCheckV2 == nil &&
|
||||
m.ConsoleUseV2UserApi == nil &&
|
||||
m.EnableRelationalTables == nil)
|
||||
}
|
||||
|
||||
func (c *Commands) SetInstanceFeatures(ctx context.Context, f *InstanceFeatures) (*domain.ObjectDetails, error) {
|
||||
|
||||
@@ -76,6 +76,7 @@ func (m *InstanceFeaturesWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
feature_v2.InstanceLoginVersion,
|
||||
feature_v2.InstancePermissionCheckV2,
|
||||
feature_v2.InstanceConsoleUseV2UserApi,
|
||||
feature_v2.InstanceEnableRelationalTables,
|
||||
).
|
||||
Builder().ResourceOwner(m.ResourceOwner)
|
||||
}
|
||||
@@ -117,6 +118,9 @@ func reduceInstanceFeature(features *InstanceFeatures, key feature.Key, value an
|
||||
case feature.KeyConsoleUseV2UserApi:
|
||||
v := value.(bool)
|
||||
features.ConsoleUseV2UserApi = &v
|
||||
case feature.KeyEnableRelationalTables:
|
||||
v := value.(bool)
|
||||
features.EnableRelationalTables = &v
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,5 +137,6 @@ func (wm *InstanceFeaturesWriteModel) setCommands(ctx context.Context, f *Instan
|
||||
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.LoginV2, f.LoginV2, feature_v2.InstanceLoginVersion)
|
||||
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.PermissionCheckV2, f.PermissionCheckV2, feature_v2.InstancePermissionCheckV2)
|
||||
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.ConsoleUseV2UserApi, f.ConsoleUseV2UserApi, feature_v2.InstanceConsoleUseV2UserApi)
|
||||
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.EnableRelationalTables, f.EnableRelationalTables, feature_v2.InstanceEnableRelationalTables)
|
||||
return cmds
|
||||
}
|
||||
|
||||
144
internal/command/instance_features_model_test.go
Normal file
144
internal/command/instance_features_model_test.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/feature"
|
||||
)
|
||||
|
||||
func Test_reduceInstanceFeature(t *testing.T) {
|
||||
type args struct {
|
||||
features *InstanceFeatures
|
||||
key feature.Key
|
||||
value any
|
||||
}
|
||||
tt := []struct {
|
||||
name string
|
||||
args args
|
||||
expected *InstanceFeatures
|
||||
}{
|
||||
{
|
||||
name: "key unspecified",
|
||||
args: args{
|
||||
features: &InstanceFeatures{},
|
||||
key: feature.KeyUnspecified,
|
||||
value: true,
|
||||
},
|
||||
expected: &InstanceFeatures{},
|
||||
},
|
||||
{
|
||||
name: "login default org",
|
||||
args: args{
|
||||
features: &InstanceFeatures{},
|
||||
key: feature.KeyLoginDefaultOrg,
|
||||
value: true,
|
||||
},
|
||||
expected: &InstanceFeatures{LoginDefaultOrg: gu.Ptr(true)},
|
||||
},
|
||||
{
|
||||
name: "token exchange",
|
||||
args: args{
|
||||
features: &InstanceFeatures{},
|
||||
key: feature.KeyTokenExchange,
|
||||
value: true,
|
||||
},
|
||||
expected: &InstanceFeatures{TokenExchange: gu.Ptr(true)},
|
||||
},
|
||||
{
|
||||
name: "user schema",
|
||||
args: args{
|
||||
features: &InstanceFeatures{},
|
||||
key: feature.KeyUserSchema,
|
||||
value: true,
|
||||
},
|
||||
expected: &InstanceFeatures{UserSchema: gu.Ptr(true)},
|
||||
},
|
||||
{
|
||||
name: "improved performance",
|
||||
args: args{
|
||||
features: &InstanceFeatures{},
|
||||
key: feature.KeyImprovedPerformance,
|
||||
value: []feature.ImprovedPerformanceType{feature.ImprovedPerformanceTypeProject},
|
||||
},
|
||||
expected: &InstanceFeatures{ImprovedPerformance: []feature.ImprovedPerformanceType{feature.ImprovedPerformanceTypeProject}},
|
||||
},
|
||||
{
|
||||
name: "debug OIDC parent error",
|
||||
args: args{
|
||||
features: &InstanceFeatures{},
|
||||
key: feature.KeyDebugOIDCParentError,
|
||||
value: true,
|
||||
},
|
||||
expected: &InstanceFeatures{DebugOIDCParentError: gu.Ptr(true)},
|
||||
},
|
||||
{
|
||||
name: "OIDC single v1 session termination",
|
||||
args: args{
|
||||
features: &InstanceFeatures{},
|
||||
key: feature.KeyOIDCSingleV1SessionTermination,
|
||||
value: true,
|
||||
},
|
||||
expected: &InstanceFeatures{OIDCSingleV1SessionTermination: gu.Ptr(true)},
|
||||
},
|
||||
{
|
||||
name: "enable back channel logout",
|
||||
args: args{
|
||||
features: &InstanceFeatures{},
|
||||
key: feature.KeyEnableBackChannelLogout,
|
||||
value: true,
|
||||
},
|
||||
expected: &InstanceFeatures{EnableBackChannelLogout: gu.Ptr(true)},
|
||||
},
|
||||
{
|
||||
name: "login v2",
|
||||
args: args{
|
||||
features: &InstanceFeatures{},
|
||||
key: feature.KeyLoginV2,
|
||||
value: &feature.LoginV2{},
|
||||
},
|
||||
expected: &InstanceFeatures{LoginV2: &feature.LoginV2{}},
|
||||
},
|
||||
{
|
||||
name: "permission check v2",
|
||||
args: args{
|
||||
features: &InstanceFeatures{},
|
||||
key: feature.KeyPermissionCheckV2,
|
||||
value: true,
|
||||
},
|
||||
expected: &InstanceFeatures{PermissionCheckV2: gu.Ptr(true)},
|
||||
},
|
||||
{
|
||||
name: "console use v2 user api",
|
||||
args: args{
|
||||
features: &InstanceFeatures{},
|
||||
key: feature.KeyConsoleUseV2UserApi,
|
||||
value: true,
|
||||
},
|
||||
expected: &InstanceFeatures{ConsoleUseV2UserApi: gu.Ptr(true)},
|
||||
},
|
||||
{
|
||||
name: "enable relational tables",
|
||||
args: args{
|
||||
features: &InstanceFeatures{},
|
||||
key: feature.KeyEnableRelationalTables,
|
||||
value: true,
|
||||
},
|
||||
expected: &InstanceFeatures{EnableRelationalTables: gu.Ptr(true)},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Test
|
||||
reduceInstanceFeature(tc.args.features, tc.args.key, tc.args.value)
|
||||
|
||||
// Verify
|
||||
assert.Equal(t, tc.expected, tc.args.features)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -12,12 +12,15 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/feature"
|
||||
feature_v1 "github.com/zitadel/zitadel/internal/repository/feature"
|
||||
"github.com/zitadel/zitadel/internal/repository/feature/feature_v2"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := authz.WithInstanceID(context.Background(), "instance1")
|
||||
aggregate := feature_v2.NewAggregate("instance1", "instance1")
|
||||
|
||||
@@ -53,7 +56,7 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
feature_v2.NewSetEvent[bool](
|
||||
feature_v2.NewSetEvent(
|
||||
ctx, aggregate,
|
||||
feature_v2.InstanceLoginDefaultOrgEventType, true,
|
||||
),
|
||||
@@ -70,7 +73,7 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
name: "set LoginDefaultOrg, update from v1",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(feature_v1.NewSetEvent[feature_v1.Boolean](
|
||||
eventFromEventPusher(feature_v1.NewSetEvent(
|
||||
ctx, &eventstore.Aggregate{
|
||||
ID: "instance1",
|
||||
ResourceOwner: "instance1",
|
||||
@@ -82,7 +85,7 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
)),
|
||||
),
|
||||
expectPush(
|
||||
feature_v2.NewSetEvent[bool](
|
||||
feature_v2.NewSetEvent(
|
||||
ctx, aggregate,
|
||||
feature_v2.InstanceLoginDefaultOrgEventType, true,
|
||||
),
|
||||
@@ -100,7 +103,7 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
feature_v2.NewSetEvent[bool](
|
||||
feature_v2.NewSetEvent(
|
||||
ctx, aggregate,
|
||||
feature_v2.InstanceUserSchemaEventType, true,
|
||||
),
|
||||
@@ -118,7 +121,7 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
expectPushFailed(io.ErrClosedPipe,
|
||||
feature_v2.NewSetEvent[bool](
|
||||
feature_v2.NewSetEvent(
|
||||
ctx, aggregate,
|
||||
feature_v2.InstanceConsoleUseV2UserApi, true,
|
||||
),
|
||||
@@ -134,24 +137,27 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
feature_v2.NewSetEvent[bool](
|
||||
feature_v2.NewSetEvent(
|
||||
ctx, aggregate,
|
||||
feature_v2.InstanceLoginDefaultOrgEventType, true,
|
||||
),
|
||||
feature_v2.NewSetEvent[bool](
|
||||
feature_v2.NewSetEvent(
|
||||
ctx, aggregate,
|
||||
feature_v2.InstanceUserSchemaEventType, true,
|
||||
),
|
||||
feature_v2.NewSetEvent[bool](
|
||||
feature_v2.NewSetEvent(
|
||||
ctx, aggregate,
|
||||
feature_v2.InstanceOIDCSingleV1SessionTerminationEventType, true,
|
||||
),
|
||||
feature_v2.NewSetEvent(ctx, aggregate,
|
||||
feature_v2.InstanceEnableRelationalTables, true),
|
||||
),
|
||||
),
|
||||
args: args{ctx, &InstanceFeatures{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
UserSchema: gu.Ptr(true),
|
||||
OIDCSingleV1SessionTermination: gu.Ptr(true),
|
||||
EnableRelationalTables: gu.Ptr(true),
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance1",
|
||||
@@ -162,7 +168,7 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
eventstore: expectEventstore(
|
||||
// throw in some set events, reset and set again.
|
||||
expectFilter(
|
||||
eventFromEventPusher(feature_v2.NewSetEvent[bool](
|
||||
eventFromEventPusher(feature_v2.NewSetEvent(
|
||||
ctx, aggregate,
|
||||
feature_v2.InstanceLoginDefaultOrgEventType, true,
|
||||
)),
|
||||
@@ -170,17 +176,17 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
ctx, aggregate,
|
||||
feature_v2.InstanceResetEventType,
|
||||
)),
|
||||
eventFromEventPusher(feature_v2.NewSetEvent[bool](
|
||||
eventFromEventPusher(feature_v2.NewSetEvent(
|
||||
ctx, aggregate,
|
||||
feature_v2.InstanceLoginDefaultOrgEventType, false,
|
||||
)),
|
||||
feature_v2.NewSetEvent[bool](
|
||||
feature_v2.NewSetEvent(
|
||||
context.Background(), aggregate,
|
||||
feature_v2.InstanceOIDCSingleV1SessionTerminationEventType, false,
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
feature_v2.NewSetEvent[bool](
|
||||
feature_v2.NewSetEvent(
|
||||
ctx, aggregate,
|
||||
feature_v2.InstanceLoginDefaultOrgEventType, true,
|
||||
),
|
||||
@@ -197,6 +203,8 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
c := &Commands{
|
||||
eventstore: tt.eventstore(t),
|
||||
}
|
||||
@@ -227,7 +235,7 @@ func TestCommands_ResetInstanceFeatures(t *testing.T) {
|
||||
name: "push error",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(feature_v2.NewSetEvent[bool](
|
||||
eventFromEventPusher(feature_v2.NewSetEvent(
|
||||
ctx, aggregate,
|
||||
feature_v2.InstanceLoginDefaultOrgEventType, true,
|
||||
)),
|
||||
@@ -242,7 +250,7 @@ func TestCommands_ResetInstanceFeatures(t *testing.T) {
|
||||
name: "success",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(feature_v2.NewSetEvent[bool](
|
||||
eventFromEventPusher(feature_v2.NewSetEvent(
|
||||
ctx, aggregate,
|
||||
feature_v2.InstanceLoginDefaultOrgEventType, true,
|
||||
)),
|
||||
@@ -259,7 +267,7 @@ func TestCommands_ResetInstanceFeatures(t *testing.T) {
|
||||
name: "no change after previous reset",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(feature_v2.NewSetEvent[bool](
|
||||
eventFromEventPusher(feature_v2.NewSetEvent(
|
||||
ctx, aggregate,
|
||||
feature_v2.InstanceLoginDefaultOrgEventType, true,
|
||||
)),
|
||||
@@ -294,3 +302,71 @@ func TestCommands_ResetInstanceFeatures(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstanceFeatures_isEmpty(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tt := []struct {
|
||||
name string
|
||||
features *InstanceFeatures
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "nil features",
|
||||
features: nil,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "empty features",
|
||||
features: &InstanceFeatures{},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "LoginDefaultOrg set",
|
||||
features: &InstanceFeatures{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "UserSchema set",
|
||||
features: &InstanceFeatures{
|
||||
UserSchema: gu.Ptr(true),
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "TokenExchange set",
|
||||
features: &InstanceFeatures{
|
||||
TokenExchange: gu.Ptr(true),
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "ImprovedPerformance set",
|
||||
features: &InstanceFeatures{
|
||||
ImprovedPerformance: []feature.ImprovedPerformanceType{},
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "multiple fields set",
|
||||
features: &InstanceFeatures{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
UserSchema: gu.Ptr(false),
|
||||
PermissionCheckV2: gu.Ptr(true),
|
||||
EnableRelationalTables: gu.Ptr(true),
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got := tc.features.isEmpty()
|
||||
assert.Equal(t, tc.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,15 @@ package command
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/mock/gomock"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
@@ -18,6 +21,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/feature"
|
||||
"github.com/zitadel/zitadel/internal/id"
|
||||
id_mock "github.com/zitadel/zitadel/internal/id/mock"
|
||||
"github.com/zitadel/zitadel/internal/repository/instance"
|
||||
@@ -1727,3 +1731,126 @@ func TestCommandSide_RemoveInstance(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstanceSetupFeatures_ToInstanceFeatures(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type fields struct {
|
||||
LoginDefaultOrg *bool
|
||||
UserSchema *bool
|
||||
TokenExchange *bool
|
||||
ImprovedPerformance []feature.ImprovedPerformanceType
|
||||
DebugOIDCParentError *bool
|
||||
OIDCSingleV1SessionTermination *bool
|
||||
EnableBackChannelLogout *bool
|
||||
LoginV2 *InstanceSetupFeatureLoginV2
|
||||
PermissionCheckV2 *bool
|
||||
ConsoleUseV2UserApi *bool
|
||||
EnableRelationalTables *bool
|
||||
}
|
||||
|
||||
correctlyParsedURI, err := url.Parse("https://example.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
tt := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want *InstanceFeatures
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "nil features returns nil",
|
||||
fields: fields{},
|
||||
want: &InstanceFeatures{},
|
||||
},
|
||||
{
|
||||
name: "all fields no login v2",
|
||||
fields: fields{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
UserSchema: gu.Ptr(false),
|
||||
TokenExchange: gu.Ptr(true),
|
||||
ImprovedPerformance: []feature.ImprovedPerformanceType{feature.ImprovedPerformanceTypeOrgDomainVerified},
|
||||
DebugOIDCParentError: gu.Ptr(true),
|
||||
OIDCSingleV1SessionTermination: gu.Ptr(false),
|
||||
EnableBackChannelLogout: gu.Ptr(true),
|
||||
PermissionCheckV2: gu.Ptr(true),
|
||||
ConsoleUseV2UserApi: gu.Ptr(false),
|
||||
EnableRelationalTables: gu.Ptr(true),
|
||||
},
|
||||
want: &InstanceFeatures{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
UserSchema: gu.Ptr(false),
|
||||
TokenExchange: gu.Ptr(true),
|
||||
ImprovedPerformance: []feature.ImprovedPerformanceType{feature.ImprovedPerformanceTypeOrgDomainVerified},
|
||||
DebugOIDCParentError: gu.Ptr(true),
|
||||
OIDCSingleV1SessionTermination: gu.Ptr(false),
|
||||
EnableBackChannelLogout: gu.Ptr(true),
|
||||
LoginV2: nil,
|
||||
PermissionCheckV2: gu.Ptr(true),
|
||||
ConsoleUseV2UserApi: gu.Ptr(false),
|
||||
EnableRelationalTables: gu.Ptr(true),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with login v2 no base uri",
|
||||
fields: fields{
|
||||
LoginV2: &InstanceSetupFeatureLoginV2{
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
want: &InstanceFeatures{
|
||||
LoginV2: &feature.LoginV2{
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with login v2 valid base uri",
|
||||
fields: fields{
|
||||
LoginV2: &InstanceSetupFeatureLoginV2{
|
||||
Required: true,
|
||||
BaseURI: gu.Ptr("https://example.com"),
|
||||
},
|
||||
},
|
||||
want: &InstanceFeatures{
|
||||
LoginV2: &feature.LoginV2{
|
||||
Required: true,
|
||||
BaseURI: correctlyParsedURI,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with login v2 invalid base uri",
|
||||
fields: fields{
|
||||
LoginV2: &InstanceSetupFeatureLoginV2{
|
||||
Required: true,
|
||||
BaseURI: gu.Ptr("://invalid"),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
f := &InstanceSetupFeatures{
|
||||
LoginDefaultOrg: tc.fields.LoginDefaultOrg,
|
||||
UserSchema: tc.fields.UserSchema,
|
||||
TokenExchange: tc.fields.TokenExchange,
|
||||
ImprovedPerformance: tc.fields.ImprovedPerformance,
|
||||
DebugOIDCParentError: tc.fields.DebugOIDCParentError,
|
||||
OIDCSingleV1SessionTermination: tc.fields.OIDCSingleV1SessionTermination,
|
||||
EnableBackChannelLogout: tc.fields.EnableBackChannelLogout,
|
||||
LoginV2: tc.fields.LoginV2,
|
||||
PermissionCheckV2: tc.fields.PermissionCheckV2,
|
||||
ConsoleUseV2UserApi: tc.fields.ConsoleUseV2UserApi,
|
||||
EnableRelationalTables: tc.fields.EnableRelationalTables,
|
||||
}
|
||||
got, err := f.ToInstanceFeatures()
|
||||
|
||||
require.Equal(t, tc.wantErr, err != nil)
|
||||
assert.Equal(t, tc.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,10 +18,11 @@ type SystemFeatures struct {
|
||||
EnableBackChannelLogout *bool
|
||||
LoginV2 *feature.LoginV2
|
||||
PermissionCheckV2 *bool
|
||||
EnableRelationalTables *bool
|
||||
}
|
||||
|
||||
func (m *SystemFeatures) isEmpty() bool {
|
||||
return m.LoginDefaultOrg == nil &&
|
||||
return m == nil || (m.LoginDefaultOrg == nil &&
|
||||
m.UserSchema == nil &&
|
||||
m.TokenExchange == nil &&
|
||||
// nil check to allow unset improvements
|
||||
@@ -29,7 +30,8 @@ func (m *SystemFeatures) isEmpty() bool {
|
||||
m.OIDCSingleV1SessionTermination == nil &&
|
||||
m.EnableBackChannelLogout == nil &&
|
||||
m.LoginV2 == nil &&
|
||||
m.PermissionCheckV2 == nil
|
||||
m.PermissionCheckV2 == nil &&
|
||||
m.EnableRelationalTables == nil)
|
||||
}
|
||||
|
||||
func (c *Commands) SetSystemFeatures(ctx context.Context, f *SystemFeatures) (*domain.ObjectDetails, error) {
|
||||
|
||||
@@ -67,6 +67,7 @@ func (m *SystemFeaturesWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
feature_v2.SystemEnableBackChannelLogout,
|
||||
feature_v2.SystemLoginVersion,
|
||||
feature_v2.SystemPermissionCheckV2,
|
||||
feature_v2.SystemEnableRelationalTables,
|
||||
).
|
||||
Builder().ResourceOwner(m.ResourceOwner)
|
||||
}
|
||||
@@ -101,6 +102,9 @@ func reduceSystemFeature(features *SystemFeatures, key feature.Key, value any) {
|
||||
case feature.KeyPermissionCheckV2:
|
||||
v := value.(bool)
|
||||
features.PermissionCheckV2 = &v
|
||||
case feature.KeyEnableRelationalTables:
|
||||
v := value.(bool)
|
||||
features.EnableRelationalTables = &v
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,23 +119,24 @@ func (wm *SystemFeaturesWriteModel) setCommands(ctx context.Context, f *SystemFe
|
||||
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.EnableBackChannelLogout, f.EnableBackChannelLogout, feature_v2.SystemEnableBackChannelLogout)
|
||||
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.LoginV2, f.LoginV2, feature_v2.SystemLoginVersion)
|
||||
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.PermissionCheckV2, f.PermissionCheckV2, feature_v2.SystemPermissionCheckV2)
|
||||
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.EnableRelationalTables, f.EnableRelationalTables, feature_v2.SystemEnableRelationalTables)
|
||||
return cmds
|
||||
}
|
||||
|
||||
func appendFeatureUpdate[T comparable](ctx context.Context, cmds []eventstore.Command, aggregate *feature_v2.Aggregate, oldValue, newValue *T, eventType eventstore.EventType) []eventstore.Command {
|
||||
if newValue != nil && (oldValue == nil || *oldValue != *newValue) {
|
||||
cmds = append(cmds, feature_v2.NewSetEvent[T](ctx, aggregate, eventType, *newValue))
|
||||
cmds = append(cmds, feature_v2.NewSetEvent(ctx, aggregate, eventType, *newValue))
|
||||
}
|
||||
return cmds
|
||||
}
|
||||
|
||||
func appendFeatureSliceUpdate[T comparable](ctx context.Context, cmds []eventstore.Command, aggregate *feature_v2.Aggregate, oldValues, newValues []T, eventType eventstore.EventType) []eventstore.Command {
|
||||
if len(newValues) != len(oldValues) {
|
||||
return append(cmds, feature_v2.NewSetEvent[[]T](ctx, aggregate, eventType, newValues))
|
||||
return append(cmds, feature_v2.NewSetEvent(ctx, aggregate, eventType, newValues))
|
||||
}
|
||||
for i, oldValue := range oldValues {
|
||||
if oldValue != newValues[i] {
|
||||
return append(cmds, feature_v2.NewSetEvent[[]T](ctx, aggregate, eventType, newValues))
|
||||
return append(cmds, feature_v2.NewSetEvent(ctx, aggregate, eventType, newValues))
|
||||
}
|
||||
}
|
||||
return cmds
|
||||
|
||||
@@ -11,11 +11,14 @@ import (
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/feature"
|
||||
"github.com/zitadel/zitadel/internal/repository/feature/feature_v2"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
func TestCommands_SetSystemFeatures(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
aggregate := feature_v2.NewAggregate("SYSTEM", "SYSTEM")
|
||||
|
||||
type args struct {
|
||||
@@ -50,7 +53,7 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
feature_v2.NewSetEvent[bool](
|
||||
feature_v2.NewSetEvent(
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemLoginDefaultOrgEventType, true,
|
||||
),
|
||||
@@ -68,7 +71,7 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
feature_v2.NewSetEvent[bool](
|
||||
feature_v2.NewSetEvent(
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemUserSchemaEventType, true,
|
||||
),
|
||||
@@ -86,7 +89,7 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
expectPushFailed(io.ErrClosedPipe,
|
||||
feature_v2.NewSetEvent[bool](
|
||||
feature_v2.NewSetEvent(
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemEnableBackChannelLogout, true,
|
||||
),
|
||||
@@ -102,24 +105,29 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
feature_v2.NewSetEvent[bool](
|
||||
feature_v2.NewSetEvent(
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemLoginDefaultOrgEventType, true,
|
||||
),
|
||||
feature_v2.NewSetEvent[bool](
|
||||
feature_v2.NewSetEvent(
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemUserSchemaEventType, true,
|
||||
),
|
||||
feature_v2.NewSetEvent[bool](
|
||||
feature_v2.NewSetEvent(
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemOIDCSingleV1SessionTerminationEventType, true,
|
||||
),
|
||||
feature_v2.NewSetEvent(
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemEnableRelationalTables, true,
|
||||
),
|
||||
),
|
||||
),
|
||||
args: args{context.Background(), &SystemFeatures{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
UserSchema: gu.Ptr(true),
|
||||
OIDCSingleV1SessionTermination: gu.Ptr(true),
|
||||
EnableRelationalTables: gu.Ptr(true),
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "SYSTEM",
|
||||
@@ -130,7 +138,7 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
|
||||
eventstore: expectEventstore(
|
||||
// throw in some set events, reset and set again.
|
||||
expectFilter(
|
||||
eventFromEventPusher(feature_v2.NewSetEvent[bool](
|
||||
eventFromEventPusher(feature_v2.NewSetEvent(
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemLoginDefaultOrgEventType, true,
|
||||
)),
|
||||
@@ -138,21 +146,21 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemResetEventType,
|
||||
)),
|
||||
eventFromEventPusher(feature_v2.NewSetEvent[bool](
|
||||
eventFromEventPusher(feature_v2.NewSetEvent(
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemLoginDefaultOrgEventType, false,
|
||||
)),
|
||||
),
|
||||
expectPush(
|
||||
feature_v2.NewSetEvent[bool](
|
||||
feature_v2.NewSetEvent(
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemLoginDefaultOrgEventType, true,
|
||||
),
|
||||
feature_v2.NewSetEvent[bool](
|
||||
feature_v2.NewSetEvent(
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemUserSchemaEventType, true,
|
||||
),
|
||||
feature_v2.NewSetEvent[bool](
|
||||
feature_v2.NewSetEvent(
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemOIDCSingleV1SessionTerminationEventType, false,
|
||||
),
|
||||
@@ -170,6 +178,8 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
c := &Commands{
|
||||
eventstore: tt.eventstore(t),
|
||||
}
|
||||
@@ -199,7 +209,7 @@ func TestCommands_ResetSystemFeatures(t *testing.T) {
|
||||
name: "push error",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(feature_v2.NewSetEvent[bool](
|
||||
eventFromEventPusher(feature_v2.NewSetEvent(
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemLoginDefaultOrgEventType, true,
|
||||
)),
|
||||
@@ -214,7 +224,7 @@ func TestCommands_ResetSystemFeatures(t *testing.T) {
|
||||
name: "success",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(feature_v2.NewSetEvent[bool](
|
||||
eventFromEventPusher(feature_v2.NewSetEvent(
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemLoginDefaultOrgEventType, true,
|
||||
)),
|
||||
@@ -231,7 +241,7 @@ func TestCommands_ResetSystemFeatures(t *testing.T) {
|
||||
name: "no change after previous reset",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(feature_v2.NewSetEvent[bool](
|
||||
eventFromEventPusher(feature_v2.NewSetEvent(
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemLoginDefaultOrgEventType, true,
|
||||
)),
|
||||
@@ -266,3 +276,32 @@ func TestCommands_ResetSystemFeatures(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystemFeatures_isEmpty(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
sysFeatures *SystemFeatures
|
||||
want bool
|
||||
}{
|
||||
{name: "nil features", sysFeatures: nil, want: true},
|
||||
{name: "empty features", sysFeatures: &SystemFeatures{}, want: true},
|
||||
{name: "LoginDefaultOrg set", sysFeatures: &SystemFeatures{LoginDefaultOrg: gu.Ptr(true)}, want: false},
|
||||
{name: "UserSchema set", sysFeatures: &SystemFeatures{UserSchema: gu.Ptr(true)}, want: false},
|
||||
{name: "TokenExchange set", sysFeatures: &SystemFeatures{TokenExchange: gu.Ptr(true)}, want: false},
|
||||
{name: "ImprovedPerformance set", sysFeatures: &SystemFeatures{ImprovedPerformance: []feature.ImprovedPerformanceType{}}, want: false},
|
||||
{name: "OIDCSingleV1SessionTermination set", sysFeatures: &SystemFeatures{OIDCSingleV1SessionTermination: gu.Ptr(true)}, want: false},
|
||||
{name: "EnableBackChannelLogout set", sysFeatures: &SystemFeatures{EnableBackChannelLogout: gu.Ptr(true)}, want: false},
|
||||
{name: "LoginV2 set", sysFeatures: &SystemFeatures{LoginV2: &feature.LoginV2{}}, want: false},
|
||||
{name: "PermissionCheckV2 set", sysFeatures: &SystemFeatures{PermissionCheckV2: gu.Ptr(true)}, want: false},
|
||||
{name: "EnableRelationalTables set", sysFeatures: &SystemFeatures{EnableRelationalTables: gu.Ptr(true)}, want: false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
got := tt.sysFeatures.isEmpty()
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ const (
|
||||
KeyLoginV2 Key = 13
|
||||
KeyPermissionCheckV2 Key = 14
|
||||
KeyConsoleUseV2UserApi Key = 15
|
||||
KeyEnableRelationalTables Key = 16
|
||||
)
|
||||
|
||||
//go:generate enumer -type Level -transform snake -trimprefix Level
|
||||
@@ -49,6 +50,7 @@ type Features struct {
|
||||
LoginV2 LoginV2 `json:"login_v2,omitempty"`
|
||||
PermissionCheckV2 bool `json:"permission_check_v2,omitempty"`
|
||||
ConsoleUseV2UserApi bool `json:"console_use_v2_user_api,omitempty"`
|
||||
EnableRelationalTables bool `json:"enable_relational_tables,omitempty"`
|
||||
}
|
||||
|
||||
/* Note: do not generate the stringer or enumer for this type, is it breaks existing events */
|
||||
|
||||
@@ -16,8 +16,8 @@ const (
|
||||
_KeyLowerName_2 = "improved_performance"
|
||||
_KeyName_3 = "debug_oidc_parent_erroroidc_single_v1_session_termination"
|
||||
_KeyLowerName_3 = "debug_oidc_parent_erroroidc_single_v1_session_termination"
|
||||
_KeyName_4 = "enable_back_channel_logoutlogin_v2permission_check_v2console_use_v2_user_api"
|
||||
_KeyLowerName_4 = "enable_back_channel_logoutlogin_v2permission_check_v2console_use_v2_user_api"
|
||||
_KeyName_4 = "enable_back_channel_logoutlogin_v2permission_check_v2console_use_v2_user_apienable_relational_tables"
|
||||
_KeyLowerName_4 = "enable_back_channel_logoutlogin_v2permission_check_v2console_use_v2_user_apienable_relational_tables"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -25,7 +25,7 @@ var (
|
||||
_KeyIndex_1 = [...]uint8{0, 11, 25}
|
||||
_KeyIndex_2 = [...]uint8{0, 20}
|
||||
_KeyIndex_3 = [...]uint8{0, 23, 57}
|
||||
_KeyIndex_4 = [...]uint8{0, 26, 34, 53, 76}
|
||||
_KeyIndex_4 = [...]uint8{0, 26, 34, 53, 76, 100}
|
||||
)
|
||||
|
||||
func (i Key) String() string {
|
||||
@@ -40,7 +40,7 @@ func (i Key) String() string {
|
||||
case 9 <= i && i <= 10:
|
||||
i -= 9
|
||||
return _KeyName_3[_KeyIndex_3[i]:_KeyIndex_3[i+1]]
|
||||
case 12 <= i && i <= 15:
|
||||
case 12 <= i && i <= 16:
|
||||
i -= 12
|
||||
return _KeyName_4[_KeyIndex_4[i]:_KeyIndex_4[i+1]]
|
||||
default:
|
||||
@@ -63,33 +63,36 @@ func _KeyNoOp() {
|
||||
_ = x[KeyLoginV2-(13)]
|
||||
_ = x[KeyPermissionCheckV2-(14)]
|
||||
_ = x[KeyConsoleUseV2UserApi-(15)]
|
||||
_ = x[KeyEnableRelationalTables-(16)]
|
||||
}
|
||||
|
||||
var _KeyValues = []Key{KeyUnspecified, KeyLoginDefaultOrg, KeyUserSchema, KeyTokenExchange, KeyImprovedPerformance, KeyDebugOIDCParentError, KeyOIDCSingleV1SessionTermination, KeyEnableBackChannelLogout, KeyLoginV2, KeyPermissionCheckV2, KeyConsoleUseV2UserApi}
|
||||
var _KeyValues = []Key{KeyUnspecified, KeyLoginDefaultOrg, KeyUserSchema, KeyTokenExchange, KeyImprovedPerformance, KeyDebugOIDCParentError, KeyOIDCSingleV1SessionTermination, KeyEnableBackChannelLogout, KeyLoginV2, KeyPermissionCheckV2, KeyConsoleUseV2UserApi, KeyEnableRelationalTables}
|
||||
|
||||
var _KeyNameToValueMap = map[string]Key{
|
||||
_KeyName_0[0:11]: KeyUnspecified,
|
||||
_KeyLowerName_0[0:11]: KeyUnspecified,
|
||||
_KeyName_0[11:28]: KeyLoginDefaultOrg,
|
||||
_KeyLowerName_0[11:28]: KeyLoginDefaultOrg,
|
||||
_KeyName_1[0:11]: KeyUserSchema,
|
||||
_KeyLowerName_1[0:11]: KeyUserSchema,
|
||||
_KeyName_1[11:25]: KeyTokenExchange,
|
||||
_KeyLowerName_1[11:25]: KeyTokenExchange,
|
||||
_KeyName_2[0:20]: KeyImprovedPerformance,
|
||||
_KeyLowerName_2[0:20]: KeyImprovedPerformance,
|
||||
_KeyName_3[0:23]: KeyDebugOIDCParentError,
|
||||
_KeyLowerName_3[0:23]: KeyDebugOIDCParentError,
|
||||
_KeyName_3[23:57]: KeyOIDCSingleV1SessionTermination,
|
||||
_KeyLowerName_3[23:57]: KeyOIDCSingleV1SessionTermination,
|
||||
_KeyName_4[0:26]: KeyEnableBackChannelLogout,
|
||||
_KeyLowerName_4[0:26]: KeyEnableBackChannelLogout,
|
||||
_KeyName_4[26:34]: KeyLoginV2,
|
||||
_KeyLowerName_4[26:34]: KeyLoginV2,
|
||||
_KeyName_4[34:53]: KeyPermissionCheckV2,
|
||||
_KeyLowerName_4[34:53]: KeyPermissionCheckV2,
|
||||
_KeyName_4[53:76]: KeyConsoleUseV2UserApi,
|
||||
_KeyLowerName_4[53:76]: KeyConsoleUseV2UserApi,
|
||||
_KeyName_0[0:11]: KeyUnspecified,
|
||||
_KeyLowerName_0[0:11]: KeyUnspecified,
|
||||
_KeyName_0[11:28]: KeyLoginDefaultOrg,
|
||||
_KeyLowerName_0[11:28]: KeyLoginDefaultOrg,
|
||||
_KeyName_1[0:11]: KeyUserSchema,
|
||||
_KeyLowerName_1[0:11]: KeyUserSchema,
|
||||
_KeyName_1[11:25]: KeyTokenExchange,
|
||||
_KeyLowerName_1[11:25]: KeyTokenExchange,
|
||||
_KeyName_2[0:20]: KeyImprovedPerformance,
|
||||
_KeyLowerName_2[0:20]: KeyImprovedPerformance,
|
||||
_KeyName_3[0:23]: KeyDebugOIDCParentError,
|
||||
_KeyLowerName_3[0:23]: KeyDebugOIDCParentError,
|
||||
_KeyName_3[23:57]: KeyOIDCSingleV1SessionTermination,
|
||||
_KeyLowerName_3[23:57]: KeyOIDCSingleV1SessionTermination,
|
||||
_KeyName_4[0:26]: KeyEnableBackChannelLogout,
|
||||
_KeyLowerName_4[0:26]: KeyEnableBackChannelLogout,
|
||||
_KeyName_4[26:34]: KeyLoginV2,
|
||||
_KeyLowerName_4[26:34]: KeyLoginV2,
|
||||
_KeyName_4[34:53]: KeyPermissionCheckV2,
|
||||
_KeyLowerName_4[34:53]: KeyPermissionCheckV2,
|
||||
_KeyName_4[53:76]: KeyConsoleUseV2UserApi,
|
||||
_KeyLowerName_4[53:76]: KeyConsoleUseV2UserApi,
|
||||
_KeyName_4[76:100]: KeyEnableRelationalTables,
|
||||
_KeyLowerName_4[76:100]: KeyEnableRelationalTables,
|
||||
}
|
||||
|
||||
var _KeyNames = []string{
|
||||
@@ -104,6 +107,7 @@ var _KeyNames = []string{
|
||||
_KeyName_4[26:34],
|
||||
_KeyName_4[34:53],
|
||||
_KeyName_4[53:76],
|
||||
_KeyName_4[76:100],
|
||||
}
|
||||
|
||||
// KeyString retrieves an enum value from the enum constants string name.
|
||||
|
||||
@@ -19,6 +19,7 @@ type InstanceFeatures struct {
|
||||
LoginV2 FeatureSource[*feature.LoginV2]
|
||||
PermissionCheckV2 FeatureSource[bool]
|
||||
ConsoleUseV2UserApi FeatureSource[bool]
|
||||
EnableRelationalTables FeatureSource[bool]
|
||||
}
|
||||
|
||||
func (q *Queries) GetInstanceFeatures(ctx context.Context, cascade bool) (_ *InstanceFeatures, err error) {
|
||||
|
||||
@@ -72,11 +72,13 @@ func (m *InstanceFeaturesReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
feature_v2.InstanceLoginVersion,
|
||||
feature_v2.InstancePermissionCheckV2,
|
||||
feature_v2.InstanceConsoleUseV2UserApi,
|
||||
feature_v2.InstanceEnableRelationalTables,
|
||||
).
|
||||
Builder().ResourceOwner(m.ResourceOwner)
|
||||
}
|
||||
|
||||
func (m *InstanceFeaturesReadModel) reduceReset() {
|
||||
m.instance.EnableRelationalTables = FeatureSource[bool]{}
|
||||
if m.populateFromSystem() {
|
||||
return
|
||||
}
|
||||
@@ -126,6 +128,8 @@ func reduceInstanceFeatureSet[T any](features *InstanceFeatures, event *feature_
|
||||
features.PermissionCheckV2.set(level, event.Value)
|
||||
case feature.KeyConsoleUseV2UserApi:
|
||||
features.ConsoleUseV2UserApi.set(level, event.Value)
|
||||
case feature.KeyEnableRelationalTables:
|
||||
features.EnableRelationalTables.set(level, event.Value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -104,6 +104,10 @@ func (*instanceFeatureProjection) Reducers() []handler.AggregateReducer {
|
||||
Event: instance.InstanceRemovedEventType,
|
||||
Reduce: reduceInstanceRemovedHelper(InstanceDomainInstanceIDCol),
|
||||
},
|
||||
{
|
||||
Event: feature_v2.InstanceEnableRelationalTables,
|
||||
Reduce: reduceInstanceSetFeature[bool],
|
||||
},
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ type SystemFeatures struct {
|
||||
EnableBackChannelLogout FeatureSource[bool]
|
||||
LoginV2 FeatureSource[*feature.LoginV2]
|
||||
PermissionCheckV2 FeatureSource[bool]
|
||||
EnableRelationalTables FeatureSource[bool]
|
||||
}
|
||||
|
||||
func (q *Queries) GetSystemFeatures(ctx context.Context) (_ *SystemFeatures, err error) {
|
||||
|
||||
@@ -63,6 +63,7 @@ func (m *SystemFeaturesReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
feature_v2.SystemEnableBackChannelLogout,
|
||||
feature_v2.SystemLoginVersion,
|
||||
feature_v2.SystemPermissionCheckV2,
|
||||
feature_v2.SystemEnableRelationalTables,
|
||||
).
|
||||
Builder().ResourceOwner(m.ResourceOwner)
|
||||
}
|
||||
@@ -96,6 +97,8 @@ func reduceSystemFeatureSet[T any](features *SystemFeatures, event *feature_v2.S
|
||||
features.LoginV2.set(level, event.Value)
|
||||
case feature.KeyPermissionCheckV2:
|
||||
features.PermissionCheckV2.set(level, event.Value)
|
||||
case feature.KeyEnableRelationalTables:
|
||||
features.EnableRelationalTables.set(level, event.Value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -53,6 +53,10 @@ func TestQueries_GetSystemFeatures(t *testing.T) {
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemUserSchemaEventType, false,
|
||||
)),
|
||||
eventFromEventPusher(feature_v2.NewSetEvent(
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemEnableRelationalTables, false,
|
||||
)),
|
||||
),
|
||||
),
|
||||
want: &SystemFeatures{
|
||||
@@ -67,6 +71,10 @@ func TestQueries_GetSystemFeatures(t *testing.T) {
|
||||
Level: feature.LevelSystem,
|
||||
Value: false,
|
||||
},
|
||||
EnableRelationalTables: FeatureSource[bool]{
|
||||
Level: feature.LevelSystem,
|
||||
Value: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@ func init() {
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, SystemEnableBackChannelLogout, eventstore.GenericEventMapper[SetEvent[bool]])
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, SystemLoginVersion, eventstore.GenericEventMapper[SetEvent[*feature.LoginV2]])
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, SystemPermissionCheckV2, eventstore.GenericEventMapper[SetEvent[bool]])
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, SystemEnableRelationalTables, eventstore.GenericEventMapper[SetEvent[bool]])
|
||||
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, InstanceResetEventType, eventstore.GenericEventMapper[ResetEvent])
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, InstanceLoginDefaultOrgEventType, eventstore.GenericEventMapper[SetEvent[bool]])
|
||||
@@ -27,4 +28,5 @@ func init() {
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, InstanceLoginVersion, eventstore.GenericEventMapper[SetEvent[*feature.LoginV2]])
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, InstancePermissionCheckV2, eventstore.GenericEventMapper[SetEvent[bool]])
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, InstanceConsoleUseV2UserApi, eventstore.GenericEventMapper[SetEvent[bool]])
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, InstanceEnableRelationalTables, eventstore.GenericEventMapper[SetEvent[bool]])
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ var (
|
||||
SystemEnableBackChannelLogout = setEventTypeFromFeature(feature.LevelSystem, feature.KeyEnableBackChannelLogout)
|
||||
SystemLoginVersion = setEventTypeFromFeature(feature.LevelSystem, feature.KeyLoginV2)
|
||||
SystemPermissionCheckV2 = setEventTypeFromFeature(feature.LevelSystem, feature.KeyPermissionCheckV2)
|
||||
SystemEnableRelationalTables = setEventTypeFromFeature(feature.LevelSystem, feature.KeyEnableRelationalTables)
|
||||
|
||||
InstanceResetEventType = resetEventTypeFromFeature(feature.LevelInstance)
|
||||
InstanceLoginDefaultOrgEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyLoginDefaultOrg)
|
||||
@@ -32,6 +33,7 @@ var (
|
||||
InstanceLoginVersion = setEventTypeFromFeature(feature.LevelInstance, feature.KeyLoginV2)
|
||||
InstancePermissionCheckV2 = setEventTypeFromFeature(feature.LevelInstance, feature.KeyPermissionCheckV2)
|
||||
InstanceConsoleUseV2UserApi = setEventTypeFromFeature(feature.LevelInstance, feature.KeyConsoleUseV2UserApi)
|
||||
InstanceEnableRelationalTables = setEventTypeFromFeature(feature.LevelInstance, feature.KeyEnableRelationalTables)
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -84,6 +84,13 @@ message SetInstanceFeaturesRequest{
|
||||
description: "If this is enabled the console web client will use the new User v2 API for certain calls";
|
||||
}
|
||||
];
|
||||
|
||||
optional bool enable_relational_tables = 16 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "true";
|
||||
description: "If this is enabled Zitadel will use the relational tables instead of the projections as the main source to store the data. Regardless of the flag state, both the relational table and the projections are kept up to date. Currently the flag has no effect.";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
message SetInstanceFeaturesResponse {
|
||||
@@ -178,4 +185,11 @@ message GetInstanceFeaturesResponse {
|
||||
description: "If this is enabled the console web client will use the new User v2 API for certain calls";
|
||||
}
|
||||
];
|
||||
|
||||
FeatureFlag enable_relational_tables = 17 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "true";
|
||||
description: "If this is enabled Zitadel will use the relational tables instead of the projections as the main source to store the data. Regardless of the flag state, both the relational table and the projections are kept up to date. Currently the flag has no effect.";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
@@ -70,6 +70,14 @@ message SetSystemFeaturesRequest{
|
||||
description: "Enable a newer, more performant, permission check used for v2 and v3 resource based APIs.";
|
||||
}
|
||||
];
|
||||
|
||||
optional bool enable_relational_tables = 13 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "true";
|
||||
description: "If this is enabled Zitadel will use the relational tables instead of the projections as the main source to store the data. Regardless of the flag state, both the relational table and the projections are kept up to date. Currently the flag has no effect.";
|
||||
}
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
message SetSystemFeaturesResponse {
|
||||
@@ -143,4 +151,11 @@ message GetSystemFeaturesResponse {
|
||||
description: "Enable a newer, more performant, permission check used for v2 and v3 resource based APIs.";
|
||||
}
|
||||
];
|
||||
|
||||
FeatureFlag enable_relational_tables = 14 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "true";
|
||||
description: "If this is enabled Zitadel will use the relational tables instead of the projections as the main source to store the data. Regardless of the flag state, both the relational table and the projections are kept up to date. Currently the flag has no effect.";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user