Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 21 additions & 4 deletions v2/arangodb/collection_documents_delete_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,20 +120,37 @@ func (c *collectionDocumentDeleteResponseReader) Read(i interface{}) (Collection

var meta CollectionDocumentDeleteResponse

if c.options != nil {
meta.Old = c.options.OldObject
}
var response Unmarshal[shared.ResponseStruct, Unmarshal[DocumentMeta, UnmarshalData]]

if err := c.array.Unmarshal(newMultiUnmarshaller(&meta, newUnmarshalInto(i))); err != nil {
if err := c.array.Unmarshal(&response); err != nil {
if err == io.EOF {
return CollectionDocumentDeleteResponse{}, shared.NoMoreDocumentsError{}
}
return CollectionDocumentDeleteResponse{}, err
}

if q := response.Current; q != nil {
meta.ResponseStruct = *q
}

if q := response.Object.Current; q != nil {
meta.DocumentMeta = *q
}

if meta.Error != nil && *meta.Error {
return meta, meta.AsArangoError()
}

if err := response.Object.Object.Inject(i); err != nil {
return CollectionDocumentDeleteResponse{}, err
}

if c.options != nil && c.options.OldObject != nil {
meta.Old = c.options.OldObject
if err := response.Object.Object.Extract("old").Inject(meta.Old); err != nil {
return CollectionDocumentDeleteResponse{}, err
}
}

return meta, nil
}
39 changes: 27 additions & 12 deletions v2/arangodb/collection_documents_read_impl.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//
// DISCLAIMER
//
// Copyright 2020-2024 ArangoDB GmbH, Cologne, Germany
// Copyright 2020-2025 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -74,23 +74,24 @@ func (c collectionDocumentRead) ReadDocument(ctx context.Context, key string, re
func (c collectionDocumentRead) ReadDocumentWithOptions(ctx context.Context, key string, result interface{}, opts *CollectionDocumentReadOptions) (DocumentMeta, error) {
url := c.collection.url("document", key)

var response struct {
shared.ResponseStruct `json:",inline"`
DocumentMeta `json:",inline"`
}

data := newUnmarshalInto(result)
var response Unmarshal[shared.ResponseStruct, Unmarshal[DocumentMeta, UnmarshalData]]

resp, err := connection.CallGet(ctx, c.collection.connection(), url, newMultiUnmarshaller(&response, data), c.collection.withModifiers(opts.modifyRequest)...)
resp, err := connection.CallGet(ctx, c.collection.connection(), url, &response, c.collection.withModifiers(opts.modifyRequest)...)
if err != nil {
return DocumentMeta{}, err
}

switch code := resp.Code(); code {
case http.StatusOK:
return response.DocumentMeta, nil
if err := response.Object.Object.Inject(result); err != nil {
return DocumentMeta{}, err
}
if z := response.Object.Current; z != nil {
return *z, nil
}
return DocumentMeta{}, nil
default:
return DocumentMeta{}, response.AsArangoErrorWithCode(code)
return DocumentMeta{}, response.Current.AsArangoErrorWithCode(code)
}
}

Expand All @@ -112,18 +113,32 @@ func (c *collectionDocumentReadResponseReader) Read(i interface{}) (CollectionDo
return CollectionDocumentReadResponse{}, shared.NoMoreDocumentsError{}
}

var meta CollectionDocumentReadResponse
var response Unmarshal[shared.ResponseStruct, Unmarshal[DocumentMeta, UnmarshalData]]

if err := c.array.Unmarshal(newMultiUnmarshaller(&meta, newUnmarshalInto(i))); err != nil {
if err := c.array.Unmarshal(&response); err != nil {
if err == io.EOF {
return CollectionDocumentReadResponse{}, shared.NoMoreDocumentsError{}
}
return CollectionDocumentReadResponse{}, err
}

var meta CollectionDocumentReadResponse

if q := response.Current; q != nil {
meta.ResponseStruct = *q
}

if q := response.Object.Current; q != nil {
meta.DocumentMeta = *q
}

if meta.Error != nil && *meta.Error {
return meta, meta.AsArangoError()
}

if err := response.Object.Object.Inject(i); err != nil {
return CollectionDocumentReadResponse{}, err
}

return meta, nil
}
15 changes: 9 additions & 6 deletions v2/arangodb/collection_indexes_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,24 +228,27 @@ func (c *collectionIndexes) EnsureInvertedIndex(ctx context.Context, options *In
func (c *collectionIndexes) ensureIndex(ctx context.Context, reqData interface{}, result interface{}) (bool, error) {
urlEndpoint := c.collection.db.url("_api", "index")

var response struct {
shared.ResponseStruct `json:",inline"`
}
data := newUnmarshalInto(&result)
var response Unmarshal[shared.ResponseStruct, UnmarshalData]

resp, err := connection.CallPost(ctx, c.collection.connection(), urlEndpoint, newMultiUnmarshaller(&response, data), &reqData,
resp, err := connection.CallPost(ctx, c.collection.connection(), urlEndpoint, &response, &reqData,
c.collection.withModifiers(connection.WithQuery("collection", c.collection.name))...)
if err != nil {
return false, errors.WithStack(err)
}

switch code := resp.Code(); code {
case http.StatusOK:
if err := response.Object.Inject(result); err != nil {
return false, err
}
return false, nil
case http.StatusCreated:
if err := response.Object.Inject(result); err != nil {
return false, err
}
return true, nil
default:
return false, response.AsArangoErrorWithCode(code)
return false, response.Current.AsArangoErrorWithCode(code)
}
}

Expand Down
19 changes: 9 additions & 10 deletions v2/arangodb/cursor_impl.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//
// DISCLAIMER
//
// Copyright 2020-2023 ArangoDB GmbH, Cologne, Germany
// Copyright 2020-2025 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -144,22 +144,21 @@ func (c *cursor) readDocument(ctx context.Context, result interface{}) (Document
}
}

var data byteDecoder
if err := c.data.Result.Read(&data); err != nil {
var res Unmarshal[DocumentMeta, UnmarshalData]

if err := c.data.Result.Read(&res); err != nil {
return DocumentMeta{}, err
}

var meta DocumentMeta

if err := data.Unmarshal(&meta); err != nil {
// Ignore error
if err := res.Object.Inject(result); err != nil {
return DocumentMeta{}, err
}

if err := data.Unmarshal(result); err != nil {
return DocumentMeta{}, err
if m := res.Current; m != nil {
return *m, nil
}

return meta, nil
return DocumentMeta{}, nil
}

func (c *cursor) getNextBatch(ctx context.Context, retryBatchID string) error {
Expand Down
10 changes: 7 additions & 3 deletions v2/arangodb/database_transaction_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,17 @@ func (d databaseTransaction) listTransactionsWithStatuses(ctx context.Context, s
} `json:"transactions,omitempty"`
}

var response shared.ResponseStruct
var response Unmarshal[shared.ResponseStruct, UnmarshalData]

resp, err := connection.CallGet(ctx, d.db.connection(), url, newMultiUnmarshaller(&result, &response), d.db.modifiers...)
resp, err := connection.CallGet(ctx, d.db.connection(), url, &response, d.db.modifiers...)
if err != nil {
return nil, errors.WithStack(err)
}

if err := response.Object.Inject(&result); err != nil {
return nil, err
}

switch code := resp.Code(); code {
case http.StatusOK:
var t []Transaction
Expand All @@ -80,7 +84,7 @@ func (d databaseTransaction) listTransactionsWithStatuses(ctx context.Context, s

return t, nil
default:
return nil, response.AsArangoErrorWithCode(code)
return nil, response.Current.AsArangoErrorWithCode(code)
}
}

Expand Down
109 changes: 109 additions & 0 deletions v2/arangodb/unmarshaller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//
// DISCLAIMER
//
// Copyright 2025 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//

package arangodb

import (
"encoding/json"

"github.com/pkg/errors"
)

type Unmarshaler interface {
json.Unmarshaler

Inject(object any) error
Extract(key string) Unmarshaler
}

type Unmarshal[C, T any] struct {
Current *C

Object T
}

func (u *Unmarshal[C, T]) UnmarshalJSON(bytes []byte) error {
u.Current = nil

var q C

if err := json.Unmarshal(bytes, &q); err == nil {
u.Current = &q
}

return json.Unmarshal(bytes, &u.Object)
}

type UnmarshalData []byte

func (u *UnmarshalData) Inject(object any) error {
if u == nil {
return errors.Errorf("Data provided is nil")
}

return json.Unmarshal(*u, object)
}

func (u *UnmarshalData) Extract(key string) Unmarshaler {
if u == nil {
return errorUnmarshalData{err: errors.Errorf("Data provided is nil")}
}

var z map[string]UnmarshalData

if err := json.Unmarshal(*u, &z); err != nil {
return errorUnmarshalData{err: err}
}

if v, ok := z[key]; ok {
return &v
}

return errorUnmarshalData{err: errors.Errorf("Key %s not found", key)}
}

func (u *UnmarshalData) UnmarshalJSON(bytes []byte) error {
if u == nil {
return errors.Errorf("Data provided is nil")
}

z := make([]byte, len(bytes))

copy(z, bytes)

*u = z
return nil
}

type errorUnmarshalData struct {
err error
}

func (e errorUnmarshalData) UnmarshalJSON(bytes []byte) error {
return e.err
}

func (e errorUnmarshalData) Inject(object any) error {
return e.err
}

func (e errorUnmarshalData) Extract(key string) Unmarshaler {
return e
}
59 changes: 0 additions & 59 deletions v2/arangodb/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,65 +30,6 @@ import (
"github.com/pkg/errors"
)

var _ json.Unmarshaler = &multiUnmarshaller{}
var _ json.Marshaler = &multiUnmarshaller{}

func newMultiUnmarshaller(obj ...interface{}) json.Unmarshaler {
return &multiUnmarshaller{
obj: obj,
}
}

type multiUnmarshaller struct {
obj []interface{}
}

func (m multiUnmarshaller) MarshalJSON() ([]byte, error) {
r := map[string]interface{}{}
for _, o := range m.obj {
z := map[string]interface{}{}
if d, err := json.Marshal(o); err != nil {
return nil, err
} else {
if err := json.Unmarshal(d, &z); err != nil {
return nil, err
}
}

for k, v := range z {
r[k] = v
}
}

return json.Marshal(r)
}

func (m multiUnmarshaller) UnmarshalJSON(d []byte) error {
for _, o := range m.obj {
if err := json.Unmarshal(d, o); err != nil {
return err
}
}

return nil
}

type byteDecoder struct {
data []byte
}

func (b *byteDecoder) UnmarshalJSON(d []byte) error {
b.data = make([]byte, len(d))

copy(b.data, d)

return nil
}

func (b *byteDecoder) Unmarshal(i interface{}) error {
return json.Unmarshal(b.data, i)
}

func newUnmarshalInto(obj interface{}) *UnmarshalInto {
return &UnmarshalInto{obj}
}
Expand Down
Loading