Skip to content
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

feat: Allow relation alias on create and update #1609

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
00c2afd
PR(MAKEFILE): Add a testing rule `make test:quick`
shahzadlone Jul 13, 2023
1a306ee
PR(UNRELATED): Test filtering on object id.
shahzadlone Jul 4, 2023
71c40ff
PR(CORE): Implement the alias logic for create
shahzadlone Jul 4, 2023
06fd941
PR(TEST): Update the collection test according to alias change.
shahzadlone Jul 4, 2023
64ef15a
PR(TEST): Add some missing tests.
shahzadlone Jul 4, 2023
f65d95c
PR(TEST): Add 1-1 tests for create with alias
shahzadlone Jul 4, 2023
5cf449d
PR(UNRELATED): Make a fixture for existing 1-to-m
shahzadlone Jul 4, 2023
72740e9
PR(TEST): Add 1-M tests for create without alias
shahzadlone Jul 13, 2023
d0d3bc0
PR(TEST): Add 1-M tests for create with alias
shahzadlone Jul 13, 2023
3c2331d
PR(FIX): Squash the dockey verification + diff bug
shahzadlone Jul 11, 2023
7e5b3c6
PR(FIX): Avoid circular dependency (import cycle).
shahzadlone Jul 11, 2023
102aa4f
PR(REFACTOR): private helper to regenerate dockey.
shahzadlone Jul 12, 2023
5913bae
PR(REFAC): Remove circular dependency issue...
shahzadlone Jul 12, 2023
cadeb99
PR(REFAC): Circular dependency fixed so refactor.
shahzadlone Jul 12, 2023
caf4319
PR(TEST): Add 1-1 tests for update without alias
shahzadlone Jul 13, 2023
4987730
PR(TEST): Add 1-M tests for update without alias
shahzadlone Jul 13, 2023
46022a6
PR(CORE): Implement the alias logic for update
shahzadlone Jul 12, 2023
1bf89be
PR(TEST): Add 1-M tests for update with alias
shahzadlone Jul 13, 2023
516bad7
PR(TEST): Add 1-1 tests for update with alias
shahzadlone Jul 13, 2023
48e3f45
PR(LINT-FIX): Make linter happy.
shahzadlone Jul 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ endif
test:
gotestsum --format pkgname -- $(DEFAULT_TEST_DIRECTORIES) $(TEST_FLAGS)

.PHONY: test\:quick
test\:quick:
gotestsum --format pkgname -- $(DEFAULT_TEST_DIRECTORIES)

# Only build the tests (don't execute them).
.PHONY: test\:build
test\:build:
Expand Down
95 changes: 73 additions & 22 deletions client/document.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@

"github.com/fxamacker/cbor/v2"
"github.com/ipfs/go-cid"
mh "github.com/multiformats/go-multihash"

"github.com/sourcenetwork/defradb/client/request"
ccid "github.com/sourcenetwork/defradb/core/cid"
)

// This is the main implementation starting point for accessing the internal Document API
Expand Down Expand Up @@ -106,26 +108,12 @@
return nil, err
}

// if no key was specified, then we assume it doesn't exist and we generate it.
// if no key was specified, then we assume it doesn't exist and we generate, and set it.
if !hasKey {
pref := cid.Prefix{
Version: 1,
Codec: cid.Raw,
MhType: mh.SHA2_256,
MhLength: -1, // default length
}

buf, err := doc.Bytes()
if err != nil {
return nil, err
}

// And then feed it some data
c, err := pref.Sum(buf)
err = doc.generateAndSetDocKey()
if err != nil {
return nil, err
}
doc.key = NewDocKeyV0(c)
}

return doc, nil
Expand Down Expand Up @@ -257,11 +245,6 @@
return nil
}

// SetAsType Sets the value of a field along with a specific type
// func (doc *Document) SetAsType(t client.CType, field string, value any) error {
// return doc.set(t, field, value)
// }

func (doc *Document) set(t CType, field string, value Value) error {
doc.mu.Lock()
defer doc.mu.Unlock()
Expand Down Expand Up @@ -479,6 +462,74 @@
return docMap, nil
}

// GenerateDocKey generates docKey/docID corresponding to the document.
func (doc *Document) GenerateDocKey() (DocKey, error) {
bytes, err := doc.Bytes()
if err != nil {
return DocKey{}, err
}

Check warning on line 470 in client/document.go

View check run for this annotation

Codecov / codecov/patch

client/document.go#L469-L470

Added lines #L469 - L470 were not covered by tests

cid, err := ccid.NewSHA256CidV1(bytes)
if err != nil {
return DocKey{}, err
}

Check warning on line 475 in client/document.go

View check run for this annotation

Codecov / codecov/patch

client/document.go#L474-L475

Added lines #L474 - L475 were not covered by tests

return NewDocKeyV0(cid), nil
}

