v0.0.31 Корректно сгенерирован резолвер получения Author для клипов
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
admin 2025-09-03 08:32:38 +03:00
parent 73946da723
commit c7e109fe99
4 changed files with 43 additions and 58 deletions

2
.env
View File

@ -13,4 +13,4 @@ SMTP_FROM=info@tailly.ru
AppURL="https://tailly.ru" AppURL="https://tailly.ru"
MESSAGE_SERVICE_ADDRESS="tailly_messages:50052" MESSAGE_SERVICE_ADDRESS="tailly_messages:50052"
SUBSCRIBE_SERVICE_ADDRESS="tailly_subscribers:50053" SUBSCRIBE_SERVICE_ADDRESS="tailly_subscribers:50053"
CLIP_SERVICE_ADDRESS="tailly_clips:50054" CLIP_SERVICE_ADDRESS="localhost:50054"

View File

@ -8,7 +8,6 @@ type Clip struct {
VideoURL string `json:"videoUrl"` VideoURL string `json:"videoUrl"`
ThumbnailURL string `json:"thumbnailUrl"` ThumbnailURL string `json:"thumbnailUrl"`
AuthorID int `json:"-"` AuthorID int `json:"-"`
Author *User `json:"author"`
LikesCount int `json:"likesCount"` LikesCount int `json:"likesCount"`
CommentsCount int `json:"commentsCount"` CommentsCount int `json:"commentsCount"`
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"fmt" "fmt"
"io" "io"
"log"
"tailly_back_v2/internal/domain" "tailly_back_v2/internal/domain"
"tailly_back_v2/proto" "tailly_back_v2/proto"
"time" "time"
@ -31,47 +30,15 @@ func (r *clipResolver) IsLiked(ctx context.Context, obj *domain.Clip) (bool, err
} }
func (r *clipResolver) Author(ctx context.Context, obj *domain.Clip) (*domain.User, error) { func (r *clipResolver) Author(ctx context.Context, obj *domain.Clip) (*domain.User, error) {
log.Printf("Resolving author for clip %d, authorID: %d", obj.ID, obj.AuthorID)
if obj.AuthorID == 0 { if obj.AuthorID == 0 {
log.Printf("WARNING: Clip %d has no authorID, returning stub", obj.ID) return nil, fmt.Errorf("clip has no author ID")
return &domain.User{
ID: 0,
Username: "Unknown User",
Avatar: "/img/logo.png",
Email: "unknown@example.com",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}, nil
} }
// Получаем пользователя из сервиса
author, err := r.Services.User.GetByID(ctx, obj.AuthorID) author, err := r.Services.User.GetByID(ctx, obj.AuthorID)
if err != nil { if err != nil {
log.Printf("ERROR: Failed to get author %d for clip %d: %v", obj.AuthorID, obj.ID, err) return nil, fmt.Errorf("failed to get author: %w", err)
return &domain.User{
ID: obj.AuthorID,
Username: "Error Loading User",
Avatar: "/img/logo.png",
Email: "error@example.com",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}, nil
}
log.Printf("Resolved author for clip %d", author)
if author == nil {
log.Printf("WARNING: Author %d not found for clip %d", obj.AuthorID, obj.ID)
return &domain.User{
ID: obj.AuthorID,
Username: "Deleted User",
Avatar: "/img/logo.png",
Email: "deleted@example.com",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}, nil
} }
log.Printf("Successfully resolved author %d for clip %d", obj.AuthorID, obj.ID)
return author, nil return author, nil
} }
@ -252,24 +219,10 @@ func (r *queryResolver) Clips(ctx context.Context, limit *int, offset *int) ([]*
Offset: int32(offsetVal), Offset: int32(offsetVal),
}) })
if err != nil { if err != nil {
log.Printf("ERROR: Failed to get clips from gRPC service: %v", err)
return nil, fmt.Errorf("failed to get clips: %w", err) return nil, fmt.Errorf("failed to get clips: %w", err)
} }
clips := r.protoClipsToDomain(resp.Clips) return r.protoClipsToDomain(resp.Clips), nil
// Детальное логирование
log.Printf("Retrieved %d clips from gRPC service", len(clips))
for i, clip := range clips {
log.Printf("Clip %d: ID=%d, Title=%s, AuthorID=%d", i, clip.ID, clip.Title, clip.AuthorID)
// Проверяем, что AuthorID заполнен
if clip.AuthorID == 0 {
log.Printf("ERROR: Clip %d has AuthorID=0!", clip.ID)
}
}
return clips, nil
} }
// UserClips is the resolver for the userClips field. // UserClips is the resolver for the userClips field.

View File

@ -303,6 +303,8 @@ type ChatResolver interface {
UpdatedAt(ctx context.Context, obj *domain.Chat) (string, error) UpdatedAt(ctx context.Context, obj *domain.Chat) (string, error)
} }
type ClipResolver interface { type ClipResolver interface {
Author(ctx context.Context, obj *domain.Clip) (*domain.User, error)
IsLiked(ctx context.Context, obj *domain.Clip) (bool, error) IsLiked(ctx context.Context, obj *domain.Clip) (bool, error)
CreatedAt(ctx context.Context, obj *domain.Clip) (string, error) CreatedAt(ctx context.Context, obj *domain.Clip) (string, error)
UpdatedAt(ctx context.Context, obj *domain.Clip) (string, error) UpdatedAt(ctx context.Context, obj *domain.Clip) (string, error)
@ -3274,7 +3276,7 @@ func (ec *executionContext) _Clip_author(ctx context.Context, field graphql.Coll
}() }()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
ctx = rctx // use context from middleware stack in children ctx = rctx // use context from middleware stack in children
return obj.Author, nil return ec.resolvers.Clip().Author(rctx, obj)
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@ -3295,8 +3297,8 @@ func (ec *executionContext) fieldContext_Clip_author(_ context.Context, field gr
fc = &graphql.FieldContext{ fc = &graphql.FieldContext{
Object: "Clip", Object: "Clip",
Field: field, Field: field,
IsMethod: false, IsMethod: true,
IsResolver: false, IsResolver: true,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
switch field.Name { switch field.Name {
case "id": case "id":
@ -13905,10 +13907,41 @@ func (ec *executionContext) _Clip(ctx context.Context, sel ast.SelectionSet, obj
atomic.AddUint32(&out.Invalids, 1) atomic.AddUint32(&out.Invalids, 1)
} }
case "author": case "author":
out.Values[i] = ec._Clip_author(ctx, field, obj) field := field
if out.Values[i] == graphql.Null {
atomic.AddUint32(&out.Invalids, 1) innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._Clip_author(ctx, field, obj)
if res == graphql.Null {
atomic.AddUint32(&fs.Invalids, 1)
}
return res
} }
if field.Deferrable != nil {
dfs, ok := deferred[field.Deferrable.Label]
di := 0
if ok {
dfs.AddField(field)
di = len(dfs.Values) - 1
} else {
dfs = graphql.NewFieldSet([]graphql.CollectedField{field})
deferred[field.Deferrable.Label] = dfs
}
dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler {
return innerFunc(ctx, dfs)
})
// don't run the out.Concurrently() call below
out.Values[i] = graphql.Null
continue
}
out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
case "commentsCount": case "commentsCount":
out.Values[i] = ec._Clip_commentsCount(ctx, field, obj) out.Values[i] = ec._Clip_commentsCount(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {