feat: System api (#3461)

* feat: start system api

* feat: remove auth

* feat: change gitignore

* feat: run system api

* feat: remove clear view form admin api

* feat: search instances

* feat: add instance

* fix: set primary domain

* Update .gitignore

* fix: add instance

* fix: add instance

* fix: handle errors

* fix: handle instance name

* fix: test

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
Fabi
2022-04-21 12:37:39 +02:00
committed by GitHub
parent a7816a43b1
commit 3d5891eb11
40 changed files with 1216 additions and 485 deletions

View File

@@ -1,9 +1,8 @@
package admin_test
package admin
import (
"testing"
admin_grpc "github.com/caos/zitadel/internal/api/grpc/admin"
"github.com/caos/zitadel/internal/test"
"github.com/caos/zitadel/internal/view/model"
admin_pb "github.com/caos/zitadel/pkg/grpc/admin"
@@ -34,7 +33,7 @@ func TestFailedEventsToPbFields(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := admin_grpc.FailedEventsViewToPb(tt.args.failedEvents)
got := FailedEventsViewToPb(tt.args.failedEvents)
for _, g := range got {
test.AssertFieldsMapped(t, g)
}
@@ -64,7 +63,7 @@ func TestFailedEventToPbFields(t *testing.T) {
},
}
for _, tt := range tests {
converted := admin_grpc.FailedEventViewToPb(tt.args.failedEvent)
converted := FailedEventViewToPb(tt.args.failedEvent)
test.AssertFieldsMapped(t, converted)
}
}
@@ -89,7 +88,7 @@ func TestRemoveFailedEventRequestToModelFields(t *testing.T) {
},
}
for _, tt := range tests {
converted := admin_grpc.RemoveFailedEventRequestToModel(tt.args.req)
converted := RemoveFailedEventRequestToModel(tt.args.req)
test.AssertFieldsMapped(t, converted, "FailureCount", "ErrMsg")
}
}

View File

@@ -22,16 +22,3 @@ func (s *Server) ListViews(ctx context.Context, _ *admin_pb.ListViewsRequest) (*
convertedCurrentSequences = append(convertedCurrentSequences, convertedViews...)
return &admin_pb.ListViewsResponse{Result: convertedCurrentSequences}, nil
}
func (s *Server) ClearView(ctx context.Context, req *admin_pb.ClearViewRequest) (*admin_pb.ClearViewResponse, error) {
var err error
if req.Database != "zitadel" {
err = s.administrator.ClearView(ctx, req.Database, req.ViewName)
} else {
err = s.query.ClearCurrentSequence(ctx, req.ViewName)
}
if err != nil {
return nil, err
}
return &admin_pb.ClearViewResponse{}, nil
}

View File

@@ -7,6 +7,46 @@ import (
instance_pb "github.com/caos/zitadel/pkg/grpc/instance"
)
func InstancesToPb(instances []*query.Instance) []*instance_pb.Instance {
list := make([]*instance_pb.Instance, len(instances))
for i, instance := range instances {
list[i] = InstanceToPb(instance)
}
return list
}
func InstanceToPb(instance *query.Instance) *instance_pb.Instance {
return &instance_pb.Instance{
Details: object.ToViewDetailsPb(
instance.Sequence,
instance.CreationDate,
instance.ChangeDate,
instance.InstanceID(),
),
Id: instance.InstanceID(),
}
}
func InstanceQueriesToModel(queries []*instance_pb.Query) (_ []query.SearchQuery, err error) {
q := make([]query.SearchQuery, len(queries))
for i, query := range queries {
q[i], err = InstanceQueryToModel(query)
if err != nil {
return nil, err
}
}
return q, nil
}
func InstanceQueryToModel(searchQuery *instance_pb.Query) (query.SearchQuery, error) {
switch q := searchQuery.Query.(type) {
case *instance_pb.Query_IdQuery:
return query.NewInstanceIDsListSearchQuery(q.IdQuery.Ids...)
default:
return nil, errors.ThrowInvalidArgument(nil, "INST-3m0se", "List.Query.Invalid")
}
}
func DomainQueriesToModel(queries []*instance_pb.DomainSearchQuery) (_ []query.SearchQuery, err error) {
q := make([]query.SearchQuery, len(queries))
for i, query := range queries {
@@ -27,7 +67,7 @@ func DomainQueryToModel(searchQuery *instance_pb.DomainSearchQuery) (query.Searc
case *instance_pb.DomainSearchQuery_PrimaryQuery:
return query.NewInstanceDomainPrimarySearchQuery(q.PrimaryQuery.Primary)
default:
return nil, errors.ThrowInvalidArgument(nil, "ORG-Ags42", "List.Query.Invalid")
return nil, errors.ThrowInvalidArgument(nil, "INST-Ags42", "List.Query.Invalid")
}
}

View File

@@ -15,6 +15,10 @@ import (
func AuthorizationInterceptor(verifier *authz.TokenVerifier, authConfig authz.Config) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
//TODO: Change as soon as we know how to authenticate system api
if verifier == nil {
return handler(ctx, req)
}
return authorize(ctx, req, info, handler, verifier, authConfig)
}
}

