mirror of
https://github.com/mainflux/mainflux.git
synced 2025-05-06 19:29:15 +08:00

* Return Auth service Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update Compose to run with SpiceDB and Auth svc Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update auth gRPC API Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Remove Users' policies Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Move Groups to internal Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Use shared groups in Users Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Remove unused code Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Use pkg Groups in Things Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Remove Things groups Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Make imports consistent Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update Groups networking Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Remove things groups-specific API Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Move Things Clients to the root Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Move Clients to Users root Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Temporarily remove tracing Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Fix imports Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Add buffer config for gRPC Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update auth type for Things Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Use Auth for login Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Add temporary solution for refresh token Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update Tokenizer interface Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Updade tokens issuing Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Fix token issuing Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update JWT validator and refactor Tokenizer Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Rename access timeout Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Rename login to authenticate Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update Identify to use SubjectID Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Add Auth to Groups Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Use the Auth service for Groups Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update auth schema Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Fix Auth for Groups Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Add auth for addons (#14) Signed-off-by: Arvindh <arvindh91@gmail.com> Speparate Login and Refresh tokens Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Merge authN and authZ requests for things Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Add connect and disconnect Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update sharing Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Fix policies addition and removal Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update relation with roels Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Add gRPC to Things Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Assign and Unassign members to group and Listing of Group members (#15) * add auth for addons Signed-off-by: Arvindh <arvindh91@gmail.com> * add assign and unassign to group Signed-off-by: Arvindh <arvindh91@gmail.com> * add group incomplete repo implementation Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Move coap mqtt and ws policies to spicedb (#16) Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Remove old policies Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> NOISSUE - Things authorize to return thingID (#18) This commit modifies the authorize endpoint to the grpc endpoint to return thingID. The authorize endpoint allows adapters to get the publisher of the message. Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Add Groups to users service (#17) * add assign and unassign to group Signed-off-by: Arvindh <arvindh91@gmail.com> * add group incomplete repo implementation Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users stable 1 Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users stable 2 Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users & things Signed-off-by: Arvindh <arvindh91@gmail.com> * Amend signature Signed-off-by: Arvindh <arvindh91@gmail.com> * fix merge error Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Fix es code (#21) Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Fix Bugs (#20) * fix bugs Signed-off-by: Arvindh <arvindh91@gmail.com> * fix bugs Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Test e2e (#19) * fix: connect method Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * fix: e2e Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * fix changes in sdk and e2e Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * feat(docker): remove unnecessary port mapping Remove the port mapping for MQTT broker in the docker-compose.yml file. Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * Enable group listing Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * feat(responses): update ChannelsPage struct The ChannelsPage struct in the responses.go file has been updated. The "Channels" field has been renamed to "Groups" to provide more accurate naming. This change ensures consistency and clarity in the codebase. Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * feat(things): add UpdateClientSecret method Add the UpdateClientSecret method to the things service. This method allows updating the client secret for a specific client identified by the provided token, id, and key parameters. Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> --------- Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Use smaller buffers for gRPC Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Clean up tests (#22) Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Add Connect Disconnect endpoints (#23) * fix bugs Signed-off-by: Arvindh <arvindh91@gmail.com> * fix bugs Signed-off-by: Arvindh <arvindh91@gmail.com> * fix list of things in a channel and Add connect disconnect endpoint Signed-off-by: Arvindh <arvindh91@gmail.com> * fix list of things in a channel and Add connect disconnect endpoint Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Add: Things share with users (#25) * fix list of things in a channel and Add connect disconnect endpoint Signed-off-by: Arvindh <arvindh91@gmail.com> * add: things share with other users Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Rename gRPC Services (#24) * Rename things and users auth service Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * docs: add authorization docs for gRPC services Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * Rename things and users grpc services Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * Remove mainflux.env package Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> --------- Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Add: Listing of things, channels, groups, users (#26) * add: listing of channels, users, groups, things Signed-off-by: Arvindh <arvindh91@gmail.com> * add: listing of channels, users, groups, things Signed-off-by: Arvindh <arvindh91@gmail.com> * add: listing of channels, users, groups, things Signed-off-by: Arvindh <arvindh91@gmail.com> * add: listing of channels, users, groups, things Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Clean Up Users (#27) * feat(groups): rename redis package to events - Renamed the `redis` package to `events` in the `internal/groups` directory. - Updated the file paths and names accordingly. - This change reflects the more accurate purpose of the package and improves code organization. Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * feat(auth): Modify identity method Change request and response of identity method Add accessToken and refreshToken to Token response Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * clean up users, remove dead code Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * feat(users): add unit tests for user service This commit adds unit tests for the user service in the `users` package. The tests cover various scenarios and ensure the correct behavior of the service. Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> --------- Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Add: List of user groups & removed repeating code in groups (#29) * removed repeating code in list groups Signed-off-by: Arvindh <arvindh91@gmail.com> * add: list of user group Signed-off-by: Arvindh <arvindh91@gmail.com> * fix: otel handler operator name for endpoints Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Clean Up Things Service (#28) * Rework things service Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * add tests Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> --------- Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Clean Up Auth Service (#30) * clean up auth service Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * feat(auth): remove unused import Remove the unused import of `emptypb` in `auth.pb.go`. This import is not being used in the codebase and can be safely removed. Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> --------- Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Update API docs (#31) Signed-off-by: rodneyosodo <blackd0t@protonmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Remove TODO comments and cleanup the code Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Update dependenices Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: rodneyosodo <blackd0t@protonmail.com> Co-authored-by: b1ackd0t <28790446+rodneyosodo@users.noreply.github.com> Co-authored-by: Arvindh <30824765+arvindh123@users.noreply.github.com>
585 lines
15 KiB
Go
585 lines
15 KiB
Go
// Package structs contains various utilities functions to work with structs.
|
|
package structs
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"reflect"
|
|
)
|
|
|
|
var (
|
|
// DefaultTagName is the default tag name for struct fields which provides
|
|
// a more granular to tweak certain structs. Lookup the necessary functions
|
|
// for more info.
|
|
DefaultTagName = "structs" // struct's field default tag name
|
|
)
|
|
|
|
// Struct encapsulates a struct type to provide several high level functions
|
|
// around the struct.
|
|
type Struct struct {
|
|
raw interface{}
|
|
value reflect.Value
|
|
TagName string
|
|
}
|
|
|
|
// New returns a new *Struct with the struct s. It panics if the s's kind is
|
|
// not struct.
|
|
func New(s interface{}) *Struct {
|
|
return &Struct{
|
|
raw: s,
|
|
value: strctVal(s),
|
|
TagName: DefaultTagName,
|
|
}
|
|
}
|
|
|
|
// Map converts the given struct to a map[string]interface{}, where the keys
|
|
// of the map are the field names and the values of the map the associated
|
|
// values of the fields. The default key string is the struct field name but
|
|
// can be changed in the struct field's tag value. The "structs" key in the
|
|
// struct's field tag value is the key name. Example:
|
|
//
|
|
// // Field appears in map as key "myName".
|
|
// Name string `structs:"myName"`
|
|
//
|
|
// A tag value with the content of "-" ignores that particular field. Example:
|
|
//
|
|
// // Field is ignored by this package.
|
|
// Field bool `structs:"-"`
|
|
//
|
|
// A tag value with the content of "string" uses the stringer to get the value. Example:
|
|
//
|
|
// // The value will be output of Animal's String() func.
|
|
// // Map will panic if Animal does not implement String().
|
|
// Field *Animal `structs:"field,string"`
|
|
//
|
|
// A tag value with the option of "flatten" used in a struct field is to flatten its fields
|
|
// in the output map. Example:
|
|
//
|
|
// // The FieldStruct's fields will be flattened into the output map.
|
|
// FieldStruct time.Time `structs:",flatten"`
|
|
//
|
|
// A tag value with the option of "omitnested" stops iterating further if the type
|
|
// is a struct. Example:
|
|
//
|
|
// // Field is not processed further by this package.
|
|
// Field time.Time `structs:"myName,omitnested"`
|
|
// Field *http.Request `structs:",omitnested"`
|
|
//
|
|
// A tag value with the option of "omitempty" ignores that particular field if
|
|
// the field value is empty. Example:
|
|
//
|
|
// // Field appears in map as key "myName", but the field is
|
|
// // skipped if empty.
|
|
// Field string `structs:"myName,omitempty"`
|
|
//
|
|
// // Field appears in map as key "Field" (the default), but
|
|
// // the field is skipped if empty.
|
|
// Field string `structs:",omitempty"`
|
|
//
|
|
// Note that only exported fields of a struct can be accessed, non exported
|
|
// fields will be neglected.
|
|
func (s *Struct) Map() map[string]interface{} {
|
|
out := make(map[string]interface{})
|
|
s.FillMap(out)
|
|
return out
|
|
}
|
|
|
|
// FillMap is the same as Map. Instead of returning the output, it fills the
|
|
// given map.
|
|
func (s *Struct) FillMap(out map[string]interface{}) {
|
|
if out == nil {
|
|
return
|
|
}
|
|
|
|
fields := s.structFields()
|
|
|
|
for _, field := range fields {
|
|
name := field.Name
|
|
val := s.value.FieldByName(name)
|
|
isSubStruct := false
|
|
var finalVal interface{}
|
|
|
|
tagName, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
|
if tagName != "" {
|
|
name = tagName
|
|
}
|
|
|
|
// if the value is a zero value and the field is marked as omitempty do
|
|
// not include
|
|
if tagOpts.Has("omitempty") {
|
|
zero := reflect.Zero(val.Type()).Interface()
|
|
current := val.Interface()
|
|
|
|
if reflect.DeepEqual(current, zero) {
|
|
continue
|
|
}
|
|
}
|
|
|
|
if !tagOpts.Has("omitnested") {
|
|
finalVal = s.nested(val)
|
|
|
|
v := reflect.ValueOf(val.Interface())
|
|
if v.Kind() == reflect.Ptr {
|
|
v = v.Elem()
|
|
}
|
|
|
|
switch v.Kind() {
|
|
case reflect.Map, reflect.Struct:
|
|
isSubStruct = true
|
|
}
|
|
} else {
|
|
finalVal = val.Interface()
|
|
}
|
|
|
|
if tagOpts.Has("string") {
|
|
s, ok := val.Interface().(fmt.Stringer)
|
|
if ok {
|
|
out[name] = s.String()
|
|
}
|
|
continue
|
|
}
|
|
|
|
if isSubStruct && (tagOpts.Has("flatten")) {
|
|
for k := range finalVal.(map[string]interface{}) {
|
|
out[k] = finalVal.(map[string]interface{})[k]
|
|
}
|
|
} else {
|
|
out[name] = finalVal
|
|
}
|
|
}
|
|
}
|
|
|
|
// Values converts the given s struct's field values to a []interface{}. A
|
|
// struct tag with the content of "-" ignores the that particular field.
|
|
// Example:
|
|
//
|
|
// // Field is ignored by this package.
|
|
// Field int `structs:"-"`
|
|
//
|
|
// A value with the option of "omitnested" stops iterating further if the type
|
|
// is a struct. Example:
|
|
//
|
|
// // Fields is not processed further by this package.
|
|
// Field time.Time `structs:",omitnested"`
|
|
// Field *http.Request `structs:",omitnested"`
|
|
//
|
|
// A tag value with the option of "omitempty" ignores that particular field and
|
|
// is not added to the values if the field value is empty. Example:
|
|
//
|
|
// // Field is skipped if empty
|
|
// Field string `structs:",omitempty"`
|
|
//
|
|
// Note that only exported fields of a struct can be accessed, non exported
|
|
// fields will be neglected.
|
|
func (s *Struct) Values() []interface{} {
|
|
fields := s.structFields()
|
|
|
|
var t []interface{}
|
|
|
|
for _, field := range fields {
|
|
val := s.value.FieldByName(field.Name)
|
|
|
|
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
|
|
|
// if the value is a zero value and the field is marked as omitempty do
|
|
// not include
|
|
if tagOpts.Has("omitempty") {
|
|
zero := reflect.Zero(val.Type()).Interface()
|
|
current := val.Interface()
|
|
|
|
if reflect.DeepEqual(current, zero) {
|
|
continue
|
|
}
|
|
}
|
|
|
|
if tagOpts.Has("string") {
|
|
s, ok := val.Interface().(fmt.Stringer)
|
|
if ok {
|
|
t = append(t, s.String())
|
|
}
|
|
continue
|
|
}
|
|
|
|
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
|
// look out for embedded structs, and convert them to a
|
|
// []interface{} to be added to the final values slice
|
|
t = append(t, Values(val.Interface())...)
|
|
} else {
|
|
t = append(t, val.Interface())
|
|
}
|
|
}
|
|
|
|
return t
|
|
}
|
|
|
|
// Fields returns a slice of Fields. A struct tag with the content of "-"
|
|
// ignores the checking of that particular field. Example:
|
|
//
|
|
// // Field is ignored by this package.
|
|
// Field bool `structs:"-"`
|
|
//
|
|
// It panics if s's kind is not struct.
|
|
func (s *Struct) Fields() []*Field {
|
|
return getFields(s.value, s.TagName)
|
|
}
|
|
|
|
// Names returns a slice of field names. A struct tag with the content of "-"
|
|
// ignores the checking of that particular field. Example:
|
|
//
|
|
// // Field is ignored by this package.
|
|
// Field bool `structs:"-"`
|
|
//
|
|
// It panics if s's kind is not struct.
|
|
func (s *Struct) Names() []string {
|
|
fields := getFields(s.value, s.TagName)
|
|
|
|
names := make([]string, len(fields))
|
|
|
|
for i, field := range fields {
|
|
names[i] = field.Name()
|
|
}
|
|
|
|
return names
|
|
}
|
|
|
|
func getFields(v reflect.Value, tagName string) []*Field {
|
|
if v.Kind() == reflect.Ptr {
|
|
v = v.Elem()
|
|
}
|
|
|
|
t := v.Type()
|
|
|
|
var fields []*Field
|
|
|
|
for i := 0; i < t.NumField(); i++ {
|
|
field := t.Field(i)
|
|
|
|
if tag := field.Tag.Get(tagName); tag == "-" {
|
|
continue
|
|
}
|
|
|
|
f := &Field{
|
|
field: field,
|
|
value: v.FieldByName(field.Name),
|
|
}
|
|
|
|
fields = append(fields, f)
|
|
|
|
}
|
|
|
|
return fields
|
|
}
|
|
|
|
// Field returns a new Field struct that provides several high level functions
|
|
// around a single struct field entity. It panics if the field is not found.
|
|
func (s *Struct) Field(name string) *Field {
|
|
f, ok := s.FieldOk(name)
|
|
if !ok {
|
|
panic("field not found")
|
|
}
|
|
|
|
return f
|
|
}
|
|
|
|
// FieldOk returns a new Field struct that provides several high level functions
|
|
// around a single struct field entity. The boolean returns true if the field
|
|
// was found.
|
|
func (s *Struct) FieldOk(name string) (*Field, bool) {
|
|
t := s.value.Type()
|
|
|
|
field, ok := t.FieldByName(name)
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
|
|
return &Field{
|
|
field: field,
|
|
value: s.value.FieldByName(name),
|
|
defaultTag: s.TagName,
|
|
}, true
|
|
}
|
|
|
|
// IsZero returns true if all fields in a struct is a zero value (not
|
|
// initialized) A struct tag with the content of "-" ignores the checking of
|
|
// that particular field. Example:
|
|
//
|
|
// // Field is ignored by this package.
|
|
// Field bool `structs:"-"`
|
|
//
|
|
// A value with the option of "omitnested" stops iterating further if the type
|
|
// is a struct. Example:
|
|
//
|
|
// // Field is not processed further by this package.
|
|
// Field time.Time `structs:"myName,omitnested"`
|
|
// Field *http.Request `structs:",omitnested"`
|
|
//
|
|
// Note that only exported fields of a struct can be accessed, non exported
|
|
// fields will be neglected. It panics if s's kind is not struct.
|
|
func (s *Struct) IsZero() bool {
|
|
fields := s.structFields()
|
|
|
|
for _, field := range fields {
|
|
val := s.value.FieldByName(field.Name)
|
|
|
|
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
|
|
|
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
|
ok := IsZero(val.Interface())
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
// zero value of the given field, such as "" for string, 0 for int
|
|
zero := reflect.Zero(val.Type()).Interface()
|
|
|
|
// current value of the given field
|
|
current := val.Interface()
|
|
|
|
if !reflect.DeepEqual(current, zero) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// HasZero returns true if a field in a struct is not initialized (zero value).
|
|
// A struct tag with the content of "-" ignores the checking of that particular
|
|
// field. Example:
|
|
//
|
|
// // Field is ignored by this package.
|
|
// Field bool `structs:"-"`
|
|
//
|
|
// A value with the option of "omitnested" stops iterating further if the type
|
|
// is a struct. Example:
|
|
//
|
|
// // Field is not processed further by this package.
|
|
// Field time.Time `structs:"myName,omitnested"`
|
|
// Field *http.Request `structs:",omitnested"`
|
|
//
|
|
// Note that only exported fields of a struct can be accessed, non exported
|
|
// fields will be neglected. It panics if s's kind is not struct.
|
|
func (s *Struct) HasZero() bool {
|
|
fields := s.structFields()
|
|
|
|
for _, field := range fields {
|
|
val := s.value.FieldByName(field.Name)
|
|
|
|
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
|
|
|
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
|
ok := HasZero(val.Interface())
|
|
if ok {
|
|
return true
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
// zero value of the given field, such as "" for string, 0 for int
|
|
zero := reflect.Zero(val.Type()).Interface()
|
|
|
|
// current value of the given field
|
|
current := val.Interface()
|
|
|
|
if reflect.DeepEqual(current, zero) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// Name returns the structs's type name within its package. For more info refer
|
|
// to Name() function.
|
|
func (s *Struct) Name() string {
|
|
return s.value.Type().Name()
|
|
}
|
|
|
|
// structFields returns the exported struct fields for a given s struct. This
|
|
// is a convenient helper method to avoid duplicate code in some of the
|
|
// functions.
|
|
func (s *Struct) structFields() []reflect.StructField {
|
|
t := s.value.Type()
|
|
|
|
var f []reflect.StructField
|
|
|
|
for i := 0; i < t.NumField(); i++ {
|
|
field := t.Field(i)
|
|
// we can't access the value of unexported fields
|
|
if field.PkgPath != "" {
|
|
continue
|
|
}
|
|
|
|
// don't check if it's omitted
|
|
if tag := field.Tag.Get(s.TagName); tag == "-" {
|
|
continue
|
|
}
|
|
|
|
f = append(f, field)
|
|
}
|
|
|
|
return f
|
|
}
|
|
|
|
func strctVal(s interface{}) reflect.Value {
|
|
v := reflect.ValueOf(s)
|
|
|
|
// if pointer get the underlying element≤
|
|
for v.Kind() == reflect.Ptr {
|
|
v = v.Elem()
|
|
}
|
|
|
|
if v.Kind() != reflect.Struct {
|
|
panic("not struct")
|
|
}
|
|
|
|
return v
|
|
}
|
|
|
|
// Map converts the given struct to a map[string]interface{}. For more info
|
|
// refer to Struct types Map() method. It panics if s's kind is not struct.
|
|
func Map(s interface{}) map[string]interface{} {
|
|
return New(s).Map()
|
|
}
|
|
|
|
// FillMap is the same as Map. Instead of returning the output, it fills the
|
|
// given map.
|
|
func FillMap(s interface{}, out map[string]interface{}) {
|
|
New(s).FillMap(out)
|
|
}
|
|
|
|
// Values converts the given struct to a []interface{}. For more info refer to
|
|
// Struct types Values() method. It panics if s's kind is not struct.
|
|
func Values(s interface{}) []interface{} {
|
|
return New(s).Values()
|
|
}
|
|
|
|
// Fields returns a slice of *Field. For more info refer to Struct types
|
|
// Fields() method. It panics if s's kind is not struct.
|
|
func Fields(s interface{}) []*Field {
|
|
return New(s).Fields()
|
|
}
|
|
|
|
// Names returns a slice of field names. For more info refer to Struct types
|
|
// Names() method. It panics if s's kind is not struct.
|
|
func Names(s interface{}) []string {
|
|
return New(s).Names()
|
|
}
|
|
|
|
// IsZero returns true if all fields is equal to a zero value. For more info
|
|
// refer to Struct types IsZero() method. It panics if s's kind is not struct.
|
|
func IsZero(s interface{}) bool {
|
|
return New(s).IsZero()
|
|
}
|
|
|
|
// HasZero returns true if any field is equal to a zero value. For more info
|
|
// refer to Struct types HasZero() method. It panics if s's kind is not struct.
|
|
func HasZero(s interface{}) bool {
|
|
return New(s).HasZero()
|
|
}
|
|
|
|
// IsStruct returns true if the given variable is a struct or a pointer to
|
|
// struct.
|
|
func IsStruct(s interface{}) bool {
|
|
v := reflect.ValueOf(s)
|
|
if v.Kind() == reflect.Ptr {
|
|
v = v.Elem()
|
|
}
|
|
|
|
// uninitialized zero value of a struct
|
|
if v.Kind() == reflect.Invalid {
|
|
return false
|
|
}
|
|
|
|
return v.Kind() == reflect.Struct
|
|
}
|
|
|
|
// Name returns the structs's type name within its package. It returns an
|
|
// empty string for unnamed types. It panics if s's kind is not struct.
|
|
func Name(s interface{}) string {
|
|
return New(s).Name()
|
|
}
|
|
|
|
// nested retrieves recursively all types for the given value and returns the
|
|
// nested value.
|
|
func (s *Struct) nested(val reflect.Value) interface{} {
|
|
var finalVal interface{}
|
|
|
|
v := reflect.ValueOf(val.Interface())
|
|
if v.Kind() == reflect.Ptr {
|
|
v = v.Elem()
|
|
}
|
|
|
|
switch v.Kind() {
|
|
case reflect.Struct:
|
|
n := New(val.Interface())
|
|
n.TagName = s.TagName
|
|
m := n.Map()
|
|
|
|
// do not add the converted value if there are no exported fields, ie:
|
|
// time.Time
|
|
if len(m) == 0 {
|
|
finalVal = val.Interface()
|
|
} else {
|
|
finalVal = m
|
|
}
|
|
case reflect.Map:
|
|
// get the element type of the map
|
|
mapElem := val.Type()
|
|
switch val.Type().Kind() {
|
|
case reflect.Ptr, reflect.Array, reflect.Map,
|
|
reflect.Slice, reflect.Chan:
|
|
mapElem = val.Type().Elem()
|
|
if mapElem.Kind() == reflect.Ptr {
|
|
mapElem = mapElem.Elem()
|
|
}
|
|
}
|
|
|
|
// only iterate over struct types, ie: map[string]StructType,
|
|
// map[string][]StructType,
|
|
if mapElem.Kind() == reflect.Struct ||
|
|
(mapElem.Kind() == reflect.Slice &&
|
|
mapElem.Elem().Kind() == reflect.Struct) {
|
|
m := make(map[string]interface{}, val.Len())
|
|
for _, k := range val.MapKeys() {
|
|
m[k.String()] = s.nested(val.MapIndex(k))
|
|
}
|
|
finalVal = m
|
|
break
|
|
}
|
|
|
|
// TODO(arslan): should this be optional?
|
|
finalVal = val.Interface()
|
|
case reflect.Slice, reflect.Array:
|
|
if val.Type().Kind() == reflect.Interface {
|
|
finalVal = val.Interface()
|
|
break
|
|
}
|
|
|
|
// TODO(arslan): should this be optional?
|
|
// do not iterate of non struct types, just pass the value. Ie: []int,
|
|
// []string, co... We only iterate further if it's a struct.
|
|
// i.e []foo or []*foo
|
|
if val.Type().Elem().Kind() != reflect.Struct &&
|
|
!(val.Type().Elem().Kind() == reflect.Ptr &&
|
|
val.Type().Elem().Elem().Kind() == reflect.Struct) {
|
|
finalVal = val.Interface()
|
|
break
|
|
}
|
|
|
|
slices := make([]interface{}, val.Len())
|
|
for x := 0; x < val.Len(); x++ {
|
|
slices[x] = s.nested(val.Index(x))
|
|
}
|
|
finalVal = slices
|
|
default:
|
|
finalVal = val.Interface()
|
|
}
|
|
|
|
return finalVal
|
|
}
|