commit
368e212a4f
2
go.mod
2
go.mod
|
@ -24,8 +24,6 @@ require (
|
|||
github.com/sethvargo/go-password v0.2.0
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/vektah/gqlparser/v2 v2.1.0
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.1 // indirect
|
||||
go.opentelemetry.io/otel v0.20.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
)
|
||||
|
|
16
go.sum
16
go.sum
|
@ -46,10 +46,7 @@ github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmC
|
|||
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||
github.com/go-pg/pg/v10 v10.9.1 h1:kU4t84zWGGaU0Qsu49FbNtToUVrlSTkNOngW8aQmwvk=
|
||||
github.com/go-pg/pg/v10 v10.9.1/go.mod h1:rgmTPgHgl5EN2CNKKoMwC7QT62t8BqsdpEkUQuiZMQs=
|
||||
github.com/go-pg/pg/v10 v10.10.1 h1:82lLX4KGs2wOFOvVVIICoU0Si1fLu6Aitniu73HaDuM=
|
||||
github.com/go-pg/pg/v10 v10.10.1/go.mod h1:EmoJGYErc+stNN/1Jf+o4csXuprjxcRztBnn6cHe38E=
|
||||
github.com/go-pg/pg/v10 v10.10.2 h1:8G2DdKrB3/0nRIlpur0HySEWBJnHYUByiC0ko4XzE8w=
|
||||
github.com/go-pg/pg/v10 v10.10.2/go.mod h1:EmoJGYErc+stNN/1Jf+o4csXuprjxcRztBnn6cHe38E=
|
||||
github.com/go-pg/zerochecker v0.2.0 h1:pp7f72c3DobMWOb2ErtZsnrPaSvHd2W4o9//8HtF4mU=
|
||||
|
@ -202,26 +199,15 @@ github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgq
|
|||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||
go.opentelemetry.io/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg=
|
||||
go.opentelemetry.io/otel v0.20.0 h1:eaP0Fqu7SXHwvjiqDq83zImeehOHX8doTvU9AwXON8g=
|
||||
go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
|
||||
go.opentelemetry.io/otel/metric v0.19.0/go.mod h1:8f9fglJPRnXuskQmKpnad31lcLJ2VmNNqIsx/uIwBSc=
|
||||
go.opentelemetry.io/otel/metric v0.20.0 h1:4kzhXFP+btKm4jwxpjIqjs41A7MakRFUS86bqLHTIw8=
|
||||
go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
|
||||
go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA=
|
||||
go.opentelemetry.io/otel/oteltest v0.20.0 h1:HiITxCawalo5vQzdHfKeZurV8x7ljcqAgiWzF6Vaeaw=
|
||||
go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=
|
||||
go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg=
|
||||
go.opentelemetry.io/otel/trace v0.20.0 h1:1DL6EXUdcg95gukhuRRvLDO/4X5THh/5dIV52lqtnbw=
|
||||
go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
|
||||
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
@ -261,8 +247,6 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 h1:dXfMednGJh/SUUFjTLsWJz3P+TQt9qnR11GgeI3vWKs=
|
||||
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
|
|
|
@ -25,22 +25,17 @@ func (opts Metadata) ToMapClaims() jwt.MapClaims {
|
|||
return mClaims
|
||||
}
|
||||
|
||||
type TokenGenerator interface {
|
||||
Generate(metadata Metadata) (string, error)
|
||||
ExtractAccessTokenMetadata(token string) (*Metadata, error)
|
||||
}
|
||||
|
||||
type tokenGenerator struct {
|
||||
type TokenGenerator struct {
|
||||
accessSecret string
|
||||
}
|
||||
|
||||
func NewTokenGenerator(accessSecret string) TokenGenerator {
|
||||
return &tokenGenerator{
|
||||
func NewTokenGenerator(accessSecret string) *TokenGenerator {
|
||||
return &TokenGenerator{
|
||||
accessSecret: accessSecret,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *tokenGenerator) Generate(metadata Metadata) (string, error) {
|
||||
func (g *TokenGenerator) Generate(metadata Metadata) (string, error) {
|
||||
atClaims := metadata.ToMapClaims()
|
||||
if !metadata.StaySignedIn {
|
||||
atClaims["exp"] = time.Now().Add(time.Hour * 24).Unix()
|
||||
|
@ -54,7 +49,7 @@ func (g *tokenGenerator) Generate(metadata Metadata) (string, error) {
|
|||
return accessToken, nil
|
||||
}
|
||||
|
||||
func (g *tokenGenerator) ExtractAccessTokenMetadata(token string) (*Metadata, error) {
|
||||
func (g *TokenGenerator) ExtractAccessTokenMetadata(token string) (*Metadata, error) {
|
||||
return extractTokenMetadata(g.accessSecret, token)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,10 +3,10 @@ package auth
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
)
|
||||
|
||||
type Usecase interface {
|
||||
SignIn(ctx context.Context, email, password string, staySignedIn bool) (*models.User, string, error)
|
||||
ExtractAccessTokenMetadata(ctx context.Context, accessToken string) (*models.User, error)
|
||||
SignIn(ctx context.Context, email, password string, staySignedIn bool) (*model.User, string, error)
|
||||
ExtractAccessTokenMetadata(ctx context.Context, accessToken string) (*model.User, error)
|
||||
}
|
||||
|
|
|
@ -3,34 +3,37 @@ package usecase
|
|||
import (
|
||||
"context"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/auth"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/auth/jwt"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/user"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/pkg/util/errorutil"
|
||||
)
|
||||
|
||||
type usecase struct {
|
||||
userRepository user.Repository
|
||||
tokenGenerator jwt.TokenGenerator
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
UserRepository user.Repository
|
||||
TokenGenerator jwt.TokenGenerator
|
||||
TokenGenerator *jwt.TokenGenerator
|
||||
}
|
||||
|
||||
func New(cfg *Config) (auth.Usecase, error) {
|
||||
type Usecase struct {
|
||||
userRepository user.Repository
|
||||
tokenGenerator *jwt.TokenGenerator
|
||||
}
|
||||
|
||||
var _ auth.Usecase = &Usecase{}
|
||||
|
||||
func New(cfg *Config) (*Usecase, error) {
|
||||
if cfg == nil || cfg.UserRepository == nil {
|
||||
return nil, errors.New("cfg.UserRepository is required")
|
||||
}
|
||||
return &usecase{
|
||||
return &Usecase{
|
||||
cfg.UserRepository,
|
||||
cfg.TokenGenerator,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ucase *usecase) SignIn(ctx context.Context, email, password string, staySignedIn bool) (*models.User, string, error) {
|
||||
func (ucase *Usecase) SignIn(ctx context.Context, email, password string, staySignedIn bool) (*model.User, string, error) {
|
||||
u, err := ucase.GetUserByCredentials(ctx, email, password)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
|
@ -50,7 +53,7 @@ func (ucase *usecase) SignIn(ctx context.Context, email, password string, staySi
|
|||
return u, token, nil
|
||||
}
|
||||
|
||||
func (ucase *usecase) ExtractAccessTokenMetadata(ctx context.Context, accessToken string) (*models.User, error) {
|
||||
func (ucase *Usecase) ExtractAccessTokenMetadata(ctx context.Context, accessToken string) (*model.User, error) {
|
||||
metadata, err := ucase.tokenGenerator.ExtractAccessTokenMetadata(accessToken)
|
||||
if err != nil {
|
||||
return nil, errorutil.Wrap(err, messageInvalidAccessToken)
|
||||
|
@ -59,11 +62,11 @@ func (ucase *usecase) ExtractAccessTokenMetadata(ctx context.Context, accessToke
|
|||
return ucase.GetUserByCredentials(ctx, metadata.Credentials.Email, metadata.Credentials.Password)
|
||||
}
|
||||
|
||||
func (ucase *usecase) GetUserByCredentials(ctx context.Context, email, password string) (*models.User, error) {
|
||||
func (ucase *Usecase) GetUserByCredentials(ctx context.Context, email, password string) (*model.User, error) {
|
||||
users, _, err := ucase.userRepository.Fetch(ctx, &user.FetchConfig{
|
||||
Limit: 1,
|
||||
Count: false,
|
||||
Filter: &models.UserFilter{
|
||||
Filter: &model.UserFilter{
|
||||
Email: []string{email},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -5,8 +5,9 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/auth"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -32,16 +33,16 @@ func Authenticate(ucase auth.Usecase) gin.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
func UserFromContext(ctx context.Context) (*models.User, error) {
|
||||
func UserFromContext(ctx context.Context) (*model.User, error) {
|
||||
user := ctx.Value(authenticateKey)
|
||||
if user == nil {
|
||||
err := errors.New("couldn't retrieve *models.User")
|
||||
err := errors.New("couldn't retrieve *model.User")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u, ok := user.(*models.User)
|
||||
u, ok := user.(*model.User)
|
||||
if !ok {
|
||||
err := errors.New("*models.User has wrong type")
|
||||
err := errors.New("*model.User has wrong type")
|
||||
return nil, err
|
||||
}
|
||||
return u, nil
|
||||
|
|
|
@ -2,10 +2,11 @@ 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/profession"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/qualification"
|
||||
)
|
||||
|
||||
|
@ -27,9 +28,9 @@ func New(cfg Config) *DataLoader {
|
|||
return &DataLoader{
|
||||
QualificationByID: NewQualificationLoader(QualificationLoaderConfig{
|
||||
Wait: wait,
|
||||
Fetch: func(ids []int) ([]*models.Qualification, []error) {
|
||||
Fetch: func(ids []int) ([]*model.Qualification, []error) {
|
||||
qualificationsNotInOrder, _, err := cfg.QualificationRepo.Fetch(context.Background(), &qualification.FetchConfig{
|
||||
Filter: &models.QualificationFilter{
|
||||
Filter: &model.QualificationFilter{
|
||||
ID: ids,
|
||||
},
|
||||
Count: false,
|
||||
|
@ -37,11 +38,11 @@ func New(cfg Config) *DataLoader {
|
|||
if err != nil {
|
||||
return nil, []error{err}
|
||||
}
|
||||
qualificationByID := make(map[int]*models.Qualification)
|
||||
qualificationByID := make(map[int]*model.Qualification)
|
||||
for _, qualification := range qualificationsNotInOrder {
|
||||
qualificationByID[qualification.ID] = qualification
|
||||
}
|
||||
qualifications := make([]*models.Qualification, len(ids))
|
||||
qualifications := make([]*model.Qualification, len(ids))
|
||||
for i, id := range ids {
|
||||
qualifications[i] = qualificationByID[id]
|
||||
}
|
||||
|
@ -50,13 +51,13 @@ func New(cfg Config) *DataLoader {
|
|||
}),
|
||||
QualificationsByProfessionID: NewQualificationSliceByProfessionIDLoader(QualificationSliceByProfessionIDLoaderConfig{
|
||||
Wait: wait,
|
||||
Fetch: func(ids []int) ([][]*models.Qualification, []error) {
|
||||
Fetch: func(ids []int) ([][]*model.Qualification, []error) {
|
||||
m, err := cfg.ProfessionRepo.GetAssociatedQualifications(context.Background(), ids...)
|
||||
if err != nil {
|
||||
return nil, []error{err}
|
||||
}
|
||||
|
||||
qualifications := make([][]*models.Qualification, len(ids))
|
||||
qualifications := make([][]*model.Qualification, len(ids))
|
||||
|
||||
for i, id := range ids {
|
||||
qualifications[i] = m[id]
|
||||
|
|
|
@ -6,13 +6,13 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
)
|
||||
|
||||
// QualificationLoaderConfig captures the config to create a new QualificationLoader
|
||||
type QualificationLoaderConfig struct {
|
||||
// Fetch is a method that provides the data for the loader
|
||||
Fetch func(keys []int) ([]*models.Qualification, []error)
|
||||
Fetch func(keys []int) ([]*model.Qualification, []error)
|
||||
|
||||
// Wait is how long wait before sending a batch
|
||||
Wait time.Duration
|
||||
|
@ -33,7 +33,7 @@ func NewQualificationLoader(config QualificationLoaderConfig) *QualificationLoad
|
|||
// QualificationLoader batches and caches requests
|
||||
type QualificationLoader struct {
|
||||
// this method provides the data for the loader
|
||||
fetch func(keys []int) ([]*models.Qualification, []error)
|
||||
fetch func(keys []int) ([]*model.Qualification, []error)
|
||||
|
||||
// how long to done before sending a batch
|
||||
wait time.Duration
|
||||
|
@ -44,7 +44,7 @@ type QualificationLoader struct {
|
|||
// INTERNAL
|
||||
|
||||
// lazily created cache
|
||||
cache map[int]*models.Qualification
|
||||
cache map[int]*model.Qualification
|
||||
|
||||
// the current batch. keys will continue to be collected until timeout is hit,
|
||||
// then everything will be sent to the fetch method and out to the listeners
|
||||
|
@ -56,25 +56,25 @@ type QualificationLoader struct {
|
|||
|
||||
type qualificationLoaderBatch struct {
|
||||
keys []int
|
||||
data []*models.Qualification
|
||||
data []*model.Qualification
|
||||
error []error
|
||||
closing bool
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// Load a Qualification by key, batching and caching will be applied automatically
|
||||
func (l *QualificationLoader) Load(key int) (*models.Qualification, error) {
|
||||
func (l *QualificationLoader) Load(key int) (*model.Qualification, error) {
|
||||
return l.LoadThunk(key)()
|
||||
}
|
||||
|
||||
// LoadThunk returns a function that when called will block waiting for a Qualification.
|
||||
// This method should be used if you want one goroutine to make requests to many
|
||||
// different data loaders without blocking until the thunk is called.
|
||||
func (l *QualificationLoader) LoadThunk(key int) func() (*models.Qualification, error) {
|
||||
func (l *QualificationLoader) LoadThunk(key int) func() (*model.Qualification, error) {
|
||||
l.mu.Lock()
|
||||
if it, ok := l.cache[key]; ok {
|
||||
l.mu.Unlock()
|
||||
return func() (*models.Qualification, error) {
|
||||
return func() (*model.Qualification, error) {
|
||||
return it, nil
|
||||
}
|
||||
}
|
||||
|
@ -85,10 +85,10 @@ func (l *QualificationLoader) LoadThunk(key int) func() (*models.Qualification,
|
|||
pos := batch.keyIndex(l, key)
|
||||
l.mu.Unlock()
|
||||
|
||||
return func() (*models.Qualification, error) {
|
||||
return func() (*model.Qualification, error) {
|
||||
<-batch.done
|
||||
|
||||
var data *models.Qualification
|
||||
var data *model.Qualification
|
||||
if pos < len(batch.data) {
|
||||
data = batch.data[pos]
|
||||
}
|
||||
|
@ -113,14 +113,14 @@ func (l *QualificationLoader) LoadThunk(key int) func() (*models.Qualification,
|
|||
|
||||
// LoadAll fetches many keys at once. It will be broken into appropriate sized
|
||||
// sub batches depending on how the loader is configured
|
||||
func (l *QualificationLoader) LoadAll(keys []int) ([]*models.Qualification, []error) {
|
||||
results := make([]func() (*models.Qualification, error), len(keys))
|
||||
func (l *QualificationLoader) LoadAll(keys []int) ([]*model.Qualification, []error) {
|
||||
results := make([]func() (*model.Qualification, error), len(keys))
|
||||
|
||||
for i, key := range keys {
|
||||
results[i] = l.LoadThunk(key)
|
||||
}
|
||||
|
||||
qualifications := make([]*models.Qualification, len(keys))
|
||||
qualifications := make([]*model.Qualification, len(keys))
|
||||
errors := make([]error, len(keys))
|
||||
for i, thunk := range results {
|
||||
qualifications[i], errors[i] = thunk()
|
||||
|
@ -131,13 +131,13 @@ func (l *QualificationLoader) LoadAll(keys []int) ([]*models.Qualification, []er
|
|||
// LoadAllThunk returns a function that when called will block waiting for a Qualifications.
|
||||
// This method should be used if you want one goroutine to make requests to many
|
||||
// different data loaders without blocking until the thunk is called.
|
||||
func (l *QualificationLoader) LoadAllThunk(keys []int) func() ([]*models.Qualification, []error) {
|
||||
results := make([]func() (*models.Qualification, error), len(keys))
|
||||
func (l *QualificationLoader) LoadAllThunk(keys []int) func() ([]*model.Qualification, []error) {
|
||||
results := make([]func() (*model.Qualification, error), len(keys))
|
||||
for i, key := range keys {
|
||||
results[i] = l.LoadThunk(key)
|
||||
}
|
||||
return func() ([]*models.Qualification, []error) {
|
||||
qualifications := make([]*models.Qualification, len(keys))
|
||||
return func() ([]*model.Qualification, []error) {
|
||||
qualifications := make([]*model.Qualification, len(keys))
|
||||
errors := make([]error, len(keys))
|
||||
for i, thunk := range results {
|
||||
qualifications[i], errors[i] = thunk()
|
||||
|
@ -149,7 +149,7 @@ func (l *QualificationLoader) LoadAllThunk(keys []int) func() ([]*models.Qualifi
|
|||
// Prime the cache with the provided key and value. If the key already exists, no change is made
|
||||
// and false is returned.
|
||||
// (To forcefully prime the cache, clear the key first with loader.clear(key).prime(key, value).)
|
||||
func (l *QualificationLoader) Prime(key int, value *models.Qualification) bool {
|
||||
func (l *QualificationLoader) Prime(key int, value *model.Qualification) bool {
|
||||
l.mu.Lock()
|
||||
var found bool
|
||||
if _, found = l.cache[key]; !found {
|
||||
|
@ -169,9 +169,9 @@ func (l *QualificationLoader) Clear(key int) {
|
|||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
func (l *QualificationLoader) unsafeSet(key int, value *models.Qualification) {
|
||||
func (l *QualificationLoader) unsafeSet(key int, value *model.Qualification) {
|
||||
if l.cache == nil {
|
||||
l.cache = map[int]*models.Qualification{}
|
||||
l.cache = map[int]*model.Qualification{}
|
||||
}
|
||||
l.cache[key] = value
|
||||
}
|
||||
|
|
|
@ -6,13 +6,13 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
)
|
||||
|
||||
// QualificationSliceByProfessionIDLoaderConfig captures the config to create a new QualificationSliceByProfessionIDLoader
|
||||
type QualificationSliceByProfessionIDLoaderConfig struct {
|
||||
// Fetch is a method that provides the data for the loader
|
||||
Fetch func(keys []int) ([][]*models.Qualification, []error)
|
||||
Fetch func(keys []int) ([][]*model.Qualification, []error)
|
||||
|
||||
// Wait is how long wait before sending a batch
|
||||
Wait time.Duration
|
||||
|
@ -33,7 +33,7 @@ func NewQualificationSliceByProfessionIDLoader(config QualificationSliceByProfes
|
|||
// QualificationSliceByProfessionIDLoader batches and caches requests
|
||||
type QualificationSliceByProfessionIDLoader struct {
|
||||
// this method provides the data for the loader
|
||||
fetch func(keys []int) ([][]*models.Qualification, []error)
|
||||
fetch func(keys []int) ([][]*model.Qualification, []error)
|
||||
|
||||
// how long to done before sending a batch
|
||||
wait time.Duration
|
||||
|
@ -44,7 +44,7 @@ type QualificationSliceByProfessionIDLoader struct {
|
|||
// INTERNAL
|
||||
|
||||
// lazily created cache
|
||||
cache map[int][]*models.Qualification
|
||||
cache map[int][]*model.Qualification
|
||||
|
||||
// the current batch. keys will continue to be collected until timeout is hit,
|
||||
// then everything will be sent to the fetch method and out to the listeners
|
||||
|
@ -56,25 +56,25 @@ type QualificationSliceByProfessionIDLoader struct {
|
|||
|
||||
type qualificationSliceByProfessionIDLoaderBatch struct {
|
||||
keys []int
|
||||
data [][]*models.Qualification
|
||||
data [][]*model.Qualification
|
||||
error []error
|
||||
closing bool
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// Load a Qualification by key, batching and caching will be applied automatically
|
||||
func (l *QualificationSliceByProfessionIDLoader) Load(key int) ([]*models.Qualification, error) {
|
||||
func (l *QualificationSliceByProfessionIDLoader) Load(key int) ([]*model.Qualification, error) {
|
||||
return l.LoadThunk(key)()
|
||||
}
|
||||
|
||||
// LoadThunk returns a function that when called will block waiting for a Qualification.
|
||||
// This method should be used if you want one goroutine to make requests to many
|
||||
// different data loaders without blocking until the thunk is called.
|
||||
func (l *QualificationSliceByProfessionIDLoader) LoadThunk(key int) func() ([]*models.Qualification, error) {
|
||||
func (l *QualificationSliceByProfessionIDLoader) LoadThunk(key int) func() ([]*model.Qualification, error) {
|
||||
l.mu.Lock()
|
||||
if it, ok := l.cache[key]; ok {
|
||||
l.mu.Unlock()
|
||||
return func() ([]*models.Qualification, error) {
|
||||
return func() ([]*model.Qualification, error) {
|
||||
return it, nil
|
||||
}
|
||||
}
|
||||
|
@ -85,10 +85,10 @@ func (l *QualificationSliceByProfessionIDLoader) LoadThunk(key int) func() ([]*m
|
|||
pos := batch.keyIndex(l, key)
|
||||
l.mu.Unlock()
|
||||
|
||||
return func() ([]*models.Qualification, error) {
|
||||
return func() ([]*model.Qualification, error) {
|
||||
<-batch.done
|
||||
|
||||
var data []*models.Qualification
|
||||
var data []*model.Qualification
|
||||
if pos < len(batch.data) {
|
||||
data = batch.data[pos]
|
||||
}
|
||||
|
@ -113,14 +113,14 @@ func (l *QualificationSliceByProfessionIDLoader) LoadThunk(key int) func() ([]*m
|
|||
|
||||
// LoadAll fetches many keys at once. It will be broken into appropriate sized
|
||||
// sub batches depending on how the loader is configured
|
||||
func (l *QualificationSliceByProfessionIDLoader) LoadAll(keys []int) ([][]*models.Qualification, []error) {
|
||||
results := make([]func() ([]*models.Qualification, error), len(keys))
|
||||
func (l *QualificationSliceByProfessionIDLoader) LoadAll(keys []int) ([][]*model.Qualification, []error) {
|
||||
results := make([]func() ([]*model.Qualification, error), len(keys))
|
||||
|
||||
for i, key := range keys {
|
||||
results[i] = l.LoadThunk(key)
|
||||
}
|
||||
|
||||
qualifications := make([][]*models.Qualification, len(keys))
|
||||
qualifications := make([][]*model.Qualification, len(keys))
|
||||
errors := make([]error, len(keys))
|
||||
for i, thunk := range results {
|
||||
qualifications[i], errors[i] = thunk()
|
||||
|
@ -131,13 +131,13 @@ func (l *QualificationSliceByProfessionIDLoader) LoadAll(keys []int) ([][]*model
|
|||
// LoadAllThunk returns a function that when called will block waiting for a Qualifications.
|
||||
// This method should be used if you want one goroutine to make requests to many
|
||||
// different data loaders without blocking until the thunk is called.
|
||||
func (l *QualificationSliceByProfessionIDLoader) LoadAllThunk(keys []int) func() ([][]*models.Qualification, []error) {
|
||||
results := make([]func() ([]*models.Qualification, error), len(keys))
|
||||
func (l *QualificationSliceByProfessionIDLoader) LoadAllThunk(keys []int) func() ([][]*model.Qualification, []error) {
|
||||
results := make([]func() ([]*model.Qualification, error), len(keys))
|
||||
for i, key := range keys {
|
||||
results[i] = l.LoadThunk(key)
|
||||
}
|
||||
return func() ([][]*models.Qualification, []error) {
|
||||
qualifications := make([][]*models.Qualification, len(keys))
|
||||
return func() ([][]*model.Qualification, []error) {
|
||||
qualifications := make([][]*model.Qualification, len(keys))
|
||||
errors := make([]error, len(keys))
|
||||
for i, thunk := range results {
|
||||
qualifications[i], errors[i] = thunk()
|
||||
|
@ -149,13 +149,13 @@ func (l *QualificationSliceByProfessionIDLoader) LoadAllThunk(keys []int) func()
|
|||
// Prime the cache with the provided key and value. If the key already exists, no change is made
|
||||
// and false is returned.
|
||||
// (To forcefully prime the cache, clear the key first with loader.clear(key).prime(key, value).)
|
||||
func (l *QualificationSliceByProfessionIDLoader) Prime(key int, value []*models.Qualification) bool {
|
||||
func (l *QualificationSliceByProfessionIDLoader) Prime(key int, value []*model.Qualification) bool {
|
||||
l.mu.Lock()
|
||||
var found bool
|
||||
if _, found = l.cache[key]; !found {
|
||||
// make a copy when writing to the cache, its easy to pass a pointer in from a loop var
|
||||
// and end up with the whole cache pointing to the same value.
|
||||
cpy := make([]*models.Qualification, len(value))
|
||||
cpy := make([]*model.Qualification, len(value))
|
||||
copy(cpy, value)
|
||||
l.unsafeSet(key, cpy)
|
||||
}
|
||||
|
@ -170,9 +170,9 @@ func (l *QualificationSliceByProfessionIDLoader) Clear(key int) {
|
|||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
func (l *QualificationSliceByProfessionIDLoader) unsafeSet(key int, value []*models.Qualification) {
|
||||
func (l *QualificationSliceByProfessionIDLoader) unsafeSet(key int, value []*model.Qualification) {
|
||||
if l.cache == nil {
|
||||
l.cache = map[int][]*models.Qualification{}
|
||||
l.cache = map[int][]*model.Qualification{}
|
||||
}
|
||||
l.cache[key] = value
|
||||
}
|
||||
|
|
|
@ -5,8 +5,9 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/gin/middleware"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/pkg/util/errorutil"
|
||||
)
|
||||
|
||||
|
@ -23,7 +24,7 @@ func (d *Directive) Authenticated(ctx context.Context, _ interface{}, next graph
|
|||
return next(ctx)
|
||||
}
|
||||
|
||||
func (d *Directive) HasRole(ctx context.Context, _ interface{}, next graphql.Resolver, role models.Role) (interface{}, error) {
|
||||
func (d *Directive) HasRole(ctx context.Context, _ interface{}, next graphql.Resolver, role model.Role) (interface{}, error) {
|
||||
user, err := middleware.UserFromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, errorutil.Wrap(err, messageMustBeSignedIn)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,30 +3,30 @@
|
|||
package generated
|
||||
|
||||
import (
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
)
|
||||
|
||||
type ProfessionList struct {
|
||||
Total int `json:"total"`
|
||||
Items []*models.Profession `json:"items"`
|
||||
Total int `json:"total"`
|
||||
Items []*model.Profession `json:"items"`
|
||||
}
|
||||
|
||||
type QualificationList struct {
|
||||
Total int `json:"total"`
|
||||
Items []*models.Qualification `json:"items"`
|
||||
Total int `json:"total"`
|
||||
Items []*model.Qualification `json:"items"`
|
||||
}
|
||||
|
||||
type QuestionList struct {
|
||||
Total int `json:"total"`
|
||||
Items []*models.Question `json:"items"`
|
||||
Total int `json:"total"`
|
||||
Items []*model.Question `json:"items"`
|
||||
}
|
||||
|
||||
type UserList struct {
|
||||
Total int `json:"total"`
|
||||
Items []*models.User `json:"items"`
|
||||
Total int `json:"total"`
|
||||
Items []*model.User `json:"items"`
|
||||
}
|
||||
|
||||
type UserWithToken struct {
|
||||
Token string `json:"token"`
|
||||
User *models.User `json:"user"`
|
||||
Token string `json:"token"`
|
||||
User *model.User `json:"user"`
|
||||
}
|
||||
|
|
|
@ -19,52 +19,52 @@ models:
|
|||
- github.com/99designs/gqlgen/graphql.Int32
|
||||
Role:
|
||||
model:
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/models.Role
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/model.Role
|
||||
User:
|
||||
model:
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/models.User
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/model.User
|
||||
UserFilter:
|
||||
model:
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/models.UserFilter
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/model.UserFilter
|
||||
UserFilterOr:
|
||||
model:
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/models.UserFilterOr
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/model.UserFilterOr
|
||||
UserInput:
|
||||
model:
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/models.UserInput
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/model.UserInput
|
||||
UpdateManyUsersInput:
|
||||
model:
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/models.UserInput
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/model.UserInput
|
||||
Profession:
|
||||
model:
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/models.Profession
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/model.Profession
|
||||
ProfessionFilter:
|
||||
model:
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/models.ProfessionFilter
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/model.ProfessionFilter
|
||||
ProfessionInput:
|
||||
model:
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/models.ProfessionInput
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/model.ProfessionInput
|
||||
Qualification:
|
||||
model:
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/models.Qualification
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/model.Qualification
|
||||
QualificationFilter:
|
||||
model:
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/models.QualificationFilter
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/model.QualificationFilter
|
||||
QualificationFilterOr:
|
||||
model:
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/models.QualificationFilterOr
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/model.QualificationFilterOr
|
||||
QualificationInput:
|
||||
model:
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/models.QualificationInput
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/model.QualificationInput
|
||||
Answer:
|
||||
model:
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/models.Answer
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/model.Answer
|
||||
Question:
|
||||
model:
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/models.Question
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/model.Question
|
||||
QuestionFilter:
|
||||
model:
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/models.QuestionFilter
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/model.QuestionFilter
|
||||
QuestionInput:
|
||||
model:
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/models.QuestionInput
|
||||
- github.com/zdam-egzamin-zawodowy/backend/internal/model.QuestionInput
|
||||
|
|
|
@ -3,8 +3,9 @@ package querycomplexity
|
|||
import (
|
||||
"github.com/99designs/gqlgen/graphql/handler/extension"
|
||||
"github.com/Kichiyaki/goutil/safeptr"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/graphql/generated"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/profession"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/qualification"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/question"
|
||||
|
@ -33,7 +34,7 @@ func GetComplexityRoot() generated.ComplexityRoot {
|
|||
complexityRoot.ProfessionList.Total = getCountComplexity
|
||||
complexityRoot.Query.Professions = func(
|
||||
childComplexity int,
|
||||
filter *models.ProfessionFilter,
|
||||
filter *model.ProfessionFilter,
|
||||
limit *int,
|
||||
offset *int,
|
||||
sort []string,
|
||||
|
@ -49,7 +50,7 @@ func GetComplexityRoot() generated.ComplexityRoot {
|
|||
complexityRoot.QualificationList.Total = getCountComplexity
|
||||
complexityRoot.Query.Qualifications = func(
|
||||
childComplexity int,
|
||||
filter *models.QualificationFilter,
|
||||
filter *model.QualificationFilter,
|
||||
limit *int,
|
||||
offset *int,
|
||||
sort []string,
|
||||
|
@ -65,7 +66,7 @@ func GetComplexityRoot() generated.ComplexityRoot {
|
|||
complexityRoot.QuestionList.Total = getCountComplexity
|
||||
complexityRoot.Query.Questions = func(
|
||||
childComplexity int,
|
||||
filter *models.QuestionFilter,
|
||||
filter *model.QuestionFilter,
|
||||
limit *int,
|
||||
offset *int,
|
||||
sort []string,
|
||||
|
@ -89,7 +90,7 @@ func GetComplexityRoot() generated.ComplexityRoot {
|
|||
complexityRoot.UserList.Total = getCountComplexity
|
||||
complexityRoot.Query.Users = func(
|
||||
childComplexity int,
|
||||
filter *models.UserFilter,
|
||||
filter *model.UserFilter,
|
||||
limit *int,
|
||||
offset *int,
|
||||
sort []string,
|
||||
|
@ -102,22 +103,22 @@ func GetComplexityRoot() generated.ComplexityRoot {
|
|||
)
|
||||
}
|
||||
|
||||
complexityRoot.Mutation.CreateProfession = func(childComplexity int, input models.ProfessionInput) int {
|
||||
complexityRoot.Mutation.CreateProfession = func(childComplexity int, input model.ProfessionInput) int {
|
||||
return (complexityLimit / 5) + childComplexity
|
||||
}
|
||||
|
||||
complexityRoot.Mutation.CreateQualification = func(
|
||||
childComplexity int,
|
||||
input models.QualificationInput,
|
||||
input model.QualificationInput,
|
||||
) int {
|
||||
return (complexityLimit / 5) + childComplexity
|
||||
}
|
||||
|
||||
complexityRoot.Mutation.CreateQuestion = func(childComplexity int, input models.QuestionInput) int {
|
||||
complexityRoot.Mutation.CreateQuestion = func(childComplexity int, input model.QuestionInput) int {
|
||||
return (complexityLimit / 4) + childComplexity
|
||||
}
|
||||
|
||||
complexityRoot.Mutation.CreateUser = func(childComplexity int, input models.UserInput) int {
|
||||
complexityRoot.Mutation.CreateUser = func(childComplexity int, input model.UserInput) int {
|
||||
return (complexityLimit / 5) + childComplexity
|
||||
}
|
||||
|
||||
|
@ -133,7 +134,7 @@ func GetComplexityRoot() generated.ComplexityRoot {
|
|||
complexityRoot.Mutation.UpdateManyUsers = func(
|
||||
childComplexity int,
|
||||
ids []int,
|
||||
input models.UserInput,
|
||||
input model.UserInput,
|
||||
) int {
|
||||
return (complexityLimit / 5) + childComplexity
|
||||
}
|
||||
|
@ -141,7 +142,7 @@ func GetComplexityRoot() generated.ComplexityRoot {
|
|||
complexityRoot.Mutation.UpdateProfession = func(
|
||||
childComplexity int,
|
||||
id int,
|
||||
input models.ProfessionInput,
|
||||
input model.ProfessionInput,
|
||||
) int {
|
||||
return (complexityLimit / 5) + childComplexity
|
||||
}
|
||||
|
@ -149,7 +150,7 @@ func GetComplexityRoot() generated.ComplexityRoot {
|
|||
complexityRoot.Mutation.UpdateQualification = func(
|
||||
childComplexity int,
|
||||
id int,
|
||||
input models.QualificationInput,
|
||||
input model.QualificationInput,
|
||||
) int {
|
||||
return (complexityLimit / 5) + childComplexity
|
||||
}
|
||||
|
@ -157,12 +158,12 @@ func GetComplexityRoot() generated.ComplexityRoot {
|
|||
complexityRoot.Mutation.UpdateQuestion = func(
|
||||
childComplexity int,
|
||||
id int,
|
||||
input models.QuestionInput,
|
||||
input model.QuestionInput,
|
||||
) int {
|
||||
return (complexityLimit / 4) + childComplexity
|
||||
}
|
||||
|
||||
complexityRoot.Mutation.UpdateUser = func(childComplexity int, id int, input models.UserInput) int {
|
||||
complexityRoot.Mutation.UpdateUser = func(childComplexity int, id int, input model.UserInput) int {
|
||||
return (complexityLimit / 5) + childComplexity
|
||||
}
|
||||
|
||||
|
|
|
@ -6,30 +6,31 @@ package resolvers
|
|||
import (
|
||||
"context"
|
||||
"github.com/Kichiyaki/goutil/safeptr"
|
||||
|
||||
"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"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/profession"
|
||||
)
|
||||
|
||||
func (r *mutationResolver) CreateProfession(ctx context.Context, input models.ProfessionInput) (*models.Profession, error) {
|
||||
func (r *mutationResolver) CreateProfession(ctx context.Context, input model.ProfessionInput) (*model.Profession, error) {
|
||||
return r.ProfessionUsecase.Store(ctx, &input)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) UpdateProfession(ctx context.Context, id int, input models.ProfessionInput) (*models.Profession, error) {
|
||||
func (r *mutationResolver) UpdateProfession(ctx context.Context, id int, input model.ProfessionInput) (*model.Profession, error) {
|
||||
return r.ProfessionUsecase.UpdateOneByID(ctx, id, &input)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) DeleteProfessions(ctx context.Context, ids []int) ([]*models.Profession, error) {
|
||||
return r.ProfessionUsecase.Delete(ctx, &models.ProfessionFilter{
|
||||
func (r *mutationResolver) DeleteProfessions(ctx context.Context, ids []int) ([]*model.Profession, error) {
|
||||
return r.ProfessionUsecase.Delete(ctx, &model.ProfessionFilter{
|
||||
ID: ids,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *queryResolver) Professions(
|
||||
ctx context.Context,
|
||||
filter *models.ProfessionFilter,
|
||||
filter *model.ProfessionFilter,
|
||||
limit *int,
|
||||
offset *int,
|
||||
sort []string,
|
||||
|
@ -49,7 +50,7 @@ func (r *queryResolver) Professions(
|
|||
return list, err
|
||||
}
|
||||
|
||||
func (r *queryResolver) Profession(ctx context.Context, id *int, slug *string) (*models.Profession, error) {
|
||||
func (r *queryResolver) Profession(ctx context.Context, id *int, slug *string) (*model.Profession, error) {
|
||||
if id != nil {
|
||||
return r.ProfessionUsecase.GetByID(ctx, *id)
|
||||
} else if slug != nil {
|
||||
|
@ -61,12 +62,12 @@ func (r *queryResolver) Profession(ctx context.Context, id *int, slug *string) (
|
|||
|
||||
func (r *professionResolver) Qualifications(
|
||||
ctx context.Context,
|
||||
obj *models.Profession,
|
||||
) ([]*models.Qualification, error) {
|
||||
obj *model.Profession,
|
||||
) ([]*model.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
|
||||
return []*model.Qualification{}, nil
|
||||
}
|
||||
|
|
|
@ -8,27 +8,27 @@ import (
|
|||
"github.com/Kichiyaki/goutil/safeptr"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/graphql/generated"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/qualification"
|
||||
)
|
||||
|
||||
func (r *mutationResolver) CreateQualification(ctx context.Context, input models.QualificationInput) (*models.Qualification, error) {
|
||||
func (r *mutationResolver) CreateQualification(ctx context.Context, input model.QualificationInput) (*model.Qualification, error) {
|
||||
return r.QualificationUsecase.Store(ctx, &input)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) UpdateQualification(ctx context.Context, id int, input models.QualificationInput) (*models.Qualification, error) {
|
||||
func (r *mutationResolver) UpdateQualification(ctx context.Context, id int, input model.QualificationInput) (*model.Qualification, error) {
|
||||
return r.QualificationUsecase.UpdateOneByID(ctx, id, &input)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) DeleteQualifications(ctx context.Context, ids []int) ([]*models.Qualification, error) {
|
||||
return r.QualificationUsecase.Delete(ctx, &models.QualificationFilter{
|
||||
func (r *mutationResolver) DeleteQualifications(ctx context.Context, ids []int) ([]*model.Qualification, error) {
|
||||
return r.QualificationUsecase.Delete(ctx, &model.QualificationFilter{
|
||||
ID: ids,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *queryResolver) Qualifications(
|
||||
ctx context.Context,
|
||||
filter *models.QualificationFilter,
|
||||
filter *model.QualificationFilter,
|
||||
limit *int,
|
||||
offset *int,
|
||||
sort []string,
|
||||
|
@ -70,7 +70,7 @@ func (r *queryResolver) SimilarQualifications(
|
|||
return list, err
|
||||
}
|
||||
|
||||
func (r *queryResolver) Qualification(ctx context.Context, id *int, slug *string) (*models.Qualification, error) {
|
||||
func (r *queryResolver) Qualification(ctx context.Context, id *int, slug *string) (*model.Qualification, error) {
|
||||
if id != nil {
|
||||
return r.QualificationUsecase.GetByID(ctx, *id)
|
||||
} else if slug != nil {
|
||||
|
|
|
@ -9,25 +9,25 @@ import (
|
|||
|
||||
"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"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/question"
|
||||
)
|
||||
|
||||
func (r *mutationResolver) CreateQuestion(ctx context.Context, input models.QuestionInput) (*models.Question, error) {
|
||||
func (r *mutationResolver) CreateQuestion(ctx context.Context, input model.QuestionInput) (*model.Question, error) {
|
||||
return r.QuestionUsecase.Store(ctx, &input)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) UpdateQuestion(ctx context.Context, id int, input models.QuestionInput) (*models.Question, error) {
|
||||
func (r *mutationResolver) UpdateQuestion(ctx context.Context, id int, input model.QuestionInput) (*model.Question, error) {
|
||||
return r.QuestionUsecase.UpdateOneByID(ctx, id, &input)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) DeleteQuestions(ctx context.Context, ids []int) ([]*models.Question, error) {
|
||||
return r.QuestionUsecase.Delete(ctx, &models.QuestionFilter{
|
||||
func (r *mutationResolver) DeleteQuestions(ctx context.Context, ids []int) ([]*model.Question, error) {
|
||||
return r.QuestionUsecase.Delete(ctx, &model.QuestionFilter{
|
||||
ID: ids,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *queryResolver) GenerateTest(ctx context.Context, qualificationIDs []int, limit *int) ([]*models.Question, error) {
|
||||
func (r *queryResolver) GenerateTest(ctx context.Context, qualificationIDs []int, limit *int) ([]*model.Question, error) {
|
||||
return r.QuestionUsecase.GenerateTest(ctx, &question.GenerateTestConfig{
|
||||
Qualifications: qualificationIDs,
|
||||
Limit: safeptr.SafeIntPointer(limit, question.TestMaxLimit),
|
||||
|
@ -36,7 +36,7 @@ func (r *queryResolver) GenerateTest(ctx context.Context, qualificationIDs []int
|
|||
|
||||
func (r *queryResolver) Questions(
|
||||
ctx context.Context,
|
||||
filter *models.QuestionFilter,
|
||||
filter *model.QuestionFilter,
|
||||
limit *int,
|
||||
offset *int,
|
||||
sort []string,
|
||||
|
@ -56,7 +56,7 @@ func (r *queryResolver) Questions(
|
|||
return list, err
|
||||
}
|
||||
|
||||
func (r *questionResolver) Qualification(ctx context.Context, obj *models.Question) (*models.Qualification, error) {
|
||||
func (r *questionResolver) Qualification(ctx context.Context, obj *model.Question) (*model.Qualification, error) {
|
||||
if obj != nil && obj.Qualification != nil {
|
||||
return obj.Qualification, nil
|
||||
}
|
||||
|
|
|
@ -9,30 +9,30 @@ import (
|
|||
|
||||
"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"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/user"
|
||||
)
|
||||
|
||||
func (r *mutationResolver) CreateUser(ctx context.Context, input models.UserInput) (*models.User, error) {
|
||||
func (r *mutationResolver) CreateUser(ctx context.Context, input model.UserInput) (*model.User, error) {
|
||||
return r.UserUsecase.Store(ctx, &input)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) UpdateUser(ctx context.Context, id int, input models.UserInput) (*models.User, error) {
|
||||
func (r *mutationResolver) UpdateUser(ctx context.Context, id int, input model.UserInput) (*model.User, error) {
|
||||
return r.UserUsecase.UpdateOneByID(ctx, id, &input)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) UpdateManyUsers(ctx context.Context, ids []int, input models.UserInput) ([]*models.User, error) {
|
||||
func (r *mutationResolver) UpdateManyUsers(ctx context.Context, ids []int, input model.UserInput) ([]*model.User, error) {
|
||||
return r.UserUsecase.UpdateMany(
|
||||
ctx,
|
||||
&models.UserFilter{
|
||||
&model.UserFilter{
|
||||
ID: ids,
|
||||
},
|
||||
&input,
|
||||
)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) DeleteUsers(ctx context.Context, ids []int) ([]*models.User, error) {
|
||||
return r.UserUsecase.Delete(ctx, &models.UserFilter{
|
||||
func (r *mutationResolver) DeleteUsers(ctx context.Context, ids []int) ([]*model.User, error) {
|
||||
return r.UserUsecase.Delete(ctx, &model.UserFilter{
|
||||
ID: ids,
|
||||
})
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ func (r *mutationResolver) SignIn(
|
|||
|
||||
func (r *queryResolver) Users(
|
||||
ctx context.Context,
|
||||
filter *models.UserFilter,
|
||||
filter *model.UserFilter,
|
||||
limit *int,
|
||||
offset *int,
|
||||
sort []string,
|
||||
|
@ -79,11 +79,11 @@ func (r *queryResolver) Users(
|
|||
return userList, err
|
||||
}
|
||||
|
||||
func (r *queryResolver) User(ctx context.Context, id int) (*models.User, error) {
|
||||
func (r *queryResolver) User(ctx context.Context, id int) (*model.User, error) {
|
||||
return r.UserUsecase.GetByID(ctx, id)
|
||||
}
|
||||
|
||||
func (r *queryResolver) Me(ctx context.Context) (*models.User, error) {
|
||||
func (r *queryResolver) Me(ctx context.Context) (*model.User, error) {
|
||||
u, _ := middleware.UserFromContext(ctx)
|
||||
return u, nil
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package models
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -1,4 +1,4 @@
|
|||
package models
|
||||
package model
|
||||
|
||||
import (
|
||||
"reflect"
|
|
@ -1,4 +1,4 @@
|
|||
package models
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
|
@ -1,4 +1,4 @@
|
|||
package models
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
|
@ -1,4 +1,4 @@
|
|||
package models
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
|
@ -1,4 +1,4 @@
|
|||
package models
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -1,4 +1,4 @@
|
|||
package models
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
|
@ -10,7 +10,8 @@ import (
|
|||
"github.com/go-pg/pg/v10/orm"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sethvargo/go-password/password"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
)
|
||||
|
||||
var log = logrus.WithField("package", "internal/postgres")
|
||||
|
@ -20,7 +21,7 @@ type Config struct {
|
|||
}
|
||||
|
||||
func init() {
|
||||
orm.RegisterTable((*models.QualificationToProfession)(nil))
|
||||
orm.RegisterTable((*model.QualificationToProfession)(nil))
|
||||
}
|
||||
|
||||
func Connect(cfg *Config) (*pg.DB, error) {
|
||||
|
@ -55,11 +56,11 @@ func prepareOptions() *pg.Options {
|
|||
func createSchema(db *pg.DB) error {
|
||||
return db.RunInTransaction(context.Background(), func(tx *pg.Tx) error {
|
||||
modelsToCreate := []interface{}{
|
||||
(*models.User)(nil),
|
||||
(*models.Profession)(nil),
|
||||
(*models.Qualification)(nil),
|
||||
(*models.QualificationToProfession)(nil),
|
||||
(*models.Question)(nil),
|
||||
(*model.User)(nil),
|
||||
(*model.Profession)(nil),
|
||||
(*model.Qualification)(nil),
|
||||
(*model.QualificationToProfession)(nil),
|
||||
(*model.Question)(nil),
|
||||
}
|
||||
|
||||
for _, model := range modelsToCreate {
|
||||
|
@ -72,7 +73,7 @@ func createSchema(db *pg.DB) error {
|
|||
}
|
||||
}
|
||||
|
||||
total, err := tx.Model(modelsToCreate[0]).Where("role = ?", models.RoleAdmin).Count()
|
||||
total, err := tx.Model(modelsToCreate[0]).Where("role = ?", model.RoleAdmin).Count()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "couldn't count admins")
|
||||
}
|
||||
|
@ -84,10 +85,10 @@ func createSchema(db *pg.DB) error {
|
|||
}
|
||||
email := "admin@admin.com"
|
||||
_, err = tx.
|
||||
Model(&models.User{
|
||||
Model(&model.User{
|
||||
DisplayName: "admin",
|
||||
Email: email,
|
||||
Role: models.RoleAdmin,
|
||||
Role: model.RoleAdmin,
|
||||
Activated: &activated,
|
||||
Password: pswd,
|
||||
}).
|
||||
|
|
|
@ -3,11 +3,11 @@ package profession
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
)
|
||||
|
||||
type FetchConfig struct {
|
||||
Filter *models.ProfessionFilter
|
||||
Filter *model.ProfessionFilter
|
||||
Offset int
|
||||
Limit int
|
||||
Sort []string
|
||||
|
@ -15,9 +15,9 @@ type FetchConfig struct {
|
|||
}
|
||||
|
||||
type Repository interface {
|
||||
Store(ctx context.Context, input *models.ProfessionInput) (*models.Profession, error)
|
||||
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)
|
||||
Store(ctx context.Context, input *model.ProfessionInput) (*model.Profession, error)
|
||||
UpdateMany(ctx context.Context, f *model.ProfessionFilter, input *model.ProfessionInput) ([]*model.Profession, error)
|
||||
Delete(ctx context.Context, f *model.ProfessionFilter) ([]*model.Profession, error)
|
||||
Fetch(ctx context.Context, cfg *FetchConfig) ([]*model.Profession, int, error)
|
||||
GetAssociatedQualifications(ctx context.Context, ids ...int) (map[int][]*model.Qualification, error)
|
||||
}
|
||||
|
|
|
@ -9,28 +9,31 @@ import (
|
|||
"github.com/zdam-egzamin-zawodowy/backend/pkg/util/errorutil"
|
||||
|
||||
"github.com/go-pg/pg/v10"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/profession"
|
||||
)
|
||||
|
||||
type pgRepository struct {
|
||||
*pg.DB
|
||||
}
|
||||
|
||||
type PGRepositoryConfig struct {
|
||||
DB *pg.DB
|
||||
}
|
||||
|
||||
func NewPGRepository(cfg *PGRepositoryConfig) (profession.Repository, error) {
|
||||
type PGRepository struct {
|
||||
*pg.DB
|
||||
}
|
||||
|
||||
var _ profession.Repository = &PGRepository{}
|
||||
|
||||
func NewPGRepository(cfg *PGRepositoryConfig) (*PGRepository, error) {
|
||||
if cfg == nil || cfg.DB == nil {
|
||||
return nil, errors.New("cfg.DB is required")
|
||||
}
|
||||
return &pgRepository{
|
||||
return &PGRepository{
|
||||
cfg.DB,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Store(ctx context.Context, input *models.ProfessionInput) (*models.Profession, error) {
|
||||
func (repo *PGRepository) Store(ctx context.Context, input *model.ProfessionInput) (*model.Profession, error) {
|
||||
item := input.ToProfession()
|
||||
if _, err := repo.
|
||||
Model(item).
|
||||
|
@ -42,9 +45,9 @@ func (repo *pgRepository) Store(ctx context.Context, input *models.ProfessionInp
|
|||
return item, nil
|
||||
}
|
||||
|
||||
func (repo *pgRepository) UpdateMany(ctx context.Context, f *models.ProfessionFilter, input *models.ProfessionInput) ([]*models.Profession, error) {
|
||||
func (repo *PGRepository) UpdateMany(ctx context.Context, f *model.ProfessionFilter, input *model.ProfessionInput) ([]*model.Profession, error) {
|
||||
if _, err := repo.
|
||||
Model(&models.Profession{}).
|
||||
Model(&model.Profession{}).
|
||||
Context(ctx).
|
||||
Apply(input.ApplyUpdate).
|
||||
Apply(f.Where).
|
||||
|
@ -61,8 +64,8 @@ func (repo *pgRepository) UpdateMany(ctx context.Context, f *models.ProfessionFi
|
|||
return items, nil
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Delete(ctx context.Context, f *models.ProfessionFilter) ([]*models.Profession, error) {
|
||||
items := make([]*models.Profession, 0)
|
||||
func (repo *PGRepository) Delete(ctx context.Context, f *model.ProfessionFilter) ([]*model.Profession, error) {
|
||||
items := make([]*model.Profession, 0)
|
||||
if _, err := repo.
|
||||
Model(&items).
|
||||
Context(ctx).
|
||||
|
@ -74,9 +77,9 @@ func (repo *pgRepository) Delete(ctx context.Context, f *models.ProfessionFilter
|
|||
return items, nil
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, cfg *profession.FetchConfig) ([]*models.Profession, int, error) {
|
||||
func (repo *PGRepository) Fetch(ctx context.Context, cfg *profession.FetchConfig) ([]*model.Profession, int, error) {
|
||||
var err error
|
||||
items := make([]*models.Profession, 0)
|
||||
items := make([]*model.Profession, 0)
|
||||
total := 0
|
||||
query := repo.
|
||||
Model(&items).
|
||||
|
@ -99,15 +102,15 @@ func (repo *pgRepository) Fetch(ctx context.Context, cfg *profession.FetchConfig
|
|||
return items, total, nil
|
||||
}
|
||||
|
||||
func (repo *pgRepository) GetAssociatedQualifications(
|
||||
func (repo *PGRepository) GetAssociatedQualifications(
|
||||
ctx context.Context,
|
||||
ids ...int,
|
||||
) (map[int][]*models.Qualification, error) {
|
||||
m := make(map[int][]*models.Qualification)
|
||||
) (map[int][]*model.Qualification, error) {
|
||||
m := make(map[int][]*model.Qualification)
|
||||
for _, id := range ids {
|
||||
m[id] = make([]*models.Qualification, 0)
|
||||
m[id] = make([]*model.Qualification, 0)
|
||||
}
|
||||
var qualificationToProfession []*models.QualificationToProfession
|
||||
var qualificationToProfession []*model.QualificationToProfession
|
||||
if err := repo.
|
||||
Model(&qualificationToProfession).
|
||||
Context(ctx).
|
||||
|
|
|
@ -3,14 +3,14 @@ package profession
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
)
|
||||
|
||||
type Usecase interface {
|
||||
Store(ctx context.Context, input *models.ProfessionInput) (*models.Profession, error)
|
||||
UpdateOneByID(ctx context.Context, id int, 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)
|
||||
GetByID(ctx context.Context, id int) (*models.Profession, error)
|
||||
GetBySlug(ctx context.Context, slug string) (*models.Profession, error)
|
||||
Store(ctx context.Context, input *model.ProfessionInput) (*model.Profession, error)
|
||||
UpdateOneByID(ctx context.Context, id int, input *model.ProfessionInput) (*model.Profession, error)
|
||||
Delete(ctx context.Context, f *model.ProfessionFilter) ([]*model.Profession, error)
|
||||
Fetch(ctx context.Context, cfg *FetchConfig) ([]*model.Profession, int, error)
|
||||
GetByID(ctx context.Context, id int) (*model.Profession, error)
|
||||
GetBySlug(ctx context.Context, slug string) (*model.Profession, error)
|
||||
}
|
||||
|
|
|
@ -4,35 +4,37 @@ import (
|
|||
"context"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/profession"
|
||||
)
|
||||
|
||||
type usecase struct {
|
||||
professionRepository profession.Repository
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
ProfessionRepository profession.Repository
|
||||
}
|
||||
|
||||
func New(cfg *Config) (profession.Usecase, error) {
|
||||
type Usecase struct {
|
||||
professionRepository profession.Repository
|
||||
}
|
||||
|
||||
var _ profession.Usecase = &Usecase{}
|
||||
|
||||
func New(cfg *Config) (*Usecase, error) {
|
||||
if cfg == nil || cfg.ProfessionRepository == nil {
|
||||
return nil, errors.New("cfg.ProfessionRepository is required")
|
||||
}
|
||||
return &usecase{
|
||||
return &Usecase{
|
||||
cfg.ProfessionRepository,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ucase *usecase) Store(ctx context.Context, input *models.ProfessionInput) (*models.Profession, error) {
|
||||
func (ucase *Usecase) Store(ctx context.Context, input *model.ProfessionInput) (*model.Profession, error) {
|
||||
if err := validateInput(input.Sanitize(), validateOptions{false}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ucase.professionRepository.Store(ctx, input)
|
||||
}
|
||||
|
||||
func (ucase *usecase) UpdateOneByID(ctx context.Context, id int, input *models.ProfessionInput) (*models.Profession, error) {
|
||||
func (ucase *Usecase) UpdateOneByID(ctx context.Context, id int, input *model.ProfessionInput) (*model.Profession, error) {
|
||||
if id <= 0 {
|
||||
return nil, errors.New(messageInvalidID)
|
||||
}
|
||||
|
@ -40,7 +42,7 @@ func (ucase *usecase) UpdateOneByID(ctx context.Context, id int, input *models.P
|
|||
return nil, err
|
||||
}
|
||||
items, err := ucase.professionRepository.UpdateMany(ctx,
|
||||
&models.ProfessionFilter{
|
||||
&model.ProfessionFilter{
|
||||
ID: []int{id},
|
||||
},
|
||||
input)
|
||||
|
@ -53,11 +55,11 @@ func (ucase *usecase) UpdateOneByID(ctx context.Context, id int, input *models.P
|
|||
return items[0], nil
|
||||
}
|
||||
|
||||
func (ucase *usecase) Delete(ctx context.Context, f *models.ProfessionFilter) ([]*models.Profession, error) {
|
||||
func (ucase *Usecase) Delete(ctx context.Context, f *model.ProfessionFilter) ([]*model.Profession, error) {
|
||||
return ucase.professionRepository.Delete(ctx, f)
|
||||
}
|
||||
|
||||
func (ucase *usecase) Fetch(ctx context.Context, cfg *profession.FetchConfig) ([]*models.Profession, int, error) {
|
||||
func (ucase *Usecase) Fetch(ctx context.Context, cfg *profession.FetchConfig) ([]*model.Profession, int, error) {
|
||||
if cfg == nil {
|
||||
cfg = &profession.FetchConfig{
|
||||
Limit: profession.FetchDefaultLimit,
|
||||
|
@ -71,11 +73,11 @@ func (ucase *usecase) Fetch(ctx context.Context, cfg *profession.FetchConfig) ([
|
|||
return ucase.professionRepository.Fetch(ctx, cfg)
|
||||
}
|
||||
|
||||
func (ucase *usecase) GetByID(ctx context.Context, id int) (*models.Profession, error) {
|
||||
func (ucase *Usecase) GetByID(ctx context.Context, id int) (*model.Profession, error) {
|
||||
items, _, err := ucase.Fetch(ctx, &profession.FetchConfig{
|
||||
Limit: 1,
|
||||
Count: false,
|
||||
Filter: &models.ProfessionFilter{
|
||||
Filter: &model.ProfessionFilter{
|
||||
ID: []int{id},
|
||||
},
|
||||
})
|
||||
|
@ -88,11 +90,11 @@ func (ucase *usecase) GetByID(ctx context.Context, id int) (*models.Profession,
|
|||
return items[0], nil
|
||||
}
|
||||
|
||||
func (ucase *usecase) GetBySlug(ctx context.Context, slug string) (*models.Profession, error) {
|
||||
func (ucase *Usecase) GetBySlug(ctx context.Context, slug string) (*model.Profession, error) {
|
||||
items, _, err := ucase.Fetch(ctx, &profession.FetchConfig{
|
||||
Limit: 1,
|
||||
Count: false,
|
||||
Filter: &models.ProfessionFilter{
|
||||
Filter: &model.ProfessionFilter{
|
||||
Slug: []string{slug},
|
||||
},
|
||||
})
|
||||
|
@ -109,7 +111,7 @@ type validateOptions struct {
|
|||
allowNilValues bool
|
||||
}
|
||||
|
||||
func validateInput(input *models.ProfessionInput, opts validateOptions) error {
|
||||
func validateInput(input *model.ProfessionInput, opts validateOptions) error {
|
||||
if input.IsEmpty() {
|
||||
return errors.New(messageEmptyPayload)
|
||||
}
|
||||
|
|
|
@ -3,11 +3,11 @@ package qualification
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
)
|
||||
|
||||
type FetchConfig struct {
|
||||
Filter *models.QualificationFilter
|
||||
Filter *model.QualificationFilter
|
||||
Offset int
|
||||
Limit int
|
||||
Sort []string
|
||||
|
@ -23,9 +23,9 @@ type GetSimilarConfig struct {
|
|||
}
|
||||
|
||||
type Repository interface {
|
||||
Store(ctx context.Context, input *models.QualificationInput) (*models.Qualification, error)
|
||||
UpdateMany(ctx context.Context, f *models.QualificationFilter, input *models.QualificationInput) ([]*models.Qualification, error)
|
||||
Delete(ctx context.Context, f *models.QualificationFilter) ([]*models.Qualification, error)
|
||||
Fetch(ctx context.Context, cfg *FetchConfig) ([]*models.Qualification, int, error)
|
||||
GetSimilar(ctx context.Context, cfg *GetSimilarConfig) ([]*models.Qualification, int, error)
|
||||
Store(ctx context.Context, input *model.QualificationInput) (*model.Qualification, error)
|
||||
UpdateMany(ctx context.Context, f *model.QualificationFilter, input *model.QualificationInput) ([]*model.Qualification, error)
|
||||
Delete(ctx context.Context, f *model.QualificationFilter) ([]*model.Qualification, error)
|
||||
Fetch(ctx context.Context, cfg *FetchConfig) ([]*model.Qualification, int, error)
|
||||
GetSimilar(ctx context.Context, cfg *GetSimilarConfig) ([]*model.Qualification, int, error)
|
||||
}
|
||||
|
|
|
@ -9,28 +9,31 @@ import (
|
|||
"github.com/zdam-egzamin-zawodowy/backend/pkg/util/errorutil"
|
||||
|
||||
"github.com/go-pg/pg/v10"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/qualification"
|
||||
)
|
||||
|
||||
type pgRepository struct {
|
||||
*pg.DB
|
||||
}
|
||||
|
||||
type PGRepositoryConfig struct {
|
||||
DB *pg.DB
|
||||
}
|
||||
|
||||
func NewPGRepository(cfg *PGRepositoryConfig) (qualification.Repository, error) {
|
||||
type PGRepository struct {
|
||||
*pg.DB
|
||||
}
|
||||
|
||||
var _ qualification.Repository = &PGRepository{}
|
||||
|
||||
func NewPGRepository(cfg *PGRepositoryConfig) (*PGRepository, error) {
|
||||
if cfg == nil || cfg.DB == nil {
|
||||
return nil, errors.New("cfg.DB is required")
|
||||
}
|
||||
return &pgRepository{
|
||||
return &PGRepository{
|
||||
cfg.DB,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Store(ctx context.Context, input *models.QualificationInput) (*models.Qualification, error) {
|
||||
func (repo *PGRepository) Store(ctx context.Context, input *model.QualificationInput) (*model.Qualification, error) {
|
||||
item := input.ToQualification()
|
||||
err := repo.RunInTransaction(ctx, func(tx *pg.Tx) error {
|
||||
if _, err := tx.
|
||||
|
@ -52,16 +55,16 @@ func (repo *pgRepository) Store(ctx context.Context, input *models.Qualification
|
|||
return item, err
|
||||
}
|
||||
|
||||
func (repo *pgRepository) UpdateMany(
|
||||
func (repo *PGRepository) UpdateMany(
|
||||
ctx context.Context,
|
||||
f *models.QualificationFilter,
|
||||
input *models.QualificationInput,
|
||||
) ([]*models.Qualification, error) {
|
||||
items := make([]*models.Qualification, 0)
|
||||
f *model.QualificationFilter,
|
||||
input *model.QualificationInput,
|
||||
) ([]*model.Qualification, error) {
|
||||
items := make([]*model.Qualification, 0)
|
||||
err := repo.RunInTransaction(ctx, func(tx *pg.Tx) error {
|
||||
if input.HasBasicDataToUpdate() {
|
||||
if _, err := tx.
|
||||
Model(&models.Qualification{}).
|
||||
Model(&model.Qualification{}).
|
||||
Context(ctx).
|
||||
Apply(input.ApplyUpdate).
|
||||
Apply(f.Where).
|
||||
|
@ -86,7 +89,7 @@ func (repo *pgRepository) UpdateMany(
|
|||
if len(qualificationIDs) > 0 {
|
||||
if len(input.DissociateProfession) > 0 {
|
||||
_, err := tx.
|
||||
Model(&models.QualificationToProfession{}).
|
||||
Model(&model.QualificationToProfession{}).
|
||||
Where(gopgutil.BuildConditionArray("profession_id"), pg.Array(input.DissociateProfession)).
|
||||
Where(gopgutil.BuildConditionArray("qualification_id"), pg.Array(qualificationIDs)).
|
||||
Delete()
|
||||
|
@ -107,8 +110,8 @@ func (repo *pgRepository) UpdateMany(
|
|||
return items, err
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Delete(ctx context.Context, f *models.QualificationFilter) ([]*models.Qualification, error) {
|
||||
items := make([]*models.Qualification, 0)
|
||||
func (repo *PGRepository) Delete(ctx context.Context, f *model.QualificationFilter) ([]*model.Qualification, error) {
|
||||
items := make([]*model.Qualification, 0)
|
||||
if _, err := repo.
|
||||
Model(&items).
|
||||
Context(ctx).
|
||||
|
@ -120,9 +123,9 @@ func (repo *pgRepository) Delete(ctx context.Context, f *models.QualificationFil
|
|||
return items, nil
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, cfg *qualification.FetchConfig) ([]*models.Qualification, int, error) {
|
||||
func (repo *PGRepository) Fetch(ctx context.Context, cfg *qualification.FetchConfig) ([]*model.Qualification, int, error) {
|
||||
var err error
|
||||
items := make([]*models.Qualification, 0)
|
||||
items := make([]*model.Qualification, 0)
|
||||
total := 0
|
||||
query := repo.
|
||||
Model(&items).
|
||||
|
@ -145,16 +148,16 @@ func (repo *pgRepository) Fetch(ctx context.Context, cfg *qualification.FetchCon
|
|||
return items, total, nil
|
||||
}
|
||||
|
||||
func (repo *pgRepository) GetSimilar(ctx context.Context, cfg *qualification.GetSimilarConfig) ([]*models.Qualification, int, error) {
|
||||
func (repo *PGRepository) GetSimilar(ctx context.Context, cfg *qualification.GetSimilarConfig) ([]*model.Qualification, int, error) {
|
||||
var err error
|
||||
subquery := repo.
|
||||
Model(&models.QualificationToProfession{}).
|
||||
Model(&model.QualificationToProfession{}).
|
||||
Context(ctx).
|
||||
Where(gopgutil.BuildConditionEquals("qualification_id"), cfg.QualificationID).
|
||||
Column("profession_id")
|
||||
var qualificationIDs []int
|
||||
err = repo.
|
||||
Model(&models.QualificationToProfession{}).
|
||||
Model(&model.QualificationToProfession{}).
|
||||
Context(ctx).
|
||||
Column("qualification_id").
|
||||
With("prof", subquery).
|
||||
|
@ -166,25 +169,25 @@ func (repo *pgRepository) GetSimilar(ctx context.Context, cfg *qualification.Get
|
|||
}
|
||||
|
||||
if len(qualificationIDs) == 0 {
|
||||
return []*models.Qualification{}, 0, nil
|
||||
return []*model.Qualification{}, 0, nil
|
||||
}
|
||||
|
||||
return repo.Fetch(ctx, &qualification.FetchConfig{
|
||||
Sort: cfg.Sort,
|
||||
Limit: cfg.Limit,
|
||||
Offset: cfg.Offset,
|
||||
Filter: &models.QualificationFilter{
|
||||
Filter: &model.QualificationFilter{
|
||||
ID: qualificationIDs,
|
||||
},
|
||||
Count: cfg.Count,
|
||||
})
|
||||
}
|
||||
|
||||
func (repo *pgRepository) associateQualificationWithProfession(tx *pg.Tx, qualificationIDs, professionIDs []int) error {
|
||||
var toInsert []*models.QualificationToProfession
|
||||
func (repo *PGRepository) associateQualificationWithProfession(tx *pg.Tx, qualificationIDs, professionIDs []int) error {
|
||||
var toInsert []*model.QualificationToProfession
|
||||
for _, professionID := range professionIDs {
|
||||
for _, qualificationID := range qualificationIDs {
|
||||
toInsert = append(toInsert, &models.QualificationToProfession{
|
||||
toInsert = append(toInsert, &model.QualificationToProfession{
|
||||
ProfessionID: professionID,
|
||||
QualificationID: qualificationID,
|
||||
})
|
||||
|
|
|
@ -3,15 +3,15 @@ package qualification
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
)
|
||||
|
||||
type Usecase interface {
|
||||
Store(ctx context.Context, input *models.QualificationInput) (*models.Qualification, error)
|
||||
UpdateOneByID(ctx context.Context, id int, input *models.QualificationInput) (*models.Qualification, error)
|
||||
Delete(ctx context.Context, f *models.QualificationFilter) ([]*models.Qualification, error)
|
||||
Fetch(ctx context.Context, cfg *FetchConfig) ([]*models.Qualification, int, error)
|
||||
GetByID(ctx context.Context, id int) (*models.Qualification, error)
|
||||
GetBySlug(ctx context.Context, slug string) (*models.Qualification, error)
|
||||
GetSimilar(ctx context.Context, cfg *GetSimilarConfig) ([]*models.Qualification, int, error)
|
||||
Store(ctx context.Context, input *model.QualificationInput) (*model.Qualification, error)
|
||||
UpdateOneByID(ctx context.Context, id int, input *model.QualificationInput) (*model.Qualification, error)
|
||||
Delete(ctx context.Context, f *model.QualificationFilter) ([]*model.Qualification, error)
|
||||
Fetch(ctx context.Context, cfg *FetchConfig) ([]*model.Qualification, int, error)
|
||||
GetByID(ctx context.Context, id int) (*model.Qualification, error)
|
||||
GetBySlug(ctx context.Context, slug string) (*model.Qualification, error)
|
||||
GetSimilar(ctx context.Context, cfg *GetSimilarConfig) ([]*model.Qualification, int, error)
|
||||
}
|
||||
|
|
|
@ -4,35 +4,37 @@ import (
|
|||
"context"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/qualification"
|
||||
)
|
||||
|
||||
type usecase struct {
|
||||
qualificationRepository qualification.Repository
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
QualificationRepository qualification.Repository
|
||||
}
|
||||
|
||||
func New(cfg *Config) (qualification.Usecase, error) {
|
||||
type Usecase struct {
|
||||
qualificationRepository qualification.Repository
|
||||
}
|
||||
|
||||
var _ qualification.Usecase = &Usecase{}
|
||||
|
||||
func New(cfg *Config) (*Usecase, error) {
|
||||
if cfg == nil || cfg.QualificationRepository == nil {
|
||||
return nil, errors.New("cfg.QualificationRepository is required")
|
||||
}
|
||||
return &usecase{
|
||||
return &Usecase{
|
||||
cfg.QualificationRepository,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ucase *usecase) Store(ctx context.Context, input *models.QualificationInput) (*models.Qualification, error) {
|
||||
func (ucase *Usecase) Store(ctx context.Context, input *model.QualificationInput) (*model.Qualification, error) {
|
||||
if err := validateInput(input.Sanitize(), validateOptions{false}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ucase.qualificationRepository.Store(ctx, input)
|
||||
}
|
||||
|
||||
func (ucase *usecase) UpdateOneByID(ctx context.Context, id int, input *models.QualificationInput) (*models.Qualification, error) {
|
||||
func (ucase *Usecase) UpdateOneByID(ctx context.Context, id int, input *model.QualificationInput) (*model.Qualification, error) {
|
||||
if id <= 0 {
|
||||
return nil, errors.New(messageInvalidID)
|
||||
}
|
||||
|
@ -40,7 +42,7 @@ func (ucase *usecase) UpdateOneByID(ctx context.Context, id int, input *models.Q
|
|||
return nil, err
|
||||
}
|
||||
items, err := ucase.qualificationRepository.UpdateMany(ctx,
|
||||
&models.QualificationFilter{
|
||||
&model.QualificationFilter{
|
||||
ID: []int{id},
|
||||
},
|
||||
input)
|
||||
|
@ -53,11 +55,11 @@ func (ucase *usecase) UpdateOneByID(ctx context.Context, id int, input *models.Q
|
|||
return items[0], nil
|
||||
}
|
||||
|
||||
func (ucase *usecase) Delete(ctx context.Context, f *models.QualificationFilter) ([]*models.Qualification, error) {
|
||||
func (ucase *Usecase) Delete(ctx context.Context, f *model.QualificationFilter) ([]*model.Qualification, error) {
|
||||
return ucase.qualificationRepository.Delete(ctx, f)
|
||||
}
|
||||
|
||||
func (ucase *usecase) Fetch(ctx context.Context, cfg *qualification.FetchConfig) ([]*models.Qualification, int, error) {
|
||||
func (ucase *Usecase) Fetch(ctx context.Context, cfg *qualification.FetchConfig) ([]*model.Qualification, int, error) {
|
||||
if cfg == nil {
|
||||
cfg = &qualification.FetchConfig{
|
||||
Limit: qualification.FetchDefaultLimit,
|
||||
|
@ -70,11 +72,11 @@ func (ucase *usecase) Fetch(ctx context.Context, cfg *qualification.FetchConfig)
|
|||
return ucase.qualificationRepository.Fetch(ctx, cfg)
|
||||
}
|
||||
|
||||
func (ucase *usecase) GetByID(ctx context.Context, id int) (*models.Qualification, error) {
|
||||
func (ucase *Usecase) GetByID(ctx context.Context, id int) (*model.Qualification, error) {
|
||||
items, _, err := ucase.Fetch(ctx, &qualification.FetchConfig{
|
||||
Limit: 1,
|
||||
Count: false,
|
||||
Filter: &models.QualificationFilter{
|
||||
Filter: &model.QualificationFilter{
|
||||
ID: []int{id},
|
||||
},
|
||||
})
|
||||
|
@ -87,11 +89,11 @@ func (ucase *usecase) GetByID(ctx context.Context, id int) (*models.Qualificatio
|
|||
return items[0], nil
|
||||
}
|
||||
|
||||
func (ucase *usecase) GetBySlug(ctx context.Context, slug string) (*models.Qualification, error) {
|
||||
func (ucase *Usecase) GetBySlug(ctx context.Context, slug string) (*model.Qualification, error) {
|
||||
items, _, err := ucase.Fetch(ctx, &qualification.FetchConfig{
|
||||
Limit: 1,
|
||||
Count: false,
|
||||
Filter: &models.QualificationFilter{
|
||||
Filter: &model.QualificationFilter{
|
||||
Slug: []string{slug},
|
||||
},
|
||||
})
|
||||
|
@ -104,7 +106,7 @@ func (ucase *usecase) GetBySlug(ctx context.Context, slug string) (*models.Quali
|
|||
return items[0], nil
|
||||
}
|
||||
|
||||
func (ucase *usecase) GetSimilar(ctx context.Context, cfg *qualification.GetSimilarConfig) ([]*models.Qualification, int, error) {
|
||||
func (ucase *Usecase) GetSimilar(ctx context.Context, cfg *qualification.GetSimilarConfig) ([]*model.Qualification, int, error) {
|
||||
if cfg == nil || cfg.QualificationID <= 0 {
|
||||
return nil, 0, errors.New(messageQualificationIDIsRequired)
|
||||
}
|
||||
|
@ -115,7 +117,7 @@ type validateOptions struct {
|
|||
allowNilValues bool
|
||||
}
|
||||
|
||||
func validateInput(input *models.QualificationInput, opts validateOptions) error {
|
||||
func validateInput(input *model.QualificationInput, opts validateOptions) error {
|
||||
if input.IsEmpty() {
|
||||
return errors.New(messageEmptyPayload)
|
||||
}
|
||||
|
|
|
@ -3,11 +3,11 @@ package question
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
)
|
||||
|
||||
type FetchConfig struct {
|
||||
Filter *models.QuestionFilter
|
||||
Filter *model.QuestionFilter
|
||||
Offset int
|
||||
Limit int
|
||||
Sort []string
|
||||
|
@ -20,9 +20,9 @@ type GenerateTestConfig struct {
|
|||
}
|
||||
|
||||
type Repository interface {
|
||||
Store(ctx context.Context, input *models.QuestionInput) (*models.Question, error)
|
||||
UpdateOneByID(ctx context.Context, id int, input *models.QuestionInput) (*models.Question, error)
|
||||
Delete(ctx context.Context, f *models.QuestionFilter) ([]*models.Question, error)
|
||||
Fetch(ctx context.Context, cfg *FetchConfig) ([]*models.Question, int, error)
|
||||
GenerateTest(ctx context.Context, cfg *GenerateTestConfig) ([]*models.Question, error)
|
||||
Store(ctx context.Context, input *model.QuestionInput) (*model.Question, error)
|
||||
UpdateOneByID(ctx context.Context, id int, input *model.QuestionInput) (*model.Question, error)
|
||||
Delete(ctx context.Context, f *model.QuestionFilter) ([]*model.Question, error)
|
||||
Fetch(ctx context.Context, cfg *FetchConfig) ([]*model.Question, int, error)
|
||||
GenerateTest(ctx context.Context, cfg *GenerateTestConfig) ([]*model.Question, error)
|
||||
}
|
||||
|
|
|
@ -8,30 +8,33 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/go-pg/pg/v10"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/question"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/pkg/fstorage"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/pkg/util/errorutil"
|
||||
)
|
||||
|
||||
type pgRepository struct {
|
||||
*pg.DB
|
||||
*repository
|
||||
}
|
||||
|
||||
type PGRepositoryConfig struct {
|
||||
DB *pg.DB
|
||||
FileStorage fstorage.FileStorage
|
||||
}
|
||||
|
||||
func NewPGRepository(cfg *PGRepositoryConfig) (question.Repository, error) {
|
||||
type PGRepository struct {
|
||||
*pg.DB
|
||||
*repository
|
||||
}
|
||||
|
||||
var _ question.Repository = &PGRepository{}
|
||||
|
||||
func NewPGRepository(cfg *PGRepositoryConfig) (*PGRepository, error) {
|
||||
if cfg == nil || cfg.DB == nil {
|
||||
return nil, errors.New("cfg.DB is required")
|
||||
}
|
||||
if cfg.FileStorage == nil {
|
||||
return nil, errors.New("cfg.FileStorage is required")
|
||||
}
|
||||
return &pgRepository{
|
||||
return &PGRepository{
|
||||
cfg.DB,
|
||||
&repository{
|
||||
fileStorage: cfg.FileStorage,
|
||||
|
@ -39,7 +42,7 @@ func NewPGRepository(cfg *PGRepositoryConfig) (question.Repository, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Store(ctx context.Context, input *models.QuestionInput) (*models.Question, error) {
|
||||
func (repo *PGRepository) Store(ctx context.Context, input *model.QuestionInput) (*model.Question, error) {
|
||||
item := input.ToQuestion()
|
||||
baseQuery := repo.
|
||||
Model(item).
|
||||
|
@ -67,8 +70,8 @@ func (repo *pgRepository) Store(ctx context.Context, input *models.QuestionInput
|
|||
return item, nil
|
||||
}
|
||||
|
||||
func (repo *pgRepository) UpdateOneByID(ctx context.Context, id int, input *models.QuestionInput) (*models.Question, error) {
|
||||
item := &models.Question{}
|
||||
func (repo *PGRepository) UpdateOneByID(ctx context.Context, id int, input *model.QuestionInput) (*model.Question, error) {
|
||||
item := &model.Question{}
|
||||
baseQuery := repo.
|
||||
Model(item).
|
||||
Context(ctx).
|
||||
|
@ -100,8 +103,8 @@ func (repo *pgRepository) UpdateOneByID(ctx context.Context, id int, input *mode
|
|||
return item, nil
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Delete(ctx context.Context, f *models.QuestionFilter) ([]*models.Question, error) {
|
||||
items := make([]*models.Question, 0)
|
||||
func (repo *PGRepository) Delete(ctx context.Context, f *model.QuestionFilter) ([]*model.Question, error) {
|
||||
items := make([]*model.Question, 0)
|
||||
if _, err := repo.
|
||||
Model(&items).
|
||||
Context(ctx).
|
||||
|
@ -116,9 +119,9 @@ func (repo *pgRepository) Delete(ctx context.Context, f *models.QuestionFilter)
|
|||
return items, nil
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, cfg *question.FetchConfig) ([]*models.Question, int, error) {
|
||||
func (repo *PGRepository) Fetch(ctx context.Context, cfg *question.FetchConfig) ([]*model.Question, int, error) {
|
||||
var err error
|
||||
items := make([]*models.Question, 0)
|
||||
items := make([]*model.Question, 0)
|
||||
total := 0
|
||||
query := repo.
|
||||
Model(&items).
|
||||
|
@ -141,14 +144,14 @@ func (repo *pgRepository) Fetch(ctx context.Context, cfg *question.FetchConfig)
|
|||
return items, total, nil
|
||||
}
|
||||
|
||||
func (repo *pgRepository) GenerateTest(ctx context.Context, cfg *question.GenerateTestConfig) ([]*models.Question, error) {
|
||||
func (repo *PGRepository) GenerateTest(ctx context.Context, cfg *question.GenerateTestConfig) ([]*model.Question, error) {
|
||||
subquery := repo.
|
||||
Model(&models.Question{}).
|
||||
Model(&model.Question{}).
|
||||
Column("id").
|
||||
Where(gopgutil.BuildConditionArray("qualification_id"), pg.Array(cfg.Qualifications)).
|
||||
OrderExpr("random()").
|
||||
Limit(cfg.Limit)
|
||||
items := make([]*models.Question, 0)
|
||||
items := make([]*model.Question, 0)
|
||||
if err := repo.
|
||||
Model(&items).
|
||||
Context(ctx).
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"github.com/zdam-egzamin-zawodowy/backend/pkg/fstorage/fstorageutil"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/pkg/fstorage/fstorageutil"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/pkg/fstorage"
|
||||
)
|
||||
|
||||
|
@ -13,7 +15,7 @@ type repository struct {
|
|||
fileStorage fstorage.FileStorage
|
||||
}
|
||||
|
||||
func (repo *repository) saveImages(destination *models.Question, input *models.QuestionInput) {
|
||||
func (repo *repository) saveImages(destination *model.Question, input *model.QuestionInput) {
|
||||
images := [...]*graphql.Upload{
|
||||
input.Image,
|
||||
input.AnswerAImage,
|
||||
|
@ -56,7 +58,7 @@ func (repo *repository) deleteImages(images []string) {
|
|||
}
|
||||
}
|
||||
|
||||
func (repo *repository) deleteImagesBasedOnInput(question *models.Question, input *models.QuestionInput) {
|
||||
func (repo *repository) deleteImagesBasedOnInput(question *model.Question, input *model.QuestionInput) {
|
||||
images := []string{}
|
||||
|
||||
if input.DeleteImage != nil &&
|
||||
|
@ -102,7 +104,7 @@ func (repo *repository) deleteImagesBasedOnInput(question *models.Question, inpu
|
|||
repo.deleteImages(images)
|
||||
}
|
||||
|
||||
func (repo *repository) getAllImagesAndDelete(questions []*models.Question) {
|
||||
func (repo *repository) getAllImagesAndDelete(questions []*model.Question) {
|
||||
images := []string{}
|
||||
|
||||
for _, question := range questions {
|
||||
|
|
|
@ -3,14 +3,14 @@ package question
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
)
|
||||
|
||||
type Usecase interface {
|
||||
Store(ctx context.Context, input *models.QuestionInput) (*models.Question, error)
|
||||
UpdateOneByID(ctx context.Context, id int, input *models.QuestionInput) (*models.Question, error)
|
||||
Delete(ctx context.Context, f *models.QuestionFilter) ([]*models.Question, error)
|
||||
Fetch(ctx context.Context, cfg *FetchConfig) ([]*models.Question, int, error)
|
||||
GetByID(ctx context.Context, id int) (*models.Question, error)
|
||||
GenerateTest(ctx context.Context, cfg *GenerateTestConfig) ([]*models.Question, error)
|
||||
Store(ctx context.Context, input *model.QuestionInput) (*model.Question, error)
|
||||
UpdateOneByID(ctx context.Context, id int, input *model.QuestionInput) (*model.Question, error)
|
||||
Delete(ctx context.Context, f *model.QuestionFilter) ([]*model.Question, error)
|
||||
Fetch(ctx context.Context, cfg *FetchConfig) ([]*model.Question, int, error)
|
||||
GetByID(ctx context.Context, id int) (*model.Question, error)
|
||||
GenerateTest(ctx context.Context, cfg *GenerateTestConfig) ([]*model.Question, error)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"context"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/question"
|
||||
)
|
||||
|
||||
|
@ -16,31 +16,33 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
type usecase struct {
|
||||
questionRepository question.Repository
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
QuestionRepository question.Repository
|
||||
}
|
||||
|
||||
func New(cfg *Config) (question.Usecase, error) {
|
||||
type Usecase struct {
|
||||
questionRepository question.Repository
|
||||
}
|
||||
|
||||
var _ question.Usecase = &Usecase{}
|
||||
|
||||
func New(cfg *Config) (*Usecase, error) {
|
||||
if cfg == nil || cfg.QuestionRepository == nil {
|
||||
return nil, errors.New("cfg.QuestionRepository is required")
|
||||
}
|
||||
return &usecase{
|
||||
return &Usecase{
|
||||
cfg.QuestionRepository,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ucase *usecase) Store(ctx context.Context, input *models.QuestionInput) (*models.Question, error) {
|
||||
func (ucase *Usecase) Store(ctx context.Context, input *model.QuestionInput) (*model.Question, error) {
|
||||
if err := validateInput(input.Sanitize(), validateOptions{false}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ucase.questionRepository.Store(ctx, input)
|
||||
}
|
||||
|
||||
func (ucase *usecase) UpdateOneByID(ctx context.Context, id int, input *models.QuestionInput) (*models.Question, error) {
|
||||
func (ucase *Usecase) UpdateOneByID(ctx context.Context, id int, input *model.QuestionInput) (*model.Question, error) {
|
||||
if id <= 0 {
|
||||
return nil, errors.New(messageInvalidID)
|
||||
}
|
||||
|
@ -59,11 +61,11 @@ func (ucase *usecase) UpdateOneByID(ctx context.Context, id int, input *models.Q
|
|||
return item, nil
|
||||
}
|
||||
|
||||
func (ucase *usecase) Delete(ctx context.Context, f *models.QuestionFilter) ([]*models.Question, error) {
|
||||
func (ucase *Usecase) Delete(ctx context.Context, f *model.QuestionFilter) ([]*model.Question, error) {
|
||||
return ucase.questionRepository.Delete(ctx, f)
|
||||
}
|
||||
|
||||
func (ucase *usecase) Fetch(ctx context.Context, cfg *question.FetchConfig) ([]*models.Question, int, error) {
|
||||
func (ucase *Usecase) Fetch(ctx context.Context, cfg *question.FetchConfig) ([]*model.Question, int, error) {
|
||||
if cfg == nil {
|
||||
cfg = &question.FetchConfig{
|
||||
Limit: question.FetchMaxLimit,
|
||||
|
@ -79,11 +81,11 @@ func (ucase *usecase) Fetch(ctx context.Context, cfg *question.FetchConfig) ([]*
|
|||
return ucase.questionRepository.Fetch(ctx, cfg)
|
||||
}
|
||||
|
||||
func (ucase *usecase) GetByID(ctx context.Context, id int) (*models.Question, error) {
|
||||
func (ucase *Usecase) GetByID(ctx context.Context, id int) (*model.Question, error) {
|
||||
items, _, err := ucase.Fetch(ctx, &question.FetchConfig{
|
||||
Limit: 1,
|
||||
Count: false,
|
||||
Filter: &models.QuestionFilter{
|
||||
Filter: &model.QuestionFilter{
|
||||
ID: []int{id},
|
||||
},
|
||||
})
|
||||
|
@ -96,7 +98,7 @@ func (ucase *usecase) GetByID(ctx context.Context, id int) (*models.Question, er
|
|||
return items[0], nil
|
||||
}
|
||||
|
||||
func (ucase *usecase) GenerateTest(ctx context.Context, cfg *question.GenerateTestConfig) ([]*models.Question, error) {
|
||||
func (ucase *Usecase) GenerateTest(ctx context.Context, cfg *question.GenerateTestConfig) ([]*model.Question, error) {
|
||||
if cfg == nil {
|
||||
cfg = &question.GenerateTestConfig{
|
||||
Limit: question.TestMaxLimit,
|
||||
|
@ -112,7 +114,7 @@ type validateOptions struct {
|
|||
allowNilValues bool
|
||||
}
|
||||
|
||||
func validateInput(input *models.QuestionInput, opts validateOptions) error {
|
||||
func validateInput(input *model.QuestionInput, opts validateOptions) error {
|
||||
if input.IsEmpty() {
|
||||
return errors.New(messageEmptyPayload)
|
||||
}
|
||||
|
|
|
@ -3,11 +3,11 @@ package user
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
)
|
||||
|
||||
type FetchConfig struct {
|
||||
Filter *models.UserFilter
|
||||
Filter *model.UserFilter
|
||||
Offset int
|
||||
Limit int
|
||||
Sort []string
|
||||
|
@ -15,8 +15,8 @@ type FetchConfig struct {
|
|||
}
|
||||
|
||||
type Repository interface {
|
||||
Store(ctx context.Context, input *models.UserInput) (*models.User, error)
|
||||
UpdateMany(ctx context.Context, f *models.UserFilter, input *models.UserInput) ([]*models.User, error)
|
||||
Delete(ctx context.Context, f *models.UserFilter) ([]*models.User, error)
|
||||
Fetch(ctx context.Context, cfg *FetchConfig) ([]*models.User, int, error)
|
||||
Store(ctx context.Context, input *model.UserInput) (*model.User, error)
|
||||
UpdateMany(ctx context.Context, f *model.UserFilter, input *model.UserInput) ([]*model.User, error)
|
||||
Delete(ctx context.Context, f *model.UserFilter) ([]*model.User, error)
|
||||
Fetch(ctx context.Context, cfg *FetchConfig) ([]*model.User, int, error)
|
||||
}
|
||||
|
|
|
@ -9,28 +9,31 @@ import (
|
|||
"github.com/zdam-egzamin-zawodowy/backend/pkg/util/errorutil"
|
||||
|
||||
"github.com/go-pg/pg/v10"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/user"
|
||||
)
|
||||
|
||||
type pgRepository struct {
|
||||
*pg.DB
|
||||
}
|
||||
|
||||
type PGRepositoryConfig struct {
|
||||
DB *pg.DB
|
||||
}
|
||||
|
||||
func NewPGRepository(cfg *PGRepositoryConfig) (user.Repository, error) {
|
||||
type PGRepository struct {
|
||||
*pg.DB
|
||||
}
|
||||
|
||||
var _ user.Repository = &PGRepository{}
|
||||
|
||||
func NewPGRepository(cfg *PGRepositoryConfig) (*PGRepository, error) {
|
||||
if cfg == nil || cfg.DB == nil {
|
||||
return nil, errors.New("cfg.DB is required")
|
||||
}
|
||||
return &pgRepository{
|
||||
return &PGRepository{
|
||||
cfg.DB,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Store(ctx context.Context, input *models.UserInput) (*models.User, error) {
|
||||
func (repo *PGRepository) Store(ctx context.Context, input *model.UserInput) (*model.User, error) {
|
||||
item := input.ToUser()
|
||||
if _, err := repo.
|
||||
Model(item).
|
||||
|
@ -42,9 +45,9 @@ func (repo *pgRepository) Store(ctx context.Context, input *models.UserInput) (*
|
|||
return item, nil
|
||||
}
|
||||
|
||||
func (repo *pgRepository) UpdateMany(ctx context.Context, f *models.UserFilter, input *models.UserInput) ([]*models.User, error) {
|
||||
func (repo *PGRepository) UpdateMany(ctx context.Context, f *model.UserFilter, input *model.UserInput) ([]*model.User, error) {
|
||||
if _, err := repo.
|
||||
Model(&models.User{}).
|
||||
Model(&model.User{}).
|
||||
Context(ctx).
|
||||
Apply(input.ApplyUpdate).
|
||||
Apply(f.Where).
|
||||
|
@ -61,8 +64,8 @@ func (repo *pgRepository) UpdateMany(ctx context.Context, f *models.UserFilter,
|
|||
return items, nil
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Delete(ctx context.Context, f *models.UserFilter) ([]*models.User, error) {
|
||||
items := make([]*models.User, 0)
|
||||
func (repo *PGRepository) Delete(ctx context.Context, f *model.UserFilter) ([]*model.User, error) {
|
||||
items := make([]*model.User, 0)
|
||||
if _, err := repo.
|
||||
Model(&items).
|
||||
Context(ctx).
|
||||
|
@ -74,9 +77,9 @@ func (repo *pgRepository) Delete(ctx context.Context, f *models.UserFilter) ([]*
|
|||
return items, nil
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, cfg *user.FetchConfig) ([]*models.User, int, error) {
|
||||
func (repo *PGRepository) Fetch(ctx context.Context, cfg *user.FetchConfig) ([]*model.User, int, error) {
|
||||
var err error
|
||||
items := make([]*models.User, 0)
|
||||
items := make([]*model.User, 0)
|
||||
total := 0
|
||||
query := repo.
|
||||
Model(&items).
|
||||
|
|
|
@ -3,15 +3,15 @@ package user
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
)
|
||||
|
||||
type Usecase interface {
|
||||
Store(ctx context.Context, input *models.UserInput) (*models.User, error)
|
||||
UpdateOneByID(ctx context.Context, id int, input *models.UserInput) (*models.User, error)
|
||||
UpdateMany(ctx context.Context, f *models.UserFilter, input *models.UserInput) ([]*models.User, error)
|
||||
Delete(ctx context.Context, f *models.UserFilter) ([]*models.User, error)
|
||||
Fetch(ctx context.Context, cfg *FetchConfig) ([]*models.User, int, error)
|
||||
GetByID(ctx context.Context, id int) (*models.User, error)
|
||||
GetByCredentials(ctx context.Context, email, password string) (*models.User, error)
|
||||
Store(ctx context.Context, input *model.UserInput) (*model.User, error)
|
||||
UpdateOneByID(ctx context.Context, id int, input *model.UserInput) (*model.User, error)
|
||||
UpdateMany(ctx context.Context, f *model.UserFilter, input *model.UserInput) ([]*model.User, error)
|
||||
Delete(ctx context.Context, f *model.UserFilter) ([]*model.User, error)
|
||||
Fetch(ctx context.Context, cfg *FetchConfig) ([]*model.User, int, error)
|
||||
GetByID(ctx context.Context, id int) (*model.User, error)
|
||||
GetByCredentials(ctx context.Context, email, password string) (*model.User, error)
|
||||
}
|
||||
|
|
|
@ -5,41 +5,43 @@ import (
|
|||
"github.com/Kichiyaki/goutil/strutil"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/model"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/user"
|
||||
)
|
||||
|
||||
type usecase struct {
|
||||
userRepository user.Repository
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
UserRepository user.Repository
|
||||
}
|
||||
|
||||
func New(cfg *Config) (user.Usecase, error) {
|
||||
type Usecase struct {
|
||||
userRepository user.Repository
|
||||
}
|
||||
|
||||
var _ user.Usecase = &Usecase{}
|
||||
|
||||
func New(cfg *Config) (*Usecase, error) {
|
||||
if cfg == nil || cfg.UserRepository == nil {
|
||||
return nil, errors.New("cfg.UserRepository is required")
|
||||
}
|
||||
return &usecase{
|
||||
return &Usecase{
|
||||
cfg.UserRepository,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ucase *usecase) Store(ctx context.Context, input *models.UserInput) (*models.User, error) {
|
||||
func (ucase *Usecase) Store(ctx context.Context, input *model.UserInput) (*model.User, error) {
|
||||
if err := validateInput(input.Sanitize(), validateOptions{false}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ucase.userRepository.Store(ctx, input)
|
||||
}
|
||||
|
||||
func (ucase *usecase) UpdateOneByID(ctx context.Context, id int, input *models.UserInput) (*models.User, error) {
|
||||
func (ucase *Usecase) UpdateOneByID(ctx context.Context, id int, input *model.UserInput) (*model.User, error) {
|
||||
if id <= 0 {
|
||||
return nil, errors.New(messageInvalidID)
|
||||
}
|
||||
items, err := ucase.UpdateMany(
|
||||
ctx,
|
||||
&models.UserFilter{
|
||||
&model.UserFilter{
|
||||
ID: []int{id},
|
||||
},
|
||||
input.Sanitize(),
|
||||
|
@ -53,9 +55,9 @@ func (ucase *usecase) UpdateOneByID(ctx context.Context, id int, input *models.U
|
|||
return items[0], nil
|
||||
}
|
||||
|
||||
func (ucase *usecase) UpdateMany(ctx context.Context, f *models.UserFilter, input *models.UserInput) ([]*models.User, error) {
|
||||
func (ucase *Usecase) UpdateMany(ctx context.Context, f *model.UserFilter, input *model.UserInput) ([]*model.User, error) {
|
||||
if f == nil {
|
||||
return []*models.User{}, nil
|
||||
return []*model.User{}, nil
|
||||
}
|
||||
if err := validateInput(input.Sanitize(), validateOptions{true}); err != nil {
|
||||
return nil, err
|
||||
|
@ -67,11 +69,11 @@ func (ucase *usecase) UpdateMany(ctx context.Context, f *models.UserFilter, inpu
|
|||
return items, nil
|
||||
}
|
||||
|
||||
func (ucase *usecase) Delete(ctx context.Context, f *models.UserFilter) ([]*models.User, error) {
|
||||
func (ucase *Usecase) Delete(ctx context.Context, f *model.UserFilter) ([]*model.User, error) {
|
||||
return ucase.userRepository.Delete(ctx, f)
|
||||
}
|
||||
|
||||
func (ucase *usecase) Fetch(ctx context.Context, cfg *user.FetchConfig) ([]*models.User, int, error) {
|
||||
func (ucase *Usecase) Fetch(ctx context.Context, cfg *user.FetchConfig) ([]*model.User, int, error) {
|
||||
if cfg == nil {
|
||||
cfg = &user.FetchConfig{
|
||||
Limit: user.FetchMaxLimit,
|
||||
|
@ -87,11 +89,11 @@ func (ucase *usecase) Fetch(ctx context.Context, cfg *user.FetchConfig) ([]*mode
|
|||
return ucase.userRepository.Fetch(ctx, cfg)
|
||||
}
|
||||
|
||||
func (ucase *usecase) GetByID(ctx context.Context, id int) (*models.User, error) {
|
||||
func (ucase *Usecase) GetByID(ctx context.Context, id int) (*model.User, error) {
|
||||
items, _, err := ucase.Fetch(ctx, &user.FetchConfig{
|
||||
Limit: 1,
|
||||
Count: false,
|
||||
Filter: &models.UserFilter{
|
||||
Filter: &model.UserFilter{
|
||||
ID: []int{id},
|
||||
},
|
||||
})
|
||||
|
@ -104,11 +106,11 @@ func (ucase *usecase) GetByID(ctx context.Context, id int) (*models.User, error)
|
|||
return items[0], nil
|
||||
}
|
||||
|
||||
func (ucase *usecase) GetByCredentials(ctx context.Context, email, password string) (*models.User, error) {
|
||||
func (ucase *Usecase) GetByCredentials(ctx context.Context, email, password string) (*model.User, error) {
|
||||
items, _, err := ucase.Fetch(ctx, &user.FetchConfig{
|
||||
Limit: 1,
|
||||
Count: false,
|
||||
Filter: &models.UserFilter{
|
||||
Filter: &model.UserFilter{
|
||||
Email: []string{email},
|
||||
},
|
||||
})
|
||||
|
@ -128,7 +130,7 @@ type validateOptions struct {
|
|||
acceptNilValues bool
|
||||
}
|
||||
|
||||
func validateInput(input *models.UserInput, opts validateOptions) error {
|
||||
func validateInput(input *model.UserInput, opts validateOptions) error {
|
||||
if input.IsEmpty() {
|
||||
return errors.New(messageEmptyPayload)
|
||||
}
|
||||
|
|
Reference in New Issue