// setDocKey sets the `doc.key` (should NOT be public).
func (doc *Document) setDocKey(docID DocKey) {
doc.mu.Lock()
defer doc.mu.Unlock()

doc.key = docID
}

// generateAndSetDocKey generates the docKey/docID and then (re)sets `doc.key`.
func (doc *Document) generateAndSetDocKey() error {
docKey, err := doc.GenerateDocKey()
if err != nil {
return err
}

Check warning on line 493 in client/document.go

View check run for this annotation

Codecov / codecov/patch

client/document.go#L492-L493

Added lines #L492 - L493 were not covered by tests

doc.setDocKey(docKey)
return nil
}

func (doc *Document) remapAliasFields(fieldDescriptions []FieldDescription) (bool, error) {
doc.mu.Lock()
defer doc.mu.Unlock()

foundAlias := false
for docField, docFieldValue := range doc.fields {
for _, fieldDescription := range fieldDescriptions {
maybeAliasField := docField + request.RelatedObjectID
if fieldDescription.Name == maybeAliasField {
foundAlias = true
doc.fields[maybeAliasField] = docFieldValue
delete(doc.fields, docField)
}
}
}

return foundAlias, nil
}

// RemapAliasFieldsAndDockey remaps the alias fields and fixes (overwrites) the dockey.
func (doc *Document) RemapAliasFieldsAndDockey(fieldDescriptions []FieldDescription) error {
foundAlias, err := doc.remapAliasFields(fieldDescriptions)
if err != nil {
return err
}

Check warning on line 523 in client/document.go

View check run for this annotation

Codecov / codecov/patch

client/document.go#L522-L523

Added lines #L522 - L523 were not covered by tests

if !foundAlias {
return nil
}

// Update the dockey so dockey isn't based on an aliased name of a field.
return doc.generateAndSetDocKey()
}

// DocumentStatus represent the state of the document in the DAG store.
// It can either be `Active“ or `Deleted`.
type DocumentStatus uint8
Expand Down
11 changes: 3 additions & 8 deletions client/document_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ package client
import (
"testing"

"github.com/ipfs/go-cid"
mh "github.com/multiformats/go-multihash"
"github.com/stretchr/testify/assert"

ccid "github.com/sourcenetwork/defradb/core/cid"
)

