user6743038
user6743038

Reputation: 169

Override struct tags in go

I have an entity in my project that is viewable by public and by admin. Not all fields should be accessible by public.

For example

type Foo struct {
    Id        bson.ObjectId `json:"id" bson:"_id"`
    DateAdded time.Time     `json:"date_added" bson:"date_added"`
    Bar       string `json:"bar" bson:"bar"`      
    AdminOnly string        `json:"admin_only" bson:"admin_only"`
}

AdminOnly field should be only visible to admins. For now, when requests comes from public, I call separate method that copies every needed field to new struct

type FooPublic struct {
    Id        bson.ObjectId `json:"id" bson:"_id"`
    DateAdded time.Time     `json:"date_added" bson:"date_added"`
    Bar       string `json:"bar" bson:"bar"`
}

func (f *Foo) Public() (res FooPublic) {
    res = FooPublic{
        Id: f.Id,
        DateAdded: f.DateAdded,
        Bar:f.Bar,
    }
    return
}

But if I need to add new field to my entity, I need to add it in 3 places. In struct itself, in PublicFoo and in Public method.
This seems to be agains DRY principle. What is correct, idiomatic solution here? Can I define FooPublic so it overrides tags of needed fields? Or probably there is at least good way to copy corresponding fields from one struct to another, so I don't need to do this manually in Public method?

Upvotes: 4

Views: 2132

Answers (1)

icza
icza

Reputation: 417412

In general this repetition can be avoided by using embedding. Your Foo type should embed FooPublic:

type FooPublic struct {
    Id        bson.ObjectId `json:"id" bson:"_id"`
    DateAdded time.Time     `json:"date_added" bson:"date_added"`
    Bar       string        `json:"bar" bson:"bar"`      
}

type Foo struct {
    FooPublic
    AdminOnly string `json:"admin_only" bson:"admin_only"`
}

func (f *Foo) Public() FooPublic {
    return f.FooPublic
}

But if someone is able to call the Foo.Public(), that someone already has the Foo or *Foo value (and so can access the exported AdminOnly field), so what's the point?

A better solution would be to use an interface, and not expose the Foo struct.

Upvotes: 4

Related Questions