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
1 change: 1 addition & 0 deletions sdk/data/azcosmos/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## 0.3.4 (Unreleased)

### Features Added
* Added `NullPartitionKey` variable to create and query documents with null partition key in CosmosDB

### Breaking Changes

Expand Down
175 changes: 175 additions & 0 deletions sdk/data/azcosmos/emulator_cosmos_item_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,181 @@ func TestItemCRUD(t *testing.T) {
}
}

func TestItemCRUDforNullPartitionKey(t *testing.T) {
emulatorTests := newEmulatorTests(t)
client := emulatorTests.getClient(t)

database := emulatorTests.createDatabase(t, context.TODO(), client, "itemCRUD")
defer emulatorTests.deleteDatabase(t, context.TODO(), database)
properties := ContainerProperties{
ID: "aContainer",
PartitionKeyDefinition: PartitionKeyDefinition{
Paths: []string{"/partitionKey"},
},
}

_, err := database.CreateContainer(context.TODO(), properties, nil)
if err != nil {
t.Fatalf("Failed to create container: %v", err)
}

item := map[string]interface{}{
"partitionKey": nil,
"id": "1",
"value": "2",
"count": 3,
"description": "4",
}

container, _ := database.NewContainer("aContainer")
pk := NullPartitionKey

marshalled, err := json.Marshal(item)
if err != nil {
t.Fatal(err)
}

itemResponse, err := container.CreateItem(context.TODO(), pk, marshalled, nil)
if err != nil {
t.Fatalf("Failed to create item: %v", err)
}

if itemResponse.SessionToken == "" {
t.Fatalf("Session token is empty")
}

// No content on write by default
if len(itemResponse.Value) != 0 {
t.Fatalf("Expected empty response, got %v", itemResponse.Value)
}

itemResponse, err = container.ReadItem(context.TODO(), pk, "1", nil)
if err != nil {
t.Fatalf("Failed to read item: %v", err)
}

if len(itemResponse.Value) == 0 {
t.Fatalf("Expected non-empty response, got %v", itemResponse.Value)
}

var itemResponseBody map[string]interface{}
err = json.Unmarshal(itemResponse.Value, &itemResponseBody)
if err != nil {
t.Fatalf("Failed to unmarshal item response: %v", err)
}
if itemResponseBody["id"] != "1" {
t.Fatalf("Expected id to be 1, got %v", itemResponseBody["id"])
}
if itemResponseBody["value"] != "2" {
t.Fatalf("Expected value to be 2, got %v", itemResponseBody["value"])
}

item["value"] = "3"
marshalled, err = json.Marshal(item)
if err != nil {
t.Fatal(err)
}
itemResponse, err = container.ReplaceItem(context.TODO(), pk, "1", marshalled, &ItemOptions{EnableContentResponseOnWrite: true})
if err != nil {
t.Fatalf("Failed to replace item: %v", err)
}

// Explicitly requesting body on write
if len(itemResponse.Value) == 0 {
t.Fatalf("Expected non-empty response, got %v", itemResponse.Value)
}

err = json.Unmarshal(itemResponse.Value, &itemResponseBody)
if err != nil {
t.Fatalf("Failed to unmarshal item response: %v", err)
}
if itemResponseBody["id"] != "1" {
t.Fatalf("Expected id to be 1, got %v", itemResponseBody["id"])
}
if itemResponseBody["value"] != "3" {
t.Fatalf("Expected value to be 3, got %v", itemResponseBody["value"])
}

item["value"] = "4"
marshalled, err = json.Marshal(item)
if err != nil {
t.Fatal(err)
}
itemResponse, err = container.UpsertItem(context.TODO(), pk, marshalled, &ItemOptions{EnableContentResponseOnWrite: true})
if err != nil {
t.Fatalf("Failed to upsert item: %v", err)
}

// Explicitly requesting body on write
if len(itemResponse.Value) == 0 {
t.Fatalf("Expected non-empty response, got %v", itemResponse.Value)
}

err = json.Unmarshal(itemResponse.Value, &itemResponseBody)
if err != nil {
t.Fatalf("Failed to unmarshal item response: %v", err)
}
if itemResponseBody["id"] != "1" {
t.Fatalf("Expected id to be 1, got %v", itemResponseBody["id"])
}
if itemResponseBody["value"] != "4" {
t.Fatalf("Expected value to be 4, got %v", itemResponseBody["value"])
}

patchItem := PatchOperations{}
patchItem.AppendReplace("/value", "5")
patchItem.AppendSet("/hello", "world")
patchItem.AppendAdd("/foo", "bar")
patchItem.AppendRemove("/description")
patchItem.AppendIncrement("/count", 1)

itemResponse, err = container.PatchItem(context.TODO(), pk, "1", patchItem, nil)
if err != nil {
t.Fatalf("Failed to patch item: %v", err)
}

// No content on write by default
if len(itemResponse.Value) != 0 {
t.Fatalf("Expected empty response, got %v", itemResponse.Value)
}

itemResponse, _ = container.ReadItem(context.TODO(), pk, "1", nil)

err = json.Unmarshal(itemResponse.Value, &itemResponseBody)
if err != nil {
t.Fatalf("Failed to unmarshal item response: %v", err)
}

if itemResponseBody["value"] != "5" {
t.Fatalf("Expected value to be 5, got %v", itemResponseBody["id"])
}

if itemResponseBody["hello"] != "world" {
t.Fatalf("Expected hello to be world, got %v", itemResponseBody["hello"])
}

if itemResponseBody["foo"] != "bar" {
t.Fatalf("Expected foo to be bar, got %v", itemResponseBody["foo"])
}

if itemResponseBody["count"].(float64) != float64(4) {
t.Fatalf("Expected count to be 4, got %v", itemResponseBody["count"])
}

if itemResponseBody["toremove"] != nil {
t.Fatalf("Expected toremove to be nil, got %v", itemResponseBody)
}

itemResponse, err = container.DeleteItem(context.TODO(), pk, "1", nil)
if err != nil {
t.Fatalf("Failed to replace item: %v", err)
}

if len(itemResponse.Value) != 0 {
t.Fatalf("Expected empty response, got %v", itemResponse.Value)
}
}

func TestItemIdEncodingRoutingGW(t *testing.T) {
emulatorTests := newEmulatorTests(t)
client := emulatorTests.getClient(t)
Expand Down
5 changes: 5 additions & 0 deletions sdk/data/azcosmos/partition_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ type PartitionKey struct {
values []interface{}
}

// NullPartitionKey represents a partition key with a null value.
var NullPartitionKey PartitionKey = PartitionKey{
values: []interface{}{nil},
}

// NewPartitionKeyString creates a partition key with a string value.
func NewPartitionKeyString(value string) PartitionKey {
components := []interface{}{value}
Expand Down
7 changes: 7 additions & 0 deletions sdk/data/azcosmos/partition_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ func TestSerialization(t *testing.T) {
"[\"some string\"]": NewPartitionKeyString("some string"),
"[true]": NewPartitionKeyBool(true),
"[false]": NewPartitionKeyBool(false),
"[null]": NullPartitionKey,
}

for expectedSerialization, pk := range validTypes {
Expand Down Expand Up @@ -68,4 +69,10 @@ func TestPartitionKeyEquality(t *testing.T) {
if !reflect.DeepEqual(pk, pk2) {
t.Errorf("Expected %v to equal %v", pk, pk2)
}

pk = NullPartitionKey
pk2 = NullPartitionKey
if !reflect.DeepEqual(pk, pk2) {
t.Errorf("Expected %v to equal %v", pk, pk2)
}
}