diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 111586ca38..2636153a30 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -50,7 +50,7 @@ jobs: # Required: the version of golangci-lint is required. # Note: The version should not pick the patch version as the latest patch # version is what will always be used. - version: v1.61 + version: v1.64 # Optional: working directory, useful for monorepos or if we wanted to run this # on a non-root directory. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f288a2e3df..fae4067c70 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -67,7 +67,7 @@ jobs: if: matrix.os == 'ubuntu-latest' uses: actions/cache/save@v4 with: - path: dist/linux_amd64_v1 + path: dist/linux_amd64 key: linux-${{ env.sha_short }} - name: Save cache on MacOS @@ -81,7 +81,7 @@ jobs: if: matrix.os == 'windows-latest' uses: actions/cache/save@v4 with: - path: dist/windows_amd64_v1 + path: dist/windows_amd64 key: windows-${{ env.sha_short }} enableCrossOsArchive: true @@ -113,7 +113,7 @@ jobs: id: restore-linux uses: actions/cache/restore@v4 with: - path: dist/linux_amd64_v1 + path: dist/linux_amd64 key: linux-${{ env.sha_short }} fail-on-cache-miss: true @@ -129,7 +129,7 @@ jobs: id: restore-windows uses: actions/cache/restore@v4 with: - path: dist/windows_amd64_v1 + path: dist/windows_amd64 key: windows-${{ env.sha_short }} fail-on-cache-miss: true enableCrossOsArchive: true diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 07e9ae1ef8..888977f377 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -6,32 +6,9 @@ before: - make deps:playground builds: - - id: "defradb" - main: ./cmd/defradb - goos: - - linux - - windows - - darwin - goarch: - - amd64 - - arm64 - # A build with the playground included. - - id: "defradb_playground" - main: ./cmd/defradb - flags: - - -tags=playground - goos: - - linux - - windows - - darwin - goarch: - - amd64 - - arm64 - # A build with telemetry included. - - id: "defradb_telemetry" + # Minimal build with no extras + - id: "defradb_lite" main: ./cmd/defradb - flags: - - -tags=telemetry goos: - linux - windows @@ -39,8 +16,8 @@ builds: goarch: - amd64 - arm64 - # A build with the playground and telemetry included. - - id: "defradb_playground_telemetry" + # Default build with playground and telemetry + - id: "defradb" main: ./cmd/defradb flags: - -tags=playground,telemetry @@ -56,13 +33,13 @@ partial: by: target archives: - - id: defradb_playground + - id: defradb_lite builds: - - defradb_playground + - defradb_lite formats: [binary] # this name template makes the OS and Arch compatible with the results of `uname`. name_template: >- - {{ .Binary }}_playground_{{ .Version }}_{{ .Os }}_ + {{ .Binary }}_lite_{{ .Version }}_{{ .Os }}_ {{- if eq .Arch "amd64" }}x86_64 {{- else }}{{ .Arch }}{{ end }} {{- if .Arm }}v{{ .Arm }}{{ end }} @@ -76,26 +53,6 @@ archives: {{- if eq .Arch "amd64" }}x86_64 {{- else }}{{ .Arch }}{{ end }} {{- if .Arm }}v{{ .Arm }}{{ end }} - - id: defradb_telemetry - builds: - - defradb_telemetry - formats: [binary] - # this name template makes the OS and Arch compatible with the results of `uname`. - name_template: >- - {{ .Binary }}_telemetry_{{ .Version }}_{{ .Os }}_ - {{- if eq .Arch "amd64" }}x86_64 - {{- else }}{{ .Arch }}{{ end }} - {{- if .Arm }}v{{ .Arm }}{{ end }} - - id: defradb_playground_telemetry - builds: - - defradb_playground_telemetry - formats: [binary] - # this name template makes the OS and Arch compatible with the results of `uname`. - name_template: >- - {{ .Binary }}_playground_telemetry_{{ .Version }}_{{ .Os }}_ - {{- if eq .Arch "amd64" }}x86_64 - {{- else }}{{ .Arch }}{{ end }} - {{- if .Arm }}v{{ .Arm }}{{ end }} release: target_commitish: '{{ .Commit }}' diff --git a/Makefile b/Makefile index 939f354170..8df1258b15 100644 --- a/Makefile +++ b/Makefile @@ -124,7 +124,7 @@ client\:add-schema: .PHONY: deps\:lint-go deps\:lint-go: - go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.61 + go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64 .PHONY: deps\:lint-yaml deps\:lint-yaml: diff --git a/cli/collection_update.go b/cli/collection_update.go index 228bff0053..9b9f9b686c 100644 --- a/cli/collection_update.go +++ b/cli/collection_update.go @@ -23,16 +23,13 @@ func MakeCollectionUpdateCommand() *cobra.Command { var filter string var updater string var cmd = &cobra.Command{ - Use: "update [-i --identity] [--filter --docID --updater ] ", + Use: "update [-i --identity] [--filter --docID ] --updater ", Short: "Update documents by docID or filter.", Long: `Update documents by docID or filter. - -Example: update from string: - defradb client collection update --name User --docID bae-123 '{ "name": "Bob" }' Example: update by filter: defradb client collection update --name User \ - --filter '{ "_gte": { "points": 100 } }' --updater '{ "verified": true }' + --filter '{ "points": { "_gte": 100 } }' --updater '{ "verified": true }' Example: update by docID: defradb client collection update --name User \ @@ -42,15 +39,18 @@ Example: update private docID, with identity: defradb client collection update -i 028d53f37a19afb9a0dbc5b4be30c65731479ee8cfa0c9bc8f8bf198cc3c075f --name User \ --docID bae-123 --updater '{ "verified": true }' `, - Args: cobra.RangeArgs(0, 1), RunE: func(cmd *cobra.Command, args []string) error { col, ok := tryGetContextCollection(cmd) if !ok { return cmd.Usage() } + if updater == "" { + return NewErrMissingRequiredFlag("updater") + } + switch { - case filter != "" || updater != "": + case filter != "": var filterValue any if err := json.Unmarshal([]byte(filter), &filterValue); err != nil { return err @@ -60,7 +60,7 @@ Example: update private docID, with identity: return err } return writeJSON(cmd, res) - case argDocID != "" && len(args) == 1: + case argDocID != "": docID, err := client.NewDocIDFromString(argDocID) if err != nil { return err @@ -69,7 +69,7 @@ Example: update private docID, with identity: if err != nil { return err } - if err := doc.SetWithJSON([]byte(args[0])); err != nil { + if err := doc.SetWithJSON([]byte(updater)); err != nil { return err } return col.Update(cmd.Context(), doc) diff --git a/cli/errors.go b/cli/errors.go index 439b89b20d..5c25533bda 100644 --- a/cli/errors.go +++ b/cli/errors.go @@ -23,6 +23,7 @@ const ( errInvalidAscensionOrder string = "invalid order: expected ASC or DESC" errInvalidInxedFieldDescription string = "invalid or malformed field description" errEmptySchemaString string = "schema cannot be empty" + errMissingRequiredFlag string = "missing required flag" ) var ( @@ -79,3 +80,7 @@ func NewErrFailedToReadSchemaFromStdin(inner error) error { func NewErrFailedToAddSchema(inner error) error { return errors.Wrap("failed to add schema", inner) } + +func NewErrMissingRequiredFlag(flag string) error { + return errors.New(errMissingRequiredFlag, errors.NewKV("Flag", flag)) +} diff --git a/docs/data_format_changes/i3477-version-query-consistency.md b/docs/data_format_changes/i3477-version-query-consistency.md new file mode 100644 index 0000000000..df13ae7023 --- /dev/null +++ b/docs/data_format_changes/i3477-version-query-consistency.md @@ -0,0 +1,2 @@ +# Consistent version query semantics +Updated semantics for commit/version/time-travel queries when using both DocID and CID. \ No newline at end of file diff --git a/docs/website/references/cli/defradb_client_collection_update.md b/docs/website/references/cli/defradb_client_collection_update.md index f21bba3437..87e2c8e38d 100644 --- a/docs/website/references/cli/defradb_client_collection_update.md +++ b/docs/website/references/cli/defradb_client_collection_update.md @@ -5,13 +5,10 @@ Update documents by docID or filter. ### Synopsis Update documents by docID or filter. - -Example: update from string: - defradb client collection update --name User --docID bae-123 '{ "name": "Bob" }' Example: update by filter: defradb client collection update --name User \ - --filter '{ "_gte": { "points": 100 } }' --updater '{ "verified": true }' + --filter '{ "points": { "_gte": 100 } }' --updater '{ "verified": true }' Example: update by docID: defradb client collection update --name User \ @@ -23,7 +20,7 @@ Example: update private docID, with identity: ``` -defradb client collection update [-i --identity] [--filter --docID --updater ] [flags] +defradb client collection update [-i --identity] [--filter --docID ] --updater [flags] ``` ### Options diff --git a/internal/db/collection_update.go b/internal/db/collection_update.go index 0918a4d045..8592531d29 100644 --- a/internal/db/collection_update.go +++ b/internal/db/collection_update.go @@ -157,6 +157,8 @@ func (c *collection) makeSelectionPlan( } case immutable.Option[request.Filter]: f = fval + case map[string]any: + f = immutable.Some(request.Filter{Conditions: fval}) default: return nil, ErrInvalidFilter } diff --git a/internal/planner/commit.go b/internal/planner/commit.go index 348a4b4e6a..767a8ff3f1 100644 --- a/internal/planner/commit.go +++ b/internal/planner/commit.go @@ -17,6 +17,7 @@ import ( "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/client/request" + "github.com/sourcenetwork/defradb/errors" "github.com/sourcenetwork/defradb/internal/core" coreblock "github.com/sourcenetwork/defradb/internal/core/block" "github.com/sourcenetwork/defradb/internal/db/fetcher" @@ -84,7 +85,11 @@ func (n *dagScanNode) Init() error { } } - return n.fetcher.Start(n.planner.ctx, n.planner.txn, n.prefix, n.commitSelect.FieldID) + // only need the head fetcher for non cid specific queries + if !n.commitSelect.Cid.HasValue() { + return n.fetcher.Start(n.planner.ctx, n.planner.txn, n.prefix, n.commitSelect.FieldID) + } + return nil } func (n *dagScanNode) Start() error { @@ -123,7 +128,10 @@ func (n *dagScanNode) Prefixes(prefixes []keys.Walkable) { } func (n *dagScanNode) Close() error { - return n.fetcher.Close() + if !n.commitSelect.Cid.HasValue() { + return n.fetcher.Close() + } + return nil } func (n *dagScanNode) Source() planNode { return nil } @@ -183,7 +191,14 @@ func (n *dagScanNode) Next() (bool, error) { if len(n.queuedCids) > 0 { currentCid = n.queuedCids[0] n.queuedCids = n.queuedCids[1:(len(n.queuedCids))] - } else { + } else if n.commitSelect.Cid.HasValue() && len(n.visitedNodes) == 0 { + cid, err := cid.Parse(n.commitSelect.Cid.Value()) + if err != nil { + return false, err + } + + currentCid = &cid + } else if !n.commitSelect.Cid.HasValue() { cid, err := n.fetcher.FetchNext() if err != nil || cid == nil { return false, err @@ -192,6 +207,8 @@ func (n *dagScanNode) Next() (bool, error) { currentCid = cid // Reset the depthVisited for each head yielded by headset n.depthVisited = 0 + } else { + return false, nil } // skip already visited CIDs @@ -207,7 +224,7 @@ func (n *dagScanNode) Next() (bool, error) { // clear the cid after block, err := store.Get(n.planner.ctx, *currentCid) if err != nil { - return false, err + return false, errors.Join(ErrMissingCID, err) } dagBlock, err := coreblock.GetFromBytes(block.RawData()) @@ -220,6 +237,18 @@ func (n *dagScanNode) Next() (bool, error) { return false, err } + // if this is a time travel query or a latestCommits + // (cid + undefined depth + docId) then we need to make sure the + // target block actually belongs to the doc, since we are + // bypassing the HeadFetcher for the first cid + currentDocID := n.commitSelect.DocumentMapping.FirstOfName(currentValue, request.DocIDArgName) + if n.commitSelect.Cid.HasValue() && + len(n.visitedNodes) == 0 && + n.commitSelect.DocID.HasValue() && + currentDocID != n.commitSelect.DocID.Value() { + return false, ErrIncorrectCIDForDocId + } + // the dagscan node can traverse into the merkle dag // based on the specified depth limit. // The default query operation 'latestCommit' only cares about @@ -230,7 +259,13 @@ func (n *dagScanNode) Next() (bool, error) { // HEAD paths. n.depthVisited++ n.visitedNodes[currentCid.String()] = true // mark the current node as "visited" - if !n.commitSelect.Depth.HasValue() || n.depthVisited < n.commitSelect.Depth.Value() { + + // the default behavior for depth is: + // doc ID, max depth + // just doc ID + CID, 0 depth + // doc ID + CID + depth, use depth + if (!n.commitSelect.Depth.HasValue() && !n.commitSelect.Cid.HasValue()) || + (n.commitSelect.Depth.HasValue() && n.depthVisited < n.commitSelect.Depth.Value()) { // Insert the newly fetched cids into the slice of queued items, in reverse order // so that the last new cid will be at the front of the slice n.queuedCids = append(make([]*cid.Cid, len(dagBlock.Heads)), n.queuedCids...) @@ -240,12 +275,6 @@ func (n *dagScanNode) Next() (bool, error) { } } - if n.commitSelect.Cid.HasValue() && currentCid.String() != n.commitSelect.Cid.Value() { - // If a specific cid has been requested, and the current item does not - // match, keep searching. - return n.Next() - } - n.currentValue = currentValue return true, nil } diff --git a/internal/planner/errors.go b/internal/planner/errors.go index 671e1db524..4b5913d1dc 100644 --- a/internal/planner/errors.go +++ b/internal/planner/errors.go @@ -36,6 +36,8 @@ var ( ErrUnknownExplainRequestType = errors.New("can not explain request of unknown type") ErrUpsertMultipleDocuments = errors.New("cannot upsert multiple matching documents") ErrMismatchLengthOnSimilarity = errors.New("source and vector must be of the same length") + ErrIncorrectCIDForDocId = errors.New("cid does not belong to document") + ErrMissingCID = errors.New("missing cid") ) func NewErrUnknownDependency(name string) error { diff --git a/internal/planner/explain.go b/internal/planner/explain.go index 3a6be07f94..749676e8b4 100644 --- a/internal/planner/explain.go +++ b/internal/planner/explain.go @@ -50,6 +50,7 @@ var ( _ explainablePlanNode = (*typeIndexJoin)(nil) _ explainablePlanNode = (*updateNode)(nil) _ explainablePlanNode = (*upsertNode)(nil) + _ explainablePlanNode = (*similarityNode)(nil) ) const ( diff --git a/internal/planner/select.go b/internal/planner/select.go index c2419c8d7e..845e4a0ae3 100644 --- a/internal/planner/select.go +++ b/internal/planner/select.go @@ -11,6 +11,7 @@ package planner import ( + "math" "slices" "strings" @@ -405,9 +406,11 @@ func (n *selectNode) initFields(selectReq *mapper.Select) ([]aggregateNode, []*s // commit. Instead, _version references the CID // of that Target version we are querying. // So instead of a LatestCommit subquery, we need - // a OneCommit subquery, with the supplied parameters. + // a commits query with max depth starting from the + // target CID version commitSlct.DocID = immutable.Some(selectReq.DocIDs.Value()[0]) // @todo check length commitSlct.Cid = selectReq.Cid + commitSlct.Depth = immutable.Some(uint64(math.MaxUint64)) } commitPlan := n.planner.DAGScan(commitSlct) diff --git a/tests/clients/cli/wrapper_collection.go b/tests/clients/cli/wrapper_collection.go index 6fbd739879..14255cb72b 100644 --- a/tests/clients/cli/wrapper_collection.go +++ b/tests/clients/cli/wrapper_collection.go @@ -136,15 +136,15 @@ func (c *Collection) Update( return client.ErrOperationNotPermittedOnNamelessCols } - args := []string{"client", "collection", "update"} - args = append(args, "--name", c.Description().Name.Value()) - args = append(args, "--docID", doc.ID().String()) - document, err := doc.ToJSONPatch() if err != nil { return err } - args = append(args, string(document)) + + args := []string{"client", "collection", "update"} + args = append(args, "--name", c.Description().Name.Value()) + args = append(args, "--docID", doc.ID().String()) + args = append(args, "--updater", string(document)) _, err = c.cmd.execute(ctx, args) if err != nil { diff --git a/tests/integration/collection/update/simple/utils.go b/tests/integration/collection/update/simple/utils.go index 224c3b9ce5..9b0d3ada52 100644 --- a/tests/integration/collection/update/simple/utils.go +++ b/tests/integration/collection/update/simple/utils.go @@ -26,18 +26,12 @@ var schema = ` ` func executeTestCase(t *testing.T, test testUtils.TestCase) { - testUtils.ExecuteTestCase( - t, - testUtils.TestCase{ - Description: test.Description, - Actions: append( - []any{ - testUtils.SchemaUpdate{ - Schema: schema, - }, - }, - test.Actions..., - ), + test.Actions = append( + []any{ + testUtils.SchemaUpdate{ + Schema: schema, + }, }, - ) + test.Actions...) + testUtils.ExecuteTestCase(t, test) } diff --git a/tests/integration/collection/update/simple/with_filter_test.go b/tests/integration/collection/update/simple/with_filter_test.go index 9be676476f..e4a76fcf50 100644 --- a/tests/integration/collection/update/simple/with_filter_test.go +++ b/tests/integration/collection/update/simple/with_filter_test.go @@ -13,16 +13,41 @@ package update import ( "testing" + "github.com/sourcenetwork/immutable" + testUtils "github.com/sourcenetwork/defradb/tests/integration" ) func TestUpdateWithInvalidFilterType_ReturnsError(t *testing.T) { + type invalidFilterType struct{ Number int } test := testUtils.TestCase{ Description: "Test update users with invalid filter type", + // http and cli clients will pass the serialize filter into json which will result in + // the payload deserialized into map[string]any. With Go client the filter is passed as is. + SupportedClientTypes: immutable.Some( + []testUtils.ClientType{testUtils.HTTPClientType, testUtils.CLIClientType}), + Actions: []any{ + testUtils.UpdateWithFilter{ + CollectionID: 0, + Filter: invalidFilterType{Number: 1}, + Updater: `{"name": "Eric"}`, + ExpectedError: "type not found", + }, + }, + } + + executeTestCase(t, test) +} + +func TestUpdateWithInvalidFilterType_WithGoClient_ReturnsError(t *testing.T) { + type invalidFilterType struct{ Number int } + test := testUtils.TestCase{ + Description: "Test update users with invalid filter type (go client)", + SupportedClientTypes: immutable.Some([]testUtils.ClientType{testUtils.GoClientType}), Actions: []any{ testUtils.UpdateWithFilter{ CollectionID: 0, - Filter: t, + Filter: invalidFilterType{Number: 1}, Updater: `{"name": "Eric"}`, ExpectedError: "invalid filter", }, diff --git a/tests/integration/explain.go b/tests/integration/explain.go index c7090a7b50..f399665c30 100644 --- a/tests/integration/explain.go +++ b/tests/integration/explain.go @@ -34,33 +34,34 @@ var ( "subType": {}, // These are all valid nodes. - "averageNode": {}, - "countNode": {}, - "createNode": {}, - "dagScanNode": {}, - "deleteNode": {}, - "groupNode": {}, - "limitNode": {}, - "maxNode": {}, - "minNode": {}, - "multiScanNode": {}, - "orderNode": {}, - "parallelNode": {}, - "pipeNode": {}, - "scanNode": {}, - "selectNode": {}, - "selectTopNode": {}, - "sumNode": {}, - "topLevelNode": {}, - "typeIndexJoin": {}, - "typeJoinMany": {}, - "typeJoinOne": {}, - "updateNode": {}, - "upsertNode": {}, - "valuesNode": {}, - "viewNode": {}, - "lensNode": {}, - "operationNode": {}, + "averageNode": {}, + "countNode": {}, + "createNode": {}, + "dagScanNode": {}, + "deleteNode": {}, + "groupNode": {}, + "limitNode": {}, + "maxNode": {}, + "minNode": {}, + "multiScanNode": {}, + "orderNode": {}, + "parallelNode": {}, + "pipeNode": {}, + "scanNode": {}, + "selectNode": {}, + "selectTopNode": {}, + "sumNode": {}, + "topLevelNode": {}, + "typeIndexJoin": {}, + "typeJoinMany": {}, + "typeJoinOne": {}, + "updateNode": {}, + "upsertNode": {}, + "valuesNode": {}, + "viewNode": {}, + "lensNode": {}, + "operationNode": {}, + "similarityNode": {}, } ) diff --git a/tests/integration/explain/debug/with_similarity_test.go b/tests/integration/explain/debug/with_similarity_test.go new file mode 100644 index 0000000000..cb61a15f3c --- /dev/null +++ b/tests/integration/explain/debug/with_similarity_test.go @@ -0,0 +1,70 @@ +// Copyright 2025 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 test_explain_debug + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" + explainUtils "github.com/sourcenetwork/defradb/tests/integration/explain" +) + +var similarityPattern = dataMap{ + "explain": dataMap{ + "operationNode": []dataMap{ + { + "selectTopNode": dataMap{ + "similarityNode": dataMap{ + "selectNode": dataMap{ + "scanNode": dataMap{}, + }, + }, + }, + }, + }, + }, +} + +func TestDebugExplainRequestWith_WithSimilarity(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Explain (debug) request with similarity.", + + Actions: []any{ + testUtils.SchemaUpdate{ + Schema: `type User { + name: String + pointsList: [Float64!] + }`, + }, + testUtils.CreateDoc{ + DocMap: map[string]any{ + "name": "John", + "pointsList": []float64{2, 4, 1}, + }, + }, + + testUtils.ExplainRequest{ + + Request: `query @explain(type: debug) { + User { + name + _similarity(pointsList: {vector: [1, 2, 0]}) + } + }`, + + ExpectedFullGraph: similarityPattern, + }, + }, + } + + explainUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/query/commits/branchables/cid_doc_id_test.go b/tests/integration/query/commits/branchables/cid_doc_id_test.go index e0f8722753..b4727f5c59 100644 --- a/tests/integration/query/commits/branchables/cid_doc_id_test.go +++ b/tests/integration/query/commits/branchables/cid_doc_id_test.go @@ -48,6 +48,7 @@ func TestQueryCommitsBranchables_WithCidAndDocIDParam(t *testing.T) { Results: map[string]any{ "commits": []map[string]any{}, }, + ExpectedError: "cid does not belong to document", }, }, } diff --git a/tests/integration/query/commits/with_cid_test.go b/tests/integration/query/commits/with_cid_test.go index ade22beb46..9dcf04b1b1 100644 --- a/tests/integration/query/commits/with_cid_test.go +++ b/tests/integration/query/commits/with_cid_test.go @@ -70,10 +70,15 @@ func TestQueryCommitsWithCidForFieldCommit(t *testing.T) { "age": 21 }`, }, + testUtils.UpdateDoc{ + Doc: `{ + "name": "Johnn" + }`, + }, testUtils.Request{ Request: `query { commits( - cid: "bafyreia2vlbfkcbyogdjzmbqcjneabwwwtw7ti2xbd7yor5mbu2sk4pcoy" + cid: "bafyreiexx65zeu6rln4yiw7lav4up5bnfnbkti4kguw3vdencwddqhv45e" ) { cid } @@ -81,7 +86,7 @@ func TestQueryCommitsWithCidForFieldCommit(t *testing.T) { Results: map[string]any{ "commits": []map[string]any{ { - "cid": "bafyreia2vlbfkcbyogdjzmbqcjneabwwwtw7ti2xbd7yor5mbu2sk4pcoy", + "cid": "bafyreiexx65zeu6rln4yiw7lav4up5bnfnbkti4kguw3vdencwddqhv45e", }, }, }, @@ -115,6 +120,7 @@ func TestQueryCommitsWithInvalidCid(t *testing.T) { Results: map[string]any{ "commits": []map[string]any{}, }, + ExpectedError: "invalid cid", }, }, } @@ -145,6 +151,7 @@ func TestQueryCommitsWithInvalidShortCid(t *testing.T) { Results: map[string]any{ "commits": []map[string]any{}, }, + ExpectedError: "invalid cid", }, }, } @@ -175,6 +182,7 @@ func TestQueryCommitsWithUnknownCid(t *testing.T) { Results: map[string]any{ "commits": []map[string]any{}, }, + ExpectedError: "missing cid", }, }, } diff --git a/tests/integration/query/commits/with_doc_id_cid_test.go b/tests/integration/query/commits/with_doc_id_cid_test.go index 7be8265cb9..9489228d64 100644 --- a/tests/integration/query/commits/with_doc_id_cid_test.go +++ b/tests/integration/query/commits/with_doc_id_cid_test.go @@ -40,6 +40,7 @@ func TestQueryCommitsWithDocIDAndCidForDifferentDoc(t *testing.T) { Results: map[string]any{ "commits": []map[string]any{}, }, + ExpectedError: "missing cid", }, }, } @@ -70,7 +71,7 @@ func TestQueryCommitsWithDocIDAndCidForDifferentDocWithUpdate(t *testing.T) { Request: ` { commits( docID: "bae-not-this-doc", - cid: "bafybeica4js2abwqjjrz7dcialbortbz32uxp7ufxu7yljbwvmhjqqxzny" + cid: "bafyreiale6qsjc7qewod3c6h2odwamfwcf7vt4zlqtw7ldcm57xdkgxja4" ) { cid } @@ -78,6 +79,7 @@ func TestQueryCommitsWithDocIDAndCidForDifferentDocWithUpdate(t *testing.T) { Results: map[string]any{ "commits": []map[string]any{}, }, + ExpectedError: "cid does not belong to document", }, }, } @@ -126,3 +128,51 @@ func TestQueryCommitsWithDocIDAndCidWithUpdate(t *testing.T) { testUtils.ExecuteTestCase(t, test) } + +func TestQueryCommitsWithDocIDAndCidWithUpdateAndDepth(t *testing.T) { + test := testUtils.TestCase{ + Description: "Simple all commits query with docID and cid, with update", + Actions: []any{ + updateUserCollectionSchema(), + testUtils.CreateDoc{ + CollectionID: 0, + Doc: `{ + "name": "John", + "age": 21 + }`, + }, + testUtils.UpdateDoc{ + CollectionID: 0, + DocID: 0, + Doc: `{ + "age": 22 + }`, + }, + // depth is pretty arbitrary here, as long as its big enough to cover the updates + // from the target cid (ie >=2) + testUtils.Request{ + Request: ` { + commits( + docID: "bae-c9fb0fa4-1195-589c-aa54-e68333fb90b3", + cid: "bafyreiale6qsjc7qewod3c6h2odwamfwcf7vt4zlqtw7ldcm57xdkgxja4", + depth: 5 + ) { + cid + } + }`, + Results: map[string]any{ + "commits": []map[string]any{ + { + "cid": "bafyreiale6qsjc7qewod3c6h2odwamfwcf7vt4zlqtw7ldcm57xdkgxja4", + }, + { + "cid": "bafyreia2vlbfkcbyogdjzmbqcjneabwwwtw7ti2xbd7yor5mbu2sk4pcoy", + }, + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/query/simple/with_cid_doc_id_test.go b/tests/integration/query/simple/with_cid_doc_id_test.go index 29a630ac19..64f56ffdca 100644 --- a/tests/integration/query/simple/with_cid_doc_id_test.go +++ b/tests/integration/query/simple/with_cid_doc_id_test.go @@ -234,12 +234,23 @@ func TestQuerySimpleWithUpdateAndMiddleCidAndDocID(t *testing.T) { docID: "bae-6845cfdf-cb0f-56a3-be3a-b5a67be5fbdc" ) { name + _version { + cid + } } }`, Results: map[string]any{ "Users": []map[string]any{ { "name": "Johnn", + "_version": []map[string]any{ + { + "cid": "bafyreig2j5zwcozovwzrxr7ivfnptlj7urlabzjbv4lls64hlkh6jmhfim", + }, + { + "cid": "bafyreib7afkd5hepl45wdtwwpai433bhnbd3ps5m2rv3masctda7b6mmxe", + }, + }, }, }, }, diff --git a/tests/integration/results.go b/tests/integration/results.go index 2a29d8887d..2939cfa10c 100644 --- a/tests/integration/results.go +++ b/tests/integration/results.go @@ -122,9 +122,12 @@ func (ucid *UniqueCid) Validate(s *state, actualValue any, msgAndArgs ...any) { } if isNew { - require.IsType(s.t, "", actualValue) + value, ok := actualValue.(string) + if !ok { + require.Fail(s.t, "UniqueCid actualValue string cast failed") + } - cid, err := cid.Decode(actualValue.(string)) + cid, err := cid.Decode(value) if err != nil { require.NoError(s.t, err) } diff --git a/tools/configs/golangci.yaml b/tools/configs/golangci.yaml index 998dcf5285..332b42a6d3 100644 --- a/tools/configs/golangci.yaml +++ b/tools/configs/golangci.yaml @@ -26,10 +26,6 @@ run: skip-dirs: # - net/ - # Enables skipping of directories: - # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ - skip-dirs-use-default: true - # which files to skip: they will be analyzed, but issues from them # won't be reported. Default value is empty list, but there is # no need to include all autogenerated files, we confidently recognize @@ -72,9 +68,6 @@ output: # print linter name in the end of issue text. print-linter-name: true - # make issues output unique by line. - uniq-by-line: true - # add a prefix to the output file references. path-prefix: "" @@ -131,6 +124,9 @@ linters: #====================================================[ Tweaks To Fix Issues or Exclude linter(s) on Select Locations ] issues: + # Make issues output unique by line. + uniq-by-line: true + # List of regexps of issue texts to exclude, empty list by default. # But independently from this option we use default exclude patterns, # it can be disabled by `exclude-use-default: false`. To list all @@ -331,9 +327,6 @@ linters-settings: # instead of using fmt.Print(fmt.Sprintf(...)), one can use fmt.Printf(...). govet: - # report about shadowed variables - check-shadowing: false - # settings per analyzer settings: printf: # analyzer name, run `go tool vet help` to see all analyzers @@ -343,18 +336,14 @@ linters-settings: - (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf - # enable or disable analyzers by name - # run `go tool vet help` to see all analyzers + enable-all: true enable: - - atomicalign - - nilness - - enable-all: false + disable-all: false disable: - shadow + - fieldalignment - disable-all: false lll: # max line length, lines longer will be reported. diff --git a/tools/defradb.containerfile b/tools/defradb.containerfile index 7612142a12..a25625922e 100644 --- a/tools/defradb.containerfile +++ b/tools/defradb.containerfile @@ -17,7 +17,7 @@ COPY go.mod go.sum Makefile ./ RUN make deps:modules COPY . . COPY --from=playground_build /repo/dist /repo/playground/dist/ -ENV BUILD_TAGS=playground +ENV BUILD_TAGS=playground,telemetry # manually copy libwasmer.so to fix linking issue https://github.com/wasmerio/wasmer-go/issues/281 RUN export WASMER_ARCH=$(go env GOHOSTARCH | sed "s/arm64/aarch64/") && \ export WASMER_PATH=$(go env GOMODCACHE)/github.com/wasmerio/wasmer-go@v1.0.4/wasmer/packaged/lib/linux-$WASMER_ARCH/libwasmer.so && \