resource

package
v0.22.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 22, 2020 License: Apache-2.0 Imports: 8 Imported by: 0

README

Description

The common interfaces, types and helper functions to work with identifiers are defined in resource file. Please see example to see how to work with identifiers.

Codecs

You could register resource.Codec for you PB type to be used to convert atlas.rpc.Identifier that defined for that type.

By default if PB resource is undefined (nil) the atlas.rpc.Identifier is converted to a string in fully qualified format specified for Atlas References, otherwise the Resource ID part is returned as string value.

If driver.Value is nil default codecs returns nil atlas.rpc.Identifier, if you want to override such behavior in order to return empty atlas.rpc.Identifier that could be rendered to null string in JSON -

If resource.Codec is not registered for a PB type the value of identifier is converted from driver.Value to a string. If Resource Type is not found it is populated from the name of PB type, the Application Name is populated if was registered. (see RegisterApplication).

protoc-gen-gorm

The plugin has support of atlas.rpc.Identifier for all association types. All you need is to define your Primary/Foreign keys as a fields of atlas.rpc.Identifier type.

In the XxxORM generated models atlas.rpc.Identifier will be replaced to the type specified in (gorm.field).tag option. The only numeric and text formats are supported. If type is not set it will be generated as interface{}.

If you want to expose foreign keys on API just leave them with empty type in gorm.field.tag and it will be calculated based on the parent's primary key type.

By default Identifiers are nillable, it means that for primary keys you need to set corresponding tag primary_key: true and for foreign keys and external references not_null: true.

The Postgres types from tags are converted as follows:

    switch type_from_tag {
    case "uuid", "text", "char", "array", "cidr", "inet", "macaddr":
        orm_model_field = "string"
    case "smallint", "integer", "bigint", "numeric", "smallserial", "serial", "bigserial":
        orm_model_field = "int64"
    case "jsonb", "bytea":
        orm_model_field = "[]byte"
    case "":
        orm_model_field = "interface{}"
    default:
        return errors.New("unknown tag type of atlas.rpc.Identifier")
    }

NOTE Be sure to set type properly for association fields.

Step 1 Let's define PB resources

syntax = "proto3";

import "github.com/infobloxopen/atlas-app-toolkit/rpc/resource/resource.proto";
import "github.com/infobloxopen/protoc-gen-gorm/options/gorm.proto";

option go_package = "github.com/yourapp/pb;pb";

message A {
    option (gorm.opts).ormable = true;
    
    atlas.rpc.Identifier id = 1 [(gorm.field).tag = {type: "integer" primary_key: true}];
    string value = 2;
    repeated B b_list = 3; // has many
    atlas.rpc.Identifier external = 4 [(gorm.field).tag = {type: "text"}];
}

message B {
    option (gorm.opts).ormable = true;
    
    atlas.rpc.Identifier id = 1 [(gorm.field).tag = {type: "integer" primary_key: true}];
    string value = 2;
     // foreign key to A  parent. !!! Will be set to the type of A.id
    atlas.rpc.Identifier a_id = 3;
    atlas.rpc.Identifier external_not_null = 4 [(gorm.field).tag = {type: "text" not_null: true}];
}

In JSON it could look like

{
  "id": "someapp/resource:a/1",
  "value": "someAvalue",
  "b_list": [
    {
      "id": "someapp/resource:b/1",
      "value": "someBvalue",
      "a_id": "someapp/resource:a/1"
    }
  ]
}

The generated code could be:

import "github.com/infobloxopen/atlas-app-toolkit/gorm/resource"

type AORM struct {
	Id int64
	Value string
	BList []*BORM
	External *string
}

type BORM struct {
	Id int64
	Value string
	AId *int64
	ExternalNotNull string
}

Step 2 The last thing you need to make identifiers work correctly is to register the name of your application, that name will be used during encoding to populate ApplicationName field of atlas.rpc.Identifier.

package main