var (
Expand All @@ -28,12 +28,7 @@ var (
}
}`)

pref = cid.Prefix{
Version: 1,
Codec: cid.Raw,
MhType: mh.SHA2_256,
MhLength: -1, // default length
}
pref = ccid.NewDefaultSHA256PrefixV1()
)

func TestNewFromJSON(t *testing.T) {
Expand Down
46 changes: 19 additions & 27 deletions client/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,34 +17,32 @@ import (
)

const (
errFieldNotExist string = "The given field does not exist"
errSelectOfNonGroupField string = "cannot select a non-group-by field at group-level"
errUnexpectedType string = "unexpected type"
errParsingFailed string = "failed to parse argument"
errUninitializeProperty string = "invalid state, required property is uninitialized"
errMaxTxnRetries string = "reached maximum transaction reties"
errFieldNotExist string = "The given field does not exist"
errUnexpectedType string = "unexpected type"
errParsingFailed string = "failed to parse argument"
errUninitializeProperty string = "invalid state, required property is uninitialized"
errMaxTxnRetries string = "reached maximum transaction reties"
)

// Errors returnable from this package.
//
// This list is incomplete and undefined errors may also be returned.
// Errors returned from this package may be tested against these errors with errors.Is.
var (
ErrFieldNotExist = errors.New(errFieldNotExist)
ErrSelectOfNonGroupField = errors.New(errSelectOfNonGroupField)
ErrUnexpectedType = errors.New(errUnexpectedType)
ErrParsingFailed = errors.New(errParsingFailed)
ErrUninitializeProperty = errors.New(errUninitializeProperty)
ErrFieldNotObject = errors.New("trying to access field on a non object type")
ErrValueTypeMismatch = errors.New("value does not match indicated type")
ErrIndexNotFound = errors.New("no index found for given ID")
ErrDocumentNotFound = errors.New("no document for the given key exists")
ErrInvalidUpdateTarget = errors.New("the target document to update is of invalid type")
ErrInvalidUpdater = errors.New("the updater of a document is of invalid type")
ErrInvalidDeleteTarget = errors.New("the target document to delete is of invalid type")
ErrMalformedDocKey = errors.New("malformed DocKey, missing either version or cid")
ErrInvalidDocKeyVersion = errors.New("invalid DocKey version")
ErrMaxTxnRetries = errors.New(errMaxTxnRetries)
ErrFieldNotExist = errors.New(errFieldNotExist)
ErrUnexpectedType = errors.New(errUnexpectedType)
ErrParsingFailed = errors.New(errParsingFailed)
ErrUninitializeProperty = errors.New(errUninitializeProperty)
ErrFieldNotObject = errors.New("trying to access field on a non object type")
ErrValueTypeMismatch = errors.New("value does not match indicated type")
ErrIndexNotFound = errors.New("no index found for given ID")
ErrDocumentNotFound = errors.New("no document for the given key exists")
ErrInvalidUpdateTarget = errors.New("the target document to update is of invalid type")
ErrInvalidUpdater = errors.New("the updater of a document is of invalid type")
ErrInvalidDeleteTarget = errors.New("the target document to delete is of invalid type")
ErrMalformedDocKey = errors.New("malformed DocKey, missing either version or cid")
ErrInvalidDocKeyVersion = errors.New("invalid DocKey version")
ErrMaxTxnRetries = errors.New(errMaxTxnRetries)
)

// NewErrFieldNotExist returns an error indicating that the given field does not exist.
Expand All @@ -58,12 +56,6 @@ func NewErrFieldIndexNotExist(index int) error {
return errors.New(errFieldNotExist, errors.NewKV("Index", index))
}

// NewErrSelectOfNonGroupField returns an error indicating that a non-group-by field
// was selected at group-level.
func NewErrSelectOfNonGroupField(name string) error {
return errors.New(errSelectOfNonGroupField, errors.NewKV("Field", name))
}

// NewErrUnexpectedType returns an error indicating that the given value is of an unexpected type.
func NewErrUnexpectedType[TExpected any](property string, actual any) error {
var expected TExpected
Expand Down
33 changes: 33 additions & 0 deletions client/request/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2023 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 request

import (
"github.com/sourcenetwork/defradb/errors"
)

const (
errSelectOfNonGroupField string = "cannot select a non-group-by field at group-level"
)

// Errors returnable from this package.
//
// This list is incomplete and undefined errors may also be returned.
// Errors returned from this package may be tested against these errors with errors.Is.
var (
ErrSelectOfNonGroupField = errors.New(errSelectOfNonGroupField)
)

// NewErrSelectOfNonGroupField returns an error indicating that a non-group-by field
// was selected at group-level.
func NewErrSelectOfNonGroupField(name string) error {
return errors.New(errSelectOfNonGroupField, errors.NewKV("Field", name))
}
4 changes: 1 addition & 3 deletions client/request/select.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ package request

import (
"github.com/sourcenetwork/immutable"

"github.com/sourcenetwork/defradb/client"
)

// SelectionType is the type of selection.
Expand Down Expand Up @@ -100,7 +98,7 @@ func (s *Select) validateGroupBy() []error {
}
}
if !fieldExistsInGroupBy && !isAliasFieldInGroupBy {
result = append(result, client.NewErrSelectOfNonGroupField(typedChildSelection.Name))
result = append(result, NewErrSelectOfNonGroupField(typedChildSelection.Name))
}
default:
// Do nothing
Expand Down
12 changes: 7 additions & 5 deletions core/cid.go → core/cid/cid.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,24 @@
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

package core
package cid

import (
"github.com/ipfs/go-cid"
mh "github.com/multiformats/go-multihash"
)

// NewSHA256CidV1 returns a new CIDv1 with the SHA256 multihash.
func NewSHA256CidV1(data []byte) (cid.Cid, error) {
pref := cid.Prefix{
func NewDefaultSHA256PrefixV1() cid.Prefix {
return cid.Prefix{
Version: 1,
Codec: cid.Raw,
MhType: mh.SHA2_256,
MhLength: -1, // default length
}
}

// NewSHA256CidV1 returns a new CIDv1 with the SHA256 multihash.
func NewSHA256CidV1(data []byte) (cid.Cid, error) {
// And then feed it some data
return pref.Sum(data)
return NewDefaultSHA256PrefixV1().Sum(data)
}
Loading