I want to define a new type in GORM like this:
type Device struct{
gorm.Model
Name string
Status Status
}
Where the values of Status can only be either one of ok, broken, or missing. Obviously, I can use the type string for this, but then I would allow every string possible. How can I restrict the options to the ones I want?
the simplest thing would be something like this:
const (
statusOk = "ok"
statusBroken = "broken"
statusMissing = "missing"
)
func isValidStatus(s string) error {
switch s {
case statusOk, statusBroken, statusMissing:
return nil
}
return errors.New("Invalid status")
}
var d Device
// your code
// your code
// your code
if err := isValidStatus(d.Status); err != nil {
// handle error
}
// valid status
Related
I'm relatively new to Go and am working on building out a request decoder. The request comes in JSON format and we decode that to a map[string]interface{}. We then pass that object data in to be decoded to our own ProcessRequest struct. As I said I'm new so I am reusing some logic in similar parts of the code wrote by previous developers. Essentially we are checking the map for the necessary pieces and then setting and returning those. Can someone explain to me why I am getting the titled error? Would I have to set the items all the way down to base structs that no longer have any nested? Is there a better way to accomplish what I want? Here is the code and the related structs. It is highlighting the error on the return of the model.ProcessRequest. TYIA
type ProcessRequest struct {
RequestID string
Message *message.Message
Rule *Rule
Options *ProcessOptions
//TODO: Context EvaluatorContext
//TODO: Links
}
type Message struct {
ID int
Key string
Source string
Headers *HeaderSet
Categories *CategorySet
Properties *PropertySet
Streams *StreamSet
}
type RuleAction struct {
Name string
Expression map[string]interface{}
}
type RuleLink struct {
LinkID int
Condition map[string]interface{}
TargetRuleID int
}
type Rule struct {
Name string
Code string
Actions []RuleAction
Links []RuleLink
}
type object = map[string]interface{}
func DecodeProcessRequest(dataObject map[string]interface{}) (*model.ProcessRequest, error) {
var (
requestID string
message *message.Message
rule *model.Rule
options *model.ProcessOptions
err error
)
if reqIDSrc, ok := dataObject["requestId"]; ok {
if requestID, err = converter.ToString(reqIDSrc); err != nil {
return nil, errors.Wrapf(err, "Error reading property 'requestID'")
}
if requestID == "" {
return nil, errors.Errorf("Property 'requestID' is an empty string")
}
} else {
return nil, errors.Errorf("Missing required property 'requestID'")
}
if messageSrc, ok := dataObject["message"]; ok {
messageData, ok := messageSrc.(object)
if !ok {
return nil, errors.Errorf("Error reading property 'message': Value is not an object")
}
if message, err = DecodeMessage(messageData); err != nil {
return nil, errors.Wrapf(err, "Error reading property 'message'")
}
} else {
return nil, errors.Errorf("Missing required property 'message'")
}
if ruleSrc, ok := dataObject["rule"]; ok {
ruleObj, ok := ruleSrc.(object)
if !ok {
return nil, errors.Errorf("Error reading property 'rule': Value is not an object")
}
if rule, err = DecodeRule(ruleObj); err != nil {
return nil, errors.Wrapf(err, "Error reading 'rule' during decoding")
}
} else {
return nil, errors.Errorf("Missing required property 'requestID'")
}
// Parse plain object to a Message struct
return &model.ProcessRequest{
requestID,
message,
rule,
options,
}, nil
}
super said in this comment:
In general, the warning says that you should prefer to use the syntax ProcessRequest{ RequestID: requestID, ... }. Naming the keys instead of unkeyed values.
That worked for me. Also the explanation by kostix in this comment really helped.
Basically the idea is that if you use "unkeyed" way of defining struct literals, the meaning of your definitions depends on the way the fields of the underlying type are layed out. Now consider that your type has three fields of type string in a certain order. Now a couple of iterations down the road some programmer moves the second field to the 1st position—your literal will still compile but will end up defining a completely different value at runtime.
func (t *ballot) initBallot(stub shim.ChaincodeStubInterface, args []string) peer.Response {
if len(args) != 2 {
return shim.Error("Incorrect number of arguments. Expecting 2")
}
// ==== Input sanitation ====
fmt.Println("- start init ballot")
if len(args[0]) == 0 {
return shim.Error("1st argument must be a non-empty string")
}
if len(args[1]) == 0 {
return shim.Error("2nd argument must be a non-empty string")
}
personFirstName := args[0]
personLastName := args[1]
hash := sha256.New()
hash.Write([]byte(personFirstName + personLastName)) // ballotID is created based on the person's name
ballotID := hex.EncodeToString(hash.Sum(nil))
voteInit := "VOTE INIT"
// ==== Create ballot object and marshal to JSON ====
Ballot := ballot{personFirstName, personLastName, ballotID, voteInit}
ballotJSONByte, err := json.Marshal(Ballot)
if err != nil {
return shim.Error(err.Error())
}
err = stub.PutState(string(ballotID), ballotJSONByte)
//FIXME:0-------------------------------------------------
ballotAsByte, err := stub.GetState(string(ballotID))
if err != nil {
return shim.Error(err.Error())
}
BBBallot := ballot{}
//umarshal the data to a new ballot struct
json.Unmarshal(ballotAsByte, &BBBallot)
//
fmt.Println(BBBallot)
fmt.Println(BBBallot.personFirstName)
return shim.Success([]byte(ballotID))
}
Above is the code and this is the test script i am running it against
func Test_Invoke_initBallot(t *testing.T) {
scc := new(ballot)
stub := shim.NewMockStub("voting", scc)
res := stub.MockInvoke("1", [][]byte{[]byte("initBallot"), []byte("John"), []byte("C")})
if res.Status != shim.OK {
t.Log("bad status received, expected: 200; received:" + strconv.FormatInt(int64(res.Status), 10))
t.Log("response: " + string(res.Message))
t.FailNow()
}
if res.Payload == nil {
t.Log("initBallot failed to create a ballot")
t.FailNow()
}
}
I am trying to read from the ledger after putting the transaction in. However, I have been getting empty responses from both of the Println statements.
// PutState puts the specified `key` and `value` into the transaction's
// writeset as a data-write proposal. PutState doesn't effect the ledger
// until the transaction is validated and successfully committed.
// Simple keys must not be an empty string and must not start with null
// character (0x00), in order to avoid range query collisions with
// composite keys, which internally get prefixed with 0x00 as composite
// key namespace.
PutState(key string, value []byte) error
It does say on the documentation that putState does not commit transactions to the ledger until its validated, but I am just trying to test my chaincode using the MockStub without setting up the fabric network. What is the fix to this problem?
P.S the problem has been solved, here is the right way to set up a struct
type ballot struct {
PersonFirstName string
PersonLastName string
BallotID string
VoteInit string
}
You haven't provided the code for the ballot struct yet. But from what you provided, I have a guess what might be going on. I think you probably haven't exported the fields and your struct looks like this:
type ballot struct {
personFirstName string
personLastName string
ballotID string
voteInit string
}
But when you tried to convert this object to JSON using json.Marshal(Ballot), none of the fields are added to the JSON object because they were not exported. All that you have to do in this case is exporting the necessary fields (using Uppercase letter at the beginning of field names). Your updated struct should look something like the following:
type ballot struct {
PersonFirstName string
PersonLastName string
BallotID string
VoteInit string
}
This is a very common mistake many newcomers make. Wish you all the best in your journey forward!!!
P.S. Please edit your question and add the code of you ballot struct here even if this solution solves your problem as that might help others in the future. Also, please add proper indentation to the code and add the last } symbol in the code block.
I'm having a bit of trouble figuring out how to either create a struct in a switch statement or assign a type to it in a switch statement.
Here's some non-working code illustrating what I'm trying to do:
var result
switch structPickingString {
case "struct1":
result = new(struct1)
case "struct2":
result = new(struct2)
}
//unmarshall some json into the appropriate struct type
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
log.Println(err)
}
//print out that json with a function specific to that type of struct
result.Print()
I think something involving an empty interface{} might be related to solving this issue but unfortunately I'm still a bit ignorant with golang and I'm not seeing how to make it work.
Here's a link to a slightly modified version of the code for more context: https://play.golang.org/p/Rb1oaMuvmU2
The issue is not defining the print function, it's assigning a specific type of struct to the result variable based on using the individual Print function that the struct implements.
Let me know if there's any more info I could provide.
Since you are calling .Print, you need to use an interface which has that method. I think you are looking for something like
type Printer interface {
Print()
}
func (s *struct1) Print() {
// ...
}
func (s *struct2) Print() {
// ...
}
var result Printer
switch structPickingString {
case "struct1":
result = new(struct1)
case "struct2":
result = new(struct2)
}
https://play.golang.org/p/W9r6UfeQSCz
You needs match "generical interface" with structs. See this:
//my struct1
type MyStruct1 struct {
ID int
}
//my struct2
type MyStruct2 struct {
ID int
}
//my interface
type MyStructGenerical interface {
Print()
}
//method (match with MyStructGenerical)
func (m1 MyStruct1) Print() {
println(m1.ID)
}
//method (match with MyStructGenerical)
func (m2 MyStruct2) Print() {
println(m2.ID)
}
In this way you can does assertions between structs and generical interface. See this:
//here result is an generical interface
var result MyStructGenerical = nil
//checkin
switch structPickingString {
case "struct1":
tmp := new(MyStruct1)
result = tmp
case "struct2":
tmp := new(MyStruct2)
result = tmp
}
The result is:
//unmarshall some json into the appropriate struct type
if err := json.NewDecoder(body()).Decode(&result); err != nil {
log.Println(err)
}
//print out that json with a function specific to that type of struct
tmp := result.(*MyStruct1)
tmp.Print()
// or
result.Print()
Run in: https://play.golang.org/p/nHrJnImsqNN
I think you want to use an interface like
type ResultIface interface {
Print()
}
Then you can do
var result ResultIface
switch structPickingString {
case "struct1":
result = new(struct1)
case "struct2":
result = new(struct2)
}
As long as your struct1 and struct2 fulfills the ResultIface by having a Print function it will print
I've got a struct like below:
type Page struct {
title string
url string
}
and a map of structs:
var mostViewed = make(map[int]Page)
With go-cache, I store the map with a TTL time.
c.Set("data", mostViewed, 60*time.Minute)
But, once I recover "data" key, how could I assing it back to a map?
a, _ := c.Get("data")
fmt.Printf("%+v\n", a)
out: map[17:{title:xxx, url:yyy}]
I tried something like:
z := map[int]Page{a}
Any clue? It's like "remapping" a mapped string.
You get an interface{} type back, but you know what it is, so you need to use a type assertion to cast it back to a map[int]Page. Here is a quick external resource. https://newfivefour.com/golang-interface-type-assertions-switch.html
Here is an example
https://play.golang.org/p/lGseg88K1m
type Page struct {
title string
url string
}
func main() {
m := make(map[int]Page)
m[1] = Page{"hi", "there"}
iface := makeIface(m)
// use type assertion to cast it back to a map
if mapAgain, ok := iface.(map[int]Page); ok {
// it really is a map[int]Page
fmt.Printf("%+v\n", mapAgain)
} else {
// its not actually a map[int]Page
fmt.Println("oops")
}
// alternatively use a type-switch if it could be multiple types
switch v := iface.(type) {
case map[int]Page:
//yay
fmt.Printf("%+v\n", v)
case string:
// its a string
fmt.Println(v)
default:
// something we didn't account for
}
}
func makeIface(m map[int]Page) interface{} {
return m
}
Edit: as a side note, you probably want to make your map type map[int]*Page because if you did something like this:
page := m[1]
page.url = "different"
fmt.Println(page) // prints url="different"
fmt.Println(m[1].url) // remains unchanged
Because page would be a copy of what is in the map, not the Page in the map itself.
I suspect I am trying to shoehorn go into behaving in an OOP way, but I don't know the go idiom to do what I want.
I have a Message struct that I use to pass data around in a client-server application:
type Message struct {
ID string `json:"id,omitempty"`
Type string `json:"type"`
Data interface{} `json:"data"`
}
the Data here can be different things, for example a number of Commands:
type Command struct {
User *types.UserInfo `json:"user"`
}
type CommandA struct {
Command
A *AData `json:"a_data"`
}
type CommandB struct {
CommandB
B *BData `json:"b_data"`
}
What I want to do is to check that the message data type is a Command and perform actions that are common to all commands, for example authorisation, all in one place and not having to type assert what type of command, calling the appropriate handler function and then do the auth, as this would result in massive code duplication.
The code below reflects what I would like to happen.
for {
select {
case m := <-in:
// what I would like to do, obviously not working as
// m.Data is not of type Command but the actual command type
if c, ok := m.Data.(msg.Command); ok {
// do auth and other common stuff
}
switch t := m.Data.(type) {
case *msg.CommandA:
go srv.handleCommandA(m.ID, t)
case *msg.CommandB:
go srv.handleCommandB(m.ID, t)
// etc etc
default:
// do something
}
}
}
How do I solve this go idiomatically?
You can define common command stuff in interface
type Commander interface{
DoCommonStuff()
}
implement it for Command struct
func (c Command) DoCommonStuff(){
//do stuff
}
and then assert
if c, ok := m.Data.(Commander); ok {
c.DoCommonStuff()
}
your other code should work unchanged
One approach is using reflection to extract common field value from the Data. In your example, since all Command has User field, we can use it to identify whether Message.Data is a command or not. If Command is not embedded to the data, simply return nil. Example code:
func GetUserInfo(v interface{}) *types.UserInfo {
vt := reflect.ValueOf(v)
if vt.Kind() == reflect.Ptr {
vt = vt.Elem()
}
if vt.Kind() != reflect.Struct {
return nil
}
u := vt.FieldByName("User")
if !u.IsValid() {
return nil
}
user, ok := u.Interface().(*types.UserInfo)
if !ok {
return nil
}
return user
}
//Call GetUserInfo then perform common operation
user := GetUserInfo(m.Data)
if user != nil {
//Do auth and other common stuff
}