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

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