1// Copyright 2018 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package datastore
16
17import (
18	"fmt"
19
20	pb "google.golang.org/genproto/googleapis/datastore/v1"
21)
22
23// A Mutation represents a change to a Datastore entity.
24type Mutation struct {
25	key *Key // needed for transaction PendingKeys and to dedup deletions
26	mut *pb.Mutation
27	err error
28}
29
30func (m *Mutation) isDelete() bool {
31	_, ok := m.mut.Operation.(*pb.Mutation_Delete)
32	return ok
33}
34
35// NewInsert creates a mutation that will save the entity src into the datastore with
36// key k, returning an error if k already exists.
37// See Client.Put for valid values of src.
38func NewInsert(k *Key, src interface{}) *Mutation {
39	if !k.valid() {
40		return &Mutation{err: ErrInvalidKey}
41	}
42	p, err := saveEntity(k, src)
43	if err != nil {
44		return &Mutation{err: err}
45	}
46	return &Mutation{
47		key: k,
48		mut: &pb.Mutation{Operation: &pb.Mutation_Insert{Insert: p}},
49	}
50}
51
52// NewUpsert creates a mutation that saves the entity src into the datastore with key
53// k, whether or not k exists. See Client.Put for valid values of src.
54func NewUpsert(k *Key, src interface{}) *Mutation {
55	if !k.valid() {
56		return &Mutation{err: ErrInvalidKey}
57	}
58	p, err := saveEntity(k, src)
59	if err != nil {
60		return &Mutation{err: err}
61	}
62	return &Mutation{
63		key: k,
64		mut: &pb.Mutation{Operation: &pb.Mutation_Upsert{Upsert: p}},
65	}
66}
67
68// NewUpdate creates a mutation that replaces the entity in the datastore with key k,
69// returning an error if k does not exist. See Client.Put for valid values of src.
70func NewUpdate(k *Key, src interface{}) *Mutation {
71	if !k.valid() {
72		return &Mutation{err: ErrInvalidKey}
73	}
74	if k.Incomplete() {
75		return &Mutation{err: fmt.Errorf("datastore: can't update the incomplete key: %v", k)}
76	}
77	p, err := saveEntity(k, src)
78	if err != nil {
79		return &Mutation{err: err}
80	}
81	return &Mutation{
82		key: k,
83		mut: &pb.Mutation{Operation: &pb.Mutation_Update{Update: p}},
84	}
85}
86
87// NewDelete creates a mutation that deletes the entity with key k.
88func NewDelete(k *Key) *Mutation {
89	if !k.valid() {
90		return &Mutation{err: ErrInvalidKey}
91	}
92	if k.Incomplete() {
93		return &Mutation{err: fmt.Errorf("datastore: can't delete the incomplete key: %v", k)}
94	}
95	return &Mutation{
96		key: k,
97		mut: &pb.Mutation{Operation: &pb.Mutation_Delete{Delete: keyToProto(k)}},
98	}
99}
100
101func mutationProtos(muts []*Mutation) ([]*pb.Mutation, error) {
102	// If any of the mutations have errors, collect and return them.
103	var merr MultiError
104	for i, m := range muts {
105		if m.err != nil {
106			if merr == nil {
107				merr = make(MultiError, len(muts))
108			}
109			merr[i] = m.err
110		}
111	}
112	if merr != nil {
113		return nil, merr
114	}
115	var protos []*pb.Mutation
116	// Collect protos. Remove duplicate deletions (see deleteMutations).
117	seen := map[string]bool{}
118	for _, m := range muts {
119		if m.isDelete() {
120			ks := m.key.String()
121			if seen[ks] {
122				continue
123			}
124			seen[ks] = true
125		}
126		protos = append(protos, m.mut)
127	}
128	return protos, nil
129}
130