github.com/cloudwan/edgelq-sdk@v1.15.4/iam/access/v1alpha2/group/group.pb.watcher.go (about)

     1  // Code generated by protoc-gen-goten-access
     2  // Resource: Group
     3  // DO NOT EDIT!!!
     4  
     5  package group_access
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"fmt"
    11  	"sync/atomic"
    12  
    13  	"google.golang.org/grpc"
    14  	"google.golang.org/protobuf/proto"
    15  
    16  	gotenaccess "github.com/cloudwan/goten-sdk/runtime/access"
    17  	gotenobservability "github.com/cloudwan/goten-sdk/runtime/observability"
    18  	gotenresource "github.com/cloudwan/goten-sdk/runtime/resource"
    19  	"github.com/cloudwan/goten-sdk/types/view"
    20  	"github.com/cloudwan/goten-sdk/types/watch_type"
    21  
    22  	group_client "github.com/cloudwan/edgelq-sdk/iam/client/v1alpha2/group"
    23  	group "github.com/cloudwan/edgelq-sdk/iam/resources/v1alpha2/group"
    24  )
    25  
    26  // TODO: This watcher is for stateless watch type only de facto as of now.
    27  // Ordering and multiple filters do not cooperate at all.
    28  // We could:
    29  // * Add Cursor as parameter to NewWatcher + page size to WatcherConfig
    30  // * Add ResetCursor method to Watcher - only one page available at the time byt still dynamic
    31  // * Merge many pages with sorting/pagination within watcher and forward aggregated changes.
    32  
    33  // Watcher is higher level stateful watcher with dynamic + multi filter support.
    34  type Watcher struct {
    35  	client             group_client.GroupServiceClient
    36  	config             *WatcherConfig
    37  	outputEvtChan      chan WatcherEvent
    38  	iOutputEvtChan     chan gotenaccess.WatcherEvent
    39  	queryWatcherStates map[int]*queryWatcherState
    40  	watcherEvtsChan    chan *QueryWatcherEvent
    41  	filters            []*WatcherFilterParams
    42  	filtersResetChan   chan struct {
    43  		f []*WatcherFilterParams
    44  		v int32
    45  	}
    46  	inSyncFlag         int32
    47  	nextIdentifier     int
    48  	watcherCtx         context.Context
    49  	watcherCtxCancel   context.CancelFunc
    50  	useIChan           int32
    51  	nextFiltersVersion int32
    52  	filtersVersion     int32
    53  }
    54  
    55  type WatcherConfig struct {
    56  	*gotenaccess.WatcherConfigBase
    57  
    58  	// common params that must be shared across queries
    59  	WatchType watch_type.WatchType
    60  	View      view.View
    61  	FieldMask *group.Group_FieldMask
    62  	OrderBy   *group.OrderBy
    63  	ChunkSize int
    64  }
    65  
    66  type WatcherFilterParams struct {
    67  	Parent *group.ParentName
    68  	Filter *group.Filter
    69  }
    70  
    71  func (p *WatcherFilterParams) String() string {
    72  	if p == nil {
    73  		return "<nil>"
    74  	}
    75  	return fmt.Sprintf("%s.%s", p.Parent, p.Filter)
    76  }
    77  
    78  func (p *WatcherFilterParams) GetIFilter() gotenresource.Filter {
    79  	return p.Filter
    80  }
    81  
    82  func (p *WatcherFilterParams) GetIParentName() gotenresource.Name {
    83  	return p.Parent
    84  }
    85  
    86  type queryWatcherState struct {
    87  	cancel          context.CancelFunc
    88  	watcher         *QueryWatcher
    89  	inSync          bool
    90  	cache           map[group.Name]*group.Group
    91  	filter          *WatcherFilterParams
    92  	inSnapshot      bool
    93  	pendingSnapshot []*group.GroupChange
    94  }
    95  
    96  func NewWatcher(client group_client.GroupServiceClient, config *WatcherConfig, filters ...*WatcherFilterParams) *Watcher {
    97  	ctx, cancel := context.WithCancel(context.Background())
    98  	watcher := &Watcher{
    99  		client:             client,
   100  		config:             config,
   101  		filters:            filters,
   102  		outputEvtChan:      make(chan WatcherEvent, config.WatcherEventBufferSize),
   103  		queryWatcherStates: map[int]*queryWatcherState{},
   104  		watcherEvtsChan:    make(chan *QueryWatcherEvent, config.WatcherEventBufferSize),
   105  		filtersResetChan: make(chan struct {
   106  			f []*WatcherFilterParams
   107  			v int32
   108  		}, 5),
   109  		watcherCtx:       ctx,
   110  		watcherCtxCancel: cancel,
   111  	}
   112  	watcher.filtersResetChan <- struct {
   113  		f []*WatcherFilterParams
   114  		v int32
   115  	}{f: filters, v: 0}
   116  	return watcher
   117  }
   118  
   119  func (pw *Watcher) Events() <-chan WatcherEvent {
   120  	return pw.outputEvtChan
   121  }
   122  
   123  func (pw *Watcher) IEvents() <-chan gotenaccess.WatcherEvent {
   124  	if atomic.CompareAndSwapInt32(&pw.useIChan, 0, 1) {
   125  		pw.iOutputEvtChan = make(chan gotenaccess.WatcherEvent, pw.config.WatcherEventBufferSize)
   126  		go func() {
   127  			for {
   128  				select {
   129  				case <-pw.watcherCtx.Done():
   130  					return
   131  				case evt := <-pw.outputEvtChan:
   132  					select {
   133  					case <-pw.watcherCtx.Done():
   134  						return
   135  					case pw.iOutputEvtChan <- &evt:
   136  					}
   137  				}
   138  			}
   139  		}()
   140  	}
   141  	return pw.iOutputEvtChan
   142  }
   143  
   144  func (pw *Watcher) InSync() bool {
   145  	return atomic.LoadInt32(&pw.inSyncFlag) > 0
   146  }
   147  
   148  func (pw *Watcher) GetFilters() []*WatcherFilterParams {
   149  	copied := make([]*WatcherFilterParams, 0, len(pw.filters))
   150  	for _, query := range pw.filters {
   151  		cQuery := &WatcherFilterParams{}
   152  		if query.Parent != nil {
   153  			cQuery.Parent = &group.ParentName{}
   154  			*cQuery.Parent = *query.Parent
   155  		}
   156  		if query.Filter != nil {
   157  			strValue, _ := query.Filter.ProtoString()
   158  			cQuery.Filter = &group.Filter{}
   159  			_ = cQuery.Filter.ParseProtoString(strValue)
   160  		}
   161  		copied = append(copied, cQuery)
   162  	}
   163  	return copied
   164  }
   165  
   166  func (pw *Watcher) GetIFilters() []gotenaccess.WatcherFilterParams {
   167  	filters := pw.GetFilters()
   168  	result := make([]gotenaccess.WatcherFilterParams, 0, len(filters))
   169  	for _, filter := range filters {
   170  		result = append(result, filter)
   171  	}
   172  	return result
   173  }
   174  
   175  func (pw *Watcher) ResetFilters(ctx context.Context, filters ...*WatcherFilterParams) (int32, error) {
   176  	pw.nextFiltersVersion++
   177  	pw.filters = filters
   178  	select {
   179  	case <-ctx.Done():
   180  		return -1, ctx.Err()
   181  	case pw.filtersResetChan <- struct {
   182  		f []*WatcherFilterParams
   183  		v int32
   184  	}{f: filters, v: pw.nextFiltersVersion}:
   185  	}
   186  	return pw.nextFiltersVersion, nil
   187  }
   188  
   189  func (pw *Watcher) ResetIFilters(ctx context.Context, filters ...gotenaccess.WatcherFilterParams) (int32, error) {
   190  	typedFilters := make([]*WatcherFilterParams, 0, len(filters))
   191  	for _, filter := range filters {
   192  		typedFilters = append(typedFilters, filter.(*WatcherFilterParams))
   193  	}
   194  	return pw.ResetFilters(ctx, typedFilters...)
   195  }
   196  
   197  func (pw *Watcher) Run(ctx context.Context) error {
   198  	log := gotenobservability.LoggerFromContext(ctx).
   199  		WithField("watcher", "group-watcher")
   200  	ctx = gotenobservability.LoggerToContext(ctx, log)
   201  
   202  	log.Debugf("running")
   203  	defer func() {
   204  		for _, state := range pw.queryWatcherStates {
   205  			state.cancel()
   206  		}
   207  		pw.watcherCtxCancel()
   208  	}()
   209  
   210  	for {
   211  		select {
   212  		case newFilters := <-pw.filtersResetChan:
   213  			pw.filtersVersion = newFilters.v
   214  			pw.resetFilters(ctx, newFilters.f)
   215  		case evt := <-pw.watcherEvtsChan:
   216  			if evt.LostSync {
   217  				pw.onQueryLostSync(ctx, evt)
   218  			} else if evt.CheckSize {
   219  				pw.onQueryVerifySnapshotSize(ctx, evt)
   220  			} else if evt.Reset {
   221  				pw.onQueryResetStateIndicator(evt)
   222  			} else {
   223  				pw.onQueryChanges(ctx, evt)
   224  			}
   225  		case <-ctx.Done():
   226  			log.Debugf("context done, reason: %s", ctx.Err())
   227  			if errors.Is(ctx.Err(), context.Canceled) {
   228  				return nil
   229  			} else {
   230  				return ctx.Err()
   231  			}
   232  		}
   233  	}
   234  }
   235  
   236  func (pw *Watcher) resetFilters(ctx context.Context, filters []*WatcherFilterParams) {
   237  	filtersMap := map[string]*WatcherFilterParams{}
   238  	for _, filter := range filters {
   239  		filtersMap[filter.String()] = filter
   240  	}
   241  
   242  	// Eliminate existing queries from queriesMap, eliminate queries that should be stopped
   243  	for identifier, state := range pw.queryWatcherStates {
   244  		key := state.filter.String()
   245  		if _, exists := filtersMap[key]; !exists {
   246  			state.cancel()
   247  			delete(pw.queryWatcherStates, identifier)
   248  		} else {
   249  			delete(filtersMap, key)
   250  		}
   251  	}
   252  
   253  	// start new queries with new filters
   254  	for _, filter := range filtersMap {
   255  		wctx, cancel := context.WithCancel(ctx)
   256  		newWatcher := NewQueryWatcher(
   257  			pw.nextIdentifier,
   258  			pw.client,
   259  			&QueryWatcherParams{
   260  				Parent:           filter.Parent,
   261  				Filter:           filter.Filter,
   262  				View:             pw.config.View,
   263  				FieldMask:        pw.config.FieldMask,
   264  				OrderBy:          pw.config.OrderBy,
   265  				ChunkSize:        pw.config.ChunkSize,
   266  				WatchType:        pw.config.WatchType,
   267  				RecoveryDeadline: pw.config.RecoveryDeadline,
   268  				RetryTimeout:     pw.config.RetryTimeout,
   269  			},
   270  			pw.watcherEvtsChan)
   271  		pw.queryWatcherStates[pw.nextIdentifier] = &queryWatcherState{
   272  			cancel:     cancel,
   273  			watcher:    newWatcher,
   274  			cache:      map[group.Name]*group.Group{},
   275  			filter:     filter,
   276  			inSnapshot: true,
   277  		}
   278  		pw.nextIdentifier++
   279  
   280  		go func() {
   281  			_ = newWatcher.Run(wctx)
   282  		}()
   283  	}
   284  
   285  	// if all in sync (possible if queries were eliminated with ResetFilters call), send snapshot, otherwise
   286  	// communicate "lost sync"
   287  	if pw.allInSync() {
   288  		pw.processSnapshot(ctx)
   289  	} else if pw.inSyncFlag > 0 {
   290  		pw.processSyncLost(ctx)
   291  	}
   292  }
   293  
   294  func (pw *Watcher) onQueryLostSync(ctx context.Context, evt *QueryWatcherEvent) {
   295  	state := pw.queryWatcherStates[evt.Identifier]
   296  	if state == nil {
   297  		return
   298  	}
   299  	state.inSync = false
   300  	state.cache = map[group.Name]*group.Group{}
   301  	state.inSnapshot = true
   302  	if pw.inSyncFlag > 0 {
   303  		pw.processSyncLost(ctx)
   304  	}
   305  }
   306  
   307  func (pw *Watcher) onQueryVerifySnapshotSize(ctx context.Context, evt *QueryWatcherEvent) {
   308  	state := pw.queryWatcherStates[evt.Identifier]
   309  	if state == nil {
   310  		return
   311  	}
   312  	currentSize := state.computeSize(evt.Changes)
   313  	if currentSize != evt.SnapshotSize {
   314  		gotenobservability.LoggerFromContext(ctx).
   315  			Warnf("Detected mismatch in snapshot size for filter %s: expected %d, has %d",
   316  				state.filter, evt.SnapshotSize, currentSize)
   317  		state.cancel()
   318  		delete(pw.queryWatcherStates, evt.Identifier)
   319  
   320  		// refresh this particular watcher
   321  		wctx, cancel := context.WithCancel(ctx)
   322  		newWatcher := NewQueryWatcher(
   323  			pw.nextIdentifier,
   324  			pw.client,
   325  			&QueryWatcherParams{
   326  				Parent:           state.filter.Parent,
   327  				Filter:           state.filter.Filter,
   328  				View:             pw.config.View,
   329  				FieldMask:        pw.config.FieldMask,
   330  				OrderBy:          pw.config.OrderBy,
   331  				ChunkSize:        pw.config.ChunkSize,
   332  				WatchType:        pw.config.WatchType,
   333  				RecoveryDeadline: pw.config.RecoveryDeadline,
   334  				RetryTimeout:     pw.config.RetryTimeout,
   335  			},
   336  			pw.watcherEvtsChan)
   337  		pw.queryWatcherStates[pw.nextIdentifier] = &queryWatcherState{
   338  			cancel:     cancel,
   339  			watcher:    newWatcher,
   340  			cache:      state.cache,
   341  			filter:     state.filter,
   342  			inSnapshot: true,
   343  			inSync:     state.inSync,
   344  		}
   345  		pw.nextIdentifier++
   346  
   347  		go func() {
   348  			_ = newWatcher.Run(wctx)
   349  		}()
   350  	}
   351  }
   352  
   353  func (pw *Watcher) onQueryResetStateIndicator(evt *QueryWatcherEvent) {
   354  	state := pw.queryWatcherStates[evt.Identifier]
   355  	if state == nil {
   356  		return
   357  	}
   358  	state.inSnapshot = true
   359  	state.pendingSnapshot = nil
   360  }
   361  
   362  func (pw *Watcher) onQueryChanges(ctx context.Context, evt *QueryWatcherEvent) {
   363  	var changes []*WatcherEventChange
   364  	state := pw.queryWatcherStates[evt.Identifier]
   365  	if state == nil {
   366  		return
   367  	}
   368  
   369  	// evt.InSync is set when snapshot is complete or for every incremental update
   370  	// Since Watcher is stateful, we will collect query snapshot data without processing
   371  	// till its complete.
   372  	if state.inSnapshot {
   373  		state.pendingSnapshot = append(state.pendingSnapshot, evt.Changes...)
   374  		if evt.InSync {
   375  			// processes snapshot for this query only
   376  			// changes however may be only a diff from previous known state
   377  			changes = state.processSnapshot()
   378  		}
   379  	} else {
   380  		changes = state.transform(evt.Changes)
   381  	}
   382  	if evt.InSync {
   383  		if !state.inSync {
   384  			state.inSync = true
   385  			if pw.allInSync() {
   386  				pw.processSnapshot(ctx)
   387  				return
   388  			}
   389  		} else if pw.inSyncFlag > 0 {
   390  			evt := &WatcherEvent{WatcherEventBase: gotenaccess.NewWatcherEventBase(false, pw.filtersVersion), Changes: changes}
   391  			pw.sendEvent(ctx, evt)
   392  		}
   393  	}
   394  }
   395  
   396  func (pw *Watcher) processSyncLost(ctx context.Context) {
   397  	gotenobservability.LoggerFromContext(ctx).Infof("Notifiying watcher-wide sync lost")
   398  	atomic.StoreInt32(&pw.inSyncFlag, 0)
   399  	evt := &WatcherEvent{WatcherEventBase: gotenaccess.NewWatcherEventBaseLostSync(pw.filtersVersion)}
   400  	pw.sendEvent(ctx, evt)
   401  }
   402  
   403  func (pw *Watcher) processSnapshot(ctx context.Context) {
   404  	gotenobservability.LoggerFromContext(ctx).Infof("Watcher in sync")
   405  	atomic.StoreInt32(&pw.inSyncFlag, 1)
   406  	snapshot := make([]*WatcherEventChange, 0)
   407  	for _, state := range pw.queryWatcherStates {
   408  		for _, item := range state.cache {
   409  			snapshot = append(snapshot, NewAddWatcherEventChange(item))
   410  		}
   411  	}
   412  	evt := &WatcherEvent{WatcherEventBase: gotenaccess.NewWatcherEventBase(true, pw.filtersVersion), Changes: snapshot}
   413  	pw.sendEvent(ctx, evt)
   414  }
   415  
   416  func (pw *Watcher) allInSync() bool {
   417  	for _, state := range pw.queryWatcherStates {
   418  		if !state.inSync {
   419  			return false
   420  		}
   421  	}
   422  	return true
   423  }
   424  
   425  func (pw *Watcher) sendEvent(ctx context.Context, evt *WatcherEvent) {
   426  	select {
   427  	case <-ctx.Done():
   428  	case pw.outputEvtChan <- *evt:
   429  	}
   430  }
   431  
   432  type WatcherEventChange struct {
   433  	pre, post *group.Group
   434  	name      *group.Name
   435  }
   436  
   437  func NewAddWatcherEventChange(resource *group.Group) *WatcherEventChange {
   438  	return &WatcherEventChange{
   439  		post: resource,
   440  		name: resource.GetName(),
   441  	}
   442  }
   443  
   444  func NewModifyWatcherEventChange(current, previous *group.Group) *WatcherEventChange {
   445  	return &WatcherEventChange{
   446  		pre:  previous,
   447  		post: current,
   448  		name: current.GetName(),
   449  	}
   450  }
   451  
   452  func NewDeleteWatcherEventChange(deleted *group.Group) *WatcherEventChange {
   453  	return &WatcherEventChange{
   454  		pre:  deleted,
   455  		name: deleted.GetName(),
   456  	}
   457  }
   458  
   459  func (c *WatcherEventChange) GetName() *group.Name {
   460  	return c.name
   461  }
   462  
   463  func (c *WatcherEventChange) GetRawName() gotenresource.Name {
   464  	return c.name
   465  }
   466  
   467  func (c *WatcherEventChange) IsAdd() bool {
   468  	return c.pre == nil && c.post != nil
   469  }
   470  
   471  func (c *WatcherEventChange) IsModify() bool {
   472  	return c.pre != nil && c.post != nil
   473  }
   474  
   475  func (c *WatcherEventChange) IsDelete() bool {
   476  	return c.pre != nil && c.post == nil
   477  }
   478  
   479  func (c *WatcherEventChange) GetAdded() *group.Group {
   480  	if c.IsAdd() {
   481  		return c.post
   482  	}
   483  	return nil
   484  }
   485  
   486  func (c *WatcherEventChange) GetDeleted() *group.Group {
   487  	if c.IsDelete() {
   488  		return c.pre
   489  	}
   490  	return nil
   491  }
   492  
   493  func (c *WatcherEventChange) GetPrevious() *group.Group {
   494  	if c.IsModify() {
   495  		return c.pre
   496  	}
   497  	return nil
   498  }
   499  
   500  func (c *WatcherEventChange) GetCurrent() *group.Group {
   501  	if c.IsAdd() || c.IsModify() {
   502  		return c.post
   503  	}
   504  	return nil
   505  }
   506  
   507  func (c *WatcherEventChange) GetRawAdded() gotenresource.Resource {
   508  	return c.GetAdded()
   509  }
   510  
   511  func (c *WatcherEventChange) GetRawDeleted() gotenresource.Resource {
   512  	return c.GetDeleted()
   513  }
   514  
   515  func (c *WatcherEventChange) GetRawPrevious() gotenresource.Resource {
   516  	return c.GetPrevious()
   517  }
   518  
   519  func (c *WatcherEventChange) GetRawCurrent() gotenresource.Resource {
   520  	return c.GetCurrent()
   521  }
   522  
   523  type WatcherEvent struct {
   524  	gotenaccess.WatcherEventBase
   525  	Changes []*WatcherEventChange
   526  }
   527  
   528  func (e *WatcherEvent) GetAt(index int) *WatcherEventChange {
   529  	if e == nil || len(e.Changes) <= index {
   530  		return nil
   531  	}
   532  	return e.Changes[index]
   533  }
   534  
   535  func (e *WatcherEvent) GetRawAt(index int) gotenaccess.WatcherEventChange {
   536  	return e.GetAt(index)
   537  }
   538  
   539  func (e *WatcherEvent) Length() int {
   540  	if e == nil {
   541  		return 0
   542  	}
   543  	return len(e.Changes)
   544  }
   545  
   546  func (e *WatcherEvent) AppendChange(change *WatcherEventChange) {
   547  	e.Changes = append(e.Changes, change)
   548  }
   549  
   550  func (e *WatcherEvent) AppendRawChange(change gotenaccess.WatcherEventChange) {
   551  	e.Changes = append(e.Changes, change.(*WatcherEventChange))
   552  }
   553  
   554  // Merge makes a shallow merge of two events
   555  func (e *WatcherEvent) Merge(src *WatcherEvent) {
   556  	if src == nil {
   557  		return
   558  	}
   559  	if src.LostSync() || src.Resync() {
   560  		e.WatcherEventBase = src.WatcherEventBase
   561  		e.Changes = src.Changes
   562  		return
   563  	}
   564  	if !e.WatcherEventBase.Resync() {
   565  		e.WatcherEventBase = src.WatcherEventBase
   566  		e.Changes = append(e.Changes, src.Changes...)
   567  		return
   568  	}
   569  
   570  	// merge non-resync into resync
   571  	byName := make(map[group.Name]*group.Group, len(src.Changes)+len(e.Changes))
   572  	for _, currentChange := range e.Changes {
   573  		group := currentChange.GetCurrent()
   574  		byName[*group.GetName()] = group
   575  	}
   576  	for _, change := range src.Changes {
   577  		if change.IsDelete() {
   578  			delete(byName, *change.GetName())
   579  		} else {
   580  			group := change.GetCurrent()
   581  			byName[*group.GetName()] = group
   582  		}
   583  	}
   584  	e.Changes = make([]*WatcherEventChange, 0, len(byName))
   585  	for _, group := range byName {
   586  		e.Changes = append(e.Changes, NewAddWatcherEventChange(group))
   587  	}
   588  }
   589  
   590  func (qws *queryWatcherState) processSnapshot() []*WatcherEventChange {
   591  	watcherEventChanges := make([]*WatcherEventChange, 0, len(qws.pendingSnapshot))
   592  	prevCache := qws.cache
   593  	qws.cache = map[group.Name]*group.Group{}
   594  
   595  	for _, change := range qws.pendingSnapshot {
   596  		var currentItem *group.Group
   597  		switch item := change.ChangeType.(type) {
   598  		case *group.GroupChange_Added_:
   599  			currentItem = item.Added.GetGroup()
   600  		case *group.GroupChange_Current_:
   601  			currentItem = item.Current.GetGroup()
   602  		}
   603  		name := *currentItem.GetName()
   604  		qws.cache[name] = currentItem
   605  		previous, exists := prevCache[name]
   606  		if !exists {
   607  			watcherEventChanges = append(watcherEventChanges, NewAddWatcherEventChange(currentItem))
   608  		} else {
   609  			if !proto.Equal(currentItem, previous) {
   610  				watcherEventChanges = append(watcherEventChanges, NewModifyWatcherEventChange(currentItem, previous))
   611  			}
   612  			delete(prevCache, name)
   613  		}
   614  	}
   615  	// remaining items
   616  	for _, resource := range prevCache {
   617  		watcherEventChanges = append(watcherEventChanges, NewDeleteWatcherEventChange(resource))
   618  	}
   619  	qws.pendingSnapshot = nil
   620  	qws.inSnapshot = false
   621  	return watcherEventChanges
   622  }
   623  
   624  func (qws *queryWatcherState) transform(rawChanges []*group.GroupChange) []*WatcherEventChange {
   625  	watcherEventChanges := make([]*WatcherEventChange, 0, len(rawChanges))
   626  
   627  	for _, change := range rawChanges {
   628  		switch item := change.ChangeType.(type) {
   629  		case *group.GroupChange_Added_:
   630  			newItem := item.Added.GetGroup()
   631  			qws.cache[*newItem.GetName()] = newItem
   632  			watcherEventChanges = append(watcherEventChanges, NewAddWatcherEventChange(newItem))
   633  		case *group.GroupChange_Modified_:
   634  			updatedItem := item.Modified.GetGroup()
   635  			previousItem := qws.cache[*updatedItem.GetName()]
   636  			qws.cache[*updatedItem.GetName()] = updatedItem
   637  			watcherEventChanges = append(watcherEventChanges, NewModifyWatcherEventChange(updatedItem, previousItem))
   638  		case *group.GroupChange_Current_:
   639  			currentItem := item.Current.GetGroup()
   640  			previousItem := qws.cache[*currentItem.GetName()]
   641  			qws.cache[*currentItem.GetName()] = currentItem
   642  			if previousItem != nil {
   643  				watcherEventChanges = append(watcherEventChanges, NewModifyWatcherEventChange(currentItem, previousItem))
   644  			} else {
   645  				watcherEventChanges = append(watcherEventChanges, NewAddWatcherEventChange(currentItem))
   646  			}
   647  		case *group.GroupChange_Removed_:
   648  			deletedItem := qws.cache[*item.Removed.Name]
   649  			delete(qws.cache, *item.Removed.Name)
   650  			watcherEventChanges = append(watcherEventChanges, NewDeleteWatcherEventChange(deletedItem))
   651  		}
   652  	}
   653  	return watcherEventChanges
   654  }
   655  
   656  func (qws *queryWatcherState) computeSize(pendingChanges []*group.GroupChange) int64 {
   657  	size := int64(len(qws.cache))
   658  	for _, pendingChange := range append(qws.pendingSnapshot, pendingChanges...) {
   659  		name := *pendingChange.GetGroupName()
   660  		if pendingChange.IsDelete() {
   661  			if _, exists := qws.cache[name]; exists {
   662  				size--
   663  			}
   664  		} else {
   665  			if _, exists := qws.cache[name]; !exists {
   666  				size++
   667  			}
   668  		}
   669  	}
   670  	return size
   671  }
   672  
   673  func init() {
   674  	gotenaccess.GetRegistry().RegisterWatcherConstructor(group.GetDescriptor(), func(cc grpc.ClientConnInterface, params *gotenaccess.WatcherConfigParams, filters ...gotenaccess.WatcherFilterParams) gotenaccess.Watcher {
   675  		cfg := &WatcherConfig{
   676  			WatcherConfigBase: params.CfgBase,
   677  			WatchType:         params.WatchType,
   678  			View:              params.View,
   679  			ChunkSize:         params.ChunkSize,
   680  		}
   681  		if params.FieldMask != nil {
   682  			cfg.FieldMask = params.FieldMask.(*group.Group_FieldMask)
   683  		}
   684  		if params.OrderBy != nil {
   685  			cfg.OrderBy = params.OrderBy.(*group.OrderBy)
   686  		}
   687  		typedFilters := make([]*WatcherFilterParams, 0, len(filters))
   688  		for _, filter := range filters {
   689  			typedFilters = append(typedFilters, filter.(*WatcherFilterParams))
   690  		}
   691  		return NewWatcher(group_client.NewGroupServiceClient(cc), cfg, typedFilters...)
   692  	})
   693  	gotenaccess.GetRegistry().RegisterWatcherFilterConstructor(group.GetDescriptor(), func(filter gotenresource.Filter, parent gotenresource.Name) gotenaccess.WatcherFilterParams {
   694  		params := &WatcherFilterParams{}
   695  		if filter != nil {
   696  			params.Filter = filter.(*group.Filter)
   697  		}
   698  		if parent != nil {
   699  			params.Parent = parent.(*group.ParentName)
   700  		}
   701  		return params
   702  	})
   703  }