Skip to content
This repository has been archived by the owner on Jan 2, 2023. It is now read-only.

Latest commit

 

History

History
197 lines (147 loc) · 5.65 KB

README.md

File metadata and controls

197 lines (147 loc) · 5.65 KB

Query for JSON patch

With patch operations patches can be specified in detail base on RFC6902 (test operation is not implemented - use RSQL Parser instead) This features are supported for MongoDB 4.2+.

There are five operations available:

Operation Description
remove remove the value at the target location.
add add a value or array to an array at the target location.
replace replaces the value at the target location with a new value.
move removes the value at a specified location and adds it to the target location.
copy copies the value from a specified location to the target location.

NOTE Mongo Object ID's can be written as 12 bytes long array or 24 character hex string. In addition - they currently only supported as single field of object or in array (not in map).

There are multiple possibilities on how to use this package:

  • Without any restrictions
  • With custom manually defined rules (soft restriction)
  • Witch reference model that enforce structure and types automatically
  • Witch reference model and rule annotations to maximize security

How to use

Without any restrictions

import (
  "github.com/StevenCyb/goapiutils/parser/mongo/jsonpatch"

  "go.mongodb.org/mongo-driver/mongo/options"
)
// ...

  var operations []jsonpatch.operation.Spec
  err = json.NewDecoder(req.Body).Decode(&operations)
  // ...

  parser := jsonpatch.NewParser()
  query, err := parser.Parse(operations...)
  // ...

  result := collection.FindOneAndUpdate(ctx, filter, query, updateOptions)
  // ...

With custom manually defined rules

Additionally, simple rules can be set:

Policy Description
DisallowPathPolicy specifies a path that is not allowed.
DisallowOperationOnPathPolicy disallows specified operation on path.
ForceTypeOnPathPolicy forces the value of a specif path to be from given type.
ForceRegexMatchPolicy forces the value of a specif path to match expression.
StrictPathPolicy forces path to be strictly one of.
The path fields can be set to * for any field name. E.g. *.version will match product.version but not version.
import (
  "github.com/StevenCyb/goapiutils/parser/mongo/jsonpatch"

  "go.mongodb.org/mongo-driver/mongo/options"
)
// ...

  var operations []jsonpatch.operation.Spec
  err = json.NewDecoder(req.Body).Decode(&operations)
  // ...

  parser := jsonpatch.NewParser(
    DisallowPathPolicy{Details: "illegal ID modification", Path: "_id"},
    ForceTypeOnPathPolicy{Details: "age as number", Path: "user.age", Kind: reflect.Int64},
  )
  query, err := parser.Parse(operations...)
  // ...

  result := collection.FindOneAndUpdate(ctx, filter, query, updateOptions)
  // ...

Witch reference model that enforce structure and types automatically

By using NewSmartParser and providing a reference type of the API resource, the parser automatically determine valid patches and types. Only fields with bson tag are considered. E.g. for the following example a path defined by patch request could be name or metadata.my_map_key.key and nothing else. A patch request event could not set a number as name since the type of name is string.

import (
  "github.com/StevenCyb/goapiutils/parser/mongo/jsonpatch"

  "go.mongodb.org/mongo-driver/mongo/options"
)
// ...

type Person struct {
  Name string `bson:"name"`
  Metadata map[string]struct{
    Key string `bson:"key"`
  } `bson:"metadata"`
  IgnoredField string
}

// ...

  var operations []jsonpatch.operation.Spec
  err = json.NewDecoder(req.Body).Decode(&operations)
  // ...

  parser := jsonpatch.NewSmartParser(reflect.TypeOf(Person{}))
  query, err := parser.Parse(operations...)
  // `err` will contain an error if any rule is violated
  // ...

  result := collection.FindOneAndUpdate(ctx, filter, query, updateOptions)
  // ...

Witch reference model and rule annotations to maximize security

The previous approach using NewSmartParser could be restricted even more.

Disallow any operation on field

Defined by jp_disallow:"<bool>". No operation with name as path is allowed.

type Person struct {
  Name string `bson:"name" jp_disallow:"true"`
}

Minimum for field

Defined by jp_min:"<float>". Differentiate by type:

  • size on array, map, slice or string.
  • numeric value on int and float
type Person struct {
  Age uint `bson:"age" jp_min:"18"`
}

Maximum for field

Defined by jp_max:"<float>". Differentiate by type:

  • size on array, map, slice or string.
  • numeric value on int and float
type Person struct {
  Points uint `bson:"points" jp_max:"100"`
}

Expression for field value

Defined by jp_expression:"<escaped_regular_expression>". The value is printed formatted (using %+v) before matching.

type Person struct {
  Name string `bson:"name" jp_expression:"^([A-Z][a-z]+ ?){2,}$"`
}

Allow only specific operations by whitelisting

Defined by jp_op_allowed:"add,remove,replace,move,copy" (would allow all). If the value of a field may only be changed (overwritten):

type Person struct {
  Name string `bson:"name" jp_op_allowed:"replace"`
}

Allow only specific operations by blacklisting

Defined by jp_op_disallowed:"add,remove,replace,move,copy" (same as jp_disallow). If a field should never be deleted:

type Person struct {
  Name string `bson:"name" jp_op_disallowed:"remove"`
}