-
Notifications
You must be signed in to change notification settings - Fork 140
CBG-4751: Allow CV to be used as OCC value in REST API for document writes (updates/deletes) #7693
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
21f3b3f
d18431d
8a1646c
ad00f83
169f034
abb7c1b
c6ffd9b
79edf12
f17891c
e5b7067
da06d68
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -310,13 +310,13 @@ func (db *DatabaseCollectionWithUser) Get1xBody(ctx context.Context, docid strin | |
| } | ||
|
|
||
| // Get Rev with all-or-none history based on specified 'history' flag | ||
| func (db *DatabaseCollectionWithUser) Get1xRevBody(ctx context.Context, docid, revid string, history bool, attachmentsSince []string) (Body, error) { | ||
| func (db *DatabaseCollectionWithUser) Get1xRevBody(ctx context.Context, docid, revOrCV string, history bool, attachmentsSince []string) (Body, error) { | ||
| maxHistory := 0 | ||
| if history { | ||
| maxHistory = math.MaxInt32 | ||
| } | ||
|
|
||
| return db.Get1xRevBodyWithHistory(ctx, docid, revid, Get1xRevBodyOptions{ | ||
| return db.Get1xRevBodyWithHistory(ctx, docid, revOrCV, Get1xRevBodyOptions{ | ||
| MaxHistory: maxHistory, | ||
| HistoryFrom: nil, | ||
| AttachmentsSince: attachmentsSince, | ||
|
|
@@ -333,8 +333,8 @@ type Get1xRevBodyOptions struct { | |
| } | ||
|
|
||
| // Retrieves rev with request history specified as collection of revids (historyFrom) | ||
| func (db *DatabaseCollectionWithUser) Get1xRevBodyWithHistory(ctx context.Context, docid, revtreeid string, opts Get1xRevBodyOptions) (Body, error) { | ||
| rev, err := db.getRev(ctx, docid, revtreeid, opts.MaxHistory, opts.HistoryFrom) | ||
| func (db *DatabaseCollectionWithUser) Get1xRevBodyWithHistory(ctx context.Context, docid, revOrCV string, opts Get1xRevBodyOptions) (Body, error) { | ||
| rev, err := db.getRev(ctx, docid, revOrCV, opts.MaxHistory, opts.HistoryFrom) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
@@ -1093,7 +1093,11 @@ func (db *DatabaseCollectionWithUser) Put(ctx context.Context, docid string, bod | |
| generation++ | ||
| delete(body, BodyRev) | ||
|
|
||
| // Not extracting it yet because we need this property around to generate a rev ID | ||
| // remove CV before RevTreeID generation | ||
| matchCV, _ := body[BodyCV].(string) | ||
| delete(body, BodyCV) | ||
|
|
||
| // Not extracting it yet because we need this property around to generate a RevTreeID | ||
| deleted, _ := body[BodyDeleted].(bool) | ||
|
|
||
| expiry, err := body.ExtractExpiry() | ||
|
|
@@ -1146,21 +1150,42 @@ func (db *DatabaseCollectionWithUser) Put(ctx context.Context, docid string, bod | |
| } | ||
|
|
||
| var conflictErr error | ||
| // Make sure matchRev matches an existing leaf revision: | ||
| if matchRev == "" { | ||
| matchRev = doc.CurrentRev | ||
| if matchRev != "" { | ||
| // PUT with no parent rev given, but there is an existing current revision. | ||
| // This is OK as long as the current one is deleted. | ||
| if !doc.History[matchRev].Deleted { | ||
| conflictErr = base.HTTPErrorf(http.StatusConflict, "Document exists") | ||
| } else { | ||
| generation, _ = ParseRevID(ctx, matchRev) | ||
| generation++ | ||
|
|
||
| // OCC check of matchCV against CV on the doc | ||
| if matchCV != "" { | ||
| if matchCV == doc.HLV.GetCurrentVersionString() { | ||
| // set matchRev to the current revision ID and allow existing codepaths to perform RevTree-based update. | ||
| matchRev = doc.CurrentRev | ||
| // bump generation based on retrieved RevTree ID | ||
| generation, _ = ParseRevID(ctx, matchRev) | ||
torcolvin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| generation++ | ||
| } else if doc.hasFlag(channels.Conflict | channels.Hidden) { | ||
| // Can't use CV as an OCC Value when a document is in conflict, or we're updating the non-winning leaf | ||
| // There's no way to get from a given old CV to a RevTreeID to perform the update correctly, since we don't maintain linear history for a given SourceID. | ||
| // Reject the request and force the user to resolve the conflict using RevTree IDs which does have linear history available. | ||
| conflictErr = base.HTTPErrorf(http.StatusBadRequest, "Cannot use CV to modify a document in conflict - resolve first with RevTree ID") | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there any way that this is going to get hit for a blip code pathway? If it does, will the blip code know how to handle a 400 with this error message?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope, it can't happen. BLIP goes through
bbrks marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } else { | ||
| conflictErr = base.HTTPErrorf(http.StatusConflict, "Document revision conflict") | ||
| } | ||
| } | ||
|
|
||
| if conflictErr == nil { | ||
| // Make sure matchRev matches an existing leaf revision: | ||
| if matchRev == "" { | ||
| matchRev = doc.CurrentRev | ||
| if matchRev != "" { | ||
| // PUT with no parent rev given, but there is an existing current revision. | ||
| // This is OK as long as the current one is deleted. | ||
| if !doc.History[matchRev].Deleted { | ||
| conflictErr = base.HTTPErrorf(http.StatusConflict, "Document exists") | ||
| } else { | ||
| generation, _ = ParseRevID(ctx, matchRev) | ||
| generation++ | ||
| } | ||
| } | ||
| } else if !doc.History.isLeaf(matchRev) || db.IsIllegalConflict(ctx, doc, matchRev, deleted, false, nil) { | ||
| conflictErr = base.HTTPErrorf(http.StatusConflict, "Document revision conflict") | ||
| } | ||
| } else if !doc.History.isLeaf(matchRev) || db.IsIllegalConflict(ctx, doc, matchRev, deleted, false, nil) { | ||
| conflictErr = base.HTTPErrorf(http.StatusConflict, "Document revision conflict") | ||
| } | ||
|
|
||
| // Make up a new _rev, and add it to the history: | ||
|
|
@@ -2893,7 +2918,8 @@ func (db *DatabaseCollectionWithUser) MarkPrincipalsChanged(ctx context.Context, | |
|
|
||
| // Creates a new document, assigning it a random doc ID. | ||
| func (db *DatabaseCollectionWithUser) Post(ctx context.Context, body Body) (docid string, rev string, doc *Document, err error) { | ||
| if body[BodyRev] != nil { | ||
| // This error isn't very accurate, you just _cannot_ use POST to update an existing document - even if it does exist. We don't even bother checking for existence. | ||
bbrks marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if body[BodyRev] != nil || body[BodyCV] != nil { | ||
| return "", "", nil, base.HTTPErrorf(http.StatusNotFound, "No previous revision to replace") | ||
| } | ||
|
|
||
|
|
@@ -2914,8 +2940,9 @@ func (db *DatabaseCollectionWithUser) Post(ctx context.Context, body Body) (doci | |
| } | ||
|
|
||
| // Deletes a document, by adding a new revision whose _deleted property is true. | ||
| func (db *DatabaseCollectionWithUser) DeleteDoc(ctx context.Context, docid string, revid string) (string, *Document, error) { | ||
| body := Body{BodyDeleted: true, BodyRev: revid} | ||
| func (db *DatabaseCollectionWithUser) DeleteDoc(ctx context.Context, docid string, docVersion DocVersion) (string, *Document, error) { | ||
| versionKey, versionStr := docVersion.Body1xKVPair() | ||
| body := Body{BodyDeleted: true, versionKey: versionStr} | ||
| newRevID, doc, err := db.Put(ctx, docid, body) | ||
| return newRevID, doc, err | ||
| } | ||
|
|
@@ -3342,6 +3369,7 @@ func (db *DatabaseCollectionWithUser) CheckProposedVersion(ctx context.Context, | |
| // previousRev may be revTreeID or version | ||
| var previousVersion Version | ||
| previousRevFormat := "version" | ||
| // TODO: CBG-4812 Use base.IsRevTreeID | ||
| if !strings.Contains(previousRev, "@") { | ||
bbrks marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| previousRevFormat = "revTreeID" | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.