├── .gitignore ├── Makefile ├── go.mod ├── go.sum ├── gqlgen.yml ├── graph ├── complexity.go ├── dataloader.go ├── db │ ├── boil_queries.go │ ├── boil_table_names.go │ ├── boil_types.go │ ├── boil_view_names.go │ ├── issues.go │ ├── projectcards.go │ ├── projects.go │ ├── pullrequests.go │ ├── repositories.go │ ├── sqlite_upsert.go │ └── users.go ├── directive.go ├── model │ ├── models_gen.go │ └── mymodel.go ├── resolver.go ├── schema.resolvers.go └── services │ ├── issues.go │ ├── projectitems.go │ ├── projects.go │ ├── pullrequests.go │ ├── repositories.go │ ├── service.go │ ├── users.go │ └── users_test.go ├── internal └── generated.go ├── middlewares └── auth │ └── auth.go ├── mock └── services │ └── service_mock.go ├── schema.graphqls ├── server.go ├── server_test.go ├── setup.sh ├── sqlboiler.toml └── testdata └── golden ├── TestNodeRepositoryIn.gpl.golden └── TestNodeRepositoryOut.json.golden /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/go 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=go 3 | 4 | ### Go ### 5 | # If you prefer the allow list template instead of the deny list, see community template: 6 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 7 | # 8 | # Binaries for programs and plugins 9 | *.exe 10 | *.exe~ 11 | *.dll 12 | *.so 13 | *.dylib 14 | 15 | # Test binary, built with `go test -c` 16 | *.test 17 | 18 | # Output of the go coverage tool, specifically when used with LiteIDE 19 | *.out 20 | 21 | # Dependency directories (remove the comment below to include it) 22 | # vendor/ 23 | 24 | # Go workspace file 25 | go.work 26 | 27 | # End of https://www.toptal.com/developers/gitignore/api/go 28 | 29 | # SQLite3 30 | *.db -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | go run server.go 3 | 4 | test: 5 | go test 6 | 7 | setup: 8 | ./setup.sh 9 | 10 | generate: 11 | gqlgen generate 12 | sqlboiler sqlite3 13 | go generate ./... -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/saki-engineering/graphql-sample 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/99designs/gqlgen v0.17.22 7 | github.com/DATA-DOG/go-sqlmock v1.5.0 8 | github.com/friendsofgo/errors v0.9.2 9 | github.com/golang/mock v1.6.0 10 | github.com/google/go-cmp v0.5.9 11 | github.com/google/uuid v1.3.0 12 | github.com/graph-gophers/dataloader/v7 v7.1.0 13 | github.com/mattn/go-sqlite3 v1.14.16 14 | github.com/tenntenn/golden v0.4.0 15 | github.com/vektah/gqlparser/v2 v2.5.1 16 | github.com/volatiletech/null/v8 v8.1.2 17 | github.com/volatiletech/sqlboiler/v4 v4.14.0 18 | github.com/volatiletech/strmangle v0.0.4 19 | ) 20 | 21 | require ( 22 | github.com/agnivade/levenshtein v1.1.1 // indirect 23 | github.com/gofrs/uuid v4.2.0+incompatible // indirect 24 | github.com/gorilla/websocket v1.5.0 // indirect 25 | github.com/hashicorp/golang-lru v0.5.4 // indirect 26 | github.com/josharian/mapfs v0.0.0-20210615234106-095c008854e6 // indirect 27 | github.com/josharian/txtarfs v0.0.0-20210615234325-77aca6df5bca // indirect 28 | github.com/mitchellh/mapstructure v1.5.0 // indirect 29 | github.com/spf13/cast v1.5.0 // indirect 30 | github.com/volatiletech/inflect v0.0.1 // indirect 31 | github.com/volatiletech/randomize v0.0.1 // indirect 32 | golang.org/x/tools v0.5.0 // indirect 33 | golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect 34 | ) 35 | -------------------------------------------------------------------------------- /gqlgen.yml: -------------------------------------------------------------------------------- 1 | # Where are all the schema files located? globs are supported eg src/**/*.graphqls 2 | schema: 3 | - ./*.graphqls 4 | 5 | # Where should the generated server code go? 6 | exec: 7 | filename: internal/generated.go 8 | package: internal 9 | 10 | # Uncomment to enable federation 11 | # federation: 12 | # filename: graph/federation.go 13 | # package: graph 14 | 15 | # Where should any generated models go? 16 | model: 17 | filename: graph/model/models_gen.go 18 | package: model 19 | 20 | # Where should the resolver implementations go? 21 | resolver: 22 | layout: follow-schema 23 | dir: graph 24 | package: graph 25 | 26 | # Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models 27 | # struct_tag: json 28 | 29 | # Optional: turn on to use []Thing instead of []*Thing 30 | # omit_slice_element_pointers: false 31 | 32 | # Optional: turn off to make struct-type struct fields not use pointers 33 | # e.g. type Thing struct { FieldA OtherThing } instead of { FieldA *OtherThing } 34 | # struct_fields_always_pointers: true 35 | 36 | # Optional: turn off to make resolvers return values instead of pointers for structs 37 | # resolvers_always_return_pointers: true 38 | 39 | # Optional: set to speed up generation time by not performing a final validation pass. 40 | # skip_validation: true 41 | 42 | # gqlgen will search for any type names in the schema in these go packages 43 | # if they match it will use them, otherwise it will generate them. 44 | autobind: 45 | # - "gqlgen-server/graph/model" 46 | 47 | # This section declares type mapping between the GraphQL and go type systems 48 | # 49 | # The first line in each type will be used as defaults for resolver arguments and 50 | # modelgen, the others will be allowed when binding to fields. Configure them to 51 | # your liking 52 | models: 53 | ID: 54 | model: 55 | - github.com/99designs/gqlgen/graphql.ID 56 | - github.com/99designs/gqlgen/graphql.Int 57 | - github.com/99designs/gqlgen/graphql.Int64 58 | - github.com/99designs/gqlgen/graphql.Int32 59 | Int: 60 | model: 61 | - github.com/99designs/gqlgen/graphql.Int 62 | - github.com/99designs/gqlgen/graphql.Int64 63 | - github.com/99designs/gqlgen/graphql.Int32 64 | DateTime: 65 | model: 66 | - github.com/99designs/gqlgen/graphql.Time 67 | URI: 68 | model: 69 | - github.com/saki-engineering/graphql-sample/graph/model.URI 70 | User: 71 | fields: 72 | projectV2: 73 | resolver: true 74 | projectV2s: 75 | resolver: true 76 | Repository: 77 | fields: 78 | owner: 79 | resolver: true 80 | issue: 81 | resolver: true 82 | issues: 83 | resolver: true 84 | pullRequest: 85 | resolver: true 86 | pullRequests: 87 | resolver: true 88 | Issue: 89 | fields: 90 | repository: 91 | resolver: true 92 | projectItems: 93 | resolver: true 94 | author: 95 | resolver: true 96 | ProjectV2: 97 | fields: 98 | items: 99 | resolver: true 100 | owner: 101 | resolver: true 102 | PullRequest: 103 | fields: 104 | repository: 105 | resolver: true 106 | projectItems: 107 | resolver: true 108 | ProjectV2Item: 109 | fields: 110 | content: 111 | resolver: true 112 | 113 | -------------------------------------------------------------------------------- /graph/complexity.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "github.com/saki-engineering/graphql-sample/internal" 5 | ) 6 | 7 | func ComplexityConfig() internal.ComplexityRoot { 8 | var c internal.ComplexityRoot 9 | 10 | c.Query.Node = func(childComplexity int, id string) int { 11 | return 1 12 | } 13 | c.ProjectV2.Title = func(childComplexity int) int { 14 | return 1 15 | } 16 | c.Repository.Issues = func(childComplexity int, after *string, before *string, first *int, last *int) int { 17 | var cnt int 18 | switch { 19 | case first != nil && last != nil: 20 | if *first < *last { 21 | cnt = *last 22 | } else { 23 | cnt = *first 24 | } 25 | case first != nil && last == nil: 26 | cnt = *first 27 | case first == nil && last != nil: 28 | cnt = *last 29 | default: 30 | cnt = 1 31 | } 32 | return cnt * childComplexity 33 | } 34 | return c 35 | } 36 | -------------------------------------------------------------------------------- /graph/dataloader.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/saki-engineering/graphql-sample/graph/model" 8 | "github.com/saki-engineering/graphql-sample/graph/services" 9 | 10 | "github.com/graph-gophers/dataloader/v7" 11 | ) 12 | 13 | type Loaders struct { 14 | UserLoader dataloader.Interface[string, *model.User] 15 | } 16 | 17 | func NewLoaders(Srv services.Services) *Loaders { 18 | userBatcher := &userBatcher{Srv: Srv} 19 | 20 | return &Loaders{ 21 | // dataloader.Loader[string, *model.User]型 22 | UserLoader: dataloader.NewBatchedLoader[string, *model.User](userBatcher.BatchGetUsers), 23 | } 24 | } 25 | 26 | type userBatcher struct { 27 | Srv services.Services 28 | } 29 | 30 | // github.com/graph-gophers/dataloader/v7 の type BatchFunc[K, V]を満たすため 31 | // dataloader.NewBatchedLoader関数の引数にできる 32 | func (u *userBatcher) BatchGetUsers(ctx context.Context, IDs []string) []*dataloader.Result[*model.User] { 33 | // 引数と戻り値のスライスlenは等しくする 34 | results := make([]*dataloader.Result[*model.User], len(IDs)) 35 | for i := range results { 36 | results[i] = &dataloader.Result[*model.User]{ 37 | Error: errors.New("not found"), 38 | } 39 | } 40 | 41 | indexs := make(map[string]int, len(IDs)) 42 | for i, ID := range IDs { 43 | indexs[ID] = i 44 | } 45 | 46 | users, err := u.Srv.ListUsersByID(ctx, IDs) 47 | for _, user := range users { 48 | var rsl *dataloader.Result[*model.User] 49 | if err != nil { 50 | rsl = &dataloader.Result[*model.User]{ 51 | Error: err, 52 | } 53 | } else { 54 | rsl = &dataloader.Result[*model.User]{ 55 | Data: user, 56 | } 57 | } 58 | // 該当するIDと同じindexに格納する 59 | results[indexs[user.ID]] = rsl 60 | } 61 | return results 62 | } 63 | -------------------------------------------------------------------------------- /graph/db/boil_queries.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 4.14.0 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package db 5 | 6 | import ( 7 | "github.com/volatiletech/sqlboiler/v4/drivers" 8 | "github.com/volatiletech/sqlboiler/v4/queries" 9 | "github.com/volatiletech/sqlboiler/v4/queries/qm" 10 | ) 11 | 12 | var dialect = drivers.Dialect{ 13 | LQ: 0x22, 14 | RQ: 0x22, 15 | 16 | UseIndexPlaceholders: false, 17 | UseLastInsertID: false, 18 | UseSchema: false, 19 | UseDefaultKeyword: true, 20 | UseAutoColumns: false, 21 | UseTopClause: false, 22 | UseOutputClause: false, 23 | UseCaseWhenExistsClause: false, 24 | } 25 | 26 | // NewQuery initializes a new Query using the passed in QueryMods 27 | func NewQuery(mods ...qm.QueryMod) *queries.Query { 28 | q := &queries.Query{} 29 | queries.SetDialect(q, &dialect) 30 | qm.Apply(q, mods...) 31 | 32 | return q 33 | } 34 | -------------------------------------------------------------------------------- /graph/db/boil_table_names.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 4.14.0 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package db 5 | 6 | var TableNames = struct { 7 | Issues string 8 | Projectcards string 9 | Projects string 10 | Pullrequests string 11 | Repositories string 12 | Users string 13 | }{ 14 | Issues: "issues", 15 | Projectcards: "projectcards", 16 | Projects: "projects", 17 | Pullrequests: "pullrequests", 18 | Repositories: "repositories", 19 | Users: "users", 20 | } 21 | -------------------------------------------------------------------------------- /graph/db/boil_types.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 4.14.0 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package db 5 | 6 | import ( 7 | "strconv" 8 | 9 | "github.com/friendsofgo/errors" 10 | "github.com/volatiletech/sqlboiler/v4/boil" 11 | "github.com/volatiletech/strmangle" 12 | ) 13 | 14 | // M type is for providing columns and column values to UpdateAll. 15 | type M map[string]interface{} 16 | 17 | // ErrSyncFail occurs during insert when the record could not be retrieved in 18 | // order to populate default value information. This usually happens when LastInsertId 19 | // fails or there was a primary key configuration that was not resolvable. 20 | var ErrSyncFail = errors.New("db: failed to synchronize data after insert") 21 | 22 | type insertCache struct { 23 | query string 24 | retQuery string 25 | valueMapping []uint64 26 | retMapping []uint64 27 | } 28 | 29 | type updateCache struct { 30 | query string 31 | valueMapping []uint64 32 | } 33 | 34 | func makeCacheKey(cols boil.Columns, nzDefaults []string) string { 35 | buf := strmangle.GetBuffer() 36 | 37 | buf.WriteString(strconv.Itoa(cols.Kind)) 38 | for _, w := range cols.Cols { 39 | buf.WriteString(w) 40 | } 41 | 42 | if len(nzDefaults) != 0 { 43 | buf.WriteByte('.') 44 | } 45 | for _, nz := range nzDefaults { 46 | buf.WriteString(nz) 47 | } 48 | 49 | str := buf.String() 50 | strmangle.PutBuffer(buf) 51 | return str 52 | } 53 | -------------------------------------------------------------------------------- /graph/db/boil_view_names.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 4.14.0 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package db 5 | 6 | var ViewNames = struct { 7 | }{} 8 | -------------------------------------------------------------------------------- /graph/db/projects.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 4.14.0 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package db 5 | 6 | import ( 7 | "context" 8 | "database/sql" 9 | "fmt" 10 | "reflect" 11 | "strconv" 12 | "strings" 13 | "sync" 14 | "time" 15 | 16 | "github.com/friendsofgo/errors" 17 | "github.com/volatiletech/sqlboiler/v4/boil" 18 | "github.com/volatiletech/sqlboiler/v4/queries" 19 | "github.com/volatiletech/sqlboiler/v4/queries/qm" 20 | "github.com/volatiletech/sqlboiler/v4/queries/qmhelper" 21 | "github.com/volatiletech/strmangle" 22 | ) 23 | 24 | // Project is an object representing the database table. 25 | type Project struct { 26 | ID string `boil:"id" json:"id" toml:"id" yaml:"id"` 27 | Title string `boil:"title" json:"title" toml:"title" yaml:"title"` 28 | URL string `boil:"url" json:"url" toml:"url" yaml:"url"` 29 | Number int64 `boil:"number" json:"number" toml:"number" yaml:"number"` 30 | Owner string `boil:"owner" json:"owner" toml:"owner" yaml:"owner"` 31 | 32 | R *projectR `boil:"-" json:"-" toml:"-" yaml:"-"` 33 | L projectL `boil:"-" json:"-" toml:"-" yaml:"-"` 34 | } 35 | 36 | var ProjectColumns = struct { 37 | ID string 38 | Title string 39 | URL string 40 | Number string 41 | Owner string 42 | }{ 43 | ID: "id", 44 | Title: "title", 45 | URL: "url", 46 | Number: "number", 47 | Owner: "owner", 48 | } 49 | 50 | var ProjectTableColumns = struct { 51 | ID string 52 | Title string 53 | URL string 54 | Number string 55 | Owner string 56 | }{ 57 | ID: "projects.id", 58 | Title: "projects.title", 59 | URL: "projects.url", 60 | Number: "projects.number", 61 | Owner: "projects.owner", 62 | } 63 | 64 | // Generated where 65 | 66 | var ProjectWhere = struct { 67 | ID whereHelperstring 68 | Title whereHelperstring 69 | URL whereHelperstring 70 | Number whereHelperint64 71 | Owner whereHelperstring 72 | }{ 73 | ID: whereHelperstring{field: "\"projects\".\"id\""}, 74 | Title: whereHelperstring{field: "\"projects\".\"title\""}, 75 | URL: whereHelperstring{field: "\"projects\".\"url\""}, 76 | Number: whereHelperint64{field: "\"projects\".\"number\""}, 77 | Owner: whereHelperstring{field: "\"projects\".\"owner\""}, 78 | } 79 | 80 | // ProjectRels is where relationship names are stored. 81 | var ProjectRels = struct { 82 | OwnerUser string 83 | Projectcards string 84 | }{ 85 | OwnerUser: "OwnerUser", 86 | Projectcards: "Projectcards", 87 | } 88 | 89 | // projectR is where relationships are stored. 90 | type projectR struct { 91 | OwnerUser *User `boil:"OwnerUser" json:"OwnerUser" toml:"OwnerUser" yaml:"OwnerUser"` 92 | Projectcards ProjectcardSlice `boil:"Projectcards" json:"Projectcards" toml:"Projectcards" yaml:"Projectcards"` 93 | } 94 | 95 | // NewStruct creates a new relationship struct 96 | func (*projectR) NewStruct() *projectR { 97 | return &projectR{} 98 | } 99 | 100 | func (r *projectR) GetOwnerUser() *User { 101 | if r == nil { 102 | return nil 103 | } 104 | return r.OwnerUser 105 | } 106 | 107 | func (r *projectR) GetProjectcards() ProjectcardSlice { 108 | if r == nil { 109 | return nil 110 | } 111 | return r.Projectcards 112 | } 113 | 114 | // projectL is where Load methods for each relationship are stored. 115 | type projectL struct{} 116 | 117 | var ( 118 | projectAllColumns = []string{"id", "title", "url", "number", "owner"} 119 | projectColumnsWithoutDefault = []string{"id", "title", "url", "number", "owner"} 120 | projectColumnsWithDefault = []string{} 121 | projectPrimaryKeyColumns = []string{"id"} 122 | projectGeneratedColumns = []string{} 123 | ) 124 | 125 | type ( 126 | // ProjectSlice is an alias for a slice of pointers to Project. 127 | // This should almost always be used instead of []Project. 128 | ProjectSlice []*Project 129 | // ProjectHook is the signature for custom Project hook methods 130 | ProjectHook func(context.Context, boil.ContextExecutor, *Project) error 131 | 132 | projectQuery struct { 133 | *queries.Query 134 | } 135 | ) 136 | 137 | // Cache for insert, update and upsert 138 | var ( 139 | projectType = reflect.TypeOf(&Project{}) 140 | projectMapping = queries.MakeStructMapping(projectType) 141 | projectPrimaryKeyMapping, _ = queries.BindMapping(projectType, projectMapping, projectPrimaryKeyColumns) 142 | projectInsertCacheMut sync.RWMutex 143 | projectInsertCache = make(map[string]insertCache) 144 | projectUpdateCacheMut sync.RWMutex 145 | projectUpdateCache = make(map[string]updateCache) 146 | projectUpsertCacheMut sync.RWMutex 147 | projectUpsertCache = make(map[string]insertCache) 148 | ) 149 | 150 | var ( 151 | // Force time package dependency for automated UpdatedAt/CreatedAt. 152 | _ = time.Second 153 | // Force qmhelper dependency for where clause generation (which doesn't 154 | // always happen) 155 | _ = qmhelper.Where 156 | ) 157 | 158 | var projectAfterSelectHooks []ProjectHook 159 | 160 | var projectBeforeInsertHooks []ProjectHook 161 | var projectAfterInsertHooks []ProjectHook 162 | 163 | var projectBeforeUpdateHooks []ProjectHook 164 | var projectAfterUpdateHooks []ProjectHook 165 | 166 | var projectBeforeDeleteHooks []ProjectHook 167 | var projectAfterDeleteHooks []ProjectHook 168 | 169 | var projectBeforeUpsertHooks []ProjectHook 170 | var projectAfterUpsertHooks []ProjectHook 171 | 172 | // doAfterSelectHooks executes all "after Select" hooks. 173 | func (o *Project) doAfterSelectHooks(ctx context.Context, exec boil.ContextExecutor) (err error) { 174 | if boil.HooksAreSkipped(ctx) { 175 | return nil 176 | } 177 | 178 | for _, hook := range projectAfterSelectHooks { 179 | if err := hook(ctx, exec, o); err != nil { 180 | return err 181 | } 182 | } 183 | 184 | return nil 185 | } 186 | 187 | // doBeforeInsertHooks executes all "before insert" hooks. 188 | func (o *Project) doBeforeInsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) { 189 | if boil.HooksAreSkipped(ctx) { 190 | return nil 191 | } 192 | 193 | for _, hook := range projectBeforeInsertHooks { 194 | if err := hook(ctx, exec, o); err != nil { 195 | return err 196 | } 197 | } 198 | 199 | return nil 200 | } 201 | 202 | // doAfterInsertHooks executes all "after Insert" hooks. 203 | func (o *Project) doAfterInsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) { 204 | if boil.HooksAreSkipped(ctx) { 205 | return nil 206 | } 207 | 208 | for _, hook := range projectAfterInsertHooks { 209 | if err := hook(ctx, exec, o); err != nil { 210 | return err 211 | } 212 | } 213 | 214 | return nil 215 | } 216 | 217 | // doBeforeUpdateHooks executes all "before Update" hooks. 218 | func (o *Project) doBeforeUpdateHooks(ctx context.Context, exec boil.ContextExecutor) (err error) { 219 | if boil.HooksAreSkipped(ctx) { 220 | return nil 221 | } 222 | 223 | for _, hook := range projectBeforeUpdateHooks { 224 | if err := hook(ctx, exec, o); err != nil { 225 | return err 226 | } 227 | } 228 | 229 | return nil 230 | } 231 | 232 | // doAfterUpdateHooks executes all "after Update" hooks. 233 | func (o *Project) doAfterUpdateHooks(ctx context.Context, exec boil.ContextExecutor) (err error) { 234 | if boil.HooksAreSkipped(ctx) { 235 | return nil 236 | } 237 | 238 | for _, hook := range projectAfterUpdateHooks { 239 | if err := hook(ctx, exec, o); err != nil { 240 | return err 241 | } 242 | } 243 | 244 | return nil 245 | } 246 | 247 | // doBeforeDeleteHooks executes all "before Delete" hooks. 248 | func (o *Project) doBeforeDeleteHooks(ctx context.Context, exec boil.ContextExecutor) (err error) { 249 | if boil.HooksAreSkipped(ctx) { 250 | return nil 251 | } 252 | 253 | for _, hook := range projectBeforeDeleteHooks { 254 | if err := hook(ctx, exec, o); err != nil { 255 | return err 256 | } 257 | } 258 | 259 | return nil 260 | } 261 | 262 | // doAfterDeleteHooks executes all "after Delete" hooks. 263 | func (o *Project) doAfterDeleteHooks(ctx context.Context, exec boil.ContextExecutor) (err error) { 264 | if boil.HooksAreSkipped(ctx) { 265 | return nil 266 | } 267 | 268 | for _, hook := range projectAfterDeleteHooks { 269 | if err := hook(ctx, exec, o); err != nil { 270 | return err 271 | } 272 | } 273 | 274 | return nil 275 | } 276 | 277 | // doBeforeUpsertHooks executes all "before Upsert" hooks. 278 | func (o *Project) doBeforeUpsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) { 279 | if boil.HooksAreSkipped(ctx) { 280 | return nil 281 | } 282 | 283 | for _, hook := range projectBeforeUpsertHooks { 284 | if err := hook(ctx, exec, o); err != nil { 285 | return err 286 | } 287 | } 288 | 289 | return nil 290 | } 291 | 292 | // doAfterUpsertHooks executes all "after Upsert" hooks. 293 | func (o *Project) doAfterUpsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) { 294 | if boil.HooksAreSkipped(ctx) { 295 | return nil 296 | } 297 | 298 | for _, hook := range projectAfterUpsertHooks { 299 | if err := hook(ctx, exec, o); err != nil { 300 | return err 301 | } 302 | } 303 | 304 | return nil 305 | } 306 | 307 | // AddProjectHook registers your hook function for all future operations. 308 | func AddProjectHook(hookPoint boil.HookPoint, projectHook ProjectHook) { 309 | switch hookPoint { 310 | case boil.AfterSelectHook: 311 | projectAfterSelectHooks = append(projectAfterSelectHooks, projectHook) 312 | case boil.BeforeInsertHook: 313 | projectBeforeInsertHooks = append(projectBeforeInsertHooks, projectHook) 314 | case boil.AfterInsertHook: 315 | projectAfterInsertHooks = append(projectAfterInsertHooks, projectHook) 316 | case boil.BeforeUpdateHook: 317 | projectBeforeUpdateHooks = append(projectBeforeUpdateHooks, projectHook) 318 | case boil.AfterUpdateHook: 319 | projectAfterUpdateHooks = append(projectAfterUpdateHooks, projectHook) 320 | case boil.BeforeDeleteHook: 321 | projectBeforeDeleteHooks = append(projectBeforeDeleteHooks, projectHook) 322 | case boil.AfterDeleteHook: 323 | projectAfterDeleteHooks = append(projectAfterDeleteHooks, projectHook) 324 | case boil.BeforeUpsertHook: 325 | projectBeforeUpsertHooks = append(projectBeforeUpsertHooks, projectHook) 326 | case boil.AfterUpsertHook: 327 | projectAfterUpsertHooks = append(projectAfterUpsertHooks, projectHook) 328 | } 329 | } 330 | 331 | // One returns a single project record from the query. 332 | func (q projectQuery) One(ctx context.Context, exec boil.ContextExecutor) (*Project, error) { 333 | o := &Project{} 334 | 335 | queries.SetLimit(q.Query, 1) 336 | 337 | err := q.Bind(ctx, exec, o) 338 | if err != nil { 339 | if errors.Is(err, sql.ErrNoRows) { 340 | return nil, sql.ErrNoRows 341 | } 342 | return nil, errors.Wrap(err, "db: failed to execute a one query for projects") 343 | } 344 | 345 | if err := o.doAfterSelectHooks(ctx, exec); err != nil { 346 | return o, err 347 | } 348 | 349 | return o, nil 350 | } 351 | 352 | // All returns all Project records from the query. 353 | func (q projectQuery) All(ctx context.Context, exec boil.ContextExecutor) (ProjectSlice, error) { 354 | var o []*Project 355 | 356 | err := q.Bind(ctx, exec, &o) 357 | if err != nil { 358 | return nil, errors.Wrap(err, "db: failed to assign all query results to Project slice") 359 | } 360 | 361 | if len(projectAfterSelectHooks) != 0 { 362 | for _, obj := range o { 363 | if err := obj.doAfterSelectHooks(ctx, exec); err != nil { 364 | return o, err 365 | } 366 | } 367 | } 368 | 369 | return o, nil 370 | } 371 | 372 | // Count returns the count of all Project records in the query. 373 | func (q projectQuery) Count(ctx context.Context, exec boil.ContextExecutor) (int64, error) { 374 | var count int64 375 | 376 | queries.SetSelect(q.Query, nil) 377 | queries.SetCount(q.Query) 378 | 379 | err := q.Query.QueryRowContext(ctx, exec).Scan(&count) 380 | if err != nil { 381 | return 0, errors.Wrap(err, "db: failed to count projects rows") 382 | } 383 | 384 | return count, nil 385 | } 386 | 387 | // Exists checks if the row exists in the table. 388 | func (q projectQuery) Exists(ctx context.Context, exec boil.ContextExecutor) (bool, error) { 389 | var count int64 390 | 391 | queries.SetSelect(q.Query, nil) 392 | queries.SetCount(q.Query) 393 | queries.SetLimit(q.Query, 1) 394 | 395 | err := q.Query.QueryRowContext(ctx, exec).Scan(&count) 396 | if err != nil { 397 | return false, errors.Wrap(err, "db: failed to check if projects exists") 398 | } 399 | 400 | return count > 0, nil 401 | } 402 | 403 | // OwnerUser pointed to by the foreign key. 404 | func (o *Project) OwnerUser(mods ...qm.QueryMod) userQuery { 405 | queryMods := []qm.QueryMod{ 406 | qm.Where("\"id\" = ?", o.Owner), 407 | } 408 | 409 | queryMods = append(queryMods, mods...) 410 | 411 | return Users(queryMods...) 412 | } 413 | 414 | // Projectcards retrieves all the projectcard's Projectcards with an executor. 415 | func (o *Project) Projectcards(mods ...qm.QueryMod) projectcardQuery { 416 | var queryMods []qm.QueryMod 417 | if len(mods) != 0 { 418 | queryMods = append(queryMods, mods...) 419 | } 420 | 421 | queryMods = append(queryMods, 422 | qm.Where("\"projectcards\".\"project\"=?", o.ID), 423 | ) 424 | 425 | return Projectcards(queryMods...) 426 | } 427 | 428 | // LoadOwnerUser allows an eager lookup of values, cached into the 429 | // loaded structs of the objects. This is for an N-1 relationship. 430 | func (projectL) LoadOwnerUser(ctx context.Context, e boil.ContextExecutor, singular bool, maybeProject interface{}, mods queries.Applicator) error { 431 | var slice []*Project 432 | var object *Project 433 | 434 | if singular { 435 | var ok bool 436 | object, ok = maybeProject.(*Project) 437 | if !ok { 438 | object = new(Project) 439 | ok = queries.SetFromEmbeddedStruct(&object, &maybeProject) 440 | if !ok { 441 | return errors.New(fmt.Sprintf("failed to set %T from embedded struct %T", object, maybeProject)) 442 | } 443 | } 444 | } else { 445 | s, ok := maybeProject.(*[]*Project) 446 | if ok { 447 | slice = *s 448 | } else { 449 | ok = queries.SetFromEmbeddedStruct(&slice, maybeProject) 450 | if !ok { 451 | return errors.New(fmt.Sprintf("failed to set %T from embedded struct %T", slice, maybeProject)) 452 | } 453 | } 454 | } 455 | 456 | args := make([]interface{}, 0, 1) 457 | if singular { 458 | if object.R == nil { 459 | object.R = &projectR{} 460 | } 461 | args = append(args, object.Owner) 462 | 463 | } else { 464 | Outer: 465 | for _, obj := range slice { 466 | if obj.R == nil { 467 | obj.R = &projectR{} 468 | } 469 | 470 | for _, a := range args { 471 | if a == obj.Owner { 472 | continue Outer 473 | } 474 | } 475 | 476 | args = append(args, obj.Owner) 477 | 478 | } 479 | } 480 | 481 | if len(args) == 0 { 482 | return nil 483 | } 484 | 485 | query := NewQuery( 486 | qm.From(`users`), 487 | qm.WhereIn(`users.id in ?`, args...), 488 | ) 489 | if mods != nil { 490 | mods.Apply(query) 491 | } 492 | 493 | results, err := query.QueryContext(ctx, e) 494 | if err != nil { 495 | return errors.Wrap(err, "failed to eager load User") 496 | } 497 | 498 | var resultSlice []*User 499 | if err = queries.Bind(results, &resultSlice); err != nil { 500 | return errors.Wrap(err, "failed to bind eager loaded slice User") 501 | } 502 | 503 | if err = results.Close(); err != nil { 504 | return errors.Wrap(err, "failed to close results of eager load for users") 505 | } 506 | if err = results.Err(); err != nil { 507 | return errors.Wrap(err, "error occurred during iteration of eager loaded relations for users") 508 | } 509 | 510 | if len(userAfterSelectHooks) != 0 { 511 | for _, obj := range resultSlice { 512 | if err := obj.doAfterSelectHooks(ctx, e); err != nil { 513 | return err 514 | } 515 | } 516 | } 517 | 518 | if len(resultSlice) == 0 { 519 | return nil 520 | } 521 | 522 | if singular { 523 | foreign := resultSlice[0] 524 | object.R.OwnerUser = foreign 525 | if foreign.R == nil { 526 | foreign.R = &userR{} 527 | } 528 | foreign.R.OwnerProjects = append(foreign.R.OwnerProjects, object) 529 | return nil 530 | } 531 | 532 | for _, local := range slice { 533 | for _, foreign := range resultSlice { 534 | if local.Owner == foreign.ID { 535 | local.R.OwnerUser = foreign 536 | if foreign.R == nil { 537 | foreign.R = &userR{} 538 | } 539 | foreign.R.OwnerProjects = append(foreign.R.OwnerProjects, local) 540 | break 541 | } 542 | } 543 | } 544 | 545 | return nil 546 | } 547 | 548 | // LoadProjectcards allows an eager lookup of values, cached into the 549 | // loaded structs of the objects. This is for a 1-M or N-M relationship. 550 | func (projectL) LoadProjectcards(ctx context.Context, e boil.ContextExecutor, singular bool, maybeProject interface{}, mods queries.Applicator) error { 551 | var slice []*Project 552 | var object *Project 553 | 554 | if singular { 555 | var ok bool 556 | object, ok = maybeProject.(*Project) 557 | if !ok { 558 | object = new(Project) 559 | ok = queries.SetFromEmbeddedStruct(&object, &maybeProject) 560 | if !ok { 561 | return errors.New(fmt.Sprintf("failed to set %T from embedded struct %T", object, maybeProject)) 562 | } 563 | } 564 | } else { 565 | s, ok := maybeProject.(*[]*Project) 566 | if ok { 567 | slice = *s 568 | } else { 569 | ok = queries.SetFromEmbeddedStruct(&slice, maybeProject) 570 | if !ok { 571 | return errors.New(fmt.Sprintf("failed to set %T from embedded struct %T", slice, maybeProject)) 572 | } 573 | } 574 | } 575 | 576 | args := make([]interface{}, 0, 1) 577 | if singular { 578 | if object.R == nil { 579 | object.R = &projectR{} 580 | } 581 | args = append(args, object.ID) 582 | } else { 583 | Outer: 584 | for _, obj := range slice { 585 | if obj.R == nil { 586 | obj.R = &projectR{} 587 | } 588 | 589 | for _, a := range args { 590 | if a == obj.ID { 591 | continue Outer 592 | } 593 | } 594 | 595 | args = append(args, obj.ID) 596 | } 597 | } 598 | 599 | if len(args) == 0 { 600 | return nil 601 | } 602 | 603 | query := NewQuery( 604 | qm.From(`projectcards`), 605 | qm.WhereIn(`projectcards.project in ?`, args...), 606 | ) 607 | if mods != nil { 608 | mods.Apply(query) 609 | } 610 | 611 | results, err := query.QueryContext(ctx, e) 612 | if err != nil { 613 | return errors.Wrap(err, "failed to eager load projectcards") 614 | } 615 | 616 | var resultSlice []*Projectcard 617 | if err = queries.Bind(results, &resultSlice); err != nil { 618 | return errors.Wrap(err, "failed to bind eager loaded slice projectcards") 619 | } 620 | 621 | if err = results.Close(); err != nil { 622 | return errors.Wrap(err, "failed to close results in eager load on projectcards") 623 | } 624 | if err = results.Err(); err != nil { 625 | return errors.Wrap(err, "error occurred during iteration of eager loaded relations for projectcards") 626 | } 627 | 628 | if len(projectcardAfterSelectHooks) != 0 { 629 | for _, obj := range resultSlice { 630 | if err := obj.doAfterSelectHooks(ctx, e); err != nil { 631 | return err 632 | } 633 | } 634 | } 635 | if singular { 636 | object.R.Projectcards = resultSlice 637 | for _, foreign := range resultSlice { 638 | if foreign.R == nil { 639 | foreign.R = &projectcardR{} 640 | } 641 | foreign.R.ProjectcardProject = object 642 | } 643 | return nil 644 | } 645 | 646 | for _, foreign := range resultSlice { 647 | for _, local := range slice { 648 | if local.ID == foreign.Project { 649 | local.R.Projectcards = append(local.R.Projectcards, foreign) 650 | if foreign.R == nil { 651 | foreign.R = &projectcardR{} 652 | } 653 | foreign.R.ProjectcardProject = local 654 | break 655 | } 656 | } 657 | } 658 | 659 | return nil 660 | } 661 | 662 | // SetOwnerUser of the project to the related item. 663 | // Sets o.R.OwnerUser to related. 664 | // Adds o to related.R.OwnerProjects. 665 | func (o *Project) SetOwnerUser(ctx context.Context, exec boil.ContextExecutor, insert bool, related *User) error { 666 | var err error 667 | if insert { 668 | if err = related.Insert(ctx, exec, boil.Infer()); err != nil { 669 | return errors.Wrap(err, "failed to insert into foreign table") 670 | } 671 | } 672 | 673 | updateQuery := fmt.Sprintf( 674 | "UPDATE \"projects\" SET %s WHERE %s", 675 | strmangle.SetParamNames("\"", "\"", 0, []string{"owner"}), 676 | strmangle.WhereClause("\"", "\"", 0, projectPrimaryKeyColumns), 677 | ) 678 | values := []interface{}{related.ID, o.ID} 679 | 680 | if boil.IsDebug(ctx) { 681 | writer := boil.DebugWriterFrom(ctx) 682 | fmt.Fprintln(writer, updateQuery) 683 | fmt.Fprintln(writer, values) 684 | } 685 | if _, err = exec.ExecContext(ctx, updateQuery, values...); err != nil { 686 | return errors.Wrap(err, "failed to update local table") 687 | } 688 | 689 | o.Owner = related.ID 690 | if o.R == nil { 691 | o.R = &projectR{ 692 | OwnerUser: related, 693 | } 694 | } else { 695 | o.R.OwnerUser = related 696 | } 697 | 698 | if related.R == nil { 699 | related.R = &userR{ 700 | OwnerProjects: ProjectSlice{o}, 701 | } 702 | } else { 703 | related.R.OwnerProjects = append(related.R.OwnerProjects, o) 704 | } 705 | 706 | return nil 707 | } 708 | 709 | // AddProjectcards adds the given related objects to the existing relationships 710 | // of the project, optionally inserting them as new records. 711 | // Appends related to o.R.Projectcards. 712 | // Sets related.R.ProjectcardProject appropriately. 713 | func (o *Project) AddProjectcards(ctx context.Context, exec boil.ContextExecutor, insert bool, related ...*Projectcard) error { 714 | var err error 715 | for _, rel := range related { 716 | if insert { 717 | rel.Project = o.ID 718 | if err = rel.Insert(ctx, exec, boil.Infer()); err != nil { 719 | return errors.Wrap(err, "failed to insert into foreign table") 720 | } 721 | } else { 722 | updateQuery := fmt.Sprintf( 723 | "UPDATE \"projectcards\" SET %s WHERE %s", 724 | strmangle.SetParamNames("\"", "\"", 0, []string{"project"}), 725 | strmangle.WhereClause("\"", "\"", 0, projectcardPrimaryKeyColumns), 726 | ) 727 | values := []interface{}{o.ID, rel.ID} 728 | 729 | if boil.IsDebug(ctx) { 730 | writer := boil.DebugWriterFrom(ctx) 731 | fmt.Fprintln(writer, updateQuery) 732 | fmt.Fprintln(writer, values) 733 | } 734 | if _, err = exec.ExecContext(ctx, updateQuery, values...); err != nil { 735 | return errors.Wrap(err, "failed to update foreign table") 736 | } 737 | 738 | rel.Project = o.ID 739 | } 740 | } 741 | 742 | if o.R == nil { 743 | o.R = &projectR{ 744 | Projectcards: related, 745 | } 746 | } else { 747 | o.R.Projectcards = append(o.R.Projectcards, related...) 748 | } 749 | 750 | for _, rel := range related { 751 | if rel.R == nil { 752 | rel.R = &projectcardR{ 753 | ProjectcardProject: o, 754 | } 755 | } else { 756 | rel.R.ProjectcardProject = o 757 | } 758 | } 759 | return nil 760 | } 761 | 762 | // Projects retrieves all the records using an executor. 763 | func Projects(mods ...qm.QueryMod) projectQuery { 764 | mods = append(mods, qm.From("\"projects\"")) 765 | q := NewQuery(mods...) 766 | if len(queries.GetSelect(q)) == 0 { 767 | queries.SetSelect(q, []string{"\"projects\".*"}) 768 | } 769 | 770 | return projectQuery{q} 771 | } 772 | 773 | // FindProject retrieves a single record by ID with an executor. 774 | // If selectCols is empty Find will return all columns. 775 | func FindProject(ctx context.Context, exec boil.ContextExecutor, iD string, selectCols ...string) (*Project, error) { 776 | projectObj := &Project{} 777 | 778 | sel := "*" 779 | if len(selectCols) > 0 { 780 | sel = strings.Join(strmangle.IdentQuoteSlice(dialect.LQ, dialect.RQ, selectCols), ",") 781 | } 782 | query := fmt.Sprintf( 783 | "select %s from \"projects\" where \"id\"=?", sel, 784 | ) 785 | 786 | q := queries.Raw(query, iD) 787 | 788 | err := q.Bind(ctx, exec, projectObj) 789 | if err != nil { 790 | if errors.Is(err, sql.ErrNoRows) { 791 | return nil, sql.ErrNoRows 792 | } 793 | return nil, errors.Wrap(err, "db: unable to select from projects") 794 | } 795 | 796 | if err = projectObj.doAfterSelectHooks(ctx, exec); err != nil { 797 | return projectObj, err 798 | } 799 | 800 | return projectObj, nil 801 | } 802 | 803 | // Insert a single record using an executor. 804 | // See boil.Columns.InsertColumnSet documentation to understand column list inference for inserts. 805 | func (o *Project) Insert(ctx context.Context, exec boil.ContextExecutor, columns boil.Columns) error { 806 | if o == nil { 807 | return errors.New("db: no projects provided for insertion") 808 | } 809 | 810 | var err error 811 | 812 | if err := o.doBeforeInsertHooks(ctx, exec); err != nil { 813 | return err 814 | } 815 | 816 | nzDefaults := queries.NonZeroDefaultSet(projectColumnsWithDefault, o) 817 | 818 | key := makeCacheKey(columns, nzDefaults) 819 | projectInsertCacheMut.RLock() 820 | cache, cached := projectInsertCache[key] 821 | projectInsertCacheMut.RUnlock() 822 | 823 | if !cached { 824 | wl, returnColumns := columns.InsertColumnSet( 825 | projectAllColumns, 826 | projectColumnsWithDefault, 827 | projectColumnsWithoutDefault, 828 | nzDefaults, 829 | ) 830 | 831 | cache.valueMapping, err = queries.BindMapping(projectType, projectMapping, wl) 832 | if err != nil { 833 | return err 834 | } 835 | cache.retMapping, err = queries.BindMapping(projectType, projectMapping, returnColumns) 836 | if err != nil { 837 | return err 838 | } 839 | if len(wl) != 0 { 840 | cache.query = fmt.Sprintf("INSERT INTO \"projects\" (\"%s\") %%sVALUES (%s)%%s", strings.Join(wl, "\",\""), strmangle.Placeholders(dialect.UseIndexPlaceholders, len(wl), 1, 1)) 841 | } else { 842 | cache.query = "INSERT INTO \"projects\" %sDEFAULT VALUES%s" 843 | } 844 | 845 | var queryOutput, queryReturning string 846 | 847 | if len(cache.retMapping) != 0 { 848 | queryReturning = fmt.Sprintf(" RETURNING \"%s\"", strings.Join(returnColumns, "\",\"")) 849 | } 850 | 851 | cache.query = fmt.Sprintf(cache.query, queryOutput, queryReturning) 852 | } 853 | 854 | value := reflect.Indirect(reflect.ValueOf(o)) 855 | vals := queries.ValuesFromMapping(value, cache.valueMapping) 856 | 857 | if boil.IsDebug(ctx) { 858 | writer := boil.DebugWriterFrom(ctx) 859 | fmt.Fprintln(writer, cache.query) 860 | fmt.Fprintln(writer, vals) 861 | } 862 | 863 | if len(cache.retMapping) != 0 { 864 | err = exec.QueryRowContext(ctx, cache.query, vals...).Scan(queries.PtrsFromMapping(value, cache.retMapping)...) 865 | } else { 866 | _, err = exec.ExecContext(ctx, cache.query, vals...) 867 | } 868 | 869 | if err != nil { 870 | return errors.Wrap(err, "db: unable to insert into projects") 871 | } 872 | 873 | if !cached { 874 | projectInsertCacheMut.Lock() 875 | projectInsertCache[key] = cache 876 | projectInsertCacheMut.Unlock() 877 | } 878 | 879 | return o.doAfterInsertHooks(ctx, exec) 880 | } 881 | 882 | // Update uses an executor to update the Project. 883 | // See boil.Columns.UpdateColumnSet documentation to understand column list inference for updates. 884 | // Update does not automatically update the record in case of default values. Use .Reload() to refresh the records. 885 | func (o *Project) Update(ctx context.Context, exec boil.ContextExecutor, columns boil.Columns) (int64, error) { 886 | var err error 887 | if err = o.doBeforeUpdateHooks(ctx, exec); err != nil { 888 | return 0, err 889 | } 890 | key := makeCacheKey(columns, nil) 891 | projectUpdateCacheMut.RLock() 892 | cache, cached := projectUpdateCache[key] 893 | projectUpdateCacheMut.RUnlock() 894 | 895 | if !cached { 896 | wl := columns.UpdateColumnSet( 897 | projectAllColumns, 898 | projectPrimaryKeyColumns, 899 | ) 900 | 901 | if !columns.IsWhitelist() { 902 | wl = strmangle.SetComplement(wl, []string{"created_at"}) 903 | } 904 | if len(wl) == 0 { 905 | return 0, errors.New("db: unable to update projects, could not build whitelist") 906 | } 907 | 908 | cache.query = fmt.Sprintf("UPDATE \"projects\" SET %s WHERE %s", 909 | strmangle.SetParamNames("\"", "\"", 0, wl), 910 | strmangle.WhereClause("\"", "\"", 0, projectPrimaryKeyColumns), 911 | ) 912 | cache.valueMapping, err = queries.BindMapping(projectType, projectMapping, append(wl, projectPrimaryKeyColumns...)) 913 | if err != nil { 914 | return 0, err 915 | } 916 | } 917 | 918 | values := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), cache.valueMapping) 919 | 920 | if boil.IsDebug(ctx) { 921 | writer := boil.DebugWriterFrom(ctx) 922 | fmt.Fprintln(writer, cache.query) 923 | fmt.Fprintln(writer, values) 924 | } 925 | var result sql.Result 926 | result, err = exec.ExecContext(ctx, cache.query, values...) 927 | if err != nil { 928 | return 0, errors.Wrap(err, "db: unable to update projects row") 929 | } 930 | 931 | rowsAff, err := result.RowsAffected() 932 | if err != nil { 933 | return 0, errors.Wrap(err, "db: failed to get rows affected by update for projects") 934 | } 935 | 936 | if !cached { 937 | projectUpdateCacheMut.Lock() 938 | projectUpdateCache[key] = cache 939 | projectUpdateCacheMut.Unlock() 940 | } 941 | 942 | return rowsAff, o.doAfterUpdateHooks(ctx, exec) 943 | } 944 | 945 | // UpdateAll updates all rows with the specified column values. 946 | func (q projectQuery) UpdateAll(ctx context.Context, exec boil.ContextExecutor, cols M) (int64, error) { 947 | queries.SetUpdate(q.Query, cols) 948 | 949 | result, err := q.Query.ExecContext(ctx, exec) 950 | if err != nil { 951 | return 0, errors.Wrap(err, "db: unable to update all for projects") 952 | } 953 | 954 | rowsAff, err := result.RowsAffected() 955 | if err != nil { 956 | return 0, errors.Wrap(err, "db: unable to retrieve rows affected for projects") 957 | } 958 | 959 | return rowsAff, nil 960 | } 961 | 962 | // UpdateAll updates all rows with the specified column values, using an executor. 963 | func (o ProjectSlice) UpdateAll(ctx context.Context, exec boil.ContextExecutor, cols M) (int64, error) { 964 | ln := int64(len(o)) 965 | if ln == 0 { 966 | return 0, nil 967 | } 968 | 969 | if len(cols) == 0 { 970 | return 0, errors.New("db: update all requires at least one column argument") 971 | } 972 | 973 | colNames := make([]string, len(cols)) 974 | args := make([]interface{}, len(cols)) 975 | 976 | i := 0 977 | for name, value := range cols { 978 | colNames[i] = name 979 | args[i] = value 980 | i++ 981 | } 982 | 983 | // Append all of the primary key values for each column 984 | for _, obj := range o { 985 | pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), projectPrimaryKeyMapping) 986 | args = append(args, pkeyArgs...) 987 | } 988 | 989 | sql := fmt.Sprintf("UPDATE \"projects\" SET %s WHERE %s", 990 | strmangle.SetParamNames("\"", "\"", 0, colNames), 991 | strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 0, projectPrimaryKeyColumns, len(o))) 992 | 993 | if boil.IsDebug(ctx) { 994 | writer := boil.DebugWriterFrom(ctx) 995 | fmt.Fprintln(writer, sql) 996 | fmt.Fprintln(writer, args...) 997 | } 998 | result, err := exec.ExecContext(ctx, sql, args...) 999 | if err != nil { 1000 | return 0, errors.Wrap(err, "db: unable to update all in project slice") 1001 | } 1002 | 1003 | rowsAff, err := result.RowsAffected() 1004 | if err != nil { 1005 | return 0, errors.Wrap(err, "db: unable to retrieve rows affected all in update all project") 1006 | } 1007 | return rowsAff, nil 1008 | } 1009 | 1010 | // Upsert attempts an insert using an executor, and does an update or ignore on conflict. 1011 | // See boil.Columns documentation for how to properly use updateColumns and insertColumns. 1012 | func (o *Project) Upsert(ctx context.Context, exec boil.ContextExecutor, updateOnConflict bool, conflictColumns []string, updateColumns, insertColumns boil.Columns) error { 1013 | if o == nil { 1014 | return errors.New("db: no projects provided for upsert") 1015 | } 1016 | 1017 | if err := o.doBeforeUpsertHooks(ctx, exec); err != nil { 1018 | return err 1019 | } 1020 | 1021 | nzDefaults := queries.NonZeroDefaultSet(projectColumnsWithDefault, o) 1022 | 1023 | // Build cache key in-line uglily - mysql vs psql problems 1024 | buf := strmangle.GetBuffer() 1025 | if updateOnConflict { 1026 | buf.WriteByte('t') 1027 | } else { 1028 | buf.WriteByte('f') 1029 | } 1030 | buf.WriteByte('.') 1031 | for _, c := range conflictColumns { 1032 | buf.WriteString(c) 1033 | } 1034 | buf.WriteByte('.') 1035 | buf.WriteString(strconv.Itoa(updateColumns.Kind)) 1036 | for _, c := range updateColumns.Cols { 1037 | buf.WriteString(c) 1038 | } 1039 | buf.WriteByte('.') 1040 | buf.WriteString(strconv.Itoa(insertColumns.Kind)) 1041 | for _, c := range insertColumns.Cols { 1042 | buf.WriteString(c) 1043 | } 1044 | buf.WriteByte('.') 1045 | for _, c := range nzDefaults { 1046 | buf.WriteString(c) 1047 | } 1048 | key := buf.String() 1049 | strmangle.PutBuffer(buf) 1050 | 1051 | projectUpsertCacheMut.RLock() 1052 | cache, cached := projectUpsertCache[key] 1053 | projectUpsertCacheMut.RUnlock() 1054 | 1055 | var err error 1056 | 1057 | if !cached { 1058 | insert, ret := insertColumns.InsertColumnSet( 1059 | projectAllColumns, 1060 | projectColumnsWithDefault, 1061 | projectColumnsWithoutDefault, 1062 | nzDefaults, 1063 | ) 1064 | update := updateColumns.UpdateColumnSet( 1065 | projectAllColumns, 1066 | projectPrimaryKeyColumns, 1067 | ) 1068 | 1069 | if updateOnConflict && len(update) == 0 { 1070 | return errors.New("db: unable to upsert projects, could not build update column list") 1071 | } 1072 | 1073 | conflict := conflictColumns 1074 | if len(conflict) == 0 { 1075 | conflict = make([]string, len(projectPrimaryKeyColumns)) 1076 | copy(conflict, projectPrimaryKeyColumns) 1077 | } 1078 | cache.query = buildUpsertQuerySQLite(dialect, "\"projects\"", updateOnConflict, ret, update, conflict, insert) 1079 | 1080 | cache.valueMapping, err = queries.BindMapping(projectType, projectMapping, insert) 1081 | if err != nil { 1082 | return err 1083 | } 1084 | if len(ret) != 0 { 1085 | cache.retMapping, err = queries.BindMapping(projectType, projectMapping, ret) 1086 | if err != nil { 1087 | return err 1088 | } 1089 | } 1090 | } 1091 | 1092 | value := reflect.Indirect(reflect.ValueOf(o)) 1093 | vals := queries.ValuesFromMapping(value, cache.valueMapping) 1094 | var returns []interface{} 1095 | if len(cache.retMapping) != 0 { 1096 | returns = queries.PtrsFromMapping(value, cache.retMapping) 1097 | } 1098 | 1099 | if boil.IsDebug(ctx) { 1100 | writer := boil.DebugWriterFrom(ctx) 1101 | fmt.Fprintln(writer, cache.query) 1102 | fmt.Fprintln(writer, vals) 1103 | } 1104 | if len(cache.retMapping) != 0 { 1105 | err = exec.QueryRowContext(ctx, cache.query, vals...).Scan(returns...) 1106 | if errors.Is(err, sql.ErrNoRows) { 1107 | err = nil // Postgres doesn't return anything when there's no update 1108 | } 1109 | } else { 1110 | _, err = exec.ExecContext(ctx, cache.query, vals...) 1111 | } 1112 | if err != nil { 1113 | return errors.Wrap(err, "db: unable to upsert projects") 1114 | } 1115 | 1116 | if !cached { 1117 | projectUpsertCacheMut.Lock() 1118 | projectUpsertCache[key] = cache 1119 | projectUpsertCacheMut.Unlock() 1120 | } 1121 | 1122 | return o.doAfterUpsertHooks(ctx, exec) 1123 | } 1124 | 1125 | // Delete deletes a single Project record with an executor. 1126 | // Delete will match against the primary key column to find the record to delete. 1127 | func (o *Project) Delete(ctx context.Context, exec boil.ContextExecutor) (int64, error) { 1128 | if o == nil { 1129 | return 0, errors.New("db: no Project provided for delete") 1130 | } 1131 | 1132 | if err := o.doBeforeDeleteHooks(ctx, exec); err != nil { 1133 | return 0, err 1134 | } 1135 | 1136 | args := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), projectPrimaryKeyMapping) 1137 | sql := "DELETE FROM \"projects\" WHERE \"id\"=?" 1138 | 1139 | if boil.IsDebug(ctx) { 1140 | writer := boil.DebugWriterFrom(ctx) 1141 | fmt.Fprintln(writer, sql) 1142 | fmt.Fprintln(writer, args...) 1143 | } 1144 | result, err := exec.ExecContext(ctx, sql, args...) 1145 | if err != nil { 1146 | return 0, errors.Wrap(err, "db: unable to delete from projects") 1147 | } 1148 | 1149 | rowsAff, err := result.RowsAffected() 1150 | if err != nil { 1151 | return 0, errors.Wrap(err, "db: failed to get rows affected by delete for projects") 1152 | } 1153 | 1154 | if err := o.doAfterDeleteHooks(ctx, exec); err != nil { 1155 | return 0, err 1156 | } 1157 | 1158 | return rowsAff, nil 1159 | } 1160 | 1161 | // DeleteAll deletes all matching rows. 1162 | func (q projectQuery) DeleteAll(ctx context.Context, exec boil.ContextExecutor) (int64, error) { 1163 | if q.Query == nil { 1164 | return 0, errors.New("db: no projectQuery provided for delete all") 1165 | } 1166 | 1167 | queries.SetDelete(q.Query) 1168 | 1169 | result, err := q.Query.ExecContext(ctx, exec) 1170 | if err != nil { 1171 | return 0, errors.Wrap(err, "db: unable to delete all from projects") 1172 | } 1173 | 1174 | rowsAff, err := result.RowsAffected() 1175 | if err != nil { 1176 | return 0, errors.Wrap(err, "db: failed to get rows affected by deleteall for projects") 1177 | } 1178 | 1179 | return rowsAff, nil 1180 | } 1181 | 1182 | // DeleteAll deletes all rows in the slice, using an executor. 1183 | func (o ProjectSlice) DeleteAll(ctx context.Context, exec boil.ContextExecutor) (int64, error) { 1184 | if len(o) == 0 { 1185 | return 0, nil 1186 | } 1187 | 1188 | if len(projectBeforeDeleteHooks) != 0 { 1189 | for _, obj := range o { 1190 | if err := obj.doBeforeDeleteHooks(ctx, exec); err != nil { 1191 | return 0, err 1192 | } 1193 | } 1194 | } 1195 | 1196 | var args []interface{} 1197 | for _, obj := range o { 1198 | pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), projectPrimaryKeyMapping) 1199 | args = append(args, pkeyArgs...) 1200 | } 1201 | 1202 | sql := "DELETE FROM \"projects\" WHERE " + 1203 | strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 0, projectPrimaryKeyColumns, len(o)) 1204 | 1205 | if boil.IsDebug(ctx) { 1206 | writer := boil.DebugWriterFrom(ctx) 1207 | fmt.Fprintln(writer, sql) 1208 | fmt.Fprintln(writer, args) 1209 | } 1210 | result, err := exec.ExecContext(ctx, sql, args...) 1211 | if err != nil { 1212 | return 0, errors.Wrap(err, "db: unable to delete all from project slice") 1213 | } 1214 | 1215 | rowsAff, err := result.RowsAffected() 1216 | if err != nil { 1217 | return 0, errors.Wrap(err, "db: failed to get rows affected by deleteall for projects") 1218 | } 1219 | 1220 | if len(projectAfterDeleteHooks) != 0 { 1221 | for _, obj := range o { 1222 | if err := obj.doAfterDeleteHooks(ctx, exec); err != nil { 1223 | return 0, err 1224 | } 1225 | } 1226 | } 1227 | 1228 | return rowsAff, nil 1229 | } 1230 | 1231 | // Reload refetches the object from the database 1232 | // using the primary keys with an executor. 1233 | func (o *Project) Reload(ctx context.Context, exec boil.ContextExecutor) error { 1234 | ret, err := FindProject(ctx, exec, o.ID) 1235 | if err != nil { 1236 | return err 1237 | } 1238 | 1239 | *o = *ret 1240 | return nil 1241 | } 1242 | 1243 | // ReloadAll refetches every row with matching primary key column values 1244 | // and overwrites the original object slice with the newly updated slice. 1245 | func (o *ProjectSlice) ReloadAll(ctx context.Context, exec boil.ContextExecutor) error { 1246 | if o == nil || len(*o) == 0 { 1247 | return nil 1248 | } 1249 | 1250 | slice := ProjectSlice{} 1251 | var args []interface{} 1252 | for _, obj := range *o { 1253 | pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), projectPrimaryKeyMapping) 1254 | args = append(args, pkeyArgs...) 1255 | } 1256 | 1257 | sql := "SELECT \"projects\".* FROM \"projects\" WHERE " + 1258 | strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 0, projectPrimaryKeyColumns, len(*o)) 1259 | 1260 | q := queries.Raw(sql, args...) 1261 | 1262 | err := q.Bind(ctx, exec, &slice) 1263 | if err != nil { 1264 | return errors.Wrap(err, "db: unable to reload all in ProjectSlice") 1265 | } 1266 | 1267 | *o = slice 1268 | 1269 | return nil 1270 | } 1271 | 1272 | // ProjectExists checks if the Project row exists. 1273 | func ProjectExists(ctx context.Context, exec boil.ContextExecutor, iD string) (bool, error) { 1274 | var exists bool 1275 | sql := "select exists(select 1 from \"projects\" where \"id\"=? limit 1)" 1276 | 1277 | if boil.IsDebug(ctx) { 1278 | writer := boil.DebugWriterFrom(ctx) 1279 | fmt.Fprintln(writer, sql) 1280 | fmt.Fprintln(writer, iD) 1281 | } 1282 | row := exec.QueryRowContext(ctx, sql, iD) 1283 | 1284 | err := row.Scan(&exists) 1285 | if err != nil { 1286 | return false, errors.Wrap(err, "db: unable to check if projects exists") 1287 | } 1288 | 1289 | return exists, nil 1290 | } 1291 | 1292 | // Exists checks if the Project row exists. 1293 | func (o *Project) Exists(ctx context.Context, exec boil.ContextExecutor) (bool, error) { 1294 | return ProjectExists(ctx, exec, o.ID) 1295 | } 1296 | -------------------------------------------------------------------------------- /graph/db/sqlite_upsert.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 4.14.0 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package db 5 | 6 | import ( 7 | "fmt" 8 | "strings" 9 | 10 | "github.com/volatiletech/sqlboiler/v4/drivers" 11 | "github.com/volatiletech/strmangle" 12 | ) 13 | 14 | // buildUpsertQuerySQLite builds a SQL statement string using the upsertData provided. 15 | func buildUpsertQuerySQLite(dia drivers.Dialect, tableName string, updateOnConflict bool, ret, update, conflict, whitelist []string) string { 16 | conflict = strmangle.IdentQuoteSlice(dia.LQ, dia.RQ, conflict) 17 | whitelist = strmangle.IdentQuoteSlice(dia.LQ, dia.RQ, whitelist) 18 | ret = strmangle.IdentQuoteSlice(dia.LQ, dia.RQ, ret) 19 | 20 | buf := strmangle.GetBuffer() 21 | defer strmangle.PutBuffer(buf) 22 | 23 | columns := "DEFAULT VALUES" 24 | if len(whitelist) != 0 { 25 | columns = fmt.Sprintf("(%s) VALUES (%s)", 26 | strings.Join(whitelist, ", "), 27 | strmangle.Placeholders(dia.UseIndexPlaceholders, len(whitelist), 1, 1)) 28 | } 29 | 30 | fmt.Fprintf( 31 | buf, 32 | "INSERT INTO %s %s ON CONFLICT ", 33 | tableName, 34 | columns, 35 | ) 36 | 37 | if !updateOnConflict || len(update) == 0 { 38 | buf.WriteString("DO NOTHING") 39 | } else { 40 | buf.WriteByte('(') 41 | buf.WriteString(strings.Join(conflict, ", ")) 42 | buf.WriteString(") DO UPDATE SET ") 43 | 44 | for i, v := range update { 45 | if i != 0 { 46 | buf.WriteByte(',') 47 | } 48 | quoted := strmangle.IdentQuote(dia.LQ, dia.RQ, v) 49 | buf.WriteString(quoted) 50 | buf.WriteString(" = EXCLUDED.") 51 | buf.WriteString(quoted) 52 | } 53 | } 54 | 55 | if len(ret) != 0 { 56 | buf.WriteString(" RETURNING ") 57 | buf.WriteString(strings.Join(ret, ", ")) 58 | } 59 | 60 | return buf.String() 61 | } 62 | -------------------------------------------------------------------------------- /graph/db/users.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 4.14.0 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package db 5 | 6 | import ( 7 | "context" 8 | "database/sql" 9 | "fmt" 10 | "reflect" 11 | "strconv" 12 | "strings" 13 | "sync" 14 | "time" 15 | 16 | "github.com/friendsofgo/errors" 17 | "github.com/volatiletech/null/v8" 18 | "github.com/volatiletech/sqlboiler/v4/boil" 19 | "github.com/volatiletech/sqlboiler/v4/queries" 20 | "github.com/volatiletech/sqlboiler/v4/queries/qm" 21 | "github.com/volatiletech/sqlboiler/v4/queries/qmhelper" 22 | "github.com/volatiletech/strmangle" 23 | ) 24 | 25 | // User is an object representing the database table. 26 | type User struct { 27 | ID string `boil:"id" json:"id" toml:"id" yaml:"id"` 28 | Name string `boil:"name" json:"name" toml:"name" yaml:"name"` 29 | ProjectV2 null.String `boil:"project_v2" json:"project_v2,omitempty" toml:"project_v2" yaml:"project_v2,omitempty"` 30 | 31 | R *userR `boil:"-" json:"-" toml:"-" yaml:"-"` 32 | L userL `boil:"-" json:"-" toml:"-" yaml:"-"` 33 | } 34 | 35 | var UserColumns = struct { 36 | ID string 37 | Name string 38 | ProjectV2 string 39 | }{ 40 | ID: "id", 41 | Name: "name", 42 | ProjectV2: "project_v2", 43 | } 44 | 45 | var UserTableColumns = struct { 46 | ID string 47 | Name string 48 | ProjectV2 string 49 | }{ 50 | ID: "users.id", 51 | Name: "users.name", 52 | ProjectV2: "users.project_v2", 53 | } 54 | 55 | // Generated where 56 | 57 | var UserWhere = struct { 58 | ID whereHelperstring 59 | Name whereHelperstring 60 | ProjectV2 whereHelpernull_String 61 | }{ 62 | ID: whereHelperstring{field: "\"users\".\"id\""}, 63 | Name: whereHelperstring{field: "\"users\".\"name\""}, 64 | ProjectV2: whereHelpernull_String{field: "\"users\".\"project_v2\""}, 65 | } 66 | 67 | // UserRels is where relationship names are stored. 68 | var UserRels = struct { 69 | AuthorIssues string 70 | OwnerProjects string 71 | OwnerRepositories string 72 | }{ 73 | AuthorIssues: "AuthorIssues", 74 | OwnerProjects: "OwnerProjects", 75 | OwnerRepositories: "OwnerRepositories", 76 | } 77 | 78 | // userR is where relationships are stored. 79 | type userR struct { 80 | AuthorIssues IssueSlice `boil:"AuthorIssues" json:"AuthorIssues" toml:"AuthorIssues" yaml:"AuthorIssues"` 81 | OwnerProjects ProjectSlice `boil:"OwnerProjects" json:"OwnerProjects" toml:"OwnerProjects" yaml:"OwnerProjects"` 82 | OwnerRepositories RepositorySlice `boil:"OwnerRepositories" json:"OwnerRepositories" toml:"OwnerRepositories" yaml:"OwnerRepositories"` 83 | } 84 | 85 | // NewStruct creates a new relationship struct 86 | func (*userR) NewStruct() *userR { 87 | return &userR{} 88 | } 89 | 90 | func (r *userR) GetAuthorIssues() IssueSlice { 91 | if r == nil { 92 | return nil 93 | } 94 | return r.AuthorIssues 95 | } 96 | 97 | func (r *userR) GetOwnerProjects() ProjectSlice { 98 | if r == nil { 99 | return nil 100 | } 101 | return r.OwnerProjects 102 | } 103 | 104 | func (r *userR) GetOwnerRepositories() RepositorySlice { 105 | if r == nil { 106 | return nil 107 | } 108 | return r.OwnerRepositories 109 | } 110 | 111 | // userL is where Load methods for each relationship are stored. 112 | type userL struct{} 113 | 114 | var ( 115 | userAllColumns = []string{"id", "name", "project_v2"} 116 | userColumnsWithoutDefault = []string{"id", "name"} 117 | userColumnsWithDefault = []string{"project_v2"} 118 | userPrimaryKeyColumns = []string{"id"} 119 | userGeneratedColumns = []string{} 120 | ) 121 | 122 | type ( 123 | // UserSlice is an alias for a slice of pointers to User. 124 | // This should almost always be used instead of []User. 125 | UserSlice []*User 126 | // UserHook is the signature for custom User hook methods 127 | UserHook func(context.Context, boil.ContextExecutor, *User) error 128 | 129 | userQuery struct { 130 | *queries.Query 131 | } 132 | ) 133 | 134 | // Cache for insert, update and upsert 135 | var ( 136 | userType = reflect.TypeOf(&User{}) 137 | userMapping = queries.MakeStructMapping(userType) 138 | userPrimaryKeyMapping, _ = queries.BindMapping(userType, userMapping, userPrimaryKeyColumns) 139 | userInsertCacheMut sync.RWMutex 140 | userInsertCache = make(map[string]insertCache) 141 | userUpdateCacheMut sync.RWMutex 142 | userUpdateCache = make(map[string]updateCache) 143 | userUpsertCacheMut sync.RWMutex 144 | userUpsertCache = make(map[string]insertCache) 145 | ) 146 | 147 | var ( 148 | // Force time package dependency for automated UpdatedAt/CreatedAt. 149 | _ = time.Second 150 | // Force qmhelper dependency for where clause generation (which doesn't 151 | // always happen) 152 | _ = qmhelper.Where 153 | ) 154 | 155 | var userAfterSelectHooks []UserHook 156 | 157 | var userBeforeInsertHooks []UserHook 158 | var userAfterInsertHooks []UserHook 159 | 160 | var userBeforeUpdateHooks []UserHook 161 | var userAfterUpdateHooks []UserHook 162 | 163 | var userBeforeDeleteHooks []UserHook 164 | var userAfterDeleteHooks []UserHook 165 | 166 | var userBeforeUpsertHooks []UserHook 167 | var userAfterUpsertHooks []UserHook 168 | 169 | // doAfterSelectHooks executes all "after Select" hooks. 170 | func (o *User) doAfterSelectHooks(ctx context.Context, exec boil.ContextExecutor) (err error) { 171 | if boil.HooksAreSkipped(ctx) { 172 | return nil 173 | } 174 | 175 | for _, hook := range userAfterSelectHooks { 176 | if err := hook(ctx, exec, o); err != nil { 177 | return err 178 | } 179 | } 180 | 181 | return nil 182 | } 183 | 184 | // doBeforeInsertHooks executes all "before insert" hooks. 185 | func (o *User) doBeforeInsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) { 186 | if boil.HooksAreSkipped(ctx) { 187 | return nil 188 | } 189 | 190 | for _, hook := range userBeforeInsertHooks { 191 | if err := hook(ctx, exec, o); err != nil { 192 | return err 193 | } 194 | } 195 | 196 | return nil 197 | } 198 | 199 | // doAfterInsertHooks executes all "after Insert" hooks. 200 | func (o *User) doAfterInsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) { 201 | if boil.HooksAreSkipped(ctx) { 202 | return nil 203 | } 204 | 205 | for _, hook := range userAfterInsertHooks { 206 | if err := hook(ctx, exec, o); err != nil { 207 | return err 208 | } 209 | } 210 | 211 | return nil 212 | } 213 | 214 | // doBeforeUpdateHooks executes all "before Update" hooks. 215 | func (o *User) doBeforeUpdateHooks(ctx context.Context, exec boil.ContextExecutor) (err error) { 216 | if boil.HooksAreSkipped(ctx) { 217 | return nil 218 | } 219 | 220 | for _, hook := range userBeforeUpdateHooks { 221 | if err := hook(ctx, exec, o); err != nil { 222 | return err 223 | } 224 | } 225 | 226 | return nil 227 | } 228 | 229 | // doAfterUpdateHooks executes all "after Update" hooks. 230 | func (o *User) doAfterUpdateHooks(ctx context.Context, exec boil.ContextExecutor) (err error) { 231 | if boil.HooksAreSkipped(ctx) { 232 | return nil 233 | } 234 | 235 | for _, hook := range userAfterUpdateHooks { 236 | if err := hook(ctx, exec, o); err != nil { 237 | return err 238 | } 239 | } 240 | 241 | return nil 242 | } 243 | 244 | // doBeforeDeleteHooks executes all "before Delete" hooks. 245 | func (o *User) doBeforeDeleteHooks(ctx context.Context, exec boil.ContextExecutor) (err error) { 246 | if boil.HooksAreSkipped(ctx) { 247 | return nil 248 | } 249 | 250 | for _, hook := range userBeforeDeleteHooks { 251 | if err := hook(ctx, exec, o); err != nil { 252 | return err 253 | } 254 | } 255 | 256 | return nil 257 | } 258 | 259 | // doAfterDeleteHooks executes all "after Delete" hooks. 260 | func (o *User) doAfterDeleteHooks(ctx context.Context, exec boil.ContextExecutor) (err error) { 261 | if boil.HooksAreSkipped(ctx) { 262 | return nil 263 | } 264 | 265 | for _, hook := range userAfterDeleteHooks { 266 | if err := hook(ctx, exec, o); err != nil { 267 | return err 268 | } 269 | } 270 | 271 | return nil 272 | } 273 | 274 | // doBeforeUpsertHooks executes all "before Upsert" hooks. 275 | func (o *User) doBeforeUpsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) { 276 | if boil.HooksAreSkipped(ctx) { 277 | return nil 278 | } 279 | 280 | for _, hook := range userBeforeUpsertHooks { 281 | if err := hook(ctx, exec, o); err != nil { 282 | return err 283 | } 284 | } 285 | 286 | return nil 287 | } 288 | 289 | // doAfterUpsertHooks executes all "after Upsert" hooks. 290 | func (o *User) doAfterUpsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) { 291 | if boil.HooksAreSkipped(ctx) { 292 | return nil 293 | } 294 | 295 | for _, hook := range userAfterUpsertHooks { 296 | if err := hook(ctx, exec, o); err != nil { 297 | return err 298 | } 299 | } 300 | 301 | return nil 302 | } 303 | 304 | // AddUserHook registers your hook function for all future operations. 305 | func AddUserHook(hookPoint boil.HookPoint, userHook UserHook) { 306 | switch hookPoint { 307 | case boil.AfterSelectHook: 308 | userAfterSelectHooks = append(userAfterSelectHooks, userHook) 309 | case boil.BeforeInsertHook: 310 | userBeforeInsertHooks = append(userBeforeInsertHooks, userHook) 311 | case boil.AfterInsertHook: 312 | userAfterInsertHooks = append(userAfterInsertHooks, userHook) 313 | case boil.BeforeUpdateHook: 314 | userBeforeUpdateHooks = append(userBeforeUpdateHooks, userHook) 315 | case boil.AfterUpdateHook: 316 | userAfterUpdateHooks = append(userAfterUpdateHooks, userHook) 317 | case boil.BeforeDeleteHook: 318 | userBeforeDeleteHooks = append(userBeforeDeleteHooks, userHook) 319 | case boil.AfterDeleteHook: 320 | userAfterDeleteHooks = append(userAfterDeleteHooks, userHook) 321 | case boil.BeforeUpsertHook: 322 | userBeforeUpsertHooks = append(userBeforeUpsertHooks, userHook) 323 | case boil.AfterUpsertHook: 324 | userAfterUpsertHooks = append(userAfterUpsertHooks, userHook) 325 | } 326 | } 327 | 328 | // One returns a single user record from the query. 329 | func (q userQuery) One(ctx context.Context, exec boil.ContextExecutor) (*User, error) { 330 | o := &User{} 331 | 332 | queries.SetLimit(q.Query, 1) 333 | 334 | err := q.Bind(ctx, exec, o) 335 | if err != nil { 336 | if errors.Is(err, sql.ErrNoRows) { 337 | return nil, sql.ErrNoRows 338 | } 339 | return nil, errors.Wrap(err, "db: failed to execute a one query for users") 340 | } 341 | 342 | if err := o.doAfterSelectHooks(ctx, exec); err != nil { 343 | return o, err 344 | } 345 | 346 | return o, nil 347 | } 348 | 349 | // All returns all User records from the query. 350 | func (q userQuery) All(ctx context.Context, exec boil.ContextExecutor) (UserSlice, error) { 351 | var o []*User 352 | 353 | err := q.Bind(ctx, exec, &o) 354 | if err != nil { 355 | return nil, errors.Wrap(err, "db: failed to assign all query results to User slice") 356 | } 357 | 358 | if len(userAfterSelectHooks) != 0 { 359 | for _, obj := range o { 360 | if err := obj.doAfterSelectHooks(ctx, exec); err != nil { 361 | return o, err 362 | } 363 | } 364 | } 365 | 366 | return o, nil 367 | } 368 | 369 | // Count returns the count of all User records in the query. 370 | func (q userQuery) Count(ctx context.Context, exec boil.ContextExecutor) (int64, error) { 371 | var count int64 372 | 373 | queries.SetSelect(q.Query, nil) 374 | queries.SetCount(q.Query) 375 | 376 | err := q.Query.QueryRowContext(ctx, exec).Scan(&count) 377 | if err != nil { 378 | return 0, errors.Wrap(err, "db: failed to count users rows") 379 | } 380 | 381 | return count, nil 382 | } 383 | 384 | // Exists checks if the row exists in the table. 385 | func (q userQuery) Exists(ctx context.Context, exec boil.ContextExecutor) (bool, error) { 386 | var count int64 387 | 388 | queries.SetSelect(q.Query, nil) 389 | queries.SetCount(q.Query) 390 | queries.SetLimit(q.Query, 1) 391 | 392 | err := q.Query.QueryRowContext(ctx, exec).Scan(&count) 393 | if err != nil { 394 | return false, errors.Wrap(err, "db: failed to check if users exists") 395 | } 396 | 397 | return count > 0, nil 398 | } 399 | 400 | // AuthorIssues retrieves all the issue's Issues with an executor via author column. 401 | func (o *User) AuthorIssues(mods ...qm.QueryMod) issueQuery { 402 | var queryMods []qm.QueryMod 403 | if len(mods) != 0 { 404 | queryMods = append(queryMods, mods...) 405 | } 406 | 407 | queryMods = append(queryMods, 408 | qm.Where("\"issues\".\"author\"=?", o.ID), 409 | ) 410 | 411 | return Issues(queryMods...) 412 | } 413 | 414 | // OwnerProjects retrieves all the project's Projects with an executor via owner column. 415 | func (o *User) OwnerProjects(mods ...qm.QueryMod) projectQuery { 416 | var queryMods []qm.QueryMod 417 | if len(mods) != 0 { 418 | queryMods = append(queryMods, mods...) 419 | } 420 | 421 | queryMods = append(queryMods, 422 | qm.Where("\"projects\".\"owner\"=?", o.ID), 423 | ) 424 | 425 | return Projects(queryMods...) 426 | } 427 | 428 | // OwnerRepositories retrieves all the repository's Repositories with an executor via owner column. 429 | func (o *User) OwnerRepositories(mods ...qm.QueryMod) repositoryQuery { 430 | var queryMods []qm.QueryMod 431 | if len(mods) != 0 { 432 | queryMods = append(queryMods, mods...) 433 | } 434 | 435 | queryMods = append(queryMods, 436 | qm.Where("\"repositories\".\"owner\"=?", o.ID), 437 | ) 438 | 439 | return Repositories(queryMods...) 440 | } 441 | 442 | // LoadAuthorIssues allows an eager lookup of values, cached into the 443 | // loaded structs of the objects. This is for a 1-M or N-M relationship. 444 | func (userL) LoadAuthorIssues(ctx context.Context, e boil.ContextExecutor, singular bool, maybeUser interface{}, mods queries.Applicator) error { 445 | var slice []*User 446 | var object *User 447 | 448 | if singular { 449 | var ok bool 450 | object, ok = maybeUser.(*User) 451 | if !ok { 452 | object = new(User) 453 | ok = queries.SetFromEmbeddedStruct(&object, &maybeUser) 454 | if !ok { 455 | return errors.New(fmt.Sprintf("failed to set %T from embedded struct %T", object, maybeUser)) 456 | } 457 | } 458 | } else { 459 | s, ok := maybeUser.(*[]*User) 460 | if ok { 461 | slice = *s 462 | } else { 463 | ok = queries.SetFromEmbeddedStruct(&slice, maybeUser) 464 | if !ok { 465 | return errors.New(fmt.Sprintf("failed to set %T from embedded struct %T", slice, maybeUser)) 466 | } 467 | } 468 | } 469 | 470 | args := make([]interface{}, 0, 1) 471 | if singular { 472 | if object.R == nil { 473 | object.R = &userR{} 474 | } 475 | args = append(args, object.ID) 476 | } else { 477 | Outer: 478 | for _, obj := range slice { 479 | if obj.R == nil { 480 | obj.R = &userR{} 481 | } 482 | 483 | for _, a := range args { 484 | if a == obj.ID { 485 | continue Outer 486 | } 487 | } 488 | 489 | args = append(args, obj.ID) 490 | } 491 | } 492 | 493 | if len(args) == 0 { 494 | return nil 495 | } 496 | 497 | query := NewQuery( 498 | qm.From(`issues`), 499 | qm.WhereIn(`issues.author in ?`, args...), 500 | ) 501 | if mods != nil { 502 | mods.Apply(query) 503 | } 504 | 505 | results, err := query.QueryContext(ctx, e) 506 | if err != nil { 507 | return errors.Wrap(err, "failed to eager load issues") 508 | } 509 | 510 | var resultSlice []*Issue 511 | if err = queries.Bind(results, &resultSlice); err != nil { 512 | return errors.Wrap(err, "failed to bind eager loaded slice issues") 513 | } 514 | 515 | if err = results.Close(); err != nil { 516 | return errors.Wrap(err, "failed to close results in eager load on issues") 517 | } 518 | if err = results.Err(); err != nil { 519 | return errors.Wrap(err, "error occurred during iteration of eager loaded relations for issues") 520 | } 521 | 522 | if len(issueAfterSelectHooks) != 0 { 523 | for _, obj := range resultSlice { 524 | if err := obj.doAfterSelectHooks(ctx, e); err != nil { 525 | return err 526 | } 527 | } 528 | } 529 | if singular { 530 | object.R.AuthorIssues = resultSlice 531 | for _, foreign := range resultSlice { 532 | if foreign.R == nil { 533 | foreign.R = &issueR{} 534 | } 535 | foreign.R.AuthorUser = object 536 | } 537 | return nil 538 | } 539 | 540 | for _, foreign := range resultSlice { 541 | for _, local := range slice { 542 | if local.ID == foreign.Author { 543 | local.R.AuthorIssues = append(local.R.AuthorIssues, foreign) 544 | if foreign.R == nil { 545 | foreign.R = &issueR{} 546 | } 547 | foreign.R.AuthorUser = local 548 | break 549 | } 550 | } 551 | } 552 | 553 | return nil 554 | } 555 | 556 | // LoadOwnerProjects allows an eager lookup of values, cached into the 557 | // loaded structs of the objects. This is for a 1-M or N-M relationship. 558 | func (userL) LoadOwnerProjects(ctx context.Context, e boil.ContextExecutor, singular bool, maybeUser interface{}, mods queries.Applicator) error { 559 | var slice []*User 560 | var object *User 561 | 562 | if singular { 563 | var ok bool 564 | object, ok = maybeUser.(*User) 565 | if !ok { 566 | object = new(User) 567 | ok = queries.SetFromEmbeddedStruct(&object, &maybeUser) 568 | if !ok { 569 | return errors.New(fmt.Sprintf("failed to set %T from embedded struct %T", object, maybeUser)) 570 | } 571 | } 572 | } else { 573 | s, ok := maybeUser.(*[]*User) 574 | if ok { 575 | slice = *s 576 | } else { 577 | ok = queries.SetFromEmbeddedStruct(&slice, maybeUser) 578 | if !ok { 579 | return errors.New(fmt.Sprintf("failed to set %T from embedded struct %T", slice, maybeUser)) 580 | } 581 | } 582 | } 583 | 584 | args := make([]interface{}, 0, 1) 585 | if singular { 586 | if object.R == nil { 587 | object.R = &userR{} 588 | } 589 | args = append(args, object.ID) 590 | } else { 591 | Outer: 592 | for _, obj := range slice { 593 | if obj.R == nil { 594 | obj.R = &userR{} 595 | } 596 | 597 | for _, a := range args { 598 | if a == obj.ID { 599 | continue Outer 600 | } 601 | } 602 | 603 | args = append(args, obj.ID) 604 | } 605 | } 606 | 607 | if len(args) == 0 { 608 | return nil 609 | } 610 | 611 | query := NewQuery( 612 | qm.From(`projects`), 613 | qm.WhereIn(`projects.owner in ?`, args...), 614 | ) 615 | if mods != nil { 616 | mods.Apply(query) 617 | } 618 | 619 | results, err := query.QueryContext(ctx, e) 620 | if err != nil { 621 | return errors.Wrap(err, "failed to eager load projects") 622 | } 623 | 624 | var resultSlice []*Project 625 | if err = queries.Bind(results, &resultSlice); err != nil { 626 | return errors.Wrap(err, "failed to bind eager loaded slice projects") 627 | } 628 | 629 | if err = results.Close(); err != nil { 630 | return errors.Wrap(err, "failed to close results in eager load on projects") 631 | } 632 | if err = results.Err(); err != nil { 633 | return errors.Wrap(err, "error occurred during iteration of eager loaded relations for projects") 634 | } 635 | 636 | if len(projectAfterSelectHooks) != 0 { 637 | for _, obj := range resultSlice { 638 | if err := obj.doAfterSelectHooks(ctx, e); err != nil { 639 | return err 640 | } 641 | } 642 | } 643 | if singular { 644 | object.R.OwnerProjects = resultSlice 645 | for _, foreign := range resultSlice { 646 | if foreign.R == nil { 647 | foreign.R = &projectR{} 648 | } 649 | foreign.R.OwnerUser = object 650 | } 651 | return nil 652 | } 653 | 654 | for _, foreign := range resultSlice { 655 | for _, local := range slice { 656 | if local.ID == foreign.Owner { 657 | local.R.OwnerProjects = append(local.R.OwnerProjects, foreign) 658 | if foreign.R == nil { 659 | foreign.R = &projectR{} 660 | } 661 | foreign.R.OwnerUser = local 662 | break 663 | } 664 | } 665 | } 666 | 667 | return nil 668 | } 669 | 670 | // LoadOwnerRepositories allows an eager lookup of values, cached into the 671 | // loaded structs of the objects. This is for a 1-M or N-M relationship. 672 | func (userL) LoadOwnerRepositories(ctx context.Context, e boil.ContextExecutor, singular bool, maybeUser interface{}, mods queries.Applicator) error { 673 | var slice []*User 674 | var object *User 675 | 676 | if singular { 677 | var ok bool 678 | object, ok = maybeUser.(*User) 679 | if !ok { 680 | object = new(User) 681 | ok = queries.SetFromEmbeddedStruct(&object, &maybeUser) 682 | if !ok { 683 | return errors.New(fmt.Sprintf("failed to set %T from embedded struct %T", object, maybeUser)) 684 | } 685 | } 686 | } else { 687 | s, ok := maybeUser.(*[]*User) 688 | if ok { 689 | slice = *s 690 | } else { 691 | ok = queries.SetFromEmbeddedStruct(&slice, maybeUser) 692 | if !ok { 693 | return errors.New(fmt.Sprintf("failed to set %T from embedded struct %T", slice, maybeUser)) 694 | } 695 | } 696 | } 697 | 698 | args := make([]interface{}, 0, 1) 699 | if singular { 700 | if object.R == nil { 701 | object.R = &userR{} 702 | } 703 | args = append(args, object.ID) 704 | } else { 705 | Outer: 706 | for _, obj := range slice { 707 | if obj.R == nil { 708 | obj.R = &userR{} 709 | } 710 | 711 | for _, a := range args { 712 | if a == obj.ID { 713 | continue Outer 714 | } 715 | } 716 | 717 | args = append(args, obj.ID) 718 | } 719 | } 720 | 721 | if len(args) == 0 { 722 | return nil 723 | } 724 | 725 | query := NewQuery( 726 | qm.From(`repositories`), 727 | qm.WhereIn(`repositories.owner in ?`, args...), 728 | ) 729 | if mods != nil { 730 | mods.Apply(query) 731 | } 732 | 733 | results, err := query.QueryContext(ctx, e) 734 | if err != nil { 735 | return errors.Wrap(err, "failed to eager load repositories") 736 | } 737 | 738 | var resultSlice []*Repository 739 | if err = queries.Bind(results, &resultSlice); err != nil { 740 | return errors.Wrap(err, "failed to bind eager loaded slice repositories") 741 | } 742 | 743 | if err = results.Close(); err != nil { 744 | return errors.Wrap(err, "failed to close results in eager load on repositories") 745 | } 746 | if err = results.Err(); err != nil { 747 | return errors.Wrap(err, "error occurred during iteration of eager loaded relations for repositories") 748 | } 749 | 750 | if len(repositoryAfterSelectHooks) != 0 { 751 | for _, obj := range resultSlice { 752 | if err := obj.doAfterSelectHooks(ctx, e); err != nil { 753 | return err 754 | } 755 | } 756 | } 757 | if singular { 758 | object.R.OwnerRepositories = resultSlice 759 | for _, foreign := range resultSlice { 760 | if foreign.R == nil { 761 | foreign.R = &repositoryR{} 762 | } 763 | foreign.R.OwnerUser = object 764 | } 765 | return nil 766 | } 767 | 768 | for _, foreign := range resultSlice { 769 | for _, local := range slice { 770 | if local.ID == foreign.Owner { 771 | local.R.OwnerRepositories = append(local.R.OwnerRepositories, foreign) 772 | if foreign.R == nil { 773 | foreign.R = &repositoryR{} 774 | } 775 | foreign.R.OwnerUser = local 776 | break 777 | } 778 | } 779 | } 780 | 781 | return nil 782 | } 783 | 784 | // AddAuthorIssues adds the given related objects to the existing relationships 785 | // of the user, optionally inserting them as new records. 786 | // Appends related to o.R.AuthorIssues. 787 | // Sets related.R.AuthorUser appropriately. 788 | func (o *User) AddAuthorIssues(ctx context.Context, exec boil.ContextExecutor, insert bool, related ...*Issue) error { 789 | var err error 790 | for _, rel := range related { 791 | if insert { 792 | rel.Author = o.ID 793 | if err = rel.Insert(ctx, exec, boil.Infer()); err != nil { 794 | return errors.Wrap(err, "failed to insert into foreign table") 795 | } 796 | } else { 797 | updateQuery := fmt.Sprintf( 798 | "UPDATE \"issues\" SET %s WHERE %s", 799 | strmangle.SetParamNames("\"", "\"", 0, []string{"author"}), 800 | strmangle.WhereClause("\"", "\"", 0, issuePrimaryKeyColumns), 801 | ) 802 | values := []interface{}{o.ID, rel.ID} 803 | 804 | if boil.IsDebug(ctx) { 805 | writer := boil.DebugWriterFrom(ctx) 806 | fmt.Fprintln(writer, updateQuery) 807 | fmt.Fprintln(writer, values) 808 | } 809 | if _, err = exec.ExecContext(ctx, updateQuery, values...); err != nil { 810 | return errors.Wrap(err, "failed to update foreign table") 811 | } 812 | 813 | rel.Author = o.ID 814 | } 815 | } 816 | 817 | if o.R == nil { 818 | o.R = &userR{ 819 | AuthorIssues: related, 820 | } 821 | } else { 822 | o.R.AuthorIssues = append(o.R.AuthorIssues, related...) 823 | } 824 | 825 | for _, rel := range related { 826 | if rel.R == nil { 827 | rel.R = &issueR{ 828 | AuthorUser: o, 829 | } 830 | } else { 831 | rel.R.AuthorUser = o 832 | } 833 | } 834 | return nil 835 | } 836 | 837 | // AddOwnerProjects adds the given related objects to the existing relationships 838 | // of the user, optionally inserting them as new records. 839 | // Appends related to o.R.OwnerProjects. 840 | // Sets related.R.OwnerUser appropriately. 841 | func (o *User) AddOwnerProjects(ctx context.Context, exec boil.ContextExecutor, insert bool, related ...*Project) error { 842 | var err error 843 | for _, rel := range related { 844 | if insert { 845 | rel.Owner = o.ID 846 | if err = rel.Insert(ctx, exec, boil.Infer()); err != nil { 847 | return errors.Wrap(err, "failed to insert into foreign table") 848 | } 849 | } else { 850 | updateQuery := fmt.Sprintf( 851 | "UPDATE \"projects\" SET %s WHERE %s", 852 | strmangle.SetParamNames("\"", "\"", 0, []string{"owner"}), 853 | strmangle.WhereClause("\"", "\"", 0, projectPrimaryKeyColumns), 854 | ) 855 | values := []interface{}{o.ID, rel.ID} 856 | 857 | if boil.IsDebug(ctx) { 858 | writer := boil.DebugWriterFrom(ctx) 859 | fmt.Fprintln(writer, updateQuery) 860 | fmt.Fprintln(writer, values) 861 | } 862 | if _, err = exec.ExecContext(ctx, updateQuery, values...); err != nil { 863 | return errors.Wrap(err, "failed to update foreign table") 864 | } 865 | 866 | rel.Owner = o.ID 867 | } 868 | } 869 | 870 | if o.R == nil { 871 | o.R = &userR{ 872 | OwnerProjects: related, 873 | } 874 | } else { 875 | o.R.OwnerProjects = append(o.R.OwnerProjects, related...) 876 | } 877 | 878 | for _, rel := range related { 879 | if rel.R == nil { 880 | rel.R = &projectR{ 881 | OwnerUser: o, 882 | } 883 | } else { 884 | rel.R.OwnerUser = o 885 | } 886 | } 887 | return nil 888 | } 889 | 890 | // AddOwnerRepositories adds the given related objects to the existing relationships 891 | // of the user, optionally inserting them as new records. 892 | // Appends related to o.R.OwnerRepositories. 893 | // Sets related.R.OwnerUser appropriately. 894 | func (o *User) AddOwnerRepositories(ctx context.Context, exec boil.ContextExecutor, insert bool, related ...*Repository) error { 895 | var err error 896 | for _, rel := range related { 897 | if insert { 898 | rel.Owner = o.ID 899 | if err = rel.Insert(ctx, exec, boil.Infer()); err != nil { 900 | return errors.Wrap(err, "failed to insert into foreign table") 901 | } 902 | } else { 903 | updateQuery := fmt.Sprintf( 904 | "UPDATE \"repositories\" SET %s WHERE %s", 905 | strmangle.SetParamNames("\"", "\"", 0, []string{"owner"}), 906 | strmangle.WhereClause("\"", "\"", 0, repositoryPrimaryKeyColumns), 907 | ) 908 | values := []interface{}{o.ID, rel.ID} 909 | 910 | if boil.IsDebug(ctx) { 911 | writer := boil.DebugWriterFrom(ctx) 912 | fmt.Fprintln(writer, updateQuery) 913 | fmt.Fprintln(writer, values) 914 | } 915 | if _, err = exec.ExecContext(ctx, updateQuery, values...); err != nil { 916 | return errors.Wrap(err, "failed to update foreign table") 917 | } 918 | 919 | rel.Owner = o.ID 920 | } 921 | } 922 | 923 | if o.R == nil { 924 | o.R = &userR{ 925 | OwnerRepositories: related, 926 | } 927 | } else { 928 | o.R.OwnerRepositories = append(o.R.OwnerRepositories, related...) 929 | } 930 | 931 | for _, rel := range related { 932 | if rel.R == nil { 933 | rel.R = &repositoryR{ 934 | OwnerUser: o, 935 | } 936 | } else { 937 | rel.R.OwnerUser = o 938 | } 939 | } 940 | return nil 941 | } 942 | 943 | // Users retrieves all the records using an executor. 944 | func Users(mods ...qm.QueryMod) userQuery { 945 | mods = append(mods, qm.From("\"users\"")) 946 | q := NewQuery(mods...) 947 | if len(queries.GetSelect(q)) == 0 { 948 | queries.SetSelect(q, []string{"\"users\".*"}) 949 | } 950 | 951 | return userQuery{q} 952 | } 953 | 954 | // FindUser retrieves a single record by ID with an executor. 955 | // If selectCols is empty Find will return all columns. 956 | func FindUser(ctx context.Context, exec boil.ContextExecutor, iD string, selectCols ...string) (*User, error) { 957 | userObj := &User{} 958 | 959 | sel := "*" 960 | if len(selectCols) > 0 { 961 | sel = strings.Join(strmangle.IdentQuoteSlice(dialect.LQ, dialect.RQ, selectCols), ",") 962 | } 963 | query := fmt.Sprintf( 964 | "select %s from \"users\" where \"id\"=?", sel, 965 | ) 966 | 967 | q := queries.Raw(query, iD) 968 | 969 | err := q.Bind(ctx, exec, userObj) 970 | if err != nil { 971 | if errors.Is(err, sql.ErrNoRows) { 972 | return nil, sql.ErrNoRows 973 | } 974 | return nil, errors.Wrap(err, "db: unable to select from users") 975 | } 976 | 977 | if err = userObj.doAfterSelectHooks(ctx, exec); err != nil { 978 | return userObj, err 979 | } 980 | 981 | return userObj, nil 982 | } 983 | 984 | // Insert a single record using an executor. 985 | // See boil.Columns.InsertColumnSet documentation to understand column list inference for inserts. 986 | func (o *User) Insert(ctx context.Context, exec boil.ContextExecutor, columns boil.Columns) error { 987 | if o == nil { 988 | return errors.New("db: no users provided for insertion") 989 | } 990 | 991 | var err error 992 | 993 | if err := o.doBeforeInsertHooks(ctx, exec); err != nil { 994 | return err 995 | } 996 | 997 | nzDefaults := queries.NonZeroDefaultSet(userColumnsWithDefault, o) 998 | 999 | key := makeCacheKey(columns, nzDefaults) 1000 | userInsertCacheMut.RLock() 1001 | cache, cached := userInsertCache[key] 1002 | userInsertCacheMut.RUnlock() 1003 | 1004 | if !cached { 1005 | wl, returnColumns := columns.InsertColumnSet( 1006 | userAllColumns, 1007 | userColumnsWithDefault, 1008 | userColumnsWithoutDefault, 1009 | nzDefaults, 1010 | ) 1011 | 1012 | cache.valueMapping, err = queries.BindMapping(userType, userMapping, wl) 1013 | if err != nil { 1014 | return err 1015 | } 1016 | cache.retMapping, err = queries.BindMapping(userType, userMapping, returnColumns) 1017 | if err != nil { 1018 | return err 1019 | } 1020 | if len(wl) != 0 { 1021 | cache.query = fmt.Sprintf("INSERT INTO \"users\" (\"%s\") %%sVALUES (%s)%%s", strings.Join(wl, "\",\""), strmangle.Placeholders(dialect.UseIndexPlaceholders, len(wl), 1, 1)) 1022 | } else { 1023 | cache.query = "INSERT INTO \"users\" %sDEFAULT VALUES%s" 1024 | } 1025 | 1026 | var queryOutput, queryReturning string 1027 | 1028 | if len(cache.retMapping) != 0 { 1029 | queryReturning = fmt.Sprintf(" RETURNING \"%s\"", strings.Join(returnColumns, "\",\"")) 1030 | } 1031 | 1032 | cache.query = fmt.Sprintf(cache.query, queryOutput, queryReturning) 1033 | } 1034 | 1035 | value := reflect.Indirect(reflect.ValueOf(o)) 1036 | vals := queries.ValuesFromMapping(value, cache.valueMapping) 1037 | 1038 | if boil.IsDebug(ctx) { 1039 | writer := boil.DebugWriterFrom(ctx) 1040 | fmt.Fprintln(writer, cache.query) 1041 | fmt.Fprintln(writer, vals) 1042 | } 1043 | 1044 | if len(cache.retMapping) != 0 { 1045 | err = exec.QueryRowContext(ctx, cache.query, vals...).Scan(queries.PtrsFromMapping(value, cache.retMapping)...) 1046 | } else { 1047 | _, err = exec.ExecContext(ctx, cache.query, vals...) 1048 | } 1049 | 1050 | if err != nil { 1051 | return errors.Wrap(err, "db: unable to insert into users") 1052 | } 1053 | 1054 | if !cached { 1055 | userInsertCacheMut.Lock() 1056 | userInsertCache[key] = cache 1057 | userInsertCacheMut.Unlock() 1058 | } 1059 | 1060 | return o.doAfterInsertHooks(ctx, exec) 1061 | } 1062 | 1063 | // Update uses an executor to update the User. 1064 | // See boil.Columns.UpdateColumnSet documentation to understand column list inference for updates. 1065 | // Update does not automatically update the record in case of default values. Use .Reload() to refresh the records. 1066 | func (o *User) Update(ctx context.Context, exec boil.ContextExecutor, columns boil.Columns) (int64, error) { 1067 | var err error 1068 | if err = o.doBeforeUpdateHooks(ctx, exec); err != nil { 1069 | return 0, err 1070 | } 1071 | key := makeCacheKey(columns, nil) 1072 | userUpdateCacheMut.RLock() 1073 | cache, cached := userUpdateCache[key] 1074 | userUpdateCacheMut.RUnlock() 1075 | 1076 | if !cached { 1077 | wl := columns.UpdateColumnSet( 1078 | userAllColumns, 1079 | userPrimaryKeyColumns, 1080 | ) 1081 | 1082 | if !columns.IsWhitelist() { 1083 | wl = strmangle.SetComplement(wl, []string{"created_at"}) 1084 | } 1085 | if len(wl) == 0 { 1086 | return 0, errors.New("db: unable to update users, could not build whitelist") 1087 | } 1088 | 1089 | cache.query = fmt.Sprintf("UPDATE \"users\" SET %s WHERE %s", 1090 | strmangle.SetParamNames("\"", "\"", 0, wl), 1091 | strmangle.WhereClause("\"", "\"", 0, userPrimaryKeyColumns), 1092 | ) 1093 | cache.valueMapping, err = queries.BindMapping(userType, userMapping, append(wl, userPrimaryKeyColumns...)) 1094 | if err != nil { 1095 | return 0, err 1096 | } 1097 | } 1098 | 1099 | values := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), cache.valueMapping) 1100 | 1101 | if boil.IsDebug(ctx) { 1102 | writer := boil.DebugWriterFrom(ctx) 1103 | fmt.Fprintln(writer, cache.query) 1104 | fmt.Fprintln(writer, values) 1105 | } 1106 | var result sql.Result 1107 | result, err = exec.ExecContext(ctx, cache.query, values...) 1108 | if err != nil { 1109 | return 0, errors.Wrap(err, "db: unable to update users row") 1110 | } 1111 | 1112 | rowsAff, err := result.RowsAffected() 1113 | if err != nil { 1114 | return 0, errors.Wrap(err, "db: failed to get rows affected by update for users") 1115 | } 1116 | 1117 | if !cached { 1118 | userUpdateCacheMut.Lock() 1119 | userUpdateCache[key] = cache 1120 | userUpdateCacheMut.Unlock() 1121 | } 1122 | 1123 | return rowsAff, o.doAfterUpdateHooks(ctx, exec) 1124 | } 1125 | 1126 | // UpdateAll updates all rows with the specified column values. 1127 | func (q userQuery) UpdateAll(ctx context.Context, exec boil.ContextExecutor, cols M) (int64, error) { 1128 | queries.SetUpdate(q.Query, cols) 1129 | 1130 | result, err := q.Query.ExecContext(ctx, exec) 1131 | if err != nil { 1132 | return 0, errors.Wrap(err, "db: unable to update all for users") 1133 | } 1134 | 1135 | rowsAff, err := result.RowsAffected() 1136 | if err != nil { 1137 | return 0, errors.Wrap(err, "db: unable to retrieve rows affected for users") 1138 | } 1139 | 1140 | return rowsAff, nil 1141 | } 1142 | 1143 | // UpdateAll updates all rows with the specified column values, using an executor. 1144 | func (o UserSlice) UpdateAll(ctx context.Context, exec boil.ContextExecutor, cols M) (int64, error) { 1145 | ln := int64(len(o)) 1146 | if ln == 0 { 1147 | return 0, nil 1148 | } 1149 | 1150 | if len(cols) == 0 { 1151 | return 0, errors.New("db: update all requires at least one column argument") 1152 | } 1153 | 1154 | colNames := make([]string, len(cols)) 1155 | args := make([]interface{}, len(cols)) 1156 | 1157 | i := 0 1158 | for name, value := range cols { 1159 | colNames[i] = name 1160 | args[i] = value 1161 | i++ 1162 | } 1163 | 1164 | // Append all of the primary key values for each column 1165 | for _, obj := range o { 1166 | pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), userPrimaryKeyMapping) 1167 | args = append(args, pkeyArgs...) 1168 | } 1169 | 1170 | sql := fmt.Sprintf("UPDATE \"users\" SET %s WHERE %s", 1171 | strmangle.SetParamNames("\"", "\"", 0, colNames), 1172 | strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 0, userPrimaryKeyColumns, len(o))) 1173 | 1174 | if boil.IsDebug(ctx) { 1175 | writer := boil.DebugWriterFrom(ctx) 1176 | fmt.Fprintln(writer, sql) 1177 | fmt.Fprintln(writer, args...) 1178 | } 1179 | result, err := exec.ExecContext(ctx, sql, args...) 1180 | if err != nil { 1181 | return 0, errors.Wrap(err, "db: unable to update all in user slice") 1182 | } 1183 | 1184 | rowsAff, err := result.RowsAffected() 1185 | if err != nil { 1186 | return 0, errors.Wrap(err, "db: unable to retrieve rows affected all in update all user") 1187 | } 1188 | return rowsAff, nil 1189 | } 1190 | 1191 | // Upsert attempts an insert using an executor, and does an update or ignore on conflict. 1192 | // See boil.Columns documentation for how to properly use updateColumns and insertColumns. 1193 | func (o *User) Upsert(ctx context.Context, exec boil.ContextExecutor, updateOnConflict bool, conflictColumns []string, updateColumns, insertColumns boil.Columns) error { 1194 | if o == nil { 1195 | return errors.New("db: no users provided for upsert") 1196 | } 1197 | 1198 | if err := o.doBeforeUpsertHooks(ctx, exec); err != nil { 1199 | return err 1200 | } 1201 | 1202 | nzDefaults := queries.NonZeroDefaultSet(userColumnsWithDefault, o) 1203 | 1204 | // Build cache key in-line uglily - mysql vs psql problems 1205 | buf := strmangle.GetBuffer() 1206 | if updateOnConflict { 1207 | buf.WriteByte('t') 1208 | } else { 1209 | buf.WriteByte('f') 1210 | } 1211 | buf.WriteByte('.') 1212 | for _, c := range conflictColumns { 1213 | buf.WriteString(c) 1214 | } 1215 | buf.WriteByte('.') 1216 | buf.WriteString(strconv.Itoa(updateColumns.Kind)) 1217 | for _, c := range updateColumns.Cols { 1218 | buf.WriteString(c) 1219 | } 1220 | buf.WriteByte('.') 1221 | buf.WriteString(strconv.Itoa(insertColumns.Kind)) 1222 | for _, c := range insertColumns.Cols { 1223 | buf.WriteString(c) 1224 | } 1225 | buf.WriteByte('.') 1226 | for _, c := range nzDefaults { 1227 | buf.WriteString(c) 1228 | } 1229 | key := buf.String() 1230 | strmangle.PutBuffer(buf) 1231 | 1232 | userUpsertCacheMut.RLock() 1233 | cache, cached := userUpsertCache[key] 1234 | userUpsertCacheMut.RUnlock() 1235 | 1236 | var err error 1237 | 1238 | if !cached { 1239 | insert, ret := insertColumns.InsertColumnSet( 1240 | userAllColumns, 1241 | userColumnsWithDefault, 1242 | userColumnsWithoutDefault, 1243 | nzDefaults, 1244 | ) 1245 | update := updateColumns.UpdateColumnSet( 1246 | userAllColumns, 1247 | userPrimaryKeyColumns, 1248 | ) 1249 | 1250 | if updateOnConflict && len(update) == 0 { 1251 | return errors.New("db: unable to upsert users, could not build update column list") 1252 | } 1253 | 1254 | conflict := conflictColumns 1255 | if len(conflict) == 0 { 1256 | conflict = make([]string, len(userPrimaryKeyColumns)) 1257 | copy(conflict, userPrimaryKeyColumns) 1258 | } 1259 | cache.query = buildUpsertQuerySQLite(dialect, "\"users\"", updateOnConflict, ret, update, conflict, insert) 1260 | 1261 | cache.valueMapping, err = queries.BindMapping(userType, userMapping, insert) 1262 | if err != nil { 1263 | return err 1264 | } 1265 | if len(ret) != 0 { 1266 | cache.retMapping, err = queries.BindMapping(userType, userMapping, ret) 1267 | if err != nil { 1268 | return err 1269 | } 1270 | } 1271 | } 1272 | 1273 | value := reflect.Indirect(reflect.ValueOf(o)) 1274 | vals := queries.ValuesFromMapping(value, cache.valueMapping) 1275 | var returns []interface{} 1276 | if len(cache.retMapping) != 0 { 1277 | returns = queries.PtrsFromMapping(value, cache.retMapping) 1278 | } 1279 | 1280 | if boil.IsDebug(ctx) { 1281 | writer := boil.DebugWriterFrom(ctx) 1282 | fmt.Fprintln(writer, cache.query) 1283 | fmt.Fprintln(writer, vals) 1284 | } 1285 | if len(cache.retMapping) != 0 { 1286 | err = exec.QueryRowContext(ctx, cache.query, vals...).Scan(returns...) 1287 | if errors.Is(err, sql.ErrNoRows) { 1288 | err = nil // Postgres doesn't return anything when there's no update 1289 | } 1290 | } else { 1291 | _, err = exec.ExecContext(ctx, cache.query, vals...) 1292 | } 1293 | if err != nil { 1294 | return errors.Wrap(err, "db: unable to upsert users") 1295 | } 1296 | 1297 | if !cached { 1298 | userUpsertCacheMut.Lock() 1299 | userUpsertCache[key] = cache 1300 | userUpsertCacheMut.Unlock() 1301 | } 1302 | 1303 | return o.doAfterUpsertHooks(ctx, exec) 1304 | } 1305 | 1306 | // Delete deletes a single User record with an executor. 1307 | // Delete will match against the primary key column to find the record to delete. 1308 | func (o *User) Delete(ctx context.Context, exec boil.ContextExecutor) (int64, error) { 1309 | if o == nil { 1310 | return 0, errors.New("db: no User provided for delete") 1311 | } 1312 | 1313 | if err := o.doBeforeDeleteHooks(ctx, exec); err != nil { 1314 | return 0, err 1315 | } 1316 | 1317 | args := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), userPrimaryKeyMapping) 1318 | sql := "DELETE FROM \"users\" WHERE \"id\"=?" 1319 | 1320 | if boil.IsDebug(ctx) { 1321 | writer := boil.DebugWriterFrom(ctx) 1322 | fmt.Fprintln(writer, sql) 1323 | fmt.Fprintln(writer, args...) 1324 | } 1325 | result, err := exec.ExecContext(ctx, sql, args...) 1326 | if err != nil { 1327 | return 0, errors.Wrap(err, "db: unable to delete from users") 1328 | } 1329 | 1330 | rowsAff, err := result.RowsAffected() 1331 | if err != nil { 1332 | return 0, errors.Wrap(err, "db: failed to get rows affected by delete for users") 1333 | } 1334 | 1335 | if err := o.doAfterDeleteHooks(ctx, exec); err != nil { 1336 | return 0, err 1337 | } 1338 | 1339 | return rowsAff, nil 1340 | } 1341 | 1342 | // DeleteAll deletes all matching rows. 1343 | func (q userQuery) DeleteAll(ctx context.Context, exec boil.ContextExecutor) (int64, error) { 1344 | if q.Query == nil { 1345 | return 0, errors.New("db: no userQuery provided for delete all") 1346 | } 1347 | 1348 | queries.SetDelete(q.Query) 1349 | 1350 | result, err := q.Query.ExecContext(ctx, exec) 1351 | if err != nil { 1352 | return 0, errors.Wrap(err, "db: unable to delete all from users") 1353 | } 1354 | 1355 | rowsAff, err := result.RowsAffected() 1356 | if err != nil { 1357 | return 0, errors.Wrap(err, "db: failed to get rows affected by deleteall for users") 1358 | } 1359 | 1360 | return rowsAff, nil 1361 | } 1362 | 1363 | // DeleteAll deletes all rows in the slice, using an executor. 1364 | func (o UserSlice) DeleteAll(ctx context.Context, exec boil.ContextExecutor) (int64, error) { 1365 | if len(o) == 0 { 1366 | return 0, nil 1367 | } 1368 | 1369 | if len(userBeforeDeleteHooks) != 0 { 1370 | for _, obj := range o { 1371 | if err := obj.doBeforeDeleteHooks(ctx, exec); err != nil { 1372 | return 0, err 1373 | } 1374 | } 1375 | } 1376 | 1377 | var args []interface{} 1378 | for _, obj := range o { 1379 | pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), userPrimaryKeyMapping) 1380 | args = append(args, pkeyArgs...) 1381 | } 1382 | 1383 | sql := "DELETE FROM \"users\" WHERE " + 1384 | strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 0, userPrimaryKeyColumns, len(o)) 1385 | 1386 | if boil.IsDebug(ctx) { 1387 | writer := boil.DebugWriterFrom(ctx) 1388 | fmt.Fprintln(writer, sql) 1389 | fmt.Fprintln(writer, args) 1390 | } 1391 | result, err := exec.ExecContext(ctx, sql, args...) 1392 | if err != nil { 1393 | return 0, errors.Wrap(err, "db: unable to delete all from user slice") 1394 | } 1395 | 1396 | rowsAff, err := result.RowsAffected() 1397 | if err != nil { 1398 | return 0, errors.Wrap(err, "db: failed to get rows affected by deleteall for users") 1399 | } 1400 | 1401 | if len(userAfterDeleteHooks) != 0 { 1402 | for _, obj := range o { 1403 | if err := obj.doAfterDeleteHooks(ctx, exec); err != nil { 1404 | return 0, err 1405 | } 1406 | } 1407 | } 1408 | 1409 | return rowsAff, nil 1410 | } 1411 | 1412 | // Reload refetches the object from the database 1413 | // using the primary keys with an executor. 1414 | func (o *User) Reload(ctx context.Context, exec boil.ContextExecutor) error { 1415 | ret, err := FindUser(ctx, exec, o.ID) 1416 | if err != nil { 1417 | return err 1418 | } 1419 | 1420 | *o = *ret 1421 | return nil 1422 | } 1423 | 1424 | // ReloadAll refetches every row with matching primary key column values 1425 | // and overwrites the original object slice with the newly updated slice. 1426 | func (o *UserSlice) ReloadAll(ctx context.Context, exec boil.ContextExecutor) error { 1427 | if o == nil || len(*o) == 0 { 1428 | return nil 1429 | } 1430 | 1431 | slice := UserSlice{} 1432 | var args []interface{} 1433 | for _, obj := range *o { 1434 | pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), userPrimaryKeyMapping) 1435 | args = append(args, pkeyArgs...) 1436 | } 1437 | 1438 | sql := "SELECT \"users\".* FROM \"users\" WHERE " + 1439 | strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 0, userPrimaryKeyColumns, len(*o)) 1440 | 1441 | q := queries.Raw(sql, args...) 1442 | 1443 | err := q.Bind(ctx, exec, &slice) 1444 | if err != nil { 1445 | return errors.Wrap(err, "db: unable to reload all in UserSlice") 1446 | } 1447 | 1448 | *o = slice 1449 | 1450 | return nil 1451 | } 1452 | 1453 | // UserExists checks if the User row exists. 1454 | func UserExists(ctx context.Context, exec boil.ContextExecutor, iD string) (bool, error) { 1455 | var exists bool 1456 | sql := "select exists(select 1 from \"users\" where \"id\"=? limit 1)" 1457 | 1458 | if boil.IsDebug(ctx) { 1459 | writer := boil.DebugWriterFrom(ctx) 1460 | fmt.Fprintln(writer, sql) 1461 | fmt.Fprintln(writer, iD) 1462 | } 1463 | row := exec.QueryRowContext(ctx, sql, iD) 1464 | 1465 | err := row.Scan(&exists) 1466 | if err != nil { 1467 | return false, errors.Wrap(err, "db: unable to check if users exists") 1468 | } 1469 | 1470 | return exists, nil 1471 | } 1472 | 1473 | // Exists checks if the User row exists. 1474 | func (o *User) Exists(ctx context.Context, exec boil.ContextExecutor) (bool, error) { 1475 | return UserExists(ctx, exec, o.ID) 1476 | } 1477 | -------------------------------------------------------------------------------- /graph/directive.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/99designs/gqlgen/graphql" 8 | "github.com/saki-engineering/graphql-sample/internal" 9 | "github.com/saki-engineering/graphql-sample/middlewares/auth" 10 | ) 11 | 12 | var Directive internal.DirectiveRoot = internal.DirectiveRoot{ 13 | IsAuthenticated: IsAuthenticated, 14 | } 15 | 16 | func IsAuthenticated(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) { 17 | if _, ok := auth.GetUserName(ctx); !ok { 18 | return nil, errors.New("not authenticated") 19 | } 20 | return next(ctx) 21 | } 22 | -------------------------------------------------------------------------------- /graph/model/models_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/99designs/gqlgen, DO NOT EDIT. 2 | 3 | package model 4 | 5 | import ( 6 | "net/url" 7 | "time" 8 | ) 9 | 10 | type Node interface { 11 | IsNode() 12 | GetID() string 13 | } 14 | 15 | type ProjectV2ItemContent interface { 16 | IsProjectV2ItemContent() 17 | } 18 | 19 | type AddProjectV2ItemByIDInput struct { 20 | ContentID string `json:"contentId"` 21 | ProjectID string `json:"projectId"` 22 | } 23 | 24 | type AddProjectV2ItemByIDPayload struct { 25 | Item *ProjectV2Item `json:"item"` 26 | } 27 | 28 | type Issue struct { 29 | ID string `json:"id"` 30 | URL url.URL `json:"url"` 31 | Title string `json:"title"` 32 | Closed bool `json:"closed"` 33 | Number int `json:"number"` 34 | Author *User `json:"author"` 35 | Repository *Repository `json:"repository"` 36 | ProjectItems *ProjectV2ItemConnection `json:"projectItems"` 37 | } 38 | 39 | func (Issue) IsNode() {} 40 | func (this Issue) GetID() string { return this.ID } 41 | 42 | func (Issue) IsProjectV2ItemContent() {} 43 | 44 | type IssueConnection struct { 45 | Edges []*IssueEdge `json:"edges"` 46 | Nodes []*Issue `json:"nodes"` 47 | PageInfo *PageInfo `json:"pageInfo"` 48 | TotalCount int `json:"totalCount"` 49 | } 50 | 51 | type IssueEdge struct { 52 | Cursor string `json:"cursor"` 53 | Node *Issue `json:"node"` 54 | } 55 | 56 | type PageInfo struct { 57 | EndCursor *string `json:"endCursor"` 58 | HasNextPage bool `json:"hasNextPage"` 59 | HasPreviousPage bool `json:"hasPreviousPage"` 60 | StartCursor *string `json:"startCursor"` 61 | } 62 | 63 | type ProjectV2 struct { 64 | ID string `json:"id"` 65 | Title string `json:"title"` 66 | URL url.URL `json:"url"` 67 | Number int `json:"number"` 68 | Items *ProjectV2ItemConnection `json:"items"` 69 | Owner *User `json:"owner"` 70 | } 71 | 72 | func (ProjectV2) IsNode() {} 73 | func (this ProjectV2) GetID() string { return this.ID } 74 | 75 | type ProjectV2Connection struct { 76 | Edges []*ProjectV2Edge `json:"edges"` 77 | Nodes []*ProjectV2 `json:"nodes"` 78 | PageInfo *PageInfo `json:"pageInfo"` 79 | TotalCount int `json:"totalCount"` 80 | } 81 | 82 | type ProjectV2Edge struct { 83 | Cursor string `json:"cursor"` 84 | Node *ProjectV2 `json:"node"` 85 | } 86 | 87 | type ProjectV2Item struct { 88 | ID string `json:"id"` 89 | Project *ProjectV2 `json:"project"` 90 | Content ProjectV2ItemContent `json:"content"` 91 | } 92 | 93 | func (ProjectV2Item) IsNode() {} 94 | func (this ProjectV2Item) GetID() string { return this.ID } 95 | 96 | type ProjectV2ItemConnection struct { 97 | Edges []*ProjectV2ItemEdge `json:"edges"` 98 | Nodes []*ProjectV2Item `json:"nodes"` 99 | PageInfo *PageInfo `json:"pageInfo"` 100 | TotalCount int `json:"totalCount"` 101 | } 102 | 103 | type ProjectV2ItemEdge struct { 104 | Cursor string `json:"cursor"` 105 | Node *ProjectV2Item `json:"node"` 106 | } 107 | 108 | type PullRequest struct { 109 | ID string `json:"id"` 110 | BaseRefName string `json:"baseRefName"` 111 | Closed bool `json:"closed"` 112 | HeadRefName string `json:"headRefName"` 113 | URL url.URL `json:"url"` 114 | Number int `json:"number"` 115 | Repository *Repository `json:"repository"` 116 | ProjectItems *ProjectV2ItemConnection `json:"projectItems"` 117 | } 118 | 119 | func (PullRequest) IsNode() {} 120 | func (this PullRequest) GetID() string { return this.ID } 121 | 122 | func (PullRequest) IsProjectV2ItemContent() {} 123 | 124 | type PullRequestConnection struct { 125 | Edges []*PullRequestEdge `json:"edges"` 126 | Nodes []*PullRequest `json:"nodes"` 127 | PageInfo *PageInfo `json:"pageInfo"` 128 | TotalCount int `json:"totalCount"` 129 | } 130 | 131 | type PullRequestEdge struct { 132 | Cursor string `json:"cursor"` 133 | Node *PullRequest `json:"node"` 134 | } 135 | 136 | type Repository struct { 137 | ID string `json:"id"` 138 | Owner *User `json:"owner"` 139 | Name string `json:"name"` 140 | CreatedAt time.Time `json:"createdAt"` 141 | Issue *Issue `json:"issue"` 142 | Issues *IssueConnection `json:"issues"` 143 | PullRequest *PullRequest `json:"pullRequest"` 144 | PullRequests *PullRequestConnection `json:"pullRequests"` 145 | } 146 | 147 | func (Repository) IsNode() {} 148 | func (this Repository) GetID() string { return this.ID } 149 | 150 | type User struct { 151 | ID string `json:"id"` 152 | Name string `json:"name"` 153 | ProjectV2 *ProjectV2 `json:"projectV2"` 154 | ProjectV2s *ProjectV2Connection `json:"projectV2s"` 155 | } 156 | 157 | func (User) IsNode() {} 158 | func (this User) GetID() string { return this.ID } 159 | -------------------------------------------------------------------------------- /graph/model/mymodel.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/url" 7 | 8 | "github.com/99designs/gqlgen/graphql" 9 | ) 10 | 11 | func MarshalURI(u url.URL) graphql.Marshaler { 12 | return graphql.WriterFunc(func(w io.Writer) { 13 | io.WriteString(w, fmt.Sprintf(`"%s"`, u.String())) 14 | }) 15 | } 16 | 17 | func UnmarshalURI(v interface{}) (url.URL, error) { 18 | switch v := v.(type) { 19 | case string: 20 | u, err := url.Parse(v) 21 | if err != nil { 22 | return url.URL{}, err 23 | } 24 | return *u, nil 25 | case []byte: 26 | u := &url.URL{} 27 | if err := u.UnmarshalBinary(v); err != nil { 28 | return url.URL{}, err 29 | } 30 | return *u, nil 31 | default: 32 | return url.URL{}, fmt.Errorf("%T is not a url.URL", v) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /graph/resolver.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import "github.com/saki-engineering/graphql-sample/graph/services" 4 | 5 | // This file will not be regenerated automatically. 6 | // 7 | // It serves as dependency injection for your app, add any dependencies you require here. 8 | 9 | type Resolver struct { 10 | Srv services.Services 11 | *Loaders 12 | } 13 | -------------------------------------------------------------------------------- /graph/schema.resolvers.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | // This file will be automatically regenerated based on the schema, any resolver implementations 4 | // will be copied through when generating and any unknown code will be moved to the end. 5 | // Code generated by github.com/99designs/gqlgen version v0.17.22 6 | 7 | import ( 8 | "context" 9 | "errors" 10 | "strings" 11 | 12 | "github.com/saki-engineering/graphql-sample/graph/model" 13 | "github.com/saki-engineering/graphql-sample/internal" 14 | ) 15 | 16 | // Author is the resolver for the author field. 17 | func (r *issueResolver) Author(ctx context.Context, obj *model.Issue) (*model.User, error) { 18 | // 1. Loaderに登録(この時点では即時実行されない) 19 | thunk := r.Loaders.UserLoader.Load(ctx, obj.Author.ID) 20 | // 2. Loaderが実行するまで待って、結果を受け取る 21 | user, err := thunk() 22 | if err != nil { 23 | return nil, err 24 | } 25 | return user, nil 26 | 27 | // N+1問題対処前 28 | // return r.Srv.GetUserByID(ctx, obj.Author.ID) 29 | } 30 | 31 | // Repository is the resolver for the repository field. 32 | func (r *issueResolver) Repository(ctx context.Context, obj *model.Issue) (*model.Repository, error) { 33 | return r.Srv.GetRepoByID(ctx, obj.Repository.ID) 34 | } 35 | 36 | // ProjectItems is the resolver for the projectItems field. 37 | func (r *issueResolver) ProjectItems(ctx context.Context, obj *model.Issue, after *string, before *string, first *int, last *int) (*model.ProjectV2ItemConnection, error) { 38 | return r.Srv.ListProjectItemOwnedByIssue(ctx, obj.ID, after, before, first, last) 39 | } 40 | 41 | // AddProjectV2ItemByID is the resolver for the addProjectV2ItemById field. 42 | func (r *mutationResolver) AddProjectV2ItemByID(ctx context.Context, input model.AddProjectV2ItemByIDInput) (*model.AddProjectV2ItemByIDPayload, error) { 43 | nElems := strings.SplitN(input.ContentID, "_", 2) 44 | nType, _ := nElems[0], nElems[1] 45 | 46 | switch nType { 47 | case "ISSUE": 48 | item, err := r.Srv.AddIssueInProjectV2(ctx, input.ProjectID, input.ContentID) 49 | if err != nil { 50 | return nil, err 51 | } 52 | return &model.AddProjectV2ItemByIDPayload{ 53 | Item: item, 54 | }, nil 55 | case "PR": 56 | item, err := r.Srv.AddPullRequestInProjectV2(ctx, input.ProjectID, input.ContentID) 57 | if err != nil { 58 | return nil, err 59 | } 60 | return &model.AddProjectV2ItemByIDPayload{ 61 | Item: item, 62 | }, nil 63 | default: 64 | return nil, errors.New("invalid content id") 65 | } 66 | } 67 | 68 | // Items is the resolver for the items field. 69 | func (r *projectV2Resolver) Items(ctx context.Context, obj *model.ProjectV2, after *string, before *string, first *int, last *int) (*model.ProjectV2ItemConnection, error) { 70 | return r.Srv.ListProjectItemOwnedByProject(ctx, obj.ID, after, before, first, last) 71 | } 72 | 73 | // Owner is the resolver for the owner field. 74 | func (r *projectV2Resolver) Owner(ctx context.Context, obj *model.ProjectV2) (*model.User, error) { 75 | return r.Srv.GetUserByID(ctx, obj.Owner.ID) 76 | } 77 | 78 | // Content is the resolver for the content field. 79 | func (r *projectV2ItemResolver) Content(ctx context.Context, obj *model.ProjectV2Item) (model.ProjectV2ItemContent, error) { 80 | item, err := r.Srv.GetProjectItemByID(ctx, obj.ID) 81 | if err != nil { 82 | return nil, err 83 | } 84 | 85 | switch content := item.Content.(type) { 86 | case *model.Issue: 87 | return r.Srv.GetIssueByID(ctx, content.ID) 88 | case *model.PullRequest: 89 | return r.Srv.GetPullRequestByID(ctx, content.ID) 90 | default: 91 | return nil, errors.New("invalid ProjectV2 Item") 92 | } 93 | } 94 | 95 | // Repository is the resolver for the repository field. 96 | func (r *pullRequestResolver) Repository(ctx context.Context, obj *model.PullRequest) (*model.Repository, error) { 97 | return r.Srv.GetRepoByID(ctx, obj.Repository.ID) 98 | } 99 | 100 | // ProjectItems is the resolver for the projectItems field. 101 | func (r *pullRequestResolver) ProjectItems(ctx context.Context, obj *model.PullRequest, after *string, before *string, first *int, last *int) (*model.ProjectV2ItemConnection, error) { 102 | return r.Srv.ListProjectItemOwnedByPullRequest(ctx, obj.ID, after, before, first, last) 103 | } 104 | 105 | // Repository is the resolver for the repository field. 106 | func (r *queryResolver) Repository(ctx context.Context, name string, owner string) (*model.Repository, error) { 107 | user, err := r.Srv.GetUserByName(ctx, owner) 108 | if err != nil { 109 | return nil, err 110 | } 111 | return r.Srv.GetRepoByFullName(ctx, user.ID, name) 112 | } 113 | 114 | // User is the resolver for the user field. 115 | func (r *queryResolver) User(ctx context.Context, name string) (*model.User, error) { 116 | return r.Srv.GetUserByName(ctx, name) 117 | } 118 | 119 | // Node is the resolver for the node field. 120 | func (r *queryResolver) Node(ctx context.Context, id string) (model.Node, error) { 121 | nElems := strings.SplitN(id, "_", 2) 122 | nType, _ := nElems[0], nElems[1] 123 | 124 | switch nType { 125 | case "U": 126 | return r.Srv.GetUserByID(ctx, id) 127 | case "REPO": 128 | return r.Srv.GetRepoByID(ctx, id) 129 | case "ISSUE": 130 | return r.Srv.GetIssueByID(ctx, id) 131 | case "PJ": 132 | return r.Srv.GetProjectByID(ctx, id) 133 | case "PR": 134 | return r.Srv.GetPullRequestByID(ctx, id) 135 | default: 136 | return nil, errors.New("invalid ID") 137 | } 138 | } 139 | 140 | // Owner is the resolver for the owner field. 141 | func (r *repositoryResolver) Owner(ctx context.Context, obj *model.Repository) (*model.User, error) { 142 | return r.Srv.GetUserByID(ctx, obj.Owner.ID) 143 | } 144 | 145 | // Issue is the resolver for the issue field. 146 | func (r *repositoryResolver) Issue(ctx context.Context, obj *model.Repository, number int) (*model.Issue, error) { 147 | return r.Srv.GetIssueByRepoAndNumber(ctx, obj.ID, number) 148 | } 149 | 150 | // Issues is the resolver for the issues field. 151 | func (r *repositoryResolver) Issues(ctx context.Context, obj *model.Repository, after *string, before *string, first *int, last *int) (*model.IssueConnection, error) { 152 | return r.Srv.ListIssueInRepository(ctx, obj.ID, after, before, first, last) 153 | } 154 | 155 | // PullRequest is the resolver for the pullRequest field. 156 | func (r *repositoryResolver) PullRequest(ctx context.Context, obj *model.Repository, number int) (*model.PullRequest, error) { 157 | return r.Srv.GetPullRequestByRepoAndNumber(ctx, obj.ID, number) 158 | } 159 | 160 | // PullRequests is the resolver for the pullRequests field. 161 | func (r *repositoryResolver) PullRequests(ctx context.Context, obj *model.Repository, after *string, before *string, first *int, last *int) (*model.PullRequestConnection, error) { 162 | return r.Srv.ListPullRequestInRepository(ctx, obj.ID, after, before, first, last) 163 | } 164 | 165 | // ProjectV2 is the resolver for the projectV2 field. 166 | func (r *userResolver) ProjectV2(ctx context.Context, obj *model.User, number int) (*model.ProjectV2, error) { 167 | return r.Srv.GetProjectByOwnerAndNumber(ctx, obj.ID, number) 168 | } 169 | 170 | // ProjectV2s is the resolver for the projectV2s field. 171 | func (r *userResolver) ProjectV2s(ctx context.Context, obj *model.User, after *string, before *string, first *int, last *int) (*model.ProjectV2Connection, error) { 172 | return r.Srv.ListProjectByOwner(ctx, obj.ID, after, before, first, last) 173 | } 174 | 175 | // Issue returns internal.IssueResolver implementation. 176 | func (r *Resolver) Issue() internal.IssueResolver { return &issueResolver{r} } 177 | 178 | // Mutation returns internal.MutationResolver implementation. 179 | func (r *Resolver) Mutation() internal.MutationResolver { return &mutationResolver{r} } 180 | 181 | // ProjectV2 returns internal.ProjectV2Resolver implementation. 182 | func (r *Resolver) ProjectV2() internal.ProjectV2Resolver { return &projectV2Resolver{r} } 183 | 184 | // ProjectV2Item returns internal.ProjectV2ItemResolver implementation. 185 | func (r *Resolver) ProjectV2Item() internal.ProjectV2ItemResolver { return &projectV2ItemResolver{r} } 186 | 187 | // PullRequest returns internal.PullRequestResolver implementation. 188 | func (r *Resolver) PullRequest() internal.PullRequestResolver { return &pullRequestResolver{r} } 189 | 190 | // Query returns internal.QueryResolver implementation. 191 | func (r *Resolver) Query() internal.QueryResolver { return &queryResolver{r} } 192 | 193 | // Repository returns internal.RepositoryResolver implementation. 194 | func (r *Resolver) Repository() internal.RepositoryResolver { return &repositoryResolver{r} } 195 | 196 | // User returns internal.UserResolver implementation. 197 | func (r *Resolver) User() internal.UserResolver { return &userResolver{r} } 198 | 199 | type issueResolver struct{ *Resolver } 200 | type mutationResolver struct{ *Resolver } 201 | type projectV2Resolver struct{ *Resolver } 202 | type projectV2ItemResolver struct{ *Resolver } 203 | type pullRequestResolver struct{ *Resolver } 204 | type queryResolver struct{ *Resolver } 205 | type repositoryResolver struct{ *Resolver } 206 | type userResolver struct{ *Resolver } 207 | -------------------------------------------------------------------------------- /graph/services/issues.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | 8 | "github.com/saki-engineering/graphql-sample/graph/db" 9 | "github.com/saki-engineering/graphql-sample/graph/model" 10 | 11 | "github.com/volatiletech/sqlboiler/v4/boil" 12 | "github.com/volatiletech/sqlboiler/v4/queries/qm" 13 | ) 14 | 15 | type issueService struct { 16 | exec boil.ContextExecutor 17 | } 18 | 19 | func convertIssue(issue *db.Issue) *model.Issue { 20 | issueURL, err := model.UnmarshalURI(issue.URL) 21 | if err != nil { 22 | log.Println("invalid URI", issue.URL) 23 | } 24 | 25 | return &model.Issue{ 26 | ID: issue.ID, 27 | URL: issueURL, 28 | Title: issue.Title, 29 | Closed: (issue.Closed == 1), 30 | Number: int(issue.Number), 31 | Author: &model.User{ID: issue.Author}, 32 | Repository: &model.Repository{ID: issue.Repository}, 33 | } 34 | } 35 | 36 | func convertIssueConnection(issues db.IssueSlice, hasPrevPage, hasNextPage bool) *model.IssueConnection { 37 | var result model.IssueConnection 38 | 39 | for _, dbi := range issues { 40 | issue := convertIssue(dbi) 41 | 42 | result.Edges = append(result.Edges, &model.IssueEdge{Cursor: issue.ID, Node: issue}) 43 | result.Nodes = append(result.Nodes, issue) 44 | } 45 | result.TotalCount = len(issues) 46 | 47 | result.PageInfo = &model.PageInfo{} 48 | if result.TotalCount != 0 { 49 | result.PageInfo.StartCursor = &result.Nodes[0].ID 50 | result.PageInfo.EndCursor = &result.Nodes[result.TotalCount-1].ID 51 | } 52 | result.PageInfo.HasPreviousPage = hasPrevPage 53 | result.PageInfo.HasNextPage = hasNextPage 54 | 55 | return &result 56 | } 57 | 58 | func (i *issueService) GetIssueByID(ctx context.Context, id string) (*model.Issue, error) { 59 | issue, err := db.FindIssue(ctx, i.exec, id, 60 | db.IssueColumns.ID, 61 | db.IssueColumns.URL, 62 | db.IssueColumns.Title, 63 | db.IssueColumns.Closed, 64 | db.IssueColumns.Number, 65 | db.IssueColumns.Author, 66 | db.IssueColumns.Repository, 67 | ) 68 | if err != nil { 69 | return nil, err 70 | } 71 | return convertIssue(issue), nil 72 | } 73 | 74 | func (i *issueService) GetIssueByRepoAndNumber(ctx context.Context, repoID string, number int) (*model.Issue, error) { 75 | issue, err := db.Issues( 76 | qm.Select( 77 | db.IssueColumns.ID, 78 | db.IssueColumns.URL, 79 | db.IssueColumns.Title, 80 | db.IssueColumns.Closed, 81 | db.IssueColumns.Number, 82 | db.IssueColumns.Author, 83 | db.IssueColumns.Repository, 84 | ), 85 | db.IssueWhere.Repository.EQ(repoID), 86 | db.IssueWhere.Number.EQ(int64(number)), 87 | ).One(ctx, i.exec) 88 | if err != nil { 89 | return nil, err 90 | } 91 | return convertIssue(issue), nil 92 | } 93 | 94 | func (i *issueService) ListIssueInRepository(ctx context.Context, repoID string, after *string, before *string, first *int, last *int) (*model.IssueConnection, error) { 95 | cond := []qm.QueryMod{ 96 | qm.Select( 97 | db.IssueColumns.ID, 98 | db.IssueColumns.URL, 99 | db.IssueColumns.Title, 100 | db.IssueColumns.Closed, 101 | db.IssueColumns.Number, 102 | db.IssueColumns.Author, 103 | db.IssueColumns.Repository, 104 | ), 105 | db.IssueWhere.Repository.EQ(repoID), 106 | } 107 | var scanDesc bool 108 | 109 | switch { 110 | case (after != nil) && (before != nil): 111 | cond = append(cond, db.IssueWhere.ID.GT(*after), db.IssueWhere.ID.LT(*before)) 112 | case after != nil: 113 | cond = append(cond, 114 | db.IssueWhere.ID.GT(*after), 115 | qm.OrderBy(fmt.Sprintf("%s asc", db.IssueColumns.ID)), 116 | ) 117 | if first != nil { 118 | cond = append(cond, qm.Limit(*first)) 119 | } 120 | case before != nil: 121 | scanDesc = true 122 | cond = append(cond, 123 | db.IssueWhere.ID.LT(*before), 124 | qm.OrderBy(fmt.Sprintf("%s desc", db.IssueColumns.ID)), 125 | ) 126 | if last != nil { 127 | cond = append(cond, qm.Limit(*last)) 128 | } 129 | default: 130 | switch { 131 | case last != nil: 132 | scanDesc = true 133 | cond = append(cond, 134 | qm.OrderBy(fmt.Sprintf("%s desc", db.IssueColumns.ID)), 135 | qm.Limit(*last), 136 | ) 137 | case first != nil: 138 | cond = append(cond, 139 | qm.OrderBy(fmt.Sprintf("%s asc", db.IssueColumns.ID)), 140 | qm.Limit(*first), 141 | ) 142 | default: 143 | cond = append(cond, 144 | qm.OrderBy(fmt.Sprintf("%s asc", db.IssueColumns.ID)), 145 | ) 146 | } 147 | } 148 | 149 | issues, err := db.Issues(cond...).All(ctx, i.exec) 150 | if err != nil { 151 | return nil, err 152 | } 153 | 154 | var hasNextPage, hasPrevPage bool 155 | if len(issues) != 0 { 156 | if scanDesc { 157 | for i, j := 0, len(issues)-1; i < j; i, j = i+1, j-1 { 158 | issues[i], issues[j] = issues[j], issues[i] 159 | } 160 | } 161 | startCursor, endCursor := issues[0].ID, issues[len(issues)-1].ID 162 | 163 | var err error 164 | hasPrevPage, err = db.Issues( 165 | db.IssueWhere.Repository.EQ(repoID), 166 | db.IssueWhere.ID.LT(startCursor), 167 | ).Exists(ctx, i.exec) 168 | if err != nil { 169 | return nil, err 170 | } 171 | hasNextPage, err = db.Issues( 172 | db.IssueWhere.Repository.EQ(repoID), 173 | db.IssueWhere.ID.GT(endCursor), 174 | ).Exists(ctx, i.exec) 175 | if err != nil { 176 | return nil, err 177 | } 178 | } 179 | 180 | return convertIssueConnection(issues, hasPrevPage, hasNextPage), nil 181 | } 182 | -------------------------------------------------------------------------------- /graph/services/projectitems.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/saki-engineering/graphql-sample/graph/db" 8 | "github.com/saki-engineering/graphql-sample/graph/model" 9 | 10 | "github.com/google/uuid" 11 | "github.com/volatiletech/null/v8" 12 | "github.com/volatiletech/sqlboiler/v4/boil" 13 | "github.com/volatiletech/sqlboiler/v4/queries/qm" 14 | ) 15 | 16 | type projectItemService struct { 17 | exec boil.ContextExecutor 18 | } 19 | 20 | func convertProjectV2Item(item *db.Projectcard) *model.ProjectV2Item { 21 | result := &model.ProjectV2Item{ 22 | ID: item.ID, 23 | Project: &model.ProjectV2{ID: item.Project}, 24 | } 25 | if item.Issue.Valid { 26 | result.Content = &model.Issue{ID: item.Issue.String} 27 | } 28 | if item.Pullrequest.Valid { 29 | result.Content = &model.PullRequest{ID: item.Pullrequest.String} 30 | } 31 | return result 32 | } 33 | 34 | func convertProjectV2ItemConnection(items db.ProjectcardSlice, hasPrevPage, hasNextPage bool) *model.ProjectV2ItemConnection { 35 | var result model.ProjectV2ItemConnection 36 | 37 | for _, dbi := range items { 38 | item := convertProjectV2Item(dbi) 39 | 40 | result.Edges = append(result.Edges, &model.ProjectV2ItemEdge{Cursor: item.ID, Node: item}) 41 | result.Nodes = append(result.Nodes, item) 42 | } 43 | result.TotalCount = len(items) 44 | 45 | result.PageInfo = &model.PageInfo{} 46 | if result.TotalCount != 0 { 47 | result.PageInfo.StartCursor = &result.Nodes[0].ID 48 | result.PageInfo.EndCursor = &result.Nodes[result.TotalCount-1].ID 49 | } 50 | result.PageInfo.HasPreviousPage = hasPrevPage 51 | result.PageInfo.HasNextPage = hasNextPage 52 | 53 | return &result 54 | } 55 | 56 | func (p *projectItemService) GetProjectItemByID(ctx context.Context, id string) (*model.ProjectV2Item, error) { 57 | item, err := db.FindProjectcard(ctx, p.exec, id, 58 | db.ProjectcardColumns.ID, 59 | db.ProjectcardColumns.Project, 60 | db.ProjectcardColumns.Issue, 61 | db.ProjectcardColumns.Pullrequest, 62 | ) 63 | if err != nil { 64 | return nil, err 65 | } 66 | return convertProjectV2Item(item), nil 67 | } 68 | 69 | func (p *projectItemService) ListProjectItemOwnedByProject(ctx context.Context, projectID string, after *string, before *string, first *int, last *int) (*model.ProjectV2ItemConnection, error) { 70 | cond := []qm.QueryMod{ 71 | qm.Select( 72 | db.ProjectcardColumns.ID, 73 | db.ProjectcardColumns.Project, 74 | db.ProjectcardColumns.Issue, 75 | db.ProjectcardColumns.Pullrequest, 76 | ), 77 | db.ProjectcardWhere.Project.EQ(projectID), 78 | } 79 | var scanDesc bool 80 | 81 | switch { 82 | case (after != nil) && (before != nil): 83 | cond = append(cond, db.ProjectcardWhere.ID.GT(*after), db.ProjectcardWhere.ID.LT(*before)) 84 | case after != nil: 85 | cond = append(cond, 86 | db.ProjectcardWhere.ID.GT(*after), 87 | qm.OrderBy(fmt.Sprintf("%s asc", db.ProjectcardColumns.ID)), 88 | ) 89 | if first != nil { 90 | cond = append(cond, qm.Limit(*first)) 91 | } 92 | case before != nil: 93 | scanDesc = true 94 | cond = append(cond, 95 | db.ProjectcardWhere.ID.LT(*before), 96 | qm.OrderBy(fmt.Sprintf("%s desc", db.ProjectcardColumns.ID)), 97 | ) 98 | if last != nil { 99 | cond = append(cond, qm.Limit(*last)) 100 | } 101 | default: 102 | switch { 103 | case last != nil: 104 | scanDesc = true 105 | cond = append(cond, 106 | qm.OrderBy(fmt.Sprintf("%s desc", db.ProjectcardColumns.ID)), 107 | qm.Limit(*last), 108 | ) 109 | case first != nil: 110 | cond = append(cond, 111 | qm.OrderBy(fmt.Sprintf("%s asc", db.ProjectcardColumns.ID)), 112 | qm.Limit(*first), 113 | ) 114 | default: 115 | cond = append(cond, 116 | qm.OrderBy(fmt.Sprintf("%s asc", db.ProjectcardColumns.ID)), 117 | ) 118 | } 119 | } 120 | 121 | items, err := db.Projectcards(cond...).All(ctx, p.exec) 122 | if err != nil { 123 | return nil, err 124 | } 125 | 126 | var hasNextPage, hasPrevPage bool 127 | if len(items) != 0 { 128 | if scanDesc { 129 | for i, j := 0, len(items)-1; i < j; i, j = i+1, j-1 { 130 | items[i], items[j] = items[j], items[i] 131 | } 132 | } 133 | startCursor, endCursor := items[0].ID, items[len(items)-1].ID 134 | 135 | var err error 136 | hasPrevPage, err = db.Projectcards( 137 | db.ProjectcardWhere.Project.EQ(projectID), 138 | db.ProjectcardWhere.ID.LT(startCursor), 139 | ).Exists(ctx, p.exec) 140 | if err != nil { 141 | return nil, err 142 | } 143 | hasNextPage, err = db.Projectcards( 144 | db.ProjectcardWhere.Project.EQ(projectID), 145 | db.ProjectcardWhere.ID.GT(endCursor), 146 | ).Exists(ctx, p.exec) 147 | if err != nil { 148 | return nil, err 149 | } 150 | } 151 | 152 | return convertProjectV2ItemConnection(items, hasPrevPage, hasNextPage), nil 153 | } 154 | 155 | func (p *projectItemService) ListProjectItemOwnedByIssue(ctx context.Context, issueID string, after *string, before *string, first *int, last *int) (*model.ProjectV2ItemConnection, error) { 156 | cond := []qm.QueryMod{ 157 | qm.Select( 158 | db.ProjectcardColumns.ID, 159 | db.ProjectcardColumns.Project, 160 | db.ProjectcardColumns.Issue, 161 | db.ProjectcardColumns.Pullrequest, 162 | ), 163 | db.ProjectcardWhere.Pullrequest.EQ(null.StringFrom(issueID)), 164 | } 165 | var scanDesc bool 166 | 167 | switch { 168 | case (after != nil) && (before != nil): 169 | cond = append(cond, db.ProjectcardWhere.ID.GT(*after), db.ProjectcardWhere.ID.LT(*before)) 170 | case after != nil: 171 | cond = append(cond, 172 | db.ProjectcardWhere.ID.GT(*after), 173 | qm.OrderBy(fmt.Sprintf("%s asc", db.ProjectcardColumns.ID)), 174 | ) 175 | if first != nil { 176 | cond = append(cond, qm.Limit(*first)) 177 | } 178 | case before != nil: 179 | scanDesc = true 180 | cond = append(cond, 181 | db.ProjectcardWhere.ID.LT(*before), 182 | qm.OrderBy(fmt.Sprintf("%s desc", db.ProjectcardColumns.ID)), 183 | ) 184 | if last != nil { 185 | cond = append(cond, qm.Limit(*last)) 186 | } 187 | default: 188 | switch { 189 | case last != nil: 190 | scanDesc = true 191 | cond = append(cond, 192 | qm.OrderBy(fmt.Sprintf("%s desc", db.ProjectcardColumns.ID)), 193 | qm.Limit(*last), 194 | ) 195 | case first != nil: 196 | cond = append(cond, 197 | qm.OrderBy(fmt.Sprintf("%s asc", db.ProjectcardColumns.ID)), 198 | qm.Limit(*first), 199 | ) 200 | default: 201 | cond = append(cond, 202 | qm.OrderBy(fmt.Sprintf("%s asc", db.ProjectcardColumns.ID)), 203 | ) 204 | } 205 | } 206 | 207 | items, err := db.Projectcards(cond...).All(ctx, p.exec) 208 | if err != nil { 209 | return nil, err 210 | } 211 | 212 | var hasNextPage, hasPrevPage bool 213 | if len(items) != 0 { 214 | if scanDesc { 215 | for i, j := 0, len(items)-1; i < j; i, j = i+1, j-1 { 216 | items[i], items[j] = items[j], items[i] 217 | } 218 | } 219 | startCursor, endCursor := items[0].ID, items[len(items)-1].ID 220 | 221 | var err error 222 | hasPrevPage, err = db.Projectcards( 223 | db.ProjectcardWhere.Pullrequest.EQ(null.StringFrom(issueID)), 224 | db.ProjectcardWhere.ID.LT(startCursor), 225 | ).Exists(ctx, p.exec) 226 | if err != nil { 227 | return nil, err 228 | } 229 | hasNextPage, err = db.Projectcards( 230 | db.ProjectcardWhere.Pullrequest.EQ(null.StringFrom(issueID)), 231 | db.ProjectcardWhere.ID.GT(endCursor), 232 | ).Exists(ctx, p.exec) 233 | if err != nil { 234 | return nil, err 235 | } 236 | } 237 | 238 | return convertProjectV2ItemConnection(items, hasPrevPage, hasNextPage), nil 239 | } 240 | 241 | func (p *projectItemService) ListProjectItemOwnedByPullRequest(ctx context.Context, pullRequestID string, after *string, before *string, first *int, last *int) (*model.ProjectV2ItemConnection, error) { 242 | cond := []qm.QueryMod{ 243 | qm.Select( 244 | db.ProjectcardColumns.ID, 245 | db.ProjectcardColumns.Project, 246 | db.ProjectcardColumns.Issue, 247 | db.ProjectcardColumns.Pullrequest, 248 | ), 249 | db.ProjectcardWhere.Pullrequest.EQ(null.StringFrom(pullRequestID)), 250 | } 251 | var scanDesc bool 252 | 253 | switch { 254 | case (after != nil) && (before != nil): 255 | cond = append(cond, db.ProjectcardWhere.ID.GT(*after), db.ProjectcardWhere.ID.LT(*before)) 256 | case after != nil: 257 | cond = append(cond, 258 | db.ProjectcardWhere.ID.GT(*after), 259 | qm.OrderBy(fmt.Sprintf("%s asc", db.ProjectcardColumns.ID)), 260 | ) 261 | if first != nil { 262 | cond = append(cond, qm.Limit(*first)) 263 | } 264 | case before != nil: 265 | scanDesc = true 266 | cond = append(cond, 267 | db.ProjectcardWhere.ID.LT(*before), 268 | qm.OrderBy(fmt.Sprintf("%s desc", db.ProjectcardColumns.ID)), 269 | ) 270 | if last != nil { 271 | cond = append(cond, qm.Limit(*last)) 272 | } 273 | default: 274 | switch { 275 | case last != nil: 276 | scanDesc = true 277 | cond = append(cond, 278 | qm.OrderBy(fmt.Sprintf("%s desc", db.ProjectcardColumns.ID)), 279 | qm.Limit(*last), 280 | ) 281 | case first != nil: 282 | cond = append(cond, 283 | qm.OrderBy(fmt.Sprintf("%s asc", db.ProjectcardColumns.ID)), 284 | qm.Limit(*first), 285 | ) 286 | default: 287 | cond = append(cond, 288 | qm.OrderBy(fmt.Sprintf("%s asc", db.ProjectcardColumns.ID)), 289 | ) 290 | } 291 | } 292 | 293 | items, err := db.Projectcards(cond...).All(ctx, p.exec) 294 | if err != nil { 295 | return nil, err 296 | } 297 | 298 | var hasNextPage, hasPrevPage bool 299 | if len(items) != 0 { 300 | if scanDesc { 301 | for i, j := 0, len(items)-1; i < j; i, j = i+1, j-1 { 302 | items[i], items[j] = items[j], items[i] 303 | } 304 | } 305 | startCursor, endCursor := items[0].ID, items[len(items)-1].ID 306 | 307 | var err error 308 | hasPrevPage, err = db.Projectcards( 309 | db.ProjectcardWhere.Pullrequest.EQ(null.StringFrom(pullRequestID)), 310 | db.ProjectcardWhere.ID.LT(startCursor), 311 | ).Exists(ctx, p.exec) 312 | if err != nil { 313 | return nil, err 314 | } 315 | hasNextPage, err = db.Projectcards( 316 | db.ProjectcardWhere.Pullrequest.EQ(null.StringFrom(pullRequestID)), 317 | db.ProjectcardWhere.ID.GT(endCursor), 318 | ).Exists(ctx, p.exec) 319 | if err != nil { 320 | return nil, err 321 | } 322 | } 323 | 324 | return convertProjectV2ItemConnection(items, hasPrevPage, hasNextPage), nil 325 | } 326 | 327 | func (p *projectItemService) AddIssueInProjectV2(ctx context.Context, projectID, issueID string) (*model.ProjectV2Item, error) { 328 | itemID := uuid.New() 329 | item := &db.Projectcard{ 330 | ID: itemID.String(), 331 | Project: projectID, 332 | Issue: null.StringFrom(issueID), 333 | } 334 | if err := item.Insert(ctx, p.exec, boil.Infer()); err != nil { 335 | return nil, err 336 | } 337 | return convertProjectV2Item(item), nil 338 | } 339 | 340 | func (p *projectItemService) AddPullRequestInProjectV2(ctx context.Context, projectID, pullRequestID string) (*model.ProjectV2Item, error) { 341 | itemID := uuid.New() 342 | item := &db.Projectcard{ 343 | ID: itemID.String(), 344 | Project: projectID, 345 | Pullrequest: null.StringFrom(pullRequestID), 346 | } 347 | if err := item.Insert(ctx, p.exec, boil.Infer()); err != nil { 348 | return nil, err 349 | } 350 | return convertProjectV2Item(item), nil 351 | } 352 | -------------------------------------------------------------------------------- /graph/services/projects.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | 8 | "github.com/saki-engineering/graphql-sample/graph/db" 9 | "github.com/saki-engineering/graphql-sample/graph/model" 10 | 11 | "github.com/volatiletech/sqlboiler/v4/boil" 12 | "github.com/volatiletech/sqlboiler/v4/queries/qm" 13 | ) 14 | 15 | type projectService struct { 16 | exec boil.ContextExecutor 17 | } 18 | 19 | func convertProjectV2(project *db.Project) *model.ProjectV2 { 20 | projectURL, err := model.UnmarshalURI(project.URL) 21 | if err != nil { 22 | log.Println("invalid URI", project.URL) 23 | } 24 | 25 | return &model.ProjectV2{ 26 | ID: project.ID, 27 | Title: project.Title, 28 | Number: int(project.Number), 29 | URL: projectURL, 30 | Owner: &model.User{ID: project.Owner}, 31 | } 32 | } 33 | 34 | func convertProjectV2Connection(projects db.ProjectSlice, hasPrevPage, hasNextPage bool) *model.ProjectV2Connection { 35 | var result model.ProjectV2Connection 36 | 37 | for _, dbp := range projects { 38 | project := convertProjectV2(dbp) 39 | 40 | result.Edges = append(result.Edges, &model.ProjectV2Edge{Cursor: project.ID, Node: project}) 41 | result.Nodes = append(result.Nodes, project) 42 | } 43 | result.TotalCount = len(projects) 44 | 45 | result.PageInfo = &model.PageInfo{} 46 | if result.TotalCount != 0 { 47 | result.PageInfo.StartCursor = &result.Nodes[0].ID 48 | result.PageInfo.EndCursor = &result.Nodes[result.TotalCount-1].ID 49 | } 50 | result.PageInfo.HasPreviousPage = hasPrevPage 51 | result.PageInfo.HasNextPage = hasNextPage 52 | 53 | return &result 54 | } 55 | 56 | func (p *projectService) GetProjectByID(ctx context.Context, id string) (*model.ProjectV2, error) { 57 | project, err := db.FindProject(ctx, p.exec, id, 58 | db.ProjectColumns.ID, 59 | db.ProjectColumns.Title, 60 | db.ProjectColumns.Number, 61 | db.ProjectColumns.URL, 62 | db.ProjectColumns.Owner, 63 | ) 64 | if err != nil { 65 | return nil, err 66 | } 67 | return convertProjectV2(project), nil 68 | } 69 | 70 | func (p *projectService) GetProjectByOwnerAndNumber(ctx context.Context, ownerID string, number int) (*model.ProjectV2, error) { 71 | project, err := db.Projects( 72 | qm.Select( 73 | db.ProjectColumns.ID, 74 | db.ProjectColumns.Title, 75 | db.ProjectColumns.Number, 76 | db.ProjectColumns.URL, 77 | db.ProjectColumns.Owner, 78 | ), 79 | db.ProjectWhere.Owner.EQ(ownerID), 80 | db.ProjectWhere.Number.EQ(int64(number)), 81 | ).One(ctx, p.exec) 82 | if err != nil { 83 | return nil, err 84 | } 85 | return convertProjectV2(project), nil 86 | } 87 | 88 | func (p *projectService) ListProjectByOwner(ctx context.Context, ownerID string, after *string, before *string, first *int, last *int) (*model.ProjectV2Connection, error) { 89 | cond := []qm.QueryMod{ 90 | qm.Select( 91 | db.ProjectColumns.ID, 92 | db.ProjectColumns.Title, 93 | db.ProjectColumns.Number, 94 | db.ProjectColumns.URL, 95 | db.ProjectColumns.Owner, 96 | ), 97 | db.ProjectWhere.Owner.EQ(ownerID), 98 | } 99 | var scanDesc bool 100 | 101 | switch { 102 | case (after != nil) && (before != nil): 103 | cond = append(cond, db.ProjectWhere.ID.GT(*after), db.ProjectWhere.ID.LT(*before)) 104 | case after != nil: 105 | cond = append(cond, 106 | db.ProjectWhere.ID.GT(*after), 107 | qm.OrderBy(fmt.Sprintf("%s asc", db.ProjectColumns.ID)), 108 | ) 109 | if first != nil { 110 | cond = append(cond, qm.Limit(*first)) 111 | } 112 | case before != nil: 113 | scanDesc = true 114 | cond = append(cond, 115 | db.ProjectWhere.ID.LT(*before), 116 | qm.OrderBy(fmt.Sprintf("%s desc", db.ProjectColumns.ID)), 117 | ) 118 | if last != nil { 119 | cond = append(cond, qm.Limit(*last)) 120 | } 121 | default: 122 | switch { 123 | case last != nil: 124 | scanDesc = true 125 | cond = append(cond, 126 | qm.OrderBy(fmt.Sprintf("%s desc", db.ProjectColumns.ID)), 127 | qm.Limit(*last), 128 | ) 129 | case first != nil: 130 | cond = append(cond, 131 | qm.OrderBy(fmt.Sprintf("%s asc", db.ProjectColumns.ID)), 132 | qm.Limit(*first), 133 | ) 134 | default: 135 | cond = append(cond, 136 | qm.OrderBy(fmt.Sprintf("%s asc", db.ProjectColumns.ID)), 137 | ) 138 | } 139 | } 140 | 141 | projects, err := db.Projects(cond...).All(ctx, p.exec) 142 | if err != nil { 143 | return nil, err 144 | } 145 | 146 | var hasNextPage, hasPrevPage bool 147 | if len(projects) != 0 { 148 | if scanDesc { 149 | for i, j := 0, len(projects)-1; i < j; i, j = i+1, j-1 { 150 | projects[i], projects[j] = projects[j], projects[i] 151 | } 152 | } 153 | startCursor, endCursor := projects[0].ID, projects[len(projects)-1].ID 154 | 155 | var err error 156 | hasPrevPage, err = db.Projects( 157 | db.ProjectWhere.Owner.EQ(ownerID), 158 | db.ProjectWhere.ID.LT(startCursor), 159 | ).Exists(ctx, p.exec) 160 | if err != nil { 161 | return nil, err 162 | } 163 | hasNextPage, err = db.Projects( 164 | db.ProjectWhere.Owner.EQ(ownerID), 165 | db.ProjectWhere.ID.GT(endCursor), 166 | ).Exists(ctx, p.exec) 167 | if err != nil { 168 | return nil, err 169 | } 170 | } 171 | 172 | return convertProjectV2Connection(projects, hasPrevPage, hasNextPage), nil 173 | } 174 | -------------------------------------------------------------------------------- /graph/services/pullrequests.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | 8 | "github.com/saki-engineering/graphql-sample/graph/db" 9 | "github.com/saki-engineering/graphql-sample/graph/model" 10 | 11 | "github.com/volatiletech/sqlboiler/v4/boil" 12 | "github.com/volatiletech/sqlboiler/v4/queries/qm" 13 | ) 14 | 15 | type pullRequestService struct { 16 | exec boil.ContextExecutor 17 | } 18 | 19 | func convertPullRequest(pr *db.Pullrequest) *model.PullRequest { 20 | prURL, err := model.UnmarshalURI(pr.URL) 21 | if err != nil { 22 | log.Println("invalid URI", pr.URL) 23 | } 24 | 25 | return &model.PullRequest{ 26 | ID: pr.ID, 27 | BaseRefName: pr.BaseRefName, 28 | Closed: (pr.Closed == 1), 29 | HeadRefName: pr.HeadRefName, 30 | URL: prURL, 31 | Number: int(pr.Number), 32 | Repository: &model.Repository{ID: pr.Repository}, 33 | } 34 | } 35 | 36 | func convertPullRequestConnection(pullRequests db.PullrequestSlice, hasPrevPage, hasNextPage bool) *model.PullRequestConnection { 37 | var result model.PullRequestConnection 38 | 39 | for _, dbpr := range pullRequests { 40 | pr := convertPullRequest(dbpr) 41 | 42 | result.Edges = append(result.Edges, &model.PullRequestEdge{Cursor: pr.ID, Node: pr}) 43 | result.Nodes = append(result.Nodes, pr) 44 | } 45 | result.TotalCount = len(pullRequests) 46 | 47 | result.PageInfo = &model.PageInfo{} 48 | if result.TotalCount != 0 { 49 | result.PageInfo.StartCursor = &result.Nodes[0].ID 50 | result.PageInfo.EndCursor = &result.Nodes[result.TotalCount-1].ID 51 | } 52 | result.PageInfo.HasPreviousPage = hasPrevPage 53 | result.PageInfo.HasNextPage = hasNextPage 54 | 55 | return &result 56 | } 57 | 58 | func (p *pullRequestService) GetPullRequestByID(ctx context.Context, id string) (*model.PullRequest, error) { 59 | pr, err := db.FindPullrequest(ctx, p.exec, id, 60 | db.PullrequestColumns.ID, 61 | db.PullrequestColumns.BaseRefName, 62 | db.PullrequestColumns.Closed, 63 | db.PullrequestColumns.HeadRefName, 64 | db.PullrequestColumns.URL, 65 | db.PullrequestColumns.Number, 66 | db.PullrequestColumns.Repository, 67 | ) 68 | if err != nil { 69 | return nil, err 70 | } 71 | return convertPullRequest(pr), nil 72 | } 73 | 74 | func (p *pullRequestService) GetPullRequestByRepoAndNumber(ctx context.Context, repoID string, number int) (*model.PullRequest, error) { 75 | pr, err := db.Pullrequests( 76 | qm.Select( 77 | db.PullrequestColumns.ID, 78 | db.PullrequestColumns.BaseRefName, 79 | db.PullrequestColumns.Closed, 80 | db.PullrequestColumns.HeadRefName, 81 | db.PullrequestColumns.URL, 82 | db.PullrequestColumns.Number, 83 | db.PullrequestColumns.Repository, 84 | ), 85 | db.PullrequestWhere.Repository.EQ(repoID), 86 | db.PullrequestWhere.Number.EQ(int64(number)), 87 | ).One(ctx, p.exec) 88 | if err != nil { 89 | return nil, err 90 | } 91 | return convertPullRequest(pr), nil 92 | } 93 | 94 | func (p *pullRequestService) ListPullRequestInRepository(ctx context.Context, repoID string, after *string, before *string, first *int, last *int) (*model.PullRequestConnection, error) { 95 | cond := []qm.QueryMod{ 96 | qm.Select( 97 | db.PullrequestColumns.ID, 98 | db.PullrequestColumns.BaseRefName, 99 | db.PullrequestColumns.Closed, 100 | db.PullrequestColumns.HeadRefName, 101 | db.PullrequestColumns.URL, 102 | db.PullrequestColumns.Number, 103 | db.PullrequestColumns.Repository, 104 | ), 105 | db.PullrequestWhere.Repository.EQ(repoID), 106 | } 107 | var scanDesc bool 108 | 109 | switch { 110 | case (after != nil) && (before != nil): 111 | cond = append(cond, db.PullrequestWhere.ID.GT(*after), db.PullrequestWhere.ID.LT(*before)) 112 | case after != nil: 113 | cond = append(cond, 114 | db.PullrequestWhere.ID.GT(*after), 115 | qm.OrderBy(fmt.Sprintf("%s asc", db.PullrequestColumns.ID)), 116 | ) 117 | if first != nil { 118 | cond = append(cond, qm.Limit(*first)) 119 | } 120 | case before != nil: 121 | scanDesc = true 122 | cond = append(cond, 123 | db.PullrequestWhere.ID.LT(*before), 124 | qm.OrderBy(fmt.Sprintf("%s desc", db.PullrequestColumns.ID)), 125 | ) 126 | if last != nil { 127 | cond = append(cond, qm.Limit(*last)) 128 | } 129 | default: 130 | switch { 131 | case last != nil: 132 | scanDesc = true 133 | cond = append(cond, 134 | qm.OrderBy(fmt.Sprintf("%s desc", db.PullrequestColumns.ID)), 135 | qm.Limit(*last), 136 | ) 137 | case first != nil: 138 | cond = append(cond, 139 | qm.OrderBy(fmt.Sprintf("%s asc", db.PullrequestColumns.ID)), 140 | qm.Limit(*first), 141 | ) 142 | default: 143 | cond = append(cond, 144 | qm.OrderBy(fmt.Sprintf("%s asc", db.PullrequestColumns.ID)), 145 | ) 146 | } 147 | } 148 | 149 | pullRequests, err := db.Pullrequests(cond...).All(ctx, p.exec) 150 | if err != nil { 151 | return nil, err 152 | } 153 | 154 | var hasNextPage, hasPrevPage bool 155 | if len(pullRequests) != 0 { 156 | if scanDesc { 157 | for i, j := 0, len(pullRequests)-1; i < j; i, j = i+1, j-1 { 158 | pullRequests[i], pullRequests[j] = pullRequests[j], pullRequests[i] 159 | } 160 | } 161 | startCursor, endCursor := pullRequests[0].ID, pullRequests[len(pullRequests)-1].ID 162 | 163 | var err error 164 | hasPrevPage, err = db.Pullrequests( 165 | db.PullrequestWhere.Repository.EQ(repoID), 166 | db.PullrequestWhere.ID.LT(startCursor), 167 | ).Exists(ctx, p.exec) 168 | if err != nil { 169 | return nil, err 170 | } 171 | hasNextPage, err = db.Pullrequests( 172 | db.PullrequestWhere.Repository.EQ(repoID), 173 | db.PullrequestWhere.ID.GT(endCursor), 174 | ).Exists(ctx, p.exec) 175 | if err != nil { 176 | return nil, err 177 | } 178 | } 179 | 180 | return convertPullRequestConnection(pullRequests, hasPrevPage, hasNextPage), nil 181 | } 182 | -------------------------------------------------------------------------------- /graph/services/repositories.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/saki-engineering/graphql-sample/graph/db" 7 | "github.com/saki-engineering/graphql-sample/graph/model" 8 | 9 | "github.com/volatiletech/sqlboiler/v4/boil" 10 | "github.com/volatiletech/sqlboiler/v4/queries/qm" 11 | ) 12 | 13 | type repoService struct { 14 | exec boil.ContextExecutor 15 | } 16 | 17 | func convertRepository(repo *db.Repository) *model.Repository { 18 | return &model.Repository{ 19 | ID: repo.ID, 20 | Owner: &model.User{ID: repo.Owner}, 21 | Name: repo.Name, 22 | CreatedAt: repo.CreatedAt, 23 | } 24 | } 25 | 26 | func (r *repoService) GetRepoByID(ctx context.Context, id string) (*model.Repository, error) { 27 | repo, err := db.FindRepository(ctx, r.exec, id, 28 | db.RepositoryColumns.ID, db.RepositoryColumns.Name, db.RepositoryColumns.Owner, db.RepositoryColumns.CreatedAt, 29 | ) 30 | if err != nil { 31 | return nil, err 32 | } 33 | return convertRepository(repo), nil 34 | } 35 | 36 | func (r *repoService) GetRepoByFullName(ctx context.Context, owner, name string) (*model.Repository, error) { 37 | repo, err := db.Repositories( 38 | qm.Select( 39 | db.RepositoryColumns.ID, 40 | db.RepositoryColumns.Name, 41 | db.RepositoryColumns.Owner, 42 | db.RepositoryColumns.CreatedAt, 43 | ), 44 | db.RepositoryWhere.Owner.EQ(owner), 45 | db.RepositoryWhere.Name.EQ(name), 46 | ).One(ctx, r.exec) 47 | if err != nil { 48 | return nil, err 49 | } 50 | return convertRepository(repo), nil 51 | } 52 | -------------------------------------------------------------------------------- /graph/services/service.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/saki-engineering/graphql-sample/graph/model" 7 | 8 | "github.com/volatiletech/sqlboiler/v4/boil" 9 | ) 10 | 11 | //go:generate mockgen -source=$GOFILE -package=$GOPACKAGE -destination=../../mock/$GOPACKAGE/service_mock.go 12 | type Services interface { 13 | UserService 14 | RepoService 15 | IssueService 16 | PullRequestService 17 | ProjectService 18 | ProjectItemService 19 | } 20 | 21 | type UserService interface { 22 | GetUserByID(ctx context.Context, id string) (*model.User, error) 23 | GetUserByName(ctx context.Context, name string) (*model.User, error) 24 | ListUsersByID(ctx context.Context, IDs []string) ([]*model.User, error) 25 | } 26 | 27 | type RepoService interface { 28 | GetRepoByID(ctx context.Context, id string) (*model.Repository, error) 29 | GetRepoByFullName(ctx context.Context, owner, name string) (*model.Repository, error) 30 | } 31 | 32 | type IssueService interface { 33 | GetIssueByID(ctx context.Context, id string) (*model.Issue, error) 34 | GetIssueByRepoAndNumber(ctx context.Context, repoID string, number int) (*model.Issue, error) 35 | ListIssueInRepository(ctx context.Context, repoID string, after *string, before *string, first *int, last *int) (*model.IssueConnection, error) 36 | } 37 | 38 | type PullRequestService interface { 39 | GetPullRequestByID(ctx context.Context, id string) (*model.PullRequest, error) 40 | GetPullRequestByRepoAndNumber(ctx context.Context, repoID string, number int) (*model.PullRequest, error) 41 | ListPullRequestInRepository(ctx context.Context, repoID string, after *string, before *string, first *int, last *int) (*model.PullRequestConnection, error) 42 | } 43 | 44 | type ProjectService interface { 45 | GetProjectByID(ctx context.Context, id string) (*model.ProjectV2, error) 46 | GetProjectByOwnerAndNumber(ctx context.Context, ownerID string, number int) (*model.ProjectV2, error) 47 | ListProjectByOwner(ctx context.Context, ownerID string, after *string, before *string, first *int, last *int) (*model.ProjectV2Connection, error) 48 | } 49 | 50 | type ProjectItemService interface { 51 | GetProjectItemByID(ctx context.Context, id string) (*model.ProjectV2Item, error) 52 | ListProjectItemOwnedByProject(ctx context.Context, projectID string, after *string, before *string, first *int, last *int) (*model.ProjectV2ItemConnection, error) 53 | ListProjectItemOwnedByIssue(ctx context.Context, issueID string, after *string, before *string, first *int, last *int) (*model.ProjectV2ItemConnection, error) 54 | ListProjectItemOwnedByPullRequest(ctx context.Context, pullRequestID string, after *string, before *string, first *int, last *int) (*model.ProjectV2ItemConnection, error) 55 | AddIssueInProjectV2(ctx context.Context, projectID, issueID string) (*model.ProjectV2Item, error) 56 | AddPullRequestInProjectV2(ctx context.Context, projectID, pullRequestID string) (*model.ProjectV2Item, error) 57 | } 58 | 59 | type services struct { 60 | *userService 61 | *repoService 62 | *issueService 63 | *pullRequestService 64 | *projectService 65 | *projectItemService 66 | } 67 | 68 | func New(exec boil.ContextExecutor) Services { 69 | return &services{ 70 | userService: &userService{exec: exec}, 71 | repoService: &repoService{exec: exec}, 72 | issueService: &issueService{exec: exec}, 73 | pullRequestService: &pullRequestService{exec: exec}, 74 | projectService: &projectService{exec: exec}, 75 | projectItemService: &projectItemService{exec: exec}, 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /graph/services/users.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/saki-engineering/graphql-sample/graph/db" 7 | "github.com/saki-engineering/graphql-sample/graph/model" 8 | 9 | "github.com/volatiletech/sqlboiler/v4/boil" 10 | "github.com/volatiletech/sqlboiler/v4/queries/qm" 11 | ) 12 | 13 | type userService struct { 14 | exec boil.ContextExecutor 15 | } 16 | 17 | func convertUser(user *db.User) *model.User { 18 | return &model.User{ 19 | ID: user.ID, 20 | Name: user.Name, 21 | } 22 | } 23 | 24 | func convertUserSlice(users db.UserSlice) []*model.User { 25 | result := make([]*model.User, 0, len(users)) 26 | for _, user := range users { 27 | result = append(result, convertUser(user)) 28 | } 29 | return result 30 | } 31 | 32 | func (u *userService) GetUserByID(ctx context.Context, id string) (*model.User, error) { 33 | user, err := db.FindUser(ctx, u.exec, id, 34 | db.UserTableColumns.ID, db.UserTableColumns.Name, 35 | ) 36 | if err != nil { 37 | return nil, err 38 | } 39 | return convertUser(user), nil 40 | } 41 | 42 | func (u *userService) GetUserByName(ctx context.Context, name string) (*model.User, error) { 43 | user, err := db.Users( 44 | qm.Select(db.UserTableColumns.ID, db.UserTableColumns.Name), 45 | db.UserWhere.Name.EQ(name), 46 | // qm.Where("name = ?", name), 47 | ).One(ctx, u.exec) 48 | if err != nil { 49 | return nil, err 50 | } 51 | return convertUser(user), nil 52 | } 53 | 54 | func (u *userService) ListUsersByID(ctx context.Context, IDs []string) ([]*model.User, error) { 55 | users, err := db.Users( 56 | qm.Select(db.UserTableColumns.ID, db.UserTableColumns.Name), 57 | db.UserWhere.ID.IN(IDs), 58 | ).All(ctx, u.exec) 59 | if err != nil { 60 | return nil, err 61 | } 62 | return convertUserSlice(users), nil 63 | } 64 | -------------------------------------------------------------------------------- /graph/services/users_test.go: -------------------------------------------------------------------------------- 1 | package services_test 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/saki-engineering/graphql-sample/graph/model" 8 | "github.com/saki-engineering/graphql-sample/graph/services" 9 | 10 | "github.com/DATA-DOG/go-sqlmock" 11 | "github.com/google/go-cmp/cmp" 12 | ) 13 | 14 | func TestGetUserByID(t *testing.T) { 15 | db, mock, err := sqlmock.New() 16 | if err != nil { 17 | t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) 18 | } 19 | defer db.Close() 20 | 21 | srv := services.New(db) 22 | ctx := context.Background() 23 | mockSetup := func(mock sqlmock.Sqlmock, id, name string) { 24 | columns := []string{"id", "name"} 25 | mock.ExpectQuery(".*").WithArgs(id).WillReturnRows( 26 | sqlmock.NewRows(columns).AddRow(id, name), 27 | ) 28 | } 29 | 30 | tests := []struct { 31 | title string 32 | id string 33 | name string 34 | expected *model.User 35 | }{ 36 | { 37 | title: "normal", 38 | id: "U_ABC", 39 | name: "hsaki", 40 | expected: &model.User{ID: "U_ABC", Name: "hsaki"}, 41 | }, 42 | { 43 | title: "normal-2", 44 | id: "U_DEF", 45 | name: "Alice", 46 | expected: &model.User{ID: "U_DEF", Name: "Alice"}, 47 | }, 48 | } 49 | 50 | for _, tt := range tests { 51 | t.Run(tt.title, func(t *testing.T) { 52 | mockSetup(mock, tt.id, tt.name) 53 | 54 | got, err := srv.GetUserByID(ctx, tt.id) 55 | if err != nil { 56 | t.Error(err) 57 | } 58 | if diff := cmp.Diff(tt.expected, got); diff != "" { 59 | t.Errorf("MakeGatewayInfo() mismatch (-want +got):\n%s", diff) 60 | } 61 | }) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /middlewares/auth/auth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "log" 7 | "net/http" 8 | "strings" 9 | ) 10 | 11 | type userNameKey struct{} 12 | 13 | const ( 14 | tokenPrefix = "UT" 15 | ) 16 | 17 | func AuthMiddleware(next http.Handler) http.Handler { 18 | return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 19 | token := req.Header.Get("Authorization") 20 | if token == "" { 21 | next.ServeHTTP(w, req) 22 | return 23 | } 24 | 25 | userName, err := validateToken(token) 26 | if err != nil { 27 | log.Println(err) 28 | http.Error(w, `{"reason": "invalid token"}`, http.StatusUnauthorized) 29 | return 30 | } 31 | 32 | ctx := context.WithValue(req.Context(), userNameKey{}, userName) 33 | next.ServeHTTP(w, req.WithContext(ctx)) 34 | }) 35 | } 36 | 37 | func GetUserName(ctx context.Context) (string, bool) { 38 | switch v := ctx.Value(userNameKey{}).(type) { 39 | case string: 40 | return v, true 41 | default: 42 | return "", false 43 | } 44 | } 45 | 46 | func validateToken(token string) (string, error) { 47 | tElems := strings.SplitN(token, "_", 2) 48 | if len(tElems) < 2 { 49 | return "", errors.New("invalid token") 50 | } 51 | 52 | tType, tUserName := tElems[0], tElems[1] 53 | if tType != tokenPrefix { 54 | return "", errors.New("invalid token") 55 | } 56 | return tUserName, nil 57 | } 58 | -------------------------------------------------------------------------------- /mock/services/service_mock.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: service.go 3 | 4 | // Package services is a generated GoMock package. 5 | package services 6 | 7 | import ( 8 | context "context" 9 | reflect "reflect" 10 | 11 | gomock "github.com/golang/mock/gomock" 12 | model "github.com/saki-engineering/graphql-sample/graph/model" 13 | ) 14 | 15 | // MockServices is a mock of Services interface. 16 | type MockServices struct { 17 | ctrl *gomock.Controller 18 | recorder *MockServicesMockRecorder 19 | } 20 | 21 | // MockServicesMockRecorder is the mock recorder for MockServices. 22 | type MockServicesMockRecorder struct { 23 | mock *MockServices 24 | } 25 | 26 | // NewMockServices creates a new mock instance. 27 | func NewMockServices(ctrl *gomock.Controller) *MockServices { 28 | mock := &MockServices{ctrl: ctrl} 29 | mock.recorder = &MockServicesMockRecorder{mock} 30 | return mock 31 | } 32 | 33 | // EXPECT returns an object that allows the caller to indicate expected use. 34 | func (m *MockServices) EXPECT() *MockServicesMockRecorder { 35 | return m.recorder 36 | } 37 | 38 | // AddIssueInProjectV2 mocks base method. 39 | func (m *MockServices) AddIssueInProjectV2(ctx context.Context, projectID, issueID string) (*model.ProjectV2Item, error) { 40 | m.ctrl.T.Helper() 41 | ret := m.ctrl.Call(m, "AddIssueInProjectV2", ctx, projectID, issueID) 42 | ret0, _ := ret[0].(*model.ProjectV2Item) 43 | ret1, _ := ret[1].(error) 44 | return ret0, ret1 45 | } 46 | 47 | // AddIssueInProjectV2 indicates an expected call of AddIssueInProjectV2. 48 | func (mr *MockServicesMockRecorder) AddIssueInProjectV2(ctx, projectID, issueID interface{}) *gomock.Call { 49 | mr.mock.ctrl.T.Helper() 50 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddIssueInProjectV2", reflect.TypeOf((*MockServices)(nil).AddIssueInProjectV2), ctx, projectID, issueID) 51 | } 52 | 53 | // AddPullRequestInProjectV2 mocks base method. 54 | func (m *MockServices) AddPullRequestInProjectV2(ctx context.Context, projectID, pullRequestID string) (*model.ProjectV2Item, error) { 55 | m.ctrl.T.Helper() 56 | ret := m.ctrl.Call(m, "AddPullRequestInProjectV2", ctx, projectID, pullRequestID) 57 | ret0, _ := ret[0].(*model.ProjectV2Item) 58 | ret1, _ := ret[1].(error) 59 | return ret0, ret1 60 | } 61 | 62 | // AddPullRequestInProjectV2 indicates an expected call of AddPullRequestInProjectV2. 63 | func (mr *MockServicesMockRecorder) AddPullRequestInProjectV2(ctx, projectID, pullRequestID interface{}) *gomock.Call { 64 | mr.mock.ctrl.T.Helper() 65 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddPullRequestInProjectV2", reflect.TypeOf((*MockServices)(nil).AddPullRequestInProjectV2), ctx, projectID, pullRequestID) 66 | } 67 | 68 | // GetIssueByID mocks base method. 69 | func (m *MockServices) GetIssueByID(ctx context.Context, id string) (*model.Issue, error) { 70 | m.ctrl.T.Helper() 71 | ret := m.ctrl.Call(m, "GetIssueByID", ctx, id) 72 | ret0, _ := ret[0].(*model.Issue) 73 | ret1, _ := ret[1].(error) 74 | return ret0, ret1 75 | } 76 | 77 | // GetIssueByID indicates an expected call of GetIssueByID. 78 | func (mr *MockServicesMockRecorder) GetIssueByID(ctx, id interface{}) *gomock.Call { 79 | mr.mock.ctrl.T.Helper() 80 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIssueByID", reflect.TypeOf((*MockServices)(nil).GetIssueByID), ctx, id) 81 | } 82 | 83 | // GetIssueByRepoAndNumber mocks base method. 84 | func (m *MockServices) GetIssueByRepoAndNumber(ctx context.Context, repoID string, number int) (*model.Issue, error) { 85 | m.ctrl.T.Helper() 86 | ret := m.ctrl.Call(m, "GetIssueByRepoAndNumber", ctx, repoID, number) 87 | ret0, _ := ret[0].(*model.Issue) 88 | ret1, _ := ret[1].(error) 89 | return ret0, ret1 90 | } 91 | 92 | // GetIssueByRepoAndNumber indicates an expected call of GetIssueByRepoAndNumber. 93 | func (mr *MockServicesMockRecorder) GetIssueByRepoAndNumber(ctx, repoID, number interface{}) *gomock.Call { 94 | mr.mock.ctrl.T.Helper() 95 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIssueByRepoAndNumber", reflect.TypeOf((*MockServices)(nil).GetIssueByRepoAndNumber), ctx, repoID, number) 96 | } 97 | 98 | // GetProjectByID mocks base method. 99 | func (m *MockServices) GetProjectByID(ctx context.Context, id string) (*model.ProjectV2, error) { 100 | m.ctrl.T.Helper() 101 | ret := m.ctrl.Call(m, "GetProjectByID", ctx, id) 102 | ret0, _ := ret[0].(*model.ProjectV2) 103 | ret1, _ := ret[1].(error) 104 | return ret0, ret1 105 | } 106 | 107 | // GetProjectByID indicates an expected call of GetProjectByID. 108 | func (mr *MockServicesMockRecorder) GetProjectByID(ctx, id interface{}) *gomock.Call { 109 | mr.mock.ctrl.T.Helper() 110 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProjectByID", reflect.TypeOf((*MockServices)(nil).GetProjectByID), ctx, id) 111 | } 112 | 113 | // GetProjectByOwnerAndNumber mocks base method. 114 | func (m *MockServices) GetProjectByOwnerAndNumber(ctx context.Context, ownerID string, number int) (*model.ProjectV2, error) { 115 | m.ctrl.T.Helper() 116 | ret := m.ctrl.Call(m, "GetProjectByOwnerAndNumber", ctx, ownerID, number) 117 | ret0, _ := ret[0].(*model.ProjectV2) 118 | ret1, _ := ret[1].(error) 119 | return ret0, ret1 120 | } 121 | 122 | // GetProjectByOwnerAndNumber indicates an expected call of GetProjectByOwnerAndNumber. 123 | func (mr *MockServicesMockRecorder) GetProjectByOwnerAndNumber(ctx, ownerID, number interface{}) *gomock.Call { 124 | mr.mock.ctrl.T.Helper() 125 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProjectByOwnerAndNumber", reflect.TypeOf((*MockServices)(nil).GetProjectByOwnerAndNumber), ctx, ownerID, number) 126 | } 127 | 128 | // GetProjectItemByID mocks base method. 129 | func (m *MockServices) GetProjectItemByID(ctx context.Context, id string) (*model.ProjectV2Item, error) { 130 | m.ctrl.T.Helper() 131 | ret := m.ctrl.Call(m, "GetProjectItemByID", ctx, id) 132 | ret0, _ := ret[0].(*model.ProjectV2Item) 133 | ret1, _ := ret[1].(error) 134 | return ret0, ret1 135 | } 136 | 137 | // GetProjectItemByID indicates an expected call of GetProjectItemByID. 138 | func (mr *MockServicesMockRecorder) GetProjectItemByID(ctx, id interface{}) *gomock.Call { 139 | mr.mock.ctrl.T.Helper() 140 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProjectItemByID", reflect.TypeOf((*MockServices)(nil).GetProjectItemByID), ctx, id) 141 | } 142 | 143 | // GetPullRequestByID mocks base method. 144 | func (m *MockServices) GetPullRequestByID(ctx context.Context, id string) (*model.PullRequest, error) { 145 | m.ctrl.T.Helper() 146 | ret := m.ctrl.Call(m, "GetPullRequestByID", ctx, id) 147 | ret0, _ := ret[0].(*model.PullRequest) 148 | ret1, _ := ret[1].(error) 149 | return ret0, ret1 150 | } 151 | 152 | // GetPullRequestByID indicates an expected call of GetPullRequestByID. 153 | func (mr *MockServicesMockRecorder) GetPullRequestByID(ctx, id interface{}) *gomock.Call { 154 | mr.mock.ctrl.T.Helper() 155 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPullRequestByID", reflect.TypeOf((*MockServices)(nil).GetPullRequestByID), ctx, id) 156 | } 157 | 158 | // GetPullRequestByRepoAndNumber mocks base method. 159 | func (m *MockServices) GetPullRequestByRepoAndNumber(ctx context.Context, repoID string, number int) (*model.PullRequest, error) { 160 | m.ctrl.T.Helper() 161 | ret := m.ctrl.Call(m, "GetPullRequestByRepoAndNumber", ctx, repoID, number) 162 | ret0, _ := ret[0].(*model.PullRequest) 163 | ret1, _ := ret[1].(error) 164 | return ret0, ret1 165 | } 166 | 167 | // GetPullRequestByRepoAndNumber indicates an expected call of GetPullRequestByRepoAndNumber. 168 | func (mr *MockServicesMockRecorder) GetPullRequestByRepoAndNumber(ctx, repoID, number interface{}) *gomock.Call { 169 | mr.mock.ctrl.T.Helper() 170 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPullRequestByRepoAndNumber", reflect.TypeOf((*MockServices)(nil).GetPullRequestByRepoAndNumber), ctx, repoID, number) 171 | } 172 | 173 | // GetRepoByFullName mocks base method. 174 | func (m *MockServices) GetRepoByFullName(ctx context.Context, owner, name string) (*model.Repository, error) { 175 | m.ctrl.T.Helper() 176 | ret := m.ctrl.Call(m, "GetRepoByFullName", ctx, owner, name) 177 | ret0, _ := ret[0].(*model.Repository) 178 | ret1, _ := ret[1].(error) 179 | return ret0, ret1 180 | } 181 | 182 | // GetRepoByFullName indicates an expected call of GetRepoByFullName. 183 | func (mr *MockServicesMockRecorder) GetRepoByFullName(ctx, owner, name interface{}) *gomock.Call { 184 | mr.mock.ctrl.T.Helper() 185 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRepoByFullName", reflect.TypeOf((*MockServices)(nil).GetRepoByFullName), ctx, owner, name) 186 | } 187 | 188 | // GetRepoByID mocks base method. 189 | func (m *MockServices) GetRepoByID(ctx context.Context, id string) (*model.Repository, error) { 190 | m.ctrl.T.Helper() 191 | ret := m.ctrl.Call(m, "GetRepoByID", ctx, id) 192 | ret0, _ := ret[0].(*model.Repository) 193 | ret1, _ := ret[1].(error) 194 | return ret0, ret1 195 | } 196 | 197 | // GetRepoByID indicates an expected call of GetRepoByID. 198 | func (mr *MockServicesMockRecorder) GetRepoByID(ctx, id interface{}) *gomock.Call { 199 | mr.mock.ctrl.T.Helper() 200 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRepoByID", reflect.TypeOf((*MockServices)(nil).GetRepoByID), ctx, id) 201 | } 202 | 203 | // GetUserByID mocks base method. 204 | func (m *MockServices) GetUserByID(ctx context.Context, id string) (*model.User, error) { 205 | m.ctrl.T.Helper() 206 | ret := m.ctrl.Call(m, "GetUserByID", ctx, id) 207 | ret0, _ := ret[0].(*model.User) 208 | ret1, _ := ret[1].(error) 209 | return ret0, ret1 210 | } 211 | 212 | // GetUserByID indicates an expected call of GetUserByID. 213 | func (mr *MockServicesMockRecorder) GetUserByID(ctx, id interface{}) *gomock.Call { 214 | mr.mock.ctrl.T.Helper() 215 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserByID", reflect.TypeOf((*MockServices)(nil).GetUserByID), ctx, id) 216 | } 217 | 218 | // GetUserByName mocks base method. 219 | func (m *MockServices) GetUserByName(ctx context.Context, name string) (*model.User, error) { 220 | m.ctrl.T.Helper() 221 | ret := m.ctrl.Call(m, "GetUserByName", ctx, name) 222 | ret0, _ := ret[0].(*model.User) 223 | ret1, _ := ret[1].(error) 224 | return ret0, ret1 225 | } 226 | 227 | // GetUserByName indicates an expected call of GetUserByName. 228 | func (mr *MockServicesMockRecorder) GetUserByName(ctx, name interface{}) *gomock.Call { 229 | mr.mock.ctrl.T.Helper() 230 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserByName", reflect.TypeOf((*MockServices)(nil).GetUserByName), ctx, name) 231 | } 232 | 233 | // ListIssueInRepository mocks base method. 234 | func (m *MockServices) ListIssueInRepository(ctx context.Context, repoID string, after, before *string, first, last *int) (*model.IssueConnection, error) { 235 | m.ctrl.T.Helper() 236 | ret := m.ctrl.Call(m, "ListIssueInRepository", ctx, repoID, after, before, first, last) 237 | ret0, _ := ret[0].(*model.IssueConnection) 238 | ret1, _ := ret[1].(error) 239 | return ret0, ret1 240 | } 241 | 242 | // ListIssueInRepository indicates an expected call of ListIssueInRepository. 243 | func (mr *MockServicesMockRecorder) ListIssueInRepository(ctx, repoID, after, before, first, last interface{}) *gomock.Call { 244 | mr.mock.ctrl.T.Helper() 245 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListIssueInRepository", reflect.TypeOf((*MockServices)(nil).ListIssueInRepository), ctx, repoID, after, before, first, last) 246 | } 247 | 248 | // ListProjectByOwner mocks base method. 249 | func (m *MockServices) ListProjectByOwner(ctx context.Context, ownerID string, after, before *string, first, last *int) (*model.ProjectV2Connection, error) { 250 | m.ctrl.T.Helper() 251 | ret := m.ctrl.Call(m, "ListProjectByOwner", ctx, ownerID, after, before, first, last) 252 | ret0, _ := ret[0].(*model.ProjectV2Connection) 253 | ret1, _ := ret[1].(error) 254 | return ret0, ret1 255 | } 256 | 257 | // ListProjectByOwner indicates an expected call of ListProjectByOwner. 258 | func (mr *MockServicesMockRecorder) ListProjectByOwner(ctx, ownerID, after, before, first, last interface{}) *gomock.Call { 259 | mr.mock.ctrl.T.Helper() 260 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListProjectByOwner", reflect.TypeOf((*MockServices)(nil).ListProjectByOwner), ctx, ownerID, after, before, first, last) 261 | } 262 | 263 | // ListProjectItemOwnedByIssue mocks base method. 264 | func (m *MockServices) ListProjectItemOwnedByIssue(ctx context.Context, issueID string, after, before *string, first, last *int) (*model.ProjectV2ItemConnection, error) { 265 | m.ctrl.T.Helper() 266 | ret := m.ctrl.Call(m, "ListProjectItemOwnedByIssue", ctx, issueID, after, before, first, last) 267 | ret0, _ := ret[0].(*model.ProjectV2ItemConnection) 268 | ret1, _ := ret[1].(error) 269 | return ret0, ret1 270 | } 271 | 272 | // ListProjectItemOwnedByIssue indicates an expected call of ListProjectItemOwnedByIssue. 273 | func (mr *MockServicesMockRecorder) ListProjectItemOwnedByIssue(ctx, issueID, after, before, first, last interface{}) *gomock.Call { 274 | mr.mock.ctrl.T.Helper() 275 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListProjectItemOwnedByIssue", reflect.TypeOf((*MockServices)(nil).ListProjectItemOwnedByIssue), ctx, issueID, after, before, first, last) 276 | } 277 | 278 | // ListProjectItemOwnedByProject mocks base method. 279 | func (m *MockServices) ListProjectItemOwnedByProject(ctx context.Context, projectID string, after, before *string, first, last *int) (*model.ProjectV2ItemConnection, error) { 280 | m.ctrl.T.Helper() 281 | ret := m.ctrl.Call(m, "ListProjectItemOwnedByProject", ctx, projectID, after, before, first, last) 282 | ret0, _ := ret[0].(*model.ProjectV2ItemConnection) 283 | ret1, _ := ret[1].(error) 284 | return ret0, ret1 285 | } 286 | 287 | // ListProjectItemOwnedByProject indicates an expected call of ListProjectItemOwnedByProject. 288 | func (mr *MockServicesMockRecorder) ListProjectItemOwnedByProject(ctx, projectID, after, before, first, last interface{}) *gomock.Call { 289 | mr.mock.ctrl.T.Helper() 290 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListProjectItemOwnedByProject", reflect.TypeOf((*MockServices)(nil).ListProjectItemOwnedByProject), ctx, projectID, after, before, first, last) 291 | } 292 | 293 | // ListProjectItemOwnedByPullRequest mocks base method. 294 | func (m *MockServices) ListProjectItemOwnedByPullRequest(ctx context.Context, pullRequestID string, after, before *string, first, last *int) (*model.ProjectV2ItemConnection, error) { 295 | m.ctrl.T.Helper() 296 | ret := m.ctrl.Call(m, "ListProjectItemOwnedByPullRequest", ctx, pullRequestID, after, before, first, last) 297 | ret0, _ := ret[0].(*model.ProjectV2ItemConnection) 298 | ret1, _ := ret[1].(error) 299 | return ret0, ret1 300 | } 301 | 302 | // ListProjectItemOwnedByPullRequest indicates an expected call of ListProjectItemOwnedByPullRequest. 303 | func (mr *MockServicesMockRecorder) ListProjectItemOwnedByPullRequest(ctx, pullRequestID, after, before, first, last interface{}) *gomock.Call { 304 | mr.mock.ctrl.T.Helper() 305 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListProjectItemOwnedByPullRequest", reflect.TypeOf((*MockServices)(nil).ListProjectItemOwnedByPullRequest), ctx, pullRequestID, after, before, first, last) 306 | } 307 | 308 | // ListPullRequestInRepository mocks base method. 309 | func (m *MockServices) ListPullRequestInRepository(ctx context.Context, repoID string, after, before *string, first, last *int) (*model.PullRequestConnection, error) { 310 | m.ctrl.T.Helper() 311 | ret := m.ctrl.Call(m, "ListPullRequestInRepository", ctx, repoID, after, before, first, last) 312 | ret0, _ := ret[0].(*model.PullRequestConnection) 313 | ret1, _ := ret[1].(error) 314 | return ret0, ret1 315 | } 316 | 317 | // ListPullRequestInRepository indicates an expected call of ListPullRequestInRepository. 318 | func (mr *MockServicesMockRecorder) ListPullRequestInRepository(ctx, repoID, after, before, first, last interface{}) *gomock.Call { 319 | mr.mock.ctrl.T.Helper() 320 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListPullRequestInRepository", reflect.TypeOf((*MockServices)(nil).ListPullRequestInRepository), ctx, repoID, after, before, first, last) 321 | } 322 | 323 | // ListUsersByID mocks base method. 324 | func (m *MockServices) ListUsersByID(ctx context.Context, IDs []string) ([]*model.User, error) { 325 | m.ctrl.T.Helper() 326 | ret := m.ctrl.Call(m, "ListUsersByID", ctx, IDs) 327 | ret0, _ := ret[0].([]*model.User) 328 | ret1, _ := ret[1].(error) 329 | return ret0, ret1 330 | } 331 | 332 | // ListUsersByID indicates an expected call of ListUsersByID. 333 | func (mr *MockServicesMockRecorder) ListUsersByID(ctx, IDs interface{}) *gomock.Call { 334 | mr.mock.ctrl.T.Helper() 335 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListUsersByID", reflect.TypeOf((*MockServices)(nil).ListUsersByID), ctx, IDs) 336 | } 337 | 338 | // MockUserService is a mock of UserService interface. 339 | type MockUserService struct { 340 | ctrl *gomock.Controller 341 | recorder *MockUserServiceMockRecorder 342 | } 343 | 344 | // MockUserServiceMockRecorder is the mock recorder for MockUserService. 345 | type MockUserServiceMockRecorder struct { 346 | mock *MockUserService 347 | } 348 | 349 | // NewMockUserService creates a new mock instance. 350 | func NewMockUserService(ctrl *gomock.Controller) *MockUserService { 351 | mock := &MockUserService{ctrl: ctrl} 352 | mock.recorder = &MockUserServiceMockRecorder{mock} 353 | return mock 354 | } 355 | 356 | // EXPECT returns an object that allows the caller to indicate expected use. 357 | func (m *MockUserService) EXPECT() *MockUserServiceMockRecorder { 358 | return m.recorder 359 | } 360 | 361 | // GetUserByID mocks base method. 362 | func (m *MockUserService) GetUserByID(ctx context.Context, id string) (*model.User, error) { 363 | m.ctrl.T.Helper() 364 | ret := m.ctrl.Call(m, "GetUserByID", ctx, id) 365 | ret0, _ := ret[0].(*model.User) 366 | ret1, _ := ret[1].(error) 367 | return ret0, ret1 368 | } 369 | 370 | // GetUserByID indicates an expected call of GetUserByID. 371 | func (mr *MockUserServiceMockRecorder) GetUserByID(ctx, id interface{}) *gomock.Call { 372 | mr.mock.ctrl.T.Helper() 373 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserByID", reflect.TypeOf((*MockUserService)(nil).GetUserByID), ctx, id) 374 | } 375 | 376 | // GetUserByName mocks base method. 377 | func (m *MockUserService) GetUserByName(ctx context.Context, name string) (*model.User, error) { 378 | m.ctrl.T.Helper() 379 | ret := m.ctrl.Call(m, "GetUserByName", ctx, name) 380 | ret0, _ := ret[0].(*model.User) 381 | ret1, _ := ret[1].(error) 382 | return ret0, ret1 383 | } 384 | 385 | // GetUserByName indicates an expected call of GetUserByName. 386 | func (mr *MockUserServiceMockRecorder) GetUserByName(ctx, name interface{}) *gomock.Call { 387 | mr.mock.ctrl.T.Helper() 388 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserByName", reflect.TypeOf((*MockUserService)(nil).GetUserByName), ctx, name) 389 | } 390 | 391 | // ListUsersByID mocks base method. 392 | func (m *MockUserService) ListUsersByID(ctx context.Context, IDs []string) ([]*model.User, error) { 393 | m.ctrl.T.Helper() 394 | ret := m.ctrl.Call(m, "ListUsersByID", ctx, IDs) 395 | ret0, _ := ret[0].([]*model.User) 396 | ret1, _ := ret[1].(error) 397 | return ret0, ret1 398 | } 399 | 400 | // ListUsersByID indicates an expected call of ListUsersByID. 401 | func (mr *MockUserServiceMockRecorder) ListUsersByID(ctx, IDs interface{}) *gomock.Call { 402 | mr.mock.ctrl.T.Helper() 403 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListUsersByID", reflect.TypeOf((*MockUserService)(nil).ListUsersByID), ctx, IDs) 404 | } 405 | 406 | // MockRepoService is a mock of RepoService interface. 407 | type MockRepoService struct { 408 | ctrl *gomock.Controller 409 | recorder *MockRepoServiceMockRecorder 410 | } 411 | 412 | // MockRepoServiceMockRecorder is the mock recorder for MockRepoService. 413 | type MockRepoServiceMockRecorder struct { 414 | mock *MockRepoService 415 | } 416 | 417 | // NewMockRepoService creates a new mock instance. 418 | func NewMockRepoService(ctrl *gomock.Controller) *MockRepoService { 419 | mock := &MockRepoService{ctrl: ctrl} 420 | mock.recorder = &MockRepoServiceMockRecorder{mock} 421 | return mock 422 | } 423 | 424 | // EXPECT returns an object that allows the caller to indicate expected use. 425 | func (m *MockRepoService) EXPECT() *MockRepoServiceMockRecorder { 426 | return m.recorder 427 | } 428 | 429 | // GetRepoByFullName mocks base method. 430 | func (m *MockRepoService) GetRepoByFullName(ctx context.Context, owner, name string) (*model.Repository, error) { 431 | m.ctrl.T.Helper() 432 | ret := m.ctrl.Call(m, "GetRepoByFullName", ctx, owner, name) 433 | ret0, _ := ret[0].(*model.Repository) 434 | ret1, _ := ret[1].(error) 435 | return ret0, ret1 436 | } 437 | 438 | // GetRepoByFullName indicates an expected call of GetRepoByFullName. 439 | func (mr *MockRepoServiceMockRecorder) GetRepoByFullName(ctx, owner, name interface{}) *gomock.Call { 440 | mr.mock.ctrl.T.Helper() 441 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRepoByFullName", reflect.TypeOf((*MockRepoService)(nil).GetRepoByFullName), ctx, owner, name) 442 | } 443 | 444 | // GetRepoByID mocks base method. 445 | func (m *MockRepoService) GetRepoByID(ctx context.Context, id string) (*model.Repository, error) { 446 | m.ctrl.T.Helper() 447 | ret := m.ctrl.Call(m, "GetRepoByID", ctx, id) 448 | ret0, _ := ret[0].(*model.Repository) 449 | ret1, _ := ret[1].(error) 450 | return ret0, ret1 451 | } 452 | 453 | // GetRepoByID indicates an expected call of GetRepoByID. 454 | func (mr *MockRepoServiceMockRecorder) GetRepoByID(ctx, id interface{}) *gomock.Call { 455 | mr.mock.ctrl.T.Helper() 456 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRepoByID", reflect.TypeOf((*MockRepoService)(nil).GetRepoByID), ctx, id) 457 | } 458 | 459 | // MockIssueService is a mock of IssueService interface. 460 | type MockIssueService struct { 461 | ctrl *gomock.Controller 462 | recorder *MockIssueServiceMockRecorder 463 | } 464 | 465 | // MockIssueServiceMockRecorder is the mock recorder for MockIssueService. 466 | type MockIssueServiceMockRecorder struct { 467 | mock *MockIssueService 468 | } 469 | 470 | // NewMockIssueService creates a new mock instance. 471 | func NewMockIssueService(ctrl *gomock.Controller) *MockIssueService { 472 | mock := &MockIssueService{ctrl: ctrl} 473 | mock.recorder = &MockIssueServiceMockRecorder{mock} 474 | return mock 475 | } 476 | 477 | // EXPECT returns an object that allows the caller to indicate expected use. 478 | func (m *MockIssueService) EXPECT() *MockIssueServiceMockRecorder { 479 | return m.recorder 480 | } 481 | 482 | // GetIssueByID mocks base method. 483 | func (m *MockIssueService) GetIssueByID(ctx context.Context, id string) (*model.Issue, error) { 484 | m.ctrl.T.Helper() 485 | ret := m.ctrl.Call(m, "GetIssueByID", ctx, id) 486 | ret0, _ := ret[0].(*model.Issue) 487 | ret1, _ := ret[1].(error) 488 | return ret0, ret1 489 | } 490 | 491 | // GetIssueByID indicates an expected call of GetIssueByID. 492 | func (mr *MockIssueServiceMockRecorder) GetIssueByID(ctx, id interface{}) *gomock.Call { 493 | mr.mock.ctrl.T.Helper() 494 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIssueByID", reflect.TypeOf((*MockIssueService)(nil).GetIssueByID), ctx, id) 495 | } 496 | 497 | // GetIssueByRepoAndNumber mocks base method. 498 | func (m *MockIssueService) GetIssueByRepoAndNumber(ctx context.Context, repoID string, number int) (*model.Issue, error) { 499 | m.ctrl.T.Helper() 500 | ret := m.ctrl.Call(m, "GetIssueByRepoAndNumber", ctx, repoID, number) 501 | ret0, _ := ret[0].(*model.Issue) 502 | ret1, _ := ret[1].(error) 503 | return ret0, ret1 504 | } 505 | 506 | // GetIssueByRepoAndNumber indicates an expected call of GetIssueByRepoAndNumber. 507 | func (mr *MockIssueServiceMockRecorder) GetIssueByRepoAndNumber(ctx, repoID, number interface{}) *gomock.Call { 508 | mr.mock.ctrl.T.Helper() 509 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIssueByRepoAndNumber", reflect.TypeOf((*MockIssueService)(nil).GetIssueByRepoAndNumber), ctx, repoID, number) 510 | } 511 | 512 | // ListIssueInRepository mocks base method. 513 | func (m *MockIssueService) ListIssueInRepository(ctx context.Context, repoID string, after, before *string, first, last *int) (*model.IssueConnection, error) { 514 | m.ctrl.T.Helper() 515 | ret := m.ctrl.Call(m, "ListIssueInRepository", ctx, repoID, after, before, first, last) 516 | ret0, _ := ret[0].(*model.IssueConnection) 517 | ret1, _ := ret[1].(error) 518 | return ret0, ret1 519 | } 520 | 521 | // ListIssueInRepository indicates an expected call of ListIssueInRepository. 522 | func (mr *MockIssueServiceMockRecorder) ListIssueInRepository(ctx, repoID, after, before, first, last interface{}) *gomock.Call { 523 | mr.mock.ctrl.T.Helper() 524 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListIssueInRepository", reflect.TypeOf((*MockIssueService)(nil).ListIssueInRepository), ctx, repoID, after, before, first, last) 525 | } 526 | 527 | // MockPullRequestService is a mock of PullRequestService interface. 528 | type MockPullRequestService struct { 529 | ctrl *gomock.Controller 530 | recorder *MockPullRequestServiceMockRecorder 531 | } 532 | 533 | // MockPullRequestServiceMockRecorder is the mock recorder for MockPullRequestService. 534 | type MockPullRequestServiceMockRecorder struct { 535 | mock *MockPullRequestService 536 | } 537 | 538 | // NewMockPullRequestService creates a new mock instance. 539 | func NewMockPullRequestService(ctrl *gomock.Controller) *MockPullRequestService { 540 | mock := &MockPullRequestService{ctrl: ctrl} 541 | mock.recorder = &MockPullRequestServiceMockRecorder{mock} 542 | return mock 543 | } 544 | 545 | // EXPECT returns an object that allows the caller to indicate expected use. 546 | func (m *MockPullRequestService) EXPECT() *MockPullRequestServiceMockRecorder { 547 | return m.recorder 548 | } 549 | 550 | // GetPullRequestByID mocks base method. 551 | func (m *MockPullRequestService) GetPullRequestByID(ctx context.Context, id string) (*model.PullRequest, error) { 552 | m.ctrl.T.Helper() 553 | ret := m.ctrl.Call(m, "GetPullRequestByID", ctx, id) 554 | ret0, _ := ret[0].(*model.PullRequest) 555 | ret1, _ := ret[1].(error) 556 | return ret0, ret1 557 | } 558 | 559 | // GetPullRequestByID indicates an expected call of GetPullRequestByID. 560 | func (mr *MockPullRequestServiceMockRecorder) GetPullRequestByID(ctx, id interface{}) *gomock.Call { 561 | mr.mock.ctrl.T.Helper() 562 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPullRequestByID", reflect.TypeOf((*MockPullRequestService)(nil).GetPullRequestByID), ctx, id) 563 | } 564 | 565 | // GetPullRequestByRepoAndNumber mocks base method. 566 | func (m *MockPullRequestService) GetPullRequestByRepoAndNumber(ctx context.Context, repoID string, number int) (*model.PullRequest, error) { 567 | m.ctrl.T.Helper() 568 | ret := m.ctrl.Call(m, "GetPullRequestByRepoAndNumber", ctx, repoID, number) 569 | ret0, _ := ret[0].(*model.PullRequest) 570 | ret1, _ := ret[1].(error) 571 | return ret0, ret1 572 | } 573 | 574 | // GetPullRequestByRepoAndNumber indicates an expected call of GetPullRequestByRepoAndNumber. 575 | func (mr *MockPullRequestServiceMockRecorder) GetPullRequestByRepoAndNumber(ctx, repoID, number interface{}) *gomock.Call { 576 | mr.mock.ctrl.T.Helper() 577 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPullRequestByRepoAndNumber", reflect.TypeOf((*MockPullRequestService)(nil).GetPullRequestByRepoAndNumber), ctx, repoID, number) 578 | } 579 | 580 | // ListPullRequestInRepository mocks base method. 581 | func (m *MockPullRequestService) ListPullRequestInRepository(ctx context.Context, repoID string, after, before *string, first, last *int) (*model.PullRequestConnection, error) { 582 | m.ctrl.T.Helper() 583 | ret := m.ctrl.Call(m, "ListPullRequestInRepository", ctx, repoID, after, before, first, last) 584 | ret0, _ := ret[0].(*model.PullRequestConnection) 585 | ret1, _ := ret[1].(error) 586 | return ret0, ret1 587 | } 588 | 589 | // ListPullRequestInRepository indicates an expected call of ListPullRequestInRepository. 590 | func (mr *MockPullRequestServiceMockRecorder) ListPullRequestInRepository(ctx, repoID, after, before, first, last interface{}) *gomock.Call { 591 | mr.mock.ctrl.T.Helper() 592 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListPullRequestInRepository", reflect.TypeOf((*MockPullRequestService)(nil).ListPullRequestInRepository), ctx, repoID, after, before, first, last) 593 | } 594 | 595 | // MockProjectService is a mock of ProjectService interface. 596 | type MockProjectService struct { 597 | ctrl *gomock.Controller 598 | recorder *MockProjectServiceMockRecorder 599 | } 600 | 601 | // MockProjectServiceMockRecorder is the mock recorder for MockProjectService. 602 | type MockProjectServiceMockRecorder struct { 603 | mock *MockProjectService 604 | } 605 | 606 | // NewMockProjectService creates a new mock instance. 607 | func NewMockProjectService(ctrl *gomock.Controller) *MockProjectService { 608 | mock := &MockProjectService{ctrl: ctrl} 609 | mock.recorder = &MockProjectServiceMockRecorder{mock} 610 | return mock 611 | } 612 | 613 | // EXPECT returns an object that allows the caller to indicate expected use. 614 | func (m *MockProjectService) EXPECT() *MockProjectServiceMockRecorder { 615 | return m.recorder 616 | } 617 | 618 | // GetProjectByID mocks base method. 619 | func (m *MockProjectService) GetProjectByID(ctx context.Context, id string) (*model.ProjectV2, error) { 620 | m.ctrl.T.Helper() 621 | ret := m.ctrl.Call(m, "GetProjectByID", ctx, id) 622 | ret0, _ := ret[0].(*model.ProjectV2) 623 | ret1, _ := ret[1].(error) 624 | return ret0, ret1 625 | } 626 | 627 | // GetProjectByID indicates an expected call of GetProjectByID. 628 | func (mr *MockProjectServiceMockRecorder) GetProjectByID(ctx, id interface{}) *gomock.Call { 629 | mr.mock.ctrl.T.Helper() 630 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProjectByID", reflect.TypeOf((*MockProjectService)(nil).GetProjectByID), ctx, id) 631 | } 632 | 633 | // GetProjectByOwnerAndNumber mocks base method. 634 | func (m *MockProjectService) GetProjectByOwnerAndNumber(ctx context.Context, ownerID string, number int) (*model.ProjectV2, error) { 635 | m.ctrl.T.Helper() 636 | ret := m.ctrl.Call(m, "GetProjectByOwnerAndNumber", ctx, ownerID, number) 637 | ret0, _ := ret[0].(*model.ProjectV2) 638 | ret1, _ := ret[1].(error) 639 | return ret0, ret1 640 | } 641 | 642 | // GetProjectByOwnerAndNumber indicates an expected call of GetProjectByOwnerAndNumber. 643 | func (mr *MockProjectServiceMockRecorder) GetProjectByOwnerAndNumber(ctx, ownerID, number interface{}) *gomock.Call { 644 | mr.mock.ctrl.T.Helper() 645 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProjectByOwnerAndNumber", reflect.TypeOf((*MockProjectService)(nil).GetProjectByOwnerAndNumber), ctx, ownerID, number) 646 | } 647 | 648 | // ListProjectByOwner mocks base method. 649 | func (m *MockProjectService) ListProjectByOwner(ctx context.Context, ownerID string, after, before *string, first, last *int) (*model.ProjectV2Connection, error) { 650 | m.ctrl.T.Helper() 651 | ret := m.ctrl.Call(m, "ListProjectByOwner", ctx, ownerID, after, before, first, last) 652 | ret0, _ := ret[0].(*model.ProjectV2Connection) 653 | ret1, _ := ret[1].(error) 654 | return ret0, ret1 655 | } 656 | 657 | // ListProjectByOwner indicates an expected call of ListProjectByOwner. 658 | func (mr *MockProjectServiceMockRecorder) ListProjectByOwner(ctx, ownerID, after, before, first, last interface{}) *gomock.Call { 659 | mr.mock.ctrl.T.Helper() 660 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListProjectByOwner", reflect.TypeOf((*MockProjectService)(nil).ListProjectByOwner), ctx, ownerID, after, before, first, last) 661 | } 662 | 663 | // MockProjectItemService is a mock of ProjectItemService interface. 664 | type MockProjectItemService struct { 665 | ctrl *gomock.Controller 666 | recorder *MockProjectItemServiceMockRecorder 667 | } 668 | 669 | // MockProjectItemServiceMockRecorder is the mock recorder for MockProjectItemService. 670 | type MockProjectItemServiceMockRecorder struct { 671 | mock *MockProjectItemService 672 | } 673 | 674 | // NewMockProjectItemService creates a new mock instance. 675 | func NewMockProjectItemService(ctrl *gomock.Controller) *MockProjectItemService { 676 | mock := &MockProjectItemService{ctrl: ctrl} 677 | mock.recorder = &MockProjectItemServiceMockRecorder{mock} 678 | return mock 679 | } 680 | 681 | // EXPECT returns an object that allows the caller to indicate expected use. 682 | func (m *MockProjectItemService) EXPECT() *MockProjectItemServiceMockRecorder { 683 | return m.recorder 684 | } 685 | 686 | // AddIssueInProjectV2 mocks base method. 687 | func (m *MockProjectItemService) AddIssueInProjectV2(ctx context.Context, projectID, issueID string) (*model.ProjectV2Item, error) { 688 | m.ctrl.T.Helper() 689 | ret := m.ctrl.Call(m, "AddIssueInProjectV2", ctx, projectID, issueID) 690 | ret0, _ := ret[0].(*model.ProjectV2Item) 691 | ret1, _ := ret[1].(error) 692 | return ret0, ret1 693 | } 694 | 695 | // AddIssueInProjectV2 indicates an expected call of AddIssueInProjectV2. 696 | func (mr *MockProjectItemServiceMockRecorder) AddIssueInProjectV2(ctx, projectID, issueID interface{}) *gomock.Call { 697 | mr.mock.ctrl.T.Helper() 698 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddIssueInProjectV2", reflect.TypeOf((*MockProjectItemService)(nil).AddIssueInProjectV2), ctx, projectID, issueID) 699 | } 700 | 701 | // AddPullRequestInProjectV2 mocks base method. 702 | func (m *MockProjectItemService) AddPullRequestInProjectV2(ctx context.Context, projectID, pullRequestID string) (*model.ProjectV2Item, error) { 703 | m.ctrl.T.Helper() 704 | ret := m.ctrl.Call(m, "AddPullRequestInProjectV2", ctx, projectID, pullRequestID) 705 | ret0, _ := ret[0].(*model.ProjectV2Item) 706 | ret1, _ := ret[1].(error) 707 | return ret0, ret1 708 | } 709 | 710 | // AddPullRequestInProjectV2 indicates an expected call of AddPullRequestInProjectV2. 711 | func (mr *MockProjectItemServiceMockRecorder) AddPullRequestInProjectV2(ctx, projectID, pullRequestID interface{}) *gomock.Call { 712 | mr.mock.ctrl.T.Helper() 713 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddPullRequestInProjectV2", reflect.TypeOf((*MockProjectItemService)(nil).AddPullRequestInProjectV2), ctx, projectID, pullRequestID) 714 | } 715 | 716 | // GetProjectItemByID mocks base method. 717 | func (m *MockProjectItemService) GetProjectItemByID(ctx context.Context, id string) (*model.ProjectV2Item, error) { 718 | m.ctrl.T.Helper() 719 | ret := m.ctrl.Call(m, "GetProjectItemByID", ctx, id) 720 | ret0, _ := ret[0].(*model.ProjectV2Item) 721 | ret1, _ := ret[1].(error) 722 | return ret0, ret1 723 | } 724 | 725 | // GetProjectItemByID indicates an expected call of GetProjectItemByID. 726 | func (mr *MockProjectItemServiceMockRecorder) GetProjectItemByID(ctx, id interface{}) *gomock.Call { 727 | mr.mock.ctrl.T.Helper() 728 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProjectItemByID", reflect.TypeOf((*MockProjectItemService)(nil).GetProjectItemByID), ctx, id) 729 | } 730 | 731 | // ListProjectItemOwnedByIssue mocks base method. 732 | func (m *MockProjectItemService) ListProjectItemOwnedByIssue(ctx context.Context, issueID string, after, before *string, first, last *int) (*model.ProjectV2ItemConnection, error) { 733 | m.ctrl.T.Helper() 734 | ret := m.ctrl.Call(m, "ListProjectItemOwnedByIssue", ctx, issueID, after, before, first, last) 735 | ret0, _ := ret[0].(*model.ProjectV2ItemConnection) 736 | ret1, _ := ret[1].(error) 737 | return ret0, ret1 738 | } 739 | 740 | // ListProjectItemOwnedByIssue indicates an expected call of ListProjectItemOwnedByIssue. 741 | func (mr *MockProjectItemServiceMockRecorder) ListProjectItemOwnedByIssue(ctx, issueID, after, before, first, last interface{}) *gomock.Call { 742 | mr.mock.ctrl.T.Helper() 743 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListProjectItemOwnedByIssue", reflect.TypeOf((*MockProjectItemService)(nil).ListProjectItemOwnedByIssue), ctx, issueID, after, before, first, last) 744 | } 745 | 746 | // ListProjectItemOwnedByProject mocks base method. 747 | func (m *MockProjectItemService) ListProjectItemOwnedByProject(ctx context.Context, projectID string, after, before *string, first, last *int) (*model.ProjectV2ItemConnection, error) { 748 | m.ctrl.T.Helper() 749 | ret := m.ctrl.Call(m, "ListProjectItemOwnedByProject", ctx, projectID, after, before, first, last) 750 | ret0, _ := ret[0].(*model.ProjectV2ItemConnection) 751 | ret1, _ := ret[1].(error) 752 | return ret0, ret1 753 | } 754 | 755 | // ListProjectItemOwnedByProject indicates an expected call of ListProjectItemOwnedByProject. 756 | func (mr *MockProjectItemServiceMockRecorder) ListProjectItemOwnedByProject(ctx, projectID, after, before, first, last interface{}) *gomock.Call { 757 | mr.mock.ctrl.T.Helper() 758 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListProjectItemOwnedByProject", reflect.TypeOf((*MockProjectItemService)(nil).ListProjectItemOwnedByProject), ctx, projectID, after, before, first, last) 759 | } 760 | 761 | // ListProjectItemOwnedByPullRequest mocks base method. 762 | func (m *MockProjectItemService) ListProjectItemOwnedByPullRequest(ctx context.Context, pullRequestID string, after, before *string, first, last *int) (*model.ProjectV2ItemConnection, error) { 763 | m.ctrl.T.Helper() 764 | ret := m.ctrl.Call(m, "ListProjectItemOwnedByPullRequest", ctx, pullRequestID, after, before, first, last) 765 | ret0, _ := ret[0].(*model.ProjectV2ItemConnection) 766 | ret1, _ := ret[1].(error) 767 | return ret0, ret1 768 | } 769 | 770 | // ListProjectItemOwnedByPullRequest indicates an expected call of ListProjectItemOwnedByPullRequest. 771 | func (mr *MockProjectItemServiceMockRecorder) ListProjectItemOwnedByPullRequest(ctx, pullRequestID, after, before, first, last interface{}) *gomock.Call { 772 | mr.mock.ctrl.T.Helper() 773 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListProjectItemOwnedByPullRequest", reflect.TypeOf((*MockProjectItemService)(nil).ListProjectItemOwnedByPullRequest), ctx, pullRequestID, after, before, first, last) 774 | } 775 | -------------------------------------------------------------------------------- /schema.graphqls: -------------------------------------------------------------------------------- 1 | directive @isAuthenticated on FIELD_DEFINITION 2 | 3 | scalar DateTime 4 | 5 | scalar URI 6 | 7 | interface Node { 8 | id: ID! 9 | } 10 | 11 | type PageInfo { 12 | endCursor: String 13 | hasNextPage: Boolean! 14 | hasPreviousPage: Boolean! 15 | startCursor: String 16 | } 17 | 18 | type Repository implements Node { 19 | id: ID! 20 | owner: User! 21 | name: String! 22 | createdAt: DateTime! 23 | issue( 24 | number: Int! 25 | ): Issue 26 | issues( 27 | after: String 28 | before: String 29 | first: Int 30 | last: Int 31 | ): IssueConnection! 32 | pullRequest( 33 | number: Int! 34 | ): PullRequest 35 | pullRequests( 36 | after: String 37 | before: String 38 | first: Int 39 | last: Int 40 | ): PullRequestConnection! 41 | } 42 | 43 | type User implements Node { 44 | id: ID! 45 | name: String! 46 | projectV2( 47 | number: Int! 48 | ): ProjectV2 49 | projectV2s( 50 | after: String 51 | before: String 52 | first: Int 53 | last: Int 54 | ): ProjectV2Connection! 55 | } 56 | 57 | type Issue implements Node { 58 | id: ID! 59 | url: URI! 60 | title: String! 61 | closed: Boolean! 62 | number: Int! 63 | author: User! 64 | repository: Repository! 65 | projectItems( 66 | after: String 67 | before: String 68 | first: Int 69 | last: Int 70 | ): ProjectV2ItemConnection! 71 | } 72 | 73 | type IssueConnection { 74 | edges: [IssueEdge] 75 | nodes: [Issue] 76 | pageInfo: PageInfo! 77 | totalCount: Int! 78 | } 79 | 80 | type IssueEdge { 81 | cursor: String! 82 | node: Issue 83 | } 84 | 85 | type PullRequest implements Node { 86 | id: ID! 87 | baseRefName: String! 88 | closed: Boolean! 89 | headRefName: String! 90 | url: URI! 91 | number: Int! 92 | repository: Repository! 93 | projectItems( 94 | after: String 95 | before: String 96 | first: Int 97 | last: Int 98 | ): ProjectV2ItemConnection! 99 | } 100 | 101 | type PullRequestConnection { 102 | edges: [PullRequestEdge] 103 | nodes: [PullRequest] 104 | pageInfo: PageInfo! 105 | totalCount: Int! 106 | } 107 | 108 | type PullRequestEdge { 109 | cursor: String! 110 | node: PullRequest 111 | } 112 | 113 | type ProjectV2 implements Node { 114 | id: ID! 115 | title: String! 116 | url: URI! 117 | number: Int! 118 | items( 119 | after: String 120 | before: String 121 | first: Int 122 | last: Int 123 | ): ProjectV2ItemConnection! 124 | owner: User! 125 | } 126 | 127 | type ProjectV2Connection { 128 | edges: [ProjectV2Edge] 129 | nodes: [ProjectV2] 130 | pageInfo: PageInfo! 131 | totalCount: Int! 132 | } 133 | 134 | type ProjectV2Edge { 135 | cursor: String! 136 | node: ProjectV2 137 | } 138 | 139 | union ProjectV2ItemContent = Issue | PullRequest 140 | 141 | type ProjectV2Item implements Node { 142 | id: ID! 143 | project: ProjectV2! 144 | content: ProjectV2ItemContent 145 | } 146 | 147 | type ProjectV2ItemConnection { 148 | edges: [ProjectV2ItemEdge] 149 | nodes: [ProjectV2Item] 150 | pageInfo: PageInfo! 151 | totalCount: Int! 152 | } 153 | 154 | type ProjectV2ItemEdge { 155 | cursor: String! 156 | node: ProjectV2Item 157 | } 158 | 159 | type Query { 160 | repository( 161 | name: String! 162 | owner: String! 163 | ): Repository 164 | 165 | user( 166 | name: String! 167 | ): User @isAuthenticated 168 | 169 | node( 170 | id: ID! 171 | ): Node 172 | 173 | } 174 | 175 | input AddProjectV2ItemByIdInput { 176 | contentId: ID! 177 | projectId: ID! 178 | } 179 | 180 | type AddProjectV2ItemByIdPayload { 181 | item: ProjectV2Item 182 | } 183 | 184 | type Mutation { 185 | addProjectV2ItemById( 186 | input: AddProjectV2ItemByIdInput! 187 | ): AddProjectV2ItemByIdPayload 188 | } 189 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | 10 | "github.com/saki-engineering/graphql-sample/graph" 11 | "github.com/saki-engineering/graphql-sample/graph/services" 12 | "github.com/saki-engineering/graphql-sample/internal" 13 | "github.com/saki-engineering/graphql-sample/middlewares/auth" 14 | 15 | "github.com/99designs/gqlgen/graphql/handler" 16 | "github.com/99designs/gqlgen/graphql/handler/extension" 17 | "github.com/99designs/gqlgen/graphql/playground" 18 | _ "github.com/mattn/go-sqlite3" 19 | "github.com/volatiletech/sqlboiler/v4/boil" 20 | ) 21 | 22 | const ( 23 | defaultPort = "8080" 24 | dbFile = "./mygraphql.db" 25 | ) 26 | 27 | func main() { 28 | port := os.Getenv("PORT") 29 | if port == "" { 30 | port = defaultPort 31 | } 32 | 33 | db, err := sql.Open("sqlite3", fmt.Sprintf("%s?_foreign_keys=on", dbFile)) 34 | if err != nil { 35 | log.Fatal(err) 36 | } 37 | defer db.Close() 38 | 39 | service := services.New(db) 40 | srv := handler.NewDefaultServer(internal.NewExecutableSchema(internal.Config{ 41 | Resolvers: &graph.Resolver{ 42 | Srv: service, 43 | Loaders: graph.NewLoaders(service), 44 | }, 45 | Directives: graph.Directive, 46 | Complexity: graph.ComplexityConfig(), 47 | })) 48 | srv.Use(extension.FixedComplexityLimit(10)) 49 | // srv.AroundRootFields(func(ctx context.Context, next graphql.RootResolver) graphql.Marshaler { 50 | // log.Println("before RootResolver") 51 | // res := next(ctx) 52 | // defer func() { 53 | // var b bytes.Buffer 54 | // res.MarshalGQL(&b) 55 | // log.Println("after RootResolver", b.String()) 56 | // }() 57 | // return res 58 | // }) 59 | // srv.AroundOperations(func(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler { 60 | // log.Println("before OperationHandler") 61 | // res := next(ctx) 62 | // defer log.Println("after OperationHandler", res) 63 | // return res 64 | // }) 65 | // srv.AroundResponses(func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response { 66 | // log.Println("before ResponseHandler") 67 | // res := next(ctx) 68 | // defer log.Println("after ResponseHandler", res) 69 | // return res 70 | // }) 71 | // srv.AroundFields(func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) { 72 | // log.Println("before Resolver") 73 | // res, err = next(ctx) 74 | // defer log.Println("after Resolver", res) 75 | // return 76 | // }) 77 | 78 | boil.DebugMode = true 79 | 80 | http.Handle("/", playground.Handler("GraphQL playground", "/query")) 81 | http.Handle("/query", auth.AuthMiddleware(srv)) 82 | 83 | log.Printf("connect to http://localhost:%s/ for GraphQL playground", port) 84 | log.Fatal(http.ListenAndServe(":"+port, nil)) 85 | } 86 | -------------------------------------------------------------------------------- /server_test.go: -------------------------------------------------------------------------------- 1 | package main_test 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "encoding/json" 7 | "flag" 8 | "io" 9 | "net/http" 10 | "net/http/httptest" 11 | "os" 12 | "testing" 13 | "time" 14 | 15 | "github.com/saki-engineering/graphql-sample/graph" 16 | "github.com/saki-engineering/graphql-sample/graph/model" 17 | "github.com/saki-engineering/graphql-sample/internal" 18 | "github.com/saki-engineering/graphql-sample/mock/services" 19 | 20 | "github.com/99designs/gqlgen/graphql/handler" 21 | "github.com/golang/mock/gomock" 22 | "github.com/tenntenn/golden" 23 | ) 24 | 25 | var ( 26 | flagUpdate bool 27 | goldenDir string = "./testdata/golden/" 28 | ) 29 | 30 | func init() { 31 | flag.BoolVar(&flagUpdate, "update", false, "update golden files") 32 | } 33 | 34 | func getRequestBody(t *testing.T, testdata, name string) io.Reader { 35 | t.Helper() 36 | 37 | queryBody, err := os.ReadFile(testdata + name + ".golden") 38 | if err != nil { 39 | t.Fatal(err) 40 | } 41 | query := struct{ Query string }{ 42 | string(queryBody), 43 | } 44 | reqBody := bytes.Buffer{} 45 | if err := json.NewEncoder(&reqBody).Encode(&query); err != nil { 46 | t.Fatal("error encode", err) 47 | } 48 | return &reqBody 49 | } 50 | 51 | func getResponseBody(t *testing.T, res *http.Response) string { 52 | t.Helper() 53 | 54 | raw, err := io.ReadAll(res.Body) 55 | if err != nil { 56 | t.Fatal("error read body", err) 57 | } 58 | var got bytes.Buffer 59 | if err := json.Indent(&got, raw, "", "\t"); err != nil { 60 | t.Fatal("json.Indent", err) 61 | } 62 | return got.String() 63 | } 64 | 65 | func TestNodeRepository(t *testing.T) { 66 | ctrl := gomock.NewController(t) 67 | t.Cleanup(func() { ctrl.Finish() }) 68 | 69 | repoID := "REPO_1" 70 | ownerID := "U_1" 71 | sm := services.NewMockServices(ctrl) 72 | sm.EXPECT().GetRepoByID(gomock.Any(), repoID).Return(&model.Repository{ 73 | ID: repoID, 74 | Owner: &model.User{ID: ownerID}, 75 | Name: "repo1", 76 | CreatedAt: time.Date(2022, 12, 30, 0, 12, 21, 0, time.UTC), 77 | }, nil) 78 | sm.EXPECT().GetUserByID(gomock.Any(), ownerID).Return(&model.User{ 79 | ID: ownerID, 80 | Name: "hsaki", 81 | }, nil) 82 | 83 | srv := httptest.NewServer( 84 | handler.NewDefaultServer(internal.NewExecutableSchema(internal.Config{Resolvers: &graph.Resolver{ 85 | Srv: sm, 86 | Loaders: graph.NewLoaders(sm), 87 | }})), 88 | ) 89 | t.Cleanup(func() { srv.Close() }) 90 | 91 | reqBody := getRequestBody(t, goldenDir, t.Name()+"In.gpl") 92 | req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, srv.URL, reqBody) 93 | if err != nil { 94 | t.Fatal("error new request", err) 95 | } 96 | req.Header.Add("Content-Type", "application/json") 97 | res, err := http.DefaultClient.Do(req) 98 | if err != nil { 99 | t.Fatal("error request", err) 100 | } 101 | t.Cleanup(func() { res.Body.Close() }) 102 | 103 | got := getResponseBody(t, res) 104 | if diff := golden.Check(t, flagUpdate, goldenDir, t.Name()+"Out.json", got); diff != "" { 105 | t.Errorf("mismatch (-want +got):\n%s", diff) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/bash 2 | 3 | set -eu 4 | 5 | readonly DBFILE_NAME="mygraphql.db" 6 | 7 | # Create DB file 8 | if [ ! -e ${DBFILE_NAME} ];then 9 | echo ".open ${DBFILE_NAME}" | sqlite3 10 | fi 11 | 12 | # Create DB Tables 13 | echo "creating tables..." 14 | sqlite3 ${DBFILE_NAME} " 15 | PRAGMA foreign_keys = ON; 16 | 17 | CREATE TABLE IF NOT EXISTS users(\ 18 | id TEXT PRIMARY KEY NOT NULL,\ 19 | name TEXT NOT NULL,\ 20 | project_v2 TEXT\ 21 | ); 22 | 23 | CREATE TABLE IF NOT EXISTS repositories(\ 24 | id TEXT PRIMARY KEY NOT NULL,\ 25 | owner TEXT NOT NULL,\ 26 | name TEXT NOT NULL,\ 27 | created_at DATETIME NOT NULL DEFAULT (DATETIME('now','localtime')),\ 28 | FOREIGN KEY (owner) REFERENCES users(id)\ 29 | ); 30 | 31 | CREATE TABLE IF NOT EXISTS issues(\ 32 | id TEXT PRIMARY KEY NOT NULL,\ 33 | url TEXT NOT NULL,\ 34 | title TEXT NOT NULL,\ 35 | closed INTEGER NOT NULL DEFAULT 0,\ 36 | number INTEGER NOT NULL,\ 37 | author TEXT NOT NULL,\ 38 | repository TEXT NOT NULL,\ 39 | CHECK (closed IN (0, 1)),\ 40 | FOREIGN KEY (repository) REFERENCES repositories(id),\ 41 | FOREIGN KEY (author) REFERENCES users(id)\ 42 | ); 43 | 44 | CREATE TABLE IF NOT EXISTS projects(\ 45 | id TEXT PRIMARY KEY NOT NULL,\ 46 | title TEXT NOT NULL,\ 47 | url TEXT NOT NULL,\ 48 | number INTEGER NOT NULL,\ 49 | owner TEXT NOT NULL,\ 50 | FOREIGN KEY (owner) REFERENCES users(id)\ 51 | ); 52 | 53 | CREATE TABLE IF NOT EXISTS pullrequests(\ 54 | id TEXT PRIMARY KEY NOT NULL,\ 55 | base_ref_name TEXT NOT NULL,\ 56 | closed INTEGER NOT NULL DEFAULT 0,\ 57 | head_ref_name TEXT NOT NULL,\ 58 | url TEXT NOT NULL,\ 59 | number INTEGER NOT NULL,\ 60 | repository TEXT NOT NULL,\ 61 | CHECK (closed IN (0, 1)),\ 62 | FOREIGN KEY (repository) REFERENCES repositories(id)\ 63 | ); 64 | 65 | CREATE TABLE IF NOT EXISTS projectcards(\ 66 | id TEXT PRIMARY KEY NOT NULL,\ 67 | project TEXT NOT NULL,\ 68 | issue TEXT,\ 69 | pullrequest TEXT,\ 70 | FOREIGN KEY (project) REFERENCES projects(id),\ 71 | FOREIGN KEY (issue) REFERENCES issues(id),\ 72 | FOREIGN KEY (pullrequest) REFERENCES pullrequests(id),\ 73 | CHECK (issue IS NOT NULL OR pullrequest IS NOT NULL)\ 74 | ); 75 | " 76 | 77 | # Insert initial data 78 | echo "inserting initial data..." 79 | sqlite3 ${DBFILE_NAME} " 80 | PRAGMA foreign_keys = ON; 81 | 82 | INSERT INTO users(id, name) VALUES\ 83 | ('U_1', 'hsaki') 84 | ; 85 | 86 | INSERT INTO repositories(id, owner, name) VALUES\ 87 | ('REPO_1', 'U_1', 'repo1') 88 | ; 89 | 90 | INSERT INTO issues(id, url, title, closed, number, author, repository) VALUES\ 91 | ('ISSUE_1', 'http://example.com/repo1/issue/1', 'First Issue', 1, 1, 'U_1', 'REPO_1'),\ 92 | ('ISSUE_2', 'http://example.com/repo1/issue/2', 'Second Issue', 0, 2, 'U_1', 'REPO_1'),\ 93 | ('ISSUE_3', 'http://example.com/repo1/issue/3', 'Third Issue', 0, 3, 'U_1', 'REPO_1'),\ 94 | ('ISSUE_4', 'http://example.com/repo1/issue/4', '', 0, 4, 'U_1', 'REPO_1'),\ 95 | ('ISSUE_5', 'http://example.com/repo1/issue/5', '', 0, 5, 'U_1', 'REPO_1'),\ 96 | ('ISSUE_6', 'http://example.com/repo1/issue/6', '', 0, 6, 'U_1', 'REPO_1'),\ 97 | ('ISSUE_7', 'http://example.com/repo1/issue/7', '', 0, 7, 'U_1', 'REPO_1')\ 98 | ; 99 | 100 | INSERT INTO projects(id, title, url, number, owner) VALUES\ 101 | ('PJ_1', 'My Project', 'http://example.com/project/1', 1, 'U_1'),\ 102 | ('PJ_2', 'My Project 2', 'http://example.com/project/2', 2, 'U_1')\ 103 | ; 104 | 105 | INSERT INTO pullrequests(id, base_ref_name, closed, head_ref_name, url, number, repository) VALUES\ 106 | ('PR_1', 'main', 1, 'feature/kinou1', 'http://example.com/repo1/pr/1', 1, 'REPO_1'),\ 107 | ('PR_2', 'main', 0, 'feature/kinou2', 'http://example.com/repo1/pr/2', 2, 'REPO_1')\ 108 | ; 109 | " 110 | -------------------------------------------------------------------------------- /sqlboiler.toml: -------------------------------------------------------------------------------- 1 | pkgname="db" 2 | output="graph/db" 3 | wipe=true 4 | add-global-variants=false 5 | no-tests=true 6 | 7 | [sqlite3] 8 | dbname = "./mygraphql.db" -------------------------------------------------------------------------------- /testdata/golden/TestNodeRepositoryIn.gpl.golden: -------------------------------------------------------------------------------- 1 | query { 2 | node(id: "REPO_1") { 3 | id 4 | ... on Repository { 5 | name 6 | createdAt 7 | owner{ 8 | name 9 | id 10 | } 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /testdata/golden/TestNodeRepositoryOut.json.golden: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "node": { 4 | "id": "REPO_1", 5 | "name": "repo1", 6 | "createdAt": "2022-12-30T00:12:21Z", 7 | "owner": { 8 | "name": "hsaki", 9 | "id": "U_1" 10 | } 11 | } 12 | } 13 | } --------------------------------------------------------------------------------