Skip to content

Commit 0cff0b8

Browse files
committed
pagination, field filtering
1 parent c7743a3 commit 0cff0b8

1 file changed

Lines changed: 62 additions & 43 deletions

File tree

pkg/github/projects.go

Lines changed: 62 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -64,18 +64,22 @@ Examples:
6464
if err != nil {
6565
return mcp.NewToolResultError(err.Error()), nil
6666
}
67+
6768
ownerType, err := RequiredParam[string](req, "owner_type")
6869
if err != nil {
6970
return mcp.NewToolResultError(err.Error()), nil
7071
}
72+
7173
queryStr, err := OptionalParam[string](req, "query")
7274
if err != nil {
7375
return mcp.NewToolResultError(err.Error()), nil
7476
}
75-
perPage, err := OptionalIntParamWithDefault(req, "per_page", MaxProjectsPerPage)
77+
78+
pagination, err := extractPaginationOptions(req)
7679
if err != nil {
7780
return mcp.NewToolResultError(err.Error()), nil
7881
}
82+
7983
client, err := getClient(ctx)
8084
if err != nil {
8185
return mcp.NewToolResultError(err.Error()), nil
@@ -86,8 +90,12 @@ Examples:
8690
minimalProjects := []MinimalProject{}
8791

8892
opts := &github.ListProjectsOptions{
89-
ListProjectsPaginationOptions: github.ListProjectsPaginationOptions{PerPage: perPage},
90-
Query: queryStr,
93+
ListProjectsPaginationOptions: github.ListProjectsPaginationOptions{
94+
PerPage: pagination.PerPage,
95+
After: pagination.After,
96+
Before: pagination.Before,
97+
},
98+
Query: queryStr,
9199
}
92100

93101
if ownerType == "org" {
@@ -109,14 +117,12 @@ Examples:
109117
minimalProjects = append(minimalProjects, *convertToMinimalProject(project))
110118
}
111119

112-
if resp.StatusCode != http.StatusOK {
113-
body, err := io.ReadAll(resp.Body)
114-
if err != nil {
115-
return nil, fmt.Errorf("failed to read response body: %w", err)
116-
}
117-
return mcp.NewToolResultError(fmt.Sprintf("failed to list projects: %s", string(body))), nil
120+
response := map[string]any{
121+
"projects": minimalProjects,
122+
"pageInfo": buildPageInfo(resp),
118123
}
119-
r, err := json.Marshal(minimalProjects)
124+
125+
r, err := json.Marshal(response)
120126
if err != nil {
121127
return nil, fmt.Errorf("failed to marshal response: %w", err)
122128
}
@@ -235,18 +241,22 @@ func ListProjectFields(getClient GetClientFn, t translations.TranslationHelperFu
235241
if err != nil {
236242
return mcp.NewToolResultError(err.Error()), nil
237243
}
244+
238245
ownerType, err := RequiredParam[string](req, "owner_type")
239246
if err != nil {
240247
return mcp.NewToolResultError(err.Error()), nil
241248
}
249+
242250
projectNumber, err := RequiredInt(req, "project_number")
243251
if err != nil {
244252
return mcp.NewToolResultError(err.Error()), nil
245253
}
246-
perPage, err := OptionalIntParamWithDefault(req, "per_page", 30)
254+
255+
pagination, err := extractPaginationOptions(req)
247256
if err != nil {
248257
return mcp.NewToolResultError(err.Error()), nil
249258
}
259+
250260
client, err := getClient(ctx)
251261
if err != nil {
252262
return mcp.NewToolResultError(err.Error()), nil
@@ -258,11 +268,8 @@ func ListProjectFields(getClient GetClientFn, t translations.TranslationHelperFu
258268
} else {
259269
url = fmt.Sprintf("users/%s/projectsV2/%d/fields", owner, projectNumber)
260270
}
261-
projectFields := []projectV2Field{}
262271

263-
opts := paginationOptions{PerPage: perPage}
264-
265-
url, err = addOptions(url, opts)
272+
url, err = addOptions(url, pagination)
266273
if err != nil {
267274
return nil, fmt.Errorf("failed to add options to request: %w", err)
268275
}
@@ -272,6 +279,8 @@ func ListProjectFields(getClient GetClientFn, t translations.TranslationHelperFu
272279
return nil, fmt.Errorf("failed to create request: %w", err)
273280
}
274281

282+
projectFields := []*projectV2Field{}
283+
275284
resp, err := client.Do(ctx, httpRequest, &projectFields)
276285
if err != nil {
277286
return ghErrors.NewGitHubAPIErrorResponse(ctx,
@@ -282,14 +291,14 @@ func ListProjectFields(getClient GetClientFn, t translations.TranslationHelperFu
282291
}
283292
defer func() { _ = resp.Body.Close() }()
284293

285-
if resp.StatusCode != http.StatusOK {
286-
body, err := io.ReadAll(resp.Body)
287-
if err != nil {
288-
return nil, fmt.Errorf("failed to read response body: %w", err)
289-
}
290-
return mcp.NewToolResultError(fmt.Sprintf("failed to list project fields: %s", string(body))), nil
294+
filteredFields := filterSpecialTypes(projectFields)
295+
296+
response := map[string]any{
297+
"fields": filteredFields,
298+
"pageInfo": buildPageInfo(resp),
291299
}
292-
r, err := json.Marshal(projectFields)
300+
301+
r, err := json.Marshal(response)
293302
if err != nil {
294303
return nil, fmt.Errorf("failed to marshal response: %w", err)
295304
}
@@ -499,23 +508,28 @@ Never:
499508
if err != nil {
500509
return mcp.NewToolResultError(err.Error()), nil
501510
}
511+
502512
ownerType, err := RequiredParam[string](req, "owner_type")
503513
if err != nil {
504514
return mcp.NewToolResultError(err.Error()), nil
505515
}
516+
506517
projectNumber, err := RequiredInt(req, "project_number")
507518
if err != nil {
508519
return mcp.NewToolResultError(err.Error()), nil
509520
}
510-
perPage, err := OptionalIntParamWithDefault(req, "per_page", 30)
521+
522+
queryStr, err := OptionalParam[string](req, "query")
511523
if err != nil {
512524
return mcp.NewToolResultError(err.Error()), nil
513525
}
514-
queryStr, err := OptionalParam[string](req, "query")
526+
527+
fields, err := OptionalStringArrayParam(req, "fields")
515528
if err != nil {
516529
return mcp.NewToolResultError(err.Error()), nil
517530
}
518-
fields, err := OptionalStringArrayParam(req, "fields")
531+
532+
pagination, err := extractPaginationOptions(req)
519533
if err != nil {
520534
return mcp.NewToolResultError(err.Error()), nil
521535
}
@@ -531,12 +545,13 @@ Never:
531545
} else {
532546
url = fmt.Sprintf("users/%s/projectsV2/%d/items", owner, projectNumber)
533547
}
548+
534549
projectItems := []projectV2Item{}
535550

536551
opts := listProjectItemsOptions{
537-
paginationOptions: paginationOptions{PerPage: perPage},
552+
paginationOptions: pagination,
538553
filterQueryOptions: filterQueryOptions{Query: queryStr},
539-
fieldSelectionOptions: fieldSelectionOptions{Fields: fields},
554+
fieldSelectionOptions: fieldSelectionOptions{Fields: strings.Join(fields, ",")},
540555
}
541556

542557
url, err = addOptions(url, opts)
@@ -559,15 +574,20 @@ Never:
559574
}
560575
defer func() { _ = resp.Body.Close() }()
561576

562-
if resp.StatusCode != http.StatusOK {
563-
body, err := io.ReadAll(resp.Body)
564-
if err != nil {
565-
return nil, fmt.Errorf("failed to read response body: %w", err)
577+
if len(projectItems) > 0 {
578+
for i := range projectItems {
579+
if len(projectItems[i].Fields) > 0 {
580+
projectItems[i].Fields = filterSpecialTypes(projectItems[i].Fields)
581+
}
566582
}
567-
return mcp.NewToolResultError(fmt.Sprintf("%s: %s", ProjectListFailedError, string(body))), nil
568583
}
569584

570-
r, err := json.Marshal(projectItems)
585+
response := map[string]any{
586+
"items": projectItems,
587+
"pageInfo": buildPageInfo(resp),
588+
}
589+
590+
r, err := json.Marshal(response)
571591
if err != nil {
572592
return nil, fmt.Errorf("failed to marshal response: %w", err)
573593
}
@@ -609,18 +629,22 @@ func GetProjectItem(getClient GetClientFn, t translations.TranslationHelperFunc)
609629
if err != nil {
610630
return mcp.NewToolResultError(err.Error()), nil
611631
}
632+
612633
ownerType, err := RequiredParam[string](req, "owner_type")
613634
if err != nil {
614635
return mcp.NewToolResultError(err.Error()), nil
615636
}
637+
616638
projectNumber, err := RequiredInt(req, "project_number")
617639
if err != nil {
618640
return mcp.NewToolResultError(err.Error()), nil
619641
}
642+
620643
itemID, err := RequiredInt(req, "item_id")
621644
if err != nil {
622645
return mcp.NewToolResultError(err.Error()), nil
623646
}
647+
624648
fields, err := OptionalStringArrayParam(req, "fields")
625649
if err != nil {
626650
return mcp.NewToolResultError(err.Error()), nil
@@ -641,7 +665,7 @@ func GetProjectItem(getClient GetClientFn, t translations.TranslationHelperFunc)
641665
opts := fieldSelectionOptions{}
642666

643667
if len(fields) > 0 {
644-
opts.Fields = fields
668+
opts.Fields = strings.Join(fields, ",")
645669
}
646670

647671
url, err = addOptions(url, opts)
@@ -666,13 +690,10 @@ func GetProjectItem(getClient GetClientFn, t translations.TranslationHelperFunc)
666690
}
667691
defer func() { _ = resp.Body.Close() }()
668692

669-
if resp.StatusCode != http.StatusOK {
670-
body, err := io.ReadAll(resp.Body)
671-
if err != nil {
672-
return nil, fmt.Errorf("failed to read response body: %w", err)
673-
}
674-
return mcp.NewToolResultError(fmt.Sprintf("failed to get project item: %s", string(body))), nil
693+
if len(projectItem.Fields) > 0 {
694+
projectItem.Fields = filterSpecialTypes(projectItem.Fields)
675695
}
696+
676697
r, err := json.Marshal(projectItem)
677698
if err != nil {
678699
return nil, fmt.Errorf("failed to marshal response: %w", err)
@@ -1075,9 +1096,7 @@ type filterQueryOptions struct {
10751096
}
10761097

10771098
type fieldSelectionOptions struct {
1078-
// Specific list of field IDs to include in the response. If not provided, only the title field is included.
1079-
// Example: fields=102589,985201,169875 or fields[]=102589&fields[]=985201&fields[]=169875
1080-
Fields []string `url:"fields,omitempty"`
1099+
Fields string `url:"fields,omitempty"`
10811100
}
10821101

10831102
type listProjectItemsOptions struct {

0 commit comments

Comments
 (0)