From 4cce6327085bc6f3ca673af2876e51f729368ca7 Mon Sep 17 00:00:00 2001 From: Gisle Aune Date: Mon, 13 Apr 2020 23:38:41 +0200 Subject: [PATCH] > be fist commit --- .idea/.gitignore | 8 + .idea/codeStyles/codeStyleConfig.xml | 5 + .idea/dataSources.xml | 11 + .idea/dictionaries/gisle.xml | 13 + .idea/misc.xml | 6 + .idea/modules.xml | 8 + .idea/sqldialects.xml | 7 + .idea/watcherTasks.xml | 29 + .idea/xiaoli.iml | 8 + database/database.go | 29 + database/drivers/mysqldriver/db.go | 103 + database/drivers/mysqldriver/db_test.go | 78 + database/drivers/mysqldriver/issues.go | 169 + database/drivers/mysqldriver/issues_test.go | 188 + database/drivers/mysqldriver/items.go | 215 + database/drivers/mysqldriver/items_test.go | 142 + database/drivers/mysqldriver/projects.go | 139 + database/drivers/mysqldriver/projects_test.go | 165 + database/drivers/mysqldriver/session.go | 49 + database/drivers/mysqldriver/users.go | 99 + database/drivers/mysqldriver/users_test.go | 117 + database/repositories/issuerepository.go | 14 + database/repositories/itemrepository.go | 15 + database/repositories/projectrepository.go | 17 + database/repositories/sessionrepository.go | 13 + database/repositories/userrepository.go | 14 + go.mod | 17 + go.sum | 143 + graph/gqlgen.yml | 18 + graph/graph.go | 30 + graph/graphcore/exec_gen.go | 4983 +++++++++++++++++ graph/graphcore/input_gen.go | 38 + graph/resolvers/issue.resolvers.go | 29 + graph/resolvers/mutation.resolvers.go | 25 + graph/resolvers/project.resolvers.go | 29 + graph/resolvers/query.resolvers.go | 37 + graph/resolvers/resolver.go | 11 + graph/schema/issue.gql | 37 + graph/schema/mutation.gql | 4 + graph/schema/project.gql | 70 + graph/schema/query.gql | 14 + graph/schema/scalars.gql | 1 + graph/schema/user.gql | 18 + internal/generate/generate.go | 48 + internal/generate/ids.go | 9 + internal/xlerrors/notfound.go | 18 + main.go | 75 + .../20200405173553_create_table_project.sql | 16 + .../20200406105917_create_table_issue.sql | 30 + .../20200406110301_create_table_item.sql | 16 + .../20200406110546_create_table_item_tag.sql | 15 + .../20200406114528_create_table_user.sql | 15 + .../20200406160317_create_table_counters.sql | 15 + ...113154_create_table_project_permission.sql | 16 + .../20200412174220_create_table_session.sql | 15 + models/activity.go | 12 + models/goal.go | 21 + models/issue.go | 39 + models/issueitem.go | 9 + models/issuetask.go | 20 + models/item.go | 21 + models/log.go | 6 + models/project.go | 40 + models/projectpermission.go | 36 + models/projectstatus.go | 9 + models/session.go | 9 + models/user.go | 39 + services/auth.go | 133 + services/bundle.go | 5 + 69 files changed, 7852 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/dataSources.xml create mode 100644 .idea/dictionaries/gisle.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/sqldialects.xml create mode 100644 .idea/watcherTasks.xml create mode 100644 .idea/xiaoli.iml create mode 100644 database/database.go create mode 100644 database/drivers/mysqldriver/db.go create mode 100644 database/drivers/mysqldriver/db_test.go create mode 100644 database/drivers/mysqldriver/issues.go create mode 100644 database/drivers/mysqldriver/issues_test.go create mode 100644 database/drivers/mysqldriver/items.go create mode 100644 database/drivers/mysqldriver/items_test.go create mode 100644 database/drivers/mysqldriver/projects.go create mode 100644 database/drivers/mysqldriver/projects_test.go create mode 100644 database/drivers/mysqldriver/session.go create mode 100644 database/drivers/mysqldriver/users.go create mode 100644 database/drivers/mysqldriver/users_test.go create mode 100644 database/repositories/issuerepository.go create mode 100644 database/repositories/itemrepository.go create mode 100644 database/repositories/projectrepository.go create mode 100644 database/repositories/sessionrepository.go create mode 100644 database/repositories/userrepository.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 graph/gqlgen.yml create mode 100644 graph/graph.go create mode 100644 graph/graphcore/exec_gen.go create mode 100644 graph/graphcore/input_gen.go create mode 100644 graph/resolvers/issue.resolvers.go create mode 100644 graph/resolvers/mutation.resolvers.go create mode 100644 graph/resolvers/project.resolvers.go create mode 100644 graph/resolvers/query.resolvers.go create mode 100644 graph/resolvers/resolver.go create mode 100644 graph/schema/issue.gql create mode 100644 graph/schema/mutation.gql create mode 100644 graph/schema/project.gql create mode 100644 graph/schema/query.gql create mode 100644 graph/schema/scalars.gql create mode 100644 graph/schema/user.gql create mode 100644 internal/generate/generate.go create mode 100644 internal/generate/ids.go create mode 100644 internal/xlerrors/notfound.go create mode 100644 main.go create mode 100644 migrations/mysql/20200405173553_create_table_project.sql create mode 100644 migrations/mysql/20200406105917_create_table_issue.sql create mode 100644 migrations/mysql/20200406110301_create_table_item.sql create mode 100644 migrations/mysql/20200406110546_create_table_item_tag.sql create mode 100644 migrations/mysql/20200406114528_create_table_user.sql create mode 100644 migrations/mysql/20200406160317_create_table_counters.sql create mode 100644 migrations/mysql/20200409113154_create_table_project_permission.sql create mode 100644 migrations/mysql/20200412174220_create_table_session.sql create mode 100644 models/activity.go create mode 100644 models/goal.go create mode 100644 models/issue.go create mode 100644 models/issueitem.go create mode 100644 models/issuetask.go create mode 100644 models/item.go create mode 100644 models/log.go create mode 100644 models/project.go create mode 100644 models/projectpermission.go create mode 100644 models/projectstatus.go create mode 100644 models/session.go create mode 100644 models/user.go create mode 100644 services/auth.go create mode 100644 services/bundle.go diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..73f69e0 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..a55e7a1 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..98ad33e --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,11 @@ + + + + + mariadb + true + org.mariadb.jdbc.Driver + jdbc:mariadb://localhost:3306/xiaoli + + + \ No newline at end of file diff --git a/.idea/dictionaries/gisle.xml b/.idea/dictionaries/gisle.xml new file mode 100644 index 0000000..fc8ac1a --- /dev/null +++ b/.idea/dictionaries/gisle.xml @@ -0,0 +1,13 @@ + + + + blargh + blendings + graphcore + qpos + sourcream + stufflog + xiaoli + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..28a804d --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..3c106bb --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml new file mode 100644 index 0000000..dcf43c9 --- /dev/null +++ b/.idea/sqldialects.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/watcherTasks.xml b/.idea/watcherTasks.xml new file mode 100644 index 0000000..97ad6d2 --- /dev/null +++ b/.idea/watcherTasks.xml @@ -0,0 +1,29 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/xiaoli.iml b/.idea/xiaoli.iml new file mode 100644 index 0000000..c956989 --- /dev/null +++ b/.idea/xiaoli.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/database/database.go b/database/database.go new file mode 100644 index 0000000..00243b1 --- /dev/null +++ b/database/database.go @@ -0,0 +1,29 @@ +package database + +import ( + "errors" + "git.aiterp.net/stufflog/server/database/drivers/mysqldriver" + "git.aiterp.net/stufflog/server/database/repositories" +) + +var ErrDriverNotSupported = errors.New("driver not found or supported") + +type Database interface { + Issues() repositories.IssueRepository + Items() repositories.ItemRepository + Projects() repositories.ProjectRepository + Session() repositories.SessionRepository + Users() repositories.UserRepository + + // Migrate the database. + Migrate() error +} + +func Open(driver, connect string) (Database, error) { + switch driver { + case "mysql": + return mysqldriver.Open(connect) + default: + return nil, ErrDriverNotSupported + } +} diff --git a/database/drivers/mysqldriver/db.go b/database/drivers/mysqldriver/db.go new file mode 100644 index 0000000..64fbd69 --- /dev/null +++ b/database/drivers/mysqldriver/db.go @@ -0,0 +1,103 @@ +package mysqldriver + +import ( + "context" + "database/sql" + "git.aiterp.net/stufflog/server/database/repositories" + "github.com/jmoiron/sqlx" + "github.com/pressly/goose" + "strings" + + // Mysql Driver + _ "github.com/go-sql-driver/mysql" +) + +type DB struct { + db *sqlx.DB + issues *issueRepository + items *itemRepository + projects *projectRepository + sessions *sessionRepository + users *userRepository +} + +func (db *DB) Issues() repositories.IssueRepository { + return db.issues +} + +func (db *DB) Items() repositories.ItemRepository { + return db.items +} + +func (db *DB) Projects() repositories.ProjectRepository { + return db.projects +} + +func (db *DB) Session() repositories.SessionRepository { + return db.sessions +} + +func (db *DB) Users() repositories.UserRepository { + return db.users +} + +func (db *DB) Migrate() error { + err := goose.SetDialect("mysql") + if err != nil { + return err + } + + return goose.Up(db.db.DB, "migrations/mysql") +} + +func Open(connectionString string) (*DB, error) { + // Ensure parseTime is true + qpos := strings.LastIndexByte(connectionString, '?') + if qpos != -1 { + connectionString += "&parseTime=true" + } else { + connectionString += "?parseTime=true" + } + + // Connect to the database + db, err := sqlx.Connect("mysql", connectionString) + if err != nil { + return nil, err + } + + // Test the connection + err = db.Ping() + if err != nil { + return nil, err + } + + issues := &issueRepository{db: db} + items := &itemRepository{db: db} + projects := &projectRepository{db: db} + users := &userRepository{db: db} + + return &DB{ + db: db, + issues: issues, + items: items, + projects: projects, + users: users, + }, nil +} + +func incCounter(ctx context.Context, tx *sqlx.Tx, kind, name string) (int, error) { + value := 1 + err := tx.GetContext(ctx, &value, ` + SELECT value FROM counters WHERE kind=? AND name=? FOR UPDATE + `, kind, name) + if err != nil && err != sql.ErrNoRows { + return -1, err + } + + _, err = tx.ExecContext(ctx, "REPLACE INTO counters (kind, name, value) VALUES (?, ?, ?)", kind, name, value+1) + if err != nil { + return -1, err + } + + return value, nil +} diff --git a/database/drivers/mysqldriver/db_test.go b/database/drivers/mysqldriver/db_test.go new file mode 100644 index 0000000..971ffbc --- /dev/null +++ b/database/drivers/mysqldriver/db_test.go @@ -0,0 +1,78 @@ +package mysqldriver + +import ( + "github.com/pressly/goose" + "log" + "os" + "testing" + "time" +) + +var testDB *DB + +func TestMain(m *testing.M) { + testDbConnect := os.Getenv("DB_TEST_CONNECT") + if testDbConnect == "" { + testDbConnect = "xiaoli_test:test1234@(localhost:3306)/xiaoli_test" + } + + db, err := Open(testDbConnect) + if err != nil { + log.Println("DB ERROR", err) + os.Exit(1) + return + } + + err = goose.SetDialect("mysql") + if err != nil { + log.Println("GOOSE ERROR", err) + os.Exit(1) + return + } + + for { + err = goose.Down(db.db.DB, "../../../migrations/mysql") + if err != nil { + break + } + } + + err = goose.Up(db.db.DB, "../../../migrations/mysql") + if err != nil && err != goose.ErrNoNextVersion && err != goose.ErrNoCurrentVersion { + log.Println("UP ERROR", err) + os.Exit(3) + return + } + + testDB = db + code := m.Run() + + os.Exit(code) +} + +func clearTable(tableName string) error { + // If you really want to SQL inject the test DB, go right ahead! + _, err := testDB.db.Exec("DELETE FROM " + tableName + " WHERE TRUE") + return err +} + +func mustParseTime(str string) time.Time { + d, err := time.Parse(time.RFC3339Nano, str) + if err != nil { + panic(err) + } + + return d.UTC() +} + +func ptrInt(v int) *int { + return &v +} + +func ptrString(v string) *string { + return &v +} + +func ptrBool(v bool) *bool { + return &v +} diff --git a/database/drivers/mysqldriver/issues.go b/database/drivers/mysqldriver/issues.go new file mode 100644 index 0000000..7e69db6 --- /dev/null +++ b/database/drivers/mysqldriver/issues.go @@ -0,0 +1,169 @@ +package mysqldriver + +import ( + "context" + "database/sql" + "errors" + "fmt" + "git.aiterp.net/stufflog/server/internal/xlerrors" + "git.aiterp.net/stufflog/server/models" + sq "github.com/Masterminds/squirrel" + "github.com/jmoiron/sqlx" + "time" +) + +var counterKindIssueID = "NextIssueID" + +type issueRepository struct { + db *sqlx.DB +} + +func (r *issueRepository) Find(ctx context.Context, id string) (*models.Issue, error) { + issue := models.Issue{} + err := r.db.GetContext(ctx, &issue, "SELECT * FROM issue WHERE issue_id=?", id) + if err != nil { + if err == sql.ErrNoRows { + return nil, xlerrors.NotFound("Issue") + } + + return nil, err + } + + return &issue, nil +} + +func (r *issueRepository) List(ctx context.Context, filter models.IssueFilter) ([]*models.Issue, error) { + q := sq.Select("*").From("issue").OrderBy("updated_time DESC") + if len(filter.IssueIDs) > 0 { + q = q.Where(sq.Eq{"issue_id": filter.IssueIDs}) + } + if len(filter.ProjectIDs) > 0 { + q = q.Where(sq.Eq{"project_id": filter.ProjectIDs}) + } + if len(filter.OwnerIDs) > 0 { + q = q.Where(sq.Eq{"owner_id": filter.OwnerIDs}) + } + if len(filter.AssigneeIDs) > 0 { + q = q.Where(sq.Eq{"assignee_id": filter.AssigneeIDs}) + } + if filter.Search != nil && *filter.Search != "" { + q = q.Where("MATCH (name, title, description) AGAINST (?)", *filter.Search) + } + if filter.MinStage != nil { + q = q.Where(sq.GtOrEq{"status_stage": *filter.MinStage}) + } + if filter.MaxStage != nil { + q = q.Where(sq.LtOrEq{"status_stage": *filter.MaxStage}) + } + if filter.Limit != nil && *filter.Limit > 0 { + q = q.Limit(uint64(*filter.Limit)) + } + + query, args, err := q.ToSql() + if err != nil { + return nil, err + } + + results := make([]*models.Issue, 0, 16) + err = r.db.SelectContext(ctx, &results, query, args...) + if err != nil { + if err == sql.ErrNoRows { + return []*models.Issue{}, nil + } + + return nil, err + } + + return results, nil +} + +func (r *issueRepository) Insert(ctx context.Context, issue models.Issue) (*models.Issue, error) { + if issue.ProjectID == "" { + return nil, errors.New("missing project id") + } + + if issue.CreatedTime.IsZero() { + issue.CreatedTime = time.Now().Truncate(time.Second) + issue.UpdatedTime = issue.CreatedTime + } + + tx, err := r.db.BeginTxx(ctx, nil) + if err != nil { + return nil, err + } + + nextID, err := incCounter(ctx, tx, counterKindIssueID, issue.ProjectID) + if err != nil { + _ = tx.Rollback() + return nil, err + } + issue.ID = fmt.Sprintf("%s-%d", issue.ProjectID, nextID) + + _, err = tx.NamedExecContext(ctx, ` + INSERT INTO issue ( + issue_id, project_id, owner_id, assignee_id, + status_stage, status_name, created_time, + updated_time, due_time, name, title, description + ) VALUES ( + :issue_id, :project_id, :owner_id, :assignee_id, + :status_stage, :status_name, :created_time, + :updated_time, :due_time, :name, :title, :description + ) + `, issue) + if err != nil { + _ = tx.Rollback() + return nil, err + } + + err = tx.Commit() + if err != nil { + _ = tx.Rollback() + return nil, err + } + + return &issue, nil +} + +func (r *issueRepository) Save(ctx context.Context, issue models.Issue) error { + _, err := r.db.NamedExecContext(ctx, ` + UPDATE issue + SET assignee_id=:assignee_id, + status_stage=:status_stage, + status_name=:status_name, + created_time=:created_time, + updated_time=:updated_time, + due_time=:due_time, + name=:name, + title=:title, + description=:description + WHERE issue_id=:issue_id + `, issue) + if err != nil { + return err + } + + return nil +} + +func (r *issueRepository) Delete(ctx context.Context, issue models.Issue) error { + tx, err := r.db.BeginTxx(ctx, nil) + if err != nil { + return err + } + + _, err = tx.ExecContext(ctx, "DELETE FROM issue WHERE issue_id=?", issue.ID) + if err != nil { + _ = tx.Rollback() + return err + } + + // TODO: delete from issue_* + + err = tx.Commit() + if err != nil { + _ = tx.Rollback() + return err + } + + return nil +} diff --git a/database/drivers/mysqldriver/issues_test.go b/database/drivers/mysqldriver/issues_test.go new file mode 100644 index 0000000..4f9d9b5 --- /dev/null +++ b/database/drivers/mysqldriver/issues_test.go @@ -0,0 +1,188 @@ +package mysqldriver + +import ( + "context" + "git.aiterp.net/stufflog/server/internal/xlerrors" + "git.aiterp.net/stufflog/server/models" + "github.com/stretchr/testify/assert" + "testing" + "time" +) + +var issue1 = models.Issue{ + ProjectID: "STUFF", + OwnerID: "Admin", + AssigneeID: "", + StatusStage: models.IssueStageActive, + StatusName: "IN PROGRESS", + DueTime: time.Now().UTC().Add(time.Hour * 72).Truncate(time.Hour * 24).Add(time.Hour * 16), + Name: "Do Stuff", + Title: "Do some important stuff.", + Description: "Stuff and things, items and artifacts, objects and creations.", +} + +var issue2 = models.Issue{ + ProjectID: "MODELING", + OwnerID: "Test", + AssigneeID: "Test", + StatusStage: models.IssueStagePostponed, + StatusName: "TOO HARD", + Name: "Hard Surface Course", + Title: "Run through the Hard Surface modeling course.", + Description: "maek robit", +} + +var issue3 = models.Issue{ + ProjectID: "MODELING", + OwnerID: "Test", + AssigneeID: "", + StatusStage: models.IssueStagePending, + StatusName: "TO DO", + Name: "Isometric Room Scene", + Title: "Create an isometric room-box scene.", + Description: "furniture and stuff.", +} + +var issue3Updated = models.Issue{ + ProjectID: "MODELING", + OwnerID: "Test", + AssigneeID: "Admin", + StatusStage: models.IssueStageActive, + StatusName: "WORK IN PROGRESS", + Name: "Room Scene", + Title: "Create a room-box scene.", + Description: "THREE DIMENSIONAL DESK CLUTTER", +} + +var issue4 = models.Issue{ + ProjectID: "DINNER", + OwnerID: "Test", + AssigneeID: "Test", + StatusStage: models.IssueStagePending, + StatusName: "SHOPPING LIST", + DueTime: mustParseTime("2020-04-22T17:30:00.000+02:00"), + Name: "Spaghetti Carbonara", + Title: "WEDNESDAY 2020-04-22: Spaghetti Carbonara", + Description: "See shopping list", +} + +func TestIssueRepository(t *testing.T) { + issues := testDB.issues + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + + assert.NoError(t, clearTable("issue")) + + // INSERT + result, err := issues.Insert(ctx, issue1) + assert.NoError(t, err) + assert.Equal(t, "STUFF-1", result.ID) + if result != nil { + issue1.ID = result.ID + issue1.CreatedTime = result.CreatedTime.UTC() + issue1.UpdatedTime = result.UpdatedTime.UTC() + } + result, err = issues.Insert(ctx, issue2) + assert.NoError(t, err) + assert.Equal(t, "MODELING-1", result.ID) + if result != nil { + issue2.ID = result.ID + issue2.CreatedTime = result.CreatedTime.UTC() + issue2.UpdatedTime = result.UpdatedTime.UTC() + } + result, err = issues.Insert(ctx, issue3) + assert.NoError(t, err) + assert.Equal(t, "MODELING-2", result.ID) + if result != nil { + issue3.ID = result.ID + issue3.CreatedTime = result.CreatedTime.UTC() + issue3.UpdatedTime = result.UpdatedTime.UTC() + issue3Updated.ID = result.ID + issue3Updated.CreatedTime = result.CreatedTime.UTC() + issue3Updated.UpdatedTime = result.UpdatedTime.UTC() + } + result, err = issues.Insert(ctx, issue4) + assert.NoError(t, err) + assert.Equal(t, "DINNER-1", result.ID) + if result != nil { + issue4.ID = result.ID + issue4.CreatedTime = result.CreatedTime.UTC() + issue4.UpdatedTime = result.UpdatedTime.UTC() + } + + // FIND + result, err = issues.Find(ctx, issue1.ID) + assert.NoError(t, err) + assert.Equal(t, &issue1, result) + result, err = issues.Find(ctx, issue3.ID) + assert.NoError(t, err) + assert.Equal(t, &issue3, result) + + // FIND't + result, err = issues.Find(ctx, "NONEXISTENT-666") + assert.Error(t, err) + assert.Nil(t, result) + assert.True(t, xlerrors.IsNotFound(err)) + + // LIST + results, err := issues.List(ctx, models.IssueFilter{}) + assert.NoError(t, err) + assert.Equal(t, []*models.Issue{&issue4, &issue2, &issue3, &issue1}, results) + results, err = issues.List(ctx, models.IssueFilter{ + ProjectIDs: []string{"DINNER", "MODELING"}, + }) + assert.NoError(t, err) + assert.Equal(t, []*models.Issue{&issue4, &issue2, &issue3}, results) + results, err = issues.List(ctx, models.IssueFilter{ + IssueIDs: []string{"MODELING-2", "DINNER-1"}, + }) + assert.NoError(t, err) + assert.Equal(t, []*models.Issue{&issue4, &issue3}, results) + results, err = issues.List(ctx, models.IssueFilter{ + OwnerIDs: []string{"Test"}, + }) + assert.NoError(t, err) + assert.Equal(t, []*models.Issue{&issue4, &issue2, &issue3}, results) + results, err = issues.List(ctx, models.IssueFilter{ + AssigneeIDs: []string{"", "Admin"}, + }) + assert.NoError(t, err) + assert.Equal(t, []*models.Issue{&issue3, &issue1}, results) + results, err = issues.List(ctx, models.IssueFilter{ + MinStage: ptrInt(models.IssueStageActive), + MaxStage: ptrInt(models.IssueStageReview), + }) + assert.NoError(t, err) + assert.Equal(t, []*models.Issue{&issue1}, results) + results, err = issues.List(ctx, models.IssueFilter{ + Limit: ptrInt(2), + }) + assert.NoError(t, err) + assert.Equal(t, []*models.Issue{&issue4, &issue2}, results) + results, err = issues.List(ctx, models.IssueFilter{ + OwnerIDs: []string{"Admin"}, + AssigneeIDs: []string{""}, + Search: ptrString("stuff"), + }) + assert.NoError(t, err) + assert.Equal(t, []*models.Issue{&issue1}, results) + + // SAVE + issue3Updated.UpdatedTime = time.Now().UTC().Truncate(time.Second) + err = issues.Save(ctx, issue3Updated) + assert.NoError(t, err) + + // FIND after SAVE + result, err = issues.Find(ctx, issue3.ID) + assert.NoError(t, err) + assert.Equal(t, &issue3Updated, result) + + // DELETE + err = issues.Delete(ctx, issue4) + assert.NoError(t, err) + + // LIST after DELETE and SAVE + results, err = issues.List(ctx, models.IssueFilter{}) + assert.NoError(t, err) + assert.Equal(t, []*models.Issue{&issue2, &issue3Updated, &issue1}, results) +} diff --git a/database/drivers/mysqldriver/items.go b/database/drivers/mysqldriver/items.go new file mode 100644 index 0000000..45a0a7c --- /dev/null +++ b/database/drivers/mysqldriver/items.go @@ -0,0 +1,215 @@ +package mysqldriver + +import ( + "context" + "database/sql" + "git.aiterp.net/stufflog/server/internal/generate" + "git.aiterp.net/stufflog/server/internal/xlerrors" + "git.aiterp.net/stufflog/server/models" + sq "github.com/Masterminds/squirrel" + "github.com/jmoiron/sqlx" +) + +type itemRepository struct { + db *sqlx.DB +} + +func (r *itemRepository) Find(ctx context.Context, id string) (*models.Item, error) { + item := models.Item{} + err := r.db.GetContext(ctx, &item, "SELECT * FROM item WHERE item_id=?", id) + if err != nil { + if err == sql.ErrNoRows { + return nil, xlerrors.NotFound("Project") + } + + return nil, err + } + + err = r.db.SelectContext(ctx, &item.Tags, "SELECT tag FROM item_tag WHERE item_id=? ORDER BY tag", id) + if err != nil { + return nil, err + } + + return &item, nil +} + +func (r *itemRepository) List(ctx context.Context, filter models.ItemFilter) ([]*models.Item, error) { + q := sq.Select("item.*").From("item").OrderBy("name") + if len(filter.ItemIDs) > 0 { + q = q.Where(sq.Eq{"item_id": filter.ItemIDs}) + } + if len(filter.Tags) > 0 { + q = q.LeftJoin("item_tag ON item_tag.item_id = item.item_id"). + Where(sq.Eq{"item_tag.tag": filter.Tags}). + GroupBy("item.item_id") + } + + query, args, err := q.ToSql() + if err != nil { + return nil, err + } + + results := make([]*models.Item, 0, 16) + err = r.db.SelectContext(ctx, &results, query, args...) + if err != nil { + if err == sql.ErrNoRows { + return []*models.Item{}, nil + } + + return nil, err + } + + err = r.fillTags(ctx, results) + if err != nil { + return nil, err + } + + return results, nil +} + +func (r *itemRepository) Insert(ctx context.Context, item models.Item) (*models.Item, error) { + item.ID = generate.ItemID() + + tx, err := r.db.BeginTxx(ctx, nil) + if err != nil { + return nil, err + } + + _, err = tx.NamedExecContext(ctx, ` + INSERT INTO item (item_id, name, description, image_url) + VALUES (:item_id, :name, :description, :image_url) + `, item) + if err != nil { + _ = tx.Rollback() + return nil, err + } + + if len(item.Tags) > 0 { + q := sq.Insert("item_tag").Columns("item_id", "tag") + for _, tag := range item.Tags { + q = q.Values(item.ID, tag) + } + + tagQuery, args, err := q.ToSql() + if err != nil { + _ = tx.Rollback() + return nil, err + } + + _, err = r.db.ExecContext(ctx, tagQuery, args...) + if err != nil { + _ = tx.Rollback() + return nil, err + } + } + + err = tx.Commit() + if err != nil { + return nil, err + } + + return &item, nil +} + +func (r *itemRepository) Save(ctx context.Context, item models.Item) error { + tx, err := r.db.BeginTxx(ctx, nil) + if err != nil { + return err + } + + _, err = tx.NamedExecContext(ctx, ` + UPDATE item + SET name=:name, description=:description, image_url=:image_url + WHERE item_id=:item_id + `, item) + if err != nil { + _ = tx.Rollback() + return err + } + + _, err = r.db.ExecContext(ctx, "DELETE FROM item_tag WHERE item_id=?", item.ID) + if err != nil && err != sql.ErrNoRows { + _ = tx.Rollback() + return err + } + + if len(item.Tags) > 0 { + q := sq.Insert("item_tag").Columns("item_id", "tag") + for _, tag := range item.Tags { + q = q.Values(item.ID, tag) + } + + tagQuery, args, err := q.ToSql() + if err != nil { + _ = tx.Rollback() + return err + } + + _, err = r.db.ExecContext(ctx, tagQuery, args...) + if err != nil { + _ = tx.Rollback() + return err + } + } + + err = tx.Commit() + if err != nil { + return err + } + + return nil +} + +func (r *itemRepository) Delete(ctx context.Context, item models.Item) error { + _, err := r.db.ExecContext(ctx, "DELETE FROM item WHERE item_id=?", item.ID) + if err != nil { + return err + } + + _, err = r.db.ExecContext(ctx, "DELETE FROM item_tag WHERE item_id=?", item.ID) + if err != nil { + return err + } + + return err +} + +func (r *itemRepository) GetTags(ctx context.Context) ([]string, error) { + tags := make([]string, 0, 16) + err := r.db.SelectContext(ctx, &tags, "SELECT DISTINCT(tag) FROM item_tag") + if err != nil { + return nil, err + } + + return tags, nil +} + +func (r *itemRepository) fillTags(ctx context.Context, items []*models.Item) error { + ids := make([]string, len(items)) + idMap := make(map[string]int, len(items)) + for i, item := range items { + ids[i] = item.ID + idMap[item.ID] = i + } + + query, args, err := sq.Select("*").From("item_tag").Where(sq.Eq{"item_id": ids}).ToSql() + if err != nil { + return err + } + + results := make([]struct { + ItemID string `db:"item_id"` + Tag string `db:"tag"` + }, 0, len(items)*4) + err = r.db.SelectContext(ctx, &results, query, args...) + if err != nil { + return err + } + + for _, result := range results { + item := items[idMap[result.ItemID]] + item.Tags = append(item.Tags, result.Tag) + } + + return nil +} diff --git a/database/drivers/mysqldriver/items_test.go b/database/drivers/mysqldriver/items_test.go new file mode 100644 index 0000000..b32c5c4 --- /dev/null +++ b/database/drivers/mysqldriver/items_test.go @@ -0,0 +1,142 @@ +package mysqldriver + +import ( + "context" + "git.aiterp.net/stufflog/server/internal/xlerrors" + "git.aiterp.net/stufflog/server/models" + "github.com/stretchr/testify/assert" + "testing" + "time" +) + +var item1ImageURL = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" + +var item1 = models.Item{ + Name: "Salmon Fillet - 500g", + Description: "Best fish", + Tags: []string{"Groceries"}, + ImageURL: &item1ImageURL, +} +var item1Updated = models.Item{ + Name: "Salmon Fillet - 450g", + Description: "Do not handle under suspicious circumstances.", + Tags: []string{"Groceries"}, + ImageURL: nil, +} +var item2 = models.Item{ + Name: "Tape - Basic", + Description: "", + Tags: []string{"Groceries", "Hardware", "Office Supplies"}, + ImageURL: nil, +} +var item3 = models.Item{ + Name: "Flour - Wheat - 1kg", + Description: "For bread and stuff", + Tags: []string{"Groceries"}, + ImageURL: nil, +} +var item4 = models.Item{ + Name: "Flour - Wheat - 2kg", + Description: "For more bread and stuff", + Tags: []string{"Groceries"}, + ImageURL: nil, +} + +func TestItemRepository(t *testing.T) { + items := testDB.items + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + + assert.NoError(t, clearTable("item")) + assert.NoError(t, clearTable("item_tag")) + + // INSERT + // IDs are random, so test data needs to be changed before comparison. + result, err := items.Insert(ctx, item1) + assert.NoError(t, err) + if result != nil { + item1.ID = result.ID + item1Updated.ID = result.ID + } + assert.Equal(t, item1, *result) + result, err = items.Insert(ctx, item2) + assert.NoError(t, err) + if result != nil { + item2.ID = result.ID + } + assert.Equal(t, item2, *result) + result, err = items.Insert(ctx, item3) + assert.NoError(t, err) + if result != nil { + item3.ID = result.ID + } + assert.Equal(t, item3, *result) + result, err = items.Insert(ctx, item4) + assert.NoError(t, err) + if result != nil { + item4.ID = result.ID + } + assert.Equal(t, item4, *result) + + if t.Failed() { + return + } + + // FIND + result, err = items.Find(ctx, item1.ID) + assert.NoError(t, err) + assert.Equal(t, &item1, result) + result, err = items.Find(ctx, item2.ID) + assert.NoError(t, err) + assert.Equal(t, &item2, result) + + // FIND't + result, err = items.Find(ctx, "Iobviouslyinvalidid") + assert.Error(t, err) + assert.True(t, xlerrors.IsNotFound(err)) + assert.Nil(t, result) + + // LIST + results, err := items.List(ctx, models.ItemFilter{}) + assert.NoError(t, err) + assert.Equal(t, []*models.Item{&item3, &item4, &item1, &item2}, results) + results, err = items.List(ctx, models.ItemFilter{ + Tags: []string{"Hardware"}, + }) + assert.NoError(t, err) + assert.Equal(t, []*models.Item{&item2}, results) + results, err = items.List(ctx, models.ItemFilter{ + ItemIDs: []string{item1.ID, item2.ID}, + }) + assert.NoError(t, err) + assert.Equal(t, []*models.Item{&item1, &item2}, results) + + // UPDATE + err = items.Save(ctx, item1Updated) + assert.NoError(t, err) + + // FIND after UPDATE + result, err = items.Find(ctx, item1.ID) + assert.NoError(t, err) + assert.NotEqual(t, &item1, result) + assert.Equal(t, &item1Updated, result) + + // TAGS + allTags, err := items.GetTags(ctx) + assert.NoError(t, err) + assert.Equal(t, item2.Tags, allTags) + + // DELETE + err = items.Delete(ctx, item2) + assert.NoError(t, err) + + // LIST after DELETE + results, err = items.List(ctx, models.ItemFilter{}) + assert.NoError(t, err) + assert.Equal(t, []*models.Item{&item3, &item4, &item1Updated}, results) + + // TAGS after DELETE + allTags, err = items.GetTags(ctx) + assert.NoError(t, err) + assert.Equal(t, item1.Tags, allTags) +} diff --git a/database/drivers/mysqldriver/projects.go b/database/drivers/mysqldriver/projects.go new file mode 100644 index 0000000..33219a4 --- /dev/null +++ b/database/drivers/mysqldriver/projects.go @@ -0,0 +1,139 @@ +package mysqldriver + +import ( + "context" + "database/sql" + "errors" + "git.aiterp.net/stufflog/server/internal/xlerrors" + "git.aiterp.net/stufflog/server/models" + sq "github.com/Masterminds/squirrel" + "github.com/jmoiron/sqlx" +) + +type projectRepository struct { + db *sqlx.DB +} + +func (r *projectRepository) Find(ctx context.Context, id string) (*models.Project, error) { + project := models.Project{} + err := r.db.GetContext(ctx, &project, "SELECT * FROM project WHERE project_id=?", id) + if err != nil { + if err == sql.ErrNoRows { + return nil, xlerrors.NotFound("Project") + } + + return nil, err + } + + return &project, nil +} + +func (r *projectRepository) List(ctx context.Context, filter models.ProjectFilter) ([]*models.Project, error) { + q := sq.Select("project.*").From("project").OrderBy("project_id") + if len(filter.ProjectIDs) > 0 { + q = q.Where(sq.Eq{"project_id": filter.ProjectIDs}) + } + if filter.Search != nil { + q = q.Where("MATCH (name, description) AGAINST (?)", *filter.Search) + } + if filter.Permission != nil && filter.Permission.Valid() { + q = q.LeftJoin("project_permission ON project.project_id = project_permission.project_id AND project_permission.user_id = ?", filter.Permission.UserID) + if filter.Permission.MaxLevel >= filter.Permission.MinLevel { + q = q.Where(sq.And{ + sq.GtOrEq{"project_permission.access_level": filter.Permission.MinLevel}, + sq.LtOrEq{"project_permission.access_level": filter.Permission.MaxLevel}, + }) + } else { + q = q.Where(sq.GtOrEq{"project_permission.access_level": filter.Permission.MinLevel}) + } + } + + query, args, err := q.ToSql() + if err != nil { + return nil, err + } + + results := make([]*models.Project, 0, 16) + err = r.db.SelectContext(ctx, &results, query, args...) + if err != nil { + if err == sql.ErrNoRows { + return []*models.Project{}, nil + } + + return nil, err + } + + return results, nil +} + +func (r *projectRepository) Insert(ctx context.Context, project models.Project) (*models.Project, error) { + if !project.ValidKey() { + return nil, errors.New("invalid project id") + } + + _, err := r.db.NamedExecContext(ctx, ` + INSERT INTO project (project_id, name, description, daily_points) + VALUES (:project_id, :name, :description, :daily_points) + `, project) + + return &project, err +} + +func (r *projectRepository) Save(ctx context.Context, project models.Project) error { + _, err := r.db.NamedExecContext(ctx, ` + UPDATE project + SET name=:name, description=:description, daily_points=:daily_points + WHERE project_id=:project_id + `, project) + + return err +} + +func (r *projectRepository) GetPermission(ctx context.Context, project models.Project, user models.User) (*models.ProjectPermission, error) { + permission := models.ProjectPermission{} + err := r.db.GetContext(ctx, &permission, "SELECT * FROM project_permission WHERE project_id=? AND user_id=?", project.ID, user.ID) + if err != nil { + if err == sql.ErrNoRows { + return &models.ProjectPermission{ + ProjectID: project.ID, + UserID: user.ID, + }, nil + } + + return nil, err + } + + return &permission, nil +} + +func (r *projectRepository) GetIssuePermission(ctx context.Context, issue models.Issue, user models.User) (*models.ProjectPermission, error) { + return r.GetPermission(ctx, models.Project{ID: issue.ProjectID}, user) +} + +func (r *projectRepository) SetPermission(ctx context.Context, permission models.ProjectPermission) error { + _, err := r.db.NamedExecContext(ctx, ` + REPLACE INTO project_permission (project_id, user_id, access_level) + VALUES (:project_id, :user_id, :access_level) + `, permission) + + return err +} + +func (r *projectRepository) Delete(ctx context.Context, project models.Project) error { + _, err := r.db.ExecContext(ctx, "DELETE FROM project WHERE project_id=?", project.ID) + if err != nil { + return err + } + + _, err = r.db.ExecContext(ctx, "DELETE FROM project_permission WHERE project_id=?", project.ID) + if err != nil { + return err + } + + //_, err = r.db.ExecContext(ctx, "DELETE FROM project_status WHERE project_id=?", project.ID) + //if err != nil { + // return err + //} + + return nil +} diff --git a/database/drivers/mysqldriver/projects_test.go b/database/drivers/mysqldriver/projects_test.go new file mode 100644 index 0000000..98d790c --- /dev/null +++ b/database/drivers/mysqldriver/projects_test.go @@ -0,0 +1,165 @@ +package mysqldriver + +import ( + "context" + "git.aiterp.net/stufflog/server/internal/xlerrors" + "git.aiterp.net/stufflog/server/models" + "github.com/stretchr/testify/assert" + "testing" + "time" +) + +var project1 = models.Project{ + ID: "STUFF", + Name: "Stuff and things", + Description: "Items and artifacts", + DailyPoints: 200, +} + +var project2 = models.Project{ + ID: "MODELING", + Name: "3D Modelling", + Description: "Making stuff.", + DailyPoints: 250, +} + +var project3 = models.Project{ + ID: "DINNER", + Name: "Dinner", + Description: "Shopping lists and meals.", + DailyPoints: 0, +} + +var projectSearch1 = "stuff" + +func TestProjectRepository(t *testing.T) { + projects := testDB.projects + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + + assert.NoError(t, clearTable("project")) + + // INSERT + result, err := projects.Insert(ctx, project1) + assert.NoError(t, err) + assert.Equal(t, project1, *result) + result, err = projects.Insert(ctx, project2) + assert.NoError(t, err) + assert.Equal(t, project2, *result) + result, err = projects.Insert(ctx, project3) + assert.NoError(t, err) + assert.Equal(t, project3, *result) + + if t.Failed() { + return + } + + // FIND + result, err = projects.Find(ctx, "MODELING") + assert.NoError(t, err) + assert.Equal(t, &project2, result) + + // FIND't + result, err = projects.Find(ctx, "BLARGH") + assert.Error(t, err) + assert.True(t, xlerrors.IsNotFound(err)) + assert.Nil(t, result) + + // LIST + results, err := projects.List(ctx, models.ProjectFilter{}) + assert.NoError(t, err) + assert.Equal(t, []*models.Project{&project3, &project2, &project1}, results) + results, err = projects.List(ctx, models.ProjectFilter{ + Search: &projectSearch1, + }) + assert.NoError(t, err) + assert.Equal(t, []*models.Project{&project2, &project1}, results) + results, err = projects.List(ctx, models.ProjectFilter{ + ProjectIDs: []string{"DINNER", "MODELING"}, + }) + assert.NoError(t, err) + assert.Equal(t, []*models.Project{&project3, &project2}, results) + + if t.Failed() { + return + } + + // UPDATE + project2fix := project2 + project2fix.Name = "3D Modeling" + project2fix.Description = "Modeling 3D stuff." + project2fix.DailyPoints = 150 + err = projects.Save(ctx, project2fix) + assert.NoError(t, err) + + if t.Failed() { + return + } + + // FIND after UPDATE + result, err = projects.Find(ctx, "MODELING") + assert.NoError(t, err) + assert.Equal(t, &project2fix, result) + + // DELETE + err = projects.Delete(ctx, project3) + assert.NoError(t, err) + + if t.Failed() { + return + } + + // LIST after DELETE + UPDATE + results, err = projects.List(ctx, models.ProjectFilter{}) + assert.NoError(t, err) + assert.Equal(t, []*models.Project{&project2fix, &project1}, results) + + // SET PERMISSION + err = projects.SetPermission(ctx, models.ProjectPermission{ + ProjectID: project1.ID, + UserID: user1.ID, + Level: models.ProjectPermissionLevelOwner, + }) + assert.NoError(t, err) + err = projects.SetPermission(ctx, models.ProjectPermission{ + ProjectID: project2fix.ID, + UserID: user1.ID, + Level: models.ProjectPermissionLevelMember, + }) + assert.NoError(t, err) + + // GET PERMISSIONS + permission, err := projects.GetPermission(ctx, project1, user1) + assert.NoError(t, err) + assert.Equal(t, project1.ID, permission.ProjectID) + assert.Equal(t, user1.ID, permission.UserID) + assert.Equal(t, models.ProjectPermissionLevelOwner, permission.Level) + permission, err = projects.GetPermission(ctx, project2fix, user1) + assert.NoError(t, err) + assert.Equal(t, models.ProjectPermissionLevelMember, permission.Level) + + // GET PERMISSION (default) + permission, err = projects.GetPermission(ctx, project1, user2) + assert.NoError(t, err) + assert.Equal(t, project1.ID, permission.ProjectID) + assert.Equal(t, user2.ID, permission.UserID) + assert.Equal(t, models.ProjectPermissionLevelNoAccess, permission.Level) + + // LIST after SET PERMISSION + results, err = projects.List(ctx, models.ProjectFilter{ + Permission: &models.ProjectFilterPermission{ + UserID: user1.ID, + MinLevel: models.ProjectPermissionLevelMember, + }, + }) + assert.NoError(t, err) + assert.Equal(t, []*models.Project{&project2fix, &project1}, results) + results, err = projects.List(ctx, models.ProjectFilter{ + Permission: &models.ProjectFilterPermission{ + UserID: user2.ID, + MinLevel: models.ProjectPermissionLevelMember, + }, + }) + assert.NoError(t, err) + assert.Equal(t, []*models.Project{}, results) +} diff --git a/database/drivers/mysqldriver/session.go b/database/drivers/mysqldriver/session.go new file mode 100644 index 0000000..0a26ed7 --- /dev/null +++ b/database/drivers/mysqldriver/session.go @@ -0,0 +1,49 @@ +package mysqldriver + +import ( + "context" + "database/sql" + "git.aiterp.net/stufflog/server/internal/xlerrors" + "git.aiterp.net/stufflog/server/models" + "github.com/jmoiron/sqlx" + "time" +) + +type sessionRepository struct { + db *sqlx.DB +} + +func (r *sessionRepository) Find(ctx context.Context, id string) (*models.Session, error) { + session := models.Session{} + err := r.db.GetContext(ctx, &session, "SELECT * FROM session WHERE session_id=?", id) + if err != nil { + if err == sql.ErrNoRows { + return nil, xlerrors.NotFound("Session") + } + + return nil, err + } else if time.Now().After(session.ExpiryTime) { + return nil, xlerrors.NotFound("Session") + } + + return &session, nil +} + +func (r *sessionRepository) Save(ctx context.Context, session models.Session) error { + _, err := r.db.NamedExecContext(ctx, ` + REPLACE INTO session (session_id, user_id, expiry_time) + VALUES (:session_id, :user_id, :expiry_time) + `, session) + + return err +} + +func (r *sessionRepository) Delete(ctx context.Context, session models.Session) error { + _, err := r.db.ExecContext(ctx, "DELETE FROM session WHERE session_id=?", session.ID) + return err +} + +func (r *sessionRepository) DeleteExpired(ctx context.Context) error { + _, err := r.db.ExecContext(ctx, "DELETE FROM session WHERE expiry_time 0 { + q = q.Where(sq.Eq{"user_id": filter.UserIDs}) + } + if filter.Active != nil { + q = q.Where(sq.Eq{"active": *filter.Active}) + } + if filter.Admin != nil { + q = q.Where(sq.Eq{"admin": *filter.Admin}) + } + if filter.Limit != nil && *filter.Limit > 0 { + q = q.Limit(uint64(*filter.Limit)) + } + + query, args, err := q.ToSql() + if err != nil { + return nil, err + } + + results := make([]*models.User, 0, 16) + err = r.db.SelectContext(ctx, &results, query, args...) + if err != nil { + if err == sql.ErrNoRows { + return []*models.User{}, nil + } + + return nil, err + } + + return results, nil +} + +func (r *userRepository) Insert(ctx context.Context, user models.User) (*models.User, error) { + if len(user.Name) < 1 || len([]byte(user.Name)) > 32 { + return nil, errors.New("user id is not valid") + } + + _, err := r.db.NamedExecContext(ctx, ` + INSERT INTO user ( + user_id, name, active, admin, hash + ) VALUES ( + :user_id, :name, :active, :admin, :hash + ) + `, user) + if err != nil { + return nil, err + } + + return &user, nil +} + +func (r *userRepository) Save(ctx context.Context, user models.User) error { + _, err := r.db.NamedExecContext(ctx, ` + UPDATE user + SET name=:name, + hash=:hash, + admin=:admin, + active=:active + WHERE user_id=:user_id + `, user) + + return err +} + +func (r *userRepository) Delete(ctx context.Context, user models.User) error { + _, err := r.db.ExecContext(ctx, "DELETE FROM user WHERE user_id=?", user.ID) + return err +} diff --git a/database/drivers/mysqldriver/users_test.go b/database/drivers/mysqldriver/users_test.go new file mode 100644 index 0000000..2ddbd51 --- /dev/null +++ b/database/drivers/mysqldriver/users_test.go @@ -0,0 +1,117 @@ +package mysqldriver + +import ( + "context" + "git.aiterp.net/stufflog/server/internal/xlerrors" + "git.aiterp.net/stufflog/server/models" + "github.com/stretchr/testify/assert" + "testing" + "time" +) + +var user1 = models.User{ + ID: "Test", + Name: "Testy Tester", + Active: true, + Admin: false, + Hash: []byte("$2y$12$tj/R/zDHrGy1Jsi57DUuSeCISYvEHb/F37p.9HGlyf72cIXnppeQK"), +} + +var user2 = models.User{ + ID: "Admin", + Name: "Administrator", + Active: true, + Admin: true, + Hash: []byte("$2y$12$tj/R/zDHrGy1Jsi57DUuSeCISYvEHb/F37p.9HGlyf72cIXnppeQK"), +} + +var user2Updated = models.User{ + ID: user2.ID, + Name: "Dethroned Dictator", + Active: false, + Admin: false, + Hash: []byte("$2y$12$7MuqYzV59HCtHJlRJCd/vOQIrFcMEMVhyySzJX.WlVtEJH3qHVPU2\n"), +} + +func TestUserRepository(t *testing.T) { + users := testDB.users + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + + assert.NoError(t, clearTable("user")) + + // INSERT + result, err := users.Insert(ctx, user1) + assert.NoError(t, err) + assert.Equal(t, user1, *result) + result, err = users.Insert(ctx, user2) + assert.NoError(t, err) + assert.Equal(t, user2, *result) + + // FIND + result, err = users.Find(ctx, user1.ID) + assert.NoError(t, err) + assert.Equal(t, &user1, result) + result, err = users.Find(ctx, user2.ID) + assert.NoError(t, err) + assert.Equal(t, &user2, result) + + // FIND't + result, err = users.Find(ctx, "NonExistent") + assert.Error(t, err) + assert.True(t, xlerrors.IsNotFound(err)) + assert.Nil(t, result) + + // LIST + results, err := users.List(ctx, models.UserFilter{}) + assert.NoError(t, err) + assert.Equal(t, []*models.User{&user2, &user1}, results) + results, err = users.List(ctx, models.UserFilter{ + Admin: ptrBool(true), + }) + assert.NoError(t, err) + assert.Equal(t, []*models.User{&user2}, results) + results, err = users.List(ctx, models.UserFilter{ + Active: ptrBool(true), + }) + assert.NoError(t, err) + assert.Equal(t, []*models.User{&user2, &user1}, results) + results, err = users.List(ctx, models.UserFilter{ + Active: ptrBool(false), + }) + assert.NoError(t, err) + assert.Equal(t, []*models.User{}, results) + results, err = users.List(ctx, models.UserFilter{ + UserIDs: []string{user1.ID}, + }) + assert.NoError(t, err) + assert.Equal(t, []*models.User{&user1}, results) + results, err = users.List(ctx, models.UserFilter{ + Limit: ptrInt(1), + }) + assert.NoError(t, err) + assert.Equal(t, []*models.User{&user2}, results) + + // UPDATE + err = users.Save(ctx, user2Updated) + assert.NoError(t, err) + + // LIST after UPDATE + results, err = users.List(ctx, models.UserFilter{}) + assert.NoError(t, err) + assert.Equal(t, []*models.User{&user2Updated, &user1}, results) + + // DELETE + err = users.Delete(ctx, user1) + assert.NoError(t, err) + + // LIST after DELETE + results, err = users.List(ctx, models.UserFilter{}) + assert.NoError(t, err) + assert.Equal(t, []*models.User{&user2Updated}, results) + + // INSERT after DELETE + result, err = users.Insert(ctx, user1) + assert.NoError(t, err) + assert.Equal(t, user1, *result) +} diff --git a/database/repositories/issuerepository.go b/database/repositories/issuerepository.go new file mode 100644 index 0000000..d4568aa --- /dev/null +++ b/database/repositories/issuerepository.go @@ -0,0 +1,14 @@ +package repositories + +import ( + "context" + "git.aiterp.net/stufflog/server/models" +) + +type IssueRepository interface { + Find(ctx context.Context, id string) (*models.Issue, error) + List(ctx context.Context, filter models.IssueFilter) ([]*models.Issue, error) + Insert(ctx context.Context, issue models.Issue) (*models.Issue, error) + Save(ctx context.Context, issue models.Issue) error + Delete(ctx context.Context, issue models.Issue) error +} diff --git a/database/repositories/itemrepository.go b/database/repositories/itemrepository.go new file mode 100644 index 0000000..e56c7db --- /dev/null +++ b/database/repositories/itemrepository.go @@ -0,0 +1,15 @@ +package repositories + +import ( + "context" + "git.aiterp.net/stufflog/server/models" +) + +type ItemRepository interface { + Find(ctx context.Context, id string) (*models.Item, error) + List(ctx context.Context, filter models.ItemFilter) ([]*models.Item, error) + Insert(ctx context.Context, item models.Item) (*models.Item, error) + Save(ctx context.Context, item models.Item) error + Delete(ctx context.Context, item models.Item) error + GetTags(ctx context.Context) ([]string, error) +} diff --git a/database/repositories/projectrepository.go b/database/repositories/projectrepository.go new file mode 100644 index 0000000..a1e34f3 --- /dev/null +++ b/database/repositories/projectrepository.go @@ -0,0 +1,17 @@ +package repositories + +import ( + "context" + "git.aiterp.net/stufflog/server/models" +) + +type ProjectRepository interface { + Find(ctx context.Context, id string) (*models.Project, error) + List(ctx context.Context, filter models.ProjectFilter) ([]*models.Project, error) + Insert(ctx context.Context, project models.Project) (*models.Project, error) + Save(ctx context.Context, project models.Project) error + GetPermission(ctx context.Context, project models.Project, user models.User) (*models.ProjectPermission, error) + GetIssuePermission(ctx context.Context, issue models.Issue, user models.User) (*models.ProjectPermission, error) + SetPermission(ctx context.Context, permission models.ProjectPermission) error + Delete(ctx context.Context, project models.Project) error +} diff --git a/database/repositories/sessionrepository.go b/database/repositories/sessionrepository.go new file mode 100644 index 0000000..41d1edb --- /dev/null +++ b/database/repositories/sessionrepository.go @@ -0,0 +1,13 @@ +package repositories + +import ( + "context" + "git.aiterp.net/stufflog/server/models" +) + +type SessionRepository interface { + Find(ctx context.Context, id string) (*models.Session, error) + Save(ctx context.Context, session models.Session) error + Delete(ctx context.Context, session models.Session) error + DeleteExpired(ctx context.Context) error +} diff --git a/database/repositories/userrepository.go b/database/repositories/userrepository.go new file mode 100644 index 0000000..277709b --- /dev/null +++ b/database/repositories/userrepository.go @@ -0,0 +1,14 @@ +package repositories + +import ( + "context" + "git.aiterp.net/stufflog/server/models" +) + +type UserRepository interface { + Find(ctx context.Context, id string) (*models.User, error) + List(ctx context.Context, filter models.UserFilter) ([]*models.User, error) + Insert(ctx context.Context, user models.User) (*models.User, error) + Save(ctx context.Context, user models.User) error + Delete(ctx context.Context, user models.User) error +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..630f1a3 --- /dev/null +++ b/go.mod @@ -0,0 +1,17 @@ +module git.aiterp.net/stufflog/server + +go 1.14 + +require ( + github.com/99designs/gqlgen v0.11.3 + github.com/Masterminds/squirrel v1.2.0 + github.com/gin-gonic/gin v1.6.2 + github.com/go-sql-driver/mysql v1.5.0 + github.com/jmoiron/sqlx v1.2.0 + github.com/pkg/errors v0.9.1 + github.com/pressly/goose v2.6.0+incompatible + github.com/stretchr/testify v1.5.1 + github.com/urfave/cli/v2 v2.2.0 + github.com/vektah/gqlparser/v2 v2.0.1 + golang.org/x/crypto v0.0.0-20200403201458-baeed622b8d8 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..53859b8 --- /dev/null +++ b/go.sum @@ -0,0 +1,143 @@ +github.com/99designs/gqlgen v0.11.3 h1:oFSxl1DFS9X///uHV3y6CEfpcXWrDUxVblR4Xib2bs4= +github.com/99designs/gqlgen v0.11.3/go.mod h1:RgX5GRRdDWNkh4pBrdzNpNPFVsdoUFY2+adM6nb1N+4= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Masterminds/squirrel v1.2.0 h1:K1NhbTO21BWG47IVR0OnIZuE0LZcXAYqywrC3Ko53KI= +github.com/Masterminds/squirrel v1.2.0/go.mod h1:yaPeOnPG5ZRwL9oKdTsO/prlkPbXWZlRVMQ/gGlzIuA= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/agnivade/levenshtein v1.0.3 h1:M5ZnqLOoZR8ygVq0FfkXsNOKzMCk0xRiow0R5+5VkQ0= +github.com/agnivade/levenshtein v1.0.3/go.mod h1:4SFRZbbXWLF4MU1T9Qg0pGgH3Pjs+t6ie5efyrwRJXs= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c h1:TUuUh0Xgj97tLMNtWtNvI9mIV6isjEb9lBMNv+77IGM= +github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.2 h1:88crIK23zO6TqlQBt+f9FrPJNKm9ZEr7qjp9vl/d5TM= +github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ= +github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= +github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= +github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4= +github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047 h1:zCoDWFD5nrJJVjbXiDZcVhOBSzKn3o9LgRLLMRNuru8= +github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pressly/goose v2.6.0+incompatible h1:3f8zIQ8rfgP9tyI0Hmcs2YNAqUCL1c+diLe3iU8Qd/k= +github.com/pressly/goose v2.6.0+incompatible/go.mod h1:m+QHWCqxR3k8D9l7qfzuC/djtlfzxr34mozWDYEu1z8= +github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= +github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4= +github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= +github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U= +github.com/vektah/gqlparser/v2 v2.0.1 h1:xgl5abVnsd4hkN9rk65OJID9bfcLSMuTaTcZj777q1o= +github.com/vektah/gqlparser/v2 v2.0.1/go.mod h1:SyUiHgLATUR8BiYURfTirrTcGpcE+4XkV2se04Px1Ms= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200403201458-baeed622b8d8 h1:fpnn/HnJONpIu6hkXi1u/7rR0NzilgWr4T0JmWkEitk= +golang.org/x/crypto v0.0.0-20200403201458-baeed622b8d8/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589 h1:rjUrONFu4kLchcZTfp3/96bR8bW8dIa8uz3cR5n0cgM= +golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +sourcegraph.com/sourcegraph/appdash v0.0.0-20180110180208-2cc67fd64755/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= +sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67/go.mod h1:L5q+DGLGOQFpo1snNEkLOJT2d1YTW66rWNzatr3He1k= diff --git a/graph/gqlgen.yml b/graph/gqlgen.yml new file mode 100644 index 0000000..23dd4a3 --- /dev/null +++ b/graph/gqlgen.yml @@ -0,0 +1,18 @@ +schema: + - schema/*.gql + +exec: + filename: graphcore/exec_gen.go + package: graphcore + +model: + filename: graphcore/input_gen.go + package: graphcore + +resolver: + layout: follow-schema + dir: resolvers + package: resolvers + +autobind: + - git.aiterp.net/stufflog/server/models diff --git a/graph/graph.go b/graph/graph.go new file mode 100644 index 0000000..e22fc2b --- /dev/null +++ b/graph/graph.go @@ -0,0 +1,30 @@ +package graph + +import ( + "git.aiterp.net/stufflog/server/graph/graphcore" + "git.aiterp.net/stufflog/server/graph/resolvers" + "git.aiterp.net/stufflog/server/services" + "github.com/99designs/gqlgen/graphql" + "github.com/99designs/gqlgen/graphql/handler" + "github.com/gin-gonic/gin" +) + +//go:generate go run github.com/99designs/gqlgen --verbose --config gqlgen.yml + +// New creates a new GraphQL schema. +func New(s services.Bundle) graphql.ExecutableSchema { + return graphcore.NewExecutableSchema(graphcore.Config{ + Resolvers: &resolvers.Resolver{S: s}, + }) +} + +func Gin(s services.Bundle) gin.HandlerFunc { + schema := New(s) + gqlHandler := handler.NewDefaultServer(schema) + + return func(c *gin.Context) { + s.Auth.CheckGinSession(c) + + gqlHandler.ServeHTTP(c.Writer, c.Request) + } +} diff --git a/graph/graphcore/exec_gen.go b/graph/graphcore/exec_gen.go new file mode 100644 index 0000000..06a94bd --- /dev/null +++ b/graph/graphcore/exec_gen.go @@ -0,0 +1,4983 @@ +// Code generated by github.com/99designs/gqlgen, DO NOT EDIT. + +package graphcore + +import ( + "bytes" + "context" + "errors" + "strconv" + "sync" + "sync/atomic" + "time" + + "git.aiterp.net/stufflog/server/models" + "github.com/99designs/gqlgen/graphql" + "github.com/99designs/gqlgen/graphql/introspection" + gqlparser "github.com/vektah/gqlparser/v2" + "github.com/vektah/gqlparser/v2/ast" +) + +// region ************************** generated!.gotpl ************************** + +// NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface. +func NewExecutableSchema(cfg Config) graphql.ExecutableSchema { + return &executableSchema{ + resolvers: cfg.Resolvers, + directives: cfg.Directives, + complexity: cfg.Complexity, + } +} + +type Config struct { + Resolvers ResolverRoot + Directives DirectiveRoot + Complexity ComplexityRoot +} + +type ResolverRoot interface { + Issue() IssueResolver + Mutation() MutationResolver + Project() ProjectResolver + Query() QueryResolver +} + +type DirectiveRoot struct { +} + +type ComplexityRoot struct { + Issue struct { + Assignee func(childComplexity int) int + AssigneeID func(childComplexity int) int + CreatedTime func(childComplexity int) int + Description func(childComplexity int) int + DueTime func(childComplexity int) int + ID func(childComplexity int) int + Name func(childComplexity int) int + Owner func(childComplexity int) int + OwnerID func(childComplexity int) int + Project func(childComplexity int) int + ProjectID func(childComplexity int) int + StatusName func(childComplexity int) int + StatusStage func(childComplexity int) int + Title func(childComplexity int) int + UpdatedTime func(childComplexity int) int + } + + Mutation struct { + LoginUser func(childComplexity int, input *UserLoginInput) int + LogoutUser func(childComplexity int) int + } + + Project struct { + DailyPoints func(childComplexity int) int + Description func(childComplexity int) int + ID func(childComplexity int) int + Issues func(childComplexity int, filter *ProjectIssueFilter) int + Name func(childComplexity int) int + Permissions func(childComplexity int) int + UserPermissions func(childComplexity int) int + } + + ProjectPermissions struct { + AccessLevel func(childComplexity int) int + User func(childComplexity int) int + UserID func(childComplexity int) int + } + + ProjectStatus struct { + Description func(childComplexity int) int + Name func(childComplexity int) int + Stage func(childComplexity int) int + } + + Query struct { + Issue func(childComplexity int, id string) int + Issues func(childComplexity int, filter *models.IssueFilter) int + Project func(childComplexity int, id string) int + Projects func(childComplexity int, filter *models.ProjectFilter) int + Session func(childComplexity int) int + } + + User struct { + Active func(childComplexity int) int + Admin func(childComplexity int) int + ID func(childComplexity int) int + Name func(childComplexity int) int + } +} + +type IssueResolver interface { + Project(ctx context.Context, obj *models.Issue) (*models.Project, error) + Owner(ctx context.Context, obj *models.Issue) (*models.User, error) + Assignee(ctx context.Context, obj *models.Issue) (*models.User, error) +} +type MutationResolver interface { + LoginUser(ctx context.Context, input *UserLoginInput) (*models.User, error) + LogoutUser(ctx context.Context) (*models.User, error) +} +type ProjectResolver interface { + Issues(ctx context.Context, obj *models.Project, filter *ProjectIssueFilter) ([]*models.Issue, error) + Permissions(ctx context.Context, obj *models.Project) ([]*ProjectPermissions, error) + UserPermissions(ctx context.Context, obj *models.Project) (*ProjectPermissions, error) +} +type QueryResolver interface { + Issue(ctx context.Context, id string) (*models.Issue, error) + Issues(ctx context.Context, filter *models.IssueFilter) ([]*models.Issue, error) + Project(ctx context.Context, id string) (*models.Project, error) + Projects(ctx context.Context, filter *models.ProjectFilter) ([]*models.Project, error) + Session(ctx context.Context) (*models.User, error) +} + +type executableSchema struct { + resolvers ResolverRoot + directives DirectiveRoot + complexity ComplexityRoot +} + +func (e *executableSchema) Schema() *ast.Schema { + return parsedSchema +} + +func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { + ec := executionContext{nil, e} + _ = ec + switch typeName + "." + field { + + case "Issue.assignee": + if e.complexity.Issue.Assignee == nil { + break + } + + return e.complexity.Issue.Assignee(childComplexity), true + + case "Issue.assigneeId": + if e.complexity.Issue.AssigneeID == nil { + break + } + + return e.complexity.Issue.AssigneeID(childComplexity), true + + case "Issue.createdTime": + if e.complexity.Issue.CreatedTime == nil { + break + } + + return e.complexity.Issue.CreatedTime(childComplexity), true + + case "Issue.description": + if e.complexity.Issue.Description == nil { + break + } + + return e.complexity.Issue.Description(childComplexity), true + + case "Issue.dueTime": + if e.complexity.Issue.DueTime == nil { + break + } + + return e.complexity.Issue.DueTime(childComplexity), true + + case "Issue.id": + if e.complexity.Issue.ID == nil { + break + } + + return e.complexity.Issue.ID(childComplexity), true + + case "Issue.name": + if e.complexity.Issue.Name == nil { + break + } + + return e.complexity.Issue.Name(childComplexity), true + + case "Issue.owner": + if e.complexity.Issue.Owner == nil { + break + } + + return e.complexity.Issue.Owner(childComplexity), true + + case "Issue.ownerId": + if e.complexity.Issue.OwnerID == nil { + break + } + + return e.complexity.Issue.OwnerID(childComplexity), true + + case "Issue.project": + if e.complexity.Issue.Project == nil { + break + } + + return e.complexity.Issue.Project(childComplexity), true + + case "Issue.projectId": + if e.complexity.Issue.ProjectID == nil { + break + } + + return e.complexity.Issue.ProjectID(childComplexity), true + + case "Issue.statusName": + if e.complexity.Issue.StatusName == nil { + break + } + + return e.complexity.Issue.StatusName(childComplexity), true + + case "Issue.statusStage": + if e.complexity.Issue.StatusStage == nil { + break + } + + return e.complexity.Issue.StatusStage(childComplexity), true + + case "Issue.title": + if e.complexity.Issue.Title == nil { + break + } + + return e.complexity.Issue.Title(childComplexity), true + + case "Issue.updatedTime": + if e.complexity.Issue.UpdatedTime == nil { + break + } + + return e.complexity.Issue.UpdatedTime(childComplexity), true + + case "Mutation.loginUser": + if e.complexity.Mutation.LoginUser == nil { + break + } + + args, err := ec.field_Mutation_loginUser_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.LoginUser(childComplexity, args["input"].(*UserLoginInput)), true + + case "Mutation.logoutUser": + if e.complexity.Mutation.LogoutUser == nil { + break + } + + return e.complexity.Mutation.LogoutUser(childComplexity), true + + case "Project.dailyPoints": + if e.complexity.Project.DailyPoints == nil { + break + } + + return e.complexity.Project.DailyPoints(childComplexity), true + + case "Project.description": + if e.complexity.Project.Description == nil { + break + } + + return e.complexity.Project.Description(childComplexity), true + + case "Project.id": + if e.complexity.Project.ID == nil { + break + } + + return e.complexity.Project.ID(childComplexity), true + + case "Project.issues": + if e.complexity.Project.Issues == nil { + break + } + + args, err := ec.field_Project_issues_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Project.Issues(childComplexity, args["filter"].(*ProjectIssueFilter)), true + + case "Project.name": + if e.complexity.Project.Name == nil { + break + } + + return e.complexity.Project.Name(childComplexity), true + + case "Project.permissions": + if e.complexity.Project.Permissions == nil { + break + } + + return e.complexity.Project.Permissions(childComplexity), true + + case "Project.userPermissions": + if e.complexity.Project.UserPermissions == nil { + break + } + + return e.complexity.Project.UserPermissions(childComplexity), true + + case "ProjectPermissions.accessLevel": + if e.complexity.ProjectPermissions.AccessLevel == nil { + break + } + + return e.complexity.ProjectPermissions.AccessLevel(childComplexity), true + + case "ProjectPermissions.user": + if e.complexity.ProjectPermissions.User == nil { + break + } + + return e.complexity.ProjectPermissions.User(childComplexity), true + + case "ProjectPermissions.userId": + if e.complexity.ProjectPermissions.UserID == nil { + break + } + + return e.complexity.ProjectPermissions.UserID(childComplexity), true + + case "ProjectStatus.description": + if e.complexity.ProjectStatus.Description == nil { + break + } + + return e.complexity.ProjectStatus.Description(childComplexity), true + + case "ProjectStatus.name": + if e.complexity.ProjectStatus.Name == nil { + break + } + + return e.complexity.ProjectStatus.Name(childComplexity), true + + case "ProjectStatus.stage": + if e.complexity.ProjectStatus.Stage == nil { + break + } + + return e.complexity.ProjectStatus.Stage(childComplexity), true + + case "Query.issue": + if e.complexity.Query.Issue == nil { + break + } + + args, err := ec.field_Query_issue_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.Issue(childComplexity, args["id"].(string)), true + + case "Query.issues": + if e.complexity.Query.Issues == nil { + break + } + + args, err := ec.field_Query_issues_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.Issues(childComplexity, args["filter"].(*models.IssueFilter)), true + + case "Query.project": + if e.complexity.Query.Project == nil { + break + } + + args, err := ec.field_Query_project_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.Project(childComplexity, args["id"].(string)), true + + case "Query.projects": + if e.complexity.Query.Projects == nil { + break + } + + args, err := ec.field_Query_projects_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.Projects(childComplexity, args["filter"].(*models.ProjectFilter)), true + + case "Query.session": + if e.complexity.Query.Session == nil { + break + } + + return e.complexity.Query.Session(childComplexity), true + + case "User.active": + if e.complexity.User.Active == nil { + break + } + + return e.complexity.User.Active(childComplexity), true + + case "User.admin": + if e.complexity.User.Admin == nil { + break + } + + return e.complexity.User.Admin(childComplexity), true + + case "User.id": + if e.complexity.User.ID == nil { + break + } + + return e.complexity.User.ID(childComplexity), true + + case "User.name": + if e.complexity.User.Name == nil { + break + } + + return e.complexity.User.Name(childComplexity), true + + } + return 0, false +} + +func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { + rc := graphql.GetOperationContext(ctx) + ec := executionContext{rc, e} + first := true + + switch rc.Operation.Operation { + case ast.Query: + return func(ctx context.Context) *graphql.Response { + if !first { + return nil + } + first = false + data := ec._Query(ctx, rc.Operation.SelectionSet) + var buf bytes.Buffer + data.MarshalGQL(&buf) + + return &graphql.Response{ + Data: buf.Bytes(), + } + } + case ast.Mutation: + return func(ctx context.Context) *graphql.Response { + if !first { + return nil + } + first = false + data := ec._Mutation(ctx, rc.Operation.SelectionSet) + var buf bytes.Buffer + data.MarshalGQL(&buf) + + return &graphql.Response{ + Data: buf.Bytes(), + } + } + + default: + return graphql.OneShot(graphql.ErrorResponse(ctx, "unsupported GraphQL operation")) + } +} + +type executionContext struct { + *graphql.OperationContext + *executableSchema +} + +func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { + if ec.DisableIntrospection { + return nil, errors.New("introspection disabled") + } + return introspection.WrapSchema(parsedSchema), nil +} + +func (ec *executionContext) introspectType(name string) (*introspection.Type, error) { + if ec.DisableIntrospection { + return nil, errors.New("introspection disabled") + } + return introspection.WrapTypeFromDef(parsedSchema, parsedSchema.Types[name]), nil +} + +var sources = []*ast.Source{ + &ast.Source{Name: "schema/issue.gql", Input: `type Issue { + id: String! + projectId: String! + ownerId: String! + assigneeId: String! + statusStage: Int! + statusName: String! + createdTime: Time! + updatedTime: Time! + dueTime: Time + name: String! + title: String! + description: String! + + project: Project + owner: User + assignee: User +} + +input IssueFilter { + "Filter by issue IDs (mostly used internally by data loaders)" + issueIds: [String!] + "Filter by project IDs" + projectIds: [String!] + "Filter by owner IDs" + ownerIds: [String!] + "Filter by assignee IDs" + assigneeIds: [String!] + "Text search" + search: String + "Earliest stage (inclusive)" + minStage: Int + "Latest stage (inclusive)" + maxStage: Int + "Limit the result set" + limit: Int +}`, BuiltIn: false}, + &ast.Source{Name: "schema/mutation.gql", Input: `type Mutation { + loginUser(input: UserLoginInput): User! + logoutUser: User! +}`, BuiltIn: false}, + &ast.Source{Name: "schema/project.gql", Input: `type Project { + "The ID of the project, which is also the prefix for all its issues." + id: String! + "The name of the project, used in place of the ID in the UI." + name: String! + "Description of the project." + description: String! + "The amount of points given as a bonus towards goals for any activity on a given day." + dailyPoints: Int! + + "Get issues within the project." + issues(filter: ProjectIssueFilter): [Issue!]! + "All users' permissions. Only available to administrators and up." + permissions: [ProjectPermissions!]! + "Own permissions to the project. Available to any logged in user." + userPermissions: ProjectPermissions! +} + +"The permissions of a user within the project." +type ProjectPermissions { + "User ID." + userId: String! + "Access level." + accessLevel: Int! + + "The user whose permissions it is. Can be null if the user no longer exists." + user: User +} + +type ProjectStatus { + "The stage of the status. 0=inactive, 1=pending, 2=active, 3=review, 4=completed, 5=failed, 6=postponed" + stage: Int! + "The name of the status." + name: String! + "A description of the status and where it's used." + description: String! +} + +"Filter for projects query" +input ProjectFilter { + "Project IDs" + projectIds: [String!] + "Text search" + search: String + "User permission" + permission: ProjectFilterPermission +} + +input ProjectFilterPermission { + "User ID" + userId: String! + "Lowest access level to filter by (inclusive)" + minLevel: Int! + "Highest access level to filter by (inclusive)" + maxLevel: Int! +} + +"A limited filter for constraining the list of issues within a project." +input ProjectIssueFilter { + "Filter by assignee IDs" + assigneeIds: [String!] + "Text search" + search: String + "Earliest stage (inclusive)" + minStage: Int + "Latest stage (inclusive)" + maxStage: Int + "Limit the result set" + limit: Int +}`, BuiltIn: false}, + &ast.Source{Name: "schema/query.gql", Input: `type Query { + "Find issue" + issue(id: String!): Issue! + "List issues" + issues(filter: IssueFilter): [Issue!]! + + "Find project" + project(id: String!): Project! + "List projects" + projects(filter: ProjectFilter): [Project!]! + + "Session checks the user session." + session: User +}`, BuiltIn: false}, + &ast.Source{Name: "schema/scalars.gql", Input: `scalar Time`, BuiltIn: false}, + &ast.Source{Name: "schema/user.gql", Input: `"User information." +type User { + "User ID, used when logging in." + id: String! + "User's display name, which can differ from the user ID." + name: String! + "Whether the user is active." + active: Boolean! + "Whether the user has general administrator privileges. This does not grant admin rights on any project." + admin: Boolean! +} + +"Input for loginUser." +input UserLoginInput { + userId: String! + password: String! + rememberMe: Boolean! +} +`, BuiltIn: false}, +} +var parsedSchema = gqlparser.MustLoadSchema(sources...) + +// endregion ************************** generated!.gotpl ************************** + +// region ***************************** args.gotpl ***************************** + +func (ec *executionContext) field_Mutation_loginUser_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 *UserLoginInput + if tmp, ok := rawArgs["input"]; ok { + arg0, err = ec.unmarshalOUserLoginInput2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋgraphᚋgraphcoreᚐUserLoginInput(ctx, tmp) + if err != nil { + return nil, err + } + } + args["input"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Project_issues_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 *ProjectIssueFilter + if tmp, ok := rawArgs["filter"]; ok { + arg0, err = ec.unmarshalOProjectIssueFilter2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋgraphᚋgraphcoreᚐProjectIssueFilter(ctx, tmp) + if err != nil { + return nil, err + } + } + args["filter"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["name"]; ok { + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["name"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Query_issue_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["id"]; ok { + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["id"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Query_issues_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 *models.IssueFilter + if tmp, ok := rawArgs["filter"]; ok { + arg0, err = ec.unmarshalOIssueFilter2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐIssueFilter(ctx, tmp) + if err != nil { + return nil, err + } + } + args["filter"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Query_project_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["id"]; ok { + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["id"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Query_projects_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 *models.ProjectFilter + if tmp, ok := rawArgs["filter"]; ok { + arg0, err = ec.unmarshalOProjectFilter2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐProjectFilter(ctx, tmp) + if err != nil { + return nil, err + } + } + args["filter"] = arg0 + return args, nil +} + +func (ec *executionContext) field___Type_enumValues_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 bool + if tmp, ok := rawArgs["includeDeprecated"]; ok { + arg0, err = ec.unmarshalOBoolean2bool(ctx, tmp) + if err != nil { + return nil, err + } + } + args["includeDeprecated"] = arg0 + return args, nil +} + +func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 bool + if tmp, ok := rawArgs["includeDeprecated"]; ok { + arg0, err = ec.unmarshalOBoolean2bool(ctx, tmp) + if err != nil { + return nil, err + } + } + args["includeDeprecated"] = arg0 + return args, nil +} + +// endregion ***************************** args.gotpl ***************************** + +// region ************************** directives.gotpl ************************** + +// endregion ************************** directives.gotpl ************************** + +// region **************************** field.gotpl ***************************** + +func (ec *executionContext) _Issue_id(ctx context.Context, field graphql.CollectedField, obj *models.Issue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Issue", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, 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) _Issue_projectId(ctx context.Context, field graphql.CollectedField, obj *models.Issue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Issue", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ProjectID, 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) _Issue_ownerId(ctx context.Context, field graphql.CollectedField, obj *models.Issue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Issue", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.OwnerID, 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) _Issue_assigneeId(ctx context.Context, field graphql.CollectedField, obj *models.Issue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Issue", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.AssigneeID, 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) _Issue_statusStage(ctx context.Context, field graphql.CollectedField, obj *models.Issue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Issue", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.StatusStage, 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.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) _Issue_statusName(ctx context.Context, field graphql.CollectedField, obj *models.Issue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Issue", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.StatusName, 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) _Issue_createdTime(ctx context.Context, field graphql.CollectedField, obj *models.Issue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Issue", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.CreatedTime, 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.(time.Time) + fc.Result = res + return ec.marshalNTime2timeᚐTime(ctx, field.Selections, res) +} + +func (ec *executionContext) _Issue_updatedTime(ctx context.Context, field graphql.CollectedField, obj *models.Issue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Issue", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.UpdatedTime, 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.(time.Time) + fc.Result = res + return ec.marshalNTime2timeᚐTime(ctx, field.Selections, res) +} + +func (ec *executionContext) _Issue_dueTime(ctx context.Context, field graphql.CollectedField, obj *models.Issue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Issue", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.DueTime, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(time.Time) + fc.Result = res + return ec.marshalOTime2timeᚐTime(ctx, field.Selections, res) +} + +func (ec *executionContext) _Issue_name(ctx context.Context, field graphql.CollectedField, obj *models.Issue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Issue", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) _Issue_title(ctx context.Context, field graphql.CollectedField, obj *models.Issue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Issue", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Title, 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) _Issue_description(ctx context.Context, field graphql.CollectedField, obj *models.Issue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Issue", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description, 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) _Issue_project(ctx context.Context, field graphql.CollectedField, obj *models.Issue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Issue", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Issue().Project(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*models.Project) + fc.Result = res + return ec.marshalOProject2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐProject(ctx, field.Selections, res) +} + +func (ec *executionContext) _Issue_owner(ctx context.Context, field graphql.CollectedField, obj *models.Issue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Issue", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Issue().Owner(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*models.User) + fc.Result = res + return ec.marshalOUser2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐUser(ctx, field.Selections, res) +} + +func (ec *executionContext) _Issue_assignee(ctx context.Context, field graphql.CollectedField, obj *models.Issue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Issue", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Issue().Assignee(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*models.User) + fc.Result = res + return ec.marshalOUser2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐUser(ctx, field.Selections, res) +} + +func (ec *executionContext) _Mutation_loginUser(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_loginUser_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().LoginUser(rctx, args["input"].(*UserLoginInput)) + }) + 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.(*models.User) + fc.Result = res + return ec.marshalNUser2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐUser(ctx, field.Selections, res) +} + +func (ec *executionContext) _Mutation_logoutUser(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().LogoutUser(rctx) + }) + 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.(*models.User) + fc.Result = res + return ec.marshalNUser2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐUser(ctx, field.Selections, res) +} + +func (ec *executionContext) _Project_id(ctx context.Context, field graphql.CollectedField, obj *models.Project) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Project", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, 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) _Project_name(ctx context.Context, field graphql.CollectedField, obj *models.Project) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Project", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) _Project_description(ctx context.Context, field graphql.CollectedField, obj *models.Project) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Project", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description, 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) _Project_dailyPoints(ctx context.Context, field graphql.CollectedField, obj *models.Project) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Project", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.DailyPoints, 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.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) _Project_issues(ctx context.Context, field graphql.CollectedField, obj *models.Project) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Project", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Project_issues_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Project().Issues(rctx, obj, args["filter"].(*ProjectIssueFilter)) + }) + 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.([]*models.Issue) + fc.Result = res + return ec.marshalNIssue2ᚕᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐIssueᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) _Project_permissions(ctx context.Context, field graphql.CollectedField, obj *models.Project) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Project", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Project().Permissions(rctx, obj) + }) + 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.([]*ProjectPermissions) + fc.Result = res + return ec.marshalNProjectPermissions2ᚕᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋgraphᚋgraphcoreᚐProjectPermissionsᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) _Project_userPermissions(ctx context.Context, field graphql.CollectedField, obj *models.Project) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Project", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Project().UserPermissions(rctx, obj) + }) + 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.(*ProjectPermissions) + fc.Result = res + return ec.marshalNProjectPermissions2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋgraphᚋgraphcoreᚐProjectPermissions(ctx, field.Selections, res) +} + +func (ec *executionContext) _ProjectPermissions_userId(ctx context.Context, field graphql.CollectedField, obj *ProjectPermissions) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "ProjectPermissions", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.UserID, 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) _ProjectPermissions_accessLevel(ctx context.Context, field graphql.CollectedField, obj *ProjectPermissions) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "ProjectPermissions", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.AccessLevel, 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.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) _ProjectPermissions_user(ctx context.Context, field graphql.CollectedField, obj *ProjectPermissions) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "ProjectPermissions", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.User, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*models.User) + fc.Result = res + return ec.marshalOUser2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐUser(ctx, field.Selections, res) +} + +func (ec *executionContext) _ProjectStatus_stage(ctx context.Context, field graphql.CollectedField, obj *models.ProjectStatus) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "ProjectStatus", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Stage, 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.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) _ProjectStatus_name(ctx context.Context, field graphql.CollectedField, obj *models.ProjectStatus) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "ProjectStatus", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) _ProjectStatus_description(ctx context.Context, field graphql.CollectedField, obj *models.ProjectStatus) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "ProjectStatus", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description, 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) _Query_issue(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Query", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Query_issue_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().Issue(rctx, args["id"].(string)) + }) + 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.(*models.Issue) + fc.Result = res + return ec.marshalNIssue2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐIssue(ctx, field.Selections, res) +} + +func (ec *executionContext) _Query_issues(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Query", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Query_issues_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().Issues(rctx, args["filter"].(*models.IssueFilter)) + }) + 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.([]*models.Issue) + fc.Result = res + return ec.marshalNIssue2ᚕᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐIssueᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) _Query_project(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Query", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Query_project_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().Project(rctx, args["id"].(string)) + }) + 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.(*models.Project) + fc.Result = res + return ec.marshalNProject2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐProject(ctx, field.Selections, res) +} + +func (ec *executionContext) _Query_projects(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Query", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Query_projects_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().Projects(rctx, args["filter"].(*models.ProjectFilter)) + }) + 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.([]*models.Project) + fc.Result = res + return ec.marshalNProject2ᚕᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐProjectᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) _Query_session(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Query", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().Session(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*models.User) + fc.Result = res + return ec.marshalOUser2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐUser(ctx, field.Selections, res) +} + +func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Query", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Query___type_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.introspectType(args["name"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Type) + fc.Result = res + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Query", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.introspectSchema() + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Schema) + fc.Result = res + return ec.marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx, field.Selections, res) +} + +func (ec *executionContext) _User_id(ctx context.Context, field graphql.CollectedField, obj *models.User) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "User", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, 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) _User_name(ctx context.Context, field graphql.CollectedField, obj *models.User) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "User", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) _User_active(ctx context.Context, field graphql.CollectedField, obj *models.User) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "User", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Active, 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.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) _User_admin(ctx context.Context, field graphql.CollectedField, obj *models.User) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "User", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Admin, 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.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Directive_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Directive", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Directive_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Directive", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalOString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Directive_locations(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Directive", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Locations, 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.marshalN__DirectiveLocation2ᚕstringᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Directive_args(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Directive", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Args, 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.([]introspection.InputValue) + fc.Result = res + return ec.marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) ___EnumValue_name(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__EnumValue", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) ___EnumValue_description(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__EnumValue", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalOString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) ___EnumValue_isDeprecated(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__EnumValue", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsDeprecated(), 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.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) ___EnumValue_deprecationReason(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__EnumValue", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.DeprecationReason(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Field_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Field", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Field_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Field", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalOString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Field_args(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Field", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Args, 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.([]introspection.InputValue) + fc.Result = res + return ec.marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Field_type(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Field", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Type, 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.(*introspection.Type) + fc.Result = res + return ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Field_isDeprecated(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Field", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsDeprecated(), 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.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Field_deprecationReason(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Field", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.DeprecationReason(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) ___InputValue_name(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__InputValue", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) ___InputValue_description(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__InputValue", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalOString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) ___InputValue_type(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__InputValue", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Type, 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.(*introspection.Type) + fc.Result = res + return ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) ___InputValue_defaultValue(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__InputValue", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.DefaultValue, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Schema_types(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Schema", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Types(), 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.([]introspection.Type) + fc.Result = res + return ec.marshalN__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Schema_queryType(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Schema", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.QueryType(), 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.(*introspection.Type) + fc.Result = res + return ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Schema_mutationType(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Schema", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.MutationType(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Type) + fc.Result = res + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Schema_subscriptionType(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Schema", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.SubscriptionType(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Type) + fc.Result = res + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Schema_directives(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Schema", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Directives(), 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.([]introspection.Directive) + fc.Result = res + return ec.marshalN__Directive2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirectiveᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Type_kind(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Type", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Kind(), 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.marshalN__TypeKind2string(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Type_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Type", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Type_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Type", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalOString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Type_fields(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Type", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field___Type_fields_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Fields(args["includeDeprecated"].(bool)), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]introspection.Field) + fc.Result = res + return ec.marshalO__Field2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐFieldᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Type_interfaces(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Type", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Interfaces(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]introspection.Type) + fc.Result = res + return ec.marshalO__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Type_possibleTypes(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Type", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.PossibleTypes(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]introspection.Type) + fc.Result = res + return ec.marshalO__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Type_enumValues(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Type", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field___Type_enumValues_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.EnumValues(args["includeDeprecated"].(bool)), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]introspection.EnumValue) + fc.Result = res + return ec.marshalO__EnumValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValueᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Type_inputFields(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Type", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.InputFields(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]introspection.InputValue) + fc.Result = res + return ec.marshalO__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Type_ofType(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "__Type", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.OfType(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Type) + fc.Result = res + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +// endregion **************************** field.gotpl ***************************** + +// region **************************** input.gotpl ***************************** + +func (ec *executionContext) unmarshalInputIssueFilter(ctx context.Context, obj interface{}) (models.IssueFilter, error) { + var it models.IssueFilter + var asMap = obj.(map[string]interface{}) + + for k, v := range asMap { + switch k { + case "issueIds": + var err error + it.IssueIDs, err = ec.unmarshalOString2ᚕstringᚄ(ctx, v) + if err != nil { + return it, err + } + case "projectIds": + var err error + it.ProjectIDs, err = ec.unmarshalOString2ᚕstringᚄ(ctx, v) + if err != nil { + return it, err + } + case "ownerIds": + var err error + it.OwnerIDs, err = ec.unmarshalOString2ᚕstringᚄ(ctx, v) + if err != nil { + return it, err + } + case "assigneeIds": + var err error + it.AssigneeIDs, err = ec.unmarshalOString2ᚕstringᚄ(ctx, v) + if err != nil { + return it, err + } + case "search": + var err error + it.Search, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + case "minStage": + var err error + it.MinStage, err = ec.unmarshalOInt2ᚖint(ctx, v) + if err != nil { + return it, err + } + case "maxStage": + var err error + it.MaxStage, err = ec.unmarshalOInt2ᚖint(ctx, v) + if err != nil { + return it, err + } + case "limit": + var err error + it.Limit, err = ec.unmarshalOInt2ᚖint(ctx, v) + if err != nil { + return it, err + } + } + } + + return it, nil +} + +func (ec *executionContext) unmarshalInputProjectFilter(ctx context.Context, obj interface{}) (models.ProjectFilter, error) { + var it models.ProjectFilter + var asMap = obj.(map[string]interface{}) + + for k, v := range asMap { + switch k { + case "projectIds": + var err error + it.ProjectIDs, err = ec.unmarshalOString2ᚕstringᚄ(ctx, v) + if err != nil { + return it, err + } + case "search": + var err error + it.Search, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + case "permission": + var err error + it.Permission, err = ec.unmarshalOProjectFilterPermission2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐProjectFilterPermission(ctx, v) + if err != nil { + return it, err + } + } + } + + return it, nil +} + +func (ec *executionContext) unmarshalInputProjectFilterPermission(ctx context.Context, obj interface{}) (models.ProjectFilterPermission, error) { + var it models.ProjectFilterPermission + var asMap = obj.(map[string]interface{}) + + for k, v := range asMap { + switch k { + case "userId": + var err error + it.UserID, err = ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + case "minLevel": + var err error + it.MinLevel, err = ec.unmarshalNInt2int(ctx, v) + if err != nil { + return it, err + } + case "maxLevel": + var err error + it.MaxLevel, err = ec.unmarshalNInt2int(ctx, v) + if err != nil { + return it, err + } + } + } + + return it, nil +} + +func (ec *executionContext) unmarshalInputProjectIssueFilter(ctx context.Context, obj interface{}) (ProjectIssueFilter, error) { + var it ProjectIssueFilter + var asMap = obj.(map[string]interface{}) + + for k, v := range asMap { + switch k { + case "assigneeIds": + var err error + it.AssigneeIds, err = ec.unmarshalOString2ᚕstringᚄ(ctx, v) + if err != nil { + return it, err + } + case "search": + var err error + it.Search, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + case "minStage": + var err error + it.MinStage, err = ec.unmarshalOInt2ᚖint(ctx, v) + if err != nil { + return it, err + } + case "maxStage": + var err error + it.MaxStage, err = ec.unmarshalOInt2ᚖint(ctx, v) + if err != nil { + return it, err + } + case "limit": + var err error + it.Limit, err = ec.unmarshalOInt2ᚖint(ctx, v) + if err != nil { + return it, err + } + } + } + + return it, nil +} + +func (ec *executionContext) unmarshalInputUserLoginInput(ctx context.Context, obj interface{}) (UserLoginInput, error) { + var it UserLoginInput + var asMap = obj.(map[string]interface{}) + + for k, v := range asMap { + switch k { + case "userId": + var err error + it.UserID, err = ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + case "password": + var err error + it.Password, err = ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + case "rememberMe": + var err error + it.RememberMe, err = ec.unmarshalNBoolean2bool(ctx, v) + if err != nil { + return it, err + } + } + } + + return it, nil +} + +// endregion **************************** input.gotpl ***************************** + +// region ************************** interface.gotpl *************************** + +// endregion ************************** interface.gotpl *************************** + +// region **************************** object.gotpl **************************** + +var issueImplementors = []string{"Issue"} + +func (ec *executionContext) _Issue(ctx context.Context, sel ast.SelectionSet, obj *models.Issue) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, issueImplementors) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Issue") + case "id": + out.Values[i] = ec._Issue_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + case "projectId": + out.Values[i] = ec._Issue_projectId(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + case "ownerId": + out.Values[i] = ec._Issue_ownerId(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + case "assigneeId": + out.Values[i] = ec._Issue_assigneeId(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + case "statusStage": + out.Values[i] = ec._Issue_statusStage(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + case "statusName": + out.Values[i] = ec._Issue_statusName(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + case "createdTime": + out.Values[i] = ec._Issue_createdTime(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + case "updatedTime": + out.Values[i] = ec._Issue_updatedTime(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + case "dueTime": + out.Values[i] = ec._Issue_dueTime(ctx, field, obj) + case "name": + out.Values[i] = ec._Issue_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + case "title": + out.Values[i] = ec._Issue_title(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + case "description": + out.Values[i] = ec._Issue_description(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + case "project": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Issue_project(ctx, field, obj) + return res + }) + case "owner": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Issue_owner(ctx, field, obj) + return res + }) + case "assignee": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Issue_assignee(ctx, field, obj) + return res + }) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var mutationImplementors = []string{"Mutation"} + +func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, mutationImplementors) + + ctx = graphql.WithFieldContext(ctx, &graphql.FieldContext{ + Object: "Mutation", + }) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Mutation") + case "loginUser": + out.Values[i] = ec._Mutation_loginUser(ctx, field) + if out.Values[i] == graphql.Null { + invalids++ + } + case "logoutUser": + out.Values[i] = ec._Mutation_logoutUser(ctx, field) + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var projectImplementors = []string{"Project"} + +func (ec *executionContext) _Project(ctx context.Context, sel ast.SelectionSet, obj *models.Project) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, projectImplementors) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Project") + case "id": + out.Values[i] = ec._Project_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + case "name": + out.Values[i] = ec._Project_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + case "description": + out.Values[i] = ec._Project_description(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + case "dailyPoints": + out.Values[i] = ec._Project_dailyPoints(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + case "issues": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Project_issues(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) + case "permissions": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Project_permissions(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) + case "userPermissions": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Project_userPermissions(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var projectPermissionsImplementors = []string{"ProjectPermissions"} + +func (ec *executionContext) _ProjectPermissions(ctx context.Context, sel ast.SelectionSet, obj *ProjectPermissions) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, projectPermissionsImplementors) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ProjectPermissions") + case "userId": + out.Values[i] = ec._ProjectPermissions_userId(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "accessLevel": + out.Values[i] = ec._ProjectPermissions_accessLevel(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "user": + out.Values[i] = ec._ProjectPermissions_user(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var projectStatusImplementors = []string{"ProjectStatus"} + +func (ec *executionContext) _ProjectStatus(ctx context.Context, sel ast.SelectionSet, obj *models.ProjectStatus) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, projectStatusImplementors) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ProjectStatus") + case "stage": + out.Values[i] = ec._ProjectStatus_stage(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "name": + out.Values[i] = ec._ProjectStatus_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "description": + out.Values[i] = ec._ProjectStatus_description(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var queryImplementors = []string{"Query"} + +func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, queryImplementors) + + ctx = graphql.WithFieldContext(ctx, &graphql.FieldContext{ + Object: "Query", + }) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Query") + case "issue": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_issue(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) + case "issues": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_issues(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) + case "project": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_project(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) + case "projects": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_projects(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) + case "session": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_session(ctx, field) + return res + }) + case "__type": + out.Values[i] = ec._Query___type(ctx, field) + case "__schema": + out.Values[i] = ec._Query___schema(ctx, field) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var userImplementors = []string{"User"} + +func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj *models.User) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, userImplementors) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("User") + case "id": + out.Values[i] = ec._User_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "name": + out.Values[i] = ec._User_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "active": + out.Values[i] = ec._User_active(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "admin": + out.Values[i] = ec._User_admin(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var __DirectiveImplementors = []string{"__Directive"} + +func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionSet, obj *introspection.Directive) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __DirectiveImplementors) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__Directive") + case "name": + out.Values[i] = ec.___Directive_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "description": + out.Values[i] = ec.___Directive_description(ctx, field, obj) + case "locations": + out.Values[i] = ec.___Directive_locations(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "args": + out.Values[i] = ec.___Directive_args(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var __EnumValueImplementors = []string{"__EnumValue"} + +func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionSet, obj *introspection.EnumValue) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __EnumValueImplementors) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__EnumValue") + case "name": + out.Values[i] = ec.___EnumValue_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "description": + out.Values[i] = ec.___EnumValue_description(ctx, field, obj) + case "isDeprecated": + out.Values[i] = ec.___EnumValue_isDeprecated(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "deprecationReason": + out.Values[i] = ec.___EnumValue_deprecationReason(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var __FieldImplementors = []string{"__Field"} + +func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, obj *introspection.Field) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __FieldImplementors) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__Field") + case "name": + out.Values[i] = ec.___Field_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "description": + out.Values[i] = ec.___Field_description(ctx, field, obj) + case "args": + out.Values[i] = ec.___Field_args(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "type": + out.Values[i] = ec.___Field_type(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "isDeprecated": + out.Values[i] = ec.___Field_isDeprecated(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "deprecationReason": + out.Values[i] = ec.___Field_deprecationReason(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var __InputValueImplementors = []string{"__InputValue"} + +func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.SelectionSet, obj *introspection.InputValue) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __InputValueImplementors) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__InputValue") + case "name": + out.Values[i] = ec.___InputValue_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "description": + out.Values[i] = ec.___InputValue_description(ctx, field, obj) + case "type": + out.Values[i] = ec.___InputValue_type(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "defaultValue": + out.Values[i] = ec.___InputValue_defaultValue(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var __SchemaImplementors = []string{"__Schema"} + +func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, obj *introspection.Schema) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __SchemaImplementors) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__Schema") + case "types": + out.Values[i] = ec.___Schema_types(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "queryType": + out.Values[i] = ec.___Schema_queryType(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "mutationType": + out.Values[i] = ec.___Schema_mutationType(ctx, field, obj) + case "subscriptionType": + out.Values[i] = ec.___Schema_subscriptionType(ctx, field, obj) + case "directives": + out.Values[i] = ec.___Schema_directives(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var __TypeImplementors = []string{"__Type"} + +func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, obj *introspection.Type) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __TypeImplementors) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__Type") + case "kind": + out.Values[i] = ec.___Type_kind(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "name": + out.Values[i] = ec.___Type_name(ctx, field, obj) + case "description": + out.Values[i] = ec.___Type_description(ctx, field, obj) + case "fields": + out.Values[i] = ec.___Type_fields(ctx, field, obj) + case "interfaces": + out.Values[i] = ec.___Type_interfaces(ctx, field, obj) + case "possibleTypes": + out.Values[i] = ec.___Type_possibleTypes(ctx, field, obj) + case "enumValues": + out.Values[i] = ec.___Type_enumValues(ctx, field, obj) + case "inputFields": + out.Values[i] = ec.___Type_inputFields(ctx, field, obj) + case "ofType": + out.Values[i] = ec.___Type_ofType(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +// endregion **************************** object.gotpl **************************** + +// region ***************************** type.gotpl ***************************** + +func (ec *executionContext) unmarshalNBoolean2bool(ctx context.Context, v interface{}) (bool, error) { + return graphql.UnmarshalBoolean(v) +} + +func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.SelectionSet, v bool) graphql.Marshaler { + res := graphql.MarshalBoolean(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + } + return res +} + +func (ec *executionContext) unmarshalNInt2int(ctx context.Context, v interface{}) (int, error) { + return graphql.UnmarshalInt(v) +} + +func (ec *executionContext) marshalNInt2int(ctx context.Context, sel ast.SelectionSet, v int) graphql.Marshaler { + res := graphql.MarshalInt(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + } + return res +} + +func (ec *executionContext) marshalNIssue2gitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐIssue(ctx context.Context, sel ast.SelectionSet, v models.Issue) graphql.Marshaler { + return ec._Issue(ctx, sel, &v) +} + +func (ec *executionContext) marshalNIssue2ᚕᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐIssueᚄ(ctx context.Context, sel ast.SelectionSet, v []*models.Issue) 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.marshalNIssue2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐIssue(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + +func (ec *executionContext) marshalNIssue2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐIssue(ctx context.Context, sel ast.SelectionSet, v *models.Issue) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._Issue(ctx, sel, v) +} + +func (ec *executionContext) marshalNProject2gitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐProject(ctx context.Context, sel ast.SelectionSet, v models.Project) graphql.Marshaler { + return ec._Project(ctx, sel, &v) +} + +func (ec *executionContext) marshalNProject2ᚕᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐProjectᚄ(ctx context.Context, sel ast.SelectionSet, v []*models.Project) 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.marshalNProject2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐProject(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + +func (ec *executionContext) marshalNProject2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐProject(ctx context.Context, sel ast.SelectionSet, v *models.Project) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._Project(ctx, sel, v) +} + +func (ec *executionContext) marshalNProjectPermissions2gitᚗaiterpᚗnetᚋxiaoliᚋserverᚋgraphᚋgraphcoreᚐProjectPermissions(ctx context.Context, sel ast.SelectionSet, v ProjectPermissions) graphql.Marshaler { + return ec._ProjectPermissions(ctx, sel, &v) +} + +func (ec *executionContext) marshalNProjectPermissions2ᚕᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋgraphᚋgraphcoreᚐProjectPermissionsᚄ(ctx context.Context, sel ast.SelectionSet, v []*ProjectPermissions) 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.marshalNProjectPermissions2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋgraphᚋgraphcoreᚐProjectPermissions(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + +func (ec *executionContext) marshalNProjectPermissions2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋgraphᚋgraphcoreᚐProjectPermissions(ctx context.Context, sel ast.SelectionSet, v *ProjectPermissions) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._ProjectPermissions(ctx, sel, v) +} + +func (ec *executionContext) unmarshalNString2string(ctx context.Context, v interface{}) (string, error) { + return graphql.UnmarshalString(v) +} + +func (ec *executionContext) marshalNString2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + res := graphql.MarshalString(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + } + return res +} + +func (ec *executionContext) unmarshalNTime2timeᚐTime(ctx context.Context, v interface{}) (time.Time, error) { + return graphql.UnmarshalTime(v) +} + +func (ec *executionContext) marshalNTime2timeᚐTime(ctx context.Context, sel ast.SelectionSet, v time.Time) graphql.Marshaler { + res := graphql.MarshalTime(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + } + return res +} + +func (ec *executionContext) marshalNUser2gitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐUser(ctx context.Context, sel ast.SelectionSet, v models.User) graphql.Marshaler { + return ec._User(ctx, sel, &v) +} + +func (ec *executionContext) marshalNUser2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐUser(ctx context.Context, sel ast.SelectionSet, v *models.User) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._User(ctx, sel, v) +} + +func (ec *executionContext) marshalN__Directive2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirective(ctx context.Context, sel ast.SelectionSet, v introspection.Directive) graphql.Marshaler { + return ec.___Directive(ctx, sel, &v) +} + +func (ec *executionContext) marshalN__Directive2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirectiveᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.Directive) 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.marshalN__Directive2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirective(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + +func (ec *executionContext) unmarshalN__DirectiveLocation2string(ctx context.Context, v interface{}) (string, error) { + return graphql.UnmarshalString(v) +} + +func (ec *executionContext) marshalN__DirectiveLocation2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + res := graphql.MarshalString(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + } + return res +} + +func (ec *executionContext) unmarshalN__DirectiveLocation2ᚕstringᚄ(ctx context.Context, v interface{}) ([]string, error) { + var vSlice []interface{} + if v != nil { + if tmp1, ok := v.([]interface{}); ok { + vSlice = tmp1 + } else { + vSlice = []interface{}{v} + } + } + var err error + res := make([]string, len(vSlice)) + for i := range vSlice { + res[i], err = ec.unmarshalN__DirectiveLocation2string(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) marshalN__DirectiveLocation2ᚕstringᚄ(ctx context.Context, sel ast.SelectionSet, v []string) 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.marshalN__DirectiveLocation2string(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + +func (ec *executionContext) marshalN__EnumValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValue(ctx context.Context, sel ast.SelectionSet, v introspection.EnumValue) graphql.Marshaler { + return ec.___EnumValue(ctx, sel, &v) +} + +func (ec *executionContext) marshalN__Field2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐField(ctx context.Context, sel ast.SelectionSet, v introspection.Field) graphql.Marshaler { + return ec.___Field(ctx, sel, &v) +} + +func (ec *executionContext) marshalN__InputValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValue(ctx context.Context, sel ast.SelectionSet, v introspection.InputValue) graphql.Marshaler { + return ec.___InputValue(ctx, sel, &v) +} + +func (ec *executionContext) marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.InputValue) 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.marshalN__InputValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValue(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + +func (ec *executionContext) marshalN__Type2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx context.Context, sel ast.SelectionSet, v introspection.Type) graphql.Marshaler { + return ec.___Type(ctx, sel, &v) +} + +func (ec *executionContext) marshalN__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.Type) 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.marshalN__Type2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + +func (ec *executionContext) marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx context.Context, sel ast.SelectionSet, v *introspection.Type) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec.___Type(ctx, sel, v) +} + +func (ec *executionContext) unmarshalN__TypeKind2string(ctx context.Context, v interface{}) (string, error) { + return graphql.UnmarshalString(v) +} + +func (ec *executionContext) marshalN__TypeKind2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + res := graphql.MarshalString(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + } + return res +} + +func (ec *executionContext) unmarshalOBoolean2bool(ctx context.Context, v interface{}) (bool, error) { + return graphql.UnmarshalBoolean(v) +} + +func (ec *executionContext) marshalOBoolean2bool(ctx context.Context, sel ast.SelectionSet, v bool) graphql.Marshaler { + return graphql.MarshalBoolean(v) +} + +func (ec *executionContext) unmarshalOBoolean2ᚖbool(ctx context.Context, v interface{}) (*bool, error) { + if v == nil { + return nil, nil + } + res, err := ec.unmarshalOBoolean2bool(ctx, v) + return &res, err +} + +func (ec *executionContext) marshalOBoolean2ᚖbool(ctx context.Context, sel ast.SelectionSet, v *bool) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec.marshalOBoolean2bool(ctx, sel, *v) +} + +func (ec *executionContext) unmarshalOInt2int(ctx context.Context, v interface{}) (int, error) { + return graphql.UnmarshalInt(v) +} + +func (ec *executionContext) marshalOInt2int(ctx context.Context, sel ast.SelectionSet, v int) graphql.Marshaler { + return graphql.MarshalInt(v) +} + +func (ec *executionContext) unmarshalOInt2ᚖint(ctx context.Context, v interface{}) (*int, error) { + if v == nil { + return nil, nil + } + res, err := ec.unmarshalOInt2int(ctx, v) + return &res, err +} + +func (ec *executionContext) marshalOInt2ᚖint(ctx context.Context, sel ast.SelectionSet, v *int) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec.marshalOInt2int(ctx, sel, *v) +} + +func (ec *executionContext) unmarshalOIssueFilter2gitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐIssueFilter(ctx context.Context, v interface{}) (models.IssueFilter, error) { + return ec.unmarshalInputIssueFilter(ctx, v) +} + +func (ec *executionContext) unmarshalOIssueFilter2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐIssueFilter(ctx context.Context, v interface{}) (*models.IssueFilter, error) { + if v == nil { + return nil, nil + } + res, err := ec.unmarshalOIssueFilter2gitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐIssueFilter(ctx, v) + return &res, err +} + +func (ec *executionContext) marshalOProject2gitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐProject(ctx context.Context, sel ast.SelectionSet, v models.Project) graphql.Marshaler { + return ec._Project(ctx, sel, &v) +} + +func (ec *executionContext) marshalOProject2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐProject(ctx context.Context, sel ast.SelectionSet, v *models.Project) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._Project(ctx, sel, v) +} + +func (ec *executionContext) unmarshalOProjectFilter2gitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐProjectFilter(ctx context.Context, v interface{}) (models.ProjectFilter, error) { + return ec.unmarshalInputProjectFilter(ctx, v) +} + +func (ec *executionContext) unmarshalOProjectFilter2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐProjectFilter(ctx context.Context, v interface{}) (*models.ProjectFilter, error) { + if v == nil { + return nil, nil + } + res, err := ec.unmarshalOProjectFilter2gitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐProjectFilter(ctx, v) + return &res, err +} + +func (ec *executionContext) unmarshalOProjectFilterPermission2gitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐProjectFilterPermission(ctx context.Context, v interface{}) (models.ProjectFilterPermission, error) { + return ec.unmarshalInputProjectFilterPermission(ctx, v) +} + +func (ec *executionContext) unmarshalOProjectFilterPermission2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐProjectFilterPermission(ctx context.Context, v interface{}) (*models.ProjectFilterPermission, error) { + if v == nil { + return nil, nil + } + res, err := ec.unmarshalOProjectFilterPermission2gitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐProjectFilterPermission(ctx, v) + return &res, err +} + +func (ec *executionContext) unmarshalOProjectIssueFilter2gitᚗaiterpᚗnetᚋxiaoliᚋserverᚋgraphᚋgraphcoreᚐProjectIssueFilter(ctx context.Context, v interface{}) (ProjectIssueFilter, error) { + return ec.unmarshalInputProjectIssueFilter(ctx, v) +} + +func (ec *executionContext) unmarshalOProjectIssueFilter2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋgraphᚋgraphcoreᚐProjectIssueFilter(ctx context.Context, v interface{}) (*ProjectIssueFilter, error) { + if v == nil { + return nil, nil + } + res, err := ec.unmarshalOProjectIssueFilter2gitᚗaiterpᚗnetᚋxiaoliᚋserverᚋgraphᚋgraphcoreᚐProjectIssueFilter(ctx, v) + return &res, err +} + +func (ec *executionContext) unmarshalOString2string(ctx context.Context, v interface{}) (string, error) { + return graphql.UnmarshalString(v) +} + +func (ec *executionContext) marshalOString2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + return graphql.MarshalString(v) +} + +func (ec *executionContext) unmarshalOString2ᚕstringᚄ(ctx context.Context, v interface{}) ([]string, error) { + var vSlice []interface{} + if v != nil { + if tmp1, ok := v.([]interface{}); ok { + vSlice = tmp1 + } else { + vSlice = []interface{}{v} + } + } + var err error + res := make([]string, len(vSlice)) + for i := range vSlice { + res[i], err = ec.unmarshalNString2string(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) marshalOString2ᚕstringᚄ(ctx context.Context, sel ast.SelectionSet, v []string) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := make(graphql.Array, len(v)) + for i := range v { + ret[i] = ec.marshalNString2string(ctx, sel, v[i]) + } + + return ret +} + +func (ec *executionContext) unmarshalOString2ᚖstring(ctx context.Context, v interface{}) (*string, error) { + if v == nil { + return nil, nil + } + res, err := ec.unmarshalOString2string(ctx, v) + return &res, err +} + +func (ec *executionContext) marshalOString2ᚖstring(ctx context.Context, sel ast.SelectionSet, v *string) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec.marshalOString2string(ctx, sel, *v) +} + +func (ec *executionContext) unmarshalOTime2timeᚐTime(ctx context.Context, v interface{}) (time.Time, error) { + return graphql.UnmarshalTime(v) +} + +func (ec *executionContext) marshalOTime2timeᚐTime(ctx context.Context, sel ast.SelectionSet, v time.Time) graphql.Marshaler { + return graphql.MarshalTime(v) +} + +func (ec *executionContext) marshalOUser2gitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐUser(ctx context.Context, sel ast.SelectionSet, v models.User) graphql.Marshaler { + return ec._User(ctx, sel, &v) +} + +func (ec *executionContext) marshalOUser2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋmodelsᚐUser(ctx context.Context, sel ast.SelectionSet, v *models.User) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._User(ctx, sel, v) +} + +func (ec *executionContext) unmarshalOUserLoginInput2gitᚗaiterpᚗnetᚋxiaoliᚋserverᚋgraphᚋgraphcoreᚐUserLoginInput(ctx context.Context, v interface{}) (UserLoginInput, error) { + return ec.unmarshalInputUserLoginInput(ctx, v) +} + +func (ec *executionContext) unmarshalOUserLoginInput2ᚖgitᚗaiterpᚗnetᚋxiaoliᚋserverᚋgraphᚋgraphcoreᚐUserLoginInput(ctx context.Context, v interface{}) (*UserLoginInput, error) { + if v == nil { + return nil, nil + } + res, err := ec.unmarshalOUserLoginInput2gitᚗaiterpᚗnetᚋxiaoliᚋserverᚋgraphᚋgraphcoreᚐUserLoginInput(ctx, v) + return &res, err +} + +func (ec *executionContext) marshalO__EnumValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValueᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.EnumValue) 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.marshalN__EnumValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValue(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + +func (ec *executionContext) marshalO__Field2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐFieldᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.Field) 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.marshalN__Field2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐField(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + +func (ec *executionContext) marshalO__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.InputValue) 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.marshalN__InputValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValue(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + +func (ec *executionContext) marshalO__Schema2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx context.Context, sel ast.SelectionSet, v introspection.Schema) graphql.Marshaler { + return ec.___Schema(ctx, sel, &v) +} + +func (ec *executionContext) marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx context.Context, sel ast.SelectionSet, v *introspection.Schema) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec.___Schema(ctx, sel, v) +} + +func (ec *executionContext) marshalO__Type2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx context.Context, sel ast.SelectionSet, v introspection.Type) graphql.Marshaler { + return ec.___Type(ctx, sel, &v) +} + +func (ec *executionContext) marshalO__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.Type) 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.marshalN__Type2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + +func (ec *executionContext) marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx context.Context, sel ast.SelectionSet, v *introspection.Type) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec.___Type(ctx, sel, v) +} + +// endregion ***************************** type.gotpl ***************************** diff --git a/graph/graphcore/input_gen.go b/graph/graphcore/input_gen.go new file mode 100644 index 0000000..616ad50 --- /dev/null +++ b/graph/graphcore/input_gen.go @@ -0,0 +1,38 @@ +// Code generated by github.com/99designs/gqlgen, DO NOT EDIT. + +package graphcore + +import ( + "git.aiterp.net/stufflog/server/models" +) + +// A limited filter for constraining the list of issues within a project. +type ProjectIssueFilter struct { + // Filter by assignee IDs + AssigneeIds []string `json:"assigneeIds"` + // Text search + Search *string `json:"search"` + // Earliest stage (inclusive) + MinStage *int `json:"minStage"` + // Latest stage (inclusive) + MaxStage *int `json:"maxStage"` + // Limit the result set + Limit *int `json:"limit"` +} + +// The permissions of a user within the project. +type ProjectPermissions struct { + // User ID. + UserID string `json:"userId"` + // Access level. + AccessLevel int `json:"accessLevel"` + // The user whose permissions it is. Can be null if the user no longer exists. + User *models.User `json:"user"` +} + +// Input for loginUser. +type UserLoginInput struct { + UserID string `json:"userId"` + Password string `json:"password"` + RememberMe bool `json:"rememberMe"` +} diff --git a/graph/resolvers/issue.resolvers.go b/graph/resolvers/issue.resolvers.go new file mode 100644 index 0000000..bfd433b --- /dev/null +++ b/graph/resolvers/issue.resolvers.go @@ -0,0 +1,29 @@ +package resolvers + +// This file will be automatically regenerated based on the schema, any resolver implementations +// will be copied through when generating and any unknown code will be moved to the end. + +import ( + "context" + "fmt" + + "git.aiterp.net/stufflog/server/graph/graphcore" + "git.aiterp.net/stufflog/server/models" +) + +func (r *issueResolver) Project(ctx context.Context, obj *models.Issue) (*models.Project, error) { + panic(fmt.Errorf("not implemented")) +} + +func (r *issueResolver) Owner(ctx context.Context, obj *models.Issue) (*models.User, error) { + panic(fmt.Errorf("not implemented")) +} + +func (r *issueResolver) Assignee(ctx context.Context, obj *models.Issue) (*models.User, error) { + panic(fmt.Errorf("not implemented")) +} + +// Issue returns graphcore.IssueResolver implementation. +func (r *Resolver) Issue() graphcore.IssueResolver { return &issueResolver{r} } + +type issueResolver struct{ *Resolver } diff --git a/graph/resolvers/mutation.resolvers.go b/graph/resolvers/mutation.resolvers.go new file mode 100644 index 0000000..57fd69d --- /dev/null +++ b/graph/resolvers/mutation.resolvers.go @@ -0,0 +1,25 @@ +package resolvers + +// This file will be automatically regenerated based on the schema, any resolver implementations +// will be copied through when generating and any unknown code will be moved to the end. + +import ( + "context" + "fmt" + + "git.aiterp.net/stufflog/server/graph/graphcore" + "git.aiterp.net/stufflog/server/models" +) + +func (r *mutationResolver) LoginUser(ctx context.Context, input *graphcore.UserLoginInput) (*models.User, error) { + panic(fmt.Errorf("not implemented")) +} + +func (r *mutationResolver) LogoutUser(ctx context.Context) (*models.User, error) { + panic(fmt.Errorf("not implemented")) +} + +// Mutation returns graphcore.MutationResolver implementation. +func (r *Resolver) Mutation() graphcore.MutationResolver { return &mutationResolver{r} } + +type mutationResolver struct{ *Resolver } diff --git a/graph/resolvers/project.resolvers.go b/graph/resolvers/project.resolvers.go new file mode 100644 index 0000000..40a3a30 --- /dev/null +++ b/graph/resolvers/project.resolvers.go @@ -0,0 +1,29 @@ +package resolvers + +// This file will be automatically regenerated based on the schema, any resolver implementations +// will be copied through when generating and any unknown code will be moved to the end. + +import ( + "context" + "fmt" + + "git.aiterp.net/stufflog/server/graph/graphcore" + "git.aiterp.net/stufflog/server/models" +) + +func (r *projectResolver) Issues(ctx context.Context, obj *models.Project, filter *graphcore.ProjectIssueFilter) ([]*models.Issue, error) { + panic(fmt.Errorf("not implemented")) +} + +func (r *projectResolver) Permissions(ctx context.Context, obj *models.Project) ([]*graphcore.ProjectPermissions, error) { + panic(fmt.Errorf("not implemented")) +} + +func (r *projectResolver) UserPermissions(ctx context.Context, obj *models.Project) (*graphcore.ProjectPermissions, error) { + panic(fmt.Errorf("not implemented")) +} + +// Project returns graphcore.ProjectResolver implementation. +func (r *Resolver) Project() graphcore.ProjectResolver { return &projectResolver{r} } + +type projectResolver struct{ *Resolver } diff --git a/graph/resolvers/query.resolvers.go b/graph/resolvers/query.resolvers.go new file mode 100644 index 0000000..b073878 --- /dev/null +++ b/graph/resolvers/query.resolvers.go @@ -0,0 +1,37 @@ +package resolvers + +// This file will be automatically regenerated based on the schema, any resolver implementations +// will be copied through when generating and any unknown code will be moved to the end. + +import ( + "context" + "fmt" + + "git.aiterp.net/stufflog/server/graph/graphcore" + "git.aiterp.net/stufflog/server/models" +) + +func (r *queryResolver) Issue(ctx context.Context, id string) (*models.Issue, error) { + panic(fmt.Errorf("not implemented")) +} + +func (r *queryResolver) Issues(ctx context.Context, filter *models.IssueFilter) ([]*models.Issue, error) { + panic(fmt.Errorf("not implemented")) +} + +func (r *queryResolver) Project(ctx context.Context, id string) (*models.Project, error) { + panic(fmt.Errorf("not implemented")) +} + +func (r *queryResolver) Projects(ctx context.Context, filter *models.ProjectFilter) ([]*models.Project, error) { + panic(fmt.Errorf("not implemented")) +} + +func (r *queryResolver) Session(ctx context.Context) (*models.User, error) { + panic(fmt.Errorf("not implemented")) +} + +// Query returns graphcore.QueryResolver implementation. +func (r *Resolver) Query() graphcore.QueryResolver { return &queryResolver{r} } + +type queryResolver struct{ *Resolver } diff --git a/graph/resolvers/resolver.go b/graph/resolvers/resolver.go new file mode 100644 index 0000000..1e87dbb --- /dev/null +++ b/graph/resolvers/resolver.go @@ -0,0 +1,11 @@ +package resolvers + +import "git.aiterp.net/stufflog/server/services" + +// This file will not be regenerated automatically. +// +// It serves as dependency injection for your app, add any dependencies you require here. + +type Resolver struct { + S services.Bundle +} diff --git a/graph/schema/issue.gql b/graph/schema/issue.gql new file mode 100644 index 0000000..e035064 --- /dev/null +++ b/graph/schema/issue.gql @@ -0,0 +1,37 @@ +type Issue { + id: String! + projectId: String! + ownerId: String! + assigneeId: String! + statusStage: Int! + statusName: String! + createdTime: Time! + updatedTime: Time! + dueTime: Time + name: String! + title: String! + description: String! + + project: Project + owner: User + assignee: User +} + +input IssueFilter { + "Filter by issue IDs (mostly used internally by data loaders)" + issueIds: [String!] + "Filter by project IDs" + projectIds: [String!] + "Filter by owner IDs" + ownerIds: [String!] + "Filter by assignee IDs" + assigneeIds: [String!] + "Text search" + search: String + "Earliest stage (inclusive)" + minStage: Int + "Latest stage (inclusive)" + maxStage: Int + "Limit the result set" + limit: Int +} \ No newline at end of file diff --git a/graph/schema/mutation.gql b/graph/schema/mutation.gql new file mode 100644 index 0000000..f79733e --- /dev/null +++ b/graph/schema/mutation.gql @@ -0,0 +1,4 @@ +type Mutation { + loginUser(input: UserLoginInput): User! + logoutUser: User! +} \ No newline at end of file diff --git a/graph/schema/project.gql b/graph/schema/project.gql new file mode 100644 index 0000000..db0169e --- /dev/null +++ b/graph/schema/project.gql @@ -0,0 +1,70 @@ +type Project { + "The ID of the project, which is also the prefix for all its issues." + id: String! + "The name of the project, used in place of the ID in the UI." + name: String! + "Description of the project." + description: String! + "The amount of points given as a bonus towards goals for any activity on a given day." + dailyPoints: Int! + + "Get issues within the project." + issues(filter: ProjectIssueFilter): [Issue!]! + "All users' permissions. Only available to administrators and up." + permissions: [ProjectPermissions!]! + "Own permissions to the project. Available to any logged in user." + userPermissions: ProjectPermissions! +} + +"The permissions of a user within the project." +type ProjectPermissions { + "User ID." + userId: String! + "Access level." + accessLevel: Int! + + "The user whose permissions it is. Can be null if the user no longer exists." + user: User +} + +type ProjectStatus { + "The stage of the status. 0=inactive, 1=pending, 2=active, 3=review, 4=completed, 5=failed, 6=postponed" + stage: Int! + "The name of the status." + name: String! + "A description of the status and where it's used." + description: String! +} + +"Filter for projects query" +input ProjectFilter { + "Project IDs" + projectIds: [String!] + "Text search" + search: String + "User permission" + permission: ProjectFilterPermission +} + +input ProjectFilterPermission { + "User ID" + userId: String! + "Lowest access level to filter by (inclusive)" + minLevel: Int! + "Highest access level to filter by (inclusive)" + maxLevel: Int! +} + +"A limited filter for constraining the list of issues within a project." +input ProjectIssueFilter { + "Filter by assignee IDs" + assigneeIds: [String!] + "Text search" + search: String + "Earliest stage (inclusive)" + minStage: Int + "Latest stage (inclusive)" + maxStage: Int + "Limit the result set" + limit: Int +} \ No newline at end of file diff --git a/graph/schema/query.gql b/graph/schema/query.gql new file mode 100644 index 0000000..43dfbe1 --- /dev/null +++ b/graph/schema/query.gql @@ -0,0 +1,14 @@ +type Query { + "Find issue" + issue(id: String!): Issue! + "List issues" + issues(filter: IssueFilter): [Issue!]! + + "Find project" + project(id: String!): Project! + "List projects" + projects(filter: ProjectFilter): [Project!]! + + "Session checks the user session." + session: User +} \ No newline at end of file diff --git a/graph/schema/scalars.gql b/graph/schema/scalars.gql new file mode 100644 index 0000000..53becdd --- /dev/null +++ b/graph/schema/scalars.gql @@ -0,0 +1 @@ +scalar Time \ No newline at end of file diff --git a/graph/schema/user.gql b/graph/schema/user.gql new file mode 100644 index 0000000..2bb73de --- /dev/null +++ b/graph/schema/user.gql @@ -0,0 +1,18 @@ +"User information." +type User { + "User ID, used when logging in." + id: String! + "User's display name, which can differ from the user ID." + name: String! + "Whether the user is active." + active: Boolean! + "Whether the user has general administrator privileges. This does not grant admin rights on any project." + admin: Boolean! +} + +"Input for loginUser." +input UserLoginInput { + userId: String! + password: String! + rememberMe: Boolean! +} diff --git a/internal/generate/generate.go b/internal/generate/generate.go new file mode 100644 index 0000000..01fc460 --- /dev/null +++ b/internal/generate/generate.go @@ -0,0 +1,48 @@ +package generate + +import ( + "crypto/rand" + "encoding/binary" + mathRand "math/rand" + "strconv" +) + +const alphabet = "0123456789abcdefghijklmnopqrstuvwxyz" + +func generateWeak(length int, prefix string) string { + result := prefix + for len(result) < length { + result += string(alphabet[mathRand.Intn(len(alphabet))]) + } + + return result +} + +// Generate generates an ID either strongly or weakly depending on the system. +func Generate(length int, prefix string) string { + var buffer [32]byte + + result := prefix + offset := 0 + + _, err := rand.Read(buffer[:32]) + if err != nil { + return generateWeak(length, prefix) + } + + for len(result) < length { + result += strconv.FormatUint(binary.LittleEndian.Uint64(buffer[offset:]), 36) + offset += 8 + + if offset >= 32 { + _, err = rand.Read(buffer[:32]) + if err != nil { + return generateWeak(length, prefix) + } + + offset = 0 + } + } + + return result[:length] +} diff --git a/internal/generate/ids.go b/internal/generate/ids.go new file mode 100644 index 0000000..bcc2e37 --- /dev/null +++ b/internal/generate/ids.go @@ -0,0 +1,9 @@ +package generate + +func ItemID() string { + return Generate(32, "I") +} + +func SessionID() string { + return Generate(32, "S") +} diff --git a/internal/xlerrors/notfound.go b/internal/xlerrors/notfound.go new file mode 100644 index 0000000..68bc3ff --- /dev/null +++ b/internal/xlerrors/notfound.go @@ -0,0 +1,18 @@ +package xlerrors + +type notFoundError struct { + Subject string +} + +func (err *notFoundError) Error() string { + return err.Subject + " not found" +} + +func NotFound(subject string) error { + return ¬FoundError{Subject: subject} +} + +func IsNotFound(err error) bool { + _, ok := err.(*notFoundError) + return ok +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..af7a85e --- /dev/null +++ b/main.go @@ -0,0 +1,75 @@ +package main + +import ( + "git.aiterp.net/stufflog/server/database" + "github.com/pkg/errors" + "github.com/pressly/goose" + "github.com/urfave/cli/v2" + "log" + "os" + "sort" +) + +func main() { + app := &cli.App{ + Name: "xiaoli", + Usage: "Issue tracker for your home and hobbies", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "db-driver", + Value: "mysql", + Usage: "Database driver", + EnvVars: []string{"DATABASE_DRIVER"}, + }, + &cli.StringFlag{ + Name: "db-connect", + Value: "xiaoli_user:stuff1234@(localhost:3306)/xiaoli", + Usage: "Database connection string or path", + EnvVars: []string{"DATABASE_CONNECT"}, + }, + }, + Commands: []*cli.Command{ + { + Name: "migrate", + Usage: "Migrate the configured database", + Action: func(c *cli.Context) error { + db, err := database.Open(c.String("db-driver"), c.String("db-connect")) + if err != nil { + return errors.Wrap(err, "Failed to connect to database") + } + + err = db.Migrate() + if err == goose.ErrNoNextVersion { + log.Println("No more migrations to run") + } else if err != nil { + return errors.Wrap(err, "Failed to run migration") + } + + log.Println("Migration succeeded") + + return nil + }, + }, + { + Name: "server", + Usage: "Run the server", + Action: func(c *cli.Context) error { + _, err := database.Open(c.String("db-driver"), c.String("db-connect")) + if err != nil { + return errors.Wrap(err, "Failed to connect to database") + } + + return nil + }, + }, + }, + } + + sort.Sort(cli.FlagsByName(app.Flags)) + sort.Sort(cli.CommandsByName(app.Commands)) + + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } +} diff --git a/migrations/mysql/20200405173553_create_table_project.sql b/migrations/mysql/20200405173553_create_table_project.sql new file mode 100644 index 0000000..79c3d2c --- /dev/null +++ b/migrations/mysql/20200405173553_create_table_project.sql @@ -0,0 +1,16 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE project ( + project_id CHAR(16) NOT NULL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + description TEXT NOT NULL, + daily_points INTEGER NOT NULL, + + FULLTEXT(name, description) +); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE project; +-- +goose StatementEnd diff --git a/migrations/mysql/20200406105917_create_table_issue.sql b/migrations/mysql/20200406105917_create_table_issue.sql new file mode 100644 index 0000000..4a32e27 --- /dev/null +++ b/migrations/mysql/20200406105917_create_table_issue.sql @@ -0,0 +1,30 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE issue ( + issue_id CHAR(32) NOT NULL PRIMARY KEY, + project_id CHAR(16) NOT NULL, + owner_id CHAR(32) NOT NULL, + assignee_id CHAR(32) NOT NULL, + status_stage INTEGER NOT NULL, + status_name CHAR(32) NOT NULL, + created_time TIMESTAMP NOT NULL, + updated_time TIMESTAMP NOT NULL, + due_time TIMESTAMP NOT NULL, + name VARCHAR(255) NOT NULL, + title VARCHAR(255) NOT NULL, + description TEXT NOT NULL, + + FULLTEXT(name, title, description), + INDEX(owner_id), + INDEX(assignee_id), + INDEX(project_id, status_stage, status_name), + INDEX(project_id), + INDEX(created_time), + INDEX(updated_time) +); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE issue; +-- +goose StatementEnd diff --git a/migrations/mysql/20200406110301_create_table_item.sql b/migrations/mysql/20200406110301_create_table_item.sql new file mode 100644 index 0000000..b9dc7c6 --- /dev/null +++ b/migrations/mysql/20200406110301_create_table_item.sql @@ -0,0 +1,16 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE item ( + item_id CHAR(32) NOT NULL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + description TEXT NOT NULL, + image_url VARCHAR(255), + + FULLTEXT(name, description) +); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE item; +-- +goose StatementEnd diff --git a/migrations/mysql/20200406110546_create_table_item_tag.sql b/migrations/mysql/20200406110546_create_table_item_tag.sql new file mode 100644 index 0000000..b595853 --- /dev/null +++ b/migrations/mysql/20200406110546_create_table_item_tag.sql @@ -0,0 +1,15 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE item_tag ( + item_id CHAR(32), + tag VARCHAR(255), + + PRIMARY KEY (item_id, tag), + INDEX (tag) +); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE item_tag; +-- +goose StatementEnd diff --git a/migrations/mysql/20200406114528_create_table_user.sql b/migrations/mysql/20200406114528_create_table_user.sql new file mode 100644 index 0000000..c3fd503 --- /dev/null +++ b/migrations/mysql/20200406114528_create_table_user.sql @@ -0,0 +1,15 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE user ( + user_id CHAR(32) NOT NULL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + active BOOL NOT NULL, + admin BOOL NOT NULL, + hash VARCHAR(255) NOT NULL +); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE user; +-- +goose StatementEnd diff --git a/migrations/mysql/20200406160317_create_table_counters.sql b/migrations/mysql/20200406160317_create_table_counters.sql new file mode 100644 index 0000000..e0685ad --- /dev/null +++ b/migrations/mysql/20200406160317_create_table_counters.sql @@ -0,0 +1,15 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE counters ( + kind CHAR(16) NOT NULL, + name CHAR(32) NOT NULL, + value INT NOT NULL, + + PRIMARY KEY (kind, name) +); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE counters; +-- +goose StatementEnd diff --git a/migrations/mysql/20200409113154_create_table_project_permission.sql b/migrations/mysql/20200409113154_create_table_project_permission.sql new file mode 100644 index 0000000..532d6e9 --- /dev/null +++ b/migrations/mysql/20200409113154_create_table_project_permission.sql @@ -0,0 +1,16 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE project_permission ( + project_id CHAR(16) NOT NULL, + user_id CHAR(32) NOT NULL, + access_level INTEGER NOT NULL DEFAULT (0), + + PRIMARY KEY (project_id, user_id), + INDEX (user_id) +); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE project_permission +-- +goose StatementEnd diff --git a/migrations/mysql/20200412174220_create_table_session.sql b/migrations/mysql/20200412174220_create_table_session.sql new file mode 100644 index 0000000..19f44ab --- /dev/null +++ b/migrations/mysql/20200412174220_create_table_session.sql @@ -0,0 +1,15 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE session ( + session_id CHAR(32) NOT NULL PRIMARY KEY, + user_id CHAR(32) NOT NULL, + expiry_time TIMESTAMP NOT NULL, + + INDEX(expiry_time) +); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE session; +-- +goose StatementEnd diff --git a/models/activity.go b/models/activity.go new file mode 100644 index 0000000..3d24021 --- /dev/null +++ b/models/activity.go @@ -0,0 +1,12 @@ +package models + +// Activity is an activity within a project that can be measured and estimated, for example "writing" or "developing". +// The points are for the "gamified" aspect of this. +type Activity struct { + ID string `db:"activity_id"` + ProjectID string `db:"project_id"` + Countable bool `db:"countable"` + UnitName string `db:"unit_name"` + UnitValue float64 `db:"unit_value"` + BaseValue float64 `db:"base_value"` +} diff --git a/models/goal.go b/models/goal.go new file mode 100644 index 0000000..5161178 --- /dev/null +++ b/models/goal.go @@ -0,0 +1,21 @@ +package models + +import "time" + +// An Goal denotes a goal for a project or an activity within a project. +// It can ask for one or more of the time, unit or score. If there is no +// activity ID provided, all activities within the project should contribute. +// If there are no activity, a non-zero unit amount is not allowed. +type Goal struct { + ActivityGoalID string `db:"goal_id"` + ProjectID string `db:"project_id"` + ActivityID string `db:"activity_id"` + UserID string `db:"user_id"` + TimeAmount time.Duration `db:"time_amount"` + AllRequired bool `db:"all_required"` + UnitAmount int `db:"unit_amount"` + ScoreAmount int `db:"score_amount"` + TaskAmount int `db:"task_amount"` + StartTime time.Time `db:"start_time"` + EndTime time.Time `db:"end_time"` +} diff --git a/models/issue.go b/models/issue.go new file mode 100644 index 0000000..9c3c25b --- /dev/null +++ b/models/issue.go @@ -0,0 +1,39 @@ +package models + +import "time" + +type Issue struct { + ID string `db:"issue_id"` + ProjectID string `db:"project_id"` + OwnerID string `db:"owner_id"` + AssigneeID string `db:"assignee_id"` + StatusStage int `db:"status_stage"` + StatusName string `db:"status_name"` + CreatedTime time.Time `db:"created_time"` + UpdatedTime time.Time `db:"updated_time"` + DueTime time.Time `db:"due_time"` + Name string `db:"name"` + Title string `db:"title"` + Description string `db:"description"` +} + +type IssueFilter struct { + IssueIDs []string + ProjectIDs []string + OwnerIDs []string + AssigneeIDs []string + Search *string + MinStage *int + MaxStage *int + Limit *int +} + +const ( + IssueStageInactive = 0 + IssueStagePending = 1 + IssueStageActive = 2 + IssueStageReview = 3 + IssueStageCompleted = 4 + IssueStageFailed = 5 + IssueStagePostponed = 6 +) diff --git a/models/issueitem.go b/models/issueitem.go new file mode 100644 index 0000000..1a2309d --- /dev/null +++ b/models/issueitem.go @@ -0,0 +1,9 @@ +package models + +// An IssueTask is a task within an issue. +type IssueItem struct { + ID string `db:"issue_item_id"` + IssueID string `db:"issue_id"` + ItemID string `db:"item_id"` + Quantity int `db:"quantity"` +} diff --git a/models/issuetask.go b/models/issuetask.go new file mode 100644 index 0000000..baa0d25 --- /dev/null +++ b/models/issuetask.go @@ -0,0 +1,20 @@ +package models + +import "time" + +// An IssueTask is a task within an issue. +type IssueTask struct { + TaskID string `db:"task_id"` + IssueID string `db:"issue_id"` + ActivityID string `db:"activity_id"` + CreatedTime time.Time `db:"created_time"` + UpdatedTime time.Time `db:"updated_time"` + DueTime time.Time `db:"due_time"` + StatusStage int `db:"status_stage"` + StatusName string `db:"status_name"` + Name string `db:"name"` + Description string `db:"description"` + EstimatedTime time.Duration `db:"estimated_time"` + EstimatedUnits int `db:"estimated_units"` + PointsMultiplier float64 `db:"points_multiplier"` +} diff --git a/models/item.go b/models/item.go new file mode 100644 index 0000000..a585f7a --- /dev/null +++ b/models/item.go @@ -0,0 +1,21 @@ +package models + +type Item struct { + ID string `db:"item_id"` + Name string `db:"name"` + Description string `db:"description"` + Tags []string `db:"tags"` + ImageURL *string `db:"image_url"` +} + +type ItemFilter struct { + ItemIDs []string + Tags []string +} + +/* + SELECT i.item_id, i.name FROM item i + LEFT JOIN tag AS t ON t.item_id = i.item_id + WHERE t.tag_name IN ("Groceries") + GROUP by i.item_id; +*/ diff --git a/models/log.go b/models/log.go new file mode 100644 index 0000000..3b12aff --- /dev/null +++ b/models/log.go @@ -0,0 +1,6 @@ +package models + +type Log struct { + ID string `db:"log_id"` + IssueID string `db:"issue_id"` +} diff --git a/models/project.go b/models/project.go new file mode 100644 index 0000000..5c8b0eb --- /dev/null +++ b/models/project.go @@ -0,0 +1,40 @@ +package models + +// A Project is a vague category for issues. It's use depend on the user's judgement, as it could represent +// an entire hobby/job or a single project within one. +type Project struct { + ID string `db:"project_id"` + Name string `db:"name"` + Description string `db:"description"` + DailyPoints int `db:"daily_points"` +} + +type ProjectFilter struct { + ProjectIDs []string + Search *string + Permission *ProjectFilterPermission +} + +type ProjectFilterPermission struct { + UserID string + MinLevel int + MaxLevel int +} + +func (project *Project) ValidKey() bool { + if len(project.ID) < 1 || len(project.ID) > 16 { + return false + } + + for _, r := range project.ID { + if r < 'A' && r > 'Z' { + return false + } + } + + return true +} + +func (pfp *ProjectFilterPermission) Valid() bool { + return pfp.UserID != "" && pfp.MinLevel > 0 +} diff --git a/models/projectpermission.go b/models/projectpermission.go new file mode 100644 index 0000000..d85466c --- /dev/null +++ b/models/projectpermission.go @@ -0,0 +1,36 @@ +package models + +// ProjectPermission is a structure that associates a user with a project. +type ProjectPermission struct { + ProjectID string `db:"project_id"` + UserID string `db:"user_id"` + Level int `db:"access_level"` +} + +const ( + ProjectPermissionLevelNoAccess int = 0 + ProjectPermissionLevelObserver int = 1 + ProjectPermissionLevelMember int = 2 + ProjectPermissionLevelAdmin int = 3 + ProjectPermissionLevelOwner int = 4 +) + +func (permission *ProjectPermission) CanViewAnyIssue() bool { + return permission.Level >= ProjectPermissionLevelObserver +} + +func (permission *ProjectPermission) CanViewOwnIssue() bool { + return permission.Level >= ProjectPermissionLevelObserver +} + +func (permission *ProjectPermission) CanManageOwnIssue() bool { + return permission.Level >= ProjectPermissionLevelMember +} + +func (permission *ProjectPermission) CanManageAnyIssue() bool { + return permission.Level >= ProjectPermissionLevelAdmin +} + +func (permission *ProjectPermission) CanManagePermissions() bool { + return permission.Level >= ProjectPermissionLevelOwner +} diff --git a/models/projectstatus.go b/models/projectstatus.go new file mode 100644 index 0000000..9da6305 --- /dev/null +++ b/models/projectstatus.go @@ -0,0 +1,9 @@ +package models + +// ProjectStatus is an entry denoting a status a project's issues can have. +type ProjectStatus struct { + ProjectID string `db:"project_id"` + Stage int `db:"status_stage"` + Name string `db:"status_name"` + Description string `db:"description"` +} diff --git a/models/session.go b/models/session.go new file mode 100644 index 0000000..b2f0941 --- /dev/null +++ b/models/session.go @@ -0,0 +1,9 @@ +package models + +import "time" + +type Session struct { + ID string `db:"session_id"` + UserID string `db:"user_id"` + ExpiryTime time.Time `db:"expiry_time"` +} diff --git a/models/user.go b/models/user.go new file mode 100644 index 0000000..e1a6d1b --- /dev/null +++ b/models/user.go @@ -0,0 +1,39 @@ +package models + +import ( + "errors" + "golang.org/x/crypto/bcrypt" +) + +type User struct { + ID string `db:"user_id"` + Name string `db:"name"` + Active bool `db:"active"` + Admin bool `db:"admin"` + Hash []byte `db:"hash"` +} + +func (user *User) CheckPassword(password string) bool { + return bcrypt.CompareHashAndPassword(user.Hash, []byte(password)) == nil +} + +func (user *User) SetPassword(password string) error { + if len(password) < 6 { + return errors.New("password too short (min: 6)") + } + + hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return err + } + + user.Hash = hash + return nil +} + +type UserFilter struct { + UserIDs []string + Active *bool + Admin *bool + Limit *int +} diff --git a/services/auth.go b/services/auth.go new file mode 100644 index 0000000..03331c4 --- /dev/null +++ b/services/auth.go @@ -0,0 +1,133 @@ +package services + +import ( + "context" + "errors" + "git.aiterp.net/stufflog/server/database/repositories" + "git.aiterp.net/stufflog/server/internal/generate" + "git.aiterp.net/stufflog/server/models" + "github.com/gin-gonic/gin" + "math/rand" + "time" +) + +var ErrLoginFailed = errors.New("login failed") +var ErrInternalLoginFailure = errors.New("login failed due to internal error") +var ErrInternalPermissionFailure = errors.New("permission check failed due to internal error") +var ErrPermissionDenied = errors.New("permission denied") + +var authCookieName = "stufflog_cookie" +var authCtxKey = "stufflog.auth" +var ginCtxKey = "stufflog.gin" + +type Auth struct { + users repositories.UserRepository + session repositories.SessionRepository + projects repositories.ProjectRepository +} + +func (auth *Auth) Login(ctx context.Context, username, password string) (*models.User, error) { + user, err := auth.users.Find(ctx, username) + if err != nil { + select { + case <-time.After(time.Millisecond * time.Duration(rand.Int63n(100)+100)): + case <-ctx.Done(): + } + return nil, ErrLoginFailed + } + + if !user.CheckPassword(password) { + select { + case <-time.After(time.Millisecond * time.Duration(rand.Int63n(50))): + case <-ctx.Done(): + } + return nil, ErrLoginFailed + } + + session := models.Session{ + ID: generate.SessionID(), + UserID: user.ID, + ExpiryTime: time.Now().Add(time.Hour * 168), + } + err = auth.session.Save(c.Request.Context(), *session) + if err != nil { + return nil, ErrInternalLoginFailure + } + + if c := ctx.Value(ginCtxKey).(*gin.Context); c != nil { + c.SetCookie(authCookieName, session.ID, 3600*168, "/", "", false, true) + } else { + return nil, ErrInternalLoginFailure + } + + return user, nil +} + +func (auth *Auth) UserFromContext(ctx context.Context) *models.User { + user, _ := ctx.Value(authCtxKey).(*models.User) + return user +} + +func (auth *Auth) ProjectPermission(ctx context.Context, project models.Project) (*models.ProjectPermission, error) { + user := auth.UserFromContext(ctx) + if user == nil { + return nil, ErrPermissionDenied + } + + permission, err := auth.projects.GetPermission(ctx, project, *user) + if err != nil { + return nil, ErrInternalPermissionFailure + } + if permission.Level == models.ProjectPermissionLevelNoAccess { + return nil, ErrPermissionDenied + } + + return permission, nil +} + +func (auth *Auth) IssuePermission(ctx context.Context, issue models.Issue) (*models.ProjectPermission, error) { + user := auth.UserFromContext(ctx) + if user == nil { + return nil, ErrPermissionDenied + } + + permission, err := auth.projects.GetIssuePermission(ctx, issue, *user) + if err != nil { + return nil, ErrInternalPermissionFailure + } + if permission.Level == models.ProjectPermissionLevelNoAccess { + return nil, ErrPermissionDenied + } + + return permission, nil +} + +func (auth *Auth) CheckGinSession(c *gin.Context) { + ctx := context.WithValue(c.Request.Context(), ginCtxKey, authCtxKey) + defer func() { + c.Request = c.Request.WithContext(ctx) + }() + + cookie, err := c.Cookie(authCookieName) + if err != nil { + return + } + + session, err := auth.session.Find(c.Request.Context(), cookie) + if err != nil { + return + } + + if time.Until(session.ExpiryTime) < time.Hour*167 { + session.ExpiryTime = time.Now().Add(time.Hour * 168) + _ = auth.session.Save(c.Request.Context(), *session) + c.SetCookie(authCookieName, session.ID, 3600*168, "/", "", false, true) + } + + user, err := auth.users.Find(c.Request.Context(), cookie) + if err != nil { + return + } + + ctx = context.WithValue(ctx, authCtxKey, user) +} diff --git a/services/bundle.go b/services/bundle.go new file mode 100644 index 0000000..d0b8fa8 --- /dev/null +++ b/services/bundle.go @@ -0,0 +1,5 @@ +package services + +type Bundle struct { + Auth *Auth +}