feat(api): feature flags (#7356)

* feat(api): feature API proto definitions

* update proto based on discussion with @livio-a

* cleanup old feature flag stuff

* authz instance queries

* align defaults

* projection definitions

* define commands and event reducers

* implement system and instance setter APIs

* api getter implementation

* unit test repository package

* command unit tests

* unit test Get queries

* grpc converter unit tests

* migrate the V1 features

* migrate oidc to dynamic features

* projection unit test

* fix instance by host

* fix instance by id data type in sql

* fix linting errors

* add system projection test

* fix behavior inversion

* resolve proto file comments

* rename SystemDefaultLoginInstanceEventType to SystemLoginDefaultOrgEventType so it's consistent with the instance level event

* use write models and conditional set events

* system features integration tests

* instance features integration tests

* error on empty request

* documentation entry

* typo in feature.proto

* fix start unit tests

* solve linting error on key case switch

* remove system defaults after discussion with @eliobischof

* fix system feature projection

* resolve comments in defaults.yaml

---------

Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
Tim Möhlmann
2024-02-28 10:55:54 +02:00
committed by GitHub
parent 2801167668
commit 26d1563643
79 changed files with 4580 additions and 868 deletions

View File

@@ -0,0 +1,30 @@
package feature
//go:generate enumer -type Key -transform snake -trimprefix Key
type Key int
const (
KeyUnspecified Key = iota
KeyLoginDefaultOrg
KeyTriggerIntrospectionProjections
KeyLegacyIntrospection
)
//go:generate enumer -type Level -transform snake -trimprefix Level
type Level int
const (
LevelUnspecified Level = iota
LevelSystem
LevelInstance
LevelOrg
LevelProject
LevelApp
LevelUser
)
type Features struct {
LoginDefaultOrg bool `json:"login_default_org,omitempty"`
TriggerIntrospectionProjections bool `json:"trigger_introspection_projections,omitempty"`
LegacyIntrospection bool `json:"legacy_introspection,omitempty"`
}

View File

@@ -0,0 +1,43 @@
package feature
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestKey(t *testing.T) {
tests := []string{
"unspecified",
"login_default_org",
"trigger_introspection_projections",
"legacy_introspection",
}
for _, want := range tests {
t.Run(want, func(t *testing.T) {
feature, err := KeyString(want)
require.NoError(t, err)
assert.Equal(t, want, feature.String())
})
}
}
func TestLevel(t *testing.T) {
tests := []string{
"unspecified",
"system",
"instance",
"org",
"project",
"app",
"user",
}
for _, want := range tests {
t.Run(want, func(t *testing.T) {
level, err := LevelString(want)
require.NoError(t, err)
assert.Equal(t, want, level.String())
})
}
}

View File

@@ -0,0 +1,86 @@
// Code generated by "enumer -type Key -transform snake -trimprefix Key"; DO NOT EDIT.
package feature
import (
"fmt"
"strings"
)
const _KeyName = "unspecifiedlogin_default_orgtrigger_introspection_projectionslegacy_introspection"
var _KeyIndex = [...]uint8{0, 11, 28, 61, 81}
const _KeyLowerName = "unspecifiedlogin_default_orgtrigger_introspection_projectionslegacy_introspection"
func (i Key) String() string {
if i < 0 || i >= Key(len(_KeyIndex)-1) {
return fmt.Sprintf("Key(%d)", i)
}
return _KeyName[_KeyIndex[i]:_KeyIndex[i+1]]
}
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
func _KeyNoOp() {
var x [1]struct{}
_ = x[KeyUnspecified-(0)]
_ = x[KeyLoginDefaultOrg-(1)]
_ = x[KeyTriggerIntrospectionProjections-(2)]
_ = x[KeyLegacyIntrospection-(3)]
}
var _KeyValues = []Key{KeyUnspecified, KeyLoginDefaultOrg, KeyTriggerIntrospectionProjections, KeyLegacyIntrospection}
var _KeyNameToValueMap = map[string]Key{
_KeyName[0:11]: KeyUnspecified,
_KeyLowerName[0:11]: KeyUnspecified,
_KeyName[11:28]: KeyLoginDefaultOrg,
_KeyLowerName[11:28]: KeyLoginDefaultOrg,
_KeyName[28:61]: KeyTriggerIntrospectionProjections,
_KeyLowerName[28:61]: KeyTriggerIntrospectionProjections,
_KeyName[61:81]: KeyLegacyIntrospection,
_KeyLowerName[61:81]: KeyLegacyIntrospection,
}
var _KeyNames = []string{
_KeyName[0:11],
_KeyName[11:28],
_KeyName[28:61],
_KeyName[61:81],
}
// KeyString retrieves an enum value from the enum constants string name.
// Throws an error if the param is not part of the enum.
func KeyString(s string) (Key, error) {
if val, ok := _KeyNameToValueMap[s]; ok {
return val, nil
}
if val, ok := _KeyNameToValueMap[strings.ToLower(s)]; ok {
return val, nil
}
return 0, fmt.Errorf("%s does not belong to Key values", s)
}
// KeyValues returns all values of the enum
func KeyValues() []Key {
return _KeyValues
}
// KeyStrings returns a slice of all String values of the enum
func KeyStrings() []string {
strs := make([]string, len(_KeyNames))
copy(strs, _KeyNames)
return strs
}
// IsAKey returns "true" if the value is listed in the enum definition. "false" otherwise
func (i Key) IsAKey() bool {
for _, v := range _KeyValues {
if i == v {
return true
}
}
return false
}

View File

@@ -0,0 +1,98 @@
// Code generated by "enumer -type Level -transform snake -trimprefix Level"; DO NOT EDIT.
package feature
import (
"fmt"
"strings"
)
const _LevelName = "unspecifiedsysteminstanceorgprojectappuser"
var _LevelIndex = [...]uint8{0, 11, 17, 25, 28, 35, 38, 42}
const _LevelLowerName = "unspecifiedsysteminstanceorgprojectappuser"
func (i Level) String() string {
if i < 0 || i >= Level(len(_LevelIndex)-1) {
return fmt.Sprintf("Level(%d)", i)
}
return _LevelName[_LevelIndex[i]:_LevelIndex[i+1]]
}
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
func _LevelNoOp() {
var x [1]struct{}
_ = x[LevelUnspecified-(0)]
_ = x[LevelSystem-(1)]
_ = x[LevelInstance-(2)]
_ = x[LevelOrg-(3)]
_ = x[LevelProject-(4)]
_ = x[LevelApp-(5)]
_ = x[LevelUser-(6)]
}
var _LevelValues = []Level{LevelUnspecified, LevelSystem, LevelInstance, LevelOrg, LevelProject, LevelApp, LevelUser}
var _LevelNameToValueMap = map[string]Level{
_LevelName[0:11]: LevelUnspecified,
_LevelLowerName[0:11]: LevelUnspecified,
_LevelName[11:17]: LevelSystem,
_LevelLowerName[11:17]: LevelSystem,
_LevelName[17:25]: LevelInstance,
_LevelLowerName[17:25]: LevelInstance,
_LevelName[25:28]: LevelOrg,
_LevelLowerName[25:28]: LevelOrg,
_LevelName[28:35]: LevelProject,
_LevelLowerName[28:35]: LevelProject,
_LevelName[35:38]: LevelApp,
_LevelLowerName[35:38]: LevelApp,
_LevelName[38:42]: LevelUser,
_LevelLowerName[38:42]: LevelUser,
}
var _LevelNames = []string{
_LevelName[0:11],
_LevelName[11:17],
_LevelName[17:25],
_LevelName[25:28],
_LevelName[28:35],
_LevelName[35:38],
_LevelName[38:42],
}
// LevelString retrieves an enum value from the enum constants string name.
// Throws an error if the param is not part of the enum.
func LevelString(s string) (Level, error) {
if val, ok := _LevelNameToValueMap[s]; ok {
return val, nil
}
if val, ok := _LevelNameToValueMap[strings.ToLower(s)]; ok {
return val, nil
}
return 0, fmt.Errorf("%s does not belong to Level values", s)
}
// LevelValues returns all values of the enum
func LevelValues() []Level {
return _LevelValues
}
// LevelStrings returns a slice of all String values of the enum
func LevelStrings() []string {
strs := make([]string, len(_LevelNames))
copy(strs, _LevelNames)
return strs
}
// IsALevel returns "true" if the value is listed in the enum definition. "false" otherwise
func (i Level) IsALevel() bool {
for _, v := range _LevelValues {
if i == v {
return true
}
}
return false
}