go.ligato.io/vpp-agent/v3@v3.5.0/pkg/models/item.go (about) 1 // Copyright (c) 2018 Cisco and/or its affiliates. 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 models 16 17 import ( 18 "fmt" 19 "path" 20 21 "github.com/go-errors/errors" 22 "google.golang.org/protobuf/proto" 23 "google.golang.org/protobuf/reflect/protoregistry" 24 "google.golang.org/protobuf/types/known/anypb" 25 26 api "go.ligato.io/vpp-agent/v3/proto/ligato/generic" 27 ) 28 29 // This constant is used as prefix for TypeUrl when marshalling to Any. 30 const ligatoModels = "models.ligato.io/" 31 32 // MarshalItem is helper function for marshalling model instance into item 33 func MarshalItem(pb proto.Message) (*api.Item, error) { 34 return MarshalItemUsingModelRegistry(pb, DefaultRegistry) 35 } 36 37 // MarshalItemUsingModelRegistry is helper function for marshalling model instance 38 // into item (using given model registry) 39 func MarshalItemUsingModelRegistry(pb proto.Message, modelRegistry Registry) (*api.Item, error) { 40 model, err := GetModelFromRegistryFor(pb, modelRegistry) 41 if err != nil { 42 return nil, errors.Errorf("can't find known model "+ 43 "for message due to: %v (message = %+v)", err, pb) 44 } 45 name, err := model.InstanceName(pb) 46 if err != nil { 47 return nil, errors.Errorf("can't compute model instance name due to: %v (message %+v)", err, pb) 48 } 49 50 any, err := anypb.New(pb) 51 if err != nil { 52 return nil, err 53 } 54 any.TypeUrl = ligatoModels + string(pb.ProtoReflect().Descriptor().FullName()) 55 56 item := &api.Item{ 57 Id: &api.Item_ID{ 58 Model: model.Name(), 59 Name: name, 60 }, 61 Data: &api.Data{ 62 Union: &api.Data_Any{Any: any}, 63 }, 64 } 65 return item, nil 66 } 67 68 // UnmarshalItem is helper function for unmarshalling items. 69 func UnmarshalItem(item *api.Item) (proto.Message, error) { 70 return UnmarshalItemUsingModelRegistry(item, DefaultRegistry) 71 } 72 73 // UnmarshalItemUsingModelRegistry is helper function for unmarshalling items (using given model registry) 74 func UnmarshalItemUsingModelRegistry(item *api.Item, modelRegistry Registry) (proto.Message, error) { 75 // check existence of known model 76 model, err := GetModelFromModelRegistryForItem(item, modelRegistry) 77 if err != nil { 78 return nil, err 79 } 80 81 // unmarshal item's inner data 82 // We must distinguish between locally and remotely known models with respect to the underlying go type. 83 // LocallyKnownModel is used for proto message with go type generated from imported proto file and known 84 // at compile time, while RemotelyKnownModel can't produce such go typed instances (we know the name of go type, 85 // but can't produce it from remote information) so dynamic proto message must be enough (*dynamicpb.Message)) 86 if _, ok := model.(*LocallyKnownModel); ok { 87 return unmarshalItemDataAnyOfLocalModel(item.GetData().GetAny()) 88 } 89 return unmarshalItemDataAnyOfRemoteModel(item.GetData().GetAny(), modelRegistry.MessageTypeRegistry()) 90 } 91 92 // unmarshalItemDataAnyOfRemoteModel unmarshalls the generic data part of api.Item that has remote model. 93 // The unmarshalled proto.Message will have dynamic type (*dynamicpb.Message). 94 func unmarshalItemDataAnyOfRemoteModel(itemAny *anypb.Any, msgTypeResolver *protoregistry.Types) (proto.Message, error) { 95 msg, err := anypb.UnmarshalNew(itemAny, proto.UnmarshalOptions{ 96 Resolver: msgTypeResolver, 97 }) 98 if err != nil { 99 return nil, err 100 } 101 return msg, nil 102 } 103 104 // unmarshalItemDataAnyOfLocalModel unmarshalls the generic data part of api.Item that has local model. 105 // The unmarshalled proto.Message will have the go type of model generated go structures (that is due to 106 // go type registering in init() method of generated go structures file). 107 func unmarshalItemDataAnyOfLocalModel(itemAny *anypb.Any) (proto.Message, error) { 108 m, err := anypb.UnmarshalNew(itemAny, proto.UnmarshalOptions{}) 109 if err != nil { 110 return nil, err 111 } 112 return m, nil 113 } 114 115 // GetModelForItem returns model for given item. 116 func GetModelForItem(item *api.Item) (KnownModel, error) { 117 return GetModelFromModelRegistryForItem(item, DefaultRegistry) 118 } 119 120 // GetModelFromModelRegistryForItem returns model for given item (using given model registry) 121 func GetModelFromModelRegistryForItem(item *api.Item, modelRegistry Registry) (KnownModel, error) { 122 if item.GetId() == nil { 123 return nil, fmt.Errorf("item id is nil") 124 } 125 modelPath := item.GetId().GetModel() 126 model, err := GetModelFromRegistry(modelPath, modelRegistry) 127 if err != nil { 128 return nil, fmt.Errorf("can't find modelpath %v in provided "+ 129 "models %+v", modelPath, modelRegistry) 130 } 131 // TODO: check prefix in type url? 132 return model, nil 133 } 134 135 // GetKeyForItem returns key for given item. 136 func GetKeyForItem(item *api.Item) (string, error) { 137 return GetKeyForItemUsingModelRegistry(item, DefaultRegistry) 138 } 139 140 // GetKeyForItem returns key for given item (using given model registry) 141 func GetKeyForItemUsingModelRegistry(item *api.Item, modelRegistry Registry) (string, error) { 142 model, err := GetModelFromModelRegistryForItem(item, modelRegistry) 143 if err != nil { 144 return "", err 145 } 146 key := path.Join(model.KeyPrefix(), item.GetId().GetName()) 147 return key, nil 148 }