import "github.com/infobloxopen/atlas-app-toolkit/gorm/resource"

func main() {
    resource.RegisterApplication("MyAppName")
}

FAQ

How to customize name of my PB type?

Add XXX_MessageName() string method to you PB type. See Name function.

I want to validate/generate atlas.rpc.Identifier for PB types in my application

Implement resource.Codec for your PB types and you will be given a full control on how Identifiers are converted to/from driver.Value.

Documentation

Overview

Example
package main

import (
	"fmt"

	resourcepb "github.com/infobloxopen/atlas-app-toolkit/rpc/resource"
	"github.com/infobloxopen/atlas-app-toolkit/util"
)

type ExampleGoType struct {
	ID         int64
	ExternalID string
	VarName    string
}

type ExampleProtoMessage struct {
	Id         *resourcepb.Identifier
	ExternalId *resourcepb.Identifier
	VarName    string
}

func (ExampleProtoMessage) XXX_MessageName() string { return "ExampleProtoMessage" }
func (ExampleProtoMessage) Reset()                  {}
func (ExampleProtoMessage) String() string          { return "ExampleProtoMessage" }
func (ExampleProtoMessage) ProtoMessage()           {}

func main() {
	RegisterApplication("simpleapp")

	// you want to convert PB type to your application type
	toGoTypeFunc := func(msg *ExampleProtoMessage) (*ExampleGoType, error) {
		var v ExampleGoType

		// arbitrary variables
		v.VarName = msg.VarName

		// convert RPC identifier using UUID Codec for ExampleProtoMessage resource type
		if id, err := DecodeInt64(msg, msg.Id); err != nil {
			return nil, err
		} else {
			v.ID = id
		}
		// convert RPC identifier using External Codec for default resource type
		if id, err := Decode(nil, msg.ExternalId); err != nil {
			return nil, err
		} else {
			v.ExternalID = id.(string)
		}
		return &v, nil
	}

	// let's create PB message
	pb := &ExampleProtoMessage{
		Id: &resourcepb.Identifier{
			ApplicationName: "simpleapp",
			ResourceType:    "example_proto_message",
			ResourceId:      "12",
		},
		// ExternalId stores data about "external_resource" that belongs to
		// "externalapp" and has id "id"
		ExternalId: &resourcepb.Identifier{
			ApplicationName: "externalapp",
			ResourceType:    "external_resource",
			ResourceId:      "id",
		},
		VarName: "somename",
	}

	val, err := toGoTypeFunc(pb)
	if err != nil {
		fmt.Printf("failed to convert TestProtoMessage to TestGoType: %s\n", err)
		return
	}

	fmt.Printf("application name of integer id: %v\n", val.ID)
	fmt.Printf("application name of fqstring id: %v\n", val.ExternalID)

	// so now you want to convert it back to PB representation
	toPBMessageFunc := func(v *ExampleGoType) (*ExampleProtoMessage, error) {
		var pb ExampleProtoMessage

		// arbitrary variables
		pb.VarName = v.VarName

		// convert internal id to RPC representation using registered UUID codec
		if id, err := Encode(pb, v.ID); err != nil {
			return nil, err
		} else {
			pb.Id = id
		}

		// convert fqstring id to RPC representation using registered External codec
		if id, err := Encode(nil, v.ExternalID); err != nil {
			return nil, err
		} else {
			pb.ExternalId = id
		}

		return &pb, nil
	}

	pb, err = toPBMessageFunc(val)
	if err != nil {
		fmt.Println("failed to convert TestGoType to TestProtoMessage")
	}

	fmt.Printf("application name of internal id: %s\n", pb.Id.GetApplicationName())
	fmt.Printf("resource type of internal id: %s\n", util.CamelToSnake(pb.Id.GetResourceType()))
	fmt.Printf("resource id of internal id: %s\n", pb.Id.GetResourceId())
	fmt.Printf("application name of fqstring id: %s\n", pb.ExternalId.GetApplicationName())
	fmt.Printf("resource type of fqstring id: %s\n", pb.ExternalId.GetResourceType())
	fmt.Printf("resource id of fqstring id: %s\n", pb.ExternalId.GetResourceId())

}
Output:

