github.com/zorawar87/trillian@v1.2.1/quota/etcd/quotaapi/conversions.go (about) 1 // Copyright 2017 Google Inc. All Rights Reserved. 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 15 package quotaapi 16 17 import ( 18 "fmt" 19 20 "github.com/google/trillian/quota/etcd/quotapb" 21 "github.com/google/trillian/quota/etcd/storagepb" 22 "google.golang.org/genproto/protobuf/field_mask" 23 ) 24 25 const ( 26 statePath = "state" 27 maxTokensPath = "max_tokens" 28 sequencingBasedPath = "sequencing_based" 29 timeBasedPath = "time_based" 30 ) 31 32 var ( 33 commonMask = &field_mask.FieldMask{Paths: []string{statePath, maxTokensPath}} 34 sequencingBasedMask = &field_mask.FieldMask{Paths: append(commonMask.Paths, sequencingBasedPath)} 35 timeBasedMask = &field_mask.FieldMask{Paths: append(commonMask.Paths, timeBasedPath)} 36 37 errBothReplenishmentStrategies = fmt.Errorf("both %q and %q paths specified, please pick a single replenishment strategy", sequencingBasedPath, timeBasedPath) 38 ) 39 40 // validateMask returns nil if mask is a valid Config mask, error otherwise. 41 // Paths must match the proto name of the fields (e.g., "time_based", not "TimeBased"). 42 // If fields belong to a oneof (such as "sequencing_based" and "time_based"), then only one field of 43 // the oneof may be specified. 44 func validateMask(mask *field_mask.FieldMask) error { 45 sequencingBasedFound := false 46 timeBasedFound := false 47 for _, path := range mask.Paths { 48 switch path { 49 case statePath, maxTokensPath: 50 // OK 51 case sequencingBasedPath: 52 if timeBasedFound { 53 return errBothReplenishmentStrategies 54 } 55 sequencingBasedFound = true 56 case timeBasedPath: 57 if sequencingBasedFound { 58 return errBothReplenishmentStrategies 59 } 60 timeBasedFound = true 61 default: 62 return fmt.Errorf("invalid field path for Config: %q", path) 63 } 64 } 65 return nil 66 } 67 68 // applyMask copies the fields specified by mask from src to dest. The mask must be first validated 69 // by validateMask(), as unknown paths are simply ignored by applyMask. 70 // Paths must match the proto name of the fields (e.g., "time_based", not "TimeBased"). 71 func applyMask(src *quotapb.Config, dest *storagepb.Config, mask *field_mask.FieldMask) { 72 for _, path := range mask.Paths { 73 switch path { 74 case statePath: 75 dest.State = storagepb.Config_State(storagepb.Config_State_value[src.State.String()]) 76 case maxTokensPath: 77 dest.MaxTokens = src.MaxTokens 78 case sequencingBasedPath: 79 if src.GetSequencingBased() == nil { 80 dest.ReplenishmentStrategy = nil 81 } else { 82 dest.ReplenishmentStrategy = &storagepb.Config_SequencingBased{ 83 SequencingBased: &storagepb.SequencingBasedStrategy{}, 84 } 85 } 86 case timeBasedPath: 87 if tb := src.GetTimeBased(); tb == nil { 88 dest.ReplenishmentStrategy = nil 89 } else { 90 dest.ReplenishmentStrategy = &storagepb.Config_TimeBased{ 91 TimeBased: &storagepb.TimeBasedStrategy{ 92 TokensToReplenish: tb.GetTokensToReplenish(), 93 ReplenishIntervalSeconds: tb.GetReplenishIntervalSeconds(), 94 }, 95 } 96 } 97 } 98 } 99 } 100 101 // convertToAPI returns the API representation of a storagepb.Config proto. 102 func convertToAPI(src *storagepb.Config) *quotapb.Config { 103 dest := "apb.Config{ 104 Name: src.Name, 105 State: quotapb.Config_State(quotapb.Config_State_value[src.State.String()]), 106 MaxTokens: src.MaxTokens, 107 } 108 sb := src.GetSequencingBased() 109 tb := src.GetTimeBased() 110 switch { 111 case sb != nil: 112 dest.ReplenishmentStrategy = "apb.Config_SequencingBased{ 113 SequencingBased: "apb.SequencingBasedStrategy{}, 114 } 115 case tb != nil: 116 dest.ReplenishmentStrategy = "apb.Config_TimeBased{ 117 TimeBased: "apb.TimeBasedStrategy{ 118 TokensToReplenish: tb.TokensToReplenish, 119 ReplenishIntervalSeconds: tb.ReplenishIntervalSeconds, 120 }, 121 } 122 } 123 return dest 124 } 125 126 // convertToStorage returns the storage representation of a quotapb.Config proto. 127 func convertToStorage(src *quotapb.Config) *storagepb.Config { 128 // Instead of potentially duplicating logic, let's take advantage of applyMask by picking a 129 // pre-made mask that contains all fields we care about and apply it to a new proto. 130 var mask *field_mask.FieldMask 131 switch { 132 case src.GetSequencingBased() != nil: 133 mask = sequencingBasedMask 134 case src.GetTimeBased() != nil: 135 mask = timeBasedMask 136 default: 137 mask = commonMask 138 } 139 dest := &storagepb.Config{Name: src.Name} 140 applyMask(src, dest, mask) 141 return dest 142 }