diff --git a/tavern/ent/gql_mutation_input.go b/tavern/ent/gql_mutation_input.go index 48647c759..14844fa99 100644 --- a/tavern/ent/gql_mutation_input.go +++ b/tavern/ent/gql_mutation_input.go @@ -28,6 +28,7 @@ func (c *JobCreate) SetInput(i CreateJobInput) *JobCreate { // UpdateSessionInput represents a mutation input for updating sessions. type UpdateSessionInput struct { + Name *string ClearHostname bool Hostname *string AddTagIDs []int @@ -36,6 +37,9 @@ type UpdateSessionInput struct { // Mutate applies the UpdateSessionInput on the SessionMutation builder. func (i *UpdateSessionInput) Mutate(m *SessionMutation) { + if v := i.Name; v != nil { + m.SetName(*v) + } if i.ClearHostname { m.ClearHostname() } diff --git a/tavern/ent/gql_node.go b/tavern/ent/gql_node.go index 074cfd9dc..5e73bdbb8 100644 --- a/tavern/ent/gql_node.go +++ b/tavern/ent/gql_node.go @@ -180,14 +180,22 @@ func (s *Session) Node(ctx context.Context) (node *Node, err error) { node = &Node{ ID: s.ID, Type: "Session", - Fields: make([]*Field, 6), + Fields: make([]*Field, 7), Edges: make([]*Edge, 2), } var buf []byte - if buf, err = json.Marshal(s.Principal); err != nil { + if buf, err = json.Marshal(s.Name); err != nil { return nil, err } node.Fields[0] = &Field{ + Type: "string", + Name: "name", + Value: string(buf), + } + if buf, err = json.Marshal(s.Principal); err != nil { + return nil, err + } + node.Fields[1] = &Field{ Type: "string", Name: "principal", Value: string(buf), @@ -195,7 +203,7 @@ func (s *Session) Node(ctx context.Context) (node *Node, err error) { if buf, err = json.Marshal(s.Hostname); err != nil { return nil, err } - node.Fields[1] = &Field{ + node.Fields[2] = &Field{ Type: "string", Name: "hostname", Value: string(buf), @@ -203,7 +211,7 @@ func (s *Session) Node(ctx context.Context) (node *Node, err error) { if buf, err = json.Marshal(s.Identifier); err != nil { return nil, err } - node.Fields[2] = &Field{ + node.Fields[3] = &Field{ Type: "string", Name: "identifier", Value: string(buf), @@ -211,7 +219,7 @@ func (s *Session) Node(ctx context.Context) (node *Node, err error) { if buf, err = json.Marshal(s.AgentIdentifier); err != nil { return nil, err } - node.Fields[3] = &Field{ + node.Fields[4] = &Field{ Type: "string", Name: "agentIdentifier", Value: string(buf), @@ -219,7 +227,7 @@ func (s *Session) Node(ctx context.Context) (node *Node, err error) { if buf, err = json.Marshal(s.HostIdentifier); err != nil { return nil, err } - node.Fields[4] = &Field{ + node.Fields[5] = &Field{ Type: "string", Name: "hostIdentifier", Value: string(buf), @@ -227,7 +235,7 @@ func (s *Session) Node(ctx context.Context) (node *Node, err error) { if buf, err = json.Marshal(s.LastSeenAt); err != nil { return nil, err } - node.Fields[5] = &Field{ + node.Fields[6] = &Field{ Type: "time.Time", Name: "lastSeenAt", Value: string(buf), diff --git a/tavern/ent/gql_where_input.go b/tavern/ent/gql_where_input.go index 825af4586..e3c47e687 100644 --- a/tavern/ent/gql_where_input.go +++ b/tavern/ent/gql_where_input.go @@ -742,6 +742,21 @@ type SessionWhereInput struct { IDLT *int `json:"idLT,omitempty"` IDLTE *int `json:"idLTE,omitempty"` + // "name" field predicates. + Name *string `json:"name,omitempty"` + NameNEQ *string `json:"nameNEQ,omitempty"` + NameIn []string `json:"nameIn,omitempty"` + NameNotIn []string `json:"nameNotIn,omitempty"` + NameGT *string `json:"nameGT,omitempty"` + NameGTE *string `json:"nameGTE,omitempty"` + NameLT *string `json:"nameLT,omitempty"` + NameLTE *string `json:"nameLTE,omitempty"` + NameContains *string `json:"nameContains,omitempty"` + NameHasPrefix *string `json:"nameHasPrefix,omitempty"` + NameHasSuffix *string `json:"nameHasSuffix,omitempty"` + NameEqualFold *string `json:"nameEqualFold,omitempty"` + NameContainsFold *string `json:"nameContainsFold,omitempty"` + // "principal" field predicates. Principal *string `json:"principal,omitempty"` PrincipalNEQ *string `json:"principalNEQ,omitempty"` @@ -941,6 +956,45 @@ func (i *SessionWhereInput) P() (predicate.Session, error) { if i.IDLTE != nil { predicates = append(predicates, session.IDLTE(*i.IDLTE)) } + if i.Name != nil { + predicates = append(predicates, session.NameEQ(*i.Name)) + } + if i.NameNEQ != nil { + predicates = append(predicates, session.NameNEQ(*i.NameNEQ)) + } + if len(i.NameIn) > 0 { + predicates = append(predicates, session.NameIn(i.NameIn...)) + } + if len(i.NameNotIn) > 0 { + predicates = append(predicates, session.NameNotIn(i.NameNotIn...)) + } + if i.NameGT != nil { + predicates = append(predicates, session.NameGT(*i.NameGT)) + } + if i.NameGTE != nil { + predicates = append(predicates, session.NameGTE(*i.NameGTE)) + } + if i.NameLT != nil { + predicates = append(predicates, session.NameLT(*i.NameLT)) + } + if i.NameLTE != nil { + predicates = append(predicates, session.NameLTE(*i.NameLTE)) + } + if i.NameContains != nil { + predicates = append(predicates, session.NameContains(*i.NameContains)) + } + if i.NameHasPrefix != nil { + predicates = append(predicates, session.NameHasPrefix(*i.NameHasPrefix)) + } + if i.NameHasSuffix != nil { + predicates = append(predicates, session.NameHasSuffix(*i.NameHasSuffix)) + } + if i.NameEqualFold != nil { + predicates = append(predicates, session.NameEqualFold(*i.NameEqualFold)) + } + if i.NameContainsFold != nil { + predicates = append(predicates, session.NameContainsFold(*i.NameContainsFold)) + } if i.Principal != nil { predicates = append(predicates, session.PrincipalEQ(*i.Principal)) } diff --git a/tavern/ent/migrate/schema.go b/tavern/ent/migrate/schema.go index f47d38b46..c9c347413 100644 --- a/tavern/ent/migrate/schema.go +++ b/tavern/ent/migrate/schema.go @@ -66,6 +66,7 @@ var ( // SessionsColumns holds the columns for the "sessions" table. SessionsColumns = []*schema.Column{ {Name: "id", Type: field.TypeInt, Increment: true}, + {Name: "name", Type: field.TypeString, Unique: true}, {Name: "principal", Type: field.TypeString, Nullable: true}, {Name: "hostname", Type: field.TypeString, Nullable: true}, {Name: "identifier", Type: field.TypeString, Unique: true}, diff --git a/tavern/ent/mutation.go b/tavern/ent/mutation.go index e3ff567a6..658a228e3 100644 --- a/tavern/ent/mutation.go +++ b/tavern/ent/mutation.go @@ -1368,6 +1368,7 @@ type SessionMutation struct { op Op typ string id *int + name *string principal *string hostname *string identifier *string @@ -1484,6 +1485,42 @@ func (m *SessionMutation) IDs(ctx context.Context) ([]int, error) { } } +// SetName sets the "name" field. +func (m *SessionMutation) SetName(s string) { + m.name = &s +} + +// Name returns the value of the "name" field in the mutation. +func (m *SessionMutation) Name() (r string, exists bool) { + v := m.name + if v == nil { + return + } + return *v, true +} + +// OldName returns the old "name" field's value of the Session entity. +// If the Session object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *SessionMutation) OldName(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldName is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldName requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldName: %w", err) + } + return oldValue.Name, nil +} + +// ResetName resets all changes to the "name" field. +func (m *SessionMutation) ResetName() { + m.name = nil +} + // SetPrincipal sets the "principal" field. func (m *SessionMutation) SetPrincipal(s string) { m.principal = &s @@ -1892,7 +1929,10 @@ func (m *SessionMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *SessionMutation) Fields() []string { - fields := make([]string, 0, 6) + fields := make([]string, 0, 7) + if m.name != nil { + fields = append(fields, session.FieldName) + } if m.principal != nil { fields = append(fields, session.FieldPrincipal) } @@ -1919,6 +1959,8 @@ func (m *SessionMutation) Fields() []string { // schema. func (m *SessionMutation) Field(name string) (ent.Value, bool) { switch name { + case session.FieldName: + return m.Name() case session.FieldPrincipal: return m.Principal() case session.FieldHostname: @@ -1940,6 +1982,8 @@ func (m *SessionMutation) Field(name string) (ent.Value, bool) { // database failed. func (m *SessionMutation) OldField(ctx context.Context, name string) (ent.Value, error) { switch name { + case session.FieldName: + return m.OldName(ctx) case session.FieldPrincipal: return m.OldPrincipal(ctx) case session.FieldHostname: @@ -1961,6 +2005,13 @@ func (m *SessionMutation) OldField(ctx context.Context, name string) (ent.Value, // type. func (m *SessionMutation) SetField(name string, value ent.Value) error { switch name { + case session.FieldName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetName(v) + return nil case session.FieldPrincipal: v, ok := value.(string) if !ok { @@ -2085,6 +2136,9 @@ func (m *SessionMutation) ClearField(name string) error { // It returns an error if the field is not defined in the schema. func (m *SessionMutation) ResetField(name string) error { switch name { + case session.FieldName: + m.ResetName() + return nil case session.FieldPrincipal: m.ResetPrincipal() return nil diff --git a/tavern/ent/runtime/runtime.go b/tavern/ent/runtime/runtime.go index aba2a2363..29e0773b1 100644 --- a/tavern/ent/runtime/runtime.go +++ b/tavern/ent/runtime/runtime.go @@ -71,26 +71,32 @@ func init() { job.NameValidator = jobDescName.Validators[0].(func(string) error) sessionFields := schema.Session{}.Fields() _ = sessionFields + // sessionDescName is the schema descriptor for name field. + sessionDescName := sessionFields[0].Descriptor() + // session.DefaultName holds the default value on creation for the name field. + session.DefaultName = sessionDescName.Default.(func() string) + // session.NameValidator is a validator for the "name" field. It is called by the builders before save. + session.NameValidator = sessionDescName.Validators[0].(func(string) error) // sessionDescPrincipal is the schema descriptor for principal field. - sessionDescPrincipal := sessionFields[0].Descriptor() + sessionDescPrincipal := sessionFields[1].Descriptor() // session.PrincipalValidator is a validator for the "principal" field. It is called by the builders before save. session.PrincipalValidator = sessionDescPrincipal.Validators[0].(func(string) error) // sessionDescHostname is the schema descriptor for hostname field. - sessionDescHostname := sessionFields[1].Descriptor() + sessionDescHostname := sessionFields[2].Descriptor() // session.HostnameValidator is a validator for the "hostname" field. It is called by the builders before save. session.HostnameValidator = sessionDescHostname.Validators[0].(func(string) error) // sessionDescIdentifier is the schema descriptor for identifier field. - sessionDescIdentifier := sessionFields[2].Descriptor() + sessionDescIdentifier := sessionFields[3].Descriptor() // session.DefaultIdentifier holds the default value on creation for the identifier field. session.DefaultIdentifier = sessionDescIdentifier.Default.(func() string) // session.IdentifierValidator is a validator for the "identifier" field. It is called by the builders before save. session.IdentifierValidator = sessionDescIdentifier.Validators[0].(func(string) error) // sessionDescAgentIdentifier is the schema descriptor for agentIdentifier field. - sessionDescAgentIdentifier := sessionFields[3].Descriptor() + sessionDescAgentIdentifier := sessionFields[4].Descriptor() // session.AgentIdentifierValidator is a validator for the "agentIdentifier" field. It is called by the builders before save. session.AgentIdentifierValidator = sessionDescAgentIdentifier.Validators[0].(func(string) error) // sessionDescHostIdentifier is the schema descriptor for hostIdentifier field. - sessionDescHostIdentifier := sessionFields[4].Descriptor() + sessionDescHostIdentifier := sessionFields[5].Descriptor() // session.HostIdentifierValidator is a validator for the "hostIdentifier" field. It is called by the builders before save. session.HostIdentifierValidator = sessionDescHostIdentifier.Validators[0].(func(string) error) tagFields := schema.Tag{}.Fields() diff --git a/tavern/ent/schema/session.go b/tavern/ent/schema/session.go index 30e8e96ee..7b637758e 100644 --- a/tavern/ent/schema/session.go +++ b/tavern/ent/schema/session.go @@ -6,6 +6,8 @@ import ( "fmt" "io" + "github.com/kcarretto/realm/tavern/namegen" + "entgo.io/contrib/entgql" "entgo.io/ent" "entgo.io/ent/schema" @@ -21,6 +23,11 @@ type Session struct { // Fields of the Session. func (Session) Fields() []ent.Field { return []ent.Field{ + field.String("name"). + NotEmpty(). + Unique(). + DefaultFunc(namegen.GetRandomName). + Comment("A human readable identifier for the session."), field.String("principal"). Optional(). NotEmpty(). diff --git a/tavern/ent/session.go b/tavern/ent/session.go index bcadd0752..8a8c5499f 100644 --- a/tavern/ent/session.go +++ b/tavern/ent/session.go @@ -16,6 +16,8 @@ type Session struct { config `json:"-"` // ID of the ent. ID int `json:"id,omitempty"` + // A human readable identifier for the session. + Name string `json:"name,omitempty"` // The identity the session is authenticated as (e.g. 'root') Principal string `json:"principal,omitempty"` // The hostname of the system the session is running on. @@ -74,7 +76,7 @@ func (*Session) scanValues(columns []string) ([]any, error) { switch columns[i] { case session.FieldID: values[i] = new(sql.NullInt64) - case session.FieldPrincipal, session.FieldHostname, session.FieldIdentifier, session.FieldAgentIdentifier, session.FieldHostIdentifier: + case session.FieldName, session.FieldPrincipal, session.FieldHostname, session.FieldIdentifier, session.FieldAgentIdentifier, session.FieldHostIdentifier: values[i] = new(sql.NullString) case session.FieldLastSeenAt: values[i] = new(sql.NullTime) @@ -99,6 +101,12 @@ func (s *Session) assignValues(columns []string, values []any) error { return fmt.Errorf("unexpected type %T for field id", value) } s.ID = int(value.Int64) + case session.FieldName: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field name", values[i]) + } else if value.Valid { + s.Name = value.String + } case session.FieldPrincipal: if value, ok := values[i].(*sql.NullString); !ok { return fmt.Errorf("unexpected type %T for field principal", values[i]) @@ -173,6 +181,9 @@ func (s *Session) String() string { var builder strings.Builder builder.WriteString("Session(") builder.WriteString(fmt.Sprintf("id=%v, ", s.ID)) + builder.WriteString("name=") + builder.WriteString(s.Name) + builder.WriteString(", ") builder.WriteString("principal=") builder.WriteString(s.Principal) builder.WriteString(", ") diff --git a/tavern/ent/session/session.go b/tavern/ent/session/session.go index ec6a651c4..9fdd38e16 100644 --- a/tavern/ent/session/session.go +++ b/tavern/ent/session/session.go @@ -7,6 +7,8 @@ const ( Label = "session" // FieldID holds the string denoting the id field in the database. FieldID = "id" + // FieldName holds the string denoting the name field in the database. + FieldName = "name" // FieldPrincipal holds the string denoting the principal field in the database. FieldPrincipal = "principal" // FieldHostname holds the string denoting the hostname field in the database. @@ -42,6 +44,7 @@ const ( // Columns holds all SQL columns for session fields. var Columns = []string{ FieldID, + FieldName, FieldPrincipal, FieldHostname, FieldIdentifier, @@ -67,6 +70,10 @@ func ValidColumn(column string) bool { } var ( + // DefaultName holds the default value on creation for the "name" field. + DefaultName func() string + // NameValidator is a validator for the "name" field. It is called by the builders before save. + NameValidator func(string) error // PrincipalValidator is a validator for the "principal" field. It is called by the builders before save. PrincipalValidator func(string) error // HostnameValidator is a validator for the "hostname" field. It is called by the builders before save. diff --git a/tavern/ent/session/where.go b/tavern/ent/session/where.go index 4e5dd8c2e..675b4da7c 100644 --- a/tavern/ent/session/where.go +++ b/tavern/ent/session/where.go @@ -81,6 +81,13 @@ func IDLTE(id int) predicate.Session { }) } +// Name applies equality check predicate on the "name" field. It's identical to NameEQ. +func Name(v string) predicate.Session { + return predicate.Session(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldName), v)) + }) +} + // Principal applies equality check predicate on the "principal" field. It's identical to PrincipalEQ. func Principal(v string) predicate.Session { return predicate.Session(func(s *sql.Selector) { @@ -123,6 +130,105 @@ func LastSeenAt(v time.Time) predicate.Session { }) } +// NameEQ applies the EQ predicate on the "name" field. +func NameEQ(v string) predicate.Session { + return predicate.Session(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldName), v)) + }) +} + +// NameNEQ applies the NEQ predicate on the "name" field. +func NameNEQ(v string) predicate.Session { + return predicate.Session(func(s *sql.Selector) { + s.Where(sql.NEQ(s.C(FieldName), v)) + }) +} + +// NameIn applies the In predicate on the "name" field. +func NameIn(vs ...string) predicate.Session { + v := make([]any, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.Session(func(s *sql.Selector) { + s.Where(sql.In(s.C(FieldName), v...)) + }) +} + +// NameNotIn applies the NotIn predicate on the "name" field. +func NameNotIn(vs ...string) predicate.Session { + v := make([]any, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.Session(func(s *sql.Selector) { + s.Where(sql.NotIn(s.C(FieldName), v...)) + }) +} + +// NameGT applies the GT predicate on the "name" field. +func NameGT(v string) predicate.Session { + return predicate.Session(func(s *sql.Selector) { + s.Where(sql.GT(s.C(FieldName), v)) + }) +} + +// NameGTE applies the GTE predicate on the "name" field. +func NameGTE(v string) predicate.Session { + return predicate.Session(func(s *sql.Selector) { + s.Where(sql.GTE(s.C(FieldName), v)) + }) +} + +// NameLT applies the LT predicate on the "name" field. +func NameLT(v string) predicate.Session { + return predicate.Session(func(s *sql.Selector) { + s.Where(sql.LT(s.C(FieldName), v)) + }) +} + +// NameLTE applies the LTE predicate on the "name" field. +func NameLTE(v string) predicate.Session { + return predicate.Session(func(s *sql.Selector) { + s.Where(sql.LTE(s.C(FieldName), v)) + }) +} + +// NameContains applies the Contains predicate on the "name" field. +func NameContains(v string) predicate.Session { + return predicate.Session(func(s *sql.Selector) { + s.Where(sql.Contains(s.C(FieldName), v)) + }) +} + +// NameHasPrefix applies the HasPrefix predicate on the "name" field. +func NameHasPrefix(v string) predicate.Session { + return predicate.Session(func(s *sql.Selector) { + s.Where(sql.HasPrefix(s.C(FieldName), v)) + }) +} + +// NameHasSuffix applies the HasSuffix predicate on the "name" field. +func NameHasSuffix(v string) predicate.Session { + return predicate.Session(func(s *sql.Selector) { + s.Where(sql.HasSuffix(s.C(FieldName), v)) + }) +} + +// NameEqualFold applies the EqualFold predicate on the "name" field. +func NameEqualFold(v string) predicate.Session { + return predicate.Session(func(s *sql.Selector) { + s.Where(sql.EqualFold(s.C(FieldName), v)) + }) +} + +// NameContainsFold applies the ContainsFold predicate on the "name" field. +func NameContainsFold(v string) predicate.Session { + return predicate.Session(func(s *sql.Selector) { + s.Where(sql.ContainsFold(s.C(FieldName), v)) + }) +} + // PrincipalEQ applies the EQ predicate on the "principal" field. func PrincipalEQ(v string) predicate.Session { return predicate.Session(func(s *sql.Selector) { diff --git a/tavern/ent/session_create.go b/tavern/ent/session_create.go index efa376c3b..4710cee69 100644 --- a/tavern/ent/session_create.go +++ b/tavern/ent/session_create.go @@ -22,6 +22,20 @@ type SessionCreate struct { hooks []Hook } +// SetName sets the "name" field. +func (sc *SessionCreate) SetName(s string) *SessionCreate { + sc.mutation.SetName(s) + return sc +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (sc *SessionCreate) SetNillableName(s *string) *SessionCreate { + if s != nil { + sc.SetName(*s) + } + return sc +} + // SetPrincipal sets the "principal" field. func (sc *SessionCreate) SetPrincipal(s string) *SessionCreate { sc.mutation.SetPrincipal(s) @@ -213,6 +227,10 @@ func (sc *SessionCreate) ExecX(ctx context.Context) { // defaults sets the default values of the builder before save. func (sc *SessionCreate) defaults() { + if _, ok := sc.mutation.Name(); !ok { + v := session.DefaultName() + sc.mutation.SetName(v) + } if _, ok := sc.mutation.Identifier(); !ok { v := session.DefaultIdentifier() sc.mutation.SetIdentifier(v) @@ -221,6 +239,14 @@ func (sc *SessionCreate) defaults() { // check runs all checks and user-defined validators on the builder. func (sc *SessionCreate) check() error { + if _, ok := sc.mutation.Name(); !ok { + return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "Session.name"`)} + } + if v, ok := sc.mutation.Name(); ok { + if err := session.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Session.name": %w`, err)} + } + } if v, ok := sc.mutation.Principal(); ok { if err := session.PrincipalValidator(v); err != nil { return &ValidationError{Name: "principal", err: fmt.Errorf(`ent: validator failed for field "Session.principal": %w`, err)} @@ -276,6 +302,10 @@ func (sc *SessionCreate) createSpec() (*Session, *sqlgraph.CreateSpec) { }, } ) + if value, ok := sc.mutation.Name(); ok { + _spec.SetField(session.FieldName, field.TypeString, value) + _node.Name = value + } if value, ok := sc.mutation.Principal(); ok { _spec.SetField(session.FieldPrincipal, field.TypeString, value) _node.Principal = value diff --git a/tavern/ent/session_query.go b/tavern/ent/session_query.go index 79c795828..3747dfdaa 100644 --- a/tavern/ent/session_query.go +++ b/tavern/ent/session_query.go @@ -330,12 +330,12 @@ func (sq *SessionQuery) WithTasks(opts ...func(*TaskQuery)) *SessionQuery { // Example: // // var v []struct { -// Principal string `json:"principal,omitempty"` +// Name string `json:"name,omitempty"` // Count int `json:"count,omitempty"` // } // // client.Session.Query(). -// GroupBy(session.FieldPrincipal). +// GroupBy(session.FieldName). // Aggregate(ent.Count()). // Scan(ctx, &v) func (sq *SessionQuery) GroupBy(field string, fields ...string) *SessionGroupBy { @@ -358,11 +358,11 @@ func (sq *SessionQuery) GroupBy(field string, fields ...string) *SessionGroupBy // Example: // // var v []struct { -// Principal string `json:"principal,omitempty"` +// Name string `json:"name,omitempty"` // } // // client.Session.Query(). -// Select(session.FieldPrincipal). +// Select(session.FieldName). // Scan(ctx, &v) func (sq *SessionQuery) Select(fields ...string) *SessionSelect { sq.fields = append(sq.fields, fields...) diff --git a/tavern/ent/session_update.go b/tavern/ent/session_update.go index d5d029563..2ac9cd885 100644 --- a/tavern/ent/session_update.go +++ b/tavern/ent/session_update.go @@ -30,6 +30,20 @@ func (su *SessionUpdate) Where(ps ...predicate.Session) *SessionUpdate { return su } +// SetName sets the "name" field. +func (su *SessionUpdate) SetName(s string) *SessionUpdate { + su.mutation.SetName(s) + return su +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (su *SessionUpdate) SetNillableName(s *string) *SessionUpdate { + if s != nil { + su.SetName(*s) + } + return su +} + // SetPrincipal sets the "principal" field. func (su *SessionUpdate) SetPrincipal(s string) *SessionUpdate { su.mutation.SetPrincipal(s) @@ -283,6 +297,11 @@ func (su *SessionUpdate) ExecX(ctx context.Context) { // check runs all checks and user-defined validators on the builder. func (su *SessionUpdate) check() error { + if v, ok := su.mutation.Name(); ok { + if err := session.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Session.name": %w`, err)} + } + } if v, ok := su.mutation.Principal(); ok { if err := session.PrincipalValidator(v); err != nil { return &ValidationError{Name: "principal", err: fmt.Errorf(`ent: validator failed for field "Session.principal": %w`, err)} @@ -329,6 +348,9 @@ func (su *SessionUpdate) sqlSave(ctx context.Context) (n int, err error) { } } } + if value, ok := su.mutation.Name(); ok { + _spec.SetField(session.FieldName, field.TypeString, value) + } if value, ok := su.mutation.Principal(); ok { _spec.SetField(session.FieldPrincipal, field.TypeString, value) } @@ -489,6 +511,20 @@ type SessionUpdateOne struct { mutation *SessionMutation } +// SetName sets the "name" field. +func (suo *SessionUpdateOne) SetName(s string) *SessionUpdateOne { + suo.mutation.SetName(s) + return suo +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (suo *SessionUpdateOne) SetNillableName(s *string) *SessionUpdateOne { + if s != nil { + suo.SetName(*s) + } + return suo +} + // SetPrincipal sets the "principal" field. func (suo *SessionUpdateOne) SetPrincipal(s string) *SessionUpdateOne { suo.mutation.SetPrincipal(s) @@ -755,6 +791,11 @@ func (suo *SessionUpdateOne) ExecX(ctx context.Context) { // check runs all checks and user-defined validators on the builder. func (suo *SessionUpdateOne) check() error { + if v, ok := suo.mutation.Name(); ok { + if err := session.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Session.name": %w`, err)} + } + } if v, ok := suo.mutation.Principal(); ok { if err := session.PrincipalValidator(v); err != nil { return &ValidationError{Name: "principal", err: fmt.Errorf(`ent: validator failed for field "Session.principal": %w`, err)} @@ -818,6 +859,9 @@ func (suo *SessionUpdateOne) sqlSave(ctx context.Context) (_node *Session, err e } } } + if value, ok := suo.mutation.Name(); ok { + _spec.SetField(session.FieldName, field.TypeString, value) + } if value, ok := suo.mutation.Principal(); ok { _spec.SetField(session.FieldPrincipal, field.TypeString, value) } diff --git a/tavern/graphql/generated/mutation.generated.go b/tavern/graphql/generated/mutation.generated.go index bf1f49846..5dd7e9704 100644 --- a/tavern/graphql/generated/mutation.generated.go +++ b/tavern/graphql/generated/mutation.generated.go @@ -354,6 +354,8 @@ func (ec *executionContext) fieldContext_Mutation_updateSession(ctx context.Cont switch field.Name { case "id": return ec.fieldContext_Session_id(ctx, field) + case "name": + return ec.fieldContext_Session_name(ctx, field) case "principal": return ec.fieldContext_Session_principal(ctx, field) case "hostname": diff --git a/tavern/graphql/generated/query.generated.go b/tavern/graphql/generated/query.generated.go index dff0c4371..1fc472065 100644 --- a/tavern/graphql/generated/query.generated.go +++ b/tavern/graphql/generated/query.generated.go @@ -1187,6 +1187,8 @@ func (ec *executionContext) fieldContext_Query_sessions(ctx context.Context, fie switch field.Name { case "id": return ec.fieldContext_Session_id(ctx, field) + case "name": + return ec.fieldContext_Session_name(ctx, field) case "principal": return ec.fieldContext_Session_principal(ctx, field) case "hostname": @@ -1555,6 +1557,50 @@ func (ec *executionContext) fieldContext_Session_id(ctx context.Context, field g return fc, nil } +func (ec *executionContext) _Session_name(ctx context.Context, field graphql.CollectedField, obj *ent.Session) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Session_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Session_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Session", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _Session_principal(ctx context.Context, field graphql.CollectedField, obj *ent.Session) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Session_principal(ctx, field) if err != nil { @@ -2088,6 +2134,8 @@ func (ec *executionContext) fieldContext_Tag_sessions(ctx context.Context, field switch field.Name { case "id": return ec.fieldContext_Session_id(ctx, field) + case "name": + return ec.fieldContext_Session_name(ctx, field) case "principal": return ec.fieldContext_Session_principal(ctx, field) case "hostname": @@ -2551,6 +2599,8 @@ func (ec *executionContext) fieldContext_Task_session(ctx context.Context, field switch field.Name { case "id": return ec.fieldContext_Session_id(ctx, field) + case "name": + return ec.fieldContext_Session_name(ctx, field) case "principal": return ec.fieldContext_Session_principal(ctx, field) case "hostname": @@ -4449,7 +4499,7 @@ func (ec *executionContext) unmarshalInputSessionWhereInput(ctx context.Context, asMap[k] = v } - fieldsInOrder := [...]string{"not", "and", "or", "id", "idNEQ", "idIn", "idNotIn", "idGT", "idGTE", "idLT", "idLTE", "principal", "principalNEQ", "principalIn", "principalNotIn", "principalGT", "principalGTE", "principalLT", "principalLTE", "principalContains", "principalHasPrefix", "principalHasSuffix", "principalIsNil", "principalNotNil", "principalEqualFold", "principalContainsFold", "hostname", "hostnameNEQ", "hostnameIn", "hostnameNotIn", "hostnameGT", "hostnameGTE", "hostnameLT", "hostnameLTE", "hostnameContains", "hostnameHasPrefix", "hostnameHasSuffix", "hostnameIsNil", "hostnameNotNil", "hostnameEqualFold", "hostnameContainsFold", "identifier", "identifierNEQ", "identifierIn", "identifierNotIn", "identifierGT", "identifierGTE", "identifierLT", "identifierLTE", "identifierContains", "identifierHasPrefix", "identifierHasSuffix", "identifierEqualFold", "identifierContainsFold", "agentidentifier", "agentidentifierNEQ", "agentidentifierIn", "agentidentifierNotIn", "agentidentifierGT", "agentidentifierGTE", "agentidentifierLT", "agentidentifierLTE", "agentidentifierContains", "agentidentifierHasPrefix", "agentidentifierHasSuffix", "agentidentifierIsNil", "agentidentifierNotNil", "agentidentifierEqualFold", "agentidentifierContainsFold", "hostidentifier", "hostidentifierNEQ", "hostidentifierIn", "hostidentifierNotIn", "hostidentifierGT", "hostidentifierGTE", "hostidentifierLT", "hostidentifierLTE", "hostidentifierContains", "hostidentifierHasPrefix", "hostidentifierHasSuffix", "hostidentifierIsNil", "hostidentifierNotNil", "hostidentifierEqualFold", "hostidentifierContainsFold", "lastseenat", "lastseenatNEQ", "lastseenatIn", "lastseenatNotIn", "lastseenatGT", "lastseenatGTE", "lastseenatLT", "lastseenatLTE", "lastseenatIsNil", "lastseenatNotNil", "hasTags", "hasTagsWith", "hasTasks", "hasTasksWith"} + fieldsInOrder := [...]string{"not", "and", "or", "id", "idNEQ", "idIn", "idNotIn", "idGT", "idGTE", "idLT", "idLTE", "name", "nameNEQ", "nameIn", "nameNotIn", "nameGT", "nameGTE", "nameLT", "nameLTE", "nameContains", "nameHasPrefix", "nameHasSuffix", "nameEqualFold", "nameContainsFold", "principal", "principalNEQ", "principalIn", "principalNotIn", "principalGT", "principalGTE", "principalLT", "principalLTE", "principalContains", "principalHasPrefix", "principalHasSuffix", "principalIsNil", "principalNotNil", "principalEqualFold", "principalContainsFold", "hostname", "hostnameNEQ", "hostnameIn", "hostnameNotIn", "hostnameGT", "hostnameGTE", "hostnameLT", "hostnameLTE", "hostnameContains", "hostnameHasPrefix", "hostnameHasSuffix", "hostnameIsNil", "hostnameNotNil", "hostnameEqualFold", "hostnameContainsFold", "identifier", "identifierNEQ", "identifierIn", "identifierNotIn", "identifierGT", "identifierGTE", "identifierLT", "identifierLTE", "identifierContains", "identifierHasPrefix", "identifierHasSuffix", "identifierEqualFold", "identifierContainsFold", "agentidentifier", "agentidentifierNEQ", "agentidentifierIn", "agentidentifierNotIn", "agentidentifierGT", "agentidentifierGTE", "agentidentifierLT", "agentidentifierLTE", "agentidentifierContains", "agentidentifierHasPrefix", "agentidentifierHasSuffix", "agentidentifierIsNil", "agentidentifierNotNil", "agentidentifierEqualFold", "agentidentifierContainsFold", "hostidentifier", "hostidentifierNEQ", "hostidentifierIn", "hostidentifierNotIn", "hostidentifierGT", "hostidentifierGTE", "hostidentifierLT", "hostidentifierLTE", "hostidentifierContains", "hostidentifierHasPrefix", "hostidentifierHasSuffix", "hostidentifierIsNil", "hostidentifierNotNil", "hostidentifierEqualFold", "hostidentifierContainsFold", "lastseenat", "lastseenatNEQ", "lastseenatIn", "lastseenatNotIn", "lastseenatGT", "lastseenatGTE", "lastseenatLT", "lastseenatLTE", "lastseenatIsNil", "lastseenatNotNil", "hasTags", "hasTagsWith", "hasTasks", "hasTasksWith"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -4544,6 +4594,110 @@ func (ec *executionContext) unmarshalInputSessionWhereInput(ctx context.Context, if err != nil { return it, err } + case "name": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) + it.Name, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + case "nameNEQ": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("nameNEQ")) + it.NameNEQ, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + case "nameIn": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("nameIn")) + it.NameIn, err = ec.unmarshalOString2ᚕstringᚄ(ctx, v) + if err != nil { + return it, err + } + case "nameNotIn": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("nameNotIn")) + it.NameNotIn, err = ec.unmarshalOString2ᚕstringᚄ(ctx, v) + if err != nil { + return it, err + } + case "nameGT": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("nameGT")) + it.NameGT, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + case "nameGTE": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("nameGTE")) + it.NameGTE, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + case "nameLT": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("nameLT")) + it.NameLT, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + case "nameLTE": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("nameLTE")) + it.NameLTE, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + case "nameContains": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("nameContains")) + it.NameContains, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + case "nameHasPrefix": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("nameHasPrefix")) + it.NameHasPrefix, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + case "nameHasSuffix": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("nameHasSuffix")) + it.NameHasSuffix, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + case "nameEqualFold": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("nameEqualFold")) + it.NameEqualFold, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + case "nameContainsFold": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("nameContainsFold")) + it.NameContainsFold, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } case "principal": var err error @@ -7065,13 +7219,21 @@ func (ec *executionContext) unmarshalInputUpdateSessionInput(ctx context.Context asMap[k] = v } - fieldsInOrder := [...]string{"clearHostname", "hostname", "addTagIDs", "removeTagIDs"} + fieldsInOrder := [...]string{"name", "clearHostname", "hostname", "addTagIDs", "removeTagIDs"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { continue } switch k { + case "name": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) + it.Name, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } case "clearHostname": var err error @@ -8064,6 +8226,13 @@ func (ec *executionContext) _Session(ctx context.Context, sel ast.SelectionSet, out.Values[i] = ec._Session_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + case "name": + + out.Values[i] = ec._Session_name(ctx, field, obj) + if out.Values[i] == graphql.Null { atomic.AddUint32(&invalids, 1) } diff --git a/tavern/graphql/generated/root_.generated.go b/tavern/graphql/generated/root_.generated.go index 1d54a2158..1ddea9e5b 100644 --- a/tavern/graphql/generated/root_.generated.go +++ b/tavern/graphql/generated/root_.generated.go @@ -96,6 +96,7 @@ type ComplexityRoot struct { ID func(childComplexity int) int Identifier func(childComplexity int) int LastSeenAt func(childComplexity int) int + Name func(childComplexity int) int Principal func(childComplexity int) int Tags func(childComplexity int) int Tasks func(childComplexity int) int @@ -486,6 +487,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Session.LastSeenAt(childComplexity), true + case "Session.name": + if e.complexity.Session.Name == nil { + break + } + + return e.complexity.Session.Name(childComplexity), true + case "Session.principal": if e.complexity.Session.Principal == nil { break @@ -1087,6 +1095,8 @@ type Query { } type Session implements Node { id: ID! + """A human readable identifier for the session.""" + name: String! """The identity the session is authenticated as (e.g. 'root')""" principal: String """The hostname of the system the session is running on.""" @@ -1130,6 +1140,20 @@ input SessionWhereInput { idGTE: ID idLT: ID idLTE: ID + """name field predicates""" + name: String + nameNEQ: String + nameIn: [String!] + nameNotIn: [String!] + nameGT: String + nameGTE: String + nameLT: String + nameLTE: String + nameContains: String + nameHasPrefix: String + nameHasSuffix: String + nameEqualFold: String + nameContainsFold: String """principal field predicates""" principal: String principalNEQ: String @@ -1562,6 +1586,8 @@ UpdateSessionInput is used for update Session object. Input was generated by ent. """ input UpdateSessionInput { + """A human readable identifier for the session.""" + name: String clearHostname: Boolean """The hostname of the system the session is running on.""" hostname: String diff --git a/tavern/graphql/schema/query.graphql b/tavern/graphql/schema/query.graphql index e039e54da..effd89f2a 100644 --- a/tavern/graphql/schema/query.graphql +++ b/tavern/graphql/schema/query.graphql @@ -293,6 +293,8 @@ type Query { } type Session implements Node { id: ID! + """A human readable identifier for the session.""" + name: String! """The identity the session is authenticated as (e.g. 'root')""" principal: String """The hostname of the system the session is running on.""" @@ -336,6 +338,20 @@ input SessionWhereInput { idGTE: ID idLT: ID idLTE: ID + """name field predicates""" + name: String + nameNEQ: String + nameIn: [String!] + nameNotIn: [String!] + nameGT: String + nameGTE: String + nameLT: String + nameLTE: String + nameContains: String + nameHasPrefix: String + nameHasSuffix: String + nameEqualFold: String + nameContainsFold: String """principal field predicates""" principal: String principalNEQ: String @@ -768,6 +784,8 @@ UpdateSessionInput is used for update Session object. Input was generated by ent. """ input UpdateSessionInput { + """A human readable identifier for the session.""" + name: String clearHostname: Boolean """The hostname of the system the session is running on.""" hostname: String diff --git a/tavern/internal/www/schema.graphql b/tavern/internal/www/schema.graphql index 4bd57b357..2d75627f5 100644 --- a/tavern/internal/www/schema.graphql +++ b/tavern/internal/www/schema.graphql @@ -365,6 +365,8 @@ type Query { } type Session implements Node { id: ID! + """A human readable identifier for the session.""" + name: String! """The identity the session is authenticated as (e.g. 'root')""" principal: String """The hostname of the system the session is running on.""" @@ -408,6 +410,20 @@ input SessionWhereInput { idGTE: ID idLT: ID idLTE: ID + """name field predicates""" + name: String + nameNEQ: String + nameIn: [String!] + nameNotIn: [String!] + nameGT: String + nameGTE: String + nameLT: String + nameLTE: String + nameContains: String + nameHasPrefix: String + nameHasSuffix: String + nameEqualFold: String + nameContainsFold: String """principal field predicates""" principal: String principalNEQ: String @@ -840,6 +856,8 @@ UpdateSessionInput is used for update Session object. Input was generated by ent. """ input UpdateSessionInput { + """A human readable identifier for the session.""" + name: String clearHostname: Boolean """The hostname of the system the session is running on.""" hostname: String diff --git a/tavern/namegen/namegen.go b/tavern/namegen/namegen.go new file mode 100644 index 000000000..73249bc06 --- /dev/null +++ b/tavern/namegen/namegen.go @@ -0,0 +1,487 @@ +package namegen + +import ( + "crypto/rand" + "fmt" + "log" + "math/big" +) + +var ( + adjectives = [...]string{ + "absolute", + "accomplished", + "accurate", + "acrobatic", + "admiring", + "adoring", + "adorable", + "affectionate", + "agitated", + "amazing", + "angry", + "awesome", + "ballin", + "beautiful", + "beloved", + "blissful", + "bold", + "boring", + "brave", + "busy", + "celebrated", + "charming", + "clever", + "compassionate", + "competent", + "condescending", + "confident", + "cool", + "cranky", + "crazy", + "dazzling", + "determined", + "distracted", + "dreamy", + "eager", + "ecstatic", + "elastic", + "elated", + "elegant", + "eloquent", + "epic", + "exciting", + "fervent", + "festive", + "flamboyant", + "focused", + "friendly", + "frosty", + "funny", + "gallant", + "gifted", + "goofy", + "gracious", + "great", + "happy", + "hardcore", + "heuristic", + "hopeful", + "hungry", + "infallible", + "inspiring", + "intelligent", + "interesting", + "jolly", + "jovial", + "keen", + "kind", + "laughing", + "loving", + "lucid", + "magical", + "modest", + "musing", + "mystifying", + "naughty", + "nervous", + "nice", + "nifty", + "nostalgic", + "objective", + "optimistic", + "peaceful", + "pedantic", + "pensive", + "practical", + "priceless", + "quantum", + "quality", + "quirky", + "quizzical", + "rare", + "recursing", + "relaxed", + "reverent", + "romantic", + "sad", + "serene", + "sharp", + "silly", + "sleepy", + "splendiforous", + "spooky", + "stoic", + "strange", + "stupefied", + "suspicious", + "sweet", + "tender", + "thirsty", + "trusting", + "unruffled", + "upbeat", + "unusual", + "vibrant", + "vigilant", + "vigorous", + "wizardly", + "wonderful", + "xenodochial", + "youthful", + "zealous", + "zen", + } + + nouns = [...]string{ + "aboleth", + "acolyte", + "adult-black-dragon", + "adult-blue-dragon", + "adult-brass-dragon", + "adult-bronze-dragon", + "adult-copper-dragon", + "adult-gold-dragon", + "adult-green-dragon", + "adult-red-dragon", + "adult-silver-dragon", + "adult-white-dragon", + "air-elemental", + "allosaurus", + "ancient-black-dragon", + "ancient-blue-dragon", + "ancient-brass-dragon", + "ancient-bronze-dragon", + "ancient-copper-dragon", + "ancient-gold-dragon", + "ancient-green-dragon", + "ancient-red-dragon", + "ancient-silver-dragon", + "ancient-white-dragon", + "androsphinx", + "animated-armor", + "ankheg", + "ankylosaurus", + "ape", + "archmage", + "assassin", + "awakened-shrub", + "awakened-tree", + "axe-beak", + "azer", + "baboon", + "badger", + "balor", + "bandit", + "bandit-captain", + "banshee", + "barbed-devil", + "basilisk", + "bat", + "bearded-devil", + "behir", + "berserker", + "black-bear", + "black-dragon-wyrmling", + "black-pudding", + "blink-dog", + "blood-hawk", + "blue-dragon-wyrmling", + "boar", + "bone-devil", + "brass-dragon-wyrmling", + "bronze-dragon-wyrmling", + "brown-bear", + "bugbear", + "bulette", + "camel", + "cat", + "centaur", + "chain-devil", + "chimera", + "chuul", + "clay-golem", + "cloaker", + "cloud-giant", + "cockatrice", + "commoner", + "constrictor-snake", + "copper-dragon-wyrmling", + "couatl", + "crab", + "crocodile", + "cult-fanatic", + "cultist", + "cyclops", + "darkmantle", + "death-dog", + "deep-gnome-(svirfneblin)", + "deer", + "deva", + "dire-wolf", + "djinni", + "doppelganger", + "draft-horse", + "dragon-turtle", + "dretch", + "drider", + "drow", + "druid", + "dryad", + "duergar", + "dust-mephit", + "eagle", + "earth-elemental", + "efreeti", + "elephant", + "elk", + "erinyes", + "ettercap", + "ettin", + "fire-elemental", + "fire-giant", + "flameskull", + "flesh-golem", + "flying-snake", + "flying-sword", + "frog", + "frost-giant", + "gargoyle", + "gelatinous-cube", + "ghast", + "ghost", + "ghoul", + "giant-ape", + "giant-badger", + "giant-bat", + "giant-boar", + "giant-centipede", + "giant-constrictor-snake", + "giant-crab", + "giant-crocodile", + "giant-eagle", + "giant-elk", + "giant-fire-beetle", + "giant-frog", + "giant-goat", + "giant-hyena", + "giant-lizard", + "giant-octopus", + "giant-owl", + "giant-poisonous-snake", + "giant-rat", + "giant-scorpion", + "giant-sea-horse", + "giant-shark", + "giant-spider", + "giant-toad", + "giant-vulture", + "giant-wasp", + "giant-weasel", + "giant-wolf-spider", + "gibbering-mouther", + "glabrezu", + "gladiator", + "gnoll", + "goat", + "goblin", + "gold-dragon-wyrmling", + "gorgon", + "gray-ooze", + "green-dragon-wyrmling", + "green-hag", + "grick", + "griffon", + "grimlock", + "guard", + "guardian-naga", + "gynosphinx", + "half-red-dragon-veteran", + "harpy", + "hawk", + "hell-hound", + "hezrou", + "hill-giant", + "hippogriff", + "hobgoblin", + "homunculus", + "horned-devil", + "hunter-shark", + "hydra", + "hyena", + "ice-devil", + "ice-mephit", + "imp", + "invisible-stalker", + "iron-golem", + "jackal", + "killer-whale", + "knight", + "kobold", + "kraken", + "lamia", + "lemure", + "lich", + "lion", + "lizard", + "lizardfolk", + "mage", + "magma-mephit", + "magmin", + "mammoth", + "manticore", + "marilith", + "mastiff", + "medusa", + "merfolk", + "merrow", + "mimic", + "minotaur", + "minotaur-skeleton", + "mule", + "mummy", + "mummy-lord", + "nalfeshnee", + "night-hag", + "nightmare", + "noble", + "nothic", + "ochre-jelly", + "octopus", + "ogre", + "ogre-zombie", + "oni", + "orc", + "otyugh", + "owl", + "owlbear", + "panther", + "pegasus", + "phase-spider", + "pit-fiend", + "planetar", + "plesiosaurus", + "poisonous-snake", + "polar-bear", + "pony", + "priest", + "pseudodragon", + "pteranodon", + "purple-worm", + "quasit", + "quipper", + "rakshasa", + "rat", + "raven", + "red-dragon-wyrmling", + "reef-shark", + "remorhaz", + "rhinoceros", + "riding-horse", + "roc", + "roper", + "rug-of-smothering", + "rust-monster", + "saber-toothed-tiger", + "sahuagin", + "salamander", + "satyr", + "scorpion", + "scout", + "sea-hag", + "sea-horse", + "shadow", + "shambling-mound", + "shield-guardian", + "shrieker", + "silver-dragon-wyrmling", + "skeleton", + "solar", + "spectator", + "specter", + "spider", + "spirit-naga", + "sprite", + "spy", + "steam-mephit", + "stirge", + "stone-giant", + "stone-golem", + "storm-giant", + "strahd", + "succubus/incubus", + "swarm-of-bats", + "swarm-of-insects", + "swarm-of-poisonous-snakes", + "swarm-of-quippers", + "swarm-of-rats", + "swarm-of-ravens", + "tarrasque", + "thug", + "tiger", + "tortle", + "treant", + "tribal-warrior", + "triceratops", + "troll", + "twig-blight", + "tyrannosaurus-rex", + "unicorn", + "vampire", + "vampire-spawn", + "veteran", + "violet-fungus", + "vrock", + "vulture", + "warhorse", + "warhorse-skeleton", + "water-elemental", + "weasel", + "werebear", + "wereboar", + "wererat", + "weretiger", + "werewolf", + "white-dragon-wyrmling", + "wight", + "will-o'-wisp", + "winter-wolf", + "wolf", + "worg", + "wraith", + "wyvern", + "xorn", + "yeti", + "young-black-dragon", + "young-blue-dragon", + "young-brass-dragon", + "young-bronze-dragon", + "young-copper-dragon", + "young-gold-dragon", + "young-green-dragon", + "young-red-dragon", + "young-silver-dragon", + "young-white-dragon", + "zombie", + } +) + +// GetRandomName generates a random name from the list of adjectives and surnames in this package +// formatted as "adjective-surname". For example 'focused-turing'. +func GetRandomName() string { + adj1Index := newRandInt(int64(len(adjectives))) + adj2Index := newRandInt(int64(len(adjectives))) + nounIndex := newRandInt(int64(len(nouns))) + randNum := newRandInt(10000000) + return fmt.Sprintf("%s-%s-%s-%d", adjectives[adj1Index], adjectives[adj2Index], nouns[nounIndex], randNum) +} + +// cryptoRandSecure is not always secure, if it errors we return 1337 % max +func newRandInt(max int64) int64 { + nBig, err := rand.Int(rand.Reader, big.NewInt(max)) + if err != nil { + log.Printf("failed to generate random number: %v", err) + return 1337 % max + } + return nBig.Int64() +} diff --git a/tavern/namegen/namegen_test.go b/tavern/namegen/namegen_test.go new file mode 100644 index 000000000..87aa02081 --- /dev/null +++ b/tavern/namegen/namegen_test.go @@ -0,0 +1,31 @@ +package namegen_test + +import ( + "testing" + + "github.com/kcarretto/realm/tavern/namegen" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGetRandomName(t *testing.T) { + t.Run("BasicName", func(t *testing.T) { + name := namegen.GetRandomName() + assert.NotEmpty(t, name) + }) + + t.Run("NoDuplicates", func(t *testing.T) { + // Ensure we don't duplicate names over the course of many trials + names := make(map[string]bool, 1000000) + count := 0 + for i := 0; i < 1000000; i++ { + name := namegen.GetRandomName() + exists, ok := names[name] + require.False(t, ok, "Name %s already exists - after %d attempts", name, count) + assert.False(t, exists) + names[name] = true + count++ + } + }) + +}