application name of integer id: 12
application name of fqstring id: externalapp/external_resource/id
application name of internal id: simpleapp
resource type of internal id: example_proto_message
resource id of internal id: 12
application name of fqstring id: externalapp
resource type of fqstring id: external_resource
resource id of fqstring id: id

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func ApplicationName

func ApplicationName() string

ApplicationName returns application name registered by RegisterApplication.

func Decode

func Decode(pb proto.Message, id *resourcepb.Identifier) (driver.Value, error)

Decode decodes identifier using a codec registered for pb if found.

If codec is not found - and id is nil, the (nil, nil) are returned. - and pb is nil the id is decoded in a fully qualified string value in format specified for Atlas References. - only Resource ID part of the identifier is returned as string value.

func DecodeBytes

func DecodeBytes(pb proto.Message, id *resourcepb.Identifier) ([]byte, error)

DecodeBytes decodes value returned by Decode as []byte. Returns an error if value is not of []byte type.

func DecodeInt64

func DecodeInt64(pb proto.Message, id *resourcepb.Identifier) (int64, error)

DecodeInt64 decodes value returned by Decode as int64. Returns an error if value is not of int64 type.

func Encode

func Encode(pb proto.Message, value driver.Value) (*resourcepb.Identifier, error)

Encode encodes identifier using a codec registered for pb.

If codec is not found - and value is not of string type an error is returned. - and pb is nil the id is encoded as it would be a string value in fully qualified format - and value is nil the (nil, nil) are returned

If Resource ID part is not empty, the Application Name and Resource Type parts are populated by ApplicationName and Name functions accordingly, otherwise the empty identifier is returned.

func Name

func Name(pb proto.Message) string

Name returns name of pb. If pb implements Namer interface it is used to return name, otherwise the the proto.MessageName is used to obtain fully qualified resource name.

The only last part of fully qualified resource name is used and converted to lower case. E.g. infoblox.rpc.Identifier -> identifier

If SetPlural is called the 's' symbol is added at the end of resource name.

func Plural added in v0.10.0

func Plural() bool

Plural returns true if resource.SetPlural was called, otherwise returns false.

func RegisterApplication

func RegisterApplication(name string)

RegisterApplication registers name of the application. Registered name is used by Encode to populate application name of Protocol Buffer Identifier.

func RegisterCodec

func RegisterCodec(codec Codec, pb proto.Message)

RegisterCodec registers codec for a given pb. If pb is nil the codec is registered as default. If codec is nil or registered twice for the same resource the panic is raised.

func ReturnEmpty added in v0.7.0

func ReturnEmpty() bool

ReturnEmpty returns flag that indicates all nil values of driver.Value type in codecs must be converted to empty instance of Identifier.

func SetPlural added in v0.10.0

func SetPlural()

SetPlural sets package flag that instructs resource.Name to return name in plural form by adding 's' at the end of the name.

func SetReturnEmpty added in v0.7.0

func SetReturnEmpty()

SetReturnEmpty sets package flag that indicates all nil values of driver.Value type in codecs must be converted to empty instance of Identifier. Default value is false.

Types

type Codec

type Codec interface {
	// Encode encodes value to Protocol Buffer representation
	Encode(driver.Value) (*resourcepb.Identifier, error)
	// Decode decodes Protocol Buffer representation to the driver.Value
	Decode(*resourcepb.Identifier) (driver.Value, error)
}

Codec defines the interface package uses to encode and decode Protocol Buffer Identifier to the driver.Value. Note that implementation must be thread safe.

type Namer added in v0.10.0

type Namer interface {
	// ResourceName returns the name of a resource.
	ResourceName() string
}

Namer is the interface that names resource.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL