Skip to content

Commit

Permalink
new: handle objectIds (#105)
Browse files Browse the repository at this point in the history
* fixed: check context done correctly

* new/manipmongo: support for ObjectIDs

* fixed: filters correctly converts oids in variadic

* fixed: check if given Id is a ObjectID before doing conversion
  • Loading branch information
primalmotion authored Sep 24, 2019
1 parent 93db1c1 commit ac8a227
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 33 deletions.
13 changes: 7 additions & 6 deletions maniphttp/manipulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -797,14 +797,15 @@ func (s *httpManipulator) send(
}

// We check is the main context expired.
// and if so, we return the last error
if time.Until(deadline) <= 0 {
select {
case <-mctx.Context().Done():
// If so, we return the last error
return nil, lastError
default:
// Otherwise we sleep backoff and we restart the retry loop.
time.Sleep(backoff.Next(try, deadline))
try++
}

// Then we sleep backoff and we restart the retry loop.
time.Sleep(backoff.Next(try, deadline))
try++
}
}

Expand Down
23 changes: 10 additions & 13 deletions maniphttp/manipulator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func TestHTTP_New(t *testing.T) {
So(m.namespace, ShouldEqual, "myns")
})

Convey("Then the it should implement Manpilater interface", func() {
Convey("Then the it should implement Manipulator interface", func() {

var i interface{} = m
var ok bool
Expand All @@ -77,7 +77,7 @@ func TestHTTP_New(t *testing.T) {
Convey("When I create a manipulator with a token manager that works", t, func() {

tm := maniptest.NewTestTokenManager()
tm.MockIssue(t, func(context.Context) (string, error) { return "token", nil })
tm.MockIssue(t, func(context.Context) (string, error) { return "old-token", nil })

mm, err := New(context.Background(), "http://url.com", OptionTokenManager(tm))
m := mm.(*httpManipulator)
Expand All @@ -90,21 +90,18 @@ func TestHTTP_New(t *testing.T) {
So(m.username, ShouldEqual, "Bearer")
})

Convey("Then password should be correct", func() {
So(m.currentPassword(), ShouldEqual, "token")
Convey("Then password should be old-token", func() {
So(m.currentPassword(), ShouldEqual, "old-token")
})

Convey("When I the token is renewed", func() {

tm.MockRun(t, func(ctx context.Context, tokenCh chan string) {
tokenCh <- "new-token"
})
tm.MockRun(t, func(ctx context.Context, tokenCh chan string) {
tokenCh <- "new-token"
})

time.Sleep(1 * time.Second) // concourse is sometimes slow...
time.Sleep(2 * time.Second) // concourse is sometimes slow...

Convey("Then password should be correct", func() {
So(m.currentPassword(), ShouldEqual, "new-token")
})
Convey("Then password should be new-token", func() {
So(m.currentPassword(), ShouldEqual, "new-token")
})
})

Expand Down
39 changes: 30 additions & 9 deletions manipmongo/internal/compiler/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,36 @@ func massageKey(key string) string {
return k
}

func massageValue(v interface{}) interface{} {
func massageValue(k string, v interface{}) interface{} {

if reflect.TypeOf(v).Name() == "Duration" {
return time.Now().Add(v.(time.Duration))
}

if k == "_id" {
switch sv := v.(type) {
case string:
if bson.IsObjectIdHex(sv) {
return bson.ObjectIdHex(sv)
}
}
}

return v
}

func massageValues(key string, values []interface{}) []interface{} {

k := massageKey(key)
out := make([]interface{}, len(values))

for i, v := range values {
out[i] = massageValue(k, v)
}

return out
}

// CompileFilter compiles the given manipulate Filter into a mongo filter.
func CompileFilter(f *elemental.Filter) bson.M {

Expand Down Expand Up @@ -84,29 +105,29 @@ func CompileFilter(f *elemental.Filter) bson.M {
)
}
default:
items = append(items, bson.M{k: bson.M{"$eq": massageValue(v)}})
items = append(items, bson.M{k: bson.M{"$eq": massageValue(k, v)}})
}

case elemental.NotEqualComparator:
items = append(items, bson.M{k: bson.M{"$ne": massageValue(f.Values()[i][0])}})
items = append(items, bson.M{k: bson.M{"$ne": massageValue(k, f.Values()[i][0])}})

case elemental.InComparator, elemental.ContainComparator:
items = append(items, bson.M{k: bson.M{"$in": f.Values()[i]}})
items = append(items, bson.M{k: bson.M{"$in": massageValues(k, f.Values()[i])}})

case elemental.NotInComparator, elemental.NotContainComparator:
items = append(items, bson.M{k: bson.M{"$nin": f.Values()[i]}})
items = append(items, bson.M{k: bson.M{"$nin": massageValues(k, f.Values()[i])}})

case elemental.GreaterOrEqualComparator:
items = append(items, bson.M{k: bson.M{"$gte": massageValue(f.Values()[i][0])}})
items = append(items, bson.M{k: bson.M{"$gte": massageValue(k, f.Values()[i][0])}})

case elemental.GreaterComparator:
items = append(items, bson.M{k: bson.M{"$gt": massageValue(f.Values()[i][0])}})
items = append(items, bson.M{k: bson.M{"$gt": massageValue(k, f.Values()[i][0])}})

case elemental.LesserOrEqualComparator:
items = append(items, bson.M{k: bson.M{"$lte": massageValue(f.Values()[i][0])}})
items = append(items, bson.M{k: bson.M{"$lte": massageValue(k, f.Values()[i][0])}})

case elemental.LesserComparator:
items = append(items, bson.M{k: bson.M{"$lt": massageValue(f.Values()[i][0])}})
items = append(items, bson.M{k: bson.M{"$lt": massageValue(k, f.Values()[i][0])}})

case elemental.ExistsComparator:
items = append(items, bson.M{k: bson.M{"$exists": true}})
Expand Down
123 changes: 121 additions & 2 deletions manipmongo/internal/compiler/filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,30 @@ import (

func TestUtils_compiler(t *testing.T) {

Convey("Given I have a empty manipulate.Filter", t, func() {

f := elemental.NewFilterComposer().Done()

Convey("When I compile the filter", func() {

b, _ := bson.MarshalJSON(CompileFilter(f))

Convey("Then the bson should be correct", func() {
So(strings.Replace(string(b), "\n", "", 1), ShouldEqual, `{}`)
})
})
})

Convey("Given I have a simple manipulate.Filter", t, func() {

f := elemental.NewFilterComposer().WithKey("id").Equals(1).Done()
f := elemental.NewFilterComposer().WithKey("id").Equals("5d83e7eedb40280001887565").Done()

Convey("When I compile the filter", func() {

b, _ := bson.MarshalJSON(CompileFilter(f))

Convey("Then the bson should be correct", func() {
So(strings.Replace(string(b), "\n", "", 1), ShouldEqual, `{"$and":[{"_id":{"$eq":1}}]}`)
So(strings.Replace(string(b), "\n", "", 1), ShouldEqual, `{"$and":[{"_id":{"$eq":{"$oid":"5d83e7eedb40280001887565"}}}]}`)
})
})
})
Expand Down Expand Up @@ -172,6 +186,111 @@ func TestUtils_compiler(t *testing.T) {
})
})

Convey("Given I have a single match on valid ID", t, func() {

f := elemental.NewFilterComposer().
WithKey("ID").Equals("5d85727b919e0c397a58e940").
Done()

Convey("When I compile the filter", func() {
b, _ := bson.MarshalJSON(CompileFilter(f))

Convey("Then the bson should be correct", func() {
So(strings.Replace(string(b), "\n", "", 1), ShouldEqual, `{"$and":[{"_id":{"$eq":{"$oid":"5d85727b919e0c397a58e940"}}}]}`)
})
})
})

Convey("Given I have a single match on invalid ID", t, func() {

f := elemental.NewFilterComposer().
WithKey("ID").Equals("not-object-id").
Done()

Convey("When I compile the filter", func() {
b, _ := bson.MarshalJSON(CompileFilter(f))

Convey("Then the bson should be correct", func() {
So(strings.Replace(string(b), "\n", "", 1), ShouldEqual, `{"$and":[{"_id":{"$eq":"not-object-id"}}]}`)
})
})
})

Convey("Given I have a single match on valid id", t, func() {

f := elemental.NewFilterComposer().
WithKey("id").Equals("5d85727b919e0c397a58e940").
Done()

Convey("When I compile the filter", func() {
b, _ := bson.MarshalJSON(CompileFilter(f))

Convey("Then the bson should be correct", func() {
So(strings.Replace(string(b), "\n", "", 1), ShouldEqual, `{"$and":[{"_id":{"$eq":{"$oid":"5d85727b919e0c397a58e940"}}}]}`)
})
})
})

Convey("Given I have a single match on valid _id", t, func() {

f := elemental.NewFilterComposer().
WithKey("_id").Equals("5d85727b919e0c397a58e940").
Done()

Convey("When I compile the filter", func() {
b, _ := bson.MarshalJSON(CompileFilter(f))

Convey("Then the bson should be correct", func() {
So(strings.Replace(string(b), "\n", "", 1), ShouldEqual, `{"$and":[{"_id":{"$eq":{"$oid":"5d85727b919e0c397a58e940"}}}]}`)
})
})
})

Convey("Given I have a In on valid ID", t, func() {

f := elemental.NewFilterComposer().
WithKey("ID").In("5d85727b919e0c397a58e940", "5d85727b919e0c397a58e941").
Done()

Convey("When I compile the filter", func() {
b, _ := bson.MarshalJSON(CompileFilter(f))

Convey("Then the bson should be correct", func() {
So(strings.Replace(string(b), "\n", "", 1), ShouldEqual, `{"$and":[{"_id":{"$in":[{"$oid":"5d85727b919e0c397a58e940"},{"$oid":"5d85727b919e0c397a58e941"}]}}]}`)
})
})
})

Convey("Given I have a In on invalid ID", t, func() {

f := elemental.NewFilterComposer().
WithKey("ID").In("not-object-id", "5d85727b919e0c397a58e941").
Done()

Convey("When I compile the filter", func() {
b, _ := bson.MarshalJSON(CompileFilter(f))

Convey("Then the bson should be correct", func() {
So(strings.Replace(string(b), "\n", "", 1), ShouldEqual, `{"$and":[{"_id":{"$in":["not-object-id",{"$oid":"5d85727b919e0c397a58e941"}]}}]}`)
})
})
})

Convey("Given I have a NotIn on valid ID", t, func() {

f := elemental.NewFilterComposer().
WithKey("ID").NotIn("5d85727b919e0c397a58e940", "5d85727b919e0c397a58e941").
Done()

Convey("When I compile the filter", func() {
b, _ := bson.MarshalJSON(CompileFilter(f))

Convey("Then the bson should be correct", func() {
So(strings.Replace(string(b), "\n", "", 1), ShouldEqual, `{"$and":[{"_id":{"$nin":[{"$oid":"5d85727b919e0c397a58e940"},{"$oid":"5d85727b919e0c397a58e941"}]}}]}`)
})
})
})

Convey("Given I have a composed filters", t, func() {

f := elemental.NewFilterComposer().
Expand Down
20 changes: 17 additions & 3 deletions manipmongo/manipulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,11 @@ func (m *mongoManipulator) Retrieve(mctx manipulate.Context, object elemental.Id
filter = compiler.CompileFilter(f)
}

filter["_id"] = object.Identifier()
if bson.IsObjectIdHex(object.Identifier()) {
filter["_id"] = bson.ObjectIdHex(object.Identifier())
} else {
filter["_id"] = object.Identifier()
}

if m.sharder != nil {
sq, err := m.sharder.FilterOne(m, mctx, object)
Expand Down Expand Up @@ -380,7 +384,12 @@ func (m *mongoManipulator) Update(mctx manipulate.Context, object elemental.Iden
sp.LogFields(log.String("object_id", object.Identifier()))
defer sp.Finish()

filter = bson.M{"_id": object.Identifier()}
if bson.IsObjectIdHex(object.Identifier()) {
filter = bson.M{"_id": bson.ObjectIdHex(object.Identifier())}
} else {
filter = bson.M{"_id": object.Identifier()}
}

if m.sharder != nil {
sq, err := m.sharder.FilterOne(m, mctx, object)
if err != nil {
Expand Down Expand Up @@ -435,7 +444,12 @@ func (m *mongoManipulator) Delete(mctx manipulate.Context, object elemental.Iden
sp.LogFields(log.String("object_id", object.Identifier()))
defer sp.Finish()

filter = bson.M{"_id": object.Identifier()}
if bson.IsObjectIdHex(object.Identifier()) {
filter = bson.M{"_id": bson.ObjectIdHex(object.Identifier())}
} else {
filter = bson.M{"_id": object.Identifier()}
}

if m.sharder != nil {
sq, err := m.sharder.FilterOne(m, mctx, object)
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions manipmongo/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ func applyOrdering(order []string, inverted bool) []string {
f = "_id"
}

if f == "-ID" || f == "-id" {
f = "-_id"
}

o = append(o, strings.ToLower(invertSortKey(f, inverted)))
}

Expand Down
Loading

0 comments on commit ac8a227

Please sign in to comment.