@@ -958,7 +958,7 @@ func (db *DatabaseCollectionWithUser) Put(ctx context.Context, docid string, bod
958958 }
959959
960960 // Make up a new _rev, and add it to the history:
961- bodyWithoutInternalProps , wasStripped := stripInternalProperties (body )
961+ bodyWithoutInternalProps , wasStripped := StripInternalProperties (body )
962962 canonicalBytesForRevID , err := base .JSONMarshalCanonical (bodyWithoutInternalProps )
963963 if err != nil {
964964 return nil , nil , false , nil , err
@@ -1174,87 +1174,47 @@ func (db *DatabaseCollectionWithUser) PutExistingRevWithBody(ctx context.Context
11741174
11751175}
11761176
1177- // SyncFnDryrun Runs a document through the sync function and returns expiry, channels doc was placed in, access map for users, roles, handler errors and sync fn exceptions
1178- func (db * DatabaseCollectionWithUser ) SyncFnDryrun (ctx context.Context , body Body , docID string ) (* channels.ChannelMapperOutput , error , error ) {
1179- doc := & Document {
1180- ID : docID ,
1181- _body : body ,
1182- }
1183- oldDoc := doc
1184- if docID != "" {
1185- if docInBucket , err := db .GetDocument (ctx , docID , DocUnmarshalAll ); err == nil {
1186- oldDoc = docInBucket
1187- if doc ._body == nil {
1188- body = oldDoc .Body (ctx )
1189- doc ._body = body
1190- // If no body is given, use doc in bucket as doc with no old doc
1191- oldDoc ._body = nil
1192- }
1193- doc ._body [BodyRev ] = oldDoc .SyncData .CurrentRev
1194- } else {
1195- return nil , err , nil
1196- }
1197- } else {
1198- oldDoc ._body = nil
1199- }
1200-
1201- delete (body , BodyId )
1202-
1203- // Get the revision ID to match, and the new generation number:
1204- matchRev , _ := body [BodyRev ].(string )
1205- generation , _ := ParseRevID (ctx , matchRev )
1206- if generation < 0 {
1207- return nil , base .HTTPErrorf (http .StatusBadRequest , "Invalid revision ID" ), nil
1177+ // SyncFnDryrun Runs the given document body through a sync function and returns expiry, channels doc was placed in,
1178+ // access map for users, roles, handler errors and sync fn exceptions.
1179+ // If syncFn is provided, it will be used instead of the one configured on the database.
1180+ func (db * DatabaseCollectionWithUser ) SyncFnDryrun (ctx context.Context , newDoc , oldDoc * Document , userMeta , syncOptions map [string ]any , syncFn string , errorLogFunc , infoLogFunc func (string )) (* channels.ChannelMapperOutput , error ) {
1181+ mutableBody , metaMap , _ , err := db .prepareSyncFn (oldDoc , newDoc )
1182+ if err != nil {
1183+ base .InfofCtx (ctx , base .KeyDiagnostic , "Failed to prepare to run sync function: %v" , err )
1184+ return nil , err
12081185 }
1209- generation ++
12101186
1211- // Create newDoc which will be used to pass around Body
1212- newDoc := & Document {
1213- ID : docID ,
1187+ if userMeta != nil {
1188+ metaMap = userMeta
12141189 }
1215- // Pull out attachments
1216- newDoc .DocAttachments = GetBodyAttachments (body )
1217- delete (body , BodyAttachments )
12181190
1219- delete (body , BodyRevisions )
1220-
1221- err := validateAPIDocUpdate (body )
1222- if err != nil {
1223- return nil , err , nil
1224- }
1225- bodyWithoutInternalProps , wasStripped := stripInternalProperties (body )
1226- canonicalBytesForRevID , err := base .JSONMarshalCanonical (bodyWithoutInternalProps )
1227- if err != nil {
1228- return nil , err , nil
1191+ if syncOptions == nil {
1192+ syncOptions = MakeUserCtx (db .user , db .ScopeName , db .Name )
12291193 }
12301194
1231- // We needed to keep _deleted around in the body until we generated a rev ID, but now we can ditch it.
1232- _ , isDeleted := body [BodyDeleted ]
1233- if isDeleted {
1234- delete (body , BodyDeleted )
1195+ // fetch configured sync function if one is not provided
1196+ if syncFn == "" {
1197+ if db .ChannelMapper != nil {
1198+ syncFn = db .ChannelMapper .Function ()
1199+ } else {
1200+ scopeAndCollectionName := db .ScopeAndCollectionName ()
1201+ syncFn = channels .GetDefaultSyncFunction (scopeAndCollectionName .Scope , scopeAndCollectionName .Collection )
1202+ }
12351203 }
12361204
1237- // and now we can finally update the newDoc body to be without any special properties
1238- newDoc .UpdateBody (body )
1239-
1240- // If no special properties were stripped and document wasn't deleted, the canonical bytes represent the current
1241- // body. In this scenario, store canonical bytes as newDoc._rawBody
1242- if ! wasStripped && ! isDeleted {
1243- newDoc ._rawBody = canonicalBytesForRevID
1205+ // create new sync runner instance for this dry run
1206+ jsTimeout := time .Duration (base .DefaultJavascriptTimeoutSecs ) * time .Second
1207+ syncRunner , err := channels .NewSyncRunnerWithLogging (ctx , syncFn , jsTimeout , errorLogFunc , infoLogFunc )
1208+ if err != nil {
1209+ return nil , fmt .Errorf ("failed to create sync runner: %v" , err )
12441210 }
12451211
1246- newRev := CreateRevIDWithBytes (generation , matchRev , canonicalBytesForRevID )
1247- newDoc .RevID = newRev
1248- mutableBody , metaMap , _ , err := db .prepareSyncFn (oldDoc , newDoc )
1212+ jsOutput , err := syncRunner .Call (ctx , mutableBody , sgbucket .JSONString (oldDoc ._rawBody ), metaMap , syncOptions )
12491213 if err != nil {
1250- base .InfofCtx (ctx , base .KeyDiagnostic , "Failed to prepare to run sync function: %v" , err )
1251- return nil , err , nil
1214+ return nil , & base.SyncFnDryRunError {Err : err }
12521215 }
12531216
1254- output , err := db .ChannelMapper .MapToChannelsAndAccess (ctx , mutableBody , string (oldDoc ._rawBody ), metaMap ,
1255- MakeUserCtx (db .user , db .ScopeName , db .Name ))
1256-
1257- return output , nil , err
1217+ return jsOutput .(* channels.ChannelMapperOutput ), nil
12581218}
12591219
12601220// resolveConflict runs the conflictResolverFunction with doc and newDoc. doc and newDoc's bodies and revision trees
@@ -1596,7 +1556,7 @@ func (db *DatabaseCollectionWithUser) storeOldBodyInRevTreeAndUpdateCurrent(ctx
15961556
15971557func (db * DatabaseCollectionWithUser ) prepareSyncFn (doc * Document , newDoc * Document ) (mutableBody Body , metaMap map [string ]interface {}, newRevID string , err error ) {
15981558 // Marshal raw user xattrs for use in Sync Fn. If this fails we can bail out so we should do early as possible.
1599- metaMap , err = doc .GetMetaMap (db .userXattrKey ())
1559+ metaMap , err = doc .GetMetaMap (db .UserXattrKey ())
16001560 if err != nil {
16011561 return
16021562 }
0 commit comments