diff --git a/pb/c1/connector/v2/annotation_trait.pb.go b/pb/c1/connector/v2/annotation_trait.pb.go index 8d0b42028..ff2b4e885 100644 --- a/pb/c1/connector/v2/annotation_trait.pb.go +++ b/pb/c1/connector/v2/annotation_trait.pb.go @@ -490,11 +490,13 @@ func (b0 UserTrait_builder) Build() *UserTrait { } type GroupTrait struct { - state protoimpl.MessageState `protogen:"hybrid.v1"` - Icon *AssetRef `protobuf:"bytes,1,opt,name=icon,proto3" json:"icon,omitempty"` - Profile *structpb.Struct `protobuf:"bytes,2,opt,name=profile,proto3" json:"profile,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"hybrid.v1"` + Icon *AssetRef `protobuf:"bytes,1,opt,name=icon,proto3" json:"icon,omitempty"` + Profile *structpb.Struct `protobuf:"bytes,2,opt,name=profile,proto3" json:"profile,omitempty"` + GroupSourceType string `protobuf:"bytes,3,opt,name=group_source_type,json=groupSourceType,proto3" json:"group_source_type,omitempty"` + RawGroupSourceType string `protobuf:"bytes,4,opt,name=raw_group_source_type,json=rawGroupSourceType,proto3" json:"raw_group_source_type,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *GroupTrait) Reset() { @@ -536,6 +538,20 @@ func (x *GroupTrait) GetProfile() *structpb.Struct { return nil } +func (x *GroupTrait) GetGroupSourceType() string { + if x != nil { + return x.GroupSourceType + } + return "" +} + +func (x *GroupTrait) GetRawGroupSourceType() string { + if x != nil { + return x.RawGroupSourceType + } + return "" +} + func (x *GroupTrait) SetIcon(v *AssetRef) { x.Icon = v } @@ -544,6 +560,14 @@ func (x *GroupTrait) SetProfile(v *structpb.Struct) { x.Profile = v } +func (x *GroupTrait) SetGroupSourceType(v string) { + x.GroupSourceType = v +} + +func (x *GroupTrait) SetRawGroupSourceType(v string) { + x.RawGroupSourceType = v +} + func (x *GroupTrait) HasIcon() bool { if x == nil { return false @@ -569,8 +593,10 @@ func (x *GroupTrait) ClearProfile() { type GroupTrait_builder struct { _ [0]func() // Prevents comparability and use of unkeyed literals for the builder. - Icon *AssetRef - Profile *structpb.Struct + Icon *AssetRef + Profile *structpb.Struct + GroupSourceType string + RawGroupSourceType string } func (b0 GroupTrait_builder) Build() *GroupTrait { @@ -579,6 +605,8 @@ func (b0 GroupTrait_builder) Build() *GroupTrait { _, _ = b, x x.Icon = b.Icon x.Profile = b.Profile + x.GroupSourceType = b.GroupSourceType + x.RawGroupSourceType = b.RawGroupSourceType return m0 } @@ -903,14 +931,16 @@ func (b0 ScopeBindingTrait_builder) Build() *ScopeBindingTrait { } type AppTrait struct { - state protoimpl.MessageState `protogen:"hybrid.v1"` - HelpUrl string `protobuf:"bytes,1,opt,name=help_url,json=helpUrl,proto3" json:"help_url,omitempty"` - Icon *AssetRef `protobuf:"bytes,2,opt,name=icon,proto3" json:"icon,omitempty"` - Logo *AssetRef `protobuf:"bytes,3,opt,name=logo,proto3" json:"logo,omitempty"` - Profile *structpb.Struct `protobuf:"bytes,4,opt,name=profile,proto3" json:"profile,omitempty"` - Flags []AppTrait_AppFlag `protobuf:"varint,5,rep,packed,name=flags,proto3,enum=c1.connector.v2.AppTrait_AppFlag" json:"flags,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"hybrid.v1"` + HelpUrl string `protobuf:"bytes,1,opt,name=help_url,json=helpUrl,proto3" json:"help_url,omitempty"` + Icon *AssetRef `protobuf:"bytes,2,opt,name=icon,proto3" json:"icon,omitempty"` + Logo *AssetRef `protobuf:"bytes,3,opt,name=logo,proto3" json:"logo,omitempty"` + Profile *structpb.Struct `protobuf:"bytes,4,opt,name=profile,proto3" json:"profile,omitempty"` + Flags []AppTrait_AppFlag `protobuf:"varint,5,rep,packed,name=flags,proto3,enum=c1.connector.v2.AppTrait_AppFlag" json:"flags,omitempty"` + AppSourceType string `protobuf:"bytes,6,opt,name=app_source_type,json=appSourceType,proto3" json:"app_source_type,omitempty"` + RawAppSourceType string `protobuf:"bytes,7,opt,name=raw_app_source_type,json=rawAppSourceType,proto3" json:"raw_app_source_type,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *AppTrait) Reset() { @@ -973,6 +1003,20 @@ func (x *AppTrait) GetFlags() []AppTrait_AppFlag { return nil } +func (x *AppTrait) GetAppSourceType() string { + if x != nil { + return x.AppSourceType + } + return "" +} + +func (x *AppTrait) GetRawAppSourceType() string { + if x != nil { + return x.RawAppSourceType + } + return "" +} + func (x *AppTrait) SetHelpUrl(v string) { x.HelpUrl = v } @@ -993,6 +1037,14 @@ func (x *AppTrait) SetFlags(v []AppTrait_AppFlag) { x.Flags = v } +func (x *AppTrait) SetAppSourceType(v string) { + x.AppSourceType = v +} + +func (x *AppTrait) SetRawAppSourceType(v string) { + x.RawAppSourceType = v +} + func (x *AppTrait) HasIcon() bool { if x == nil { return false @@ -1029,11 +1081,13 @@ func (x *AppTrait) ClearProfile() { type AppTrait_builder struct { _ [0]func() // Prevents comparability and use of unkeyed literals for the builder. - HelpUrl string - Icon *AssetRef - Logo *AssetRef - Profile *structpb.Struct - Flags []AppTrait_AppFlag + HelpUrl string + Icon *AssetRef + Logo *AssetRef + Profile *structpb.Struct + Flags []AppTrait_AppFlag + AppSourceType string + RawAppSourceType string } func (b0 AppTrait_builder) Build() *AppTrait { @@ -1045,6 +1099,8 @@ func (b0 AppTrait_builder) Build() *AppTrait { x.Logo = b.Logo x.Profile = b.Profile x.Flags = b.Flags + x.AppSourceType = b.AppSourceType + x.RawAppSourceType = b.RawAppSourceType return m0 } @@ -1813,11 +1869,14 @@ const file_c1_connector_v2_annotation_trait_proto_rawDesc = "" + "\x18ACCOUNT_TYPE_UNSPECIFIED\x10\x00\x12\x16\n" + "\x12ACCOUNT_TYPE_HUMAN\x10\x01\x12\x18\n" + "\x14ACCOUNT_TYPE_SERVICE\x10\x02\x12\x17\n" + - "\x13ACCOUNT_TYPE_SYSTEM\x10\x03\"n\n" + + "\x13ACCOUNT_TYPE_SYSTEM\x10\x03\"\xe6\x01\n" + "\n" + "GroupTrait\x12-\n" + "\x04icon\x18\x01 \x01(\v2\x19.c1.connector.v2.AssetRefR\x04icon\x121\n" + - "\aprofile\x18\x02 \x01(\v2\x17.google.protobuf.StructR\aprofile\"\x98\x01\n" + + "\aprofile\x18\x02 \x01(\v2\x17.google.protobuf.StructR\aprofile\x126\n" + + "\x11group_source_type\x18\x03 \x01(\tB\n" + + "\xfaB\ar\x05(@\xd0\x01\x01R\x0fgroupSourceType\x12>\n" + + "\x15raw_group_source_type\x18\x04 \x01(\tB\v\xfaB\br\x06(\x80\x02\xd0\x01\x01R\x12rawGroupSourceType\"\x98\x01\n" + "\tRoleTrait\x121\n" + "\aprofile\x18\x01 \x01(\v2\x17.google.protobuf.StructR\aprofile\x12X\n" + "\x15role_scope_conditions\x18\x02 \x01(\v2$.c1.connector.v2.RoleScopeConditionsR\x13roleScopeConditions\"n\n" + @@ -1832,13 +1891,16 @@ const file_c1_connector_v2_annotation_trait_proto_rawDesc = "" + "expression\"\xa6\x01\n" + "\x11ScopeBindingTrait\x12>\n" + "\arole_id\x18\x01 \x01(\v2\x1b.c1.connector.v2.ResourceIdB\b\xfaB\x05\x8a\x01\x02\x10\x01R\x06roleId\x12Q\n" + - "\x11scope_resource_id\x18\x02 \x01(\v2\x1b.c1.connector.v2.ResourceIdB\b\xfaB\x05\x8a\x01\x02\x10\x01R\x0fscopeResourceId\"\x9a\x03\n" + + "\x11scope_resource_id\x18\x02 \x01(\v2\x1b.c1.connector.v2.ResourceIdB\b\xfaB\x05\x8a\x01\x02\x10\x01R\x0fscopeResourceId\"\x8a\x04\n" + "\bAppTrait\x125\n" + "\bhelp_url\x18\x01 \x01(\tB\x1a\xfaB\x17r\x15 \x01(\x80\b:\bhttps://\xd0\x01\x01\x88\x01\x01R\ahelpUrl\x12-\n" + "\x04icon\x18\x02 \x01(\v2\x19.c1.connector.v2.AssetRefR\x04icon\x12-\n" + "\x04logo\x18\x03 \x01(\v2\x19.c1.connector.v2.AssetRefR\x04logo\x121\n" + "\aprofile\x18\x04 \x01(\v2\x17.google.protobuf.StructR\aprofile\x127\n" + - "\x05flags\x18\x05 \x03(\x0e2!.c1.connector.v2.AppTrait.AppFlagR\x05flags\"\x8c\x01\n" + + "\x05flags\x18\x05 \x03(\x0e2!.c1.connector.v2.AppTrait.AppFlagR\x05flags\x122\n" + + "\x0fapp_source_type\x18\x06 \x01(\tB\n" + + "\xfaB\ar\x05(@\xd0\x01\x01R\rappSourceType\x12:\n" + + "\x13raw_app_source_type\x18\a \x01(\tB\v\xfaB\br\x06(\x80\x02\xd0\x01\x01R\x10rawAppSourceType\"\x8c\x01\n" + "\aAppFlag\x12\x18\n" + "\x14APP_FLAG_UNSPECIFIED\x10\x00\x12\x13\n" + "\x0fAPP_FLAG_HIDDEN\x10\x01\x12\x15\n" + diff --git a/pb/c1/connector/v2/annotation_trait.pb.validate.go b/pb/c1/connector/v2/annotation_trait.pb.validate.go index 55a2a43d0..f3d7ee7a9 100644 --- a/pb/c1/connector/v2/annotation_trait.pb.validate.go +++ b/pb/c1/connector/v2/annotation_trait.pb.validate.go @@ -504,6 +504,36 @@ func (m *GroupTrait) validate(all bool) error { } } + if m.GetGroupSourceType() != "" { + + if len(m.GetGroupSourceType()) > 64 { + err := GroupTraitValidationError{ + field: "GroupSourceType", + reason: "value length must be at most 64 bytes", + } + if !all { + return err + } + errors = append(errors, err) + } + + } + + if m.GetRawGroupSourceType() != "" { + + if len(m.GetRawGroupSourceType()) > 256 { + err := GroupTraitValidationError{ + field: "RawGroupSourceType", + reason: "value length must be at most 256 bytes", + } + if !all { + return err + } + errors = append(errors, err) + } + + } + if len(errors) > 0 { return GroupTraitMultiError(errors) } @@ -1318,6 +1348,36 @@ func (m *AppTrait) validate(all bool) error { } } + if m.GetAppSourceType() != "" { + + if len(m.GetAppSourceType()) > 64 { + err := AppTraitValidationError{ + field: "AppSourceType", + reason: "value length must be at most 64 bytes", + } + if !all { + return err + } + errors = append(errors, err) + } + + } + + if m.GetRawAppSourceType() != "" { + + if len(m.GetRawAppSourceType()) > 256 { + err := AppTraitValidationError{ + field: "RawAppSourceType", + reason: "value length must be at most 256 bytes", + } + if !all { + return err + } + errors = append(errors, err) + } + + } + if len(errors) > 0 { return AppTraitMultiError(errors) } diff --git a/pb/c1/connector/v2/annotation_trait_protoopaque.pb.go b/pb/c1/connector/v2/annotation_trait_protoopaque.pb.go index 650293052..0e1643418 100644 --- a/pb/c1/connector/v2/annotation_trait_protoopaque.pb.go +++ b/pb/c1/connector/v2/annotation_trait_protoopaque.pb.go @@ -490,11 +490,13 @@ func (b0 UserTrait_builder) Build() *UserTrait { } type GroupTrait struct { - state protoimpl.MessageState `protogen:"opaque.v1"` - xxx_hidden_Icon *AssetRef `protobuf:"bytes,1,opt,name=icon,proto3"` - xxx_hidden_Profile *structpb.Struct `protobuf:"bytes,2,opt,name=profile,proto3"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"opaque.v1"` + xxx_hidden_Icon *AssetRef `protobuf:"bytes,1,opt,name=icon,proto3"` + xxx_hidden_Profile *structpb.Struct `protobuf:"bytes,2,opt,name=profile,proto3"` + xxx_hidden_GroupSourceType string `protobuf:"bytes,3,opt,name=group_source_type,json=groupSourceType,proto3"` + xxx_hidden_RawGroupSourceType string `protobuf:"bytes,4,opt,name=raw_group_source_type,json=rawGroupSourceType,proto3"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *GroupTrait) Reset() { @@ -536,6 +538,20 @@ func (x *GroupTrait) GetProfile() *structpb.Struct { return nil } +func (x *GroupTrait) GetGroupSourceType() string { + if x != nil { + return x.xxx_hidden_GroupSourceType + } + return "" +} + +func (x *GroupTrait) GetRawGroupSourceType() string { + if x != nil { + return x.xxx_hidden_RawGroupSourceType + } + return "" +} + func (x *GroupTrait) SetIcon(v *AssetRef) { x.xxx_hidden_Icon = v } @@ -544,6 +560,14 @@ func (x *GroupTrait) SetProfile(v *structpb.Struct) { x.xxx_hidden_Profile = v } +func (x *GroupTrait) SetGroupSourceType(v string) { + x.xxx_hidden_GroupSourceType = v +} + +func (x *GroupTrait) SetRawGroupSourceType(v string) { + x.xxx_hidden_RawGroupSourceType = v +} + func (x *GroupTrait) HasIcon() bool { if x == nil { return false @@ -569,8 +593,10 @@ func (x *GroupTrait) ClearProfile() { type GroupTrait_builder struct { _ [0]func() // Prevents comparability and use of unkeyed literals for the builder. - Icon *AssetRef - Profile *structpb.Struct + Icon *AssetRef + Profile *structpb.Struct + GroupSourceType string + RawGroupSourceType string } func (b0 GroupTrait_builder) Build() *GroupTrait { @@ -579,6 +605,8 @@ func (b0 GroupTrait_builder) Build() *GroupTrait { _, _ = b, x x.xxx_hidden_Icon = b.Icon x.xxx_hidden_Profile = b.Profile + x.xxx_hidden_GroupSourceType = b.GroupSourceType + x.xxx_hidden_RawGroupSourceType = b.RawGroupSourceType return m0 } @@ -904,14 +932,16 @@ func (b0 ScopeBindingTrait_builder) Build() *ScopeBindingTrait { } type AppTrait struct { - state protoimpl.MessageState `protogen:"opaque.v1"` - xxx_hidden_HelpUrl string `protobuf:"bytes,1,opt,name=help_url,json=helpUrl,proto3"` - xxx_hidden_Icon *AssetRef `protobuf:"bytes,2,opt,name=icon,proto3"` - xxx_hidden_Logo *AssetRef `protobuf:"bytes,3,opt,name=logo,proto3"` - xxx_hidden_Profile *structpb.Struct `protobuf:"bytes,4,opt,name=profile,proto3"` - xxx_hidden_Flags []AppTrait_AppFlag `protobuf:"varint,5,rep,packed,name=flags,proto3,enum=c1.connector.v2.AppTrait_AppFlag"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"opaque.v1"` + xxx_hidden_HelpUrl string `protobuf:"bytes,1,opt,name=help_url,json=helpUrl,proto3"` + xxx_hidden_Icon *AssetRef `protobuf:"bytes,2,opt,name=icon,proto3"` + xxx_hidden_Logo *AssetRef `protobuf:"bytes,3,opt,name=logo,proto3"` + xxx_hidden_Profile *structpb.Struct `protobuf:"bytes,4,opt,name=profile,proto3"` + xxx_hidden_Flags []AppTrait_AppFlag `protobuf:"varint,5,rep,packed,name=flags,proto3,enum=c1.connector.v2.AppTrait_AppFlag"` + xxx_hidden_AppSourceType string `protobuf:"bytes,6,opt,name=app_source_type,json=appSourceType,proto3"` + xxx_hidden_RawAppSourceType string `protobuf:"bytes,7,opt,name=raw_app_source_type,json=rawAppSourceType,proto3"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *AppTrait) Reset() { @@ -974,6 +1004,20 @@ func (x *AppTrait) GetFlags() []AppTrait_AppFlag { return nil } +func (x *AppTrait) GetAppSourceType() string { + if x != nil { + return x.xxx_hidden_AppSourceType + } + return "" +} + +func (x *AppTrait) GetRawAppSourceType() string { + if x != nil { + return x.xxx_hidden_RawAppSourceType + } + return "" +} + func (x *AppTrait) SetHelpUrl(v string) { x.xxx_hidden_HelpUrl = v } @@ -994,6 +1038,14 @@ func (x *AppTrait) SetFlags(v []AppTrait_AppFlag) { x.xxx_hidden_Flags = v } +func (x *AppTrait) SetAppSourceType(v string) { + x.xxx_hidden_AppSourceType = v +} + +func (x *AppTrait) SetRawAppSourceType(v string) { + x.xxx_hidden_RawAppSourceType = v +} + func (x *AppTrait) HasIcon() bool { if x == nil { return false @@ -1030,11 +1082,13 @@ func (x *AppTrait) ClearProfile() { type AppTrait_builder struct { _ [0]func() // Prevents comparability and use of unkeyed literals for the builder. - HelpUrl string - Icon *AssetRef - Logo *AssetRef - Profile *structpb.Struct - Flags []AppTrait_AppFlag + HelpUrl string + Icon *AssetRef + Logo *AssetRef + Profile *structpb.Struct + Flags []AppTrait_AppFlag + AppSourceType string + RawAppSourceType string } func (b0 AppTrait_builder) Build() *AppTrait { @@ -1046,6 +1100,8 @@ func (b0 AppTrait_builder) Build() *AppTrait { x.xxx_hidden_Logo = b.Logo x.xxx_hidden_Profile = b.Profile x.xxx_hidden_Flags = b.Flags + x.xxx_hidden_AppSourceType = b.AppSourceType + x.xxx_hidden_RawAppSourceType = b.RawAppSourceType return m0 } @@ -1807,11 +1863,14 @@ const file_c1_connector_v2_annotation_trait_proto_rawDesc = "" + "\x18ACCOUNT_TYPE_UNSPECIFIED\x10\x00\x12\x16\n" + "\x12ACCOUNT_TYPE_HUMAN\x10\x01\x12\x18\n" + "\x14ACCOUNT_TYPE_SERVICE\x10\x02\x12\x17\n" + - "\x13ACCOUNT_TYPE_SYSTEM\x10\x03\"n\n" + + "\x13ACCOUNT_TYPE_SYSTEM\x10\x03\"\xe6\x01\n" + "\n" + "GroupTrait\x12-\n" + "\x04icon\x18\x01 \x01(\v2\x19.c1.connector.v2.AssetRefR\x04icon\x121\n" + - "\aprofile\x18\x02 \x01(\v2\x17.google.protobuf.StructR\aprofile\"\x98\x01\n" + + "\aprofile\x18\x02 \x01(\v2\x17.google.protobuf.StructR\aprofile\x126\n" + + "\x11group_source_type\x18\x03 \x01(\tB\n" + + "\xfaB\ar\x05(@\xd0\x01\x01R\x0fgroupSourceType\x12>\n" + + "\x15raw_group_source_type\x18\x04 \x01(\tB\v\xfaB\br\x06(\x80\x02\xd0\x01\x01R\x12rawGroupSourceType\"\x98\x01\n" + "\tRoleTrait\x121\n" + "\aprofile\x18\x01 \x01(\v2\x17.google.protobuf.StructR\aprofile\x12X\n" + "\x15role_scope_conditions\x18\x02 \x01(\v2$.c1.connector.v2.RoleScopeConditionsR\x13roleScopeConditions\"n\n" + @@ -1826,13 +1885,16 @@ const file_c1_connector_v2_annotation_trait_proto_rawDesc = "" + "expression\"\xa6\x01\n" + "\x11ScopeBindingTrait\x12>\n" + "\arole_id\x18\x01 \x01(\v2\x1b.c1.connector.v2.ResourceIdB\b\xfaB\x05\x8a\x01\x02\x10\x01R\x06roleId\x12Q\n" + - "\x11scope_resource_id\x18\x02 \x01(\v2\x1b.c1.connector.v2.ResourceIdB\b\xfaB\x05\x8a\x01\x02\x10\x01R\x0fscopeResourceId\"\x9a\x03\n" + + "\x11scope_resource_id\x18\x02 \x01(\v2\x1b.c1.connector.v2.ResourceIdB\b\xfaB\x05\x8a\x01\x02\x10\x01R\x0fscopeResourceId\"\x8a\x04\n" + "\bAppTrait\x125\n" + "\bhelp_url\x18\x01 \x01(\tB\x1a\xfaB\x17r\x15 \x01(\x80\b:\bhttps://\xd0\x01\x01\x88\x01\x01R\ahelpUrl\x12-\n" + "\x04icon\x18\x02 \x01(\v2\x19.c1.connector.v2.AssetRefR\x04icon\x12-\n" + "\x04logo\x18\x03 \x01(\v2\x19.c1.connector.v2.AssetRefR\x04logo\x121\n" + "\aprofile\x18\x04 \x01(\v2\x17.google.protobuf.StructR\aprofile\x127\n" + - "\x05flags\x18\x05 \x03(\x0e2!.c1.connector.v2.AppTrait.AppFlagR\x05flags\"\x8c\x01\n" + + "\x05flags\x18\x05 \x03(\x0e2!.c1.connector.v2.AppTrait.AppFlagR\x05flags\x122\n" + + "\x0fapp_source_type\x18\x06 \x01(\tB\n" + + "\xfaB\ar\x05(@\xd0\x01\x01R\rappSourceType\x12:\n" + + "\x13raw_app_source_type\x18\a \x01(\tB\v\xfaB\br\x06(\x80\x02\xd0\x01\x01R\x10rawAppSourceType\"\x8c\x01\n" + "\aAppFlag\x12\x18\n" + "\x14APP_FLAG_UNSPECIFIED\x10\x00\x12\x13\n" + "\x0fAPP_FLAG_HIDDEN\x10\x01\x12\x15\n" + diff --git a/pkg/types/resource/app_trait.go b/pkg/types/resource/app_trait.go index b1d416726..77c3622eb 100644 --- a/pkg/types/resource/app_trait.go +++ b/pkg/types/resource/app_trait.go @@ -8,6 +8,8 @@ import ( "google.golang.org/protobuf/types/known/structpb" ) +type AppSourceType string + type AppTraitOption func(gt *v2.AppTrait) error func WithAppIcon(assetRef *v2.AssetRef) AppTraitOption { @@ -53,6 +55,20 @@ func WithAppHelpURL(helpURL string) AppTraitOption { } } +func WithAppSourceType(sourceType AppSourceType) AppTraitOption { + return func(at *v2.AppTrait) error { + at.SetAppSourceType(string(sourceType)) + return nil + } +} + +func WithRawAppSourceType(raw string) AppTraitOption { + return func(at *v2.AppTrait) error { + at.SetRawAppSourceType(raw) + return nil + } +} + // NewAppTrait creates a new `AppTrait` with the given help URL, and profile. func NewAppTrait(opts ...AppTraitOption) (*v2.AppTrait, error) { at := &v2.AppTrait{} diff --git a/pkg/types/resource/app_trait_test.go b/pkg/types/resource/app_trait_test.go index b5ed70696..92fc4ac2f 100644 --- a/pkg/types/resource/app_trait_test.go +++ b/pkg/types/resource/app_trait_test.go @@ -1,9 +1,12 @@ package resource import ( + "errors" + "strings" "testing" v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" + "github.com/conductorone/baton-sdk/pkg/annotations" "github.com/stretchr/testify/require" ) @@ -64,3 +67,61 @@ func TestAppTrait(t *testing.T) { require.NotNil(t, at.GetLogo()) require.Equal(t, "logoID", at.GetLogo().GetId()) } + +func TestAppTraitSourceType(t *testing.T) { + at, err := NewAppTrait() + require.NoError(t, err) + require.Empty(t, at.GetAppSourceType()) + require.Empty(t, at.GetRawAppSourceType()) + + at, err = NewAppTrait( + WithAppSourceType("native"), + WithRawAppSourceType("OKTA_APP"), + ) + require.NoError(t, err) + require.Equal(t, "native", at.GetAppSourceType()) + require.Equal(t, "OKTA_APP", at.GetRawAppSourceType()) +} + +func TestAppTraitValidateSourceType(t *testing.T) { + cases := []struct { + name string + srcType string + rawType string + errField string + }{ + {"empty passes", "", "", ""}, + {"valid passes", "saml", "OKTA_APP", ""}, + {"normalized too long", strings.Repeat("a", 65), "", "AppSourceType"}, + {"raw too long", "saml", strings.Repeat("a", 257), "RawAppSourceType"}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + at := &v2.AppTrait{} + at.SetAppSourceType(tc.srcType) + at.SetRawAppSourceType(tc.rawType) + err := at.Validate() + if tc.errField == "" { + require.NoError(t, err) + return + } + require.Error(t, err) + var vErr v2.AppTraitValidationError + require.True(t, errors.As(err, &vErr)) + require.Equal(t, tc.errField, vErr.Field()) + }) + } +} + +func TestAppTraitAnnotationRoundTrip(t *testing.T) { + src, err := NewAppTrait( + WithAppSourceType("saml"), + WithRawAppSourceType("OKTA_APP"), + ) + require.NoError(t, err) + resource := &v2.Resource{Annotations: annotations.New(src)} + got, err := GetAppTrait(resource) + require.NoError(t, err) + require.Equal(t, "saml", got.GetAppSourceType()) + require.Equal(t, "OKTA_APP", got.GetRawAppSourceType()) +} diff --git a/pkg/types/resource/group_trait.go b/pkg/types/resource/group_trait.go index 2857fe991..16cee8470 100644 --- a/pkg/types/resource/group_trait.go +++ b/pkg/types/resource/group_trait.go @@ -8,6 +8,17 @@ import ( "google.golang.org/protobuf/types/known/structpb" ) +type GroupSourceType string + +const ( + GroupSourceTypeNative GroupSourceType = "native" + GroupSourceTypeAppImported GroupSourceType = "app_imported" + GroupSourceTypeBuiltIn GroupSourceType = "built_in" + GroupSourceTypeDirectorySynced GroupSourceType = "directory_synced" + GroupSourceTypeDynamic GroupSourceType = "dynamic" + GroupSourceTypeDistribution GroupSourceType = "distribution" +) + type GroupTraitOption func(gt *v2.GroupTrait) error func WithGroupProfile(profile map[string]interface{}) GroupTraitOption { @@ -30,6 +41,20 @@ func WithGroupIcon(assetRef *v2.AssetRef) GroupTraitOption { } } +func WithGroupSourceType(sourceType GroupSourceType) GroupTraitOption { + return func(gt *v2.GroupTrait) error { + gt.SetGroupSourceType(string(sourceType)) + return nil + } +} + +func WithRawGroupSourceType(raw string) GroupTraitOption { + return func(gt *v2.GroupTrait) error { + gt.SetRawGroupSourceType(raw) + return nil + } +} + // NewGroupTrait creates a new `GroupTrait` with the provided profile. func NewGroupTrait(opts ...GroupTraitOption) (*v2.GroupTrait, error) { groupTrait := &v2.GroupTrait{} diff --git a/pkg/types/resource/group_trait_test.go b/pkg/types/resource/group_trait_test.go index 62741443c..b32618c0a 100644 --- a/pkg/types/resource/group_trait_test.go +++ b/pkg/types/resource/group_trait_test.go @@ -1,10 +1,14 @@ package resource import ( + "errors" + "strings" "testing" v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" + "github.com/conductorone/baton-sdk/pkg/annotations" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" ) func TestGroupTrait(t *testing.T) { @@ -36,3 +40,87 @@ func TestGroupTrait(t *testing.T) { require.True(t, ok) require.Equal(t, "group-profile-field", val) } + +func TestGroupTraitSourceType(t *testing.T) { + gt, err := NewGroupTrait() + require.NoError(t, err) + require.Empty(t, gt.GetGroupSourceType()) + require.Empty(t, gt.GetRawGroupSourceType()) + + gt, err = NewGroupTrait( + WithGroupSourceType(GroupSourceTypeAppImported), + WithRawGroupSourceType("OKTA_GROUP"), + ) + require.NoError(t, err) + require.Equal(t, "app_imported", gt.GetGroupSourceType()) + require.Equal(t, "OKTA_GROUP", gt.GetRawGroupSourceType()) +} + +// A constant rename or typo trips this; a same-value alias does not. +func TestGroupSourceTypeWireFormat(t *testing.T) { + cases := []struct { + c GroupSourceType + want string + }{ + {GroupSourceTypeNative, "native"}, + {GroupSourceTypeAppImported, "app_imported"}, + {GroupSourceTypeBuiltIn, "built_in"}, + {GroupSourceTypeDirectorySynced, "directory_synced"}, + {GroupSourceTypeDynamic, "dynamic"}, + {GroupSourceTypeDistribution, "distribution"}, + } + for _, tc := range cases { + t.Run(tc.want, func(t *testing.T) { + src, err := NewGroupTrait(WithGroupSourceType(tc.c)) + require.NoError(t, err) + wire, err := proto.Marshal(src) + require.NoError(t, err) + got := &v2.GroupTrait{} + require.NoError(t, proto.Unmarshal(wire, got)) + require.Equal(t, tc.want, got.GetGroupSourceType()) + }) + } +} + +func TestGroupTraitValidateSourceType(t *testing.T) { + cases := []struct { + name string + srcType string + rawType string + errField string + }{ + {"empty passes", "", "", ""}, + {"valid passes", "native", "OKTA_GROUP", ""}, + {"normalized too long", strings.Repeat("a", 65), "", "GroupSourceType"}, + {"raw too long", "native", strings.Repeat("a", 257), "RawGroupSourceType"}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + gt := &v2.GroupTrait{} + gt.SetGroupSourceType(tc.srcType) + gt.SetRawGroupSourceType(tc.rawType) + err := gt.Validate() + if tc.errField == "" { + require.NoError(t, err) + return + } + require.Error(t, err) + var vErr v2.GroupTraitValidationError + require.True(t, errors.As(err, &vErr)) + require.Equal(t, tc.errField, vErr.Field()) + }) + } +} + +func TestGroupTraitAnnotationRoundTrip(t *testing.T) { + src, err := NewGroupTrait( + WithGroupSourceType(GroupSourceTypeBuiltIn), + WithRawGroupSourceType("OKTA_GROUP"), + ) + require.NoError(t, err) + resource := &v2.Resource{Annotations: annotations.New(src)} + got, err := GetGroupTrait(resource) + require.NoError(t, err) + require.Equal(t, "built_in", got.GetGroupSourceType()) + require.Equal(t, "OKTA_GROUP", got.GetRawGroupSourceType()) +} diff --git a/proto/c1/connector/v2/annotation_trait.proto b/proto/c1/connector/v2/annotation_trait.proto index f15af648b..e17bef9e8 100644 --- a/proto/c1/connector/v2/annotation_trait.proto +++ b/proto/c1/connector/v2/annotation_trait.proto @@ -76,6 +76,14 @@ message UserTrait { message GroupTrait { c1.connector.v2.AssetRef icon = 1; google.protobuf.Struct profile = 2; + string group_source_type = 3 [(validate.rules).string = { + max_bytes: 64 + ignore_empty: true + }]; + string raw_group_source_type = 4 [(validate.rules).string = { + max_bytes: 256 + ignore_empty: true + }]; } message RoleTrait { @@ -122,6 +130,14 @@ message AppTrait { APP_FLAG_BOOKMARK = 5; } repeated AppFlag flags = 5; + string app_source_type = 6 [(validate.rules).string = { + max_bytes: 64 + ignore_empty: true + }]; + string raw_app_source_type = 7 [(validate.rules).string = { + max_bytes: 256 + ignore_empty: true + }]; } message SecretTrait {