MCPcopy Create free account
hub / github.com/couchbase/sync_gateway / getOCCValue

Method getOCCValue

rest/doc_api.go:354–413  ·  view source on GitHub ↗

getOCCValue retrieves the optimistic concurrency control value for a document (Revision ID or CV) from several possible sources: - Query parameter "rev" - If-Match header - Body field "_rev" or "_cv" (if the body is provided in the request) It also validates that the provided OCC value exactly match

(optionalBody db.Body)

Source from the content-addressed store, hash-verified

352// - Body field "_rev" or "_cv" (if the body is provided in the request)
353// It also validates that the provided OCC value exactly matches the corresponding body field if it exists in multiple places.
354func (h *handler) getOCCValue(optionalBody db.Body) (occValue string, occValueType occVersionType, err error) {
355 // skipBodyMatchValidation can skip the validation of the occValue inside the body, since in some cases we're pulling that value out of the body anyway.
356 var skipBodyMatchValidation bool
357
358 // occValue is the optimistic concurrency control value, which can be either the current rev ID or CV - used to prevent lost updates.
359 // we grab occValue from either query param, Etag header, or request body (in that order)
360 if revQuery := h.getQuery("rev"); revQuery != "" {
361 occValue = revQuery
362 // try to detect occ Values that are not URL Query escaped
363 // - `+` which can appear in base64 strings is converted to a space when not escaped properly
364 // other characters are difficult to correctly detect, since the value is already unescaped
365 if strings.ContainsAny(occValue, " ") {
366 return "", 0, base.HTTPErrorf(http.StatusBadRequest, "Bad rev query parameter: %q - ensure this query parameter value is URL Encoded", occValue)
367 }
368 occValueType = guessOCCVersionTypeFromValue(occValue)
369 } else if ifMatch, err := h.getEtag("If-Match"); err != nil {
370 return "", 0, err
371 } else if ifMatch != "" {
372 occValue = ifMatch
373 occValueType = guessOCCVersionTypeFromValue(occValue)
374 } else if bodyCV, ok := optionalBody[db.BodyCV]; ok {
375 if bodyCVStr, ok := bodyCV.(string); ok {
376 occValue = bodyCVStr
377 occValueType = VersionTypeCV
378 skipBodyMatchValidation = true
379 }
380 } else if bodyRev, ok := optionalBody[db.BodyRev]; ok {
381 if bodyRevStr, ok := bodyRev.(string); ok {
382 occValue = bodyRevStr
383 occValueType = VersionTypeRevTreeID
384 skipBodyMatchValidation = true
385 }
386 } else {
387 // empty occValue - treat as a create operation without any parent
388 return "", VersionTypeRevTreeID, nil
389 }
390
391 // defensive measure against falling out of above without a type set
392 if occValueType == VersionTypeUnknown {
393 return "", 0, base.HTTPErrorf(http.StatusBadRequest, "Invalid version type for OCC value: %q", occValue)
394 }
395
396 // ensure the value provided matches exactly the one that may also be supplied in the body
397 if !skipBodyMatchValidation {
398 switch occValueType {
399 case VersionTypeRevTreeID:
400 if optionalBody[db.BodyRev] != nil && occValue != optionalBody[db.BodyRev] {
401 return "", 0, base.HTTPErrorf(http.StatusBadRequest, "Revision IDs provided do not match")
402 }
403 case VersionTypeCV:
404 if optionalBody[db.BodyCV] != nil && occValue != optionalBody[db.BodyCV] {
405 return "", 0, base.HTTPErrorf(http.StatusBadRequest, "CVs provided do not match")
406 }
407 default:
408 return "", 0, base.HTTPErrorf(http.StatusBadRequest, "Unknown version type provided: %q", occValue)
409 }
410 }
411

Callers 4

handlePutAttachmentMethod · 0.95
handlePutDocMethod · 0.95
handleDeleteDocMethod · 0.95

Calls 4

getQueryMethod · 0.95
getEtagMethod · 0.95
HTTPErrorfFunction · 0.92

Tested by

no test coverage detected