View File

@@ -3,6 +3,7 @@ package middleware
import (
"context"
"fmt"
"strings"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
@@ -16,13 +17,19 @@ type InstanceVerifier interface {
GetInstance(ctx context.Context)
}
func InstanceInterceptor(verifier authz.InstanceVerifier, headerName string) grpc.UnaryServerInterceptor {
func InstanceInterceptor(verifier authz.InstanceVerifier, headerName string, ignoredServices ...string) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
return setInstance(ctx, req, info, handler, verifier, headerName)
return setInstance(ctx, req, info, handler, verifier, headerName, ignoredServices...)
}
}
func setInstance(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler, verifier authz.InstanceVerifier, headerName string) (_ interface{}, err error) {
func setInstance(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler, verifier authz.InstanceVerifier, headerName string, ignoredServices ...string) (_ interface{}, err error) {
for _, service := range ignoredServices {
if strings.HasPrefix(info.FullMethod, service) {
return handler(ctx, req)
}
}
host, err := hostNameFromContext(ctx, headerName)
if err != nil {
return nil, status.Error(codes.PermissionDenied, err.Error())

View File

@@ -30,7 +30,8 @@ func CreateServer(verifier *authz.TokenVerifier, authConfig authz.Config, querie
middleware.SentryHandler(),
middleware.NoCacheInterceptor(),
middleware.ErrorHandler(),
middleware.InstanceInterceptor(queries, hostHeaderName),
//TODO: Handle Ignored Services
middleware.InstanceInterceptor(queries, hostHeaderName, "/zitadel.system.v1.SystemService"),
middleware.AuthorizationInterceptor(verifier, authConfig),
middleware.TranslationHandler(queries),
middleware.ValidationHandler(),

View File

@@ -0,0 +1,37 @@
package system
import (
"context"
"github.com/caos/zitadel/internal/query"
system_pb "github.com/caos/zitadel/pkg/grpc/system"
)
func (s *Server) ListFailedEvents(ctx context.Context, req *system_pb.ListFailedEventsRequest) (*system_pb.ListFailedEventsResponse, error) {
failedEventsOld, err := s.administrator.GetFailedEvents(ctx)
if err != nil {
return nil, err
}
convertedOld := FailedEventsViewToPb(failedEventsOld)
failedEvents, err := s.query.SearchFailedEvents(ctx, new(query.FailedEventSearchQueries))
if err != nil {
return nil, err
}
convertedNew := FailedEventsToPb(failedEvents)
convertedOld = append(convertedOld, convertedNew...)
return &system_pb.ListFailedEventsResponse{Result: convertedOld}, nil
}
func (s *Server) RemoveFailedEvent(ctx context.Context, req *system_pb.RemoveFailedEventRequest) (*system_pb.RemoveFailedEventResponse, error) {
var err error
if req.Database != "zitadel" {
err = s.administrator.RemoveFailedEvent(ctx, RemoveFailedEventRequestToModel(req))
} else {
err = s.query.RemoveFailedEvent(ctx, req.ViewName, req.FailedSequence)
}
if err != nil {
return nil, err
}
return &system_pb.RemoveFailedEventResponse{}, nil
}

View File

@@ -0,0 +1,51 @@
package system
import (
"github.com/caos/zitadel/internal/query"
"github.com/caos/zitadel/internal/view/model"
system_pb "github.com/caos/zitadel/pkg/grpc/system"
)
func FailedEventsViewToPb(failedEvents []*model.FailedEvent) []*system_pb.FailedEvent {
events := make([]*system_pb.FailedEvent, len(failedEvents))
for i, failedEvent := range failedEvents {
events[i] = FailedEventViewToPb(failedEvent)
}
return events
}
func FailedEventViewToPb(failedEvent *model.FailedEvent) *system_pb.FailedEvent {
return &system_pb.FailedEvent{
Database: failedEvent.Database,
ViewName: failedEvent.ViewName,
FailedSequence: failedEvent.FailedSequence,
FailureCount: failedEvent.FailureCount,
ErrorMessage: failedEvent.ErrMsg,
}
}
func FailedEventsToPb(failedEvents *query.FailedEvents) []*system_pb.FailedEvent {
events := make([]*system_pb.FailedEvent, len(failedEvents.FailedEvents))
for i, failedEvent := range failedEvents.FailedEvents {
events[i] = FailedEventToPb(failedEvent)
}
return events
}
func FailedEventToPb(failedEvent *query.FailedEvent) *system_pb.FailedEvent {
return &system_pb.FailedEvent{
Database: "zitadel",
ViewName: failedEvent.ProjectionName,
FailedSequence: failedEvent.FailedSequence,
FailureCount: failedEvent.FailureCount,
ErrorMessage: failedEvent.Error,
}
}
func RemoveFailedEventRequestToModel(req *system_pb.RemoveFailedEventRequest) *model.FailedEvent {
return &model.FailedEvent{
Database: req.Database,
ViewName: req.ViewName,
FailedSequence: req.FailedSequence,
}
}

View File

@@ -0,0 +1,95 @@
package system_test
import (
"testing"
system_grpc "github.com/caos/zitadel/internal/api/grpc/system"
"github.com/caos/zitadel/internal/test"
"github.com/caos/zitadel/internal/view/model"
system_pb "github.com/caos/zitadel/pkg/grpc/system"
)
func TestFailedEventsToPbFields(t *testing.T) {
type args struct {
failedEvents []*model.FailedEvent
}
tests := []struct {
name string
args args
}{
{
name: "all fields",
args: args{
failedEvents: []*model.FailedEvent{
{
Database: "admin",
ViewName: "users",
FailedSequence: 456,
FailureCount: 5,
ErrMsg: "some error",
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := system_grpc.FailedEventsViewToPb(tt.args.failedEvents)
for _, g := range got {
test.AssertFieldsMapped(t, g)
}
})
}
}
func TestFailedEventToPbFields(t *testing.T) {
type args struct {
failedEvent *model.FailedEvent
}
tests := []struct {
name string
args args
}{
{
"all fields",
args{
failedEvent: &model.FailedEvent{
Database: "admin",
ViewName: "users",
FailedSequence: 456,
FailureCount: 5,
ErrMsg: "some error",
},
},
},
}
for _, tt := range tests {
converted := system_grpc.FailedEventViewToPb(tt.args.failedEvent)
test.AssertFieldsMapped(t, converted)
}
}
func TestRemoveFailedEventRequestToModelFields(t *testing.T) {
type args struct {
req *system_pb.RemoveFailedEventRequest
}
tests := []struct {
name string
args args
}{
{
"all fields",
args{
req: &system_pb.RemoveFailedEventRequest{
Database: "admin",
ViewName: "users",
FailedSequence: 456,
},
},
},
}
for _, tt := range tests {
converted := system_grpc.RemoveFailedEventRequestToModel(tt.args.req)
test.AssertFieldsMapped(t, converted, "FailureCount", "ErrMsg")
}
}

View File

@@ -0,0 +1,127 @@
package system
import (
"context"
"github.com/caos/zitadel/internal/api/authz"
instance_grpc "github.com/caos/zitadel/internal/api/grpc/instance"
"github.com/caos/zitadel/internal/api/grpc/object"
object_pb "github.com/caos/zitadel/pkg/grpc/object"
system_pb "github.com/caos/zitadel/pkg/grpc/system"
)
func (s *Server) ListInstances(ctx context.Context, req *system_pb.ListInstancesRequest) (*system_pb.ListInstancesResponse, error) {
queries, err := ListInstancesRequestToModel(req)
if err != nil {
return nil, err
}
result, err := s.query.SearchInstances(ctx, queries)
if err != nil {
return nil, err
}
return &system_pb.ListInstancesResponse{
Result: instance_grpc.InstancesToPb(result.Instances),
Details: &object_pb.ListDetails{
TotalResult: result.Count,
},
}, nil
}
func (s *Server) GetInstance(ctx context.Context, req *system_pb.GetInstanceRequest) (*system_pb.GetInstanceResponse, error) {
ctx = authz.WithInstanceID(ctx, req.Id)
instance, err := s.query.Instance(ctx)
if err != nil {
return nil, err
}
return &system_pb.GetInstanceResponse{
Instance: instance_grpc.InstanceToPb(instance),
}, nil
}
func (s *Server) AddInstance(ctx context.Context, req *system_pb.AddInstanceRequest) (*system_pb.AddInstanceResponse, error) {
id, details, err := s.command.SetUpInstance(ctx, AddInstancePbToSetupInstance(req, s.DefaultInstance), s.ExternalSecure, s.BaseURL)
if err != nil {
return nil, err
}
return &system_pb.AddInstanceResponse{
Id: id,
Details: object.AddToDetailsPb(
details.Sequence,
details.EventDate,
details.ResourceOwner,
),
}, nil
return nil, nil
}
func (s *Server) ListDomains(ctx context.Context, req *system_pb.ListDomainsRequest) (*system_pb.ListDomainsResponse, error) {
ctx = authz.WithInstanceID(ctx, req.Id)
queries, err := ListInstanceDomainsRequestToModel(req)
if err != nil {
return nil, err
}
domains, err := s.query.SearchInstanceDomains(ctx, queries)
if err != nil {
return nil, err
}
return &system_pb.ListDomainsResponse{
Result: instance_grpc.DomainsToPb(domains.Domains),
Details: object.ToListDetails(
domains.Count,
domains.Sequence,
domains.Timestamp,
),
}, nil
}
func (s *Server) AddDomain(ctx context.Context, req *system_pb.AddDomainRequest) (*system_pb.AddDomainResponse, error) {
ctx = authz.WithInstanceID(ctx, req.Id)
instance, err := s.query.Instance(ctx)
if err != nil {
return nil, err
}
ctx = authz.WithInstance(ctx, instance)
details, err := s.command.AddInstanceDomain(ctx, req.Domain)
if err != nil {
return nil, err
}
return &system_pb.AddDomainResponse{
Details: object.AddToDetailsPb(
details.Sequence,
details.EventDate,
details.ResourceOwner,
),
}, nil
}
func (s *Server) RemoveDomain(ctx context.Context, req *system_pb.RemoveDomainRequest) (*system_pb.RemoveDomainResponse, error) {
ctx = authz.WithInstanceID(ctx, req.Id)
details, err := s.command.RemoveInstanceDomain(ctx, req.Domain)
if err != nil {
return nil, err
}
return &system_pb.RemoveDomainResponse{
Details: object.ChangeToDetailsPb(
details.Sequence,
details.EventDate,
details.ResourceOwner,
),
}, nil
}
func (s *Server) SetPrimaryDomain(ctx context.Context, req *system_pb.SetPrimaryDomainRequest) (*system_pb.SetPrimaryDomainResponse, error) {
ctx = authz.WithInstanceID(ctx, req.Id)
details, err := s.command.SetPrimaryInstanceDomain(ctx, req.Domain)
if err != nil {
return nil, err
}
return &system_pb.SetPrimaryDomainResponse{
Details: object.ChangeToDetailsPb(
details.Sequence,
details.EventDate,
details.ResourceOwner,
),
}, nil
}

View File

@@ -0,0 +1,97 @@
package system
import (
instance_grpc "github.com/caos/zitadel/internal/api/grpc/instance"
"github.com/caos/zitadel/internal/api/grpc/object"
"github.com/caos/zitadel/internal/command"
"github.com/caos/zitadel/internal/query"
instance_pb "github.com/caos/zitadel/pkg/grpc/instance"
system_pb "github.com/caos/zitadel/pkg/grpc/system"
)
func AddInstancePbToSetupInstance(req *system_pb.AddInstanceRequest, defaultInstance command.InstanceSetup) *command.InstanceSetup {
if req.InstanceName != "" {
defaultInstance.InstanceName = req.InstanceName
defaultInstance.Org.Name = req.InstanceName
}
if req.CustomDomain != "" {
defaultInstance.CustomDomain = req.CustomDomain
}
if req.FirstOrgName != "" {
defaultInstance.Org.Name = req.FirstOrgName
}
if req.OwnerEmail != "" {
defaultInstance.Org.Human.Email.Address = req.OwnerEmail
}
if req.OwnerUsername != "" {
defaultInstance.Org.Human.Username = req.OwnerUsername
}
if req.OwnerFirstName != "" {
defaultInstance.Org.Human.FirstName = req.OwnerFirstName
}
if req.OwnerLastName != "" {
defaultInstance.Org.Human.LastName = req.OwnerLastName
}
return &defaultInstance
}
func ListInstancesRequestToModel(req *system_pb.ListInstancesRequest) (*query.InstanceSearchQueries, error) {
offset, limit, asc := object.ListQueryToModel(req.Query)
queries, err := instance_grpc.InstanceQueriesToModel(req.Queries)
if err != nil {
return nil, err
}
return &query.InstanceSearchQueries{
SearchRequest: query.SearchRequest{
Offset: offset,
Limit: limit,
Asc: asc,
SortingColumn: fieldNameToInstanceColumn(req.SortingColumn),
},
Queries: queries,
}, nil
}
func fieldNameToInstanceColumn(fieldName instance_pb.FieldName) query.Column {
switch fieldName {
case instance_pb.FieldName_FIELD_NAME_ID:
return query.InstanceColumnID
case instance_pb.FieldName_FIELD_NAME_NAME:
return query.InstanceColumnName
case instance_pb.FieldName_FIELD_NAME_CREATION_DATE:
return query.InstanceColumnCreationDate
default:
return query.Column{}
}
}
func ListInstanceDomainsRequestToModel(req *system_pb.ListDomainsRequest) (*query.InstanceDomainSearchQueries, error) {
offset, limit, asc := object.ListQueryToModel(req.Query)
queries, err := instance_grpc.DomainQueriesToModel(req.Queries)
if err != nil {
return nil, err
}
return &query.InstanceDomainSearchQueries{
SearchRequest: query.SearchRequest{
Offset: offset,
Limit: limit,
Asc: asc,
SortingColumn: fieldNameToInstanceDomainColumn(req.SortingColumn),
},
Queries: queries,
}, nil
}
func fieldNameToInstanceDomainColumn(fieldName instance_pb.DomainFieldName) query.Column {
switch fieldName {
case instance_pb.DomainFieldName_DOMAIN_FIELD_NAME_DOMAIN:
return query.InstanceDomainDomainCol
case instance_pb.DomainFieldName_DOMAIN_FIELD_NAME_GENERATED:
return query.InstanceDomainIsGeneratedCol
case instance_pb.DomainFieldName_DOMAIN_FIELD_NAME_PRIMARY:
return query.InstanceDomainIsPrimaryCol
case instance_pb.DomainFieldName_DOMAIN_FIELD_NAME_CREATION_DATE:
return query.InstanceDomainCreationDateCol
default:
return query.Column{}
}
}

View File

@@ -0,0 +1,75 @@
package system
import (
"github.com/caos/zitadel/internal/admin/repository"
http_util "github.com/caos/zitadel/internal/api/http"
"google.golang.org/grpc"
"github.com/caos/zitadel/internal/admin/repository/eventsourcing"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/api/grpc/server"
"github.com/caos/zitadel/internal/command"
"github.com/caos/zitadel/internal/query"
"github.com/caos/zitadel/pkg/grpc/system"
)
const (
systemAPI = "System-API"
)
var _ system.SystemServiceServer = (*Server)(nil)
type Server struct {
system.UnimplementedSystemServiceServer
command *command.Commands
query *query.Queries
administrator repository.AdministratorRepository
DefaultInstance command.InstanceSetup
ExternalSecure bool
BaseURL string
}
type Config struct {
Repository eventsourcing.Config
}
func CreateServer(command *command.Commands,
query *query.Queries,
repo repository.Repository,
defaultInstance command.InstanceSetup,
externalPort uint16,
externalDomain string,
externalSecure bool) *Server {
return &Server{
command: command,
query: query,
administrator: repo,
DefaultInstance: defaultInstance,
ExternalSecure: externalSecure,
BaseURL: http_util.BuildHTTP(externalDomain, externalPort, externalSecure),
}
}
func (s *Server) RegisterServer(grpcServer *grpc.Server) {
system.RegisterSystemServiceServer(grpcServer, s)
}
func (s *Server) AppName() string {
return systemAPI
}
func (s *Server) MethodPrefix() string {
return system.SystemService_MethodPrefix
}
func (s *Server) AuthMethods() authz.MethodMapping {
return system.SystemService_AuthMethods
}
func (s *Server) RegisterGateway() server.GatewayFunc {
return system.RegisterSystemServiceHandlerFromEndpoint
}
func (s *Server) GatewayPathPrefix() string {
return "/system/v1"
}

View File

@@ -0,0 +1,37 @@
package system
import (
"context"
"github.com/caos/zitadel/internal/query"
system_pb "github.com/caos/zitadel/pkg/grpc/system"
)
func (s *Server) ListViews(ctx context.Context, _ *system_pb.ListViewsRequest) (*system_pb.ListViewsResponse, error) {
currentSequences, err := s.query.SearchCurrentSequences(ctx, new(query.CurrentSequencesSearchQueries))
if err != nil {
return nil, err
}
convertedCurrentSequences := CurrentSequencesToPb(currentSequences)
views, err := s.administrator.GetViews()
if err != nil {
return nil, err
}
convertedViews := ViewsToPb(views)
convertedCurrentSequences = append(convertedCurrentSequences, convertedViews...)
return &system_pb.ListViewsResponse{Result: convertedCurrentSequences}, nil
}
func (s *Server) ClearView(ctx context.Context, req *system_pb.ClearViewRequest) (*system_pb.ClearViewResponse, error) {
var err error
if req.Database != "zitadel" {
err = s.administrator.ClearView(ctx, req.Database, req.ViewName)
} else {
err = s.query.ClearCurrentSequence(ctx, req.ViewName)
}
if err != nil {
return nil, err
}
return &system_pb.ClearViewResponse{}, nil
}

View File

@@ -0,0 +1,43 @@
package system
import (
"github.com/caos/zitadel/internal/query"
"github.com/caos/zitadel/internal/view/model"
system_pb "github.com/caos/zitadel/pkg/grpc/system"
"google.golang.org/protobuf/types/known/timestamppb"
)
func ViewsToPb(views []*model.View) []*system_pb.View {
v := make([]*system_pb.View, len(views))
for i, view := range views {
v[i] = ViewToPb(view)
}
return v
}
func ViewToPb(view *model.View) *system_pb.View {
return &system_pb.View{
Database: view.Database,
ViewName: view.ViewName,
LastSuccessfulSpoolerRun: timestamppb.New(view.LastSuccessfulSpoolerRun),
ProcessedSequence: view.CurrentSequence,
EventTimestamp: timestamppb.New(view.EventTimestamp),
}
}
func CurrentSequencesToPb(currentSequences *query.CurrentSequences) []*system_pb.View {
v := make([]*system_pb.View, len(currentSequences.CurrentSequences))
for i, currentSequence := range currentSequences.CurrentSequences {
v[i] = CurrentSequenceToPb(currentSequence)
}
return v
}
func CurrentSequenceToPb(currentSequence *query.CurrentSequence) *system_pb.View {
return &system_pb.View{
Database: "zitadel",
ViewName: currentSequence.ProjectionName,
ProcessedSequence: currentSequence.CurrentSequence,
EventTimestamp: timestamppb.New(currentSequence.Timestamp),
}
}