diff --git a/tavern/e2e_test.go b/tavern/e2e_test.go deleted file mode 100644 index aea8aef1b..000000000 --- a/tavern/e2e_test.go +++ /dev/null @@ -1,216 +0,0 @@ -package main_test - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "net/http/httptest" - "strconv" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - tavern "realm.pub/tavern" - "realm.pub/tavern/internal/ent" - "realm.pub/tavern/internal/ent/beacon" - "realm.pub/tavern/internal/ent/enttest" - "realm.pub/tavern/internal/ent/host" - "realm.pub/tavern/internal/ent/quest" - "realm.pub/tavern/internal/ent/tome" -) - -func TestEndToEnd(t *testing.T) { - // Setup - ctx := context.Background() - graph := enttest.Open(t, "sqlite3", "file:ent?mode=memory&cache=shared&_fk=1") - defer graph.Close() - srv, err := tavern.NewServer(ctx, tavern.ConfigureMySQLFromClient(graph)) - require.NoError(t, err) - defer srv.Close() - ts := httptest.NewUnstartedServer(srv.HTTP.Handler) - ts.Config = srv.HTTP - ts.Start() - defer ts.Close() - - // Status Test - t.Run("StatusHandler", func(t *testing.T) { - resp, err := ts.Client().Get(testURL(ts, "status")) - require.NoError(t, err) - body, err := io.ReadAll(resp.Body) - require.NoError(t, err) - assert.Equal(t, tavern.OKStatusText, string(body)) - }) - - // Helper for performing beacon callbacks - beaconCallback := func(t *testing.T, identifier string) (*ent.Beacon, claimTasksResponse) { - req := graphQLQuery{ - OperationName: "ClaimTasks", - Query: `mutation ClaimTasks($input: ClaimTasksInput!) { - claimTasks(input: $input) { - id - quest { - id - tome { - id - } - bundle { - id - } - } - } - }`, - Variables: map[string]any{ - "input": map[string]any{ - "principal": "root", - "hostname": "some_hostname", - "hostPlatform": host.PlatformUnknown, - "beaconIdentifier": identifier, - "hostIdentifier": "uniquely_identifies_host.For_example_a_serial_number", - "agentIdentifier": "uniquely_identifies_this_agent", - }, - }, - } - data, err := json.Marshal(req) - require.NoError(t, err) - resp, err := ts.Client().Post(testURL(ts, "graphql"), "application/json", bytes.NewBuffer(data)) - require.NoError(t, err) - body, err := io.ReadAll(resp.Body) - require.NoError(t, err) - - var graphQLResp claimTasksResponse - err = json.Unmarshal(body, &graphQLResp) - require.NoError(t, err) - - testBeacon, err := graph.Beacon.Query(). - Where(beacon.Identifier(identifier)). - Only(ctx) - require.NoError(t, err) - assert.NotZero(t, testBeacon.LastSeenAt) - return testBeacon, graphQLResp - } - - var ( - createdQuestID int - createdTaskID int - defaultTomeID int - createdBundleID int - ) - - // Beacon First Callback - firstBeaconIdentifier := "first_beacon" - t.Run("BeaconFirstCallback", func(t *testing.T) { - _, resp := beaconCallback(t, firstBeaconIdentifier) - assert.Len(t, resp.Data.ClaimTasks, 0) - }) - - // Create a Quest (assumes a default tome has been properly configured) - t.Run("CreateQuest", func(t *testing.T) { - testBeacon, err := graph.Beacon.Query(). - Where(beacon.Identifier(firstBeaconIdentifier)). - Only(ctx) - require.NoError(t, err) - - testTome, err := graph.Tome.Query().Where(tome.NameEQ("example")).First(ctx) - require.NoError(t, err) - require.NotNil(t, testTome) - defaultTomeID = testTome.ID - - req := graphQLQuery{ - OperationName: "CreateQuest", - Query: "mutation CreateQuest($beaconIDs: [ID!]!, $input: CreateQuestInput!) { createQuest(beaconIDs: $beaconIDs, input: $input) { id } }", - Variables: map[string]any{ - "beaconIDs": []int{testBeacon.ID}, - "input": map[string]any{ - "name": "unit test", - "tomeID": testTome.ID, - }, - }, - } - data, err := json.Marshal(req) - require.NoError(t, err) - _, err = ts.Client().Post(testURL(ts, "graphql"), "application/json", bytes.NewBuffer(data)) - require.NoError(t, err) - - testQuest, err := graph.Quest.Query(). - Where(quest.Name("unit test")). - Only(ctx) - require.NoError(t, err) - createdQuestID = testQuest.ID - - testBundle, err := testQuest.Bundle(ctx) - require.NoError(t, err) - require.NotNil(t, testBundle) - createdBundleID = testBundle.ID - - testTasks, err := testQuest.Tasks(ctx) - require.NoError(t, err) - require.NotEmpty(t, testTasks) - assert.Len(t, testTasks, 1) - createdTaskID = testTasks[0].ID - - testTaskBeacon, err := testTasks[0].Beacon(ctx) - require.NoError(t, err) - assert.Equal(t, testBeacon.ID, testTaskBeacon.ID) - }) - - // Beacon Second Callback - t.Run("BeaconSecondCallback", func(t *testing.T) { - _, resp := beaconCallback(t, firstBeaconIdentifier) - require.NotEmpty(t, resp.Data.ClaimTasks) - assert.Len(t, resp.Data.ClaimTasks, 1) - - questID := convertID(resp.Data.ClaimTasks[0].Quest.ID) - assert.Equal(t, createdQuestID, questID) - - taskID := convertID(resp.Data.ClaimTasks[0].ID) - assert.Equal(t, createdTaskID, taskID) - - tomeID := convertID(resp.Data.ClaimTasks[0].Quest.Tome.ID) - assert.Equal(t, defaultTomeID, tomeID) - - bundleID := convertID(resp.Data.ClaimTasks[0].Quest.Bundle.ID) - assert.Equal(t, createdBundleID, bundleID) - }) - -} - -type graphQLQuery struct { - OperationName string `json:"operationName"` - Query string `json:"query"` - Variables map[string]any `json:"variables"` -} - -type claimTasksResponse struct { - Data struct { - ClaimTasks []struct { - ID string `json:"id"` - Quest struct { - ID string `json:"id"` - Tome struct { - ID string `json:"id"` - } `json:"tome"` - Bundle struct { - ID string `json:"id"` - } `json:"bundle"` - } `json:"quest"` - } `json:"claimTasks"` - } `json:"data"` -} - -func testURL(ts *httptest.Server, endpoint string) string { - return fmt.Sprintf("%s/%s", ts.URL, endpoint) -} - -// convertID turns a GraphQL string ID response into an int. -func convertID(id string) int { - if id == "" { - return 0 - } - intID, err := strconv.Atoi(id) - if err != nil { - panic(err) - } - return intID -} diff --git a/tavern/internal/c2/c2test/ent.go b/tavern/internal/c2/c2test/ent.go index d1caaf0c8..652b441b4 100644 --- a/tavern/internal/c2/c2test/ent.go +++ b/tavern/internal/c2/c2test/ent.go @@ -98,6 +98,7 @@ func NewRandomAssignedTask(ctx context.Context, graph *ent.Client, beaconIdentif SetName(namegen.NewComplex()). SetEldritch(fmt.Sprintf(`print("%s")`, namegen.NewComplex())). SetDescription(string(newRandomBytes(120))). + SetAuthor("kcarretto"). SetParamDefs(`[{"name":"test-param","label":"Test","type":"string","placeholder":"Enter text..."}]`). AddFiles(files...). SaveX(ctx) diff --git a/tavern/internal/ent/client.go b/tavern/internal/ent/client.go index f0d35c1b7..414bc9321 100644 --- a/tavern/internal/ent/client.go +++ b/tavern/internal/ent/client.go @@ -1579,6 +1579,22 @@ func (c *TomeClient) QueryFiles(t *Tome) *FileQuery { return query } +// QueryUploader queries the uploader edge of a Tome. +func (c *TomeClient) QueryUploader(t *Tome) *UserQuery { + query := (&UserClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := t.ID + step := sqlgraph.NewStep( + sqlgraph.From(tome.Table, tome.FieldID, id), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, tome.UploaderTable, tome.UploaderColumn), + ) + fromV = sqlgraph.Neighbors(t.driver.Dialect(), step) + return fromV, nil + } + return query +} + // Hooks returns the client hooks. func (c *TomeClient) Hooks() []Hook { hooks := c.hooks.Tome @@ -1713,6 +1729,22 @@ func (c *UserClient) GetX(ctx context.Context, id int) *User { return obj } +// QueryTomes queries the tomes edge of a User. +func (c *UserClient) QueryTomes(u *User) *TomeQuery { + query := (&TomeClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := u.ID + step := sqlgraph.NewStep( + sqlgraph.From(user.Table, user.FieldID, id), + sqlgraph.To(tome.Table, tome.FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, user.TomesTable, user.TomesColumn), + ) + fromV = sqlgraph.Neighbors(u.driver.Dialect(), step) + return fromV, nil + } + return query +} + // Hooks returns the client hooks. func (c *UserClient) Hooks() []Hook { return c.hooks.User diff --git a/tavern/internal/ent/gql_collection.go b/tavern/internal/ent/gql_collection.go index 08b1282af..ca786b974 100644 --- a/tavern/internal/ent/gql_collection.go +++ b/tavern/internal/ent/gql_collection.go @@ -997,6 +997,16 @@ func (t *TomeQuery) collectField(ctx context.Context, opCtx *graphql.OperationCo t.WithNamedFiles(alias, func(wq *FileQuery) { *wq = *query }) + case "uploader": + var ( + alias = field.Alias + path = append(path, alias) + query = (&UserClient{config: t.config}).Query() + ) + if err := query.collectField(ctx, opCtx, field, path, satisfies...); err != nil { + return err + } + t.withUploader = query case "createdAt": if _, ok := fieldSeen[tome.FieldCreatedAt]; !ok { selectedFields = append(selectedFields, tome.FieldCreatedAt) @@ -1017,6 +1027,21 @@ func (t *TomeQuery) collectField(ctx context.Context, opCtx *graphql.OperationCo selectedFields = append(selectedFields, tome.FieldDescription) fieldSeen[tome.FieldDescription] = struct{}{} } + case "author": + if _, ok := fieldSeen[tome.FieldAuthor]; !ok { + selectedFields = append(selectedFields, tome.FieldAuthor) + fieldSeen[tome.FieldAuthor] = struct{}{} + } + case "supportModel": + if _, ok := fieldSeen[tome.FieldSupportModel]; !ok { + selectedFields = append(selectedFields, tome.FieldSupportModel) + fieldSeen[tome.FieldSupportModel] = struct{}{} + } + case "tactic": + if _, ok := fieldSeen[tome.FieldTactic]; !ok { + selectedFields = append(selectedFields, tome.FieldTactic) + fieldSeen[tome.FieldTactic] = struct{}{} + } case "paramDefs": if _, ok := fieldSeen[tome.FieldParamDefs]; !ok { selectedFields = append(selectedFields, tome.FieldParamDefs) @@ -1111,6 +1136,18 @@ func (u *UserQuery) collectField(ctx context.Context, opCtx *graphql.OperationCo ) for _, field := range graphql.CollectFields(opCtx, collected.Selections, satisfies) { switch field.Name { + case "tomes": + var ( + alias = field.Alias + path = append(path, alias) + query = (&TomeClient{config: u.config}).Query() + ) + if err := query.collectField(ctx, opCtx, field, path, satisfies...); err != nil { + return err + } + u.WithNamedTomes(alias, func(wq *TomeQuery) { + *wq = *query + }) case "name": if _, ok := fieldSeen[user.FieldName]; !ok { selectedFields = append(selectedFields, user.FieldName) diff --git a/tavern/internal/ent/gql_edge.go b/tavern/internal/ent/gql_edge.go index 311a30c75..d9218ced7 100644 --- a/tavern/internal/ent/gql_edge.go +++ b/tavern/internal/ent/gql_edge.go @@ -179,3 +179,23 @@ func (t *Tome) Files(ctx context.Context) (result []*File, err error) { } return result, err } + +func (t *Tome) Uploader(ctx context.Context) (*User, error) { + result, err := t.Edges.UploaderOrErr() + if IsNotLoaded(err) { + result, err = t.QueryUploader().Only(ctx) + } + return result, MaskNotFound(err) +} + +func (u *User) Tomes(ctx context.Context) (result []*Tome, err error) { + if fc := graphql.GetFieldContext(ctx); fc != nil && fc.Field.Alias != "" { + result, err = u.NamedTomes(graphql.GetFieldContext(ctx).Field.Alias) + } else { + result, err = u.Edges.TomesOrErr() + } + if IsNotLoaded(err) { + result, err = u.QueryTomes().All(ctx) + } + return result, err +} diff --git a/tavern/internal/ent/gql_mutation_input.go b/tavern/internal/ent/gql_mutation_input.go index 85e45c3e7..ac9bc1517 100644 --- a/tavern/internal/ent/gql_mutation_input.go +++ b/tavern/internal/ent/gql_mutation_input.go @@ -3,7 +3,10 @@ package ent import ( + "time" + "realm.pub/tavern/internal/ent/tag" + "realm.pub/tavern/internal/ent/tome" ) // UpdateBeaconInput represents a mutation input for updating beacons. @@ -180,17 +183,27 @@ func (c *TagUpdateOne) SetInput(i UpdateTagInput) *TagUpdateOne { // CreateTomeInput represents a mutation input for creating tomes. type CreateTomeInput struct { - Name string - Description string - ParamDefs *string - Eldritch string - FileIDs []int + Name string + Description string + Author string + SupportModel *tome.SupportModel + Tactic *tome.Tactic + ParamDefs *string + Eldritch string + FileIDs []int } // Mutate applies the CreateTomeInput on the TomeMutation builder. func (i *CreateTomeInput) Mutate(m *TomeMutation) { m.SetName(i.Name) m.SetDescription(i.Description) + m.SetAuthor(i.Author) + if v := i.SupportModel; v != nil { + m.SetSupportModel(*v) + } + if v := i.Tactic; v != nil { + m.SetTactic(*v) + } if v := i.ParamDefs; v != nil { m.SetParamDefs(*v) } @@ -206,12 +219,83 @@ func (c *TomeCreate) SetInput(i CreateTomeInput) *TomeCreate { return c } +// UpdateTomeInput represents a mutation input for updating tomes. +type UpdateTomeInput struct { + LastModifiedAt *time.Time + Name *string + Description *string + Author *string + SupportModel *tome.SupportModel + Tactic *tome.Tactic + ClearParamDefs bool + ParamDefs *string + Eldritch *string + ClearFiles bool + AddFileIDs []int + RemoveFileIDs []int +} + +// Mutate applies the UpdateTomeInput on the TomeMutation builder. +func (i *UpdateTomeInput) Mutate(m *TomeMutation) { + if v := i.LastModifiedAt; v != nil { + m.SetLastModifiedAt(*v) + } + if v := i.Name; v != nil { + m.SetName(*v) + } + if v := i.Description; v != nil { + m.SetDescription(*v) + } + if v := i.Author; v != nil { + m.SetAuthor(*v) + } + if v := i.SupportModel; v != nil { + m.SetSupportModel(*v) + } + if v := i.Tactic; v != nil { + m.SetTactic(*v) + } + if i.ClearParamDefs { + m.ClearParamDefs() + } + if v := i.ParamDefs; v != nil { + m.SetParamDefs(*v) + } + if v := i.Eldritch; v != nil { + m.SetEldritch(*v) + } + if i.ClearFiles { + m.ClearFiles() + } + if v := i.AddFileIDs; len(v) > 0 { + m.AddFileIDs(v...) + } + if v := i.RemoveFileIDs; len(v) > 0 { + m.RemoveFileIDs(v...) + } +} + +// SetInput applies the change-set in the UpdateTomeInput on the TomeUpdate builder. +func (c *TomeUpdate) SetInput(i UpdateTomeInput) *TomeUpdate { + i.Mutate(c.Mutation()) + return c +} + +// SetInput applies the change-set in the UpdateTomeInput on the TomeUpdateOne builder. +func (c *TomeUpdateOne) SetInput(i UpdateTomeInput) *TomeUpdateOne { + i.Mutate(c.Mutation()) + return c +} + // UpdateUserInput represents a mutation input for updating users. type UpdateUserInput struct { - Name *string - PhotoURL *string - IsActivated *bool - IsAdmin *bool + Name *string + PhotoURL *string + IsActivated *bool + IsAdmin *bool + ClearTomes bool + AddTomeIDs []int + RemoveTomeIDs []int } // Mutate applies the UpdateUserInput on the UserMutation builder. @@ -228,6 +312,15 @@ func (i *UpdateUserInput) Mutate(m *UserMutation) { if v := i.IsAdmin; v != nil { m.SetIsAdmin(*v) } + if i.ClearTomes { + m.ClearTomes() + } + if v := i.AddTomeIDs; len(v) > 0 { + m.AddTomeIDs(v...) + } + if v := i.RemoveTomeIDs; len(v) > 0 { + m.RemoveTomeIDs(v...) + } } // SetInput applies the change-set in the UpdateUserInput on the UserUpdate builder. diff --git a/tavern/internal/ent/gql_where_input.go b/tavern/internal/ent/gql_where_input.go index 03d381f4d..cad442d9e 100644 --- a/tavern/internal/ent/gql_where_input.go +++ b/tavern/internal/ent/gql_where_input.go @@ -2888,6 +2888,33 @@ type TomeWhereInput struct { DescriptionEqualFold *string `json:"descriptionEqualFold,omitempty"` DescriptionContainsFold *string `json:"descriptionContainsFold,omitempty"` + // "author" field predicates. + Author *string `json:"author,omitempty"` + AuthorNEQ *string `json:"authorNEQ,omitempty"` + AuthorIn []string `json:"authorIn,omitempty"` + AuthorNotIn []string `json:"authorNotIn,omitempty"` + AuthorGT *string `json:"authorGT,omitempty"` + AuthorGTE *string `json:"authorGTE,omitempty"` + AuthorLT *string `json:"authorLT,omitempty"` + AuthorLTE *string `json:"authorLTE,omitempty"` + AuthorContains *string `json:"authorContains,omitempty"` + AuthorHasPrefix *string `json:"authorHasPrefix,omitempty"` + AuthorHasSuffix *string `json:"authorHasSuffix,omitempty"` + AuthorEqualFold *string `json:"authorEqualFold,omitempty"` + AuthorContainsFold *string `json:"authorContainsFold,omitempty"` + + // "support_model" field predicates. + SupportModel *tome.SupportModel `json:"supportModel,omitempty"` + SupportModelNEQ *tome.SupportModel `json:"supportModelNEQ,omitempty"` + SupportModelIn []tome.SupportModel `json:"supportModelIn,omitempty"` + SupportModelNotIn []tome.SupportModel `json:"supportModelNotIn,omitempty"` + + // "tactic" field predicates. + Tactic *tome.Tactic `json:"tactic,omitempty"` + TacticNEQ *tome.Tactic `json:"tacticNEQ,omitempty"` + TacticIn []tome.Tactic `json:"tacticIn,omitempty"` + TacticNotIn []tome.Tactic `json:"tacticNotIn,omitempty"` + // "param_defs" field predicates. ParamDefs *string `json:"paramDefs,omitempty"` ParamDefsNEQ *string `json:"paramDefsNEQ,omitempty"` @@ -2923,6 +2950,10 @@ type TomeWhereInput struct { // "files" edge predicates. HasFiles *bool `json:"hasFiles,omitempty"` HasFilesWith []*FileWhereInput `json:"hasFilesWith,omitempty"` + + // "uploader" edge predicates. + HasUploader *bool `json:"hasUploader,omitempty"` + HasUploaderWith []*UserWhereInput `json:"hasUploaderWith,omitempty"` } // AddPredicates adds custom predicates to the where input to be used during the filtering phase. @@ -3146,6 +3177,69 @@ func (i *TomeWhereInput) P() (predicate.Tome, error) { if i.DescriptionContainsFold != nil { predicates = append(predicates, tome.DescriptionContainsFold(*i.DescriptionContainsFold)) } + if i.Author != nil { + predicates = append(predicates, tome.AuthorEQ(*i.Author)) + } + if i.AuthorNEQ != nil { + predicates = append(predicates, tome.AuthorNEQ(*i.AuthorNEQ)) + } + if len(i.AuthorIn) > 0 { + predicates = append(predicates, tome.AuthorIn(i.AuthorIn...)) + } + if len(i.AuthorNotIn) > 0 { + predicates = append(predicates, tome.AuthorNotIn(i.AuthorNotIn...)) + } + if i.AuthorGT != nil { + predicates = append(predicates, tome.AuthorGT(*i.AuthorGT)) + } + if i.AuthorGTE != nil { + predicates = append(predicates, tome.AuthorGTE(*i.AuthorGTE)) + } + if i.AuthorLT != nil { + predicates = append(predicates, tome.AuthorLT(*i.AuthorLT)) + } + if i.AuthorLTE != nil { + predicates = append(predicates, tome.AuthorLTE(*i.AuthorLTE)) + } + if i.AuthorContains != nil { + predicates = append(predicates, tome.AuthorContains(*i.AuthorContains)) + } + if i.AuthorHasPrefix != nil { + predicates = append(predicates, tome.AuthorHasPrefix(*i.AuthorHasPrefix)) + } + if i.AuthorHasSuffix != nil { + predicates = append(predicates, tome.AuthorHasSuffix(*i.AuthorHasSuffix)) + } + if i.AuthorEqualFold != nil { + predicates = append(predicates, tome.AuthorEqualFold(*i.AuthorEqualFold)) + } + if i.AuthorContainsFold != nil { + predicates = append(predicates, tome.AuthorContainsFold(*i.AuthorContainsFold)) + } + if i.SupportModel != nil { + predicates = append(predicates, tome.SupportModelEQ(*i.SupportModel)) + } + if i.SupportModelNEQ != nil { + predicates = append(predicates, tome.SupportModelNEQ(*i.SupportModelNEQ)) + } + if len(i.SupportModelIn) > 0 { + predicates = append(predicates, tome.SupportModelIn(i.SupportModelIn...)) + } + if len(i.SupportModelNotIn) > 0 { + predicates = append(predicates, tome.SupportModelNotIn(i.SupportModelNotIn...)) + } + if i.Tactic != nil { + predicates = append(predicates, tome.TacticEQ(*i.Tactic)) + } + if i.TacticNEQ != nil { + predicates = append(predicates, tome.TacticNEQ(*i.TacticNEQ)) + } + if len(i.TacticIn) > 0 { + predicates = append(predicates, tome.TacticIn(i.TacticIn...)) + } + if len(i.TacticNotIn) > 0 { + predicates = append(predicates, tome.TacticNotIn(i.TacticNotIn...)) + } if i.ParamDefs != nil { predicates = append(predicates, tome.ParamDefsEQ(*i.ParamDefs)) } @@ -3249,6 +3343,24 @@ func (i *TomeWhereInput) P() (predicate.Tome, error) { } predicates = append(predicates, tome.HasFilesWith(with...)) } + if i.HasUploader != nil { + p := tome.HasUploader() + if !*i.HasUploader { + p = tome.Not(p) + } + predicates = append(predicates, p) + } + if len(i.HasUploaderWith) > 0 { + with := make([]predicate.User, 0, len(i.HasUploaderWith)) + for _, w := range i.HasUploaderWith { + p, err := w.P() + if err != nil { + return nil, fmt.Errorf("%w: field 'HasUploaderWith'", err) + } + with = append(with, p) + } + predicates = append(predicates, tome.HasUploaderWith(with...)) + } switch len(predicates) { case 0: return nil, ErrEmptyTomeWhereInput @@ -3328,6 +3440,10 @@ type UserWhereInput struct { // "is_admin" field predicates. IsAdmin *bool `json:"isAdmin,omitempty"` IsAdminNEQ *bool `json:"isAdminNEQ,omitempty"` + + // "tomes" edge predicates. + HasTomes *bool `json:"hasTomes,omitempty"` + HasTomesWith []*TomeWhereInput `json:"hasTomesWith,omitempty"` } // AddPredicates adds custom predicates to the where input to be used during the filtering phase. @@ -3555,6 +3671,24 @@ func (i *UserWhereInput) P() (predicate.User, error) { predicates = append(predicates, user.IsAdminNEQ(*i.IsAdminNEQ)) } + if i.HasTomes != nil { + p := user.HasTomes() + if !*i.HasTomes { + p = user.Not(p) + } + predicates = append(predicates, p) + } + if len(i.HasTomesWith) > 0 { + with := make([]predicate.Tome, 0, len(i.HasTomesWith)) + for _, w := range i.HasTomesWith { + p, err := w.P() + if err != nil { + return nil, fmt.Errorf("%w: field 'HasTomesWith'", err) + } + with = append(with, p) + } + predicates = append(predicates, user.HasTomesWith(with...)) + } switch len(predicates) { case 0: return nil, ErrEmptyUserWhereInput diff --git a/tavern/internal/ent/migrate/schema.go b/tavern/internal/ent/migrate/schema.go index 4cb1fff0c..e168e12e4 100644 --- a/tavern/internal/ent/migrate/schema.go +++ b/tavern/internal/ent/migrate/schema.go @@ -192,15 +192,27 @@ var ( {Name: "last_modified_at", Type: field.TypeTime}, {Name: "name", Type: field.TypeString, Unique: true}, {Name: "description", Type: field.TypeString}, + {Name: "author", Type: field.TypeString}, + {Name: "support_model", Type: field.TypeEnum, Enums: []string{"UNSPECIFIED", "FIRST_PARTY", "COMMUNITY"}, Default: "UNSPECIFIED"}, + {Name: "tactic", Type: field.TypeEnum, Enums: []string{"UNSPECIFIED", "RECON", "RESOURCE_DEVELOPMENT", "INITIAL_ACCESS", "EXECUTION", "PERSISTENCE", "PRIVILEGE_ESCALATION", "DEFENSE_EVASION", "CREDENTIAL_ACCESS", "DISCOVERY", "LATERAL_MOVEMENT", "COLLECTION", "COMMAND_AND_CONTROL", "EXFILTRATION", "IMPACT"}, Default: "UNSPECIFIED"}, {Name: "param_defs", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"mysql": "LONGTEXT"}}, {Name: "hash", Type: field.TypeString, Size: 100}, {Name: "eldritch", Type: field.TypeString, SchemaType: map[string]string{"mysql": "LONGTEXT"}}, + {Name: "tome_uploader", Type: field.TypeInt, Nullable: true}, } // TomesTable holds the schema information for the "tomes" table. TomesTable = &schema.Table{ Name: "tomes", Columns: TomesColumns, PrimaryKey: []*schema.Column{TomesColumns[0]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "tomes_users_uploader", + Columns: []*schema.Column{TomesColumns[11]}, + RefColumns: []*schema.Column{UsersColumns[0]}, + OnDelete: schema.SetNull, + }, + }, } // UsersColumns holds the columns for the "users" table. UsersColumns = []*schema.Column{ @@ -294,6 +306,7 @@ func init() { QuestsTable.ForeignKeys[2].RefTable = UsersTable TasksTable.ForeignKeys[0].RefTable = QuestsTable TasksTable.ForeignKeys[1].RefTable = BeaconsTable + TomesTable.ForeignKeys[0].RefTable = UsersTable HostTagsTable.ForeignKeys[0].RefTable = HostsTable HostTagsTable.ForeignKeys[1].RefTable = TagsTable TomeFilesTable.ForeignKeys[0].RefTable = TomesTable diff --git a/tavern/internal/ent/mutation.go b/tavern/internal/ent/mutation.go index 14059f6b4..9794de318 100644 --- a/tavern/internal/ent/mutation.go +++ b/tavern/internal/ent/mutation.go @@ -5509,6 +5509,9 @@ type TomeMutation struct { last_modified_at *time.Time name *string description *string + author *string + support_model *tome.SupportModel + tactic *tome.Tactic param_defs *string hash *string eldritch *string @@ -5516,6 +5519,8 @@ type TomeMutation struct { files map[int]struct{} removedfiles map[int]struct{} clearedfiles bool + uploader *int + cleareduploader bool done bool oldValue func(context.Context) (*Tome, error) predicates []predicate.Tome @@ -5763,6 +5768,114 @@ func (m *TomeMutation) ResetDescription() { m.description = nil } +// SetAuthor sets the "author" field. +func (m *TomeMutation) SetAuthor(s string) { + m.author = &s +} + +// Author returns the value of the "author" field in the mutation. +func (m *TomeMutation) Author() (r string, exists bool) { + v := m.author + if v == nil { + return + } + return *v, true +} + +// OldAuthor returns the old "author" field's value of the Tome entity. +// If the Tome 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 *TomeMutation) OldAuthor(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAuthor is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAuthor requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAuthor: %w", err) + } + return oldValue.Author, nil +} + +// ResetAuthor resets all changes to the "author" field. +func (m *TomeMutation) ResetAuthor() { + m.author = nil +} + +// SetSupportModel sets the "support_model" field. +func (m *TomeMutation) SetSupportModel(tm tome.SupportModel) { + m.support_model = &tm +} + +// SupportModel returns the value of the "support_model" field in the mutation. +func (m *TomeMutation) SupportModel() (r tome.SupportModel, exists bool) { + v := m.support_model + if v == nil { + return + } + return *v, true +} + +// OldSupportModel returns the old "support_model" field's value of the Tome entity. +// If the Tome 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 *TomeMutation) OldSupportModel(ctx context.Context) (v tome.SupportModel, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSupportModel is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSupportModel requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSupportModel: %w", err) + } + return oldValue.SupportModel, nil +} + +// ResetSupportModel resets all changes to the "support_model" field. +func (m *TomeMutation) ResetSupportModel() { + m.support_model = nil +} + +// SetTactic sets the "tactic" field. +func (m *TomeMutation) SetTactic(t tome.Tactic) { + m.tactic = &t +} + +// Tactic returns the value of the "tactic" field in the mutation. +func (m *TomeMutation) Tactic() (r tome.Tactic, exists bool) { + v := m.tactic + if v == nil { + return + } + return *v, true +} + +// OldTactic returns the old "tactic" field's value of the Tome entity. +// If the Tome 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 *TomeMutation) OldTactic(ctx context.Context) (v tome.Tactic, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldTactic is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldTactic requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldTactic: %w", err) + } + return oldValue.Tactic, nil +} + +// ResetTactic resets all changes to the "tactic" field. +func (m *TomeMutation) ResetTactic() { + m.tactic = nil +} + // SetParamDefs sets the "param_defs" field. func (m *TomeMutation) SetParamDefs(s string) { m.param_defs = &s @@ -5938,6 +6051,45 @@ func (m *TomeMutation) ResetFiles() { m.removedfiles = nil } +// SetUploaderID sets the "uploader" edge to the User entity by id. +func (m *TomeMutation) SetUploaderID(id int) { + m.uploader = &id +} + +// ClearUploader clears the "uploader" edge to the User entity. +func (m *TomeMutation) ClearUploader() { + m.cleareduploader = true +} + +// UploaderCleared reports if the "uploader" edge to the User entity was cleared. +func (m *TomeMutation) UploaderCleared() bool { + return m.cleareduploader +} + +// UploaderID returns the "uploader" edge ID in the mutation. +func (m *TomeMutation) UploaderID() (id int, exists bool) { + if m.uploader != nil { + return *m.uploader, true + } + return +} + +// UploaderIDs returns the "uploader" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// UploaderID instead. It exists only for internal usage by the builders. +func (m *TomeMutation) UploaderIDs() (ids []int) { + if id := m.uploader; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetUploader resets all changes to the "uploader" edge. +func (m *TomeMutation) ResetUploader() { + m.uploader = nil + m.cleareduploader = false +} + // Where appends a list predicates to the TomeMutation builder. func (m *TomeMutation) Where(ps ...predicate.Tome) { m.predicates = append(m.predicates, ps...) @@ -5972,7 +6124,7 @@ func (m *TomeMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *TomeMutation) Fields() []string { - fields := make([]string, 0, 7) + fields := make([]string, 0, 10) if m.created_at != nil { fields = append(fields, tome.FieldCreatedAt) } @@ -5985,6 +6137,15 @@ func (m *TomeMutation) Fields() []string { if m.description != nil { fields = append(fields, tome.FieldDescription) } + if m.author != nil { + fields = append(fields, tome.FieldAuthor) + } + if m.support_model != nil { + fields = append(fields, tome.FieldSupportModel) + } + if m.tactic != nil { + fields = append(fields, tome.FieldTactic) + } if m.param_defs != nil { fields = append(fields, tome.FieldParamDefs) } @@ -6010,6 +6171,12 @@ func (m *TomeMutation) Field(name string) (ent.Value, bool) { return m.Name() case tome.FieldDescription: return m.Description() + case tome.FieldAuthor: + return m.Author() + case tome.FieldSupportModel: + return m.SupportModel() + case tome.FieldTactic: + return m.Tactic() case tome.FieldParamDefs: return m.ParamDefs() case tome.FieldHash: @@ -6033,6 +6200,12 @@ func (m *TomeMutation) OldField(ctx context.Context, name string) (ent.Value, er return m.OldName(ctx) case tome.FieldDescription: return m.OldDescription(ctx) + case tome.FieldAuthor: + return m.OldAuthor(ctx) + case tome.FieldSupportModel: + return m.OldSupportModel(ctx) + case tome.FieldTactic: + return m.OldTactic(ctx) case tome.FieldParamDefs: return m.OldParamDefs(ctx) case tome.FieldHash: @@ -6076,6 +6249,27 @@ func (m *TomeMutation) SetField(name string, value ent.Value) error { } m.SetDescription(v) return nil + case tome.FieldAuthor: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAuthor(v) + return nil + case tome.FieldSupportModel: + v, ok := value.(tome.SupportModel) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSupportModel(v) + return nil + case tome.FieldTactic: + v, ok := value.(tome.Tactic) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetTactic(v) + return nil case tome.FieldParamDefs: v, ok := value.(string) if !ok { @@ -6167,6 +6361,15 @@ func (m *TomeMutation) ResetField(name string) error { case tome.FieldDescription: m.ResetDescription() return nil + case tome.FieldAuthor: + m.ResetAuthor() + return nil + case tome.FieldSupportModel: + m.ResetSupportModel() + return nil + case tome.FieldTactic: + m.ResetTactic() + return nil case tome.FieldParamDefs: m.ResetParamDefs() return nil @@ -6182,10 +6385,13 @@ func (m *TomeMutation) ResetField(name string) error { // AddedEdges returns all edge names that were set/added in this mutation. func (m *TomeMutation) AddedEdges() []string { - edges := make([]string, 0, 1) + edges := make([]string, 0, 2) if m.files != nil { edges = append(edges, tome.EdgeFiles) } + if m.uploader != nil { + edges = append(edges, tome.EdgeUploader) + } return edges } @@ -6199,13 +6405,17 @@ func (m *TomeMutation) AddedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case tome.EdgeUploader: + if id := m.uploader; id != nil { + return []ent.Value{*id} + } } return nil } // RemovedEdges returns all edge names that were removed in this mutation. func (m *TomeMutation) RemovedEdges() []string { - edges := make([]string, 0, 1) + edges := make([]string, 0, 2) if m.removedfiles != nil { edges = append(edges, tome.EdgeFiles) } @@ -6228,10 +6438,13 @@ func (m *TomeMutation) RemovedIDs(name string) []ent.Value { // ClearedEdges returns all edge names that were cleared in this mutation. func (m *TomeMutation) ClearedEdges() []string { - edges := make([]string, 0, 1) + edges := make([]string, 0, 2) if m.clearedfiles { edges = append(edges, tome.EdgeFiles) } + if m.cleareduploader { + edges = append(edges, tome.EdgeUploader) + } return edges } @@ -6241,6 +6454,8 @@ func (m *TomeMutation) EdgeCleared(name string) bool { switch name { case tome.EdgeFiles: return m.clearedfiles + case tome.EdgeUploader: + return m.cleareduploader } return false } @@ -6249,6 +6464,9 @@ func (m *TomeMutation) EdgeCleared(name string) bool { // if that edge is not defined in the schema. func (m *TomeMutation) ClearEdge(name string) error { switch name { + case tome.EdgeUploader: + m.ClearUploader() + return nil } return fmt.Errorf("unknown Tome unique edge %s", name) } @@ -6260,6 +6478,9 @@ func (m *TomeMutation) ResetEdge(name string) error { case tome.EdgeFiles: m.ResetFiles() return nil + case tome.EdgeUploader: + m.ResetUploader() + return nil } return fmt.Errorf("unknown Tome edge %s", name) } @@ -6277,6 +6498,9 @@ type UserMutation struct { is_activated *bool is_admin *bool clearedFields map[string]struct{} + tomes map[int]struct{} + removedtomes map[int]struct{} + clearedtomes bool done bool oldValue func(context.Context) (*User, error) predicates []predicate.User @@ -6596,6 +6820,60 @@ func (m *UserMutation) ResetIsAdmin() { m.is_admin = nil } +// AddTomeIDs adds the "tomes" edge to the Tome entity by ids. +func (m *UserMutation) AddTomeIDs(ids ...int) { + if m.tomes == nil { + m.tomes = make(map[int]struct{}) + } + for i := range ids { + m.tomes[ids[i]] = struct{}{} + } +} + +// ClearTomes clears the "tomes" edge to the Tome entity. +func (m *UserMutation) ClearTomes() { + m.clearedtomes = true +} + +// TomesCleared reports if the "tomes" edge to the Tome entity was cleared. +func (m *UserMutation) TomesCleared() bool { + return m.clearedtomes +} + +// RemoveTomeIDs removes the "tomes" edge to the Tome entity by IDs. +func (m *UserMutation) RemoveTomeIDs(ids ...int) { + if m.removedtomes == nil { + m.removedtomes = make(map[int]struct{}) + } + for i := range ids { + delete(m.tomes, ids[i]) + m.removedtomes[ids[i]] = struct{}{} + } +} + +// RemovedTomes returns the removed IDs of the "tomes" edge to the Tome entity. +func (m *UserMutation) RemovedTomesIDs() (ids []int) { + for id := range m.removedtomes { + ids = append(ids, id) + } + return +} + +// TomesIDs returns the "tomes" edge IDs in the mutation. +func (m *UserMutation) TomesIDs() (ids []int) { + for id := range m.tomes { + ids = append(ids, id) + } + return +} + +// ResetTomes resets all changes to the "tomes" edge. +func (m *UserMutation) ResetTomes() { + m.tomes = nil + m.clearedtomes = false + m.removedtomes = nil +} + // Where appends a list predicates to the UserMutation builder. func (m *UserMutation) Where(ps ...predicate.User) { m.predicates = append(m.predicates, ps...) @@ -6814,48 +7092,84 @@ func (m *UserMutation) ResetField(name string) error { // AddedEdges returns all edge names that were set/added in this mutation. func (m *UserMutation) AddedEdges() []string { - edges := make([]string, 0, 0) + edges := make([]string, 0, 1) + if m.tomes != nil { + edges = append(edges, user.EdgeTomes) + } return edges } // AddedIDs returns all IDs (to other nodes) that were added for the given edge // name in this mutation. func (m *UserMutation) AddedIDs(name string) []ent.Value { + switch name { + case user.EdgeTomes: + ids := make([]ent.Value, 0, len(m.tomes)) + for id := range m.tomes { + ids = append(ids, id) + } + return ids + } return nil } // RemovedEdges returns all edge names that were removed in this mutation. func (m *UserMutation) RemovedEdges() []string { - edges := make([]string, 0, 0) + edges := make([]string, 0, 1) + if m.removedtomes != nil { + edges = append(edges, user.EdgeTomes) + } return edges } // RemovedIDs returns all IDs (to other nodes) that were removed for the edge with // the given name in this mutation. func (m *UserMutation) RemovedIDs(name string) []ent.Value { + switch name { + case user.EdgeTomes: + ids := make([]ent.Value, 0, len(m.removedtomes)) + for id := range m.removedtomes { + ids = append(ids, id) + } + return ids + } return nil } // ClearedEdges returns all edge names that were cleared in this mutation. func (m *UserMutation) ClearedEdges() []string { - edges := make([]string, 0, 0) + edges := make([]string, 0, 1) + if m.clearedtomes { + edges = append(edges, user.EdgeTomes) + } return edges } // EdgeCleared returns a boolean which indicates if the edge with the given name // was cleared in this mutation. func (m *UserMutation) EdgeCleared(name string) bool { + switch name { + case user.EdgeTomes: + return m.clearedtomes + } return false } // ClearEdge clears the value of the edge with the given name. It returns an error // if that edge is not defined in the schema. func (m *UserMutation) ClearEdge(name string) error { + switch name { + } return fmt.Errorf("unknown User unique edge %s", name) } // ResetEdge resets all changes to the edge with the given name in this mutation. // It returns an error if the edge is not defined in the schema. func (m *UserMutation) ResetEdge(name string) error { + switch name { + case user.EdgeTomes: + m.ResetTomes() + return nil + } return fmt.Errorf("unknown User edge %s", name) } diff --git a/tavern/internal/ent/runtime/runtime.go b/tavern/internal/ent/runtime/runtime.go index 3190980f6..f80fa829c 100644 --- a/tavern/internal/ent/runtime/runtime.go +++ b/tavern/internal/ent/runtime/runtime.go @@ -177,11 +177,11 @@ func init() { // tome.NameValidator is a validator for the "name" field. It is called by the builders before save. tome.NameValidator = tomeDescName.Validators[0].(func(string) error) // tomeDescParamDefs is the schema descriptor for param_defs field. - tomeDescParamDefs := tomeFields[2].Descriptor() + tomeDescParamDefs := tomeFields[5].Descriptor() // tome.ParamDefsValidator is a validator for the "param_defs" field. It is called by the builders before save. tome.ParamDefsValidator = tomeDescParamDefs.Validators[0].(func(string) error) // tomeDescHash is the schema descriptor for hash field. - tomeDescHash := tomeFields[3].Descriptor() + tomeDescHash := tomeFields[6].Descriptor() // tome.HashValidator is a validator for the "hash" field. It is called by the builders before save. tome.HashValidator = tomeDescHash.Validators[0].(func(string) error) userFields := schema.User{}.Fields() diff --git a/tavern/internal/ent/schema/file_test.go b/tavern/internal/ent/schema/file_test.go index 7c8a3bf19..7d30f728b 100644 --- a/tavern/internal/ent/schema/file_test.go +++ b/tavern/internal/ent/schema/file_test.go @@ -47,12 +47,14 @@ func TestMultipleTomes(t *testing.T) { SetName("TestTome001"). SetEldritch(`print("hello world")`). SetDescription("Hello World"). + SetAuthor("kcarretto"). AddFiles(files...). SaveX(ctx) graph.Tome.Create(). SetName("TestTome002"). SetEldritch(`print("hello world")`). SetDescription("Hello World"). + SetAuthor("kcarretto"). AddFiles(files...). SaveX(ctx) } diff --git a/tavern/internal/ent/schema/tome.go b/tavern/internal/ent/schema/tome.go index a5758f187..0b455950a 100644 --- a/tavern/internal/ent/schema/tome.go +++ b/tavern/internal/ent/schema/tome.go @@ -32,6 +32,32 @@ func (Tome) Fields() []ent.Field { Comment("Name of the tome"), field.String("description"). Comment("Information about the tome"), + field.String("author"). + Comment("Name of the author who created the tome."), + field.Enum("support_model"). + Values("UNSPECIFIED", "FIRST_PARTY", "COMMUNITY"). + Default("UNSPECIFIED"). + Comment("Information about the tomes support model."), + field.Enum("tactic"). + Values( + "UNSPECIFIED", + "RECON", + "RESOURCE_DEVELOPMENT", + "INITIAL_ACCESS", + "EXECUTION", + "PERSISTENCE", + "PRIVILEGE_ESCALATION", + "DEFENSE_EVASION", + "CREDENTIAL_ACCESS", + "DISCOVERY", + "LATERAL_MOVEMENT", + "COLLECTION", + "COMMAND_AND_CONTROL", + "EXFILTRATION", + "IMPACT", + ). + Default("UNSPECIFIED"). + Comment("MITRE ATT&CK tactic provided by the tome."), field.String("param_defs"). Validate(validators.NewTomeParameterDefinitions()). Optional(). @@ -58,6 +84,12 @@ func (Tome) Edges() []ent.Edge { return []ent.Edge{ edge.To("files", File.Type). Comment("Any files required for tome execution that will be bundled and provided to the agent for download"), + edge.To("uploader", User.Type). + Unique(). + Annotations( + entgql.Skip(entgql.SkipMutationCreateInput, entgql.SkipMutationUpdateInput), + ). + Comment("User who uploaded the tome (may be null)."), } } @@ -66,6 +98,7 @@ func (Tome) Annotations() []schema.Annotation { return []schema.Annotation{ entgql.Mutations( entgql.MutationCreate(), + entgql.MutationUpdate(), ), } } diff --git a/tavern/internal/ent/schema/user.go b/tavern/internal/ent/schema/user.go index a02cfd395..5f944d13c 100644 --- a/tavern/internal/ent/schema/user.go +++ b/tavern/internal/ent/schema/user.go @@ -10,6 +10,7 @@ import ( "entgo.io/ent" "entgo.io/ent/dialect" "entgo.io/ent/schema" + "entgo.io/ent/schema/edge" "entgo.io/ent/schema/field" ) @@ -54,7 +55,11 @@ func (User) Fields() []ent.Field { // Edges of the User. func (User) Edges() []ent.Edge { - return nil + return []ent.Edge{ + edge.From("tomes", Tome.Type). + Ref("uploader"). + Comment("Tomes uploaded by the user."), + } } // Annotations describes additional information for the ent. diff --git a/tavern/internal/ent/tome.go b/tavern/internal/ent/tome.go index 54f362f43..dfba08795 100644 --- a/tavern/internal/ent/tome.go +++ b/tavern/internal/ent/tome.go @@ -10,6 +10,7 @@ import ( "entgo.io/ent" "entgo.io/ent/dialect/sql" "realm.pub/tavern/internal/ent/tome" + "realm.pub/tavern/internal/ent/user" ) // Tome is the model entity for the Tome schema. @@ -25,6 +26,12 @@ type Tome struct { Name string `json:"name,omitempty"` // Information about the tome Description string `json:"description,omitempty"` + // Name of the author who created the tome. + Author string `json:"author,omitempty"` + // Information about the tomes support model. + SupportModel tome.SupportModel `json:"support_model,omitempty"` + // MITRE ATT&CK tactic provided by the tome. + Tactic tome.Tactic `json:"tactic,omitempty"` // JSON string describing what parameters are used with the tome. Requires a list of JSON objects, one for each parameter. ParamDefs string `json:"param_defs,omitempty"` // A SHA3 digest of the eldritch field @@ -33,19 +40,22 @@ type Tome struct { Eldritch string `json:"eldritch,omitempty"` // Edges holds the relations/edges for other nodes in the graph. // The values are being populated by the TomeQuery when eager-loading is set. - Edges TomeEdges `json:"edges"` - selectValues sql.SelectValues + Edges TomeEdges `json:"edges"` + tome_uploader *int + selectValues sql.SelectValues } // TomeEdges holds the relations/edges for other nodes in the graph. type TomeEdges struct { // Any files required for tome execution that will be bundled and provided to the agent for download Files []*File `json:"files,omitempty"` + // User who uploaded the tome (may be null). + Uploader *User `json:"uploader,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [1]bool + loadedTypes [2]bool // totalCount holds the count of the edges above. - totalCount [1]map[string]int + totalCount [2]map[string]int namedFiles map[string][]*File } @@ -59,6 +69,19 @@ func (e TomeEdges) FilesOrErr() ([]*File, error) { return nil, &NotLoadedError{edge: "files"} } +// UploaderOrErr returns the Uploader value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e TomeEdges) UploaderOrErr() (*User, error) { + if e.loadedTypes[1] { + if e.Uploader == nil { + // Edge was loaded but was not found. + return nil, &NotFoundError{label: user.Label} + } + return e.Uploader, nil + } + return nil, &NotLoadedError{edge: "uploader"} +} + // scanValues returns the types for scanning values from sql.Rows. func (*Tome) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) @@ -66,10 +89,12 @@ func (*Tome) scanValues(columns []string) ([]any, error) { switch columns[i] { case tome.FieldID: values[i] = new(sql.NullInt64) - case tome.FieldName, tome.FieldDescription, tome.FieldParamDefs, tome.FieldHash, tome.FieldEldritch: + case tome.FieldName, tome.FieldDescription, tome.FieldAuthor, tome.FieldSupportModel, tome.FieldTactic, tome.FieldParamDefs, tome.FieldHash, tome.FieldEldritch: values[i] = new(sql.NullString) case tome.FieldCreatedAt, tome.FieldLastModifiedAt: values[i] = new(sql.NullTime) + case tome.ForeignKeys[0]: // tome_uploader + values[i] = new(sql.NullInt64) default: values[i] = new(sql.UnknownType) } @@ -115,6 +140,24 @@ func (t *Tome) assignValues(columns []string, values []any) error { } else if value.Valid { t.Description = value.String } + case tome.FieldAuthor: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field author", values[i]) + } else if value.Valid { + t.Author = value.String + } + case tome.FieldSupportModel: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field support_model", values[i]) + } else if value.Valid { + t.SupportModel = tome.SupportModel(value.String) + } + case tome.FieldTactic: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field tactic", values[i]) + } else if value.Valid { + t.Tactic = tome.Tactic(value.String) + } case tome.FieldParamDefs: if value, ok := values[i].(*sql.NullString); !ok { return fmt.Errorf("unexpected type %T for field param_defs", values[i]) @@ -133,6 +176,13 @@ func (t *Tome) assignValues(columns []string, values []any) error { } else if value.Valid { t.Eldritch = value.String } + case tome.ForeignKeys[0]: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field tome_uploader", value) + } else if value.Valid { + t.tome_uploader = new(int) + *t.tome_uploader = int(value.Int64) + } default: t.selectValues.Set(columns[i], values[i]) } @@ -151,6 +201,11 @@ func (t *Tome) QueryFiles() *FileQuery { return NewTomeClient(t.config).QueryFiles(t) } +// QueryUploader queries the "uploader" edge of the Tome entity. +func (t *Tome) QueryUploader() *UserQuery { + return NewTomeClient(t.config).QueryUploader(t) +} + // Update returns a builder for updating this Tome. // Note that you need to call Tome.Unwrap() before calling this method if this Tome // was returned from a transaction, and the transaction was committed or rolled back. @@ -186,6 +241,15 @@ func (t *Tome) String() string { builder.WriteString("description=") builder.WriteString(t.Description) builder.WriteString(", ") + builder.WriteString("author=") + builder.WriteString(t.Author) + builder.WriteString(", ") + builder.WriteString("support_model=") + builder.WriteString(fmt.Sprintf("%v", t.SupportModel)) + builder.WriteString(", ") + builder.WriteString("tactic=") + builder.WriteString(fmt.Sprintf("%v", t.Tactic)) + builder.WriteString(", ") builder.WriteString("param_defs=") builder.WriteString(t.ParamDefs) builder.WriteString(", ") diff --git a/tavern/internal/ent/tome/tome.go b/tavern/internal/ent/tome/tome.go index 474e543d6..c5029cb58 100644 --- a/tavern/internal/ent/tome/tome.go +++ b/tavern/internal/ent/tome/tome.go @@ -3,6 +3,9 @@ package tome import ( + "fmt" + "io" + "strconv" "time" "entgo.io/ent" @@ -23,6 +26,12 @@ const ( FieldName = "name" // FieldDescription holds the string denoting the description field in the database. FieldDescription = "description" + // FieldAuthor holds the string denoting the author field in the database. + FieldAuthor = "author" + // FieldSupportModel holds the string denoting the support_model field in the database. + FieldSupportModel = "support_model" + // FieldTactic holds the string denoting the tactic field in the database. + FieldTactic = "tactic" // FieldParamDefs holds the string denoting the param_defs field in the database. FieldParamDefs = "param_defs" // FieldHash holds the string denoting the hash field in the database. @@ -31,6 +40,8 @@ const ( FieldEldritch = "eldritch" // EdgeFiles holds the string denoting the files edge name in mutations. EdgeFiles = "files" + // EdgeUploader holds the string denoting the uploader edge name in mutations. + EdgeUploader = "uploader" // Table holds the table name of the tome in the database. Table = "tomes" // FilesTable is the table that holds the files relation/edge. The primary key declared below. @@ -38,6 +49,13 @@ const ( // FilesInverseTable is the table name for the File entity. // It exists in this package in order to avoid circular dependency with the "file" package. FilesInverseTable = "files" + // UploaderTable is the table that holds the uploader relation/edge. + UploaderTable = "tomes" + // UploaderInverseTable is the table name for the User entity. + // It exists in this package in order to avoid circular dependency with the "user" package. + UploaderInverseTable = "users" + // UploaderColumn is the table column denoting the uploader relation/edge. + UploaderColumn = "tome_uploader" ) // Columns holds all SQL columns for tome fields. @@ -47,11 +65,20 @@ var Columns = []string{ FieldLastModifiedAt, FieldName, FieldDescription, + FieldAuthor, + FieldSupportModel, + FieldTactic, FieldParamDefs, FieldHash, FieldEldritch, } +// ForeignKeys holds the SQL foreign-keys that are owned by the "tomes" +// table and are not defined as standalone fields in the schema. +var ForeignKeys = []string{ + "tome_uploader", +} + var ( // FilesPrimaryKey and FilesColumn2 are the table columns denoting the // primary key for the files relation (M2M). @@ -65,6 +92,11 @@ func ValidColumn(column string) bool { return true } } + for i := range ForeignKeys { + if column == ForeignKeys[i] { + return true + } + } return false } @@ -89,6 +121,72 @@ var ( HashValidator func(string) error ) +// SupportModel defines the type for the "support_model" enum field. +type SupportModel string + +// SupportModelUNSPECIFIED is the default value of the SupportModel enum. +const DefaultSupportModel = SupportModelUNSPECIFIED + +// SupportModel values. +const ( + SupportModelUNSPECIFIED SupportModel = "UNSPECIFIED" + SupportModelFIRST_PARTY SupportModel = "FIRST_PARTY" + SupportModelCOMMUNITY SupportModel = "COMMUNITY" +) + +func (sm SupportModel) String() string { + return string(sm) +} + +// SupportModelValidator is a validator for the "support_model" field enum values. It is called by the builders before save. +func SupportModelValidator(sm SupportModel) error { + switch sm { + case SupportModelUNSPECIFIED, SupportModelFIRST_PARTY, SupportModelCOMMUNITY: + return nil + default: + return fmt.Errorf("tome: invalid enum value for support_model field: %q", sm) + } +} + +// Tactic defines the type for the "tactic" enum field. +type Tactic string + +// TacticUNSPECIFIED is the default value of the Tactic enum. +const DefaultTactic = TacticUNSPECIFIED + +// Tactic values. +const ( + TacticUNSPECIFIED Tactic = "UNSPECIFIED" + TacticRECON Tactic = "RECON" + TacticRESOURCE_DEVELOPMENT Tactic = "RESOURCE_DEVELOPMENT" + TacticINITIAL_ACCESS Tactic = "INITIAL_ACCESS" + TacticEXECUTION Tactic = "EXECUTION" + TacticPERSISTENCE Tactic = "PERSISTENCE" + TacticPRIVILEGE_ESCALATION Tactic = "PRIVILEGE_ESCALATION" + TacticDEFENSE_EVASION Tactic = "DEFENSE_EVASION" + TacticCREDENTIAL_ACCESS Tactic = "CREDENTIAL_ACCESS" + TacticDISCOVERY Tactic = "DISCOVERY" + TacticLATERAL_MOVEMENT Tactic = "LATERAL_MOVEMENT" + TacticCOLLECTION Tactic = "COLLECTION" + TacticCOMMAND_AND_CONTROL Tactic = "COMMAND_AND_CONTROL" + TacticEXFILTRATION Tactic = "EXFILTRATION" + TacticIMPACT Tactic = "IMPACT" +) + +func (t Tactic) String() string { + return string(t) +} + +// TacticValidator is a validator for the "tactic" field enum values. It is called by the builders before save. +func TacticValidator(t Tactic) error { + switch t { + case TacticUNSPECIFIED, TacticRECON, TacticRESOURCE_DEVELOPMENT, TacticINITIAL_ACCESS, TacticEXECUTION, TacticPERSISTENCE, TacticPRIVILEGE_ESCALATION, TacticDEFENSE_EVASION, TacticCREDENTIAL_ACCESS, TacticDISCOVERY, TacticLATERAL_MOVEMENT, TacticCOLLECTION, TacticCOMMAND_AND_CONTROL, TacticEXFILTRATION, TacticIMPACT: + return nil + default: + return fmt.Errorf("tome: invalid enum value for tactic field: %q", t) + } +} + // OrderOption defines the ordering options for the Tome queries. type OrderOption func(*sql.Selector) @@ -117,6 +215,21 @@ func ByDescription(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldDescription, opts...).ToFunc() } +// ByAuthor orders the results by the author field. +func ByAuthor(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAuthor, opts...).ToFunc() +} + +// BySupportModel orders the results by the support_model field. +func BySupportModel(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSupportModel, opts...).ToFunc() +} + +// ByTactic orders the results by the tactic field. +func ByTactic(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldTactic, opts...).ToFunc() +} + // ByParamDefs orders the results by the param_defs field. func ByParamDefs(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldParamDefs, opts...).ToFunc() @@ -145,6 +258,13 @@ func ByFiles(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { sqlgraph.OrderByNeighborTerms(s, newFilesStep(), append([]sql.OrderTerm{term}, terms...)...) } } + +// ByUploaderField orders the results by uploader field. +func ByUploaderField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newUploaderStep(), sql.OrderByField(field, opts...)) + } +} func newFilesStep() *sqlgraph.Step { return sqlgraph.NewStep( sqlgraph.From(Table, FieldID), @@ -152,3 +272,46 @@ func newFilesStep() *sqlgraph.Step { sqlgraph.Edge(sqlgraph.M2M, false, FilesTable, FilesPrimaryKey...), ) } +func newUploaderStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(UploaderInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, UploaderTable, UploaderColumn), + ) +} + +// MarshalGQL implements graphql.Marshaler interface. +func (e SupportModel) MarshalGQL(w io.Writer) { + io.WriteString(w, strconv.Quote(e.String())) +} + +// UnmarshalGQL implements graphql.Unmarshaler interface. +func (e *SupportModel) UnmarshalGQL(val interface{}) error { + str, ok := val.(string) + if !ok { + return fmt.Errorf("enum %T must be a string", val) + } + *e = SupportModel(str) + if err := SupportModelValidator(*e); err != nil { + return fmt.Errorf("%s is not a valid SupportModel", str) + } + return nil +} + +// MarshalGQL implements graphql.Marshaler interface. +func (e Tactic) MarshalGQL(w io.Writer) { + io.WriteString(w, strconv.Quote(e.String())) +} + +// UnmarshalGQL implements graphql.Unmarshaler interface. +func (e *Tactic) UnmarshalGQL(val interface{}) error { + str, ok := val.(string) + if !ok { + return fmt.Errorf("enum %T must be a string", val) + } + *e = Tactic(str) + if err := TacticValidator(*e); err != nil { + return fmt.Errorf("%s is not a valid Tactic", str) + } + return nil +} diff --git a/tavern/internal/ent/tome/where.go b/tavern/internal/ent/tome/where.go index 91714ae88..bef67df1c 100644 --- a/tavern/internal/ent/tome/where.go +++ b/tavern/internal/ent/tome/where.go @@ -75,6 +75,11 @@ func Description(v string) predicate.Tome { return predicate.Tome(sql.FieldEQ(FieldDescription, v)) } +// Author applies equality check predicate on the "author" field. It's identical to AuthorEQ. +func Author(v string) predicate.Tome { + return predicate.Tome(sql.FieldEQ(FieldAuthor, v)) +} + // ParamDefs applies equality check predicate on the "param_defs" field. It's identical to ParamDefsEQ. func ParamDefs(v string) predicate.Tome { return predicate.Tome(sql.FieldEQ(FieldParamDefs, v)) @@ -300,6 +305,111 @@ func DescriptionContainsFold(v string) predicate.Tome { return predicate.Tome(sql.FieldContainsFold(FieldDescription, v)) } +// AuthorEQ applies the EQ predicate on the "author" field. +func AuthorEQ(v string) predicate.Tome { + return predicate.Tome(sql.FieldEQ(FieldAuthor, v)) +} + +// AuthorNEQ applies the NEQ predicate on the "author" field. +func AuthorNEQ(v string) predicate.Tome { + return predicate.Tome(sql.FieldNEQ(FieldAuthor, v)) +} + +// AuthorIn applies the In predicate on the "author" field. +func AuthorIn(vs ...string) predicate.Tome { + return predicate.Tome(sql.FieldIn(FieldAuthor, vs...)) +} + +// AuthorNotIn applies the NotIn predicate on the "author" field. +func AuthorNotIn(vs ...string) predicate.Tome { + return predicate.Tome(sql.FieldNotIn(FieldAuthor, vs...)) +} + +// AuthorGT applies the GT predicate on the "author" field. +func AuthorGT(v string) predicate.Tome { + return predicate.Tome(sql.FieldGT(FieldAuthor, v)) +} + +// AuthorGTE applies the GTE predicate on the "author" field. +func AuthorGTE(v string) predicate.Tome { + return predicate.Tome(sql.FieldGTE(FieldAuthor, v)) +} + +// AuthorLT applies the LT predicate on the "author" field. +func AuthorLT(v string) predicate.Tome { + return predicate.Tome(sql.FieldLT(FieldAuthor, v)) +} + +// AuthorLTE applies the LTE predicate on the "author" field. +func AuthorLTE(v string) predicate.Tome { + return predicate.Tome(sql.FieldLTE(FieldAuthor, v)) +} + +// AuthorContains applies the Contains predicate on the "author" field. +func AuthorContains(v string) predicate.Tome { + return predicate.Tome(sql.FieldContains(FieldAuthor, v)) +} + +// AuthorHasPrefix applies the HasPrefix predicate on the "author" field. +func AuthorHasPrefix(v string) predicate.Tome { + return predicate.Tome(sql.FieldHasPrefix(FieldAuthor, v)) +} + +// AuthorHasSuffix applies the HasSuffix predicate on the "author" field. +func AuthorHasSuffix(v string) predicate.Tome { + return predicate.Tome(sql.FieldHasSuffix(FieldAuthor, v)) +} + +// AuthorEqualFold applies the EqualFold predicate on the "author" field. +func AuthorEqualFold(v string) predicate.Tome { + return predicate.Tome(sql.FieldEqualFold(FieldAuthor, v)) +} + +// AuthorContainsFold applies the ContainsFold predicate on the "author" field. +func AuthorContainsFold(v string) predicate.Tome { + return predicate.Tome(sql.FieldContainsFold(FieldAuthor, v)) +} + +// SupportModelEQ applies the EQ predicate on the "support_model" field. +func SupportModelEQ(v SupportModel) predicate.Tome { + return predicate.Tome(sql.FieldEQ(FieldSupportModel, v)) +} + +// SupportModelNEQ applies the NEQ predicate on the "support_model" field. +func SupportModelNEQ(v SupportModel) predicate.Tome { + return predicate.Tome(sql.FieldNEQ(FieldSupportModel, v)) +} + +// SupportModelIn applies the In predicate on the "support_model" field. +func SupportModelIn(vs ...SupportModel) predicate.Tome { + return predicate.Tome(sql.FieldIn(FieldSupportModel, vs...)) +} + +// SupportModelNotIn applies the NotIn predicate on the "support_model" field. +func SupportModelNotIn(vs ...SupportModel) predicate.Tome { + return predicate.Tome(sql.FieldNotIn(FieldSupportModel, vs...)) +} + +// TacticEQ applies the EQ predicate on the "tactic" field. +func TacticEQ(v Tactic) predicate.Tome { + return predicate.Tome(sql.FieldEQ(FieldTactic, v)) +} + +// TacticNEQ applies the NEQ predicate on the "tactic" field. +func TacticNEQ(v Tactic) predicate.Tome { + return predicate.Tome(sql.FieldNEQ(FieldTactic, v)) +} + +// TacticIn applies the In predicate on the "tactic" field. +func TacticIn(vs ...Tactic) predicate.Tome { + return predicate.Tome(sql.FieldIn(FieldTactic, vs...)) +} + +// TacticNotIn applies the NotIn predicate on the "tactic" field. +func TacticNotIn(vs ...Tactic) predicate.Tome { + return predicate.Tome(sql.FieldNotIn(FieldTactic, vs...)) +} + // ParamDefsEQ applies the EQ predicate on the "param_defs" field. func ParamDefsEQ(v string) predicate.Tome { return predicate.Tome(sql.FieldEQ(FieldParamDefs, v)) @@ -528,6 +638,29 @@ func HasFilesWith(preds ...predicate.File) predicate.Tome { }) } +// HasUploader applies the HasEdge predicate on the "uploader" edge. +func HasUploader() predicate.Tome { + return predicate.Tome(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, UploaderTable, UploaderColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasUploaderWith applies the HasEdge predicate on the "uploader" edge with a given conditions (other predicates). +func HasUploaderWith(preds ...predicate.User) predicate.Tome { + return predicate.Tome(func(s *sql.Selector) { + step := newUploaderStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // And groups predicates with the AND operator between them. func And(predicates ...predicate.Tome) predicate.Tome { return predicate.Tome(sql.AndPredicates(predicates...)) diff --git a/tavern/internal/ent/tome_create.go b/tavern/internal/ent/tome_create.go index 8b39f323d..01665df67 100644 --- a/tavern/internal/ent/tome_create.go +++ b/tavern/internal/ent/tome_create.go @@ -13,6 +13,7 @@ import ( "entgo.io/ent/schema/field" "realm.pub/tavern/internal/ent/file" "realm.pub/tavern/internal/ent/tome" + "realm.pub/tavern/internal/ent/user" ) // TomeCreate is the builder for creating a Tome entity. @@ -63,6 +64,40 @@ func (tc *TomeCreate) SetDescription(s string) *TomeCreate { return tc } +// SetAuthor sets the "author" field. +func (tc *TomeCreate) SetAuthor(s string) *TomeCreate { + tc.mutation.SetAuthor(s) + return tc +} + +// SetSupportModel sets the "support_model" field. +func (tc *TomeCreate) SetSupportModel(tm tome.SupportModel) *TomeCreate { + tc.mutation.SetSupportModel(tm) + return tc +} + +// SetNillableSupportModel sets the "support_model" field if the given value is not nil. +func (tc *TomeCreate) SetNillableSupportModel(tm *tome.SupportModel) *TomeCreate { + if tm != nil { + tc.SetSupportModel(*tm) + } + return tc +} + +// SetTactic sets the "tactic" field. +func (tc *TomeCreate) SetTactic(t tome.Tactic) *TomeCreate { + tc.mutation.SetTactic(t) + return tc +} + +// SetNillableTactic sets the "tactic" field if the given value is not nil. +func (tc *TomeCreate) SetNillableTactic(t *tome.Tactic) *TomeCreate { + if t != nil { + tc.SetTactic(*t) + } + return tc +} + // SetParamDefs sets the "param_defs" field. func (tc *TomeCreate) SetParamDefs(s string) *TomeCreate { tc.mutation.SetParamDefs(s) @@ -104,6 +139,25 @@ func (tc *TomeCreate) AddFiles(f ...*File) *TomeCreate { return tc.AddFileIDs(ids...) } +// SetUploaderID sets the "uploader" edge to the User entity by ID. +func (tc *TomeCreate) SetUploaderID(id int) *TomeCreate { + tc.mutation.SetUploaderID(id) + return tc +} + +// SetNillableUploaderID sets the "uploader" edge to the User entity by ID if the given value is not nil. +func (tc *TomeCreate) SetNillableUploaderID(id *int) *TomeCreate { + if id != nil { + tc = tc.SetUploaderID(*id) + } + return tc +} + +// SetUploader sets the "uploader" edge to the User entity. +func (tc *TomeCreate) SetUploader(u *User) *TomeCreate { + return tc.SetUploaderID(u.ID) +} + // Mutation returns the TomeMutation object of the builder. func (tc *TomeCreate) Mutation() *TomeMutation { return tc.mutation @@ -155,6 +209,14 @@ func (tc *TomeCreate) defaults() error { v := tome.DefaultLastModifiedAt() tc.mutation.SetLastModifiedAt(v) } + if _, ok := tc.mutation.SupportModel(); !ok { + v := tome.DefaultSupportModel + tc.mutation.SetSupportModel(v) + } + if _, ok := tc.mutation.Tactic(); !ok { + v := tome.DefaultTactic + tc.mutation.SetTactic(v) + } return nil } @@ -177,6 +239,25 @@ func (tc *TomeCreate) check() error { if _, ok := tc.mutation.Description(); !ok { return &ValidationError{Name: "description", err: errors.New(`ent: missing required field "Tome.description"`)} } + if _, ok := tc.mutation.Author(); !ok { + return &ValidationError{Name: "author", err: errors.New(`ent: missing required field "Tome.author"`)} + } + if _, ok := tc.mutation.SupportModel(); !ok { + return &ValidationError{Name: "support_model", err: errors.New(`ent: missing required field "Tome.support_model"`)} + } + if v, ok := tc.mutation.SupportModel(); ok { + if err := tome.SupportModelValidator(v); err != nil { + return &ValidationError{Name: "support_model", err: fmt.Errorf(`ent: validator failed for field "Tome.support_model": %w`, err)} + } + } + if _, ok := tc.mutation.Tactic(); !ok { + return &ValidationError{Name: "tactic", err: errors.New(`ent: missing required field "Tome.tactic"`)} + } + if v, ok := tc.mutation.Tactic(); ok { + if err := tome.TacticValidator(v); err != nil { + return &ValidationError{Name: "tactic", err: fmt.Errorf(`ent: validator failed for field "Tome.tactic": %w`, err)} + } + } if v, ok := tc.mutation.ParamDefs(); ok { if err := tome.ParamDefsValidator(v); err != nil { return &ValidationError{Name: "param_defs", err: fmt.Errorf(`ent: validator failed for field "Tome.param_defs": %w`, err)} @@ -236,6 +317,18 @@ func (tc *TomeCreate) createSpec() (*Tome, *sqlgraph.CreateSpec) { _spec.SetField(tome.FieldDescription, field.TypeString, value) _node.Description = value } + if value, ok := tc.mutation.Author(); ok { + _spec.SetField(tome.FieldAuthor, field.TypeString, value) + _node.Author = value + } + if value, ok := tc.mutation.SupportModel(); ok { + _spec.SetField(tome.FieldSupportModel, field.TypeEnum, value) + _node.SupportModel = value + } + if value, ok := tc.mutation.Tactic(); ok { + _spec.SetField(tome.FieldTactic, field.TypeEnum, value) + _node.Tactic = value + } if value, ok := tc.mutation.ParamDefs(); ok { _spec.SetField(tome.FieldParamDefs, field.TypeString, value) _node.ParamDefs = value @@ -264,6 +357,23 @@ func (tc *TomeCreate) createSpec() (*Tome, *sqlgraph.CreateSpec) { } _spec.Edges = append(_spec.Edges, edge) } + if nodes := tc.mutation.UploaderIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: tome.UploaderTable, + Columns: []string{tome.UploaderColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.tome_uploader = &nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } return _node, _spec } @@ -352,6 +462,42 @@ func (u *TomeUpsert) UpdateDescription() *TomeUpsert { return u } +// SetAuthor sets the "author" field. +func (u *TomeUpsert) SetAuthor(v string) *TomeUpsert { + u.Set(tome.FieldAuthor, v) + return u +} + +// UpdateAuthor sets the "author" field to the value that was provided on create. +func (u *TomeUpsert) UpdateAuthor() *TomeUpsert { + u.SetExcluded(tome.FieldAuthor) + return u +} + +// SetSupportModel sets the "support_model" field. +func (u *TomeUpsert) SetSupportModel(v tome.SupportModel) *TomeUpsert { + u.Set(tome.FieldSupportModel, v) + return u +} + +// UpdateSupportModel sets the "support_model" field to the value that was provided on create. +func (u *TomeUpsert) UpdateSupportModel() *TomeUpsert { + u.SetExcluded(tome.FieldSupportModel) + return u +} + +// SetTactic sets the "tactic" field. +func (u *TomeUpsert) SetTactic(v tome.Tactic) *TomeUpsert { + u.Set(tome.FieldTactic, v) + return u +} + +// UpdateTactic sets the "tactic" field to the value that was provided on create. +func (u *TomeUpsert) UpdateTactic() *TomeUpsert { + u.SetExcluded(tome.FieldTactic) + return u +} + // SetParamDefs sets the "param_defs" field. func (u *TomeUpsert) SetParamDefs(v string) *TomeUpsert { u.Set(tome.FieldParamDefs, v) @@ -481,6 +627,48 @@ func (u *TomeUpsertOne) UpdateDescription() *TomeUpsertOne { }) } +// SetAuthor sets the "author" field. +func (u *TomeUpsertOne) SetAuthor(v string) *TomeUpsertOne { + return u.Update(func(s *TomeUpsert) { + s.SetAuthor(v) + }) +} + +// UpdateAuthor sets the "author" field to the value that was provided on create. +func (u *TomeUpsertOne) UpdateAuthor() *TomeUpsertOne { + return u.Update(func(s *TomeUpsert) { + s.UpdateAuthor() + }) +} + +// SetSupportModel sets the "support_model" field. +func (u *TomeUpsertOne) SetSupportModel(v tome.SupportModel) *TomeUpsertOne { + return u.Update(func(s *TomeUpsert) { + s.SetSupportModel(v) + }) +} + +// UpdateSupportModel sets the "support_model" field to the value that was provided on create. +func (u *TomeUpsertOne) UpdateSupportModel() *TomeUpsertOne { + return u.Update(func(s *TomeUpsert) { + s.UpdateSupportModel() + }) +} + +// SetTactic sets the "tactic" field. +func (u *TomeUpsertOne) SetTactic(v tome.Tactic) *TomeUpsertOne { + return u.Update(func(s *TomeUpsert) { + s.SetTactic(v) + }) +} + +// UpdateTactic sets the "tactic" field to the value that was provided on create. +func (u *TomeUpsertOne) UpdateTactic() *TomeUpsertOne { + return u.Update(func(s *TomeUpsert) { + s.UpdateTactic() + }) +} + // SetParamDefs sets the "param_defs" field. func (u *TomeUpsertOne) SetParamDefs(v string) *TomeUpsertOne { return u.Update(func(s *TomeUpsert) { @@ -783,6 +971,48 @@ func (u *TomeUpsertBulk) UpdateDescription() *TomeUpsertBulk { }) } +// SetAuthor sets the "author" field. +func (u *TomeUpsertBulk) SetAuthor(v string) *TomeUpsertBulk { + return u.Update(func(s *TomeUpsert) { + s.SetAuthor(v) + }) +} + +// UpdateAuthor sets the "author" field to the value that was provided on create. +func (u *TomeUpsertBulk) UpdateAuthor() *TomeUpsertBulk { + return u.Update(func(s *TomeUpsert) { + s.UpdateAuthor() + }) +} + +// SetSupportModel sets the "support_model" field. +func (u *TomeUpsertBulk) SetSupportModel(v tome.SupportModel) *TomeUpsertBulk { + return u.Update(func(s *TomeUpsert) { + s.SetSupportModel(v) + }) +} + +// UpdateSupportModel sets the "support_model" field to the value that was provided on create. +func (u *TomeUpsertBulk) UpdateSupportModel() *TomeUpsertBulk { + return u.Update(func(s *TomeUpsert) { + s.UpdateSupportModel() + }) +} + +// SetTactic sets the "tactic" field. +func (u *TomeUpsertBulk) SetTactic(v tome.Tactic) *TomeUpsertBulk { + return u.Update(func(s *TomeUpsert) { + s.SetTactic(v) + }) +} + +// UpdateTactic sets the "tactic" field to the value that was provided on create. +func (u *TomeUpsertBulk) UpdateTactic() *TomeUpsertBulk { + return u.Update(func(s *TomeUpsert) { + s.UpdateTactic() + }) +} + // SetParamDefs sets the "param_defs" field. func (u *TomeUpsertBulk) SetParamDefs(v string) *TomeUpsertBulk { return u.Update(func(s *TomeUpsert) { diff --git a/tavern/internal/ent/tome_query.go b/tavern/internal/ent/tome_query.go index 02c90e02c..935452e7f 100644 --- a/tavern/internal/ent/tome_query.go +++ b/tavern/internal/ent/tome_query.go @@ -14,6 +14,7 @@ import ( "realm.pub/tavern/internal/ent/file" "realm.pub/tavern/internal/ent/predicate" "realm.pub/tavern/internal/ent/tome" + "realm.pub/tavern/internal/ent/user" ) // TomeQuery is the builder for querying Tome entities. @@ -24,6 +25,8 @@ type TomeQuery struct { inters []Interceptor predicates []predicate.Tome withFiles *FileQuery + withUploader *UserQuery + withFKs bool modifiers []func(*sql.Selector) loadTotal []func(context.Context, []*Tome) error withNamedFiles map[string]*FileQuery @@ -85,6 +88,28 @@ func (tq *TomeQuery) QueryFiles() *FileQuery { return query } +// QueryUploader chains the current query on the "uploader" edge. +func (tq *TomeQuery) QueryUploader() *UserQuery { + query := (&UserClient{config: tq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := tq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := tq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(tome.Table, tome.FieldID, selector), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, tome.UploaderTable, tome.UploaderColumn), + ) + fromU = sqlgraph.SetNeighbors(tq.driver.Dialect(), step) + return fromU, nil + } + return query +} + // First returns the first Tome entity from the query. // Returns a *NotFoundError when no Tome was found. func (tq *TomeQuery) First(ctx context.Context) (*Tome, error) { @@ -272,12 +297,13 @@ func (tq *TomeQuery) Clone() *TomeQuery { return nil } return &TomeQuery{ - config: tq.config, - ctx: tq.ctx.Clone(), - order: append([]tome.OrderOption{}, tq.order...), - inters: append([]Interceptor{}, tq.inters...), - predicates: append([]predicate.Tome{}, tq.predicates...), - withFiles: tq.withFiles.Clone(), + config: tq.config, + ctx: tq.ctx.Clone(), + order: append([]tome.OrderOption{}, tq.order...), + inters: append([]Interceptor{}, tq.inters...), + predicates: append([]predicate.Tome{}, tq.predicates...), + withFiles: tq.withFiles.Clone(), + withUploader: tq.withUploader.Clone(), // clone intermediate query. sql: tq.sql.Clone(), path: tq.path, @@ -295,6 +321,17 @@ func (tq *TomeQuery) WithFiles(opts ...func(*FileQuery)) *TomeQuery { return tq } +// WithUploader tells the query-builder to eager-load the nodes that are connected to +// the "uploader" edge. The optional arguments are used to configure the query builder of the edge. +func (tq *TomeQuery) WithUploader(opts ...func(*UserQuery)) *TomeQuery { + query := (&UserClient{config: tq.config}).Query() + for _, opt := range opts { + opt(query) + } + tq.withUploader = query + return tq +} + // GroupBy is used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -372,11 +409,19 @@ func (tq *TomeQuery) prepareQuery(ctx context.Context) error { func (tq *TomeQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Tome, error) { var ( nodes = []*Tome{} + withFKs = tq.withFKs _spec = tq.querySpec() - loadedTypes = [1]bool{ + loadedTypes = [2]bool{ tq.withFiles != nil, + tq.withUploader != nil, } ) + if tq.withUploader != nil { + withFKs = true + } + if withFKs { + _spec.Node.Columns = append(_spec.Node.Columns, tome.ForeignKeys...) + } _spec.ScanValues = func(columns []string) ([]any, error) { return (*Tome).scanValues(nil, columns) } @@ -405,6 +450,12 @@ func (tq *TomeQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Tome, e return nil, err } } + if query := tq.withUploader; query != nil { + if err := tq.loadUploader(ctx, query, nodes, nil, + func(n *Tome, e *User) { n.Edges.Uploader = e }); err != nil { + return nil, err + } + } for name, query := range tq.withNamedFiles { if err := tq.loadFiles(ctx, query, nodes, func(n *Tome) { n.appendNamedFiles(name) }, @@ -481,6 +532,38 @@ func (tq *TomeQuery) loadFiles(ctx context.Context, query *FileQuery, nodes []*T } return nil } +func (tq *TomeQuery) loadUploader(ctx context.Context, query *UserQuery, nodes []*Tome, init func(*Tome), assign func(*Tome, *User)) error { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*Tome) + for i := range nodes { + if nodes[i].tome_uploader == nil { + continue + } + fk := *nodes[i].tome_uploader + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "tome_uploader" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} func (tq *TomeQuery) sqlCount(ctx context.Context) (int, error) { _spec := tq.querySpec() diff --git a/tavern/internal/ent/tome_update.go b/tavern/internal/ent/tome_update.go index 83469d3b3..b6bf03bb6 100644 --- a/tavern/internal/ent/tome_update.go +++ b/tavern/internal/ent/tome_update.go @@ -14,6 +14,7 @@ import ( "realm.pub/tavern/internal/ent/file" "realm.pub/tavern/internal/ent/predicate" "realm.pub/tavern/internal/ent/tome" + "realm.pub/tavern/internal/ent/user" ) // TomeUpdate is the builder for updating Tome entities. @@ -47,6 +48,40 @@ func (tu *TomeUpdate) SetDescription(s string) *TomeUpdate { return tu } +// SetAuthor sets the "author" field. +func (tu *TomeUpdate) SetAuthor(s string) *TomeUpdate { + tu.mutation.SetAuthor(s) + return tu +} + +// SetSupportModel sets the "support_model" field. +func (tu *TomeUpdate) SetSupportModel(tm tome.SupportModel) *TomeUpdate { + tu.mutation.SetSupportModel(tm) + return tu +} + +// SetNillableSupportModel sets the "support_model" field if the given value is not nil. +func (tu *TomeUpdate) SetNillableSupportModel(tm *tome.SupportModel) *TomeUpdate { + if tm != nil { + tu.SetSupportModel(*tm) + } + return tu +} + +// SetTactic sets the "tactic" field. +func (tu *TomeUpdate) SetTactic(t tome.Tactic) *TomeUpdate { + tu.mutation.SetTactic(t) + return tu +} + +// SetNillableTactic sets the "tactic" field if the given value is not nil. +func (tu *TomeUpdate) SetNillableTactic(t *tome.Tactic) *TomeUpdate { + if t != nil { + tu.SetTactic(*t) + } + return tu +} + // SetParamDefs sets the "param_defs" field. func (tu *TomeUpdate) SetParamDefs(s string) *TomeUpdate { tu.mutation.SetParamDefs(s) @@ -94,6 +129,25 @@ func (tu *TomeUpdate) AddFiles(f ...*File) *TomeUpdate { return tu.AddFileIDs(ids...) } +// SetUploaderID sets the "uploader" edge to the User entity by ID. +func (tu *TomeUpdate) SetUploaderID(id int) *TomeUpdate { + tu.mutation.SetUploaderID(id) + return tu +} + +// SetNillableUploaderID sets the "uploader" edge to the User entity by ID if the given value is not nil. +func (tu *TomeUpdate) SetNillableUploaderID(id *int) *TomeUpdate { + if id != nil { + tu = tu.SetUploaderID(*id) + } + return tu +} + +// SetUploader sets the "uploader" edge to the User entity. +func (tu *TomeUpdate) SetUploader(u *User) *TomeUpdate { + return tu.SetUploaderID(u.ID) +} + // Mutation returns the TomeMutation object of the builder. func (tu *TomeUpdate) Mutation() *TomeMutation { return tu.mutation @@ -120,6 +174,12 @@ func (tu *TomeUpdate) RemoveFiles(f ...*File) *TomeUpdate { return tu.RemoveFileIDs(ids...) } +// ClearUploader clears the "uploader" edge to the User entity. +func (tu *TomeUpdate) ClearUploader() *TomeUpdate { + tu.mutation.ClearUploader() + return tu +} + // Save executes the query and returns the number of nodes affected by the update operation. func (tu *TomeUpdate) Save(ctx context.Context) (int, error) { if err := tu.defaults(); err != nil { @@ -169,6 +229,16 @@ func (tu *TomeUpdate) check() error { return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Tome.name": %w`, err)} } } + if v, ok := tu.mutation.SupportModel(); ok { + if err := tome.SupportModelValidator(v); err != nil { + return &ValidationError{Name: "support_model", err: fmt.Errorf(`ent: validator failed for field "Tome.support_model": %w`, err)} + } + } + if v, ok := tu.mutation.Tactic(); ok { + if err := tome.TacticValidator(v); err != nil { + return &ValidationError{Name: "tactic", err: fmt.Errorf(`ent: validator failed for field "Tome.tactic": %w`, err)} + } + } if v, ok := tu.mutation.ParamDefs(); ok { if err := tome.ParamDefsValidator(v); err != nil { return &ValidationError{Name: "param_defs", err: fmt.Errorf(`ent: validator failed for field "Tome.param_defs": %w`, err)} @@ -203,6 +273,15 @@ func (tu *TomeUpdate) sqlSave(ctx context.Context) (n int, err error) { if value, ok := tu.mutation.Description(); ok { _spec.SetField(tome.FieldDescription, field.TypeString, value) } + if value, ok := tu.mutation.Author(); ok { + _spec.SetField(tome.FieldAuthor, field.TypeString, value) + } + if value, ok := tu.mutation.SupportModel(); ok { + _spec.SetField(tome.FieldSupportModel, field.TypeEnum, value) + } + if value, ok := tu.mutation.Tactic(); ok { + _spec.SetField(tome.FieldTactic, field.TypeEnum, value) + } if value, ok := tu.mutation.ParamDefs(); ok { _spec.SetField(tome.FieldParamDefs, field.TypeString, value) } @@ -260,6 +339,35 @@ func (tu *TomeUpdate) sqlSave(ctx context.Context) (n int, err error) { } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if tu.mutation.UploaderCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: tome.UploaderTable, + Columns: []string{tome.UploaderColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := tu.mutation.UploaderIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: tome.UploaderTable, + Columns: []string{tome.UploaderColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if n, err = sqlgraph.UpdateNodes(ctx, tu.driver, _spec); err != nil { if _, ok := err.(*sqlgraph.NotFoundError); ok { err = &NotFoundError{tome.Label} @@ -298,6 +406,40 @@ func (tuo *TomeUpdateOne) SetDescription(s string) *TomeUpdateOne { return tuo } +// SetAuthor sets the "author" field. +func (tuo *TomeUpdateOne) SetAuthor(s string) *TomeUpdateOne { + tuo.mutation.SetAuthor(s) + return tuo +} + +// SetSupportModel sets the "support_model" field. +func (tuo *TomeUpdateOne) SetSupportModel(tm tome.SupportModel) *TomeUpdateOne { + tuo.mutation.SetSupportModel(tm) + return tuo +} + +// SetNillableSupportModel sets the "support_model" field if the given value is not nil. +func (tuo *TomeUpdateOne) SetNillableSupportModel(tm *tome.SupportModel) *TomeUpdateOne { + if tm != nil { + tuo.SetSupportModel(*tm) + } + return tuo +} + +// SetTactic sets the "tactic" field. +func (tuo *TomeUpdateOne) SetTactic(t tome.Tactic) *TomeUpdateOne { + tuo.mutation.SetTactic(t) + return tuo +} + +// SetNillableTactic sets the "tactic" field if the given value is not nil. +func (tuo *TomeUpdateOne) SetNillableTactic(t *tome.Tactic) *TomeUpdateOne { + if t != nil { + tuo.SetTactic(*t) + } + return tuo +} + // SetParamDefs sets the "param_defs" field. func (tuo *TomeUpdateOne) SetParamDefs(s string) *TomeUpdateOne { tuo.mutation.SetParamDefs(s) @@ -345,6 +487,25 @@ func (tuo *TomeUpdateOne) AddFiles(f ...*File) *TomeUpdateOne { return tuo.AddFileIDs(ids...) } +// SetUploaderID sets the "uploader" edge to the User entity by ID. +func (tuo *TomeUpdateOne) SetUploaderID(id int) *TomeUpdateOne { + tuo.mutation.SetUploaderID(id) + return tuo +} + +// SetNillableUploaderID sets the "uploader" edge to the User entity by ID if the given value is not nil. +func (tuo *TomeUpdateOne) SetNillableUploaderID(id *int) *TomeUpdateOne { + if id != nil { + tuo = tuo.SetUploaderID(*id) + } + return tuo +} + +// SetUploader sets the "uploader" edge to the User entity. +func (tuo *TomeUpdateOne) SetUploader(u *User) *TomeUpdateOne { + return tuo.SetUploaderID(u.ID) +} + // Mutation returns the TomeMutation object of the builder. func (tuo *TomeUpdateOne) Mutation() *TomeMutation { return tuo.mutation @@ -371,6 +532,12 @@ func (tuo *TomeUpdateOne) RemoveFiles(f ...*File) *TomeUpdateOne { return tuo.RemoveFileIDs(ids...) } +// ClearUploader clears the "uploader" edge to the User entity. +func (tuo *TomeUpdateOne) ClearUploader() *TomeUpdateOne { + tuo.mutation.ClearUploader() + return tuo +} + // Where appends a list predicates to the TomeUpdate builder. func (tuo *TomeUpdateOne) Where(ps ...predicate.Tome) *TomeUpdateOne { tuo.mutation.Where(ps...) @@ -433,6 +600,16 @@ func (tuo *TomeUpdateOne) check() error { return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Tome.name": %w`, err)} } } + if v, ok := tuo.mutation.SupportModel(); ok { + if err := tome.SupportModelValidator(v); err != nil { + return &ValidationError{Name: "support_model", err: fmt.Errorf(`ent: validator failed for field "Tome.support_model": %w`, err)} + } + } + if v, ok := tuo.mutation.Tactic(); ok { + if err := tome.TacticValidator(v); err != nil { + return &ValidationError{Name: "tactic", err: fmt.Errorf(`ent: validator failed for field "Tome.tactic": %w`, err)} + } + } if v, ok := tuo.mutation.ParamDefs(); ok { if err := tome.ParamDefsValidator(v); err != nil { return &ValidationError{Name: "param_defs", err: fmt.Errorf(`ent: validator failed for field "Tome.param_defs": %w`, err)} @@ -484,6 +661,15 @@ func (tuo *TomeUpdateOne) sqlSave(ctx context.Context) (_node *Tome, err error) if value, ok := tuo.mutation.Description(); ok { _spec.SetField(tome.FieldDescription, field.TypeString, value) } + if value, ok := tuo.mutation.Author(); ok { + _spec.SetField(tome.FieldAuthor, field.TypeString, value) + } + if value, ok := tuo.mutation.SupportModel(); ok { + _spec.SetField(tome.FieldSupportModel, field.TypeEnum, value) + } + if value, ok := tuo.mutation.Tactic(); ok { + _spec.SetField(tome.FieldTactic, field.TypeEnum, value) + } if value, ok := tuo.mutation.ParamDefs(); ok { _spec.SetField(tome.FieldParamDefs, field.TypeString, value) } @@ -541,6 +727,35 @@ func (tuo *TomeUpdateOne) sqlSave(ctx context.Context) (_node *Tome, err error) } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if tuo.mutation.UploaderCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: tome.UploaderTable, + Columns: []string{tome.UploaderColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := tuo.mutation.UploaderIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: tome.UploaderTable, + Columns: []string{tome.UploaderColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } _node = &Tome{config: tuo.config} _spec.Assign = _node.assignValues _spec.ScanValues = _node.scanValues diff --git a/tavern/internal/ent/user.go b/tavern/internal/ent/user.go index 05fa0903a..db6f54604 100644 --- a/tavern/internal/ent/user.go +++ b/tavern/internal/ent/user.go @@ -27,10 +27,35 @@ type User struct { // True if the user is active and able to authenticate IsActivated bool `json:"is_activated,omitempty"` // True if the user is an Admin - IsAdmin bool `json:"is_admin,omitempty"` + IsAdmin bool `json:"is_admin,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the UserQuery when eager-loading is set. + Edges UserEdges `json:"edges"` selectValues sql.SelectValues } +// UserEdges holds the relations/edges for other nodes in the graph. +type UserEdges struct { + // Tomes uploaded by the user. + Tomes []*Tome `json:"tomes,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [1]bool + // totalCount holds the count of the edges above. + totalCount [1]map[string]int + + namedTomes map[string][]*Tome +} + +// TomesOrErr returns the Tomes value or an error if the edge +// was not loaded in eager-loading. +func (e UserEdges) TomesOrErr() ([]*Tome, error) { + if e.loadedTypes[0] { + return e.Tomes, nil + } + return nil, &NotLoadedError{edge: "tomes"} +} + // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) @@ -112,6 +137,11 @@ func (u *User) Value(name string) (ent.Value, error) { return u.selectValues.Get(name) } +// QueryTomes queries the "tomes" edge of the User entity. +func (u *User) QueryTomes() *TomeQuery { + return NewUserClient(u.config).QueryTomes(u) +} + // Update returns a builder for updating this User. // Note that you need to call User.Unwrap() before calling this method if this User // was returned from a transaction, and the transaction was committed or rolled back. @@ -154,5 +184,29 @@ func (u *User) String() string { return builder.String() } +// NamedTomes returns the Tomes named value or an error if the edge was not +// loaded in eager-loading with this name. +func (u *User) NamedTomes(name string) ([]*Tome, error) { + if u.Edges.namedTomes == nil { + return nil, &NotLoadedError{edge: name} + } + nodes, ok := u.Edges.namedTomes[name] + if !ok { + return nil, &NotLoadedError{edge: name} + } + return nodes, nil +} + +func (u *User) appendNamedTomes(name string, edges ...*Tome) { + if u.Edges.namedTomes == nil { + u.Edges.namedTomes = make(map[string][]*Tome) + } + if len(edges) == 0 { + u.Edges.namedTomes[name] = []*Tome{} + } else { + u.Edges.namedTomes[name] = append(u.Edges.namedTomes[name], edges...) + } +} + // Users is a parsable slice of User. type Users []*User diff --git a/tavern/internal/ent/user/user.go b/tavern/internal/ent/user/user.go index bb6c78281..f9122b980 100644 --- a/tavern/internal/ent/user/user.go +++ b/tavern/internal/ent/user/user.go @@ -4,6 +4,7 @@ package user import ( "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" ) const ( @@ -23,8 +24,17 @@ const ( FieldIsActivated = "is_activated" // FieldIsAdmin holds the string denoting the is_admin field in the database. FieldIsAdmin = "is_admin" + // EdgeTomes holds the string denoting the tomes edge name in mutations. + EdgeTomes = "tomes" // Table holds the table name of the user in the database. Table = "users" + // TomesTable is the table that holds the tomes relation/edge. + TomesTable = "tomes" + // TomesInverseTable is the table name for the Tome entity. + // It exists in this package in order to avoid circular dependency with the "tome" package. + TomesInverseTable = "tomes" + // TomesColumn is the table column denoting the tomes relation/edge. + TomesColumn = "tome_uploader" ) // Columns holds all SQL columns for user fields. @@ -98,3 +108,24 @@ func ByIsActivated(opts ...sql.OrderTermOption) OrderOption { func ByIsAdmin(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldIsAdmin, opts...).ToFunc() } + +// ByTomesCount orders the results by tomes count. +func ByTomesCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newTomesStep(), opts...) + } +} + +// ByTomes orders the results by tomes terms. +func ByTomes(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newTomesStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} +func newTomesStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(TomesInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, TomesTable, TomesColumn), + ) +} diff --git a/tavern/internal/ent/user/where.go b/tavern/internal/ent/user/where.go index 1b28cf426..f66e72fa5 100644 --- a/tavern/internal/ent/user/where.go +++ b/tavern/internal/ent/user/where.go @@ -4,6 +4,7 @@ package user import ( "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" "realm.pub/tavern/internal/ent/predicate" ) @@ -362,6 +363,29 @@ func IsAdminNEQ(v bool) predicate.User { return predicate.User(sql.FieldNEQ(FieldIsAdmin, v)) } +// HasTomes applies the HasEdge predicate on the "tomes" edge. +func HasTomes() predicate.User { + return predicate.User(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, TomesTable, TomesColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasTomesWith applies the HasEdge predicate on the "tomes" edge with a given conditions (other predicates). +func HasTomesWith(preds ...predicate.Tome) predicate.User { + return predicate.User(func(s *sql.Selector) { + step := newTomesStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // And groups predicates with the AND operator between them. func And(predicates ...predicate.User) predicate.User { return predicate.User(sql.AndPredicates(predicates...)) diff --git a/tavern/internal/ent/user_create.go b/tavern/internal/ent/user_create.go index 142f3fc4e..d8c2f8111 100644 --- a/tavern/internal/ent/user_create.go +++ b/tavern/internal/ent/user_create.go @@ -10,6 +10,7 @@ import ( "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" + "realm.pub/tavern/internal/ent/tome" "realm.pub/tavern/internal/ent/user" ) @@ -81,6 +82,21 @@ func (uc *UserCreate) SetNillableIsAdmin(b *bool) *UserCreate { return uc } +// AddTomeIDs adds the "tomes" edge to the Tome entity by IDs. +func (uc *UserCreate) AddTomeIDs(ids ...int) *UserCreate { + uc.mutation.AddTomeIDs(ids...) + return uc +} + +// AddTomes adds the "tomes" edges to the Tome entity. +func (uc *UserCreate) AddTomes(t ...*Tome) *UserCreate { + ids := make([]int, len(t)) + for i := range t { + ids[i] = t[i].ID + } + return uc.AddTomeIDs(ids...) +} + // Mutation returns the UserMutation object of the builder. func (uc *UserCreate) Mutation() *UserMutation { return uc.mutation @@ -211,6 +227,22 @@ func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) { _spec.SetField(user.FieldIsAdmin, field.TypeBool, value) _node.IsAdmin = value } + if nodes := uc.mutation.TomesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: user.TomesTable, + Columns: []string{user.TomesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(tome.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } return _node, _spec } diff --git a/tavern/internal/ent/user_query.go b/tavern/internal/ent/user_query.go index 86f4d01d4..ea44d0bd8 100644 --- a/tavern/internal/ent/user_query.go +++ b/tavern/internal/ent/user_query.go @@ -4,6 +4,7 @@ package ent import ( "context" + "database/sql/driver" "fmt" "math" @@ -11,18 +12,21 @@ import ( "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" "realm.pub/tavern/internal/ent/predicate" + "realm.pub/tavern/internal/ent/tome" "realm.pub/tavern/internal/ent/user" ) // UserQuery is the builder for querying User entities. type UserQuery struct { config - ctx *QueryContext - order []user.OrderOption - inters []Interceptor - predicates []predicate.User - modifiers []func(*sql.Selector) - loadTotal []func(context.Context, []*User) error + ctx *QueryContext + order []user.OrderOption + inters []Interceptor + predicates []predicate.User + withTomes *TomeQuery + modifiers []func(*sql.Selector) + loadTotal []func(context.Context, []*User) error + withNamedTomes map[string]*TomeQuery // intermediate query (i.e. traversal path). sql *sql.Selector path func(context.Context) (*sql.Selector, error) @@ -59,6 +63,28 @@ func (uq *UserQuery) Order(o ...user.OrderOption) *UserQuery { return uq } +// QueryTomes chains the current query on the "tomes" edge. +func (uq *UserQuery) QueryTomes() *TomeQuery { + query := (&TomeClient{config: uq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := uq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := uq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(user.Table, user.FieldID, selector), + sqlgraph.To(tome.Table, tome.FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, user.TomesTable, user.TomesColumn), + ) + fromU = sqlgraph.SetNeighbors(uq.driver.Dialect(), step) + return fromU, nil + } + return query +} + // First returns the first User entity from the query. // Returns a *NotFoundError when no User was found. func (uq *UserQuery) First(ctx context.Context) (*User, error) { @@ -251,12 +277,24 @@ func (uq *UserQuery) Clone() *UserQuery { order: append([]user.OrderOption{}, uq.order...), inters: append([]Interceptor{}, uq.inters...), predicates: append([]predicate.User{}, uq.predicates...), + withTomes: uq.withTomes.Clone(), // clone intermediate query. sql: uq.sql.Clone(), path: uq.path, } } +// WithTomes tells the query-builder to eager-load the nodes that are connected to +// the "tomes" edge. The optional arguments are used to configure the query builder of the edge. +func (uq *UserQuery) WithTomes(opts ...func(*TomeQuery)) *UserQuery { + query := (&TomeClient{config: uq.config}).Query() + for _, opt := range opts { + opt(query) + } + uq.withTomes = query + return uq +} + // GroupBy is used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -333,8 +371,11 @@ func (uq *UserQuery) prepareQuery(ctx context.Context) error { func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, error) { var ( - nodes = []*User{} - _spec = uq.querySpec() + nodes = []*User{} + _spec = uq.querySpec() + loadedTypes = [1]bool{ + uq.withTomes != nil, + } ) _spec.ScanValues = func(columns []string) ([]any, error) { return (*User).scanValues(nil, columns) @@ -342,6 +383,7 @@ func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e _spec.Assign = func(columns []string, values []any) error { node := &User{config: uq.config} nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes return node.assignValues(columns, values) } if len(uq.modifiers) > 0 { @@ -356,6 +398,20 @@ func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e if len(nodes) == 0 { return nodes, nil } + if query := uq.withTomes; query != nil { + if err := uq.loadTomes(ctx, query, nodes, + func(n *User) { n.Edges.Tomes = []*Tome{} }, + func(n *User, e *Tome) { n.Edges.Tomes = append(n.Edges.Tomes, e) }); err != nil { + return nil, err + } + } + for name, query := range uq.withNamedTomes { + if err := uq.loadTomes(ctx, query, nodes, + func(n *User) { n.appendNamedTomes(name) }, + func(n *User, e *Tome) { n.appendNamedTomes(name, e) }); err != nil { + return nil, err + } + } for i := range uq.loadTotal { if err := uq.loadTotal[i](ctx, nodes); err != nil { return nil, err @@ -364,6 +420,38 @@ func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e return nodes, nil } +func (uq *UserQuery) loadTomes(ctx context.Context, query *TomeQuery, nodes []*User, init func(*User), assign func(*User, *Tome)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[int]*User) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + if init != nil { + init(nodes[i]) + } + } + query.withFKs = true + query.Where(predicate.Tome(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(user.TomesColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.tome_uploader + if fk == nil { + return fmt.Errorf(`foreign-key "tome_uploader" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "tome_uploader" returned %v for node %v`, *fk, n.ID) + } + assign(node, n) + } + return nil +} + func (uq *UserQuery) sqlCount(ctx context.Context) (int, error) { _spec := uq.querySpec() if len(uq.modifiers) > 0 { @@ -448,6 +536,20 @@ func (uq *UserQuery) sqlQuery(ctx context.Context) *sql.Selector { return selector } +// WithNamedTomes tells the query-builder to eager-load the nodes that are connected to the "tomes" +// edge with the given name. The optional arguments are used to configure the query builder of the edge. +func (uq *UserQuery) WithNamedTomes(name string, opts ...func(*TomeQuery)) *UserQuery { + query := (&TomeClient{config: uq.config}).Query() + for _, opt := range opts { + opt(query) + } + if uq.withNamedTomes == nil { + uq.withNamedTomes = make(map[string]*TomeQuery) + } + uq.withNamedTomes[name] = query + return uq +} + // UserGroupBy is the group-by builder for User entities. type UserGroupBy struct { selector diff --git a/tavern/internal/ent/user_update.go b/tavern/internal/ent/user_update.go index 3483a69ac..5f5e1fac1 100644 --- a/tavern/internal/ent/user_update.go +++ b/tavern/internal/ent/user_update.go @@ -11,6 +11,7 @@ import ( "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" "realm.pub/tavern/internal/ent/predicate" + "realm.pub/tavern/internal/ent/tome" "realm.pub/tavern/internal/ent/user" ) @@ -81,11 +82,47 @@ func (uu *UserUpdate) SetNillableIsAdmin(b *bool) *UserUpdate { return uu } +// AddTomeIDs adds the "tomes" edge to the Tome entity by IDs. +func (uu *UserUpdate) AddTomeIDs(ids ...int) *UserUpdate { + uu.mutation.AddTomeIDs(ids...) + return uu +} + +// AddTomes adds the "tomes" edges to the Tome entity. +func (uu *UserUpdate) AddTomes(t ...*Tome) *UserUpdate { + ids := make([]int, len(t)) + for i := range t { + ids[i] = t[i].ID + } + return uu.AddTomeIDs(ids...) +} + // Mutation returns the UserMutation object of the builder. func (uu *UserUpdate) Mutation() *UserMutation { return uu.mutation } +// ClearTomes clears all "tomes" edges to the Tome entity. +func (uu *UserUpdate) ClearTomes() *UserUpdate { + uu.mutation.ClearTomes() + return uu +} + +// RemoveTomeIDs removes the "tomes" edge to Tome entities by IDs. +func (uu *UserUpdate) RemoveTomeIDs(ids ...int) *UserUpdate { + uu.mutation.RemoveTomeIDs(ids...) + return uu +} + +// RemoveTomes removes "tomes" edges to Tome entities. +func (uu *UserUpdate) RemoveTomes(t ...*Tome) *UserUpdate { + ids := make([]int, len(t)) + for i := range t { + ids[i] = t[i].ID + } + return uu.RemoveTomeIDs(ids...) +} + // Save executes the query and returns the number of nodes affected by the update operation. func (uu *UserUpdate) Save(ctx context.Context) (int, error) { return withHooks(ctx, uu.sqlSave, uu.mutation, uu.hooks) @@ -155,6 +192,51 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) { if value, ok := uu.mutation.IsAdmin(); ok { _spec.SetField(user.FieldIsAdmin, field.TypeBool, value) } + if uu.mutation.TomesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: user.TomesTable, + Columns: []string{user.TomesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(tome.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uu.mutation.RemovedTomesIDs(); len(nodes) > 0 && !uu.mutation.TomesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: user.TomesTable, + Columns: []string{user.TomesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(tome.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uu.mutation.TomesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: user.TomesTable, + Columns: []string{user.TomesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(tome.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if n, err = sqlgraph.UpdateNodes(ctx, uu.driver, _spec); err != nil { if _, ok := err.(*sqlgraph.NotFoundError); ok { err = &NotFoundError{user.Label} @@ -229,11 +311,47 @@ func (uuo *UserUpdateOne) SetNillableIsAdmin(b *bool) *UserUpdateOne { return uuo } +// AddTomeIDs adds the "tomes" edge to the Tome entity by IDs. +func (uuo *UserUpdateOne) AddTomeIDs(ids ...int) *UserUpdateOne { + uuo.mutation.AddTomeIDs(ids...) + return uuo +} + +// AddTomes adds the "tomes" edges to the Tome entity. +func (uuo *UserUpdateOne) AddTomes(t ...*Tome) *UserUpdateOne { + ids := make([]int, len(t)) + for i := range t { + ids[i] = t[i].ID + } + return uuo.AddTomeIDs(ids...) +} + // Mutation returns the UserMutation object of the builder. func (uuo *UserUpdateOne) Mutation() *UserMutation { return uuo.mutation } +// ClearTomes clears all "tomes" edges to the Tome entity. +func (uuo *UserUpdateOne) ClearTomes() *UserUpdateOne { + uuo.mutation.ClearTomes() + return uuo +} + +// RemoveTomeIDs removes the "tomes" edge to Tome entities by IDs. +func (uuo *UserUpdateOne) RemoveTomeIDs(ids ...int) *UserUpdateOne { + uuo.mutation.RemoveTomeIDs(ids...) + return uuo +} + +// RemoveTomes removes "tomes" edges to Tome entities. +func (uuo *UserUpdateOne) RemoveTomes(t ...*Tome) *UserUpdateOne { + ids := make([]int, len(t)) + for i := range t { + ids[i] = t[i].ID + } + return uuo.RemoveTomeIDs(ids...) +} + // Where appends a list predicates to the UserUpdate builder. func (uuo *UserUpdateOne) Where(ps ...predicate.User) *UserUpdateOne { uuo.mutation.Where(ps...) @@ -333,6 +451,51 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) if value, ok := uuo.mutation.IsAdmin(); ok { _spec.SetField(user.FieldIsAdmin, field.TypeBool, value) } + if uuo.mutation.TomesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: user.TomesTable, + Columns: []string{user.TomesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(tome.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uuo.mutation.RemovedTomesIDs(); len(nodes) > 0 && !uuo.mutation.TomesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: user.TomesTable, + Columns: []string{user.TomesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(tome.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uuo.mutation.TomesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: user.TomesTable, + Columns: []string{user.TomesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(tome.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } _node = &User{config: uuo.config} _spec.Assign = _node.assignValues _spec.ScanValues = _node.scanValues diff --git a/tavern/internal/graphql/beacon_test.go b/tavern/internal/graphql/beacon_test.go deleted file mode 100644 index b20d0be2f..000000000 --- a/tavern/internal/graphql/beacon_test.go +++ /dev/null @@ -1,270 +0,0 @@ -package graphql_test - -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/99designs/gqlgen/client" - "github.com/99designs/gqlgen/graphql/handler" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "realm.pub/tavern/internal/ent" - "realm.pub/tavern/internal/ent/beacon" - "realm.pub/tavern/internal/ent/enttest" - "realm.pub/tavern/internal/ent/host" - "realm.pub/tavern/internal/ent/tag" - "realm.pub/tavern/internal/graphql" - tavernhttp "realm.pub/tavern/internal/http" -) - -func TestBeaconMutations(t *testing.T) { - // Setup - ctx := context.Background() - graph := enttest.Open(t, "sqlite3", "file:ent?mode=memory&cache=shared&_fk=1") - defer graph.Close() - srv := tavernhttp.NewServer( - tavernhttp.RouteMap{ - "/graphql": handler.NewDefaultServer(graphql.NewSchema(graph)), - }, - tavernhttp.WithAuthenticationBypass(graph), - ) - gqlClient := client.New(srv, client.Path("/graphql")) - - // Initialize sample data - testTags := []*ent.Tag{ - graph.Tag.Create(). - SetKind(tag.KindGroup). - SetName("TestTag1"). - SaveX(ctx), - graph.Tag.Create(). - SetKind(tag.KindGroup). - SetName("TestTag2"). - SaveX(ctx), - } - testHost := graph.Host.Create(). - SetIdentifier("SOME_HOST_ID"). - SetName("SOME_HOSTNAME"). - AddTags(testTags[1]). - SaveX(ctx) - - testBeacons := []*ent.Beacon{ - graph.Beacon.Create(). - SetPrincipal("admin"). - SetAgentIdentifier("TEST"). - SetIdentifier("SOME_ID"). - SetHost(testHost). - SetLastSeenAt(time.Now().Add(-10 * time.Minute)). - SaveX(ctx), - graph.Beacon.Create(). - SetIdentifier("ANOTHER_ID"). - SetHost(testHost). - SetLastSeenAt(time.Now().Add(-10 * time.Minute)). - SaveX(ctx), - } - testTome := graph.Tome.Create(). - SetName("Test Tome"). - SetDescription("Ensures the world feels greeted"). - SetEldritch(`print("Hello World!")`). - SaveX(ctx) - testQuest := graph.Quest.Create(). - SetName("howdy-ho"). - SetTome(testTome). - SaveX(ctx) - testTasks := []*ent.Task{ - graph.Task.Create(). - SetQuest(testQuest). - SetBeacon(testBeacons[0]). - SaveX(ctx), - } - - t.Run("ClaimTasks", func(t *testing.T) { - // Define the ClaimTasks mutation - mut := ` -mutation newClaimTasksTest($input: ClaimTasksInput!) { - claimTasks(input: $input) { - id - quest { - id - } - } -}` - // Create a closure to execute the mutation - claimTasks := func(input map[string]any) ([]int, error) { - // Make our request to the GraphQL API - var resp struct { - ClaimTasks []struct { - ID string - Quest struct { - ID string - } `json:"quest"` - } - } - err := gqlClient.Post(mut, &resp, client.Var("input", input)) - if err != nil { - return nil, err - } - - // Parse Task IDs from response - ids := make([]int, 0, len(resp.ClaimTasks)) - for _, task := range resp.ClaimTasks { - ids = append(ids, convertID(task.ID)) - } - return ids, nil - } - - /* - * Test when the `claimTasks` mutation is run by a beacon that does not exist. - * - * Expected that the beacon is created, but no tasks are returned. - */ - t.Run("NewBeacon", func(t *testing.T) { - expectedIdentifier := "NEW_ID" - expected := map[string]any{ - "principal": "newuser", - "hostname": "NEW_HOSTNAME", - "hostPlatform": host.PlatformWindows, - "beaconIdentifier": expectedIdentifier, - "hostIdentifier": "NEW_HOST_ID", - "agentIdentifier": "NEW_AGENT_ID", - } - ids, err := claimTasks(expected) - require.NoError(t, err) - assert.Empty(t, ids) - testBeacon, err := graph.Beacon.Query(). - Where(beacon.Identifier(expectedIdentifier)). - Only(ctx) - require.NoError(t, err) - assert.NotNil(t, testBeacon) - assert.NotZero(t, testBeacon.LastSeenAt) - assert.Equal(t, expected["principal"], testBeacon.Principal) - assert.Equal(t, expected["beaconIdentifier"], testBeacon.Identifier) - assert.Equal(t, expected["agentIdentifier"], testBeacon.AgentIdentifier) - - testHost, err := testBeacon.Host(ctx) - require.NoError(t, err) - require.NotNil(t, testHost) - assert.Equal(t, expected["hostIdentifier"], testHost.Identifier) - assert.Equal(t, expected["hostname"], testHost.Name) - - }) - - /* - * Test when the `claimTasks` mutation is run by a beacon that already exists. - * - * Expected that the beacon is updated, and our test task is returned. - */ - t.Run("ExistingBeacon", func(t *testing.T) { - expected := map[string]any{ - "principal": "admin", - "hostname": "SOME_HOSTNAME", - "hostPlatform": host.PlatformMacOS, - "hostPrimaryIP": "10.0.0.1", - "beaconIdentifier": "SOME_ID", - "hostIdentifier": "SOME_HOST_ID", - "agentIdentifier": "SOME_AGENT_ID", - } - ids, err := claimTasks(expected) - require.NoError(t, err) - assert.Len(t, ids, 1) - assert.Equal(t, testTasks[0].ID, ids[0]) - testTask := graph.Task.GetX(ctx, testTasks[0].ID) - assert.NotZero(t, testTask.ClaimedAt) - - testBeacon, err := testTask.Beacon(ctx) - require.NoError(t, err) - assert.Equal(t, testBeacons[0].ID, testBeacon.ID) - assert.NotZero(t, testBeacon.LastSeenAt) - assert.Equal(t, expected["principal"], testBeacon.Principal) - assert.Equal(t, expected["beaconIdentifier"], testBeacon.Identifier) - assert.Equal(t, expected["agentIdentifier"], testBeacon.AgentIdentifier) - - testHost, err := testBeacon.Host(ctx) - require.NoError(t, err) - require.NotNil(t, testHost) - assert.Equal(t, expected["hostIdentifier"], testHost.Identifier) - assert.Equal(t, expected["hostname"], testHost.Name) - assert.Equal(t, expected["hostPlatform"], testHost.Platform) - assert.Equal(t, expected["hostPrimaryIP"], testHost.PrimaryIP) - }) - }) - - t.Run("SubmitTaskResult", func(t *testing.T) { - // Define the SubmitTaskResult mutation - mut := ` -mutation newSubmitTaskResultTest($input: SubmitTaskResultInput!) { - submitTaskResult(input: $input) { - id - } -}` - // Create a closure to execute the mutation - submitTaskResult := func(input map[string]any) (int, error) { - // Make our request to the GraphQL API - var resp struct { - SubmitTaskResult struct{ ID string } - } - err := gqlClient.Post(mut, &resp, client.Var("input", input)) - if err != nil { - return 0, err - } - - // Parse Task ID from response - return convertID(resp.SubmitTaskResult.ID), nil - } - - expectedExecStartedAt := time.Now() - t.Run("FirstSubmit", func(t *testing.T) { - expected := map[string]any{ - "taskID": fmt.Sprintf("%d", testTasks[0].ID), - "execStartedAt": expectedExecStartedAt, - "output": "one", - } - id, err := submitTaskResult(expected) - require.NoError(t, err) - assert.NotZero(t, id) - testTask := graph.Task.GetX(ctx, id) - assert.Equal(t, testTasks[0].ID, testTask.ID) - assert.Equal(t, expectedExecStartedAt.UnixNano(), testTask.ExecStartedAt.UnixNano()) - assert.Equal(t, "one", testTask.Output) - assert.Zero(t, testTask.ExecFinishedAt) - assert.Zero(t, testTask.Error) - }) - t.Run("SecondSubmit", func(t *testing.T) { - expected := map[string]any{ - "taskID": fmt.Sprintf("%d", testTasks[0].ID), - "execStartedAt": expectedExecStartedAt, - "output": "_two", - } - id, err := submitTaskResult(expected) - require.NoError(t, err) - assert.NotZero(t, id) - testTask := graph.Task.GetX(ctx, id) - assert.Equal(t, testTasks[0].ID, testTask.ID) - assert.Equal(t, expectedExecStartedAt.UnixNano(), testTask.ExecStartedAt.UnixNano()) - assert.Equal(t, "one_two", testTask.Output) - assert.Zero(t, testTask.ExecFinishedAt) - assert.Zero(t, testTask.Error) - }) - - expectedExecFinishedAt := time.Now().Add(1 * time.Second) - t.Run("Complete", func(t *testing.T) { - expected := map[string]any{ - "taskID": fmt.Sprintf("%d", testTasks[0].ID), - "execStartedAt": expectedExecStartedAt, - "execFinishedAt": expectedExecFinishedAt, - "output": "_three", - "error": "uh oh", - } - id, err := submitTaskResult(expected) - require.NoError(t, err) - assert.NotZero(t, id) - testTask := graph.Task.GetX(ctx, id) - assert.Equal(t, testTasks[0].ID, testTask.ID) - assert.Equal(t, expectedExecStartedAt.UnixNano(), testTask.ExecStartedAt.UnixNano()) - assert.Equal(t, "one_two_three", testTask.Output) - assert.Equal(t, expectedExecFinishedAt.UnixNano(), testTask.ExecFinishedAt.UnixNano()) - assert.Equal(t, expected["error"], testTask.Error) - }) - }) -} diff --git a/tavern/internal/graphql/generated/ent.generated.go b/tavern/internal/graphql/generated/ent.generated.go index d0c0da9c5..c3de66577 100644 --- a/tavern/internal/graphql/generated/ent.generated.go +++ b/tavern/internal/graphql/generated/ent.generated.go @@ -18,6 +18,7 @@ import ( "realm.pub/tavern/internal/ent" "realm.pub/tavern/internal/ent/host" "realm.pub/tavern/internal/ent/tag" + "realm.pub/tavern/internal/ent/tome" ) // region ************************** generated!.gotpl ************************** @@ -1009,12 +1010,20 @@ func (ec *executionContext) fieldContext_File_tomes(ctx context.Context, field g return ec.fieldContext_Tome_name(ctx, field) case "description": return ec.fieldContext_Tome_description(ctx, field) + case "author": + return ec.fieldContext_Tome_author(ctx, field) + case "supportModel": + return ec.fieldContext_Tome_supportModel(ctx, field) + case "tactic": + return ec.fieldContext_Tome_tactic(ctx, field) case "paramDefs": return ec.fieldContext_Tome_paramDefs(ctx, field) case "eldritch": return ec.fieldContext_Tome_eldritch(ctx, field) case "files": return ec.fieldContext_Tome_files(ctx, field) + case "uploader": + return ec.fieldContext_Tome_uploader(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type Tome", field.Name) }, @@ -2764,12 +2773,20 @@ func (ec *executionContext) fieldContext_Query_tomes(ctx context.Context, field return ec.fieldContext_Tome_name(ctx, field) case "description": return ec.fieldContext_Tome_description(ctx, field) + case "author": + return ec.fieldContext_Tome_author(ctx, field) + case "supportModel": + return ec.fieldContext_Tome_supportModel(ctx, field) + case "tactic": + return ec.fieldContext_Tome_tactic(ctx, field) case "paramDefs": return ec.fieldContext_Tome_paramDefs(ctx, field) case "eldritch": return ec.fieldContext_Tome_eldritch(ctx, field) case "files": return ec.fieldContext_Tome_files(ctx, field) + case "uploader": + return ec.fieldContext_Tome_uploader(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type Tome", field.Name) }, @@ -2861,6 +2878,8 @@ func (ec *executionContext) fieldContext_Query_users(ctx context.Context, field return ec.fieldContext_User_isActivated(ctx, field) case "isAdmin": return ec.fieldContext_User_isAdmin(ctx, field) + case "tomes": + return ec.fieldContext_User_tomes(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -2928,6 +2947,8 @@ func (ec *executionContext) fieldContext_Query_me(ctx context.Context, field gra return ec.fieldContext_User_isActivated(ctx, field) case "isAdmin": return ec.fieldContext_User_isAdmin(ctx, field) + case "tomes": + return ec.fieldContext_User_tomes(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -3330,12 +3351,20 @@ func (ec *executionContext) fieldContext_Quest_tome(ctx context.Context, field g return ec.fieldContext_Tome_name(ctx, field) case "description": return ec.fieldContext_Tome_description(ctx, field) + case "author": + return ec.fieldContext_Tome_author(ctx, field) + case "supportModel": + return ec.fieldContext_Tome_supportModel(ctx, field) + case "tactic": + return ec.fieldContext_Tome_tactic(ctx, field) case "paramDefs": return ec.fieldContext_Tome_paramDefs(ctx, field) case "eldritch": return ec.fieldContext_Tome_eldritch(ctx, field) case "files": return ec.fieldContext_Tome_files(ctx, field) + case "uploader": + return ec.fieldContext_Tome_uploader(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type Tome", field.Name) }, @@ -3513,6 +3542,8 @@ func (ec *executionContext) fieldContext_Quest_creator(ctx context.Context, fiel return ec.fieldContext_User_isActivated(ctx, field) case "isAdmin": return ec.fieldContext_User_isAdmin(ctx, field) + case "tomes": + return ec.fieldContext_User_tomes(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -4757,6 +4788,138 @@ func (ec *executionContext) fieldContext_Tome_description(ctx context.Context, f return fc, nil } +func (ec *executionContext) _Tome_author(ctx context.Context, field graphql.CollectedField, obj *ent.Tome) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Tome_author(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.Author, 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_Tome_author(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Tome", + 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) _Tome_supportModel(ctx context.Context, field graphql.CollectedField, obj *ent.Tome) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Tome_supportModel(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.SupportModel, 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.(tome.SupportModel) + fc.Result = res + return ec.marshalNTomeSupportModel2realmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐSupportModel(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Tome_supportModel(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Tome", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type TomeSupportModel does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Tome_tactic(ctx context.Context, field graphql.CollectedField, obj *ent.Tome) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Tome_tactic(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.Tactic, 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.(tome.Tactic) + fc.Result = res + return ec.marshalNTomeTactic2realmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐTactic(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Tome_tactic(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Tome", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type TomeTactic does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _Tome_paramDefs(ctx context.Context, field graphql.CollectedField, obj *ent.Tome) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Tome_paramDefs(ctx, field) if err != nil { @@ -4899,6 +5062,61 @@ func (ec *executionContext) fieldContext_Tome_files(ctx context.Context, field g return fc, nil } +func (ec *executionContext) _Tome_uploader(ctx context.Context, field graphql.CollectedField, obj *ent.Tome) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Tome_uploader(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.Uploader(ctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*ent.User) + fc.Result = res + return ec.marshalOUser2ᚖrealmᚗpubᚋtavernᚋinternalᚋentᚐUser(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Tome_uploader(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Tome", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_User_id(ctx, field) + case "name": + return ec.fieldContext_User_name(ctx, field) + case "photoURL": + return ec.fieldContext_User_photoURL(ctx, field) + case "isActivated": + return ec.fieldContext_User_isActivated(ctx, field) + case "isAdmin": + return ec.fieldContext_User_isAdmin(ctx, field) + case "tomes": + return ec.fieldContext_User_tomes(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type User", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _User_id(ctx context.Context, field graphql.CollectedField, obj *ent.User) (ret graphql.Marshaler) { fc, err := ec.fieldContext_User_id(ctx, field) if err != nil { @@ -5119,6 +5337,73 @@ func (ec *executionContext) fieldContext_User_isAdmin(ctx context.Context, field return fc, nil } +func (ec *executionContext) _User_tomes(ctx context.Context, field graphql.CollectedField, obj *ent.User) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_User_tomes(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.Tomes(ctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]*ent.Tome) + fc.Result = res + return ec.marshalOTome2ᚕᚖrealmᚗpubᚋtavernᚋinternalᚋentᚐTomeᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_User_tomes(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "User", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Tome_id(ctx, field) + case "createdAt": + return ec.fieldContext_Tome_createdAt(ctx, field) + case "lastModifiedAt": + return ec.fieldContext_Tome_lastModifiedAt(ctx, field) + case "name": + return ec.fieldContext_Tome_name(ctx, field) + case "description": + return ec.fieldContext_Tome_description(ctx, field) + case "author": + return ec.fieldContext_Tome_author(ctx, field) + case "supportModel": + return ec.fieldContext_Tome_supportModel(ctx, field) + case "tactic": + return ec.fieldContext_Tome_tactic(ctx, field) + case "paramDefs": + return ec.fieldContext_Tome_paramDefs(ctx, field) + case "eldritch": + return ec.fieldContext_Tome_eldritch(ctx, field) + case "files": + return ec.fieldContext_Tome_files(ctx, field) + case "uploader": + return ec.fieldContext_Tome_uploader(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Tome", field.Name) + }, + } + return fc, nil +} + // endregion **************************** field.gotpl ***************************** // region **************************** input.gotpl ***************************** @@ -6105,7 +6390,7 @@ func (ec *executionContext) unmarshalInputCreateTomeInput(ctx context.Context, o asMap[k] = v } - fieldsInOrder := [...]string{"name", "description", "paramDefs", "eldritch", "fileIDs"} + fieldsInOrder := [...]string{"name", "description", "author", "supportModel", "tactic", "paramDefs", "eldritch", "fileIDs"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -6130,6 +6415,33 @@ func (ec *executionContext) unmarshalInputCreateTomeInput(ctx context.Context, o return it, err } it.Description = data + case "author": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("author")) + data, err := ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + it.Author = data + case "supportModel": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("supportModel")) + data, err := ec.unmarshalOTomeSupportModel2ᚖrealmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐSupportModel(ctx, v) + if err != nil { + return it, err + } + it.SupportModel = data + case "tactic": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tactic")) + data, err := ec.unmarshalOTomeTactic2ᚖrealmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐTactic(ctx, v) + if err != nil { + return it, err + } + it.Tactic = data case "paramDefs": var err error @@ -10164,7 +10476,7 @@ func (ec *executionContext) unmarshalInputTomeWhereInput(ctx context.Context, ob asMap[k] = v } - fieldsInOrder := [...]string{"not", "and", "or", "id", "idNEQ", "idIn", "idNotIn", "idGT", "idGTE", "idLT", "idLTE", "createdAt", "createdAtNEQ", "createdAtIn", "createdAtNotIn", "createdAtGT", "createdAtGTE", "createdAtLT", "createdAtLTE", "lastModifiedAt", "lastModifiedAtNEQ", "lastModifiedAtIn", "lastModifiedAtNotIn", "lastModifiedAtGT", "lastModifiedAtGTE", "lastModifiedAtLT", "lastModifiedAtLTE", "name", "nameNEQ", "nameIn", "nameNotIn", "nameGT", "nameGTE", "nameLT", "nameLTE", "nameContains", "nameHasPrefix", "nameHasSuffix", "nameEqualFold", "nameContainsFold", "description", "descriptionNEQ", "descriptionIn", "descriptionNotIn", "descriptionGT", "descriptionGTE", "descriptionLT", "descriptionLTE", "descriptionContains", "descriptionHasPrefix", "descriptionHasSuffix", "descriptionEqualFold", "descriptionContainsFold", "paramDefs", "paramDefsNEQ", "paramDefsIn", "paramDefsNotIn", "paramDefsGT", "paramDefsGTE", "paramDefsLT", "paramDefsLTE", "paramDefsContains", "paramDefsHasPrefix", "paramDefsHasSuffix", "paramDefsIsNil", "paramDefsNotNil", "paramDefsEqualFold", "paramDefsContainsFold", "eldritch", "eldritchNEQ", "eldritchIn", "eldritchNotIn", "eldritchGT", "eldritchGTE", "eldritchLT", "eldritchLTE", "eldritchContains", "eldritchHasPrefix", "eldritchHasSuffix", "eldritchEqualFold", "eldritchContainsFold", "hasFiles", "hasFilesWith"} + fieldsInOrder := [...]string{"not", "and", "or", "id", "idNEQ", "idIn", "idNotIn", "idGT", "idGTE", "idLT", "idLTE", "createdAt", "createdAtNEQ", "createdAtIn", "createdAtNotIn", "createdAtGT", "createdAtGTE", "createdAtLT", "createdAtLTE", "lastModifiedAt", "lastModifiedAtNEQ", "lastModifiedAtIn", "lastModifiedAtNotIn", "lastModifiedAtGT", "lastModifiedAtGTE", "lastModifiedAtLT", "lastModifiedAtLTE", "name", "nameNEQ", "nameIn", "nameNotIn", "nameGT", "nameGTE", "nameLT", "nameLTE", "nameContains", "nameHasPrefix", "nameHasSuffix", "nameEqualFold", "nameContainsFold", "description", "descriptionNEQ", "descriptionIn", "descriptionNotIn", "descriptionGT", "descriptionGTE", "descriptionLT", "descriptionLTE", "descriptionContains", "descriptionHasPrefix", "descriptionHasSuffix", "descriptionEqualFold", "descriptionContainsFold", "author", "authorNEQ", "authorIn", "authorNotIn", "authorGT", "authorGTE", "authorLT", "authorLTE", "authorContains", "authorHasPrefix", "authorHasSuffix", "authorEqualFold", "authorContainsFold", "supportModel", "supportModelNEQ", "supportModelIn", "supportModelNotIn", "tactic", "tacticNEQ", "tacticIn", "tacticNotIn", "paramDefs", "paramDefsNEQ", "paramDefsIn", "paramDefsNotIn", "paramDefsGT", "paramDefsGTE", "paramDefsLT", "paramDefsLTE", "paramDefsContains", "paramDefsHasPrefix", "paramDefsHasSuffix", "paramDefsIsNil", "paramDefsNotNil", "paramDefsEqualFold", "paramDefsContainsFold", "eldritch", "eldritchNEQ", "eldritchIn", "eldritchNotIn", "eldritchGT", "eldritchGTE", "eldritchLT", "eldritchLTE", "eldritchContains", "eldritchHasPrefix", "eldritchHasSuffix", "eldritchEqualFold", "eldritchContainsFold", "hasFiles", "hasFilesWith", "hasUploader", "hasUploaderWith"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -10648,55 +10960,244 @@ func (ec *executionContext) unmarshalInputTomeWhereInput(ctx context.Context, ob return it, err } it.DescriptionContainsFold = data - case "paramDefs": + case "author": var err error - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("paramDefs")) + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("author")) data, err := ec.unmarshalOString2ᚖstring(ctx, v) if err != nil { return it, err } - it.ParamDefs = data - case "paramDefsNEQ": + it.Author = data + case "authorNEQ": var err error - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("paramDefsNEQ")) + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("authorNEQ")) data, err := ec.unmarshalOString2ᚖstring(ctx, v) if err != nil { return it, err } - it.ParamDefsNEQ = data - case "paramDefsIn": + it.AuthorNEQ = data + case "authorIn": var err error - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("paramDefsIn")) + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("authorIn")) data, err := ec.unmarshalOString2ᚕstringᚄ(ctx, v) if err != nil { return it, err } - it.ParamDefsIn = data - case "paramDefsNotIn": + it.AuthorIn = data + case "authorNotIn": var err error - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("paramDefsNotIn")) + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("authorNotIn")) data, err := ec.unmarshalOString2ᚕstringᚄ(ctx, v) if err != nil { return it, err } - it.ParamDefsNotIn = data - case "paramDefsGT": + it.AuthorNotIn = data + case "authorGT": var err error - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("paramDefsGT")) + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("authorGT")) data, err := ec.unmarshalOString2ᚖstring(ctx, v) if err != nil { return it, err } - it.ParamDefsGT = data - case "paramDefsGTE": + it.AuthorGT = data + case "authorGTE": var err error - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("paramDefsGTE")) + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("authorGTE")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.AuthorGTE = data + case "authorLT": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("authorLT")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.AuthorLT = data + case "authorLTE": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("authorLTE")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.AuthorLTE = data + case "authorContains": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("authorContains")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.AuthorContains = data + case "authorHasPrefix": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("authorHasPrefix")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.AuthorHasPrefix = data + case "authorHasSuffix": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("authorHasSuffix")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.AuthorHasSuffix = data + case "authorEqualFold": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("authorEqualFold")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.AuthorEqualFold = data + case "authorContainsFold": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("authorContainsFold")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.AuthorContainsFold = data + case "supportModel": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("supportModel")) + data, err := ec.unmarshalOTomeSupportModel2ᚖrealmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐSupportModel(ctx, v) + if err != nil { + return it, err + } + it.SupportModel = data + case "supportModelNEQ": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("supportModelNEQ")) + data, err := ec.unmarshalOTomeSupportModel2ᚖrealmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐSupportModel(ctx, v) + if err != nil { + return it, err + } + it.SupportModelNEQ = data + case "supportModelIn": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("supportModelIn")) + data, err := ec.unmarshalOTomeSupportModel2ᚕrealmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐSupportModelᚄ(ctx, v) + if err != nil { + return it, err + } + it.SupportModelIn = data + case "supportModelNotIn": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("supportModelNotIn")) + data, err := ec.unmarshalOTomeSupportModel2ᚕrealmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐSupportModelᚄ(ctx, v) + if err != nil { + return it, err + } + it.SupportModelNotIn = data + case "tactic": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tactic")) + data, err := ec.unmarshalOTomeTactic2ᚖrealmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐTactic(ctx, v) + if err != nil { + return it, err + } + it.Tactic = data + case "tacticNEQ": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tacticNEQ")) + data, err := ec.unmarshalOTomeTactic2ᚖrealmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐTactic(ctx, v) + if err != nil { + return it, err + } + it.TacticNEQ = data + case "tacticIn": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tacticIn")) + data, err := ec.unmarshalOTomeTactic2ᚕrealmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐTacticᚄ(ctx, v) + if err != nil { + return it, err + } + it.TacticIn = data + case "tacticNotIn": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tacticNotIn")) + data, err := ec.unmarshalOTomeTactic2ᚕrealmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐTacticᚄ(ctx, v) + if err != nil { + return it, err + } + it.TacticNotIn = data + case "paramDefs": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("paramDefs")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.ParamDefs = data + case "paramDefsNEQ": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("paramDefsNEQ")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.ParamDefsNEQ = data + case "paramDefsIn": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("paramDefsIn")) + data, err := ec.unmarshalOString2ᚕstringᚄ(ctx, v) + if err != nil { + return it, err + } + it.ParamDefsIn = data + case "paramDefsNotIn": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("paramDefsNotIn")) + data, err := ec.unmarshalOString2ᚕstringᚄ(ctx, v) + if err != nil { + return it, err + } + it.ParamDefsNotIn = data + case "paramDefsGT": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("paramDefsGT")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.ParamDefsGT = data + case "paramDefsGTE": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("paramDefsGTE")) data, err := ec.unmarshalOString2ᚖstring(ctx, v) if err != nil { return it, err @@ -10918,6 +11419,24 @@ func (ec *executionContext) unmarshalInputTomeWhereInput(ctx context.Context, ob return it, err } it.HasFilesWith = data + case "hasUploader": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("hasUploader")) + data, err := ec.unmarshalOBoolean2ᚖbool(ctx, v) + if err != nil { + return it, err + } + it.HasUploader = data + case "hasUploaderWith": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("hasUploaderWith")) + data, err := ec.unmarshalOUserWhereInput2ᚕᚖrealmᚗpubᚋtavernᚋinternalᚋentᚐUserWhereInputᚄ(ctx, v) + if err != nil { + return it, err + } + it.HasUploaderWith = data } } @@ -11137,6 +11656,134 @@ func (ec *executionContext) unmarshalInputUpdateTagInput(ctx context.Context, ob return it, nil } +func (ec *executionContext) unmarshalInputUpdateTomeInput(ctx context.Context, obj interface{}) (ent.UpdateTomeInput, error) { + var it ent.UpdateTomeInput + asMap := map[string]interface{}{} + for k, v := range obj.(map[string]interface{}) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"lastModifiedAt", "name", "description", "author", "supportModel", "tactic", "paramDefs", "clearParamDefs", "eldritch", "addFileIDs", "removeFileIDs", "clearFiles"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "lastModifiedAt": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("lastModifiedAt")) + data, err := ec.unmarshalOTime2ᚖtimeᚐTime(ctx, v) + if err != nil { + return it, err + } + it.LastModifiedAt = data + case "name": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.Name = data + case "description": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("description")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.Description = data + case "author": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("author")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.Author = data + case "supportModel": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("supportModel")) + data, err := ec.unmarshalOTomeSupportModel2ᚖrealmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐSupportModel(ctx, v) + if err != nil { + return it, err + } + it.SupportModel = data + case "tactic": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tactic")) + data, err := ec.unmarshalOTomeTactic2ᚖrealmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐTactic(ctx, v) + if err != nil { + return it, err + } + it.Tactic = data + case "paramDefs": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("paramDefs")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.ParamDefs = data + case "clearParamDefs": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("clearParamDefs")) + data, err := ec.unmarshalOBoolean2bool(ctx, v) + if err != nil { + return it, err + } + it.ClearParamDefs = data + case "eldritch": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("eldritch")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.Eldritch = data + case "addFileIDs": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("addFileIDs")) + data, err := ec.unmarshalOID2ᚕintᚄ(ctx, v) + if err != nil { + return it, err + } + it.AddFileIDs = data + case "removeFileIDs": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("removeFileIDs")) + data, err := ec.unmarshalOID2ᚕintᚄ(ctx, v) + if err != nil { + return it, err + } + it.RemoveFileIDs = data + case "clearFiles": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("clearFiles")) + data, err := ec.unmarshalOBoolean2bool(ctx, v) + if err != nil { + return it, err + } + it.ClearFiles = data + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputUpdateUserInput(ctx context.Context, obj interface{}) (ent.UpdateUserInput, error) { var it ent.UpdateUserInput asMap := map[string]interface{}{} @@ -11144,7 +11791,7 @@ func (ec *executionContext) unmarshalInputUpdateUserInput(ctx context.Context, o asMap[k] = v } - fieldsInOrder := [...]string{"name", "photoURL", "isActivated", "isAdmin"} + fieldsInOrder := [...]string{"name", "photoURL", "isActivated", "isAdmin", "addTomeIDs", "removeTomeIDs", "clearTomes"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -11187,6 +11834,33 @@ func (ec *executionContext) unmarshalInputUpdateUserInput(ctx context.Context, o return it, err } it.IsAdmin = data + case "addTomeIDs": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("addTomeIDs")) + data, err := ec.unmarshalOID2ᚕintᚄ(ctx, v) + if err != nil { + return it, err + } + it.AddTomeIDs = data + case "removeTomeIDs": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("removeTomeIDs")) + data, err := ec.unmarshalOID2ᚕintᚄ(ctx, v) + if err != nil { + return it, err + } + it.RemoveTomeIDs = data + case "clearTomes": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("clearTomes")) + data, err := ec.unmarshalOBoolean2bool(ctx, v) + if err != nil { + return it, err + } + it.ClearTomes = data } } @@ -11200,7 +11874,7 @@ func (ec *executionContext) unmarshalInputUserWhereInput(ctx context.Context, ob asMap[k] = v } - 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", "photoURL", "photoURLNEQ", "photoURLIn", "photoURLNotIn", "photoURLGT", "photoURLGTE", "photoURLLT", "photoURLLTE", "photoURLContains", "photoURLHasPrefix", "photoURLHasSuffix", "photoURLEqualFold", "photoURLContainsFold", "isActivated", "isActivatedNEQ", "isAdmin", "isAdminNEQ"} + 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", "photoURL", "photoURLNEQ", "photoURLIn", "photoURLNotIn", "photoURLGT", "photoURLGTE", "photoURLLT", "photoURLLTE", "photoURLContains", "photoURLHasPrefix", "photoURLHasSuffix", "photoURLEqualFold", "photoURLContainsFold", "isActivated", "isActivatedNEQ", "isAdmin", "isAdminNEQ", "hasTomes", "hasTomesWith"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -11576,6 +12250,24 @@ func (ec *executionContext) unmarshalInputUserWhereInput(ctx context.Context, ob return it, err } it.IsAdminNEQ = data + case "hasTomes": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("hasTomes")) + data, err := ec.unmarshalOBoolean2ᚖbool(ctx, v) + if err != nil { + return it, err + } + it.HasTomes = data + case "hasTomesWith": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("hasTomesWith")) + data, err := ec.unmarshalOTomeWhereInput2ᚕᚖrealmᚗpubᚋtavernᚋinternalᚋentᚐTomeWhereInputᚄ(ctx, v) + if err != nil { + return it, err + } + it.HasTomesWith = data } } @@ -13090,6 +13782,21 @@ func (ec *executionContext) _Tome(ctx context.Context, sel ast.SelectionSet, obj if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } + case "author": + out.Values[i] = ec._Tome_author(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "supportModel": + out.Values[i] = ec._Tome_supportModel(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "tactic": + out.Values[i] = ec._Tome_tactic(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } case "paramDefs": out.Values[i] = ec._Tome_paramDefs(ctx, field, obj) case "eldritch": @@ -13097,7 +13804,40 @@ func (ec *executionContext) _Tome(ctx context.Context, sel ast.SelectionSet, obj if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } - case "files": + case "files": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Tome_files(ctx, field, obj) + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "uploader": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -13106,7 +13846,7 @@ func (ec *executionContext) _Tome(ctx context.Context, sel ast.SelectionSet, obj ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Tome_files(ctx, field, obj) + res = ec._Tome_uploader(ctx, field, obj) return res } @@ -13167,28 +13907,61 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj case "id": out.Values[i] = ec._User_id(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "name": out.Values[i] = ec._User_name(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "photoURL": out.Values[i] = ec._User_photoURL(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "isActivated": out.Values[i] = ec._User_isActivated(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "isAdmin": out.Values[i] = ec._User_isAdmin(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) + } + case "tomes": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._User_tomes(ctx, field, obj) + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -13731,50 +14504,6 @@ func (ec *executionContext) unmarshalNTagWhereInput2ᚖrealmᚗpubᚋtavernᚋin return &res, graphql.ErrorOnPath(ctx, err) } -func (ec *executionContext) marshalNTask2ᚕᚖrealmᚗpubᚋtavernᚋinternalᚋentᚐTaskᚄ(ctx context.Context, sel ast.SelectionSet, v []*ent.Task) graphql.Marshaler { - ret := make(graphql.Array, len(v)) - var wg sync.WaitGroup - isLen1 := len(v) == 1 - if !isLen1 { - wg.Add(len(v)) - } - for i := range v { - i := i - fc := &graphql.FieldContext{ - Index: &i, - Result: &v[i], - } - ctx := graphql.WithFieldContext(ctx, fc) - f := func(i int) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = nil - } - }() - if !isLen1 { - defer wg.Done() - } - ret[i] = ec.marshalNTask2ᚖrealmᚗpubᚋtavernᚋinternalᚋentᚐTask(ctx, sel, v[i]) - } - if isLen1 { - f(i) - } else { - go f(i) - } - - } - wg.Wait() - - for _, e := range ret { - if e == graphql.Null { - return graphql.Null - } - } - - return ret -} - func (ec *executionContext) marshalNTask2ᚖrealmᚗpubᚋtavernᚋinternalᚋentᚐTask(ctx context.Context, sel ast.SelectionSet, v *ent.Task) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -13899,6 +14628,26 @@ func (ec *executionContext) marshalNTomeOrderField2ᚖrealmᚗpubᚋtavernᚋint return v } +func (ec *executionContext) unmarshalNTomeSupportModel2realmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐSupportModel(ctx context.Context, v interface{}) (tome.SupportModel, error) { + var res tome.SupportModel + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNTomeSupportModel2realmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐSupportModel(ctx context.Context, sel ast.SelectionSet, v tome.SupportModel) graphql.Marshaler { + return v +} + +func (ec *executionContext) unmarshalNTomeTactic2realmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐTactic(ctx context.Context, v interface{}) (tome.Tactic, error) { + var res tome.Tactic + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNTomeTactic2realmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐTactic(ctx context.Context, sel ast.SelectionSet, v tome.Tactic) graphql.Marshaler { + return v +} + func (ec *executionContext) unmarshalNTomeWhereInput2ᚖrealmᚗpubᚋtavernᚋinternalᚋentᚐTomeWhereInput(ctx context.Context, v interface{}) (*ent.TomeWhereInput, error) { res, err := ec.unmarshalInputTomeWhereInput(ctx, v) return &res, graphql.ErrorOnPath(ctx, err) @@ -13919,6 +14668,11 @@ func (ec *executionContext) unmarshalNUpdateTagInput2realmᚗpubᚋtavernᚋinte return res, graphql.ErrorOnPath(ctx, err) } +func (ec *executionContext) unmarshalNUpdateTomeInput2realmᚗpubᚋtavernᚋinternalᚋentᚐUpdateTomeInput(ctx context.Context, v interface{}) (ent.UpdateTomeInput, error) { + res, err := ec.unmarshalInputUpdateTomeInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + func (ec *executionContext) unmarshalNUpdateUserInput2realmᚗpubᚋtavernᚋinternalᚋentᚐUpdateUserInput(ctx context.Context, v interface{}) (ent.UpdateUserInput, error) { res, err := ec.unmarshalInputUpdateUserInput(ctx, v) return res, graphql.ErrorOnPath(ctx, err) @@ -14790,6 +15544,172 @@ func (ec *executionContext) marshalOTome2ᚕᚖrealmᚗpubᚋtavernᚋinternal return ret } +func (ec *executionContext) unmarshalOTomeSupportModel2ᚕrealmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐSupportModelᚄ(ctx context.Context, v interface{}) ([]tome.SupportModel, error) { + if v == nil { + return nil, nil + } + var vSlice []interface{} + if v != nil { + vSlice = graphql.CoerceList(v) + } + var err error + res := make([]tome.SupportModel, len(vSlice)) + for i := range vSlice { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) + res[i], err = ec.unmarshalNTomeSupportModel2realmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐSupportModel(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) marshalOTomeSupportModel2ᚕrealmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐSupportModelᚄ(ctx context.Context, sel ast.SelectionSet, v []tome.SupportModel) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNTomeSupportModel2realmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐSupportModel(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) unmarshalOTomeSupportModel2ᚖrealmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐSupportModel(ctx context.Context, v interface{}) (*tome.SupportModel, error) { + if v == nil { + return nil, nil + } + var res = new(tome.SupportModel) + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOTomeSupportModel2ᚖrealmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐSupportModel(ctx context.Context, sel ast.SelectionSet, v *tome.SupportModel) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return v +} + +func (ec *executionContext) unmarshalOTomeTactic2ᚕrealmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐTacticᚄ(ctx context.Context, v interface{}) ([]tome.Tactic, error) { + if v == nil { + return nil, nil + } + var vSlice []interface{} + if v != nil { + vSlice = graphql.CoerceList(v) + } + var err error + res := make([]tome.Tactic, len(vSlice)) + for i := range vSlice { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) + res[i], err = ec.unmarshalNTomeTactic2realmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐTactic(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) marshalOTomeTactic2ᚕrealmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐTacticᚄ(ctx context.Context, sel ast.SelectionSet, v []tome.Tactic) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNTomeTactic2realmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐTactic(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) unmarshalOTomeTactic2ᚖrealmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐTactic(ctx context.Context, v interface{}) (*tome.Tactic, error) { + if v == nil { + return nil, nil + } + var res = new(tome.Tactic) + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOTomeTactic2ᚖrealmᚗpubᚋtavernᚋinternalᚋentᚋtomeᚐTactic(ctx context.Context, sel ast.SelectionSet, v *tome.Tactic) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return v +} + func (ec *executionContext) unmarshalOTomeWhereInput2ᚕᚖrealmᚗpubᚋtavernᚋinternalᚋentᚐTomeWhereInputᚄ(ctx context.Context, v interface{}) ([]*ent.TomeWhereInput, error) { if v == nil { return nil, nil diff --git a/tavern/internal/graphql/generated/inputs.generated.go b/tavern/internal/graphql/generated/inputs.generated.go index 2280c2c00..41f50985f 100644 --- a/tavern/internal/graphql/generated/inputs.generated.go +++ b/tavern/internal/graphql/generated/inputs.generated.go @@ -187,14 +187,4 @@ func (ec *executionContext) unmarshalInputSubmitTaskResultInput(ctx context.Cont // region ***************************** type.gotpl ***************************** -func (ec *executionContext) unmarshalNClaimTasksInput2realmᚗpubᚋtavernᚋinternalᚋgraphqlᚋmodelsᚐClaimTasksInput(ctx context.Context, v interface{}) (models.ClaimTasksInput, error) { - res, err := ec.unmarshalInputClaimTasksInput(ctx, v) - return res, graphql.ErrorOnPath(ctx, err) -} - -func (ec *executionContext) unmarshalNSubmitTaskResultInput2realmᚗpubᚋtavernᚋinternalᚋgraphqlᚋmodelsᚐSubmitTaskResultInput(ctx context.Context, v interface{}) (models.SubmitTaskResultInput, error) { - res, err := ec.unmarshalInputSubmitTaskResultInput(ctx, v) - return res, graphql.ErrorOnPath(ctx, err) -} - // endregion ***************************** type.gotpl ***************************** diff --git a/tavern/internal/graphql/generated/mutation.generated.go b/tavern/internal/graphql/generated/mutation.generated.go index 46f97c766..9954db535 100644 --- a/tavern/internal/graphql/generated/mutation.generated.go +++ b/tavern/internal/graphql/generated/mutation.generated.go @@ -12,7 +12,6 @@ import ( "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/ast" "realm.pub/tavern/internal/ent" - "realm.pub/tavern/internal/graphql/models" ) // region ************************** generated!.gotpl ************************** @@ -23,9 +22,9 @@ type MutationResolver interface { UpdateHost(ctx context.Context, hostID int, input ent.UpdateHostInput) (*ent.Host, error) CreateTag(ctx context.Context, input ent.CreateTagInput) (*ent.Tag, error) UpdateTag(ctx context.Context, tagID int, input ent.UpdateTagInput) (*ent.Tag, error) - ClaimTasks(ctx context.Context, input models.ClaimTasksInput) ([]*ent.Task, error) - SubmitTaskResult(ctx context.Context, input models.SubmitTaskResultInput) (*ent.Task, error) CreateTome(ctx context.Context, input ent.CreateTomeInput) (*ent.Tome, error) + UpdateTome(ctx context.Context, tomeID int, input ent.UpdateTomeInput) (*ent.Tome, error) + DeleteTome(ctx context.Context, tomeID int) (int, error) UpdateUser(ctx context.Context, userID int, input ent.UpdateUserInput) (*ent.User, error) } @@ -33,21 +32,6 @@ type MutationResolver interface { // region ***************************** args.gotpl ***************************** -func (ec *executionContext) field_Mutation_claimTasks_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { - var err error - args := map[string]interface{}{} - var arg0 models.ClaimTasksInput - if tmp, ok := rawArgs["input"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("input")) - arg0, err = ec.unmarshalNClaimTasksInput2realmᚗpubᚋtavernᚋinternalᚋgraphqlᚋmodelsᚐClaimTasksInput(ctx, tmp) - if err != nil { - return nil, err - } - } - args["input"] = arg0 - return args, nil -} - func (ec *executionContext) field_Mutation_createQuest_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -102,18 +86,18 @@ func (ec *executionContext) field_Mutation_createTome_args(ctx context.Context, return args, nil } -func (ec *executionContext) field_Mutation_submitTaskResult_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { +func (ec *executionContext) field_Mutation_deleteTome_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 models.SubmitTaskResultInput - if tmp, ok := rawArgs["input"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("input")) - arg0, err = ec.unmarshalNSubmitTaskResultInput2realmᚗpubᚋtavernᚋinternalᚋgraphqlᚋmodelsᚐSubmitTaskResultInput(ctx, tmp) + var arg0 int + if tmp, ok := rawArgs["tomeID"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tomeID")) + arg0, err = ec.unmarshalNID2int(ctx, tmp) if err != nil { return nil, err } } - args["input"] = arg0 + args["tomeID"] = arg0 return args, nil } @@ -189,6 +173,30 @@ func (ec *executionContext) field_Mutation_updateTag_args(ctx context.Context, r return args, nil } +func (ec *executionContext) field_Mutation_updateTome_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 int + if tmp, ok := rawArgs["tomeID"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tomeID")) + arg0, err = ec.unmarshalNID2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["tomeID"] = arg0 + var arg1 ent.UpdateTomeInput + if tmp, ok := rawArgs["input"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("input")) + arg1, err = ec.unmarshalNUpdateTomeInput2realmᚗpubᚋtavernᚋinternalᚋentᚐUpdateTomeInput(ctx, tmp) + if err != nil { + return nil, err + } + } + args["input"] = arg1 + return args, nil +} + func (ec *executionContext) field_Mutation_updateUser_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -693,8 +701,8 @@ func (ec *executionContext) fieldContext_Mutation_updateTag(ctx context.Context, return fc, nil } -func (ec *executionContext) _Mutation_claimTasks(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Mutation_claimTasks(ctx, field) +func (ec *executionContext) _Mutation_createTome(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_createTome(ctx, field) if err != nil { return graphql.Null } @@ -706,8 +714,32 @@ func (ec *executionContext) _Mutation_claimTasks(ctx context.Context, field grap } }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().ClaimTasks(rctx, fc.Args["input"].(models.ClaimTasksInput)) + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().CreateTome(rctx, fc.Args["input"].(ent.CreateTomeInput)) + } + directive1 := func(ctx context.Context) (interface{}, error) { + role, err := ec.unmarshalNRole2realmᚗpubᚋtavernᚋinternalᚋgraphqlᚋmodelsᚐRole(ctx, "USER") + if err != nil { + return nil, err + } + if ec.directives.RequireRole == nil { + return nil, errors.New("directive requireRole is not implemented") + } + return ec.directives.RequireRole(ctx, nil, directive0, role) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(*ent.Tome); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be *realm.pub/tavern/internal/ent.Tome`, tmp) }) if err != nil { ec.Error(ctx, err) @@ -719,12 +751,12 @@ func (ec *executionContext) _Mutation_claimTasks(ctx context.Context, field grap } return graphql.Null } - res := resTmp.([]*ent.Task) + res := resTmp.(*ent.Tome) fc.Result = res - return ec.marshalNTask2ᚕᚖrealmᚗpubᚋtavernᚋinternalᚋentᚐTaskᚄ(ctx, field.Selections, res) + return ec.marshalNTome2ᚖrealmᚗpubᚋtavernᚋinternalᚋentᚐTome(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Mutation_claimTasks(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_createTome(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, @@ -733,31 +765,31 @@ func (ec *executionContext) fieldContext_Mutation_claimTasks(ctx context.Context Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "id": - return ec.fieldContext_Task_id(ctx, field) + return ec.fieldContext_Tome_id(ctx, field) case "createdAt": - return ec.fieldContext_Task_createdAt(ctx, field) + return ec.fieldContext_Tome_createdAt(ctx, field) case "lastModifiedAt": - return ec.fieldContext_Task_lastModifiedAt(ctx, field) - case "claimedAt": - return ec.fieldContext_Task_claimedAt(ctx, field) - case "execStartedAt": - return ec.fieldContext_Task_execStartedAt(ctx, field) - case "execFinishedAt": - return ec.fieldContext_Task_execFinishedAt(ctx, field) - case "output": - return ec.fieldContext_Task_output(ctx, field) - case "outputSize": - return ec.fieldContext_Task_outputSize(ctx, field) - case "error": - return ec.fieldContext_Task_error(ctx, field) - case "quest": - return ec.fieldContext_Task_quest(ctx, field) - case "beacon": - return ec.fieldContext_Task_beacon(ctx, field) - case "reportedProcesses": - return ec.fieldContext_Task_reportedProcesses(ctx, field) + return ec.fieldContext_Tome_lastModifiedAt(ctx, field) + case "name": + return ec.fieldContext_Tome_name(ctx, field) + case "description": + return ec.fieldContext_Tome_description(ctx, field) + case "author": + return ec.fieldContext_Tome_author(ctx, field) + case "supportModel": + return ec.fieldContext_Tome_supportModel(ctx, field) + case "tactic": + return ec.fieldContext_Tome_tactic(ctx, field) + case "paramDefs": + return ec.fieldContext_Tome_paramDefs(ctx, field) + case "eldritch": + return ec.fieldContext_Tome_eldritch(ctx, field) + case "files": + return ec.fieldContext_Tome_files(ctx, field) + case "uploader": + return ec.fieldContext_Tome_uploader(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type Task", field.Name) + return nil, fmt.Errorf("no field named %q was found under type Tome", field.Name) }, } defer func() { @@ -767,15 +799,15 @@ func (ec *executionContext) fieldContext_Mutation_claimTasks(ctx context.Context } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_claimTasks_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_createTome_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Mutation_submitTaskResult(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Mutation_submitTaskResult(ctx, field) +func (ec *executionContext) _Mutation_updateTome(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_updateTome(ctx, field) if err != nil { return graphql.Null } @@ -787,22 +819,49 @@ func (ec *executionContext) _Mutation_submitTaskResult(ctx context.Context, fiel } }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().SubmitTaskResult(rctx, fc.Args["input"].(models.SubmitTaskResultInput)) + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().UpdateTome(rctx, fc.Args["tomeID"].(int), fc.Args["input"].(ent.UpdateTomeInput)) + } + directive1 := func(ctx context.Context) (interface{}, error) { + role, err := ec.unmarshalNRole2realmᚗpubᚋtavernᚋinternalᚋgraphqlᚋmodelsᚐRole(ctx, "ADMIN") + if err != nil { + return nil, err + } + if ec.directives.RequireRole == nil { + return nil, errors.New("directive requireRole is not implemented") + } + return ec.directives.RequireRole(ctx, nil, directive0, role) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(*ent.Tome); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be *realm.pub/tavern/internal/ent.Tome`, tmp) }) 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.(*ent.Task) + res := resTmp.(*ent.Tome) fc.Result = res - return ec.marshalOTask2ᚖrealmᚗpubᚋtavernᚋinternalᚋentᚐTask(ctx, field.Selections, res) + return ec.marshalNTome2ᚖrealmᚗpubᚋtavernᚋinternalᚋentᚐTome(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Mutation_submitTaskResult(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_updateTome(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, @@ -811,31 +870,31 @@ func (ec *executionContext) fieldContext_Mutation_submitTaskResult(ctx context.C Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "id": - return ec.fieldContext_Task_id(ctx, field) + return ec.fieldContext_Tome_id(ctx, field) case "createdAt": - return ec.fieldContext_Task_createdAt(ctx, field) + return ec.fieldContext_Tome_createdAt(ctx, field) case "lastModifiedAt": - return ec.fieldContext_Task_lastModifiedAt(ctx, field) - case "claimedAt": - return ec.fieldContext_Task_claimedAt(ctx, field) - case "execStartedAt": - return ec.fieldContext_Task_execStartedAt(ctx, field) - case "execFinishedAt": - return ec.fieldContext_Task_execFinishedAt(ctx, field) - case "output": - return ec.fieldContext_Task_output(ctx, field) - case "outputSize": - return ec.fieldContext_Task_outputSize(ctx, field) - case "error": - return ec.fieldContext_Task_error(ctx, field) - case "quest": - return ec.fieldContext_Task_quest(ctx, field) - case "beacon": - return ec.fieldContext_Task_beacon(ctx, field) - case "reportedProcesses": - return ec.fieldContext_Task_reportedProcesses(ctx, field) + return ec.fieldContext_Tome_lastModifiedAt(ctx, field) + case "name": + return ec.fieldContext_Tome_name(ctx, field) + case "description": + return ec.fieldContext_Tome_description(ctx, field) + case "author": + return ec.fieldContext_Tome_author(ctx, field) + case "supportModel": + return ec.fieldContext_Tome_supportModel(ctx, field) + case "tactic": + return ec.fieldContext_Tome_tactic(ctx, field) + case "paramDefs": + return ec.fieldContext_Tome_paramDefs(ctx, field) + case "eldritch": + return ec.fieldContext_Tome_eldritch(ctx, field) + case "files": + return ec.fieldContext_Tome_files(ctx, field) + case "uploader": + return ec.fieldContext_Tome_uploader(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type Task", field.Name) + return nil, fmt.Errorf("no field named %q was found under type Tome", field.Name) }, } defer func() { @@ -845,15 +904,15 @@ func (ec *executionContext) fieldContext_Mutation_submitTaskResult(ctx context.C } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_submitTaskResult_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_updateTome_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Mutation_createTome(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Mutation_createTome(ctx, field) +func (ec *executionContext) _Mutation_deleteTome(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_deleteTome(ctx, field) if err != nil { return graphql.Null } @@ -867,10 +926,10 @@ func (ec *executionContext) _Mutation_createTome(ctx context.Context, field grap resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { directive0 := func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().CreateTome(rctx, fc.Args["input"].(ent.CreateTomeInput)) + return ec.resolvers.Mutation().DeleteTome(rctx, fc.Args["tomeID"].(int)) } directive1 := func(ctx context.Context) (interface{}, error) { - role, err := ec.unmarshalNRole2realmᚗpubᚋtavernᚋinternalᚋgraphqlᚋmodelsᚐRole(ctx, "USER") + role, err := ec.unmarshalNRole2realmᚗpubᚋtavernᚋinternalᚋgraphqlᚋmodelsᚐRole(ctx, "ADMIN") if err != nil { return nil, err } @@ -887,10 +946,10 @@ func (ec *executionContext) _Mutation_createTome(ctx context.Context, field grap if tmp == nil { return nil, nil } - if data, ok := tmp.(*ent.Tome); ok { + if data, ok := tmp.(int); ok { return data, nil } - return nil, fmt.Errorf(`unexpected type %T from directive, should be *realm.pub/tavern/internal/ent.Tome`, tmp) + return nil, fmt.Errorf(`unexpected type %T from directive, should be int`, tmp) }) if err != nil { ec.Error(ctx, err) @@ -902,37 +961,19 @@ func (ec *executionContext) _Mutation_createTome(ctx context.Context, field grap } return graphql.Null } - res := resTmp.(*ent.Tome) + res := resTmp.(int) fc.Result = res - return ec.marshalNTome2ᚖrealmᚗpubᚋtavernᚋinternalᚋentᚐTome(ctx, field.Selections, res) + return ec.marshalNID2int(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Mutation_createTome(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_deleteTome(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "id": - return ec.fieldContext_Tome_id(ctx, field) - case "createdAt": - return ec.fieldContext_Tome_createdAt(ctx, field) - case "lastModifiedAt": - return ec.fieldContext_Tome_lastModifiedAt(ctx, field) - case "name": - return ec.fieldContext_Tome_name(ctx, field) - case "description": - return ec.fieldContext_Tome_description(ctx, field) - case "paramDefs": - return ec.fieldContext_Tome_paramDefs(ctx, field) - case "eldritch": - return ec.fieldContext_Tome_eldritch(ctx, field) - case "files": - return ec.fieldContext_Tome_files(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Tome", field.Name) + return nil, errors.New("field of type ID does not have child fields") }, } defer func() { @@ -942,7 +983,7 @@ func (ec *executionContext) fieldContext_Mutation_createTome(ctx context.Context } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_createTome_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_deleteTome_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } @@ -1019,6 +1060,8 @@ func (ec *executionContext) fieldContext_Mutation_updateUser(ctx context.Context return ec.fieldContext_User_isActivated(ctx, field) case "isAdmin": return ec.fieldContext_User_isAdmin(ctx, field) + case "tomes": + return ec.fieldContext_User_tomes(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -1100,20 +1143,23 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { out.Invalids++ } - case "claimTasks": + case "createTome": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { - return ec._Mutation_claimTasks(ctx, field) + return ec._Mutation_createTome(ctx, field) }) if out.Values[i] == graphql.Null { out.Invalids++ } - case "submitTaskResult": + case "updateTome": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { - return ec._Mutation_submitTaskResult(ctx, field) + return ec._Mutation_updateTome(ctx, field) }) - case "createTome": + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "deleteTome": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { - return ec._Mutation_createTome(ctx, field) + return ec._Mutation_deleteTome(ctx, field) }) if out.Values[i] == graphql.Null { out.Invalids++ diff --git a/tavern/internal/graphql/generated/root_.generated.go b/tavern/internal/graphql/generated/root_.generated.go index 28a7705ab..2c6cdea11 100644 --- a/tavern/internal/graphql/generated/root_.generated.go +++ b/tavern/internal/graphql/generated/root_.generated.go @@ -81,15 +81,15 @@ type ComplexityRoot struct { } Mutation struct { - ClaimTasks func(childComplexity int, input models.ClaimTasksInput) int - CreateQuest func(childComplexity int, beaconIDs []int, input ent.CreateQuestInput) int - CreateTag func(childComplexity int, input ent.CreateTagInput) int - CreateTome func(childComplexity int, input ent.CreateTomeInput) int - SubmitTaskResult func(childComplexity int, input models.SubmitTaskResultInput) int - UpdateBeacon func(childComplexity int, beaconID int, input ent.UpdateBeaconInput) int - UpdateHost func(childComplexity int, hostID int, input ent.UpdateHostInput) int - UpdateTag func(childComplexity int, tagID int, input ent.UpdateTagInput) int - UpdateUser func(childComplexity int, userID int, input ent.UpdateUserInput) int + CreateQuest func(childComplexity int, beaconIDs []int, input ent.CreateQuestInput) int + CreateTag func(childComplexity int, input ent.CreateTagInput) int + CreateTome func(childComplexity int, input ent.CreateTomeInput) int + DeleteTome func(childComplexity int, tomeID int) int + UpdateBeacon func(childComplexity int, beaconID int, input ent.UpdateBeaconInput) int + UpdateHost func(childComplexity int, hostID int, input ent.UpdateHostInput) int + UpdateTag func(childComplexity int, tagID int, input ent.UpdateTagInput) int + UpdateTome func(childComplexity int, tomeID int, input ent.UpdateTomeInput) int + UpdateUser func(childComplexity int, userID int, input ent.UpdateUserInput) int } PageInfo struct { @@ -170,6 +170,7 @@ type ComplexityRoot struct { } Tome struct { + Author func(childComplexity int) int CreatedAt func(childComplexity int) int Description func(childComplexity int) int Eldritch func(childComplexity int) int @@ -178,6 +179,9 @@ type ComplexityRoot struct { LastModifiedAt func(childComplexity int) int Name func(childComplexity int) int ParamDefs func(childComplexity int) int + SupportModel func(childComplexity int) int + Tactic func(childComplexity int) int + Uploader func(childComplexity int) int } User struct { @@ -186,6 +190,7 @@ type ComplexityRoot struct { IsAdmin func(childComplexity int) int Name func(childComplexity int) int PhotoURL func(childComplexity int) int + Tomes func(childComplexity int) int } } @@ -383,18 +388,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Host.Tags(childComplexity), true - case "Mutation.claimTasks": - if e.complexity.Mutation.ClaimTasks == nil { - break - } - - args, err := ec.field_Mutation_claimTasks_args(context.TODO(), rawArgs) - if err != nil { - return 0, false - } - - return e.complexity.Mutation.ClaimTasks(childComplexity, args["input"].(models.ClaimTasksInput)), true - case "Mutation.createQuest": if e.complexity.Mutation.CreateQuest == nil { break @@ -431,17 +424,17 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.CreateTome(childComplexity, args["input"].(ent.CreateTomeInput)), true - case "Mutation.submitTaskResult": - if e.complexity.Mutation.SubmitTaskResult == nil { + case "Mutation.deleteTome": + if e.complexity.Mutation.DeleteTome == nil { break } - args, err := ec.field_Mutation_submitTaskResult_args(context.TODO(), rawArgs) + args, err := ec.field_Mutation_deleteTome_args(context.TODO(), rawArgs) if err != nil { return 0, false } - return e.complexity.Mutation.SubmitTaskResult(childComplexity, args["input"].(models.SubmitTaskResultInput)), true + return e.complexity.Mutation.DeleteTome(childComplexity, args["tomeID"].(int)), true case "Mutation.updateBeacon": if e.complexity.Mutation.UpdateBeacon == nil { @@ -479,6 +472,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.UpdateTag(childComplexity, args["tagID"].(int), args["input"].(ent.UpdateTagInput)), true + case "Mutation.updateTome": + if e.complexity.Mutation.UpdateTome == nil { + break + } + + args, err := ec.field_Mutation_updateTome_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.UpdateTome(childComplexity, args["tomeID"].(int), args["input"].(ent.UpdateTomeInput)), true + case "Mutation.updateUser": if e.complexity.Mutation.UpdateUser == nil { break @@ -912,6 +917,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.TaskEdge.Node(childComplexity), true + case "Tome.author": + if e.complexity.Tome.Author == nil { + break + } + + return e.complexity.Tome.Author(childComplexity), true + case "Tome.createdAt": if e.complexity.Tome.CreatedAt == nil { break @@ -968,6 +980,27 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Tome.ParamDefs(childComplexity), true + case "Tome.supportModel": + if e.complexity.Tome.SupportModel == nil { + break + } + + return e.complexity.Tome.SupportModel(childComplexity), true + + case "Tome.tactic": + if e.complexity.Tome.Tactic == nil { + break + } + + return e.complexity.Tome.Tactic(childComplexity), true + + case "Tome.uploader": + if e.complexity.Tome.Uploader == nil { + break + } + + return e.complexity.Tome.Uploader(childComplexity), true + case "User.id": if e.complexity.User.ID == nil { break @@ -1003,6 +1036,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.User.PhotoURL(childComplexity), true + case "User.tomes": + if e.complexity.User.Tomes == nil { + break + } + + return e.complexity.User.Tomes(childComplexity), true + } return 0, false } @@ -1035,6 +1075,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputUpdateBeaconInput, ec.unmarshalInputUpdateHostInput, ec.unmarshalInputUpdateTagInput, + ec.unmarshalInputUpdateTomeInput, ec.unmarshalInputUpdateUserInput, ec.unmarshalInputUserWhereInput, ) @@ -1310,6 +1351,12 @@ input CreateTomeInput { name: String! """Information about the tome""" description: String! + """Name of the author who created the tome.""" + author: String! + """Information about the tomes support model.""" + supportModel: TomeSupportModel + """MITRE ATT&CK tactic provided by the tome.""" + tactic: TomeTactic """JSON string describing what parameters are used with the tome. Requires a list of JSON objects, one for each parameter.""" paramDefs: String """Eldritch script that will be executed when the tome is run""" @@ -2061,12 +2108,20 @@ type Tome implements Node { name: String! """Information about the tome""" description: String! + """Name of the author who created the tome.""" + author: String! + """Information about the tomes support model.""" + supportModel: TomeSupportModel! + """MITRE ATT&CK tactic provided by the tome.""" + tactic: TomeTactic! """JSON string describing what parameters are used with the tome. Requires a list of JSON objects, one for each parameter.""" paramDefs: String """Eldritch script that will be executed when the tome is run""" eldritch: String! """Any files required for tome execution that will be bundled and provided to the agent for download""" files: [File!] + """User who uploaded the tome (may be null).""" + uploader: User } """Ordering options for Tome connections""" input TomeOrder { @@ -2081,6 +2136,30 @@ enum TomeOrderField { LAST_MODIFIED_AT NAME } +"""TomeSupportModel is enum for the field support_model""" +enum TomeSupportModel @goModel(model: "realm.pub/tavern/internal/ent/tome.SupportModel") { + UNSPECIFIED + FIRST_PARTY + COMMUNITY +} +"""TomeTactic is enum for the field tactic""" +enum TomeTactic @goModel(model: "realm.pub/tavern/internal/ent/tome.Tactic") { + UNSPECIFIED + RECON + RESOURCE_DEVELOPMENT + INITIAL_ACCESS + EXECUTION + PERSISTENCE + PRIVILEGE_ESCALATION + DEFENSE_EVASION + CREDENTIAL_ACCESS + DISCOVERY + LATERAL_MOVEMENT + COLLECTION + COMMAND_AND_CONTROL + EXFILTRATION + IMPACT +} """ TomeWhereInput is used for filtering Tome objects. Input was generated by ent. @@ -2144,6 +2223,30 @@ input TomeWhereInput { descriptionHasSuffix: String descriptionEqualFold: String descriptionContainsFold: String + """author field predicates""" + author: String + authorNEQ: String + authorIn: [String!] + authorNotIn: [String!] + authorGT: String + authorGTE: String + authorLT: String + authorLTE: String + authorContains: String + authorHasPrefix: String + authorHasSuffix: String + authorEqualFold: String + authorContainsFold: String + """support_model field predicates""" + supportModel: TomeSupportModel + supportModelNEQ: TomeSupportModel + supportModelIn: [TomeSupportModel!] + supportModelNotIn: [TomeSupportModel!] + """tactic field predicates""" + tactic: TomeTactic + tacticNEQ: TomeTactic + tacticIn: [TomeTactic!] + tacticNotIn: [TomeTactic!] """param_defs field predicates""" paramDefs: String paramDefsNEQ: String @@ -2177,6 +2280,9 @@ input TomeWhereInput { """files edge predicates""" hasFiles: Boolean hasFilesWith: [FileWhereInput!] + """uploader edge predicates""" + hasUploader: Boolean + hasUploaderWith: [UserWhereInput!] } """ UpdateBeaconInput is used for update Beacon object. @@ -2217,6 +2323,32 @@ input UpdateTagInput { clearHosts: Boolean } """ +UpdateTomeInput is used for update Tome object. +Input was generated by ent. +""" +input UpdateTomeInput { + """Timestamp of when this ent was last updated""" + lastModifiedAt: Time + """Name of the tome""" + name: String + """Information about the tome""" + description: String + """Name of the author who created the tome.""" + author: String + """Information about the tomes support model.""" + supportModel: TomeSupportModel + """MITRE ATT&CK tactic provided by the tome.""" + tactic: TomeTactic + """JSON string describing what parameters are used with the tome. Requires a list of JSON objects, one for each parameter.""" + paramDefs: String + clearParamDefs: Boolean + """Eldritch script that will be executed when the tome is run""" + eldritch: String + addFileIDs: [ID!] + removeFileIDs: [ID!] + clearFiles: Boolean +} +""" UpdateUserInput is used for update User object. Input was generated by ent. """ @@ -2229,6 +2361,9 @@ input UpdateUserInput { isActivated: Boolean """True if the user is an Admin""" isAdmin: Boolean + addTomeIDs: [ID!] + removeTomeIDs: [ID!] + clearTomes: Boolean } type User implements Node { id: ID! @@ -2240,6 +2375,8 @@ type User implements Node { isActivated: Boolean! """True if the user is an Admin""" isAdmin: Boolean! + """Tomes uploaded by the user.""" + tomes: [Tome!] } """ UserWhereInput is used for filtering User objects. @@ -2292,6 +2429,9 @@ input UserWhereInput { """is_admin field predicates""" isAdmin: Boolean isAdminNEQ: Boolean + """tomes edge predicates""" + hasTomes: Boolean + hasTomesWith: [TomeWhereInput!] } `, BuiltIn: false}, {Name: "../schema/scalars.graphql", Input: `scalar Time @@ -2349,16 +2489,12 @@ scalar Uint64 createTag(input: CreateTagInput!): Tag! @requireRole(role: ADMIN) updateTag(tagID: ID!, input: UpdateTagInput!): Tag! @requireRole(role: USER) - ### - # Task - ### - claimTasks(input: ClaimTasksInput!,): [Task!]! - submitTaskResult(input: SubmitTaskResultInput!,): Task - ### # Tome ### createTome(input: CreateTomeInput!,): Tome! @requireRole(role: USER) + updateTome(tomeID: ID!, input: UpdateTomeInput!,): Tome! @requireRole(role: ADMIN) + deleteTome(tomeID: ID!): ID! @requireRole(role: ADMIN) ### # User diff --git a/tavern/internal/graphql/gqlgen.yml b/tavern/internal/graphql/gqlgen.yml index e6a203251..90ce0a7e0 100644 --- a/tavern/internal/graphql/gqlgen.yml +++ b/tavern/internal/graphql/gqlgen.yml @@ -59,3 +59,9 @@ models: Platform: model: - realm.pub/tavern/internal/ent/host.Platform + SupportModel: + model: + - realm.pub/tavern/internal/ent/tome.SupportModel + Tactic: + model: + - realm.pub/tavern/internal/ent/tome.Tactic diff --git a/tavern/internal/graphql/mutation.resolvers.go b/tavern/internal/graphql/mutation.resolvers.go index 141196657..031df1a58 100644 --- a/tavern/internal/graphql/mutation.resolvers.go +++ b/tavern/internal/graphql/mutation.resolvers.go @@ -7,16 +7,11 @@ package graphql import ( "context" "fmt" - "time" "realm.pub/tavern/internal/auth" "realm.pub/tavern/internal/ent" - "realm.pub/tavern/internal/ent/beacon" "realm.pub/tavern/internal/ent/file" - "realm.pub/tavern/internal/ent/host" - "realm.pub/tavern/internal/ent/task" "realm.pub/tavern/internal/graphql/generated" - "realm.pub/tavern/internal/graphql/models" ) // CreateQuest is the resolver for the createQuest field. @@ -122,120 +117,30 @@ func (r *mutationResolver) UpdateTag(ctx context.Context, tagID int, input ent.U return r.client.Tag.UpdateOneID(tagID).SetInput(input).Save(ctx) } -// ClaimTasks is the resolver for the claimTasks field. -func (r *mutationResolver) ClaimTasks(ctx context.Context, input models.ClaimTasksInput) ([]*ent.Task, error) { - now := time.Now() - - // 1. Upsert the host - hostID, err := r.client.Host.Create(). - SetIdentifier(input.HostIdentifier). - SetName(input.Hostname). - SetPlatform(host.Platform(input.HostPlatform)). - SetNillablePrimaryIP(input.HostPrimaryIP). - SetLastSeenAt(now). - OnConflict(). - UpdateNewValues(). - ID(ctx) - if err != nil { - return nil, fmt.Errorf("failed to upsert host entity: %w", err) - } - - // 2. Upsert the beacon - beaconID, err := r.client.Beacon.Create(). - SetPrincipal(input.Principal). - SetIdentifier(input.BeaconIdentifier). - SetAgentIdentifier(input.AgentIdentifier). - SetHostID(hostID). - SetLastSeenAt(now). - OnConflict(). - UpdateNewValues(). - ID(ctx) - if err != nil { - return nil, fmt.Errorf("failed to upsert beacon entity: %w", err) - } - - // 3. Load Tasks - tasks, err := r.client.Task.Query(). - Where(task.And( - task.HasBeaconWith(beacon.ID(beaconID)), - task.ClaimedAtIsNil(), - )). - All(ctx) - if err != nil { - return nil, fmt.Errorf("failed to query tasks: %w", err) - } - - // 4. Prepare Transaction for Claiming Tasks - tx, err := r.client.Tx(ctx) - if err != nil { - return nil, fmt.Errorf("failed to initialize transaction: %w", err) - } - client := tx.Client() - - // 5. Rollback transaction if we panic - defer func() { - if v := recover(); v != nil { - tx.Rollback() - panic(v) - } - }() - - // 6. Update all ClaimedAt timestamps to claim tasks - // ** Note: If one fails to update, we roll back the transaction and return the error - taskIDs := make([]int, 0, len(tasks)) - for _, t := range tasks { - _, err := client.Task.UpdateOne(t). - SetClaimedAt(now). - Save(ctx) - if err != nil { - return nil, rollback(tx, fmt.Errorf("failed to update task %d: %w", t.ID, err)) - } - taskIDs = append(taskIDs, t.ID) - } - - // 7. Commit the transaction - if err := tx.Commit(); err != nil { - return nil, rollback(tx, fmt.Errorf("failed to commit transaction: %w", err)) - } - - // 8. Load the tasks with our non transactional client (cannot use transaction after commit) - result := make([]*ent.Task, 0, len(taskIDs)) - for _, taskID := range taskIDs { - updatedTask, err := r.client.Task.Get(ctx, taskID) - if err != nil { - return nil, fmt.Errorf("failed to load updated task (but they were still updated) %d: %w", taskID, err) - } - result = append(result, updatedTask) - } - - // 9. Return claimed tasks - return result, nil -} - -// SubmitTaskResult is the resolver for the submitTaskResult field. -func (r *mutationResolver) SubmitTaskResult(ctx context.Context, input models.SubmitTaskResultInput) (*ent.Task, error) { - // 1. Load the task - t, err := r.client.Task.Get(ctx, input.TaskID) - if err != nil { - return nil, fmt.Errorf("failed to submit task result: %w", err) +// CreateTome is the resolver for the createTome field. +func (r *mutationResolver) CreateTome(ctx context.Context, input ent.CreateTomeInput) (*ent.Tome, error) { + var uploaderID *int + if uploader := auth.UserFromContext(ctx); uploader != nil { + uploaderID = &uploader.ID } - t, err = t.Update(). - SetExecStartedAt(input.ExecStartedAt). - SetOutput(fmt.Sprintf("%s%s", t.Output, input.Output)). - SetNillableExecFinishedAt(input.ExecFinishedAt). - SetNillableError(input.Error). + return r.client.Tome.Create(). + SetNillableUploaderID(uploaderID). + SetInput(input). Save(ctx) - if err != nil { - return nil, fmt.Errorf("failed to save submitted task result: %w", err) - } +} - return t, nil +// UpdateTome is the resolver for the updateTome field. +func (r *mutationResolver) UpdateTome(ctx context.Context, tomeID int, input ent.UpdateTomeInput) (*ent.Tome, error) { + return r.client.Tome.UpdateOneID(tomeID).SetInput(input).Save(ctx) } -// CreateTome is the resolver for the createTome field. -func (r *mutationResolver) CreateTome(ctx context.Context, input ent.CreateTomeInput) (*ent.Tome, error) { - return r.client.Tome.Create().SetInput(input).Save(ctx) +// DeleteTome is the resolver for the deleteTome field. +func (r *mutationResolver) DeleteTome(ctx context.Context, tomeID int) (int, error) { + if err := r.client.Tome.DeleteOneID(tomeID).Exec(ctx); err != nil { + return 0, err + } + return tomeID, nil } // UpdateUser is the resolver for the updateUser field. diff --git a/tavern/internal/graphql/quest_test.go b/tavern/internal/graphql/quest_test.go index 105683844..2886daed5 100644 --- a/tavern/internal/graphql/quest_test.go +++ b/tavern/internal/graphql/quest_test.go @@ -47,6 +47,7 @@ func TestCreateQuest(t *testing.T) { testTome := graph.Tome.Create(). SetName("Test Tome"). SetDescription("Ensures the world feels greeted"). + SetAuthor("kcarretto"). SetEldritch(`print("Hello World!")`). SaveX(ctx) @@ -63,6 +64,7 @@ func TestCreateQuest(t *testing.T) { testTomeWithFiles := graph.Tome.Create(). SetName("Test Tome With Files"). SetDescription("Ensures the world feels greeted"). + SetAuthor("kcarretto"). SetEldritch(`print("Hello World!")`). AddFiles(testFiles...). SaveX(ctx) diff --git a/tavern/internal/graphql/schema.graphql b/tavern/internal/graphql/schema.graphql index 9873e5171..4e3de17cf 100644 --- a/tavern/internal/graphql/schema.graphql +++ b/tavern/internal/graphql/schema.graphql @@ -173,6 +173,12 @@ input CreateTomeInput { name: String! """Information about the tome""" description: String! + """Name of the author who created the tome.""" + author: String! + """Information about the tomes support model.""" + supportModel: TomeSupportModel + """MITRE ATT&CK tactic provided by the tome.""" + tactic: TomeTactic """JSON string describing what parameters are used with the tome. Requires a list of JSON objects, one for each parameter.""" paramDefs: String """Eldritch script that will be executed when the tome is run""" @@ -924,12 +930,20 @@ type Tome implements Node { name: String! """Information about the tome""" description: String! + """Name of the author who created the tome.""" + author: String! + """Information about the tomes support model.""" + supportModel: TomeSupportModel! + """MITRE ATT&CK tactic provided by the tome.""" + tactic: TomeTactic! """JSON string describing what parameters are used with the tome. Requires a list of JSON objects, one for each parameter.""" paramDefs: String """Eldritch script that will be executed when the tome is run""" eldritch: String! """Any files required for tome execution that will be bundled and provided to the agent for download""" files: [File!] + """User who uploaded the tome (may be null).""" + uploader: User } """Ordering options for Tome connections""" input TomeOrder { @@ -944,6 +958,30 @@ enum TomeOrderField { LAST_MODIFIED_AT NAME } +"""TomeSupportModel is enum for the field support_model""" +enum TomeSupportModel @goModel(model: "realm.pub/tavern/internal/ent/tome.SupportModel") { + UNSPECIFIED + FIRST_PARTY + COMMUNITY +} +"""TomeTactic is enum for the field tactic""" +enum TomeTactic @goModel(model: "realm.pub/tavern/internal/ent/tome.Tactic") { + UNSPECIFIED + RECON + RESOURCE_DEVELOPMENT + INITIAL_ACCESS + EXECUTION + PERSISTENCE + PRIVILEGE_ESCALATION + DEFENSE_EVASION + CREDENTIAL_ACCESS + DISCOVERY + LATERAL_MOVEMENT + COLLECTION + COMMAND_AND_CONTROL + EXFILTRATION + IMPACT +} """ TomeWhereInput is used for filtering Tome objects. Input was generated by ent. @@ -1007,6 +1045,30 @@ input TomeWhereInput { descriptionHasSuffix: String descriptionEqualFold: String descriptionContainsFold: String + """author field predicates""" + author: String + authorNEQ: String + authorIn: [String!] + authorNotIn: [String!] + authorGT: String + authorGTE: String + authorLT: String + authorLTE: String + authorContains: String + authorHasPrefix: String + authorHasSuffix: String + authorEqualFold: String + authorContainsFold: String + """support_model field predicates""" + supportModel: TomeSupportModel + supportModelNEQ: TomeSupportModel + supportModelIn: [TomeSupportModel!] + supportModelNotIn: [TomeSupportModel!] + """tactic field predicates""" + tactic: TomeTactic + tacticNEQ: TomeTactic + tacticIn: [TomeTactic!] + tacticNotIn: [TomeTactic!] """param_defs field predicates""" paramDefs: String paramDefsNEQ: String @@ -1040,6 +1102,9 @@ input TomeWhereInput { """files edge predicates""" hasFiles: Boolean hasFilesWith: [FileWhereInput!] + """uploader edge predicates""" + hasUploader: Boolean + hasUploaderWith: [UserWhereInput!] } """ UpdateBeaconInput is used for update Beacon object. @@ -1080,6 +1145,32 @@ input UpdateTagInput { clearHosts: Boolean } """ +UpdateTomeInput is used for update Tome object. +Input was generated by ent. +""" +input UpdateTomeInput { + """Timestamp of when this ent was last updated""" + lastModifiedAt: Time + """Name of the tome""" + name: String + """Information about the tome""" + description: String + """Name of the author who created the tome.""" + author: String + """Information about the tomes support model.""" + supportModel: TomeSupportModel + """MITRE ATT&CK tactic provided by the tome.""" + tactic: TomeTactic + """JSON string describing what parameters are used with the tome. Requires a list of JSON objects, one for each parameter.""" + paramDefs: String + clearParamDefs: Boolean + """Eldritch script that will be executed when the tome is run""" + eldritch: String + addFileIDs: [ID!] + removeFileIDs: [ID!] + clearFiles: Boolean +} +""" UpdateUserInput is used for update User object. Input was generated by ent. """ @@ -1092,6 +1183,9 @@ input UpdateUserInput { isActivated: Boolean """True if the user is an Admin""" isAdmin: Boolean + addTomeIDs: [ID!] + removeTomeIDs: [ID!] + clearTomes: Boolean } type User implements Node { id: ID! @@ -1103,6 +1197,8 @@ type User implements Node { isActivated: Boolean! """True if the user is an Admin""" isAdmin: Boolean! + """Tomes uploaded by the user.""" + tomes: [Tome!] } """ UserWhereInput is used for filtering User objects. @@ -1155,6 +1251,9 @@ input UserWhereInput { """is_admin field predicates""" isAdmin: Boolean isAdminNEQ: Boolean + """tomes edge predicates""" + hasTomes: Boolean + hasTomesWith: [TomeWhereInput!] } input ClaimTasksInput { """The identity the beacon is authenticated as (e.g. 'root')""" @@ -1220,16 +1319,12 @@ type Mutation { createTag(input: CreateTagInput!): Tag! @requireRole(role: ADMIN) updateTag(tagID: ID!, input: UpdateTagInput!): Tag! @requireRole(role: USER) - ### - # Task - ### - claimTasks(input: ClaimTasksInput!,): [Task!]! - submitTaskResult(input: SubmitTaskResultInput!,): Task - ### # Tome ### createTome(input: CreateTomeInput!,): Tome! @requireRole(role: USER) + updateTome(tomeID: ID!, input: UpdateTomeInput!,): Tome! @requireRole(role: ADMIN) + deleteTome(tomeID: ID!): ID! @requireRole(role: ADMIN) ### # User diff --git a/tavern/internal/graphql/schema/ent.graphql b/tavern/internal/graphql/schema/ent.graphql index e111cdb63..8418090cd 100644 --- a/tavern/internal/graphql/schema/ent.graphql +++ b/tavern/internal/graphql/schema/ent.graphql @@ -168,6 +168,12 @@ input CreateTomeInput { name: String! """Information about the tome""" description: String! + """Name of the author who created the tome.""" + author: String! + """Information about the tomes support model.""" + supportModel: TomeSupportModel + """MITRE ATT&CK tactic provided by the tome.""" + tactic: TomeTactic """JSON string describing what parameters are used with the tome. Requires a list of JSON objects, one for each parameter.""" paramDefs: String """Eldritch script that will be executed when the tome is run""" @@ -919,12 +925,20 @@ type Tome implements Node { name: String! """Information about the tome""" description: String! + """Name of the author who created the tome.""" + author: String! + """Information about the tomes support model.""" + supportModel: TomeSupportModel! + """MITRE ATT&CK tactic provided by the tome.""" + tactic: TomeTactic! """JSON string describing what parameters are used with the tome. Requires a list of JSON objects, one for each parameter.""" paramDefs: String """Eldritch script that will be executed when the tome is run""" eldritch: String! """Any files required for tome execution that will be bundled and provided to the agent for download""" files: [File!] + """User who uploaded the tome (may be null).""" + uploader: User } """Ordering options for Tome connections""" input TomeOrder { @@ -939,6 +953,30 @@ enum TomeOrderField { LAST_MODIFIED_AT NAME } +"""TomeSupportModel is enum for the field support_model""" +enum TomeSupportModel @goModel(model: "realm.pub/tavern/internal/ent/tome.SupportModel") { + UNSPECIFIED + FIRST_PARTY + COMMUNITY +} +"""TomeTactic is enum for the field tactic""" +enum TomeTactic @goModel(model: "realm.pub/tavern/internal/ent/tome.Tactic") { + UNSPECIFIED + RECON + RESOURCE_DEVELOPMENT + INITIAL_ACCESS + EXECUTION + PERSISTENCE + PRIVILEGE_ESCALATION + DEFENSE_EVASION + CREDENTIAL_ACCESS + DISCOVERY + LATERAL_MOVEMENT + COLLECTION + COMMAND_AND_CONTROL + EXFILTRATION + IMPACT +} """ TomeWhereInput is used for filtering Tome objects. Input was generated by ent. @@ -1002,6 +1040,30 @@ input TomeWhereInput { descriptionHasSuffix: String descriptionEqualFold: String descriptionContainsFold: String + """author field predicates""" + author: String + authorNEQ: String + authorIn: [String!] + authorNotIn: [String!] + authorGT: String + authorGTE: String + authorLT: String + authorLTE: String + authorContains: String + authorHasPrefix: String + authorHasSuffix: String + authorEqualFold: String + authorContainsFold: String + """support_model field predicates""" + supportModel: TomeSupportModel + supportModelNEQ: TomeSupportModel + supportModelIn: [TomeSupportModel!] + supportModelNotIn: [TomeSupportModel!] + """tactic field predicates""" + tactic: TomeTactic + tacticNEQ: TomeTactic + tacticIn: [TomeTactic!] + tacticNotIn: [TomeTactic!] """param_defs field predicates""" paramDefs: String paramDefsNEQ: String @@ -1035,6 +1097,9 @@ input TomeWhereInput { """files edge predicates""" hasFiles: Boolean hasFilesWith: [FileWhereInput!] + """uploader edge predicates""" + hasUploader: Boolean + hasUploaderWith: [UserWhereInput!] } """ UpdateBeaconInput is used for update Beacon object. @@ -1075,6 +1140,32 @@ input UpdateTagInput { clearHosts: Boolean } """ +UpdateTomeInput is used for update Tome object. +Input was generated by ent. +""" +input UpdateTomeInput { + """Timestamp of when this ent was last updated""" + lastModifiedAt: Time + """Name of the tome""" + name: String + """Information about the tome""" + description: String + """Name of the author who created the tome.""" + author: String + """Information about the tomes support model.""" + supportModel: TomeSupportModel + """MITRE ATT&CK tactic provided by the tome.""" + tactic: TomeTactic + """JSON string describing what parameters are used with the tome. Requires a list of JSON objects, one for each parameter.""" + paramDefs: String + clearParamDefs: Boolean + """Eldritch script that will be executed when the tome is run""" + eldritch: String + addFileIDs: [ID!] + removeFileIDs: [ID!] + clearFiles: Boolean +} +""" UpdateUserInput is used for update User object. Input was generated by ent. """ @@ -1087,6 +1178,9 @@ input UpdateUserInput { isActivated: Boolean """True if the user is an Admin""" isAdmin: Boolean + addTomeIDs: [ID!] + removeTomeIDs: [ID!] + clearTomes: Boolean } type User implements Node { id: ID! @@ -1098,6 +1192,8 @@ type User implements Node { isActivated: Boolean! """True if the user is an Admin""" isAdmin: Boolean! + """Tomes uploaded by the user.""" + tomes: [Tome!] } """ UserWhereInput is used for filtering User objects. @@ -1150,4 +1246,7 @@ input UserWhereInput { """is_admin field predicates""" isAdmin: Boolean isAdminNEQ: Boolean + """tomes edge predicates""" + hasTomes: Boolean + hasTomesWith: [TomeWhereInput!] } diff --git a/tavern/internal/graphql/schema/mutation.graphql b/tavern/internal/graphql/schema/mutation.graphql index 01dad2ac5..adf5b1194 100644 --- a/tavern/internal/graphql/schema/mutation.graphql +++ b/tavern/internal/graphql/schema/mutation.graphql @@ -20,16 +20,12 @@ type Mutation { createTag(input: CreateTagInput!): Tag! @requireRole(role: ADMIN) updateTag(tagID: ID!, input: UpdateTagInput!): Tag! @requireRole(role: USER) - ### - # Task - ### - claimTasks(input: ClaimTasksInput!,): [Task!]! - submitTaskResult(input: SubmitTaskResultInput!,): Task - ### # Tome ### createTome(input: CreateTomeInput!,): Tome! @requireRole(role: USER) + updateTome(tomeID: ID!, input: UpdateTomeInput!,): Tome! @requireRole(role: ADMIN) + deleteTome(tomeID: ID!): ID! @requireRole(role: ADMIN) ### # User diff --git a/tavern/internal/graphql/testdata/mutations/claimTasks/ExistingBeacon.yml b/tavern/internal/graphql/testdata/mutations/claimTasks/ExistingBeacon.yml deleted file mode 100644 index 71b5f7b49..000000000 --- a/tavern/internal/graphql/testdata/mutations/claimTasks/ExistingBeacon.yml +++ /dev/null @@ -1,22 +0,0 @@ -state: | - INSERT INTO `hosts` (id, name, identifier) - VALUES (1010,"db1","EXISTING-HOST"); - INSERT INTO `beacons` (id, name, identifier, beacon_host) - VALUES (1337,"delightful-lich","EXISTING-BEACON",1010); -query: | - mutation ExistingBeacon($input: ClaimTasksInput!) { - claimTasks(input: $input) { - id - } - } -variables: - input: - principal: root - hostname: some-machine - hostPlatform: Linux - beaconIdentifier: EXISTING-BEACON - hostIdentifier: MY-HOST - agentIdentifier: COOL-TEST - -expected: - claimTasks: [] diff --git a/tavern/internal/graphql/testdata/mutations/claimTasks/Filters.yml b/tavern/internal/graphql/testdata/mutations/claimTasks/Filters.yml deleted file mode 100644 index 86072d214..000000000 --- a/tavern/internal/graphql/testdata/mutations/claimTasks/Filters.yml +++ /dev/null @@ -1,38 +0,0 @@ -state: | - INSERT INTO `hosts` (id, name, identifier) - VALUES (1010,"db1","EXISTING-HOST"); - INSERT INTO `beacons` (id, name, identifier, beacon_host) - VALUES (1337,"delightful-lich","EXISTING-BEACON",1010); - INSERT INTO `beacons` (id, name, identifier,beacon_host) - VALUES (1338,"bad-boi","BAD-BEACON",1010); - INSERT INTO `tomes` (id, name, description, eldritch, hash, created_at, last_modified_at) - VALUES (2000,"Test Tome","Used in a unit test :D","print('Hello World!')","abcdefg","2023-03-04 14:51:13","2023-03-04 14:51:13"); - INSERT INTO `quests` (id, quest_tome, name, created_at, last_modified_at) - VALUES (7000,2000,"Test Quest","2023-03-04 14:51:13","2023-03-04 14:51:13"); - INSERT INTO `tasks` (id, task_beacon, quest_tasks, created_at, last_modified_at) - VALUES (8000,1337,7000,"2023-03-04 14:51:13","2023-03-04 14:51:13"); - INSERT INTO `tasks` (id, task_beacon, quest_tasks, created_at, last_modified_at) - VALUES (8001,1337,7000,"2023-03-04 14:51:13","2023-03-04 14:51:13"); - INSERT INTO `tasks` (id, task_beacon, quest_tasks, created_at, last_modified_at) - VALUES (8002,1338,7000,"2023-03-04 14:51:13","2023-03-04 14:51:13"); - INSERT INTO `tasks` (id, task_beacon, quest_tasks, created_at, last_modified_at, claimed_at) - VALUES (8003,1337,7000,"2023-03-04 14:51:13","2023-03-04 14:51:13","2023-03-04 14:51:13"); -query: | - mutation Filters($input: ClaimTasksInput!) { - claimTasks(input: $input) { - id - } - } -variables: - input: - principal: root - hostname: some-machine - hostPlatform: Linux - beaconIdentifier: EXISTING-BEACON - hostIdentifier: MY-HOST - agentIdentifier: COOL-TEST - -expected: - claimTasks: - - id: "8000" - - id: "8001" diff --git a/tavern/internal/graphql/testdata/mutations/claimTasks/MultiTask.yml b/tavern/internal/graphql/testdata/mutations/claimTasks/MultiTask.yml deleted file mode 100644 index 3f63502e4..000000000 --- a/tavern/internal/graphql/testdata/mutations/claimTasks/MultiTask.yml +++ /dev/null @@ -1,32 +0,0 @@ -state: | - INSERT INTO `hosts` (id, name, identifier) - VALUES (1010,"db1","EXISTING-HOST"); - INSERT INTO `beacons` (id, name, identifier, beacon_host) - VALUES (1337,"delightful-lich","EXISTING-BEACON",1010); - INSERT INTO `tomes` (id, name, description, eldritch, hash, created_at, last_modified_at) - VALUES (2000,"Test Tome","Used in a unit test :D","print('Hello World!')","abcdefg","2023-03-04 14:51:13","2023-03-04 14:51:13"); - INSERT INTO `quests` (id, quest_tome, name, created_at, last_modified_at) - VALUES (7000,2000,"Test Quest","2023-03-04 14:51:13","2023-03-04 14:51:13"); - INSERT INTO `tasks` (id, task_beacon, quest_tasks, created_at, last_modified_at) - VALUES (8000,1337,7000,"2023-03-04 14:51:13","2023-03-04 14:51:13"); - INSERT INTO `tasks` (id, task_beacon, quest_tasks, created_at, last_modified_at) - VALUES (8001,1337,7000,"2023-03-04 14:51:13","2023-03-04 14:51:13"); -query: | - mutation MutiTask($input: ClaimTasksInput!) { - claimTasks(input: $input) { - id - } - } -variables: - input: - principal: root - hostname: some-machine - hostPlatform: Linux - beaconIdentifier: EXISTING-BEACON - hostIdentifier: MY-HOST - agentIdentifier: COOL-TEST - -expected: - claimTasks: - - id: "8000" - - id: "8001" diff --git a/tavern/internal/graphql/testdata/mutations/claimTasks/NewBeacon.yml b/tavern/internal/graphql/testdata/mutations/claimTasks/NewBeacon.yml deleted file mode 100644 index ce5f0337f..000000000 --- a/tavern/internal/graphql/testdata/mutations/claimTasks/NewBeacon.yml +++ /dev/null @@ -1,17 +0,0 @@ -query: | - mutation NewBeacon($input: ClaimTasksInput!) { - claimTasks(input: $input) { - id - } - } -variables: - input: - principal: root - hostname: some-machine - hostPlatform: Linux - beaconIdentifier: BEACON-IDENTIFIER - hostIdentifier: MY-HOST - agentIdentifier: COOL-TEST - -expected: - claimTasks: [] \ No newline at end of file diff --git a/tavern/internal/graphql/testdata/mutations/claimTasks/OneTask.yml b/tavern/internal/graphql/testdata/mutations/claimTasks/OneTask.yml deleted file mode 100644 index ad03496b7..000000000 --- a/tavern/internal/graphql/testdata/mutations/claimTasks/OneTask.yml +++ /dev/null @@ -1,29 +0,0 @@ -state: | - INSERT INTO `hosts` (id, name, identifier) - VALUES (1010,"db1","EXISTING-HOST"); - INSERT INTO `beacons` (id, name, identifier, beacon_host) - VALUES (1337,"delightful-lich","EXISTING-BEACON",1010); - INSERT INTO `tomes` (id, name, description, eldritch, hash, created_at, last_modified_at) - VALUES (2000,"Test Tome","Used in a unit test :D","print('Hello World!')","abcdefg","2023-03-04 14:51:13","2023-03-04 14:51:13"); - INSERT INTO `quests` (id, quest_tome, name, created_at, last_modified_at) - VALUES (7000,2000,"Test Quest","2023-03-04 14:51:13","2023-03-04 14:51:13"); - INSERT INTO `tasks` (id, task_beacon, quest_tasks, created_at, last_modified_at) - VALUES (8000,1337,7000,"2023-03-04 14:51:13","2023-03-04 14:51:13"); -query: | - mutation OneTask($input: ClaimTasksInput!) { - claimTasks(input: $input) { - id - } - } -variables: - input: - principal: root - hostname: some-machine - hostPlatform: Linux - beaconIdentifier: EXISTING-BEACON - hostIdentifier: MY-HOST - agentIdentifier: COOL-TEST - -expected: - claimTasks: - - id: "8000" diff --git a/tavern/internal/graphql/testdata/mutations/createQuest/NoFiles.yml b/tavern/internal/graphql/testdata/mutations/createQuest/NoFiles.yml index 6c06efdcf..260e3801e 100644 --- a/tavern/internal/graphql/testdata/mutations/createQuest/NoFiles.yml +++ b/tavern/internal/graphql/testdata/mutations/createQuest/NoFiles.yml @@ -5,8 +5,8 @@ state: | VALUES (1010,"db1","EXISTING-HOST"); INSERT INTO `beacons` (id, name, identifier, beacon_host) VALUES (1337,"delightful-lich","ABCDEFG-123456",1010); - INSERT INTO `tomes` (id, name, description, eldritch, hash, created_at, last_modified_at) - VALUES (2000,"Test Tome","Used in a unit test :D", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `tomes` (id, name, description, author, eldritch, hash, created_at, last_modified_at) + VALUES (2000,"Test Tome","Used in a unit test :D", "kcarretto", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); requestor: session_token: secretToken query: | diff --git a/tavern/internal/graphql/testdata/mutations/createQuest/WithFiles.yml b/tavern/internal/graphql/testdata/mutations/createQuest/WithFiles.yml index bc3be0772..7bfe516ff 100644 --- a/tavern/internal/graphql/testdata/mutations/createQuest/WithFiles.yml +++ b/tavern/internal/graphql/testdata/mutations/createQuest/WithFiles.yml @@ -5,8 +5,8 @@ state: | VALUES (1010,"db1","EXISTING-HOST"); INSERT INTO `beacons` (id, name, identifier, beacon_host) VALUES (1337,"delightful-lich","ABCDEFG-123456",1010); - INSERT INTO `tomes` (id, name, description, eldritch, hash, created_at, last_modified_at) - VALUES (2000,"Test Tome","Used in a unit test :D", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `tomes` (id, name, description, author, eldritch, hash, created_at, last_modified_at) + VALUES (2000,"Test Tome","Used in a unit test :D", "kcarretto", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); INSERT INTO `files` (id, name, content, hash, created_at, last_modified_at) VALUES (3000, "TestFile1", "hello world", "a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); INSERT INTO `files` (id, name, content, hash, created_at, last_modified_at) diff --git a/tavern/internal/graphql/testdata/mutations/createTome/NoFiles.yml b/tavern/internal/graphql/testdata/mutations/createTome/NoFiles.yml index bedcaaef0..3ca3e9044 100644 --- a/tavern/internal/graphql/testdata/mutations/createTome/NoFiles.yml +++ b/tavern/internal/graphql/testdata/mutations/createTome/NoFiles.yml @@ -8,17 +8,26 @@ query: | createTome(input: $input) { name description + author eldritch + + uploader { + id + } } } variables: input: name: "WonderfulTome" description: It's for a test + author: kcarretto eldritch: print("Hello World") expected: createTome: name: "WonderfulTome" description: It's for a test - eldritch: print("Hello World") \ No newline at end of file + author: kcarretto + eldritch: print("Hello World") + uploader: + id: "5" diff --git a/tavern/internal/graphql/testdata/mutations/createTome/WithFiles.yml b/tavern/internal/graphql/testdata/mutations/createTome/WithFiles.yml index 9ea024a18..9f6cc5978 100644 --- a/tavern/internal/graphql/testdata/mutations/createTome/WithFiles.yml +++ b/tavern/internal/graphql/testdata/mutations/createTome/WithFiles.yml @@ -16,6 +16,7 @@ query: | createTome(input: $input) { name description + author eldritch files { id @@ -26,6 +27,7 @@ variables: input: name: "WonderfulTome" description: So we can test things + author: kcarretto eldritch: print("yay!") fileIDs: - 3000 @@ -35,6 +37,7 @@ expected: createTome: name: "WonderfulTome" description: So we can test things + author: kcarretto eldritch: print("yay!") files: - id: "3000" diff --git a/tavern/internal/graphql/testdata/mutations/deleteTome/PermissionDenied.yml b/tavern/internal/graphql/testdata/mutations/deleteTome/PermissionDenied.yml new file mode 100644 index 000000000..11a7a91d6 --- /dev/null +++ b/tavern/internal/graphql/testdata/mutations/deleteTome/PermissionDenied.yml @@ -0,0 +1,27 @@ +state: | + INSERT INTO `users` (id,oauth_id,photo_url,name,session_token,is_activated,is_admin) + VALUES (5,"test_oauth_id","https://photos.com","test","secretToken",true,false); + INSERT INTO `hosts` (id, name, identifier) + VALUES (1010,"db1","EXISTING-HOST"); + INSERT INTO `beacons` (id, name, identifier, beacon_host) + VALUES (1337,"delightful-lich","ABCDEFG-123456",1010); + INSERT INTO `tomes` (id, name, description, author, eldritch, hash, created_at, last_modified_at) + VALUES (2000,"Test Tome","Used in a unit test :D", "kcarretto", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `files` (id, name, content, hash, created_at, last_modified_at) + VALUES (3000, "TestFile1", "hello world", "a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `files` (id, name, content, hash, created_at, last_modified_at) + VALUES (3001, "TestFile2", "some test", "a9d9e8df0488c7e7e9236e43fe0c9385d7ea6920700db55d305f55dca76ddb0b", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `tome_files` (tome_id, file_id) + VALUES (2000, 3000); + INSERT INTO `tome_files` (tome_id, file_id) + VALUES (2000, 3001); +requestor: + session_token: secretToken +query: | + mutation DeleteTome($tomeID: ID!) { + deleteTome(tomeID:$tomeID) + } +variables: + tomeID: 2000 + +expected_error: "permission denied" diff --git a/tavern/internal/graphql/testdata/mutations/deleteTome/Singular.yml b/tavern/internal/graphql/testdata/mutations/deleteTome/Singular.yml new file mode 100644 index 000000000..4550bbee4 --- /dev/null +++ b/tavern/internal/graphql/testdata/mutations/deleteTome/Singular.yml @@ -0,0 +1,28 @@ +state: | + INSERT INTO `users` (id,oauth_id,photo_url,name,session_token,is_activated,is_admin) + VALUES (5,"test_oauth_id","https://photos.com","test","secretToken",true,true); + INSERT INTO `hosts` (id, name, identifier) + VALUES (1010,"db1","EXISTING-HOST"); + INSERT INTO `beacons` (id, name, identifier, beacon_host) + VALUES (1337,"delightful-lich","ABCDEFG-123456",1010); + INSERT INTO `tomes` (id, name, description, author, eldritch, hash, created_at, last_modified_at) + VALUES (2000,"Test Tome","Used in a unit test :D", "kcarretto", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `files` (id, name, content, hash, created_at, last_modified_at) + VALUES (3000, "TestFile1", "hello world", "a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `files` (id, name, content, hash, created_at, last_modified_at) + VALUES (3001, "TestFile2", "some test", "a9d9e8df0488c7e7e9236e43fe0c9385d7ea6920700db55d305f55dca76ddb0b", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `tome_files` (tome_id, file_id) + VALUES (2000, 3000); + INSERT INTO `tome_files` (tome_id, file_id) + VALUES (2000, 3001); +requestor: + session_token: secretToken +query: | + mutation DeleteTome($tomeID: ID!) { + deleteTome(tomeID:$tomeID) + } +variables: + tomeID: 2000 + +expected: + deleteTome: "2000" diff --git a/tavern/internal/graphql/testdata/mutations/submitTaskResult/Append.yml b/tavern/internal/graphql/testdata/mutations/submitTaskResult/Append.yml deleted file mode 100644 index da1e1adcd..000000000 --- a/tavern/internal/graphql/testdata/mutations/submitTaskResult/Append.yml +++ /dev/null @@ -1,31 +0,0 @@ -state: | - INSERT INTO `hosts` (id, name, identifier) - VALUES (1010,"db1","EXISTING-HOST"); - INSERT INTO `beacons` (id, name, identifier, beacon_host) - VALUES (1337,"delightful-lich","EXISTING-BEACON",1010); - INSERT INTO `tomes` (id, name, description, eldritch, hash, created_at, last_modified_at) - VALUES (2000,"Test Tome","Used in a unit test :D","print('Hello World!')","abcdefg","2023-03-04 14:51:13","2023-03-04 14:51:13"); - INSERT INTO `quests` (id, quest_tome, name, created_at, last_modified_at) - VALUES (7000,2000,"Test Quest","2023-03-04 14:51:13","2023-03-04 14:51:13"); - INSERT INTO `tasks` (id, task_beacon, quest_tasks, created_at, last_modified_at) - VALUES (8000,1337,7000,"2023-03-04 14:51:13","2023-03-04 14:51:13"); - INSERT INTO `tasks` (id, task_beacon, quest_tasks, created_at, last_modified_at, output) - VALUES (8001,1337,7000,"2023-03-04 14:51:13","2023-03-04 14:51:13", "one"); -query: | - mutation Append($input: SubmitTaskResultInput!) { - submitTaskResult(input: $input) { - id - output - } - } -variables: - input: - taskID: 8001 - execStartedAt: 2023-03-04 14:51:13 - execFinishedAt: 2023-03-04 14:51:13 - output: -two - -expected: - submitTaskResult: - id: "8001" - output: one-two diff --git a/tavern/internal/graphql/testdata/mutations/submitTaskResult/OneAndDone.yml b/tavern/internal/graphql/testdata/mutations/submitTaskResult/OneAndDone.yml deleted file mode 100644 index 688809c45..000000000 --- a/tavern/internal/graphql/testdata/mutations/submitTaskResult/OneAndDone.yml +++ /dev/null @@ -1,31 +0,0 @@ -state: | - INSERT INTO `hosts` (id, name, identifier) - VALUES (1010,"db1","EXISTING-HOST"); - INSERT INTO `beacons` (id, name, identifier, beacon_host) - VALUES (1337,"delightful-lich","EXISTING-BEACON",1010); - INSERT INTO `tomes` (id, name, description, eldritch, hash, created_at, last_modified_at) - VALUES (2000,"Test Tome","Used in a unit test :D","print('Hello World!')","abcdefg","2023-03-04 14:51:13","2023-03-04 14:51:13"); - INSERT INTO `quests` (id, quest_tome, name, created_at, last_modified_at) - VALUES (7000,2000,"Test Quest","2023-03-04 14:51:13","2023-03-04 14:51:13"); - INSERT INTO `tasks` (id, task_beacon, quest_tasks, created_at, last_modified_at) - VALUES (8000,1337,7000,"2023-03-04 14:51:13","2023-03-04 14:51:13"); - INSERT INTO `tasks` (id, task_beacon, quest_tasks, created_at, last_modified_at) - VALUES (8001,1337,7000,"2023-03-04 14:51:13","2023-03-04 14:51:13"); -query: | - mutation Complete($input: SubmitTaskResultInput!) { - submitTaskResult(input: $input) { - id - output - } - } -variables: - input: - taskID: 8000 - execStartedAt: 2023-03-04 14:51:13 - execFinishedAt: 2023-03-04 14:51:13 - output: I'm done - -expected: - submitTaskResult: - id: "8000" - output: I'm done diff --git a/tavern/internal/graphql/testdata/mutations/updateTome/PermissionDenied.yml b/tavern/internal/graphql/testdata/mutations/updateTome/PermissionDenied.yml new file mode 100644 index 000000000..615dc2b80 --- /dev/null +++ b/tavern/internal/graphql/testdata/mutations/updateTome/PermissionDenied.yml @@ -0,0 +1,33 @@ +state: | + INSERT INTO `users` (id,oauth_id,photo_url,name,session_token,is_activated,is_admin) + VALUES (5,"test_oauth_id","https://photos.com","test","secretToken",true,false); + INSERT INTO `hosts` (id, name, identifier) + VALUES (1010,"db1","EXISTING-HOST"); + INSERT INTO `beacons` (id, name, identifier, beacon_host) + VALUES (1337,"delightful-lich","ABCDEFG-123456",1010); + INSERT INTO `tomes` (id, name, description, author, eldritch, hash, created_at, last_modified_at) + VALUES (2000,"Test Tome","Used in a unit test :D", "kcarretto", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `files` (id, name, content, hash, created_at, last_modified_at) + VALUES (3000, "TestFile1", "hello world", "a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `files` (id, name, content, hash, created_at, last_modified_at) + VALUES (3001, "TestFile2", "some test", "a9d9e8df0488c7e7e9236e43fe0c9385d7ea6920700db55d305f55dca76ddb0b", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `tome_files` (tome_id, file_id) + VALUES (2000, 3000); + INSERT INTO `tome_files` (tome_id, file_id) + VALUES (2000, 3001); +requestor: + session_token: secretToken +query: | + mutation UpdateTome($tomeID: ID!, $input: UpdateTomeInput!) { + updateTome(tomeID:$tomeID, input:$input) { + supportModel + tactic + } + } +variables: + tomeID: 2000 + input: + supportModel: FIRST_PARTY + tactic: RECON + +expected_error: "permission denied" diff --git a/tavern/internal/graphql/testdata/mutations/updateTome/Singular.yml b/tavern/internal/graphql/testdata/mutations/updateTome/Singular.yml new file mode 100644 index 000000000..4d609f9a6 --- /dev/null +++ b/tavern/internal/graphql/testdata/mutations/updateTome/Singular.yml @@ -0,0 +1,36 @@ +state: | + INSERT INTO `users` (id,oauth_id,photo_url,name,session_token,is_activated,is_admin) + VALUES (5,"test_oauth_id","https://photos.com","test","secretToken",true,true); + INSERT INTO `hosts` (id, name, identifier) + VALUES (1010,"db1","EXISTING-HOST"); + INSERT INTO `beacons` (id, name, identifier, beacon_host) + VALUES (1337,"delightful-lich","ABCDEFG-123456",1010); + INSERT INTO `tomes` (id, name, description, author, eldritch, hash, created_at, last_modified_at) + VALUES (2000,"Test Tome","Used in a unit test :D", "kcarretto", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `files` (id, name, content, hash, created_at, last_modified_at) + VALUES (3000, "TestFile1", "hello world", "a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `files` (id, name, content, hash, created_at, last_modified_at) + VALUES (3001, "TestFile2", "some test", "a9d9e8df0488c7e7e9236e43fe0c9385d7ea6920700db55d305f55dca76ddb0b", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `tome_files` (tome_id, file_id) + VALUES (2000, 3000); + INSERT INTO `tome_files` (tome_id, file_id) + VALUES (2000, 3001); +requestor: + session_token: secretToken +query: | + mutation UpdateTome($tomeID: ID!, $input: UpdateTomeInput!) { + updateTome(tomeID:$tomeID, input:$input) { + supportModel + tactic + } + } +variables: + tomeID: 2000 + input: + supportModel: FIRST_PARTY + tactic: RECON + +expected: + updateTome: + supportModel: FIRST_PARTY + tactic: RECON diff --git a/tavern/internal/graphql/testdata/queries/quests/FilterByID.yml b/tavern/internal/graphql/testdata/queries/quests/FilterByID.yml index 2dd9c890f..611c5c731 100644 --- a/tavern/internal/graphql/testdata/queries/quests/FilterByID.yml +++ b/tavern/internal/graphql/testdata/queries/quests/FilterByID.yml @@ -5,8 +5,8 @@ state: | VALUES (1010,"db1","EXISTING-HOST"); INSERT INTO `beacons` (id, name, identifier, beacon_host) VALUES (1337,"delightful-lich","ABCDEFG-123456",1010); - INSERT INTO `tomes` (id, name, description, eldritch, hash, created_at, last_modified_at) - VALUES (2000,"Test Tome","Used in a unit test :D", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `tomes` (id, name, description, author, eldritch, hash, created_at, last_modified_at) + VALUES (2000,"Test Tome","Used in a unit test :D", "kcarretto", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); INSERT INTO `quests` (id, quest_tome, name, created_at, last_modified_at) VALUES (7000,2000,"Test Quest","2023-03-04 14:51:13","2023-03-04 14:51:13"); INSERT INTO `quests` (id, quest_tome, name, created_at, last_modified_at) diff --git a/tavern/internal/graphql/testdata/queries/quests/Singular.yml b/tavern/internal/graphql/testdata/queries/quests/Singular.yml index daa8c93ce..99e68c492 100644 --- a/tavern/internal/graphql/testdata/queries/quests/Singular.yml +++ b/tavern/internal/graphql/testdata/queries/quests/Singular.yml @@ -5,8 +5,8 @@ state: | VALUES (1010,"db1","EXISTING-HOST"); INSERT INTO `beacons` (id, name, identifier, beacon_host) VALUES (1337,"delightful-lich","ABCDEFG-123456",1010); - INSERT INTO `tomes` (id, name, description, eldritch, hash, created_at, last_modified_at) - VALUES (2000,"Test Tome","Used in a unit test :D", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `tomes` (id, name, description, author, eldritch, hash, created_at, last_modified_at) + VALUES (2000,"Test Tome","Used in a unit test :D", "kcarretto", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); INSERT INTO `quests` (id, quest_tome, name, created_at, last_modified_at) VALUES (7000,2000,"Test Quest","2023-03-04 14:51:13","2023-03-04 14:51:13"); requestor: diff --git a/tavern/internal/graphql/testdata/queries/tasks/BackwardPaginate.yml b/tavern/internal/graphql/testdata/queries/tasks/BackwardPaginate.yml index 8209dd524..19df4c8ca 100644 --- a/tavern/internal/graphql/testdata/queries/tasks/BackwardPaginate.yml +++ b/tavern/internal/graphql/testdata/queries/tasks/BackwardPaginate.yml @@ -5,8 +5,8 @@ state: | VALUES (1010,"db1","EXISTING-HOST"); INSERT INTO `beacons` (id, name, identifier, beacon_host) VALUES (1337,"delightful-lich","ABCDEFG-123456",1010); - INSERT INTO `tomes` (id, name, description, eldritch, hash, created_at, last_modified_at) - VALUES (2000,"Test Tome","Used in a unit test :D", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `tomes` (id, name, description, author, eldritch, hash, created_at, last_modified_at) + VALUES (2000,"Test Tome","Used in a unit test :D", "kcarretto", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); INSERT INTO `quests` (id, quest_tome, name, created_at, last_modified_at) VALUES (7000,2000,"Test Quest","2023-03-04 14:51:13","2023-03-04 14:51:13"); INSERT INTO `tasks` (id, task_beacon, quest_tasks, created_at, last_modified_at) diff --git a/tavern/internal/graphql/testdata/queries/tasks/FilterByID.yml b/tavern/internal/graphql/testdata/queries/tasks/FilterByID.yml index 7176fb2a8..e463ebf19 100644 --- a/tavern/internal/graphql/testdata/queries/tasks/FilterByID.yml +++ b/tavern/internal/graphql/testdata/queries/tasks/FilterByID.yml @@ -5,8 +5,8 @@ state: | VALUES (1010,"db1","EXISTING-HOST"); INSERT INTO `beacons` (id, name, identifier, beacon_host) VALUES (1337,"delightful-lich","ABCDEFG-123456",1010); - INSERT INTO `tomes` (id, name, description, eldritch, hash, created_at, last_modified_at) - VALUES (2000,"Test Tome","Used in a unit test :D", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `tomes` (id, name, description, author, eldritch, hash, created_at, last_modified_at) + VALUES (2000,"Test Tome","Used in a unit test :D", "kcarretto", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); INSERT INTO `quests` (id, quest_tome, name, created_at, last_modified_at) VALUES (7000,2000,"Test Quest","2023-03-04 14:51:13","2023-03-04 14:51:13"); INSERT INTO `quests` (id, quest_tome, name, created_at, last_modified_at) diff --git a/tavern/internal/graphql/testdata/queries/tasks/ForwardPaginate.yml b/tavern/internal/graphql/testdata/queries/tasks/ForwardPaginate.yml index 5b2eb9cc4..b42d6c2a2 100644 --- a/tavern/internal/graphql/testdata/queries/tasks/ForwardPaginate.yml +++ b/tavern/internal/graphql/testdata/queries/tasks/ForwardPaginate.yml @@ -5,8 +5,8 @@ state: | VALUES (1010,"db1","EXISTING-HOST"); INSERT INTO `beacons` (id, name, identifier, beacon_host) VALUES (1337,"delightful-lich","ABCDEFG-123456",1010); - INSERT INTO `tomes` (id, name, description, eldritch, hash, created_at, last_modified_at) - VALUES (2000,"Test Tome","Used in a unit test :D", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `tomes` (id, name, description, author, eldritch, hash, created_at, last_modified_at) + VALUES (2000,"Test Tome","Used in a unit test :D", "kcarretto", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); INSERT INTO `quests` (id, quest_tome, name, created_at, last_modified_at) VALUES (7000,2000,"Test Quest","2023-03-04 14:51:13","2023-03-04 14:51:13"); INSERT INTO `tasks` (id, task_beacon, quest_tasks, created_at, last_modified_at) diff --git a/tavern/internal/graphql/testdata/queries/tasks/Multiple.yml b/tavern/internal/graphql/testdata/queries/tasks/Multiple.yml index 1b7e4ec87..83bef3ec1 100644 --- a/tavern/internal/graphql/testdata/queries/tasks/Multiple.yml +++ b/tavern/internal/graphql/testdata/queries/tasks/Multiple.yml @@ -5,8 +5,8 @@ state: | VALUES (1010,"db1","EXISTING-HOST"); INSERT INTO `beacons` (id, name, identifier, beacon_host) VALUES (1337,"delightful-lich","ABCDEFG-123456",1010); - INSERT INTO `tomes` (id, name, description, eldritch, hash, created_at, last_modified_at) - VALUES (2000,"Test Tome","Used in a unit test :D", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `tomes` (id, name, description, author, eldritch, hash, created_at, last_modified_at) + VALUES (2000,"Test Tome","Used in a unit test :D", "kcarretto", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); INSERT INTO `quests` (id, quest_tome, name, created_at, last_modified_at) VALUES (7000,2000,"Test Quest","2023-03-04 14:51:13","2023-03-04 14:51:13"); INSERT INTO `tasks` (id, task_beacon, quest_tasks, created_at, last_modified_at) diff --git a/tavern/internal/graphql/testdata/queries/tasks/OrderByPaginate.yml b/tavern/internal/graphql/testdata/queries/tasks/OrderByPaginate.yml index 480821d52..b264a6680 100644 --- a/tavern/internal/graphql/testdata/queries/tasks/OrderByPaginate.yml +++ b/tavern/internal/graphql/testdata/queries/tasks/OrderByPaginate.yml @@ -5,8 +5,8 @@ state: | VALUES (1010,"db1","EXISTING-HOST"); INSERT INTO `beacons` (id, name, identifier, beacon_host) VALUES (1337,"delightful-lich","ABCDEFG-123456",1010); - INSERT INTO `tomes` (id, name, description, eldritch, hash, created_at, last_modified_at) - VALUES (2000,"Test Tome","Used in a unit test :D", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `tomes` (id, name, description, author, eldritch, hash, created_at, last_modified_at) + VALUES (2000,"Test Tome","Used in a unit test :D", "kcarretto", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); INSERT INTO `quests` (id, quest_tome, name, created_at, last_modified_at) VALUES (7000,2000,"Test Quest","2023-03-04 14:51:13","2023-03-04 14:51:13"); INSERT INTO `tasks` (id, task_beacon, quest_tasks, created_at, last_modified_at) diff --git a/tavern/internal/graphql/testdata/queries/tasks/Singular.yml b/tavern/internal/graphql/testdata/queries/tasks/Singular.yml index ee73a8f25..503776c46 100644 --- a/tavern/internal/graphql/testdata/queries/tasks/Singular.yml +++ b/tavern/internal/graphql/testdata/queries/tasks/Singular.yml @@ -5,8 +5,8 @@ state: | VALUES (1010,"db1","EXISTING-HOST"); INSERT INTO `beacons` (id, name, identifier, beacon_host) VALUES (1337,"delightful-lich","ABCDEFG-123456",1010); - INSERT INTO `tomes` (id, name, description, eldritch, hash, created_at, last_modified_at) - VALUES (2000,"Test Tome","Used in a unit test :D", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `tomes` (id, name, description, author, eldritch, hash, created_at, last_modified_at) + VALUES (2000,"Test Tome","Used in a unit test :D", "kcarretto", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); INSERT INTO `quests` (id, quest_tome, name, created_at, last_modified_at) VALUES (7000,2000,"Test Quest","2023-03-04 14:51:13","2023-03-04 14:51:13"); INSERT INTO `tasks` (id, task_beacon, quest_tasks, created_at, last_modified_at) diff --git a/tavern/internal/graphql/testdata/queries/tomes/FilterByID.yml b/tavern/internal/graphql/testdata/queries/tomes/FilterByID.yml index 089e907de..b3113424a 100644 --- a/tavern/internal/graphql/testdata/queries/tomes/FilterByID.yml +++ b/tavern/internal/graphql/testdata/queries/tomes/FilterByID.yml @@ -1,10 +1,10 @@ state: | INSERT INTO `users` (id,oauth_id,photo_url,name,session_token,is_activated,is_admin) VALUES (5,"test_oauth_id","https://photos.com","test","secretToken",true,true); - INSERT INTO `tomes` (id, name, description, eldritch, hash, created_at, last_modified_at) - VALUES (2000,"Test Tome","Used in a unit test :D", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); - INSERT INTO `tomes` (id, name, description, eldritch, hash, created_at, last_modified_at) - VALUES (2001,"Test Tome 2","Filtered by a unit test :D", "print('Goodbye World!')", "gfedcba", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `tomes` (id, name, description, author, eldritch, hash, created_at, last_modified_at) + VALUES (2000,"Test Tome","Used in a unit test :D", "kcarretto", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `tomes` (id, name, description, author, eldritch, hash, created_at, last_modified_at) + VALUES (2001,"Test Tome 2","Filtered by a unit test :D", "kcarretto", "print('Goodbye World!')", "gfedcba", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); requestor: session_token: secretToken @@ -16,4 +16,4 @@ query: | } expected: tomes: - - id: "2000" \ No newline at end of file + - id: "2000" diff --git a/tavern/internal/graphql/testdata/queries/tomes/Singular.yml b/tavern/internal/graphql/testdata/queries/tomes/Singular.yml index 012b5b9b0..fdee1b889 100644 --- a/tavern/internal/graphql/testdata/queries/tomes/Singular.yml +++ b/tavern/internal/graphql/testdata/queries/tomes/Singular.yml @@ -1,8 +1,8 @@ state: | INSERT INTO `users` (id,oauth_id,photo_url,name,session_token,is_activated,is_admin) VALUES (5,"test_oauth_id","https://photos.com","test","secretToken",true,true); - INSERT INTO `tomes` (id, name, description, eldritch, hash, created_at, last_modified_at) - VALUES (2000,"Test Tome","Used in a unit test :D", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `tomes` (id, name, description, author, eldritch, hash, created_at, last_modified_at) + VALUES (2000,"Test Tome","Used in a unit test :D", "kcarretto", "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); requestor: session_token: secretToken query: | @@ -13,4 +13,4 @@ query: | } expected: tomes: - - id: "2000" \ No newline at end of file + - id: "2000" diff --git a/tavern/internal/graphql/testdata/queries/users/UploadedTomes.yml b/tavern/internal/graphql/testdata/queries/users/UploadedTomes.yml new file mode 100644 index 000000000..fecad5cab --- /dev/null +++ b/tavern/internal/graphql/testdata/queries/users/UploadedTomes.yml @@ -0,0 +1,23 @@ +state: | + INSERT INTO `users` (id,oauth_id,photo_url,name,session_token,is_activated,is_admin) + VALUES (5,"test_oauth_id","https://photos.com","test","secretToken",true,true); + INSERT INTO `tomes` (id, name, description, author, tome_uploader, eldritch, hash, created_at, last_modified_at) + VALUES (2000,"Test Tome","Used in a unit test :D", "kcarretto", 5, "print('Hello World!')", "abcdefg", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + INSERT INTO `tomes` (id, name, description, author, tome_uploader, eldritch, hash, created_at, last_modified_at) + VALUES (2001,"Test Tome 2","Filtered by a unit test :D", "kcarretto", 5, "print('Goodbye World!')", "gfedcba", "2023-03-04 14:51:13", "2023-03-04 14:51:13"); + +requestor: + session_token: secretToken +query: | + query MyTomes { + me { + tomes { + id + } + } + } +expected: + me: + tomes: + - id: "2000" + - id: "2001" diff --git a/tavern/internal/graphql/tome_test.go b/tavern/internal/graphql/tome_test.go index bba655a1d..648cd09bf 100644 --- a/tavern/internal/graphql/tome_test.go +++ b/tavern/internal/graphql/tome_test.go @@ -68,6 +68,7 @@ mutation newCreateTomeTest($input: CreateTomeInput!) { expected := map[string]any{ "name": "TestTome", "description": "Helps us make sure this all works", + "author": "kcarretto", "eldritch": `print("hello world")`, } id, err := createTome(expected) @@ -92,6 +93,7 @@ mutation newCreateTomeTest($input: CreateTomeInput!) { expected := map[string]any{ "name": "TestTomeWithFiles", "description": "Helps us make sure this all works", + "author": "kcarretto", "eldritch": `print("hello world")`, "fileIDs": expectedFileIDs, } diff --git a/tavern/internal/www/schema.graphql b/tavern/internal/www/schema.graphql index 9873e5171..4e3de17cf 100644 --- a/tavern/internal/www/schema.graphql +++ b/tavern/internal/www/schema.graphql @@ -173,6 +173,12 @@ input CreateTomeInput { name: String! """Information about the tome""" description: String! + """Name of the author who created the tome.""" + author: String! + """Information about the tomes support model.""" + supportModel: TomeSupportModel + """MITRE ATT&CK tactic provided by the tome.""" + tactic: TomeTactic """JSON string describing what parameters are used with the tome. Requires a list of JSON objects, one for each parameter.""" paramDefs: String """Eldritch script that will be executed when the tome is run""" @@ -924,12 +930,20 @@ type Tome implements Node { name: String! """Information about the tome""" description: String! + """Name of the author who created the tome.""" + author: String! + """Information about the tomes support model.""" + supportModel: TomeSupportModel! + """MITRE ATT&CK tactic provided by the tome.""" + tactic: TomeTactic! """JSON string describing what parameters are used with the tome. Requires a list of JSON objects, one for each parameter.""" paramDefs: String """Eldritch script that will be executed when the tome is run""" eldritch: String! """Any files required for tome execution that will be bundled and provided to the agent for download""" files: [File!] + """User who uploaded the tome (may be null).""" + uploader: User } """Ordering options for Tome connections""" input TomeOrder { @@ -944,6 +958,30 @@ enum TomeOrderField { LAST_MODIFIED_AT NAME } +"""TomeSupportModel is enum for the field support_model""" +enum TomeSupportModel @goModel(model: "realm.pub/tavern/internal/ent/tome.SupportModel") { + UNSPECIFIED + FIRST_PARTY + COMMUNITY +} +"""TomeTactic is enum for the field tactic""" +enum TomeTactic @goModel(model: "realm.pub/tavern/internal/ent/tome.Tactic") { + UNSPECIFIED + RECON + RESOURCE_DEVELOPMENT + INITIAL_ACCESS + EXECUTION + PERSISTENCE + PRIVILEGE_ESCALATION + DEFENSE_EVASION + CREDENTIAL_ACCESS + DISCOVERY + LATERAL_MOVEMENT + COLLECTION + COMMAND_AND_CONTROL + EXFILTRATION + IMPACT +} """ TomeWhereInput is used for filtering Tome objects. Input was generated by ent. @@ -1007,6 +1045,30 @@ input TomeWhereInput { descriptionHasSuffix: String descriptionEqualFold: String descriptionContainsFold: String + """author field predicates""" + author: String + authorNEQ: String + authorIn: [String!] + authorNotIn: [String!] + authorGT: String + authorGTE: String + authorLT: String + authorLTE: String + authorContains: String + authorHasPrefix: String + authorHasSuffix: String + authorEqualFold: String + authorContainsFold: String + """support_model field predicates""" + supportModel: TomeSupportModel + supportModelNEQ: TomeSupportModel + supportModelIn: [TomeSupportModel!] + supportModelNotIn: [TomeSupportModel!] + """tactic field predicates""" + tactic: TomeTactic + tacticNEQ: TomeTactic + tacticIn: [TomeTactic!] + tacticNotIn: [TomeTactic!] """param_defs field predicates""" paramDefs: String paramDefsNEQ: String @@ -1040,6 +1102,9 @@ input TomeWhereInput { """files edge predicates""" hasFiles: Boolean hasFilesWith: [FileWhereInput!] + """uploader edge predicates""" + hasUploader: Boolean + hasUploaderWith: [UserWhereInput!] } """ UpdateBeaconInput is used for update Beacon object. @@ -1080,6 +1145,32 @@ input UpdateTagInput { clearHosts: Boolean } """ +UpdateTomeInput is used for update Tome object. +Input was generated by ent. +""" +input UpdateTomeInput { + """Timestamp of when this ent was last updated""" + lastModifiedAt: Time + """Name of the tome""" + name: String + """Information about the tome""" + description: String + """Name of the author who created the tome.""" + author: String + """Information about the tomes support model.""" + supportModel: TomeSupportModel + """MITRE ATT&CK tactic provided by the tome.""" + tactic: TomeTactic + """JSON string describing what parameters are used with the tome. Requires a list of JSON objects, one for each parameter.""" + paramDefs: String + clearParamDefs: Boolean + """Eldritch script that will be executed when the tome is run""" + eldritch: String + addFileIDs: [ID!] + removeFileIDs: [ID!] + clearFiles: Boolean +} +""" UpdateUserInput is used for update User object. Input was generated by ent. """ @@ -1092,6 +1183,9 @@ input UpdateUserInput { isActivated: Boolean """True if the user is an Admin""" isAdmin: Boolean + addTomeIDs: [ID!] + removeTomeIDs: [ID!] + clearTomes: Boolean } type User implements Node { id: ID! @@ -1103,6 +1197,8 @@ type User implements Node { isActivated: Boolean! """True if the user is an Admin""" isAdmin: Boolean! + """Tomes uploaded by the user.""" + tomes: [Tome!] } """ UserWhereInput is used for filtering User objects. @@ -1155,6 +1251,9 @@ input UserWhereInput { """is_admin field predicates""" isAdmin: Boolean isAdminNEQ: Boolean + """tomes edge predicates""" + hasTomes: Boolean + hasTomesWith: [TomeWhereInput!] } input ClaimTasksInput { """The identity the beacon is authenticated as (e.g. 'root')""" @@ -1220,16 +1319,12 @@ type Mutation { createTag(input: CreateTagInput!): Tag! @requireRole(role: ADMIN) updateTag(tagID: ID!, input: UpdateTagInput!): Tag! @requireRole(role: USER) - ### - # Task - ### - claimTasks(input: ClaimTasksInput!,): [Task!]! - submitTaskResult(input: SubmitTaskResultInput!,): Task - ### # Tome ### createTome(input: CreateTomeInput!,): Tome! @requireRole(role: USER) + updateTome(tomeID: ID!, input: UpdateTomeInput!,): Tome! @requireRole(role: ADMIN) + deleteTome(tomeID: ID!): ID! @requireRole(role: ADMIN) ### # User diff --git a/tavern/test_data.go b/tavern/test_data.go index 2b20e74f7..ffb7e3e6a 100644 --- a/tavern/test_data.go +++ b/tavern/test_data.go @@ -92,6 +92,7 @@ func createTestData(ctx context.Context, client *ent.Client) { client.Tome.Create(). SetName("ParamDefFormatExample"). SetDescription("This tome is an example that takes parameters and has parameter definitions defined (e.g. ParamDefs)"). + SetAuthor("kcarretto"). SetEldritch(``). SetParamDefs(`[ { @@ -115,6 +116,7 @@ func createTestData(ctx context.Context, client *ent.Client) { printMsgTome := client.Tome.Create(). SetName("PrintMessage"). SetDescription("Print a message for fun!"). + SetAuthor("kcarretto"). SetEldritch(`print(input_params['msg'])`). SetParamDefs(`[{"name":"msg","label":"Message","type":"string","placeholder":"something to print"}]`). SaveX(ctx) @@ -356,6 +358,7 @@ func createQuest(ctx context.Context, client *ent.Client, beacons ...*ent.Beacon testTome := client.Tome.Create(). SetName(namegen.NewComplex()). SetDescription("Print a message for fun!"). + SetAuthor("kcarretto"). SetEldritch(`print(input_params['msg'])`). SetParamDefs(`[{"name":"msg","label":"Message","type":"string","placeholder":"something to print"}]`). SaveX(ctx) diff --git a/tavern/tomes/cat/metadata.yml b/tavern/tomes/cat/metadata.yml index 6e2bbffad..018c2f10f 100644 --- a/tavern/tomes/cat/metadata.yml +++ b/tavern/tomes/cat/metadata.yml @@ -1,5 +1,8 @@ name: cat description: Display contents of a file +author: adm1nPanda +support_model: FIRST_PARTY +tactic: RECON paramdefs: - name: path type: string diff --git a/tavern/tomes/download_and_execute/metadata.yml b/tavern/tomes/download_and_execute/metadata.yml index f0a1ac6a6..a91c9e62e 100644 --- a/tavern/tomes/download_and_execute/metadata.yml +++ b/tavern/tomes/download_and_execute/metadata.yml @@ -1,5 +1,8 @@ name: Download and execute description: Download a file and execute it. If possible background and disown the process. +author: hulto +support_model: FIRST_PARTY +tactic: EXECUTION paramdefs: - name: url type: string diff --git a/tavern/tomes/example/metadata.yml b/tavern/tomes/example/metadata.yml index f7e3761ec..ea86b7123 100644 --- a/tavern/tomes/example/metadata.yml +++ b/tavern/tomes/example/metadata.yml @@ -1,5 +1,8 @@ name: Example description: An example tome! +author: kcarretto +support_model: FIRST_PARTY +tactic: UNSPECIFIED paramdefs: - label: Message name: msg diff --git a/tavern/tomes/file_list/metadata.yml b/tavern/tomes/file_list/metadata.yml index 8beb8708d..65640f03d 100644 --- a/tavern/tomes/file_list/metadata.yml +++ b/tavern/tomes/file_list/metadata.yml @@ -1,5 +1,8 @@ name: List files description: List the files and directories found at the path +author: hulto +support_model: FIRST_PARTY +tactic: RECON paramdefs: - name: path type: string diff --git a/tavern/tomes/file_tree/metadata.yml b/tavern/tomes/file_tree/metadata.yml index fb85407c0..5e42bd14b 100644 --- a/tavern/tomes/file_tree/metadata.yml +++ b/tavern/tomes/file_tree/metadata.yml @@ -1,5 +1,8 @@ name: List file tree description: List the files and directories found at the path in tree-like format +author: adm1nPanda +support_model: FIRST_PARTY +tactic: RECON paramdefs: - name: path type: string diff --git a/tavern/tomes/hostname/metadata.yml b/tavern/tomes/hostname/metadata.yml index 50cb0182b..29d465d03 100644 --- a/tavern/tomes/hostname/metadata.yml +++ b/tavern/tomes/hostname/metadata.yml @@ -1,2 +1,5 @@ name: Hostname description: Get the hostname of the system +author: hulto +support_model: FIRST_PARTY +tactic: RECON diff --git a/tavern/tomes/ifconfig/metadata.yml b/tavern/tomes/ifconfig/metadata.yml index d9d8752f0..b700c4f5e 100644 --- a/tavern/tomes/ifconfig/metadata.yml +++ b/tavern/tomes/ifconfig/metadata.yml @@ -1,2 +1,5 @@ name: Ifconfig description: Get ip and network interface information +author: hulto +support_model: FIRST_PARTY +tactic: RECON diff --git a/tavern/tomes/netstat/metadata.yml b/tavern/tomes/netstat/metadata.yml index a5c527387..e5b422c90 100644 --- a/tavern/tomes/netstat/metadata.yml +++ b/tavern/tomes/netstat/metadata.yml @@ -1,2 +1,5 @@ name: netstat description: List network connections +author: adm1nPanda +support_model: FIRST_PARTY +tactic: RECON diff --git a/tavern/tomes/parse.go b/tavern/tomes/parse.go index 27729ef48..2355b833e 100644 --- a/tavern/tomes/parse.go +++ b/tavern/tomes/parse.go @@ -40,9 +40,12 @@ func (paramDef ParamDefinition) Validate() error { } type metadataDefinition struct { - Name string - Description string - ParamDefs []ParamDefinition + Name string `yaml:"name"` + Description string `yaml:"description"` + Author string `yaml:"author"` + SupportModel string `yaml:"support_model"` + Tactic string `yaml:"tactic"` + ParamDefs []ParamDefinition } // UploadTomes traverses the provided filesystem and creates tomes using the provided graph. @@ -138,7 +141,10 @@ func UploadTomes(ctx context.Context, graph *ent.Client, fileSystem fs.ReadDirFS if _, err := graph.Tome.Create(). SetName(entry.Name()). SetDescription(metadata.Description). + SetAuthor(metadata.Author). SetParamDefs(string(paramdefs)). + SetSupportModel(tome.SupportModel(metadata.SupportModel)). + SetTactic(tome.Tactic(metadata.Tactic)). SetEldritch(eldritch). AddFiles(tomeFiles...). Save(ctx); err != nil { diff --git a/tavern/tomes/persist_service/metadata.yml b/tavern/tomes/persist_service/metadata.yml index 7974e3178..ae7ca09ba 100644 --- a/tavern/tomes/persist_service/metadata.yml +++ b/tavern/tomes/persist_service/metadata.yml @@ -1,5 +1,8 @@ name: Create Service description: Create a service for a specific binary +author: hulto +support_model: FIRST_PARTY +tactic: PERSISTENCE paramdefs: - name: executable_url type: string diff --git a/tavern/tomes/port_scan/metadata.yml b/tavern/tomes/port_scan/metadata.yml index 1058df344..6fd871c2d 100644 --- a/tavern/tomes/port_scan/metadata.yml +++ b/tavern/tomes/port_scan/metadata.yml @@ -1,5 +1,8 @@ name: Port scan description: Scan a list of ports in a list of CIDRs. +author: hulto +support_model: FIRST_PARTY +tactic: DISCOVERY paramdefs: - name: cidrs type: list diff --git a/tavern/tomes/process_list/metadata.yml b/tavern/tomes/process_list/metadata.yml index 296c73666..688b57e41 100644 --- a/tavern/tomes/process_list/metadata.yml +++ b/tavern/tomes/process_list/metadata.yml @@ -1,5 +1,8 @@ name: Process list description: Get a list of running processes +author: hulto +support_model: FIRST_PARTY +tactic: RECON paramdefs: - name: cmd_substring label: Process search string diff --git a/tavern/tomes/process_tree/metadata.yml b/tavern/tomes/process_tree/metadata.yml index 71895288c..1b279d1d1 100644 --- a/tavern/tomes/process_tree/metadata.yml +++ b/tavern/tomes/process_tree/metadata.yml @@ -1,5 +1,8 @@ name: Process tree description: Get a tree view of running processes +author: arunjohnkuruvilla +support_model: FIRST_PARTY +tactic: RECON paramdefs: - name: cmd_substring label: Process search string diff --git a/tavern/tomes/shell_cmd/metadata.yml b/tavern/tomes/shell_cmd/metadata.yml index c554df669..44a31efd0 100644 --- a/tavern/tomes/shell_cmd/metadata.yml +++ b/tavern/tomes/shell_cmd/metadata.yml @@ -1,5 +1,8 @@ name: Shell command description: Execute a shell command in the systems default shell. +author: hulto +support_model: FIRST_PARTY +tactic: EXECUTION paramdefs: - name: cmd label: Command