🏗️ Resolve race condition
This commit is contained in:
1 parent
2ed87e34ee
commit
75f7aa4cd6
4 files changed
+109
-104
No files matched your search
+48
-70
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
"github.com/eat-pray-ai/yutu/pkg"
|
||||
"github.com/eat-pray-ai/yutu/pkg/channel"
|
||||
"github.com/eat-pray-ai/yutu/pkg/utils"
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -33,10 +32,10 @@ type listIn struct {
|
||||
ForUsername string `json:"forUsername"`
|
||||
Hl string `json:"hl"`
|
||||
Ids []string `json:"ids"`
|
||||
ManagedByMe *string `json:"managedByMe,omitempty"`
|
||||
ManagedByMe *bool `json:"managedByMe,omitempty"`
|
||||
MaxResults int64 `json:"maxResults"`
|
||||
Mine *string `json:"mine,omitempty"`
|
||||
MySubscribers *string `json:"mySubscribers,omitempty"`
|
||||
Mine *bool `json:"mine,omitempty"`
|
||||
MySubscribers *bool `json:"mySubscribers,omitempty"`
|
||||
OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"`
|
||||
Parts []string `json:"parts"`
|
||||
Output string `json:"output"`
|
||||
@@ -47,32 +46,17 @@ var listInSchema = &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Required: []string{},
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"categoryId": {
|
||||
Type: "string", Description: cidUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"forHandle": {
|
||||
Type: "string", Description: fhUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"forUsername": {
|
||||
Type: "string", Description: fuUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"hl": {
|
||||
Type: "string", Description: hlUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"categoryId": {Type: "string", Description: cidUsage},
|
||||
"forHandle": {Type: "string", Description: fhUsage},
|
||||
"forUsername": {Type: "string", Description: fuUsage},
|
||||
"hl": {Type: "string", Description: hlUsage},
|
||||
"ids": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Type: "array", Items: &jsonschema.Schema{Type: "string"},
|
||||
Description: listIdsUsage,
|
||||
Default: json.RawMessage(`[]`),
|
||||
},
|
||||
"managedByMe": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: mbmUsage, Default: json.RawMessage(`""`),
|
||||
Type: "boolean", Enum: []any{true, false}, Description: mbmUsage,
|
||||
},
|
||||
"maxResults": {
|
||||
Type: "number", Description: pkg.MRUsage,
|
||||
@@ -80,21 +64,14 @@ var listInSchema = &jsonschema.Schema{
|
||||
Minimum: jsonschema.Ptr(float64(0)),
|
||||
},
|
||||
"mine": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: mineUsage, Default: json.RawMessage(`""`),
|
||||
Type: "boolean", Enum: []any{true, false}, Description: mineUsage,
|
||||
},
|
||||
"mySubscribers": {
|
||||
Type: "string", Enum: []any{"true", "false", ""},
|
||||
Description: msUsage, Default: json.RawMessage(`""`),
|
||||
},
|
||||
"onBehalfOfContentOwner": {
|
||||
Type: "string", Description: "",
|
||||
Default: json.RawMessage(`""`),
|
||||
Type: "boolean", Enum: []any{true, false}, Description: msUsage,
|
||||
},
|
||||
"onBehalfOfContentOwner": {Type: "string", Description: ""},
|
||||
"parts": {
|
||||
Type: "array", Items: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
Type: "array", Items: &jsonschema.Schema{Type: "string"},
|
||||
Description: pkg.PartsUsage,
|
||||
Default: json.RawMessage(`["id","snippet","status"]`),
|
||||
},
|
||||
@@ -102,10 +79,7 @@ var listInSchema = &jsonschema.Schema{
|
||||
Type: "string", Enum: []any{"json", "yaml", "table"},
|
||||
Description: pkg.TableUsage, Default: json.RawMessage(`"yaml"`),
|
||||
},
|
||||
"jsonpath": {
|
||||
Type: "string", Description: pkg.JPUsage,
|
||||
Default: json.RawMessage(`""`),
|
||||
},
|
||||
"jsonpath": {Type: "string", Description: pkg.JPUsage},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -159,7 +133,22 @@ var listCmd = &cobra.Command{
|
||||
Short: listShort,
|
||||
Long: listLong,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := list(cmd.OutOrStdout())
|
||||
input := &listIn{
|
||||
CategoryId: categoryId,
|
||||
ForHandle: forHandle,
|
||||
ForUsername: forUsername,
|
||||
Hl: hl,
|
||||
Ids: ids,
|
||||
ManagedByMe: managedByMe,
|
||||
MaxResults: maxResults,
|
||||
Mine: mine,
|
||||
MySubscribers: mySubscribers,
|
||||
OnBehalfOfContentOwner: onBehalfOfContentOwner,
|
||||
Parts: parts,
|
||||
Output: output,
|
||||
Jsonpath: jsonpath,
|
||||
}
|
||||
err := input.call(cmd.OutOrStdout())
|
||||
if err != nil {
|
||||
_ = cmd.Help()
|
||||
cmd.PrintErrf("Error: %v\n", err)
|
||||
@@ -177,22 +166,8 @@ func listHandler(
|
||||
),
|
||||
)
|
||||
|
||||
categoryId = input.CategoryId
|
||||
forHandle = input.ForHandle
|
||||
forUsername = input.ForUsername
|
||||
hl = input.Hl
|
||||
ids = input.Ids
|
||||
managedByMe = utils.StrToBoolPtr(input.ManagedByMe)
|
||||
maxResults = input.MaxResults
|
||||
mine = utils.StrToBoolPtr(input.Mine)
|
||||
mySubscribers = utils.StrToBoolPtr(input.MySubscribers)
|
||||
onBehalfOfContentOwner = input.OnBehalfOfContentOwner
|
||||
parts = input.Parts
|
||||
output = input.Output
|
||||
jsonpath = input.Jsonpath
|
||||
|
||||
var writer bytes.Buffer
|
||||
err := list(&writer)
|
||||
err := input.call(&writer)
|
||||
if err != nil {
|
||||
logger.ErrorContext(ctx, err.Error(), "input", input)
|
||||
return nil, nil, err
|
||||
@@ -200,20 +175,23 @@ func listHandler(
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func list(writer io.Writer) error {
|
||||
c := channel.NewChannel(
|
||||
channel.WithCategoryId(categoryId),
|
||||
channel.WithForHandle(forHandle),
|
||||
channel.WithForUsername(forUsername),
|
||||
channel.WithHl(hl),
|
||||
channel.WithIDs(ids),
|
||||
channel.WithChannelManagedByMe(managedByMe),
|
||||
channel.WithMaxResults(maxResults),
|
||||
channel.WithMine(mine),
|
||||
channel.WithMySubscribers(mySubscribers),
|
||||
channel.WithOnBehalfOfContentOwner(onBehalfOfContentOwner),
|
||||
func (l *listIn) call(writer io.Writer, opts ...channel.Option) error {
|
||||
defaultOpts := []channel.Option{
|
||||
channel.WithCategoryId(l.CategoryId),
|
||||
channel.WithForHandle(l.ForHandle),
|
||||
channel.WithForUsername(l.ForUsername),
|
||||
channel.WithHl(l.Hl),
|
||||
channel.WithIDs(l.Ids),
|
||||
channel.WithChannelManagedByMe(l.ManagedByMe),
|
||||
channel.WithMaxResults(l.MaxResults),
|
||||
channel.WithMine(l.Mine),
|
||||
channel.WithMySubscribers(l.MySubscribers),
|
||||
channel.WithOnBehalfOfContentOwner(l.OnBehalfOfContentOwner),
|
||||
channel.WithService(nil),
|
||||
)
|
||||
}
|
||||
defaultOpts = append(defaultOpts, opts...)
|
||||
|
||||
return c.List(parts, output, jsonpath, writer)
|
||||
c := channel.NewChannel(defaultOpts...)
|
||||
|
||||
return c.List(l.Parts, l.Output, l.Jsonpath, writer)
|
||||
}
|
||||
+25
-21
@@ -114,7 +114,17 @@ var updateCmd = &cobra.Command{
|
||||
Short: updateShort,
|
||||
Long: updateLong,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := update(cmd.OutOrStdout())
|
||||
input := &updateIn{
|
||||
Ids: ids,
|
||||
Country: country,
|
||||
CustomUrl: customUrl,
|
||||
DefaultLanguage: defaultLanguage,
|
||||
Description: description,
|
||||
Title: title,
|
||||
Output: output,
|
||||
Jsonpath: jsonpath,
|
||||
}
|
||||
err := input.call(cmd.OutOrStdout())
|
||||
if err != nil {
|
||||
_ = cmd.Help()
|
||||
cmd.PrintErrf("Error: %v\n", err)
|
||||
@@ -134,17 +144,8 @@ func updateHandler(
|
||||
),
|
||||
)
|
||||
|
||||
ids = input.Ids
|
||||
country = input.Country
|
||||
customUrl = input.CustomUrl
|
||||
defaultLanguage = input.DefaultLanguage
|
||||
description = input.Description
|
||||
title = input.Title
|
||||
output = input.Output
|
||||
jsonpath = input.Jsonpath
|
||||
|
||||
var writer bytes.Buffer
|
||||
err := update(&writer)
|
||||
err := input.call(&writer)
|
||||
if err != nil {
|
||||
logger.ErrorContext(ctx, err.Error(), "input", input)
|
||||
return nil, nil, err
|
||||
@@ -152,17 +153,20 @@ func updateHandler(
|
||||
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil
|
||||
}
|
||||
|
||||
func update(writer io.Writer) error {
|
||||
c := channel.NewChannel(
|
||||
channel.WithIDs(ids),
|
||||
channel.WithCountry(country),
|
||||
channel.WithCustomUrl(customUrl),
|
||||
channel.WithDefaultLanguage(defaultLanguage),
|
||||
channel.WithDescription(description),
|
||||
channel.WithTitle(title),
|
||||
func (u *updateIn) call(writer io.Writer, opts ...channel.Option) error {
|
||||
defaultOpts := []channel.Option{
|
||||
channel.WithIDs(u.Ids),
|
||||
channel.WithCountry(u.Country),
|
||||
channel.WithCustomUrl(u.CustomUrl),
|
||||
channel.WithDefaultLanguage(u.DefaultLanguage),
|
||||
channel.WithDescription(u.Description),
|
||||
channel.WithTitle(u.Title),
|
||||
channel.WithMaxResults(1),
|
||||
channel.WithService(nil),
|
||||
)
|
||||
}
|
||||
defaultOpts = append(defaultOpts, opts...)
|
||||
|
||||
return c.Update(output, jsonpath, writer)
|
||||
c := channel.NewChannel(defaultOpts...)
|
||||
|
||||
return c.Update(u.Output, u.Jsonpath, writer)
|
||||
}
|
||||
+12
-11
@@ -18,12 +18,12 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
service *youtube.Service
|
||||
errGetChannel = errors.New("failed to get channel")
|
||||
errUpdateChannel = errors.New("failed to update channel")
|
||||
)
|
||||
|
||||
type channel struct {
|
||||
service *youtube.Service
|
||||
CategoryId string `yaml:"category_id" json:"category_id"`
|
||||
ForHandle string `yaml:"for_handle" json:"for_handle"`
|
||||
ForUsername string `yaml:"for_username" json:"for_username"`
|
||||
@@ -57,11 +57,18 @@ func NewChannel(opts ...Option) Channel[youtube.Channel] {
|
||||
opt(c)
|
||||
}
|
||||
|
||||
if c.service == nil {
|
||||
c.service = auth.NewY2BService(
|
||||
auth.WithCredential("", pkg.Root.FS()),
|
||||
auth.WithCacheToken("", pkg.Root.FS()),
|
||||
).GetService()
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *channel) Get(parts []string) ([]*youtube.Channel, error) {
|
||||
call := service.Channels.List(parts)
|
||||
call := c.service.Channels.List(parts)
|
||||
if c.CategoryId != "" {
|
||||
call = call.CategoryId(c.CategoryId)
|
||||
}
|
||||
@@ -168,7 +175,7 @@ func (c *channel) Update(output string, jsonpath string, writer io.Writer) error
|
||||
cha.Snippet.Title = c.Title
|
||||
}
|
||||
|
||||
call := service.Channels.Update(parts, cha)
|
||||
call := c.service.Channels.Update(parts, cha)
|
||||
res, err := call.Do()
|
||||
if err != nil {
|
||||
return errors.Join(errUpdateChannel, err)
|
||||
@@ -288,13 +295,7 @@ func WithTitle(title string) Option {
|
||||
}
|
||||
|
||||
func WithService(svc *youtube.Service) Option {
|
||||
return func(_ *channel) {
|
||||
if svc == nil {
|
||||
svc = auth.NewY2BService(
|
||||
auth.WithCredential("", pkg.Root.FS()),
|
||||
auth.WithCacheToken("", pkg.Root.FS()),
|
||||
).GetService()
|
||||
}
|
||||
service = svc
|
||||
return func(c *channel) {
|
||||
c.service = svc
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,9 @@ func TestNewChannel(t *testing.T) {
|
||||
{
|
||||
name: "with no options",
|
||||
args: args{
|
||||
opts: []Option{},
|
||||
opts: []Option{
|
||||
WithService(&youtube.Service{}),
|
||||
},
|
||||
},
|
||||
want: &channel{},
|
||||
},
|
||||
@@ -82,6 +84,7 @@ func TestNewChannel(t *testing.T) {
|
||||
WithChannelManagedByMe(nil),
|
||||
WithMine(nil),
|
||||
WithMySubscribers(nil),
|
||||
WithService(&youtube.Service{}),
|
||||
},
|
||||
},
|
||||
want: &channel{},
|
||||
@@ -93,6 +96,7 @@ func TestNewChannel(t *testing.T) {
|
||||
WithChannelManagedByMe(&managedByMeFalse),
|
||||
WithMine(&mineFalse),
|
||||
WithMySubscribers(&mySubscribersFalse),
|
||||
WithService(&youtube.Service{}),
|
||||
},
|
||||
},
|
||||
want: &channel{
|
||||
@@ -106,6 +110,7 @@ func TestNewChannel(t *testing.T) {
|
||||
args: args{
|
||||
opts: []Option{
|
||||
WithMaxResults(0),
|
||||
WithService(&youtube.Service{}),
|
||||
},
|
||||
},
|
||||
want: &channel{
|
||||
@@ -117,6 +122,7 @@ func TestNewChannel(t *testing.T) {
|
||||
args: args{
|
||||
opts: []Option{
|
||||
WithMaxResults(-5),
|
||||
WithService(&youtube.Service{}),
|
||||
},
|
||||
},
|
||||
want: &channel{
|
||||
@@ -137,6 +143,7 @@ func TestNewChannel(t *testing.T) {
|
||||
WithDefaultLanguage(""),
|
||||
WithDescription(""),
|
||||
WithTitle(""),
|
||||
WithService(&youtube.Service{}),
|
||||
},
|
||||
},
|
||||
want: &channel{
|
||||
@@ -160,6 +167,7 @@ func TestNewChannel(t *testing.T) {
|
||||
WithTitle("My Channel"),
|
||||
WithCountry("UK"),
|
||||
WithMaxResults(50),
|
||||
WithService(&youtube.Service{}),
|
||||
},
|
||||
},
|
||||
want: &channel{
|
||||
@@ -174,7 +182,21 @@ func TestNewChannel(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(
|
||||
tt.name, func(t *testing.T) {
|
||||
if got := NewChannel(tt.args.opts...); !reflect.DeepEqual(got, tt.want) {
|
||||
if got := NewChannel(tt.args.opts...); !reflect.DeepEqual(got.(*channel).CategoryId, tt.want.(*channel).CategoryId) ||
|
||||
!reflect.DeepEqual(got.(*channel).ForHandle, tt.want.(*channel).ForHandle) ||
|
||||
!reflect.DeepEqual(got.(*channel).ForUsername, tt.want.(*channel).ForUsername) ||
|
||||
!reflect.DeepEqual(got.(*channel).Hl, tt.want.(*channel).Hl) ||
|
||||
!reflect.DeepEqual(got.(*channel).IDs, tt.want.(*channel).IDs) ||
|
||||
!reflect.DeepEqual(got.(*channel).ManagedByMe, tt.want.(*channel).ManagedByMe) ||
|
||||
!reflect.DeepEqual(got.(*channel).MaxResults, tt.want.(*channel).MaxResults) ||
|
||||
!reflect.DeepEqual(got.(*channel).Mine, tt.want.(*channel).Mine) ||
|
||||
!reflect.DeepEqual(got.(*channel).MySubscribers, tt.want.(*channel).MySubscribers) ||
|
||||
!reflect.DeepEqual(got.(*channel).OnBehalfOfContentOwner, tt.want.(*channel).OnBehalfOfContentOwner) ||
|
||||
!reflect.DeepEqual(got.(*channel).Country, tt.want.(*channel).Country) ||
|
||||
!reflect.DeepEqual(got.(*channel).CustomUrl, tt.want.(*channel).CustomUrl) ||
|
||||
!reflect.DeepEqual(got.(*channel).DefaultLanguage, tt.want.(*channel).DefaultLanguage) ||
|
||||
!reflect.DeepEqual(got.(*channel).Description, tt.want.(*channel).Description) ||
|
||||
!reflect.DeepEqual(got.(*channel).Title, tt.want.(*channel).Title) {
|
||||
t.Errorf("NewChannel() = %v, want %v", got, tt.want)
|
||||
}
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user