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  }