add a new field to the Profession graphql type (Qualifications)
This commit is contained in:
parent
827a353fba
commit
e936b9b0ed
2
go.mod
2
go.mod
|
@ -19,7 +19,7 @@ require (
|
|||
github.com/sirupsen/logrus v1.8.0
|
||||
github.com/vektah/gqlparser/v2 v2.1.0
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
|
||||
golang.org/x/sys v0.0.0-20210313110737-8e9fff1a3a18 // indirect
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
|
4
go.sum
4
go.sum
|
@ -223,8 +223,8 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210218155724-8ebf48af031b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210313110737-8e9fff1a3a18 h1:jxr7/dEo+rR29uEBoLSWJ1tRHCFAMwFbGUU9nRqzpds=
|
||||
golang.org/x/sys v0.0.0-20210313110737-8e9fff1a3a18/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 h1:EZ2mChiOa8udjfp6rRmswTbtZN/QzUQp4ptM4rnjHvc=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
|
|
|
@ -2,24 +2,31 @@ package dataloader
|
|||
|
||||
import (
|
||||
"context"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/profession"
|
||||
"time"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/qualification"
|
||||
)
|
||||
|
||||
const (
|
||||
wait = 2 * time.Millisecond
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ProfessionRepo profession.Repository
|
||||
QualificationRepo qualification.Repository
|
||||
}
|
||||
|
||||
type DataLoader struct {
|
||||
QualificationByID *QualificationLoader
|
||||
QualificationByID *QualificationLoader
|
||||
QualificationsByProfessionID *QualificationSliceByProfessionIDLoader
|
||||
}
|
||||
|
||||
func New(cfg Config) *DataLoader {
|
||||
return &DataLoader{
|
||||
QualificationByID: NewQualificationLoader(QualificationLoaderConfig{
|
||||
Wait: 2 * time.Millisecond,
|
||||
Wait: wait,
|
||||
Fetch: func(ids []int) ([]*models.Qualification, []error) {
|
||||
qualificationsNotInOrder, _, err := cfg.QualificationRepo.Fetch(context.Background(), &qualification.FetchConfig{
|
||||
Filter: &models.QualificationFilter{
|
||||
|
@ -41,5 +48,22 @@ func New(cfg Config) *DataLoader {
|
|||
return qualifications, nil
|
||||
},
|
||||
}),
|
||||
QualificationsByProfessionID: NewQualificationSliceByProfessionIDLoader(QualificationSliceByProfessionIDLoaderConfig{
|
||||
Wait: wait,
|
||||
Fetch: func(ids []int) ([][]*models.Qualification, []error) {
|
||||
m, err := cfg.ProfessionRepo.GetAssociatedQualifications(context.Background(), ids...)
|
||||
if err != nil {
|
||||
return nil, []error{err}
|
||||
}
|
||||
|
||||
qualifications := make([][]*models.Qualification, len(ids))
|
||||
|
||||
for i, id := range ids {
|
||||
qualifications[i] = m[id]
|
||||
}
|
||||
|
||||
return qualifications, nil
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -194,5 +194,8 @@ func getComplexityRoot() generated.ComplexityRoot {
|
|||
complexityRoot.Mutation.UpdateUser = func(childComplexity int, id int, input models.UserInput) int {
|
||||
return 200 + childComplexity
|
||||
}
|
||||
complexityRoot.Profession.Qualifications = func(childComplexity int) int {
|
||||
return 50 + childComplexity
|
||||
}
|
||||
return complexityRoot
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ type Config struct {
|
|||
|
||||
type ResolverRoot interface {
|
||||
Mutation() MutationResolver
|
||||
Profession() ProfessionResolver
|
||||
Query() QueryResolver
|
||||
Question() QuestionResolver
|
||||
}
|
||||
|
@ -66,11 +67,12 @@ type ComplexityRoot struct {
|
|||
}
|
||||
|
||||
Profession struct {
|
||||
CreatedAt func(childComplexity int) int
|
||||
Description func(childComplexity int) int
|
||||
ID func(childComplexity int) int
|
||||
Name func(childComplexity int) int
|
||||
Slug func(childComplexity int) int
|
||||
CreatedAt func(childComplexity int) int
|
||||
Description func(childComplexity int) int
|
||||
ID func(childComplexity int) int
|
||||
Name func(childComplexity int) int
|
||||
Qualifications func(childComplexity int) int
|
||||
Slug func(childComplexity int) int
|
||||
}
|
||||
|
||||
ProfessionList struct {
|
||||
|
@ -166,6 +168,9 @@ type MutationResolver interface {
|
|||
DeleteUsers(ctx context.Context, ids []int) ([]*models.User, error)
|
||||
SignIn(ctx context.Context, email string, password string, staySignedIn *bool) (*UserWithToken, error)
|
||||
}
|
||||
type ProfessionResolver interface {
|
||||
Qualifications(ctx context.Context, obj *models.Profession) ([]*models.Qualification, error)
|
||||
}
|
||||
type QueryResolver interface {
|
||||
Professions(ctx context.Context, filter *models.ProfessionFilter, limit *int, offset *int, sort []string) (*ProfessionList, error)
|
||||
Profession(ctx context.Context, id *int, slug *string) (*models.Profession, error)
|
||||
|
@ -392,6 +397,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||
|
||||
return e.complexity.Profession.Name(childComplexity), true
|
||||
|
||||
case "Profession.qualifications":
|
||||
if e.complexity.Profession.Qualifications == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Profession.Qualifications(childComplexity), true
|
||||
|
||||
case "Profession.slug":
|
||||
if e.complexity.Profession.Slug == nil {
|
||||
break
|
||||
|
@ -860,6 +872,7 @@ directive @hasRole(role: Role!) on FIELD_DEFINITION
|
|||
name: String!
|
||||
description: String
|
||||
createdAt: Time!
|
||||
qualifications: [Qualification!]! @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type ProfessionList {
|
||||
|
@ -3011,6 +3024,41 @@ func (ec *executionContext) _Profession_createdAt(ctx context.Context, field gra
|
|||
return ec.marshalNTime2timeᚐTime(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Profession_qualifications(ctx context.Context, field graphql.CollectedField, obj *models.Profession) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "Profession",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: true,
|
||||
IsResolver: true,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return ec.resolvers.Profession().Qualifications(rctx, obj)
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.([]*models.Qualification)
|
||||
fc.Result = res
|
||||
return ec.marshalNQualification2ᚕᚖgithubᚗcomᚋzdamᚑegzaminᚑzawodowyᚋbackendᚋinternalᚋmodelsᚐQualificationᚄ(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _ProfessionList_total(ctx context.Context, field graphql.CollectedField, obj *ProfessionList) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -7085,25 +7133,39 @@ func (ec *executionContext) _Profession(ctx context.Context, sel ast.SelectionSe
|
|||
case "id":
|
||||
out.Values[i] = ec._Profession_id(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
case "slug":
|
||||
out.Values[i] = ec._Profession_slug(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
case "name":
|
||||
out.Values[i] = ec._Profession_name(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
case "description":
|
||||
out.Values[i] = ec._Profession_description(ctx, field, obj)
|
||||
case "createdAt":
|
||||
out.Values[i] = ec._Profession_createdAt(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
case "qualifications":
|
||||
field := field
|
||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
}
|
||||
}()
|
||||
res = ec._Profession_qualifications(ctx, field, obj)
|
||||
if res == graphql.Null {
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
return res
|
||||
})
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
}
|
||||
|
@ -7967,6 +8029,43 @@ func (ec *executionContext) marshalNProfessionList2ᚖgithubᚗcomᚋzdamᚑegza
|
|||
return ec._ProfessionList(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNQualification2ᚕᚖgithubᚗcomᚋzdamᚑegzaminᚑzawodowyᚋbackendᚋinternalᚋmodelsᚐQualificationᚄ(ctx context.Context, sel ast.SelectionSet, v []*models.Qualification) graphql.Marshaler {
|
||||
ret := make(graphql.Array, len(v))
|
||||
var wg sync.WaitGroup
|
||||
isLen1 := len(v) == 1
|
||||
if !isLen1 {
|
||||
wg.Add(len(v))
|
||||
}
|
||||
for i := range v {
|
||||
i := i
|
||||
fc := &graphql.FieldContext{
|
||||
Index: &i,
|
||||
Result: &v[i],
|
||||
}
|
||||
ctx := graphql.WithFieldContext(ctx, fc)
|
||||
f := func(i int) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = nil
|
||||
}
|
||||
}()
|
||||
if !isLen1 {
|
||||
defer wg.Done()
|
||||
}
|
||||
ret[i] = ec.marshalNQualification2ᚖgithubᚗcomᚋzdamᚑegzaminᚑzawodowyᚋbackendᚋinternalᚋmodelsᚐQualification(ctx, sel, v[i])
|
||||
}
|
||||
if isLen1 {
|
||||
f(i)
|
||||
} else {
|
||||
go f(i)
|
||||
}
|
||||
|
||||
}
|
||||
wg.Wait()
|
||||
return ret
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNQualification2ᚖgithubᚗcomᚋzdamᚑegzaminᚑzawodowyᚋbackendᚋinternalᚋmodelsᚐQualification(ctx context.Context, sel ast.SelectionSet, v *models.Qualification) graphql.Marshaler {
|
||||
if v == nil {
|
||||
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
||||
|
|
|
@ -5,6 +5,7 @@ package resolvers
|
|||
|
||||
import (
|
||||
"context"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/gin/middleware"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/graphql/generated"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
|
@ -57,3 +58,15 @@ func (r *queryResolver) Profession(ctx context.Context, id *int, slug *string) (
|
|||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *professionResolver) Qualifications(
|
||||
ctx context.Context,
|
||||
obj *models.Profession,
|
||||
) ([]*models.Qualification, error) {
|
||||
if obj != nil {
|
||||
if dataloader, err := middleware.DataLoaderFromContext(ctx); err == nil && dataloader != nil {
|
||||
return dataloader.QualificationsByProfessionID.Load(obj.ID)
|
||||
}
|
||||
}
|
||||
return []*models.Qualification{}, nil
|
||||
}
|
||||
|
|
|
@ -23,8 +23,10 @@ type Resolver struct {
|
|||
|
||||
type mutationResolver struct{ *Resolver }
|
||||
type queryResolver struct{ *Resolver }
|
||||
type professionResolver struct{ *Resolver }
|
||||
type questionResolver struct{ *Resolver }
|
||||
|
||||
func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} }
|
||||
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
|
||||
func (r *Resolver) Question() generated.QuestionResolver { return &questionResolver{r} }
|
||||
func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} }
|
||||
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
|
||||
func (r *Resolver) Profession() generated.ProfessionResolver { return &professionResolver{r} }
|
||||
func (r *Resolver) Question() generated.QuestionResolver { return &questionResolver{r} }
|
||||
|
|
|
@ -4,6 +4,7 @@ type Profession {
|
|||
name: String!
|
||||
description: String
|
||||
createdAt: Time!
|
||||
qualifications: [Qualification!]! @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type ProfessionList {
|
||||
|
|
|
@ -19,4 +19,5 @@ type Repository interface {
|
|||
UpdateMany(ctx context.Context, f *models.ProfessionFilter, input *models.ProfessionInput) ([]*models.Profession, error)
|
||||
Delete(ctx context.Context, f *models.ProfessionFilter) ([]*models.Profession, error)
|
||||
Fetch(ctx context.Context, cfg *FetchConfig) ([]*models.Profession, int, error)
|
||||
GetAssociatedQualifications(ctx context.Context, ids ...int) (map[int][]*models.Qualification, error)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package repository
|
||||
|
||||
const (
|
||||
messageNameIsAlreadyTaken = "Istnieje już zawód o podanej nazwie."
|
||||
messageFailedToSaveModel = "Wystąpił błąd podczas zapisywania zawodu, prosimy spróbować później."
|
||||
messageFailedToDeleteModel = "Wystąpił błąd podczas usuwania zawodu, prosimy spróbować później."
|
||||
messageFailedToFetchModel = "Wystąpił błąd podczas pobierania zawodów, prosimy spróbować później."
|
||||
messageNameIsAlreadyTaken = "Istnieje już zawód o podanej nazwie."
|
||||
messageFailedToSaveModel = "Wystąpił błąd podczas zapisywania zawodu."
|
||||
messageFailedToDeleteModel = "Wystąpił błąd podczas usuwania zawodu."
|
||||
messageFailedToFetchModel = "Wystąpił błąd podczas pobierania zawodów."
|
||||
messageFailedToFetchAssociatedQualifications = "Wystąpił błąd poczas pobierania powiązanych kwalifikacji."
|
||||
)
|
||||
|
|
|
@ -3,6 +3,7 @@ package repository
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
sqlutils "github.com/zdam-egzamin-zawodowy/backend/pkg/utils/sql"
|
||||
"strings"
|
||||
|
||||
errorutils "github.com/zdam-egzamin-zawodowy/backend/pkg/utils/error"
|
||||
|
@ -101,3 +102,26 @@ func (repo *pgRepository) Fetch(ctx context.Context, cfg *profession.FetchConfig
|
|||
}
|
||||
return items, total, nil
|
||||
}
|
||||
|
||||
func (repo *pgRepository) GetAssociatedQualifications(
|
||||
ctx context.Context,
|
||||
ids ...int,
|
||||
) (map[int][]*models.Qualification, error) {
|
||||
m := make(map[int][]*models.Qualification)
|
||||
for _, id := range ids {
|
||||
m[id] = []*models.Qualification{}
|
||||
}
|
||||
qualificationToProfession := []*models.QualificationToProfession{}
|
||||
if err := repo.
|
||||
Model(&qualificationToProfession).
|
||||
Context(ctx).
|
||||
Where(sqlutils.BuildConditionArray("profession_id"), pg.Array(ids)).
|
||||
Relation("Qualification").
|
||||
Select(); err != nil {
|
||||
return nil, errorutils.Wrap(err, messageFailedToFetchAssociatedQualifications)
|
||||
}
|
||||
for _, record := range qualificationToProfession {
|
||||
m[record.ProfessionID] = append(m[record.ProfessionID], record.Qualification)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package repository
|
|||
const (
|
||||
messageNameIsAlreadyTaken = "Istnieje już kwalifikacja o podanej nazwie."
|
||||
messageCodeIsAlreadyTaken = "Istnieje już kwalifikacja o podanym oznaczeniu."
|
||||
messageFailedToSaveModel = "Wystąpił błąd podczas zapisywania kwalifikacji, prosimy spróbować później."
|
||||
messageFailedToDeleteModel = "Wystąpił błąd podczas usuwania kwalifikacji, prosimy spróbować później."
|
||||
messageFailedToFetchModel = "Wystąpił błąd podczas pobierania kwalifikacji, prosimy spróbować później."
|
||||
messageFailedToSaveModel = "Wystąpił błąd podczas zapisywania kwalifikacji."
|
||||
messageFailedToDeleteModel = "Wystąpił błąd podczas usuwania kwalifikacji."
|
||||
messageFailedToFetchModel = "Wystąpił błąd podczas pobierania kwalifikacji."
|
||||
)
|
||||
|
|
|
@ -2,8 +2,8 @@ package repository
|
|||
|
||||
const (
|
||||
messageSimilarRecordExists = "Istnieje już podobne pytanie."
|
||||
messageFailedToSaveModel = "Wystąpił błąd podczas zapisywania pytania, prosimy spróbować później."
|
||||
messageFailedToDeleteModel = "Wystąpił błąd podczas usuwania pytania, prosimy spróbować później."
|
||||
messageFailedToFetchModel = "Wystąpił błąd podczas pobierania pytań, prosimy spróbować później."
|
||||
messageFailedToSaveModel = "Wystąpił błąd podczas zapisywania pytania."
|
||||
messageFailedToDeleteModel = "Wystąpił błąd podczas usuwania pytania."
|
||||
messageFailedToFetchModel = "Wystąpił błąd podczas pobierania pytań."
|
||||
messageItemNotFound = "Nie znaleziono pytania."
|
||||
)
|
||||
|
|
|
@ -2,7 +2,7 @@ package repository
|
|||
|
||||
const (
|
||||
messageEmailIsAlreadyTaken = "Istnieje już użytkownik o podanym adresie e-mail."
|
||||
messageFailedToSaveModel = "Wystąpił błąd podczas zapisywania użytkownika, prosimy spróbować później."
|
||||
messageFailedToDeleteModel = "Wystąpił błąd podczas usuwania użytkownika, prosimy spróbować później."
|
||||
messageFailedToFetchModel = "Wystąpił błąd podczas pobierania użytkowników, prosimy spróbować później."
|
||||
messageFailedToSaveModel = "Wystąpił błąd podczas zapisywania użytkownika."
|
||||
messageFailedToDeleteModel = "Wystąpił błąd podczas usuwania użytkownika."
|
||||
messageFailedToFetchModel = "Wystąpił błąd podczas pobierania użytkowników."
|
||||
)
|
||||
|
|
Reference in New Issue