@@ -42,6 +42,8 @@ type ChangesOptions struct {
4242 VersionType ChangesVersionType // The type of version to use for the changes feed. This is used to determine whether to use send revtree IDs or CV in the changes feed entries.
4343}
4444
45+ // ChangesVersionType determines the preferred version type to use in changes feed entries.
46+ // If the requested version type is not available, the feed will attempt to fall back to ChangesVersionTypeRevTreeID which should always be available.
4547type ChangesVersionType string
4648
4749const (
@@ -60,6 +62,15 @@ func ParseChangesVersionType(s string) (ChangesVersionType, error) {
6062 }
6163}
6264
65+ // ChangeVersionString attempts to return the version string for the preferred ChangesVersionType, but will fall back to rev if cv is not available when requested.
66+ func (ce * ChangeEntry ) ChangeVersionString (versionType ChangesVersionType ) string {
67+ if s , ok := ce .Changes [0 ][versionType ]; ok {
68+ return s
69+ }
70+ // requested version type not found, return `rev` as a fallback.
71+ return ce .Changes [0 ][ChangesVersionTypeRevTreeID ]
72+ }
73+
6374// A changes entry; Database.GetChanges returns an array of these.
6475// Marshals into the standard CouchDB _changes format.
6576type ChangeEntry struct {
@@ -128,7 +139,10 @@ func (db *DatabaseCollectionWithUser) addDocToChangeEntry(ctx context.Context, e
128139 base .WarnfCtx (ctx , "Changes feed: error getting doc %q: %v" , base .UD (entry .ID ), err )
129140 return
130141 }
131- db .AddDocInstanceToChangeEntry (ctx , entry , doc , options )
142+ if err := db .AddDocInstanceToChangeEntry (ctx , entry , doc , options ); err != nil {
143+ base .WarnfCtx (ctx , "Changes feed: error adding doc %q to change entry: %v" , base .UD (entry .ID ), err )
144+ return
145+ }
132146
133147 } else if includeConflicts {
134148 // Load doc metadata only
@@ -139,11 +153,14 @@ func (db *DatabaseCollectionWithUser) addDocToChangeEntry(ctx context.Context, e
139153 base .WarnfCtx (ctx , "Changes feed: error getting doc sync data %q: %v" , base .UD (entry .ID ), err )
140154 return
141155 }
142- db .AddDocInstanceToChangeEntry (ctx , entry , doc , options )
156+ if err := db .AddDocInstanceToChangeEntry (ctx , entry , doc , options ); err != nil {
157+ base .WarnfCtx (ctx , "Changes feed: error adding doc %q to change entry: %v" , base .UD (entry .ID ), err )
158+ return
159+ }
143160
144161 } else if options .IncludeDocs {
145162 // Retrieve document via rev cache
146- revID := entry .Changes [ 0 ][ "rev" ]
163+ revID := entry .ChangeVersionString ( options . VersionType )
147164 err := db .AddDocToChangeEntryUsingRevCache (ctx , entry , revID )
148165 if err != nil {
149166 base .WarnfCtx (ctx , "Changes feed: error getting revision body for %q (%s): %v" , base .UD (entry .ID ), revID , err )
@@ -162,12 +179,16 @@ func (db *DatabaseCollectionWithUser) AddDocToChangeEntryUsingRevCache(ctx conte
162179}
163180
164181// Adds a document body and/or its conflicts to a ChangeEntry
165- func (db * DatabaseCollectionWithUser ) AddDocInstanceToChangeEntry (ctx context.Context , entry * ChangeEntry , doc * Document , options ChangesOptions ) {
182+ func (db * DatabaseCollectionWithUser ) AddDocInstanceToChangeEntry (ctx context.Context , entry * ChangeEntry , doc * Document , options ChangesOptions ) error {
166183
167184 includeConflicts := options .Conflicts && entry .branched
168185
169- revID := entry .Changes [ 0 ][ "rev" ]
186+ revID := entry .ChangeVersionString ( options . VersionType )
170187 if includeConflicts {
188+ // should've been validated in the handler layer but be defensive
189+ if options .VersionType == ChangesVersionTypeCV {
190+ return fmt .Errorf ("changes feed does not support showing in-conflict revisions when using version_type=cv" )
191+ }
171192 doc .History .forEachLeaf (func (leaf * RevInfo ) {
172193 if leaf .ID != revID {
173194 if ! leaf .Deleted {
@@ -187,6 +208,8 @@ func (db *DatabaseCollectionWithUser) AddDocInstanceToChangeEntry(ctx context.Co
187208 base .WarnfCtx (ctx , "Changes feed: error getting doc %q/%q: %v" , base .UD (doc .ID ), revID , err )
188209 }
189210 }
211+
212+ return nil
190213}
191214
192215// Parameters
@@ -553,7 +576,7 @@ func makeRevocationChangeEntry(ctx context.Context, logEntry *LogEntry, seqID Se
553576}
554577
555578// AuditReadEvent issues a read event for this change entry. If there is no document body, there will be no event used.
556- func (ce * ChangeEntry ) AuditReadEvent (ctx context.Context ) {
579+ func (ce * ChangeEntry ) AuditReadEvent (ctx context.Context , versionType ChangesVersionType ) {
557580 if ce .Err != nil {
558581 return
559582 }
@@ -562,7 +585,7 @@ func (ce *ChangeEntry) AuditReadEvent(ctx context.Context) {
562585 }
563586 base .Audit (ctx , base .AuditIDDocumentRead , base.AuditFields {
564587 base .AuditFieldDocID : ce .ID ,
565- base .AuditFieldDocVersion : ce .Changes [ 0 ][ "rev" ] ,
588+ base .AuditFieldDocVersion : ce .ChangeVersionString ( versionType ) ,
566589 })
567590}
568591
@@ -1387,7 +1410,10 @@ func createChangesEntry(ctx context.Context, docid string, db *DatabaseCollectio
13871410
13881411 row .Removed = base .SetFromArray (removedChannels )
13891412 if options .IncludeDocs || options .Conflicts {
1390- db .AddDocInstanceToChangeEntry (ctx , row , populatedDoc , options )
1413+ if err := db .AddDocInstanceToChangeEntry (ctx , row , populatedDoc , options ); err != nil {
1414+ base .WarnfCtx (ctx , "Unable to add doc instance to change entry for %s: %v" , base .UD (docid ), err )
1415+ return nil
1416+ }
13911417 }
13921418
13931419 return row
0 commit comments