github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/pkg/database/update.go (about)

     1  // Copyright © 2021 Kaleido, Inc.
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  //
     5  // Licensed under the Apache License, Version 2.0 (the "License");
     6  // you may not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //     http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing, software
    12  // distributed under the License is distributed on an "AS IS" BASIS,
    13  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  // See the License for the specific language governing permissions and
    15  // limitations under the License.
    16  
    17  package database
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"strings"
    23  
    24  	"github.com/kaleido-io/firefly/internal/i18n"
    25  )
    26  
    27  // UpdateBuilder is the output of the builder
    28  type UpdateBuilder interface {
    29  	// Set starts creation of a set operation
    30  	Set(field string, value interface{}) Update
    31  
    32  	// S starts an update that doesn't have any fields
    33  	S() Update
    34  
    35  	// Fields returns the available fields on the update
    36  	Fields() []string
    37  }
    38  
    39  type Update interface {
    40  	// Set adds a set condition to the update
    41  	Set(field string, value interface{}) Update
    42  
    43  	// IsEmpty
    44  	IsEmpty() bool
    45  
    46  	// Finalize completes the update, and for the plugin to validated output structure to convert
    47  	Finalize() (*UpdateInfo, error)
    48  }
    49  
    50  // UpdateFactory creates a update builder in the given context, and contains the rules on
    51  // which fields can be used by the builder (and how they are serialized)
    52  type UpdateFactory interface {
    53  	New(ctx context.Context) UpdateBuilder
    54  }
    55  
    56  // SetOperation is an individual update action to perform
    57  type SetOperation struct {
    58  	Field string
    59  	Value FieldSerialization
    60  }
    61  
    62  // UpdateInfo is the structure returned by Finalize to the plugin, to serialize this uilter
    63  // into the underlying database mechanism's uilter language
    64  type UpdateInfo struct {
    65  	SetOperations []*SetOperation
    66  }
    67  
    68  type setOperation struct {
    69  	field string
    70  	value interface{}
    71  }
    72  
    73  type updateBuilder struct {
    74  	ctx         context.Context
    75  	queryFields queryFields
    76  }
    77  
    78  func (ub *updateBuilder) Fields() []string {
    79  	keys := make([]string, len(ub.queryFields))
    80  	i := 0
    81  	for k := range ub.queryFields {
    82  		keys[i] = k
    83  		i++
    84  	}
    85  	return keys
    86  }
    87  
    88  func (ub *updateBuilder) Set(field string, value interface{}) Update {
    89  	return &setUpdate{
    90  		ub:            ub,
    91  		setOperations: []*setOperation{{field, value}},
    92  	}
    93  }
    94  
    95  func (ub *updateBuilder) S() Update {
    96  	return &setUpdate{
    97  		ub:            ub,
    98  		setOperations: []*setOperation{},
    99  	}
   100  }
   101  
   102  type setUpdate struct {
   103  	ub            *updateBuilder
   104  	setOperations []*setOperation
   105  }
   106  
   107  func (u *setUpdate) IsEmpty() bool {
   108  	return len(u.setOperations) == 0
   109  }
   110  
   111  func (u *setUpdate) Set(field string, value interface{}) Update {
   112  	u.setOperations = append(u.setOperations, &setOperation{field, value})
   113  	return u
   114  }
   115  
   116  func (u *UpdateInfo) String() string {
   117  	var buf strings.Builder
   118  	for i, si := range u.SetOperations {
   119  		if i > 0 {
   120  			buf.WriteString(", ")
   121  		}
   122  		buf.WriteString(fmt.Sprintf("%s=%s", si.Field, valueString(si.Value)))
   123  	}
   124  	return buf.String()
   125  }
   126  
   127  func (u *setUpdate) Finalize() (*UpdateInfo, error) {
   128  	ui := &UpdateInfo{
   129  		SetOperations: make([]*SetOperation, len(u.setOperations)),
   130  	}
   131  	for i, si := range u.setOperations {
   132  		name := strings.ToLower(si.field)
   133  		field, ok := u.ub.queryFields[name]
   134  		if !ok {
   135  			return nil, i18n.NewError(u.ub.ctx, i18n.MsgInvalidFilterField, name)
   136  		}
   137  		value := field.getSerialization()
   138  		if err := value.Scan(si.value); err != nil {
   139  			return nil, i18n.WrapError(u.ub.ctx, err, i18n.MsgInvalidValueForFilterField, name)
   140  		}
   141  		ui.SetOperations[i] = &SetOperation{
   142  			Field: name,
   143  			Value: value,
   144  		}
   145  	}
   146  	return ui, nil
   147  }