Skip to content

Commit

Permalink
Error if creating 1-1 from wrong side
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewSisley committed Sep 16, 2022
1 parent d80e00b commit 2c0991b
Show file tree
Hide file tree
Showing 10 changed files with 464 additions and 0 deletions.
5 changes: 5 additions & 0 deletions client/descriptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ func (f FieldDescription) IsObjectArray() bool {
return (f.Kind == FieldKind_FOREIGN_OBJECT_ARRAY)
}

// IsPrimaryRelation returns true if this field is a relation, and is the primary side.
func (f FieldDescription) IsPrimaryRelation() bool {
return f.RelationType > 0 && f.RelationType&Relation_Type_Primary == 0
}

func (m RelationType) IsSet(target RelationType) bool {
return m&target > 0
}
30 changes: 30 additions & 0 deletions db/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"encoding/json"
"fmt"
"strconv"
"strings"

"github.com/fxamacker/cbor/v2"
"github.com/sourcenetwork/defradb/client"
Expand Down Expand Up @@ -602,6 +603,10 @@ func (c *collection) save(
return cid.Undef, client.ErrFieldNotExist(k)
}

if c.isFieldRelationId(k) {
return cid.Undef, client.ErrFieldNotExist(k)
}

c, err := c.saveDocValue(ctx, txn, fieldKey, val)
if err != nil {
return cid.Undef, err
Expand Down Expand Up @@ -939,6 +944,31 @@ func (c *collection) tryGetSchemaFieldID(fieldName string) (uint32, bool) {
return uint32(0), false
}

// isFieldRelationId returns true if the given field is the id field backing a relationship.
// Will return false if the field is not found at all.
func (c *collection) isFieldRelationId(fieldName string) bool {
fieldDescription, valid := c.desc.GetField(fieldName)
if !valid {
return false
}

return c.isFieldRelationIdD(&fieldDescription)
}

// isFieldRelationIdD returns true if the given field is the id field backing a relationship.
func (c *collection) isFieldRelationIdD(fieldDescription *client.FieldDescription) bool {
if fieldDescription.RelationType == client.Relation_Type_INTERNAL_ID {
relationDescription, valid := c.desc.GetField(strings.TrimSuffix(fieldDescription.Name, "_id"))
if !valid {
return false
}
if relationDescription.IsPrimaryRelation() {
return true
}
}
return false
}

// makeCollectionKey returns a formatted collection key for the system data store.
// it assumes the name of the collection is non-empty.
// func makeCollectionDataKey(collectionID uint32) core.Key {
Expand Down
4 changes: 4 additions & 0 deletions db/collection_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,10 @@ func (c *collection) applyMerge(
return errors.New("invalid field in Patch")
}

if c.isFieldRelationIdD(&fd) {
return client.ErrFieldNotExist(mfield)
}

cborVal, err := validateFieldSchema(mval, fd)
if err != nil {
return err
Expand Down
90 changes: 90 additions & 0 deletions tests/integration/collection/create/one_to_one/save_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright 2022 Democratized Data Foundation
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

package create

import (
"context"
"testing"

"github.com/sourcenetwork/defradb/client"
testUtils "github.com/sourcenetwork/defradb/tests/integration/collection"
"github.com/stretchr/testify/assert"
)

func TestCollectionCreateSaveErrorsGivenRelationSetOnWrongSide(t *testing.T) {
doc, err := client.NewDocFromJSON(
[]byte(
`{
"name": "Painted House",
"author_id": "bae-fd541c25-229e-5280-b44b-e5c2af3e374d"
}`,
),
)
if err != nil {
assert.Fail(t, err.Error())
}

test := testUtils.TestCase{
CollectionCalls: map[string][]func(client.Collection) error{
"book": []func(c client.Collection) error{
func(c client.Collection) error {
return c.Save(context.Background(), doc)
},
},
},
ExpectedError: "The given field does not exist",
}

executeTestCase(t, test)
}

func TestCollectionCreateSaveCreatesDoc(t *testing.T) {
doc, err := client.NewDocFromJSON(
[]byte(
`{
"name": "John",
"published_id": "bae-fd541c25-229e-5280-b44b-e5c2af3e374d"
}`,
),
)
if err != nil {
assert.Fail(t, err.Error())
}

test := testUtils.TestCase{
CollectionCalls: map[string][]func(client.Collection) error{
"author": []func(c client.Collection) error{
func(c client.Collection) error {
err := c.Save(context.Background(), doc)
if err != nil {
return err
}

d, err := c.Get(context.Background(), doc.Key())
if err != nil {
return err
}

name, err := d.Get("name")
if err != nil {
return err
}

assert.Equal(t, "John", name)

return nil
},
},
},
}

executeTestCase(t, test)
}
36 changes: 36 additions & 0 deletions tests/integration/collection/create/one_to_one/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2022 Democratized Data Foundation
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

package create

import (
"testing"

testUtils "github.com/sourcenetwork/defradb/tests/integration/collection"
)

var schema = `
type book {
name: String
rating: Float
author: author
}
type author {
name: String
age: Int
verified: Boolean
published: book @primary
}
`

func executeTestCase(t *testing.T, test testUtils.TestCase) {
testUtils.ExecuteQueryTestCase(t, schema, test)
}
109 changes: 109 additions & 0 deletions tests/integration/collection/update/one_to_one/save_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright 2022 Democratized Data Foundation
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

package update

import (
"context"
"testing"

"github.com/sourcenetwork/defradb/client"
testUtils "github.com/sourcenetwork/defradb/tests/integration/collection"
"github.com/stretchr/testify/assert"
)

func TestUpdateOneToOneSaveErrorsGivenWrongSideOfRelation(t *testing.T) {
doc, err := client.NewDocFromJSON(
[]byte(
`{
"name": "Painted House"
}`,
),
)
if err != nil {
assert.Fail(t, err.Error())
}

err = doc.SetWithJSON(
[]byte(
`{
"author_id": "ValueDoesntMatter"
}`,
),
)
if err != nil {
assert.Fail(t, err.Error())
}

test := testUtils.TestCase{
Docs: map[string][]string{
"book": {
`{
"name": "Painted House"
}`,
},
},
CollectionCalls: map[string][]func(client.Collection) error{
"book": []func(c client.Collection) error{
func(c client.Collection) error {
return c.Save(context.Background(), doc)
},
},
},
ExpectedError: "The given field does not exist",
}

executeTestCase(t, test)
}

// Note: This test should probably not pass, as it contains a
// reference to a document that doesnt exist.
func TestUpdateOneToOneSavesGivenNewRelationValue(t *testing.T) {
doc, err := client.NewDocFromJSON(
[]byte(
`{
"name": "John Grisham"
}`,
),
)
if err != nil {
assert.Fail(t, err.Error())
}

err = doc.SetWithJSON(
[]byte(
`{
"published_id": "bae-fd541c25-229e-5280-b44b-e5c2af3e374d"
}`,
),
)
if err != nil {
assert.Fail(t, err.Error())
}

test := testUtils.TestCase{
Docs: map[string][]string{
"author": {
`{
"name": "John Grisham"
}`,
},
},
CollectionCalls: map[string][]func(client.Collection) error{
"author": []func(c client.Collection) error{
func(c client.Collection) error {
return c.Save(context.Background(), doc)
},
},
},
}

executeTestCase(t, test)
}
36 changes: 36 additions & 0 deletions tests/integration/collection/update/one_to_one/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2022 Democratized Data Foundation
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

package update

import (
"testing"

testUtils "github.com/sourcenetwork/defradb/tests/integration/collection"
)

var schema = `
type book {
name: String
rating: Float
author: author
}
type author {
name: String
age: Int
verified: Boolean
published: book @primary
}
`

func executeTestCase(t *testing.T, test testUtils.TestCase) {
testUtils.ExecuteQueryTestCase(t, schema, test)
}
Loading

0 comments on commit 2c0991b

Please sign in to comment.