diff --git a/go.mod b/go.mod index db6261d21..422366fba 100644 --- a/go.mod +++ b/go.mod @@ -7,9 +7,11 @@ require ( cloud.google.com/go/iam v1.1.10 cloud.google.com/go/longrunning v0.5.9 cloud.google.com/go/storage v1.41.0 + code.sajari.com/docconv v1.3.8 github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/JohannesKaufmann/html-to-markdown v1.5.0 github.com/PaesslerAG/jsonpath v0.1.1 + github.com/PuerkitoBio/goquery v1.9.1 github.com/anthropics/anthropic-sdk-go v0.2.0-alpha.3 github.com/belong-inc/go-hubspot v0.9.0 github.com/chromedp/chromedp v0.10.0 @@ -23,9 +25,12 @@ require ( github.com/gabriel-vasile/mimetype v1.4.3 github.com/gage-technologies/mistral-go v1.1.0 github.com/go-audio/audio v1.0.0 + github.com/go-audio/wav v1.1.0 github.com/go-chi/chi/v5 v5.1.0 github.com/go-openapi/strfmt v0.23.0 github.com/go-redis/redismock/v9 v9.2.0 + github.com/go-resty/resty/v2 v2.12.0 + github.com/go-sql-driver/mysql v1.8.1 github.com/gocolly/colly/v2 v2.1.0 github.com/gofrs/uuid v4.4.0+incompatible github.com/gogo/status v1.1.1 @@ -33,6 +38,7 @@ require ( github.com/golang-migrate/migrate/v4 v4.15.2 github.com/google/go-cmp v0.6.0 github.com/google/go-github/v62 v62.0.0 + github.com/google/uuid v1.6.0 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 github.com/h2non/filetype v1.1.3 @@ -42,6 +48,7 @@ require ( github.com/instill-ai/usage-client v0.2.4-alpha.0.20240123081026-6c78d9a5197a github.com/instill-ai/x v0.6.0-alpha github.com/itchyny/gojq v0.12.14 + github.com/jackc/pgconn v1.14.3 github.com/jackc/pgx/v5 v5.5.5 github.com/jmoiron/sqlx v1.4.0 github.com/json-iterator/go v1.1.12 @@ -53,6 +60,7 @@ require ( github.com/lestrrat-go/option v1.0.0 github.com/lestrrat-go/pdebug v0.0.0-20210111095411-35b07dbf089b github.com/lestrrat-go/structinfo v0.0.0-20210312050401-7f8bd69d6acb + github.com/lib/pq v1.10.9 github.com/mennanov/fieldmask-utils v1.0.0 github.com/minio/minio-go/v7 v7.0.76 github.com/nakagami/firebirdsql v0.9.10 @@ -62,6 +70,7 @@ require ( github.com/redis/go-redis/v9 v9.5.1 github.com/sijms/go-ora v1.3.2 github.com/slack-go/slack v0.12.5 + github.com/stretchr/testify v1.9.0 github.com/tmc/langchaingo v0.1.10 github.com/u2takey/ffmpeg-go v0.5.0 github.com/warmans/ffmpeg-go v1.0.0 @@ -69,6 +78,7 @@ require ( github.com/weaviate/weaviate-go-client/v4 v4.15.0 github.com/xuri/excelize/v2 v2.8.1 github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 + github.com/zaf/resample v1.5.0 go.einride.tech/aip v0.60.0 go.mongodb.org/mongo-driver v1.16.0 go.opentelemetry.io/contrib/propagators/b3 v1.17.0 @@ -83,17 +93,20 @@ require ( go.temporal.io/api v1.16.0 go.temporal.io/sdk v1.21.0 go.uber.org/zap v1.26.0 + golang.org/x/crypto v0.31.0 golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 golang.org/x/image v0.18.0 golang.org/x/mod v0.17.0 golang.org/x/net v0.33.0 golang.org/x/oauth2 v0.21.0 + golang.org/x/text v0.21.0 google.golang.org/api v0.189.0 google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 gopkg.in/guregu/null.v4 v4.0.0 + gopkg.in/yaml.v3 v3.0.1 gorm.io/datatypes v1.2.0 gorm.io/driver/postgres v1.5.7 gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde @@ -109,7 +122,6 @@ require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/JalfResi/justext v0.0.0-20221106200834-be571e3e3052 // indirect github.com/PaesslerAG/gval v1.0.0 // indirect - github.com/PuerkitoBio/goquery v1.9.1 github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/advancedlogic/GoOse v0.0.0-20191112112754-e742535969c1 // indirect @@ -144,7 +156,6 @@ require ( github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gigawattio/window v0.0.0-20180317192513-0f5467e35573 // indirect github.com/go-audio/riff v1.0.0 // indirect - github.com/go-audio/wav v1.1.0 github.com/go-ini/ini v1.67.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -156,8 +167,6 @@ require ( github.com/go-openapi/spec v0.20.4 // indirect github.com/go-openapi/swag v0.22.4 // indirect github.com/go-openapi/validate v0.21.0 // indirect - github.com/go-resty/resty/v2 v2.12.0 - github.com/go-sql-driver/mysql v1.8.1 github.com/gobwas/glob v0.2.3 // indirect github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/pool v0.2.1 // indirect @@ -173,7 +182,6 @@ require ( github.com/google/flatbuffers v23.5.26+incompatible // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/s2a-go v0.1.7 // indirect - github.com/google/uuid v1.6.0 github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.5 // indirect github.com/gorilla/websocket v1.5.1 // indirect @@ -182,7 +190,6 @@ require ( github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect github.com/itchyny/timefmt-go v0.1.5 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect - github.com/jackc/pgconn v1.14.3 github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgproto3/v2 v2.3.3 // indirect @@ -200,7 +207,6 @@ require ( github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/levigross/exp-html v0.0.0-20120902181939-8df60c69a8f5 // indirect - github.com/lib/pq v1.10.9 github.com/machinebox/graphql v0.2.2 github.com/mailru/easyjson v0.7.7 // indirect github.com/matryer/is v1.4.1 // indirect @@ -235,7 +241,6 @@ require ( github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect github.com/streamer45/silero-vad-go v0.2.1 github.com/stretchr/objx v0.5.2 // indirect - github.com/stretchr/testify v1.9.0 github.com/temoto/robotstxt v1.1.2 // indirect github.com/tidwall/gjson v1.14.4 // indirect github.com/tidwall/match v1.1.1 // indirect @@ -248,7 +253,6 @@ require ( github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53 // indirect github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05 // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect - github.com/zaf/resample v1.5.0 github.com/zeebo/xxh3 v1.0.2 // indirect gitlab.com/golang-commonmark/html v0.0.0-20191124015941-a22733972181 // indirect gitlab.com/golang-commonmark/linkify v0.0.0-20191026162114-a0c2df6c8f82 // indirect @@ -264,16 +268,13 @@ require ( go.opentelemetry.io/proto/otlp v1.1.0 // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.10.0 // indirect - golang.org/x/crypto v0.31.0 golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect - golang.org/x/text v0.21.0 golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20240722135656-d784300faade // indirect - gopkg.in/yaml.v3 v3.0.1 gorm.io/driver/mysql v1.4.7 // indirect modernc.org/mathutil v1.5.0 // indirect ) diff --git a/pkg/component/application/github/v0/client.go b/pkg/component/application/github/v0/client.go index d705b13b4..3c899a5e3 100644 --- a/pkg/component/application/github/v0/client.go +++ b/pkg/component/application/github/v0/client.go @@ -13,54 +13,14 @@ import ( "github.com/instill-ai/x/errmsg" ) -// RepositoriesService is a wrapper around the github.RepositoriesService -type RepositoriesService interface { - GetCommit(ctx context.Context, owner string, repository string, sha string, opts *github.ListOptions) (*github.RepositoryCommit, *github.Response, error) - ListHooks(ctx context.Context, owner string, repository string, opts *github.ListOptions) ([]*github.Hook, *github.Response, error) - GetHook(ctx context.Context, owner string, repository string, id int64) (*github.Hook, *github.Response, error) - CreateHook(ctx context.Context, owner string, repository string, hook *github.Hook) (*github.Hook, *github.Response, error) - DeleteHook(ctx context.Context, owner string, repository string, id int64) (*github.Response, error) - EditHook(ctx context.Context, owner string, repository string, id int64, hook *github.Hook) (*github.Hook, *github.Response, error) -} - -// RepoInfoInterface is an interface for the RepoInfo struct -type RepoInfoInterface interface { - getOwner() (string, error) - getRepository() (string, error) -} - -// RepoInfo is a struct that contains the owner and repository of a repository -type RepoInfo struct { - Owner string `instill:"owner"` - Repository string `instill:"repository"` -} - -func (info RepoInfo) getOwner() (string, error) { - if info.Owner == "" { - return "", errmsg.AddMessage( - fmt.Errorf("owner not provided"), - "Owner not provided.", - ) - } - return info.Owner, nil -} - -func (info RepoInfo) getRepository() (string, error) { - if info.Repository == "" { - return "", errmsg.AddMessage( - fmt.Errorf("repository not provided"), - "Repository not provided.", - ) - } - return info.Repository, nil -} - // Client is a struct that contains the github client and the repositories service type Client struct { *github.Client - Repositories RepositoriesService - PullRequests PullRequestService - Issues IssuesService + Repositories RepositoriesService + PullRequests PullRequestsService + Issues IssuesService + Users UsersService + Organizations OrganizationsService } func newClient(ctx context.Context, setup *structpb.Struct) Client { @@ -75,10 +35,12 @@ func newClient(ctx context.Context, setup *structpb.Struct) Client { } client := github.NewClient(oauth2Client) return Client{ - Client: client, - Repositories: client.Repositories, - PullRequests: client.PullRequests, - Issues: client.Issues, + Client: client, + Repositories: client.Repositories, + PullRequests: client.PullRequests, + Issues: client.Issues, + Users: client.Users, + Organizations: client.Organizations, } } diff --git a/pkg/component/application/github/v0/commits.go b/pkg/component/application/github/v0/commits.go index 7c2794f7f..7ba41b969 100644 --- a/pkg/component/application/github/v0/commits.go +++ b/pkg/component/application/github/v0/commits.go @@ -40,23 +40,24 @@ func (client *Client) extractCommitFile(file *github.CommitFile) CommitFile { } } -func (client *Client) extractCommitInformation(ctx context.Context, owner, repository string, originalCommit *github.RepositoryCommit, needCommitDetails bool) Commit { +func (client *Client) extractCommitInformation(ctx context.Context, owner, repository string, originalCommit *github.RepositoryCommit, needCommitDetails bool) (Commit, error) { if !needCommitDetails { return Commit{ SHA: originalCommit.GetSHA(), Message: originalCommit.GetCommit().GetMessage(), - } + }, nil } stats := originalCommit.GetStats() commitFiles := originalCommit.Files if stats == nil || commitFiles == nil { - commit, err := client.getCommitFunc(ctx, owner, repository, originalCommit.GetSHA()) - if err == nil { - // only update stats and files if there is no error - // otherwise, we will maintain the original commit information - stats = commit.GetStats() - commitFiles = commit.Files + commit, _, err := client.Repositories.GetCommit(ctx, owner, repository, originalCommit.GetSHA(), nil) + if err != nil { + return Commit{}, addErrMsgToClientError(err) } + // only update stats and files if there is no error + // otherwise, we will maintain the original commit information + stats = commit.GetStats() + commitFiles = commit.Files } files := make([]CommitFile, len(commitFiles)) for idx, file := range commitFiles { @@ -71,10 +72,5 @@ func (client *Client) extractCommitInformation(ctx context.Context, owner, repos Changes: stats.GetTotal(), }, Files: files, - } -} - -func (client *Client) getCommitFunc(ctx context.Context, owner string, repository string, sha string) (*github.RepositoryCommit, error) { - commit, _, err := client.Repositories.GetCommit(ctx, owner, repository, sha, nil) - return commit, addErrMsgToClientError(err) + }, nil } diff --git a/pkg/component/application/github/v0/component_test.go b/pkg/component/application/github/v0/component_test.go index 3ad76821c..22f730fb0 100644 --- a/pkg/component/application/github/v0/component_test.go +++ b/pkg/component/application/github/v0/component_test.go @@ -16,9 +16,11 @@ const ( ) var MockGithubClient = &Client{ - Repositories: &MockRepositoriesService{}, - PullRequests: &MockPullRequestService{}, - Issues: &MockIssuesService{}, + Repositories: &MockRepositoriesService{}, + PullRequests: &MockPullRequestsService{}, + Issues: &MockIssuesService{}, + Users: &MockUsersService{}, + Organizations: &MockOrganizationsService{}, } type TaskCase[inType any, outType any] struct { diff --git a/pkg/component/application/github/v0/config/definition.yaml b/pkg/component/application/github/v0/config/definition.yaml index 387107f5e..e45546e4e 100644 --- a/pkg/component/application/github/v0/config/definition.yaml +++ b/pkg/component/application/github/v0/config/definition.yaml @@ -6,6 +6,8 @@ availableTasks: - TASK_CREATE_REVIEW_COMMENT - TASK_LIST_ISSUES - TASK_GET_ISSUE + - TASK_GET_USER + - TASK_GET_ORGANIZATION - TASK_CREATE_ISSUE - TASK_CREATE_WEBHOOK availableEvents: diff --git a/pkg/component/application/github/v0/config/events.yaml b/pkg/component/application/github/v0/config/events.yaml index cfefea4ef..ed07776b2 100644 --- a/pkg/component/application/github/v0/config/events.yaml +++ b/pkg/component/application/github/v0/config/events.yaml @@ -20,7 +20,7 @@ EVENT_STAR_CREATED: format: string starred-at: format: string - description: 'The time the star was created. This is a timestamp in ISO 8601 format: `YYYY-MM-DDTHH:MM:SSZ`. Will be `null` for the `deleted` action.' + description: "The time the star was created. This is a timestamp in ISO 8601 format: `YYYY-MM-DDTHH:MM:SSZ`. Will be `null` for the `deleted` action." repository: $schema: http://json-schema.org/draft-07/schema description: A git repository @@ -151,7 +151,7 @@ EVENT_STAR_CREATED: email: format: - string - - 'null' + - "null" avatar-url: format: string gravatar-id: @@ -196,7 +196,7 @@ EVENT_STAR_CREATED: description: The repository description. format: - string - - 'null' + - "null" fork: description: Whether the repository is a fork. format: boolean @@ -321,7 +321,7 @@ EVENT_STAR_CREATED: oneOf: - format: integer - format: string - - format: 'null' + - format: "null" git-url: format: string ssh-url: @@ -333,7 +333,7 @@ EVENT_STAR_CREATED: homepage: format: - string - - 'null' + - "null" size: format: integer stargazers-count: @@ -343,7 +343,7 @@ EVENT_STAR_CREATED: language: format: - string - - 'null' + - "null" has-issues: description: Whether issues are enabled. default: true @@ -371,7 +371,7 @@ EVENT_STAR_CREATED: mirror-url: format: - string - - 'null' + - "null" archived: description: Whether the repository is archived. default: false @@ -400,13 +400,13 @@ EVENT_STAR_CREATED: url: format: - string - - 'null' + - "null" node-id: format: string additionalProperties: false title: License format: object - - format: 'null' + - format: "null" forks: format: integer open-issues: @@ -494,7 +494,7 @@ EVENT_STAR_CREATED: custom-properties: additionalProperties: oneOf: - - format: 'null' + - format: "null" - format: string - items: format: string @@ -536,7 +536,7 @@ EVENT_STAR_CREATED: email: format: - string - - 'null' + - "null" avatar-url: format: string gravatar-id: @@ -617,7 +617,7 @@ EVENT_STAR_CREATED: description: format: - string - - 'null' + - "null" additionalProperties: false title: Organization format: object @@ -641,7 +641,7 @@ EVENT_STAR_CREATED: format: object messageExamples: - action: created - starred-at: '2019-05-15T15:20:40Z' + starred-at: "2019-05-15T15:20:40Z" repository: id: 186853002 node-id: MDEwOlJlcG9zaXRvcnkxODY4NTMwMDI= @@ -653,7 +653,7 @@ EVENT_STAR_CREATED: id: 21031067 node-id: MDQ6VXNlcjIxMDMxMDY3 avatar-url: https://avatars1.githubusercontent.com/u/21031067?v=4 - gravatar-id: '' + gravatar-id: "" url: https://api.github.com/users/Codertocat html-url: https://github.com/Codertocat followers-url: https://api.github.com/users/Codertocat/followers @@ -707,9 +707,9 @@ EVENT_STAR_CREATED: labels-url: https://api.github.com/repos/Codertocat/Hello-World/labels{/name} releases-url: https://api.github.com/repos/Codertocat/Hello-World/releases{/id} deployments-url: https://api.github.com/repos/Codertocat/Hello-World/deployments - created-at: '2019-05-15T15:19:25Z' - updated-at: '2019-05-15T15:20:40Z' - pushed-at: '2019-05-15T15:20:33Z' + created-at: "2019-05-15T15:19:25Z" + updated-at: "2019-05-15T15:20:40Z" + pushed-at: "2019-05-15T15:20:33Z" git-url: git://github.com/Codertocat/Hello-World.git ssh-url: git@github.com:Codertocat/Hello-World.git clone-url: https://github.com/Codertocat/Hello-World.git @@ -744,7 +744,7 @@ EVENT_STAR_CREATED: id: 21031067 node-id: MDQ6VXNlcjIxMDMxMDY3 avatar-url: https://avatars1.githubusercontent.com/u/21031067?v=4 - gravatar-id: '' + gravatar-id: "" url: https://api.github.com/users/Codertocat html-url: https://github.com/Codertocat followers-url: https://api.github.com/users/Codertocat/followers diff --git a/pkg/component/application/github/v0/config/tasks.json b/pkg/component/application/github/v0/config/tasks.json new file mode 100644 index 000000000..25684accb --- /dev/null +++ b/pkg/component/application/github/v0/config/tasks.json @@ -0,0 +1,2774 @@ +{ + "$defs": { + "commit": { + "description": "A commit object.", + "properties": { + "sha": { + "description": "SHA of the commit.", + "instillUIOrder": 0, + "title": "Commit SHA", + "instillFormat": "string", + "type": "string" + }, + "message": { + "description": "Message of the commit.", + "instillUIOrder": 1, + "title": "Commit message", + "instillFormat": "string", + "type": "string" + }, + "stats": { + "instillUIOrder": 2, + "$ref": "#/$defs/commitStats", + "required": [] + }, + "files": { + "description": "Files in the commit.", + "instillUIOrder": 3, + "title": "Files", + "instillFormat": "array:object", + "type": "array", + "items": { + "$ref": "#/$defs/commitFile", + "required": [], + "description": "A file in the commit." + } + } + }, + "required": [], + "title": "Commit", + "type": "object" + }, + "commitFile": { + "description": "A commit file object.", + "properties": { + "filename": { + "description": "Name of the file.", + "instillUIOrder": 0, + "title": "File name", + "instillFormat": "string", + "type": "string" + }, + "patch": { + "description": "Patch of the file.", + "instillUIOrder": 1, + "title": "Patch", + "instillFormat": "string", + "type": "string" + }, + "$ref": "#/$defs/commitStats/properties" + }, + "required": [], + "title": "Commit File", + "type": "object" + }, + "commitStats": { + "description": "Stats of changes.", + "properties": { + "additions": { + "description": "Number of additions in the commit.", + "instillUIOrder": 0, + "title": "Additions", + "instillFormat": "integer", + "type": "integer" + }, + "deletions": { + "description": "Number of deletions in the commit.", + "instillUIOrder": 1, + "title": "Deletions", + "instillFormat": "integer", + "type": "integer" + }, + "changes": { + "description": "Total number of changes in the commit.", + "instillUIOrder": 2, + "title": "Total changes", + "instillFormat": "integer", + "type": "integer" + } + }, + "required": [], + "title": "Commit stats", + "type": "object" + }, + "issue": { + "description": "An issue object.", + "properties": { + "number": { + "description": "Number of the issue.", + "title": "Issue Number", + "instillFormat": "integer", + "type": "integer", + "instillUIOrder": 0 + }, + "state": { + "description": "State of the issue.", + "title": "Issue state", + "instillFormat": "string", + "type": "string", + "instillUIOrder": 1 + }, + "title": { + "description": "Title of the issue.", + "title": "Issue title", + "instillFormat": "string", + "type": "string", + "instillUIOrder": 2 + }, + "body": { + "description": "Body of the issue.", + "title": "Issue body", + "instillFormat": "string", + "type": "string", + "instillUIOrder": 3 + }, + "assignee": { + "description": "Assignee of the issue.", + "title": "Assignee", + "instillFormat": "string", + "type": "string", + "instillUIOrder": 4 + }, + "assignees": { + "description": "Assignees of the issue.", + "title": "Assignees", + "instillFormat": "array:string", + "type": "array", + "instillUIOrder": 5, + "items": { + "instillFormat": "string", + "type": "string" + } + }, + "labels": { + "description": "Labels of the issue.", + "title": "Labels", + "instillFormat": "array:string", + "type": "array", + "instillUIOrder": 6, + "items": { + "instillFormat": "string", + "type": "string" + } + }, + "is-pull-request": { + "description": "Whether the issue is a pull request.", + "title": "Is Pull Request", + "instillFormat": "boolean", + "type": "boolean", + "instillUIOrder": 7 + } + }, + "title": "Issue", + "type": "object" + }, + "listOptions": { + "description": "Options for listing pages.", + "properties": { + "page": { + "default": 1, + "description": "Page number of the results to fetch. Default is 1.", + "title": "Page", + "instillFormat": "integer", + "instillAcceptFormats": [ + "integer" + ], + "instillUpstreamTypes": [ + "value", + "reference" + ], + "type": "integer", + "instillUIOrder": 0 + }, + "per-page": { + "default": 30, + "description": "Number of results to fetch per page. Default is 30.", + "title": "Per Page", + "instillFormat": "integer", + "instillAcceptFormats": [ + "integer" + ], + "instillUpstreamTypes": [ + "value", + "reference" + ], + "type": "integer", + "instillUIOrder": 1 + } + }, + "title": "List options", + "type": "object" + }, + "organization": { + "description": "Organization Full", + "properties": { + "login": { + "type": "string", + "examples": [ + "github" + ], + "instillUIOrder": 0 + }, + "id": { + "type": "integer", + "examples": [ + 1 + ], + "instillUIOrder": 1 + }, + "node-id": { + "type": "string", + "examples": [ + "MDEyOk9yZ2FuaXphdGlvbjE=" + ], + "instillUIOrder": 2 + }, + "url": { + "type": "string", + "format": "uri", + "examples": [ + "https://api.github.com/orgs/github" + ], + "instillUIOrder": 3 + }, + "repos-url": { + "type": "string", + "format": "uri", + "examples": [ + "https://api.github.com/orgs/github/repos" + ], + "instillUIOrder": 4 + }, + "events-url": { + "type": "string", + "format": "uri", + "examples": [ + "https://api.github.com/orgs/github/events" + ], + "instillUIOrder": 5 + }, + "hooks-url": { + "type": "string", + "examples": [ + "https://api.github.com/orgs/github/hooks" + ], + "instillUIOrder": 6 + }, + "issues-url": { + "type": "string", + "examples": [ + "https://api.github.com/orgs/github/issues" + ], + "instillUIOrder": 7 + }, + "members-url": { + "type": "string", + "examples": [ + "https://api.github.com/orgs/github/members{/member}" + ], + "instillUIOrder": 8 + }, + "public-members-url": { + "type": "string", + "examples": [ + "https://api.github.com/orgs/github/public_members{/member}" + ], + "instillUIOrder": 9 + }, + "avatar-url": { + "type": "string", + "examples": [ + "https://github.com/images/error/octocat_happy.gif" + ], + "instillUIOrder": 10 + }, + "description": { + "type": [ + "string", + "null" + ], + "examples": [ + "A great organization" + ], + "instillUIOrder": 11 + }, + "name": { + "type": "string", + "examples": [ + "github" + ], + "instillUIOrder": 12 + }, + "company": { + "type": "string", + "examples": [ + "GitHub" + ], + "instillUIOrder": 13 + }, + "blog": { + "type": "string", + "format": "uri", + "examples": [ + "https://github.com/blog" + ], + "instillUIOrder": 14 + }, + "location": { + "type": "string", + "examples": [ + "San Francisco" + ], + "instillUIOrder": 15 + }, + "email": { + "type": "string", + "format": "email", + "examples": [ + "octocat@github.com" + ], + "instillUIOrder": 16 + }, + "twitter-username": { + "type": [ + "string", + "null" + ], + "examples": [ + "github" + ], + "instillUIOrder": 17 + }, + "is-verified": { + "type": "boolean", + "examples": [ + true + ], + "instillUIOrder": 18 + }, + "has-organization-projects": { + "type": "boolean", + "examples": [ + true + ], + "instillUIOrder": 19 + }, + "has-repository-projects": { + "type": "boolean", + "examples": [ + true + ], + "instillUIOrder": 20 + }, + "public-repos": { + "type": "integer", + "examples": [ + 2 + ], + "instillUIOrder": 21 + }, + "public-gists": { + "type": "integer", + "examples": [ + 1 + ], + "instillUIOrder": 22 + }, + "followers": { + "type": "integer", + "examples": [ + 20 + ], + "instillUIOrder": 23 + }, + "following": { + "type": "integer", + "examples": [ + 0 + ], + "instillUIOrder": 24 + }, + "html-url": { + "type": "string", + "format": "uri", + "examples": [ + "https://github.com/octocat" + ], + "instillUIOrder": 25 + }, + "type": { + "type": "string", + "examples": [ + "Organization" + ], + "instillUIOrder": 26 + }, + "total-private-repos": { + "type": "integer", + "examples": [ + 100 + ], + "instillUIOrder": 27 + }, + "owned-private-repos": { + "type": "integer", + "examples": [ + 100 + ], + "instillUIOrder": 28 + }, + "private-gists": { + "type": [ + "integer", + "null" + ], + "examples": [ + 81 + ], + "instillUIOrder": 29 + }, + "disk-usage": { + "type": [ + "integer", + "null" + ], + "examples": [ + 10000 + ], + "instillUIOrder": 30 + }, + "collaborators": { + "type": [ + "integer", + "null" + ], + "description": "The number of collaborators on private repositories.\n\nThis field may be null if the number of private repositories is over 50,000.", + "examples": [ + 8 + ], + "instillUIOrder": 31 + }, + "billing-email": { + "type": [ + "string", + "null" + ], + "format": "email", + "examples": [ + "org@example.com" + ], + "instillUIOrder": 32 + }, + "plan": { + "type": "object", + "properties": { + "name": { + "type": "string", + "instillUIOrder": 0 + }, + "space": { + "type": "integer", + "instillUIOrder": 1 + }, + "private-repos": { + "type": "integer", + "instillUIOrder": 2 + }, + "filled-seats": { + "type": "integer", + "instillUIOrder": 3 + }, + "seats": { + "type": "integer", + "instillUIOrder": 4 + } + }, + "required": [ + "name", + "space", + "private-repos" + ], + "instillUIOrder": 33 + }, + "default-repository-permission": { + "type": [ + "string", + "null" + ], + "instillUIOrder": 34 + }, + "members-can-create-repositories": { + "type": [ + "boolean", + "null" + ], + "examples": [ + true + ], + "instillUIOrder": 35 + }, + "two-factor-requirement-enabled": { + "type": [ + "boolean", + "null" + ], + "examples": [ + true + ], + "instillUIOrder": 36 + }, + "members-allowed-repository-creation-type": { + "type": "string", + "examples": [ + "all" + ], + "instillUIOrder": 37 + }, + "members-can-create-public-repositories": { + "type": "boolean", + "examples": [ + true + ], + "instillUIOrder": 38 + }, + "members-can-create-private-repositories": { + "type": "boolean", + "examples": [ + true + ], + "instillUIOrder": 39 + }, + "members-can-create-internal-repositories": { + "type": "boolean", + "examples": [ + true + ], + "instillUIOrder": 40 + }, + "members-can-create-pages": { + "type": "boolean", + "examples": [ + true + ], + "instillUIOrder": 41 + }, + "members-can-create-public-pages": { + "type": "boolean", + "examples": [ + true + ], + "instillUIOrder": 42 + }, + "members-can-create-private-pages": { + "type": "boolean", + "examples": [ + true + ], + "instillUIOrder": 43 + }, + "members-can-fork-private-repositories": { + "type": [ + "boolean", + "null" + ], + "examples": [ + false + ], + "instillUIOrder": 44 + }, + "web-commit-signoff-required": { + "type": "boolean", + "examples": [ + false + ], + "instillUIOrder": 45 + }, + "advanced-security-enabled-for-new-repositories": { + "type": "boolean", + "description": "**Endpoint closing down notice.** Please use [code security configurations](https://docs.github.com/rest/code-security/configurations) instead.\n\nWhether GitHub Advanced Security is enabled for new repositories and repositories transferred to this organization.\n\nThis field is only visible to organization owners or members of a team with the security manager role.", + "examples": [ + false + ], + "deprecated": true, + "instillUIOrder": 46 + }, + "dependabot-alerts-enabled-for-new-repositories": { + "type": "boolean", + "description": "**Endpoint closing down notice.** Please use [code security configurations](https://docs.github.com/rest/code-security/configurations) instead.\n\nWhether Dependabot alerts are automatically enabled for new repositories and repositories transferred to this organization.\n\nThis field is only visible to organization owners or members of a team with the security manager role.", + "examples": [ + false + ], + "deprecated": true, + "instillUIOrder": 47 + }, + "dependabot-security-updates-enabled-for-new-repositories": { + "type": "boolean", + "description": "**Endpoint closing down notice.** Please use [code security configurations](https://docs.github.com/rest/code-security/configurations) instead.\n\nWhether Dependabot security updates are automatically enabled for new repositories and repositories transferred to this organization.\n\nThis field is only visible to organization owners or members of a team with the security manager role.", + "examples": [ + false + ], + "deprecated": true, + "instillUIOrder": 48 + }, + "dependency-graph-enabled-for-new-repositories": { + "type": "boolean", + "description": "**Endpoint closing down notice.** Please use [code security configurations](https://docs.github.com/rest/code-security/configurations) instead.\n\nWhether dependency graph is automatically enabled for new repositories and repositories transferred to this organization.\n\nThis field is only visible to organization owners or members of a team with the security manager role.", + "examples": [ + false + ], + "deprecated": true, + "instillUIOrder": 49 + }, + "secret-scanning-enabled-for-new-repositories": { + "type": "boolean", + "description": "**Endpoint closing down notice.** Please use [code security configurations](https://docs.github.com/rest/code-security/configurations) instead.\n\nWhether secret scanning is automatically enabled for new repositories and repositories transferred to this organization.\n\nThis field is only visible to organization owners or members of a team with the security manager role.", + "examples": [ + false + ], + "deprecated": true, + "instillUIOrder": 50 + }, + "secret-scanning-push-protection-enabled-for-new-repositories": { + "type": "boolean", + "description": "**Endpoint closing down notice.** Please use [code security configurations](https://docs.github.com/rest/code-security/configurations) instead.\n\nWhether secret scanning push protection is automatically enabled for new repositories and repositories transferred to this organization.\n\nThis field is only visible to organization owners or members of a team with the security manager role.", + "examples": [ + false + ], + "deprecated": true, + "instillUIOrder": 51 + }, + "secret-scanning-push-protection-custom-link-enabled": { + "type": "boolean", + "description": "Whether a custom link is shown to contributors who are blocked from pushing a secret by push protection.", + "examples": [ + false + ], + "instillUIOrder": 52 + }, + "secret-scanning-push-protection-custom-link": { + "type": [ + "string", + "null" + ], + "description": "An optional URL string to display to contributors who are blocked from pushing a secret.", + "examples": [ + "https://github.com/test-org/test-repo/blob/main/README.md" + ], + "instillUIOrder": 53 + }, + "created-at": { + "type": "string", + "format": "date-time", + "examples": [ + "2008-01-14T04:33:35Z" + ], + "instillUIOrder": 54 + }, + "updated-at": { + "type": "string", + "format": "date-time", + "instillUIOrder": 55 + }, + "archived-at": { + "type": [ + "string", + "null" + ], + "format": "date-time", + "instillUIOrder": 56 + }, + "deploy-keys-enabled-for-repositories": { + "type": "boolean", + "description": "Controls whether or not deploy keys may be added and used for repositories in the organization.", + "examples": [ + false + ], + "instillUIOrder": 57 + } + }, + "required": [ + "login", + "url", + "id", + "node-id", + "repos-url", + "events-url", + "hooks-url", + "issues-url", + "members-url", + "public-members-url", + "avatar-url", + "description", + "html-url", + "has-organization-projects", + "has-repository-projects", + "public-repos", + "public-gists", + "followers", + "following", + "type", + "created-at", + "updated-at", + "archived-at" + ], + "title": "Organization Full", + "type": "object" + }, + "pullRequest": { + "description": "A pull request object.", + "properties": { + "id": { + "description": "ID of the PR.", + "instillUIOrder": 0, + "title": "PR id", + "instillFormat": "integer", + "type": "integer" + }, + "number": { + "description": "Number of the PR.", + "instillFormat": "integer", + "instillUIOrder": 1, + "title": "PR number", + "type": "integer" + }, + "state": { + "description": "State of the PR.", + "instillFormat": "string", + "instillUIOrder": 2, + "title": "PR state", + "type": "string" + }, + "title": { + "description": "Title of the PR.", + "instillFormat": "string", + "instillUIOrder": 3, + "title": "PR Title", + "type": "string" + }, + "body": { + "description": "Body of the PR.", + "instillFormat": "string", + "instillUIOrder": 4, + "title": "PR body", + "type": "string" + }, + "diff-url": { + "description": "URL to the diff of the PR.", + "instillFormat": "string", + "instillUIOrder": 5, + "title": "PR diff url", + "type": "string" + }, + "head": { + "description": "Head commit of the PR (in SHA value).", + "instillFormat": "string", + "instillUIOrder": 6, + "title": "PR head", + "type": "string" + }, + "base": { + "description": "Base commit of the PR (in SHA value).", + "instillFormat": "string", + "instillUIOrder": 7, + "title": "PR base", + "type": "string" + }, + "comments-num": { + "description": "Number of comments on the PR.", + "instillFormat": "integer", + "instillUIOrder": 8, + "title": "Number of PR comments", + "type": "integer" + }, + "commits-num": { + "description": "Number of commits in the PR.", + "instillFormat": "integer", + "instillUIOrder": 9, + "title": "Number of PR commits", + "type": "integer" + }, + "reviewComment-num": { + "description": "Number of review comments in the PR.", + "instillFormat": "integer", + "instillUIOrder": 10, + "title": "Number of PR review comments", + "type": "integer" + }, + "commits": { + "description": "Commits in the PR.", + "instillUIOrder": 11, + "title": "Commits", + "instillFormat": "array:object", + "type": "array", + "items": { + "$ref": "#/$defs/commit", + "required": [], + "description": "A commit in the PR." + } + } + }, + "required": [], + "title": "Pull Request", + "type": "object" + }, + "repositoryInfo": { + "description": "Repository information.", + "properties": { + "owner": { + "description": "Owner of the repository.", + "instillUIMultiline": false, + "instillUIOrder": 0, + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "instillFormat": "string", + "title": "Owner", + "type": "string" + }, + "repository": { + "description": "Repository name.", + "instillUIMultiline": false, + "instillUIOrder": 1, + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "instillFormat": "string", + "title": "Repository", + "type": "string" + } + }, + "required": [ + "owner", + "repository" + ], + "title": "Repository Info", + "type": "object" + }, + "response": { + "description": "A GitHub API response that wraps the standard HTTP response and provides convenient access to pagination information.", + "properties": { + "next-page": { + "description": "The next page number for offset pagination", + "type": "integer", + "instillUIOrder": 0 + }, + "prev-page": { + "description": "The previous page number for offset pagination", + "type": "integer", + "instillUIOrder": 1 + }, + "first-page": { + "description": "The first page number for offset pagination", + "type": "integer", + "instillUIOrder": 2 + }, + "last-page": { + "description": "The last page number for offset pagination", + "type": "integer", + "instillUIOrder": 3 + }, + "next-page-token": { + "description": "Token pointing to the next record for cursor pagination", + "type": "string", + "instillUIOrder": 4 + }, + "cursor": { + "description": "Cursor value for cursor-based pagination", + "type": "string", + "instillUIOrder": 5 + }, + "before": { + "description": "Token for before/after pagination", + "type": "string", + "instillUIOrder": 6 + }, + "after": { + "description": "Token for before/after pagination", + "type": "string", + "instillUIOrder": 7 + } + }, + "title": "Response", + "type": "object" + }, + "reviewComment": { + "description": "A review comment object.", + "properties": { + "id": { + "description": "ID of the comment.", + "title": "Comment id", + "instillFormat": "integer", + "type": "integer", + "instillUIOrder": 0 + }, + "in-reply-to-id": { + "description": "ID of the comment this comment is in reply to.", + "instillFormat": "integer", + "title": "In Reply To", + "type": "integer", + "instillUIOrder": 1 + }, + "commit-id": { + "description": "SHA of the commit on which you want to comment.", + "instillFormat": "string", + "title": "Commit SHA", + "type": "string", + "instillUIOrder": 2 + }, + "body": { + "description": "Body of the comment.", + "instillFormat": "string", + "title": "Comment body", + "type": "string", + "instillUIOrder": 3 + }, + "path": { + "description": "Path of the file the comment is on.", + "instillFormat": "string", + "title": "Comment path", + "type": "string", + "instillUIOrder": 4 + }, + "line": { + "instillShortDescription": "The last line of the range that your comment applies to. Your comment will be placed under this line.", + "description": "The line of the blob in the pull request diff that the comment applies to. For a multi-line comment, the last line of the range that your comment applies to.", + "title": "Comment end line", + "instillFormat": "integer", + "type": "integer", + "instillUIOrder": 5 + }, + "start-line": { + "description": "The first line in the pull request diff that your multi-line comment applies to. Only multi-line comment needs to fill this field.", + "title": "Comment start line", + "instillFormat": "integer", + "type": "integer", + "instillUIOrder": 6 + }, + "side": { + "instillShortDescription": "Side of the end line, can be one of: LEFT, RIGHT, side. Default is side.", + "description": "Side of the end line, can be one of: LEFT, RIGHT, side. LEFT is the left side of the diff (deletion), RIGHT is the right side of the diff (addition), and side is the comment on the PR as a whole. Default is side.", + "default": "side", + "enum": [ + "LEFT", + "RIGHT", + "side" + ], + "title": "Comment end side", + "instillFormat": "string", + "type": "string", + "instillUIOrder": 7 + }, + "start-side": { + "instillShortDescription": "Side of the start line, can be one of: LEFT, RIGHT, side. Default is side.", + "description": "Side of the start line, can be one of: LEFT, RIGHT, side. LEFT is the left side of the diff (deletion), RIGHT is the right side of the diff (addition), and side is the comment on the PR as a whole. Default is side.", + "default": "side", + "enum": [ + "LEFT", + "RIGHT", + "side" + ], + "title": "Comment start side", + "instillFormat": "string", + "type": "string", + "instillUIOrder": 8 + }, + "subject-type": { + "description": "Subject type of the comment, can be one of: line, file. Default is line.", + "title": "Comment type", + "instillFormat": "string", + "default": "line", + "enum": [ + "line", + "file" + ], + "type": "string", + "instillUIOrder": 9 + }, + "created-at": { + "description": "Time the comment was created.", + "title": "Comment created at", + "instillFormat": "string", + "type": "string", + "instillUIOrder": 10 + }, + "updated-at": { + "description": "Time the comment was updated.", + "title": "Comment updated at", + "instillFormat": "string", + "type": "string", + "instillUIOrder": 11 + }, + "user": { + "description": "User who created the comment.", + "title": "User", + "instillFormat": "object", + "type": "object", + "instillUIOrder": 12, + "properties": { + "id": { + "description": "ID of the user.", + "title": "User id", + "instillFormat": "integer", + "type": "integer", + "instillUIOrder": 0 + }, + "url": { + "description": "URL of the user.", + "title": "User URL", + "instillFormat": "string", + "type": "string", + "instillUIOrder": 1 + } + }, + "required": [] + } + }, + "title": "Review Comment", + "type": "object" + }, + "user": { + "oneOf": [ + { + "title": "Private User", + "description": "Private User", + "type": "object", + "properties": { + "login": { + "type": "string", + "examples": [ + "octocat" + ], + "instillUIOrder": 0 + }, + "id": { + "type": "integer", + "format": "int64", + "examples": [ + 1 + ], + "instillUIOrder": 1 + }, + "user_view_type": { + "type": "string", + "instillUIOrder": 2 + }, + "node_id": { + "type": "string", + "examples": [ + "MDQ6VXNlcjE=" + ], + "instillUIOrder": 3 + }, + "avatar_url": { + "type": "string", + "format": "uri", + "examples": [ + "https://github.com/images/error/octocat_happy.gif" + ], + "instillUIOrder": 4 + }, + "gravatar_id": { + "type": [ + "string", + "null" + ], + "examples": [ + "41d064eb2195891e12d0413f63227ea7" + ], + "instillUIOrder": 5 + }, + "url": { + "type": "string", + "format": "uri", + "examples": [ + "https://api.github.com/users/octocat" + ], + "instillUIOrder": 6 + }, + "html_url": { + "type": "string", + "format": "uri", + "examples": [ + "https://github.com/octocat" + ], + "instillUIOrder": 7 + }, + "followers_url": { + "type": "string", + "format": "uri", + "examples": [ + "https://api.github.com/users/octocat/followers" + ], + "instillUIOrder": 8 + }, + "following_url": { + "type": "string", + "examples": [ + "https://api.github.com/users/octocat/following{/other_user}" + ], + "instillUIOrder": 9 + }, + "gists_url": { + "type": "string", + "examples": [ + "https://api.github.com/users/octocat/gists{/gist_id}" + ], + "instillUIOrder": 10 + }, + "starred_url": { + "type": "string", + "examples": [ + "https://api.github.com/users/octocat/starred{/owner}{/repo}" + ], + "instillUIOrder": 11 + }, + "subscriptions_url": { + "type": "string", + "format": "uri", + "examples": [ + "https://api.github.com/users/octocat/subscriptions" + ], + "instillUIOrder": 12 + }, + "organizations_url": { + "type": "string", + "format": "uri", + "examples": [ + "https://api.github.com/users/octocat/orgs" + ], + "instillUIOrder": 13 + }, + "repos_url": { + "type": "string", + "format": "uri", + "examples": [ + "https://api.github.com/users/octocat/repos" + ], + "instillUIOrder": 14 + }, + "events_url": { + "type": "string", + "examples": [ + "https://api.github.com/users/octocat/events{/privacy}" + ], + "instillUIOrder": 15 + }, + "received_events_url": { + "type": "string", + "format": "uri", + "examples": [ + "https://api.github.com/users/octocat/received_events" + ], + "instillUIOrder": 16 + }, + "type": { + "type": "string", + "examples": [ + "User" + ], + "instillUIOrder": 17 + }, + "site_admin": { + "type": "boolean", + "instillUIOrder": 18 + }, + "name": { + "type": [ + "string", + "null" + ], + "examples": [ + "monalisa octocat" + ], + "instillUIOrder": 19 + }, + "company": { + "type": [ + "string", + "null" + ], + "examples": [ + "GitHub" + ], + "instillUIOrder": 20 + }, + "blog": { + "type": [ + "string", + "null" + ], + "examples": [ + "https://github.com/blog" + ], + "instillUIOrder": 21 + }, + "location": { + "type": [ + "string", + "null" + ], + "examples": [ + "San Francisco" + ], + "instillUIOrder": 22 + }, + "email": { + "type": [ + "string", + "null" + ], + "format": "email", + "examples": [ + "octocat@github.com" + ], + "instillUIOrder": 23 + }, + "notification_email": { + "type": [ + "string", + "null" + ], + "format": "email", + "examples": [ + "octocat@github.com" + ], + "instillUIOrder": 24 + }, + "hireable": { + "type": [ + "boolean", + "null" + ], + "instillUIOrder": 25 + }, + "bio": { + "type": [ + "string", + "null" + ], + "examples": [ + "There once was..." + ], + "instillUIOrder": 26 + }, + "twitter_username": { + "type": [ + "string", + "null" + ], + "examples": [ + "monalisa" + ], + "instillUIOrder": 27 + }, + "public_repos": { + "type": "integer", + "examples": [ + 2 + ], + "instillUIOrder": 28 + }, + "public_gists": { + "type": "integer", + "examples": [ + 1 + ], + "instillUIOrder": 29 + }, + "followers": { + "type": "integer", + "examples": [ + 20 + ], + "instillUIOrder": 30 + }, + "following": { + "type": "integer", + "examples": [ + 0 + ], + "instillUIOrder": 31 + }, + "created_at": { + "type": "string", + "format": "date-time", + "examples": [ + "2008-01-14T04:33:35Z" + ], + "instillUIOrder": 32 + }, + "updated_at": { + "type": "string", + "format": "date-time", + "examples": [ + "2008-01-14T04:33:35Z" + ], + "instillUIOrder": 33 + }, + "private_gists": { + "type": "integer", + "examples": [ + 81 + ], + "instillUIOrder": 34 + }, + "total_private_repos": { + "type": "integer", + "examples": [ + 100 + ], + "instillUIOrder": 35 + }, + "owned_private_repos": { + "type": "integer", + "examples": [ + 100 + ], + "instillUIOrder": 36 + }, + "disk_usage": { + "type": "integer", + "examples": [ + 10000 + ], + "instillUIOrder": 37 + }, + "collaborators": { + "type": "integer", + "examples": [ + 8 + ], + "instillUIOrder": 38 + }, + "two_factor_authentication": { + "type": "boolean", + "examples": [ + true + ], + "instillUIOrder": 39 + }, + "plan": { + "type": "object", + "properties": { + "collaborators": { + "type": "integer", + "instillUIOrder": 0 + }, + "name": { + "type": "string", + "instillUIOrder": 1 + }, + "space": { + "type": "integer", + "instillUIOrder": 2 + }, + "private_repos": { + "type": "integer", + "instillUIOrder": 3 + } + }, + "required": [ + "collaborators", + "name", + "space", + "private_repos" + ], + "instillUIOrder": 40 + }, + "business_plus": { + "type": "boolean", + "instillUIOrder": 41 + }, + "ldap_dn": { + "type": "string", + "instillUIOrder": 42 + } + }, + "required": [ + "avatar_url", + "events_url", + "followers_url", + "following_url", + "gists_url", + "gravatar_id", + "html_url", + "id", + "node_id", + "login", + "organizations_url", + "received_events_url", + "repos_url", + "site_admin", + "starred_url", + "subscriptions_url", + "type", + "url", + "bio", + "blog", + "company", + "email", + "followers", + "following", + "hireable", + "location", + "name", + "public_gists", + "public_repos", + "created_at", + "updated_at", + "collaborators", + "disk_usage", + "owned_private_repos", + "private_gists", + "total_private_repos", + "two_factor_authentication" + ] + }, + { + "title": "Public User", + "description": "Public User", + "type": "object", + "properties": { + "login": { + "type": "string", + "instillUIOrder": 0 + }, + "id": { + "type": "integer", + "format": "int64", + "instillUIOrder": 1 + }, + "user_view_type": { + "type": "string", + "instillUIOrder": 2 + }, + "node_id": { + "type": "string", + "instillUIOrder": 3 + }, + "avatar_url": { + "type": "string", + "format": "uri", + "instillUIOrder": 4 + }, + "gravatar_id": { + "type": [ + "string", + "null" + ], + "instillUIOrder": 5 + }, + "url": { + "type": "string", + "format": "uri", + "instillUIOrder": 6 + }, + "html_url": { + "type": "string", + "format": "uri", + "instillUIOrder": 7 + }, + "followers_url": { + "type": "string", + "format": "uri", + "instillUIOrder": 8 + }, + "following_url": { + "type": "string", + "instillUIOrder": 9 + }, + "gists_url": { + "type": "string", + "instillUIOrder": 10 + }, + "starred_url": { + "type": "string", + "instillUIOrder": 11 + }, + "subscriptions_url": { + "type": "string", + "format": "uri", + "instillUIOrder": 12 + }, + "organizations_url": { + "type": "string", + "format": "uri", + "instillUIOrder": 13 + }, + "repos_url": { + "type": "string", + "format": "uri", + "instillUIOrder": 14 + }, + "events_url": { + "type": "string", + "instillUIOrder": 15 + }, + "received_events_url": { + "type": "string", + "format": "uri", + "instillUIOrder": 16 + }, + "type": { + "type": "string", + "instillUIOrder": 17 + }, + "site_admin": { + "type": "boolean", + "instillUIOrder": 18 + }, + "name": { + "type": [ + "string", + "null" + ], + "instillUIOrder": 19 + }, + "company": { + "type": [ + "string", + "null" + ], + "instillUIOrder": 20 + }, + "blog": { + "type": [ + "string", + "null" + ], + "instillUIOrder": 21 + }, + "location": { + "type": [ + "string", + "null" + ], + "instillUIOrder": 22 + }, + "email": { + "type": [ + "string", + "null" + ], + "format": "email", + "instillUIOrder": 23 + }, + "notification_email": { + "type": [ + "string", + "null" + ], + "format": "email", + "instillUIOrder": 24 + }, + "hireable": { + "type": [ + "boolean", + "null" + ], + "instillUIOrder": 25 + }, + "bio": { + "type": [ + "string", + "null" + ], + "instillUIOrder": 26 + }, + "twitter_username": { + "type": [ + "string", + "null" + ], + "instillUIOrder": 27 + }, + "public_repos": { + "type": "integer", + "instillUIOrder": 28 + }, + "public_gists": { + "type": "integer", + "instillUIOrder": 29 + }, + "followers": { + "type": "integer", + "instillUIOrder": 30 + }, + "following": { + "type": "integer", + "instillUIOrder": 31 + }, + "created_at": { + "type": "string", + "format": "date-time", + "instillUIOrder": 32 + }, + "updated_at": { + "type": "string", + "format": "date-time", + "instillUIOrder": 33 + }, + "plan": { + "type": "object", + "properties": { + "collaborators": { + "type": "integer", + "instillUIOrder": 0 + }, + "name": { + "type": "string", + "instillUIOrder": 1 + }, + "space": { + "type": "integer", + "instillUIOrder": 2 + }, + "private_repos": { + "type": "integer", + "instillUIOrder": 3 + } + }, + "required": [ + "collaborators", + "name", + "space", + "private_repos" + ], + "instillUIOrder": 34 + }, + "private_gists": { + "type": "integer", + "examples": [ + 1 + ], + "instillUIOrder": 35 + }, + "total_private_repos": { + "type": "integer", + "examples": [ + 2 + ], + "instillUIOrder": 36 + }, + "owned_private_repos": { + "type": "integer", + "examples": [ + 2 + ], + "instillUIOrder": 37 + }, + "disk_usage": { + "type": "integer", + "examples": [ + 1 + ], + "instillUIOrder": 38 + }, + "collaborators": { + "type": "integer", + "examples": [ + 3 + ], + "instillUIOrder": 39 + } + }, + "required": [ + "avatar_url", + "events_url", + "followers_url", + "following_url", + "gists_url", + "gravatar_id", + "html_url", + "id", + "node_id", + "login", + "organizations_url", + "received_events_url", + "repos_url", + "site_admin", + "starred_url", + "subscriptions_url", + "type", + "url", + "bio", + "blog", + "company", + "email", + "followers", + "following", + "hireable", + "location", + "name", + "public_gists", + "public_repos", + "created_at", + "updated_at" + ], + "additionalProperties": false + } + ] + } + }, + "TASK_CREATE_ISSUE": { + "description": "Create an issue.", + "instillShortDescription": "Create an issue.", + "input": { + "description": "Please input the repository name and owner.", + "instillEditOnNodeFields": [ + "owner", + "repository", + "title", + "body" + ], + "properties": { + "$ref": "#/$defs/repositoryInfo", + "title": { + "$ref": "#/$defs/issue/properties/title", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "instillUIOrder": 0 + }, + "body": { + "$ref": "#/$defs/issue/properties/body", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "instillUIOrder": 1 + }, + "assignees": { + "$ref": "#/$defs/issue/properties/assignees", + "instillAcceptFormats": [ + "array:string" + ], + "instillUpstreamTypes": [ + "value", + "reference" + ], + "items": { + "instillUIMultiline": false, + "type": "string" + }, + "title": "Assignees", + "instillUIOrder": 2 + }, + "labels": { + "$ref": "#/$defs/issue/properties/labels", + "instillAcceptFormats": [ + "array:string" + ], + "instillUpstreamTypes": [ + "value", + "reference" + ], + "items": { + "instillUIMultiline": false, + "type": "string" + }, + "title": "Labels", + "instillUIOrder": 3 + } + }, + "required": [ + "owner", + "repository", + "title", + "body" + ], + "title": "Input", + "instillFormat": "object", + "type": "object" + }, + "output": { + "description": "The created issue.", + "properties": { + "$ref": "#/$defs/issue/properties" + }, + "required": [], + "title": "Output", + "instillFormat": "object", + "type": "object" + } + }, + "TASK_CREATE_REVIEW_COMMENT": { + "instillShortDescription": "Create a review comment in pull request.", + "description": "Create a review comment in a pull request. The comment can be a general comment or a review comment. The comment can be on a specific line or on the PR as a whole.", + "input": { + "description": "Please input the repository name and owner, and the PR number.", + "instillEditOnNodeFields": [ + "owner", + "repository", + "pr-number" + ], + "properties": { + "$ref": "#/$defs/repositoryInfo", + "pr-number": { + "title": "PR Number", + "description": "Number of the PR.", + "instillFormat": "integer", + "instillAcceptFormats": [ + "integer" + ], + "instillUpstreamTypes": [ + "value", + "reference" + ], + "type": "integer", + "instillUIOrder": 0 + }, + "comment": { + "title": "Comment", + "description": "The comment to be added.", + "instillFormat": "object", + "type": "object", + "instillUIOrder": 1, + "properties": { + "commit-id": { + "$ref": "#/$defs/reviewComment/properties/commit-id", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference" + ], + "instillUIOrder": 0 + }, + "body": { + "$ref": "#/$defs/reviewComment/properties/body", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "instillUIOrder": 1 + }, + "path": { + "$ref": "#/$defs/reviewComment/properties/path", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "instillUIOrder": 2 + }, + "start-line": { + "$ref": "#/$defs/reviewComment/properties/start-line", + "instillAcceptFormats": [ + "integer" + ], + "instillUpstreamTypes": [ + "value", + "reference" + ], + "instillUIOrder": 3 + }, + "line": { + "$ref": "#/$defs/reviewComment/properties/line", + "instillAcceptFormats": [ + "integer" + ], + "instillUpstreamTypes": [ + "value", + "reference" + ], + "instillUIOrder": 4 + }, + "start-side": { + "$ref": "#/$defs/reviewComment/properties/start-side", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value" + ], + "instillUIOrder": 5 + }, + "side": { + "$ref": "#/$defs/reviewComment/properties/side", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value" + ], + "instillUIOrder": 6 + }, + "subject-type": { + "$ref": "#/$defs/reviewComment/properties/subject-type", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value" + ], + "instillUIOrder": 7 + } + }, + "required": [ + "body", + "path", + "commit-id" + ] + } + }, + "required": [ + "owner", + "repository", + "pr-number", + "comment" + ], + "title": "Input", + "instillFormat": "object", + "type": "object" + }, + "output": { + "description": "The created comment.", + "properties": { + "$ref": "#/$defs/reviewComment/properties" + }, + "required": [], + "title": "Output", + "instillFormat": "object", + "type": "object" + } + }, + "TASK_CREATE_WEBHOOK": { + "description": "Create a webhook for a repository.", + "instillShortDescription": "Create a webhook for a repository.", + "input": { + "description": "Please input the repository name and owner.", + "instillEditOnNodeFields": [ + "owner", + "repository", + "hook-url", + "events" + ], + "properties": { + "$ref": "#/$defs/repositoryInfo", + "hook-url": { + "title": "Webhook URL", + "description": "URL to send the payload to.", + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string", + "instillUIOrder": 0 + }, + "events": { + "title": "Events", + "description": "Events to trigger the webhook. Please see the github document for more information.", + "instillFormat": "array:string", + "instillAcceptFormats": [ + "array" + ], + "instillUpstreamTypes": [ + "reference" + ], + "type": "array", + "instillUIOrder": 1, + "items": { + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + } + }, + "active": { + "title": "Active", + "default": false, + "description": "Whether the webhook is active. Default is false.", + "instillFormat": "boolean", + "instillAcceptFormats": [ + "boolean" + ], + "instillUpstreamTypes": [ + "value" + ], + "type": "boolean", + "instillUIOrder": 2 + }, + "content-type": { + "default": "json", + "title": "Content Type", + "description": "Content type of the webhook, can be one of: json, form. Default is json.", + "enum": [ + "json", + "form" + ], + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value" + ], + "type": "string", + "instillUIOrder": 3 + }, + "hook-secret": { + "title": "Hook Secret", + "description": "If provided, the secret will be used as the key to generate the HMAC hex digest value for delivery signature headers. (see the document).", + "instillSecret": true, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "reference" + ], + "type": "string", + "instillUIOrder": 4 + } + }, + "required": [ + "owner", + "repository", + "hook-url", + "events" + ], + "title": "Input", + "instillFormat": "object", + "type": "object" + }, + "output": { + "description": "The created webhook.", + "properties": { + "id": { + "description": "ID of the webhook.", + "title": "Webhook ID", + "instillFormat": "integer", + "type": "integer", + "instillUIOrder": 0 + }, + "url": { + "description": "URL of the webhook.", + "title": "Webhook URL", + "instillFormat": "string", + "type": "string", + "instillUIOrder": 1 + }, + "ping-url": { + "description": "URL to ping the webhook.", + "title": "Ping URL", + "instillFormat": "string", + "type": "string", + "instillUIOrder": 2 + }, + "test-url": { + "description": "URL to test the webhook.", + "title": "Test URL", + "instillFormat": "string", + "type": "string", + "instillUIOrder": 3 + }, + "config": { + "description": "Configuration of the webhook.", + "title": "Config", + "instillFormat": "object", + "type": "object", + "instillUIOrder": 4, + "properties": { + "url": { + "description": "URL of the webhook.", + "title": "Webhook URL", + "instillFormat": "string", + "type": "string", + "instillUIOrder": 0 + }, + "content-type": { + "description": "Content type of the webhook.", + "title": "Content Type", + "instillFormat": "string", + "type": "string", + "instillUIOrder": 1 + }, + "insecure-ssl": { + "description": "Whether the webhook is insecure.", + "title": "Insecure SSL", + "instillFormat": "string", + "type": "string", + "instillUIOrder": 2 + } + }, + "required": [] + } + }, + "required": [], + "title": "Output", + "instillFormat": "object", + "type": "object" + } + }, + "TASK_GET_COMMIT": { + "instillShortDescription": "Get a commit from a repository, given the commit SHA", + "input": { + "description": "Please input the repository name and owner, and the commit SHA.", + "instillEditOnNodeFields": [ + "owner", + "repository", + "sha" + ], + "properties": { + "$ref": "#/$defs/repositoryInfo", + "sha": { + "$ref": "#/$defs/commit/properties/sha", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference" + ], + "instillUIOrder": 0 + } + }, + "required": [ + "owner", + "repository", + "sha" + ], + "title": "Input", + "instillFormat": "object", + "type": "object" + }, + "output": { + "description": "The specific commit in GitHub repository.", + "properties": { + "$ref": "#/$defs/commit/properties" + }, + "required": [], + "title": "Output", + "instillFormat": "object", + "type": "object" + } + }, + "TASK_GET_ISSUE": { + "description": "Get an issue. This can be a pull request or a general issue, and you can tell by the `is-pull-request` field.", + "instillShortDescription": "Get an issue.", + "input": { + "description": "Please input the repository name and owner.", + "instillEditOnNodeFields": [ + "owner", + "repository", + "issue-number" + ], + "properties": { + "$ref": "#/$defs/repositoryInfo", + "issue-number": { + "default": 0, + "title": "Issue Number", + "description": "Number of the issue.", + "instillFormat": "integer", + "instillAcceptFormats": [ + "integer" + ], + "instillUpstreamTypes": [ + "value", + "reference" + ], + "type": "integer", + "instillUIOrder": 0 + } + }, + "required": [ + "owner", + "repository", + "issue-number" + ], + "title": "Input", + "instillFormat": "object", + "type": "object" + }, + "output": { + "description": "The specific issue in GitHub repository.", + "properties": { + "$ref": "#/$defs/issue/properties" + }, + "required": [], + "title": "Output", + "instillFormat": "object", + "type": "object" + } + }, + "TASK_GET_ORGANIZATION": { + "description": "Get an organization by name or ID.", + "instillShortDescription": "Get an organization by name or ID.", + "input": { + "description": "Please input either the organization name or organization ID.", + "instillEditOnNodeFields": [ + "name", + "id" + ], + "properties": { + "name": { + "title": "Name", + "description": "The GitHub organization name.", + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference" + ], + "type": "string", + "instillUIOrder": 0 + }, + "id": { + "title": "ID", + "description": "The GitHub organization ID. If both `name` and `id` are provided, `id` takes precedence.", + "instillFormat": "integer", + "instillAcceptFormats": [ + "integer" + ], + "instillUpstreamTypes": [ + "value", + "reference" + ], + "type": "integer", + "instillUIOrder": 1 + } + }, + "required": [], + "title": "Input", + "instillFormat": "object", + "type": "object" + }, + "output": { + "description": "The GitHub organization information.", + "properties": { + "organization": { + "$ref": "#/$defs/organization" + } + }, + "required": [], + "title": "Output", + "instillFormat": "object", + "type": "object" + } + }, + "TASK_GET_PULL_REQUEST": { + "instillShortDescription": "Get a pull request from a repository, given the PR number. This will default to the latest PR if no PR number is provided.", + "input": { + "description": "Please input the repository name and owner, and the PR number.", + "instillEditOnNodeFields": [ + "owner", + "repository", + "pr-number" + ], + "properties": { + "$ref": "#/$defs/repositoryInfo", + "pr-number": { + "default": 0, + "title": "PR Number", + "description": "Number of the PR. `0` for the latest PR.", + "instillFormat": "integer", + "instillAcceptFormats": [ + "integer" + ], + "instillUpstreamTypes": [ + "value", + "reference" + ], + "type": "integer", + "instillUIOrder": 4 + } + }, + "required": [ + "owner", + "repository" + ], + "title": "Input", + "instillFormat": "object", + "type": "object" + }, + "output": { + "description": "The specific PR in GitHub repository.", + "$ref": "#/$defs/pullRequest", + "required": [], + "title": "Output", + "instillFormat": "object", + "type": "object" + } + }, + "TASK_GET_USER": { + "description": "Get a user by name or ID.", + "instillShortDescription": "Get a user by name or ID.", + "input": { + "description": "Please input either the username or user ID.", + "instillEditOnNodeFields": [ + "name", + "id" + ], + "properties": { + "name": { + "title": "Name", + "description": "The GitHub username of the user.", + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference" + ], + "type": "string", + "instillUIOrder": 0 + }, + "id": { + "title": "ID", + "description": "The GitHub user ID. If both `name` and `id` are provided, `id` takes precedence.", + "instillFormat": "integer", + "instillAcceptFormats": [ + "integer" + ], + "instillUpstreamTypes": [ + "value", + "reference" + ], + "type": "integer", + "instillUIOrder": 1 + } + }, + "required": [], + "title": "Input", + "instillFormat": "object", + "type": "object" + }, + "output": { + "description": "The GitHub user information.", + "properties": { + "user": { + "$ref": "#/$defs/user" + } + }, + "required": [], + "title": "Output", + "instillFormat": "object", + "type": "object" + } + }, + "TASK_LIST_ISSUES": { + "description": "Get the list of all issues in a repository,This can be a pull request or a general issue, and you can tell by the `is-pull-request` field.", + "instillShortDescription": "Get the list of all issues in a repository", + "input": { + "description": "Please input the repository name and owner.", + "instillEditOnNodeFields": [ + "owner", + "repository" + ], + "allOf": [ + { + "$ref": "#/$defs/listOptions" + }, + { + "$ref": "#/$defs/repositoryInfo" + } + ], + "properties": { + "state": { + "default": "open", + "title": "State", + "description": "State of the issues, can be one of: open, closed, all. Default is open.", + "enum": [ + "open", + "closed", + "all" + ], + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value" + ], + "type": "string", + "instillUIOrder": 0 + }, + "sort": { + "default": "created", + "title": "Sort", + "description": "Sort the issues by created, updated, popularity, or long-running. Default is created.", + "enum": [ + "created", + "updated", + "popularity", + "long-running" + ], + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value" + ], + "type": "string", + "instillUIOrder": 1 + }, + "direction": { + "default": "desc", + "title": "Direction", + "description": "Direction of the sort, can be one of: asc, desc. Default is desc.", + "enum": [ + "asc", + "desc" + ], + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value" + ], + "type": "string", + "instillUIOrder": 2 + }, + "since": { + "default": "2020-06-01", + "title": "Since", + "description": "Date (in `YYYY-MM-DD` format) from which issues will start to be fetched. The date will be in the UTC timezone.", + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string", + "instillUIOrder": 3 + }, + "no-pull-request": { + "title": "No Pull Request", + "description": "Whether to `not` include pull requests in the response. Since issue and PR use the same indexing system in GitHub, the API returns all relevant objects (issues and PR). Default is false.", + "instillFormat": "boolean", + "instillAcceptFormats": [ + "boolean" + ], + "instillUpstreamTypes": [ + "value" + ], + "type": "boolean", + "instillUIOrder": 4 + } + }, + "title": "Input", + "instillFormat": "object", + "type": "object" + }, + "output": { + "description": "All issues in GitHub repository.", + "properties": { + "issues": { + "description": "An array of issues.", + "title": "Issues", + "instillFormat": "array:object", + "type": "array", + "items": { + "$ref": "#/$defs/issue", + "required": [], + "description": "An issue in GitHub." + }, + "instillUIOrder": 0 + }, + "response": { + "$ref": "#/$defs/response/properties", + "instillUIOrder": 1 + } + }, + "required": [ + "issues" + ], + "title": "Output", + "instillFormat": "object", + "type": "object" + } + }, + "TASK_LIST_PULL_REQUESTS": { + "instillShortDescription": "Get the list of all pull requests in a repository", + "description": "Get the list of all pull requests in a repository. Detailed information about each commit in a PR is omitted, please use the `Get Commit` task or the `Get Pull Request` task to get the details of a commit.", + "input": { + "description": "Please input the repository name and owner.", + "instillEditOnNodeFields": [ + "owner", + "repository" + ], + "allOf": [ + { + "$ref": "#/$defs/repositoryInfo" + }, + { + "$ref": "#/$defs/listOptions" + } + ], + "properties": { + "allOf": [ + { + "$ref": "#/$defs/listOptions" + } + ], + "state": { + "default": "open", + "title": "State", + "description": "State of the PRs, including open, closed, all. Default is open.", + "enum": [ + "open", + "closed", + "all" + ], + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string", + "instillUIOrder": 0 + }, + "sort": { + "default": "created", + "title": "Sort", + "description": "Sort the PRs by created, updated, popularity, or long-running. Default is created.", + "enum": [ + "created", + "updated", + "popularity", + "long-running" + ], + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string", + "instillUIOrder": 1 + }, + "direction": { + "default": "desc", + "title": "Direction", + "description": "Direction of the sort, including asc or desc. Default is desc.", + "enum": [ + "asc", + "desc" + ], + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string", + "instillUIOrder": 2 + } + }, + "required": [ + "owner", + "repository" + ], + "title": "Input", + "instillFormat": "object", + "type": "object" + }, + "output": { + "description": "All PRs in GitHub repository.", + "properties": { + "pull-requests": { + "description": "An array of PRs.", + "title": "Pull Requests", + "instillFormat": "array:object", + "type": "array", + "items": { + "$ref": "#/$defs/pullRequest", + "required": [], + "description": "A pull request in GitHub." + }, + "instillUIOrder": 0 + }, + "response": { + "$ref": "#/$defs/response/properties", + "instillUIOrder": 1 + } + }, + "required": [ + "pull-requests" + ], + "title": "Output", + "instillFormat": "object", + "type": "object" + } + }, + "TASK_LIST_REVIEW_COMMENTS": { + "instillShortDescription": "Get the review comments in a pull request", + "description": "Get the review comments in a pull request. The comments can be on a specific line or on the PR as a whole.", + "input": { + "description": "Please input the repository name and owner, and the PR number. Set PR number as`0` to get all comments on all PRs in the repository.", + "instillEditOnNodeFields": [ + "owner", + "repository", + "pr-number" + ], + "allOf": [ + { + "$ref": "#/$defs/repositoryInfo" + }, + { + "$ref": "#/$defs/listOptions" + } + ], + "properties": { + "pr-number": { + "default": 0, + "title": "PR Number", + "description": "Number of the PR. Default is `0`, which retrieves all comments on all PRs in the repository.", + "instillFormat": "integer", + "instillAcceptFormats": [ + "integer" + ], + "instillUpstreamTypes": [ + "value", + "reference" + ], + "type": "integer", + "instillUIOrder": 0 + }, + "sort": { + "default": "created", + "title": "Sort", + "description": "Sort the comments by created, updated. Default is created.", + "enum": [ + "created", + "updated" + ], + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value" + ], + "type": "string", + "instillUIOrder": 1 + }, + "direction": { + "default": "desc", + "title": "Direction", + "description": "Direction of the sort, including asc or desc. Default is desc.", + "enum": [ + "asc", + "desc" + ], + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value" + ], + "type": "string", + "instillUIOrder": 2 + }, + "since": { + "default": "2020-06-01", + "title": "Since", + "description": "Date (in `YYYY-MM-DD` format) from which comments will start to be fetched. The date will be in the UTC timezone.", + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string", + "instillUIOrder": 3 + } + }, + "required": [ + "owner", + "repository" + ], + "title": "Input", + "instillFormat": "object", + "type": "object" + }, + "output": { + "description": "Comments in the PR.", + "properties": { + "comments": { + "description": "An array of comments.", + "title": "Comments", + "instillFormat": "array:object", + "type": "array", + "items": { + "$ref": "#/$defs/reviewComment", + "required": [], + "description": "Comments in the PR." + }, + "instillUIOrder": 0 + }, + "response": { + "$ref": "#/$defs/response/properties", + "instillUIOrder": 1 + } + }, + "required": [ + "comments" + ], + "title": "Output", + "instillFormat": "object", + "type": "object" + } + } +} diff --git a/pkg/component/application/github/v0/config/tasks.yaml b/pkg/component/application/github/v0/config/tasks.yaml index 9cd4f996b..75351e952 100644 --- a/pkg/component/application/github/v0/config/tasks.yaml +++ b/pkg/component/application/github/v0/config/tasks.yaml @@ -1,4 +1,455 @@ $defs: + commit: + description: A commit object. + properties: + sha: + description: SHA of the commit. + uiOrder: 1 + title: Commit SHA + format: string + message: + description: Message of the commit. + uiOrder: 2 + title: Commit message + format: string + stats: + uiOrder: 3 + $ref: "#/$defs/commitStats" + required: [] + files: + description: Files in the commit. + uiOrder: 4 + title: Files + items: + $ref: "#/$defs/commitFile" + required: [] + description: A file in the commit. + format: array + required: [] + title: Commit + format: object + commitStats: + description: Stats of changes. + uiOrder: 1 + properties: + additions: + description: Number of additions in the commit. + uiOrder: 1 + title: Additions + format: integer + deletions: + description: Number of deletions in the commit. + uiOrder: 2 + title: Deletions + format: integer + changes: + description: Total number of changes in the commit. + uiOrder: 3 + title: Total changes + format: integer + required: [] + title: Commit stats + format: object + commitFile: + description: A commit file object. + properties: + filename: + description: Name of the file. + uiOrder: 1 + title: File name + format: string + $ref: "#/$defs/commitStats/properties" + patch: + description: Patch of the file. + uiOrder: 3 + title: Patch + format: string + required: [] + title: Commit File + format: object + issue: + description: An issue object. + properties: + number: + description: Number of the issue. + uiOrder: 2 + title: Issue Number + format: integer + state: + description: State of the issue. + uiOrder: 3 + title: Issue state + format: string + title: + description: Title of the issue. + uiOrder: 4 + title: Issue title + format: string + body: + description: Body of the issue. + uiOrder: 5 + title: Issue body + format: string + assignee: + description: Assignee of the issue. + uiOrder: 6 + title: Assignee + format: string + assignees: + description: Assignees of the issue. + uiOrder: 7 + title: Assignees + items: + format: string + format: array + labels: + description: Labels of the issue. + uiOrder: 8 + title: Labels + items: + format: string + format: array + is-pull-request: + description: Whether the issue is a pull request. + uiOrder: 9 + title: Is Pull Request + format: boolean + title: Issue + format: object + organization: + description: Organization Full + properties: + login: + format: string # type: string + examples: [github] + uiOrder: 0 + id: + format: integer # type: integer + examples: [1] + uiOrder: 1 + node-id: + format: string # type: string + examples: [MDEyOk9yZ2FuaXphdGlvbjE=] + uiOrder: 2 + url: + format: string # type: string, format: uri + examples: ["https://api.github.com/orgs/github"] + uiOrder: 3 + repos-url: + format: string # type: string, format: uri + examples: ["https://api.github.com/orgs/github/repos"] + uiOrder: 4 + events-url: + format: string # type: string, format: uri + examples: ["https://api.github.com/orgs/github/events"] + uiOrder: 5 + hooks-url: + format: string # type: string + examples: ["https://api.github.com/orgs/github/hooks"] + uiOrder: 6 + issues-url: + format: string # type: string + examples: ["https://api.github.com/orgs/github/issues"] + uiOrder: 7 + members-url: + format: string # type: string + examples: ["https://api.github.com/orgs/github/members{/member}"] + uiOrder: 8 + public-members-url: + format: string # type: string + examples: ["https://api.github.com/orgs/github/public_members{/member}"] + uiOrder: 9 + avatar-url: + format: string # type: string + examples: ["https://github.com/images/error/octocat_happy.gif"] + uiOrder: 10 + description: + format: string # type: [string, null] + examples: [A great organization] + uiOrder: 11 + name: + format: string # type: string + examples: [github] + uiOrder: 12 + company: + format: string # type: string + examples: [GitHub] + uiOrder: 13 + blog: + format: string # type: string, format: uri + examples: ["https://github.com/blog"] + uiOrder: 14 + location: + format: string # type: string + examples: [San Francisco] + uiOrder: 15 + email: + format: string # type: string, format: email + examples: [octocat@github.com] + uiOrder: 16 + twitter-username: + format: string # type: [string, null] + examples: [github] + uiOrder: 17 + is-verified: + format: boolean # type: boolean + examples: [true] + uiOrder: 18 + has-organization-projects: + format: boolean # type: boolean + examples: [true] + uiOrder: 19 + has-repository-projects: + format: boolean # type: boolean + examples: [true] + uiOrder: 20 + public-repos: + format: integer # type: integer + examples: [2] + uiOrder: 21 + public-gists: + format: integer # type: integer + examples: [1] + uiOrder: 22 + followers: + format: integer # type: integer + examples: [20] + uiOrder: 23 + following: + format: integer # type: integer + examples: [0] + uiOrder: 24 + html-url: + format: string # type: string, format: uri + examples: ["https://github.com/octocat"] + uiOrder: 25 + type: + format: string # type: string + examples: [Organization] + uiOrder: 26 + total-private-repos: + format: integer # type: integer + examples: [100] + uiOrder: 27 + owned-private-repos: + format: integer # type: integer + examples: [100] + uiOrder: 28 + private-gists: + format: integer # type: [integer, null] + examples: [81] + uiOrder: 29 + disk-usage: + format: integer # type: [integer, null] + examples: [10000] + uiOrder: 30 + collaborators: + format: integer # type: [integer, null] + description: > + The number of collaborators on private repositories. + This field may be null if the number of private repositories is over 50,000. + examples: [8] + uiOrder: 31 + billing-email: + format: string # type: [string, null], format: email + examples: [org@example.com] + uiOrder: 32 + plan: + format: object # type: object + properties: + name: + format: string # type: string + uiOrder: 0 + space: + format: integer # type: integer + uiOrder: 1 + private-repos: + format: integer # type: integer + uiOrder: 2 + filled-seats: + format: integer # type: integer + uiOrder: 3 + seats: + format: integer # type: integer + uiOrder: 4 + required: + - name + - space + - private-repos + uiOrder: 33 + default-repository-permission: + format: string # type: [string, null] + uiOrder: 34 + members-can-create-repositories: + format: boolean # type: [boolean, null] + examples: [true] + uiOrder: 35 + two-factor-requirement-enabled: + format: boolean # type: [boolean, null] + examples: [true] + uiOrder: 36 + members-allowed-repository-creation-type: + format: string # type: string + examples: [all] + uiOrder: 37 + members-can-create-public-repositories: + format: boolean # type: boolean + examples: [true] + uiOrder: 38 + members-can-create-private-repositories: + format: boolean # type: boolean + examples: [true] + uiOrder: 39 + members-can-create-internal-repositories: + format: boolean # type: boolean + examples: [true] + uiOrder: 40 + members-can-create-pages: + format: boolean # type: boolean + examples: [true] + uiOrder: 41 + members-can-create-public-pages: + format: boolean # type: boolean + examples: [true] + uiOrder: 42 + members-can-create-private-pages: + format: boolean # type: boolean + examples: [true] + uiOrder: 43 + members-can-fork-private-repositories: + format: boolean # type: [boolean, null] + examples: [false] + uiOrder: 44 + web-commit-signoff-required: + format: boolean # type: boolean + examples: [false] + uiOrder: 45 + advanced-security-enabled-for-new-repositories: + format: boolean # type: boolean + description: > + **Endpoint closing down notice.** Please use [code security configurations](https://docs.github.com/rest/code-security/configurations) instead. + Whether GitHub Advanced Security is enabled for new repositories and repositories transferred to this organization. + This field is only visible to organization owners or members of a team with the security manager role. + examples: [false] + deprecated: true + uiOrder: 46 + dependabot-alerts-enabled-for-new-repositories: + format: boolean # type: boolean + description: > + **Endpoint closing down notice.** Please use [code security configurations](https://docs.github.com/rest/code-security/configurations) instead. + Whether Dependabot alerts are automatically enabled for new repositories and repositories transferred to this organization. + This field is only visible to organization owners or members of a team with the security manager role. + examples: [false] + deprecated: true + uiOrder: 47 + dependabot-security-updates-enabled-for-new-repositories: + format: boolean # type: boolean + description: > + **Endpoint closing down notice.** Please use [code security configurations](https://docs.github.com/rest/code-security/configurations) instead. + Whether Dependabot security updates are automatically enabled for new repositories and repositories transferred to this organization. + This field is only visible to organization owners or members of a team with the security manager role. + examples: [false] + deprecated: true + uiOrder: 48 + dependency-graph-enabled-for-new-repositories: + format: boolean # type: boolean + description: > + **Endpoint closing down notice.** Please use [code security configurations](https://docs.github.com/rest/code-security/configurations) instead. + Whether dependency graph is automatically enabled for new repositories and repositories transferred to this organization. + This field is only visible to organization owners or members of a team with the security manager role. + examples: [false] + deprecated: true + uiOrder: 49 + secret-scanning-enabled-for-new-repositories: + format: boolean # type: boolean + description: > + **Endpoint closing down notice.** Please use [code security configurations](https://docs.github.com/rest/code-security/configurations) instead. + Whether secret scanning is automatically enabled for new repositories and repositories transferred to this organization. + This field is only visible to organization owners or members of a team with the security manager role. + examples: [false] + deprecated: true + uiOrder: 50 + secret-scanning-push-protection-enabled-for-new-repositories: + format: boolean # type: boolean + description: > + **Endpoint closing down notice.** Please use [code security configurations](https://docs.github.com/rest/code-security/configurations) instead. + Whether secret scanning push protection is automatically enabled for new repositories and repositories transferred to this organization. + This field is only visible to organization owners or members of a team with the security manager role. + examples: [false] + deprecated: true + uiOrder: 51 + secret-scanning-push-protection-custom-link-enabled: + format: boolean # type: boolean + description: Whether a custom link is shown to contributors who are blocked from pushing a secret by push protection. + examples: [false] + uiOrder: 52 + secret-scanning-push-protection-custom-link: + format: string # type: [string, null] + description: An optional URL string to display to contributors who are blocked from pushing a secret. + examples: ["https://github.com/test-org/test-repo/blob/main/README.md"] + uiOrder: 53 + created-at: + format: string # type: string, format: date-time + examples: ["2008-01-14T04:33:35Z"] + uiOrder: 54 + updated-at: + format: string # type: string, format: date-time + uiOrder: 55 + archived-at: + format: string # type: [string, null], format: date-time + uiOrder: 56 + deploy-keys-enabled-for-repositories: + format: boolean # type: boolean + description: Controls whether or not deploy keys may be added and used for repositories in the organization. + examples: [false] + uiOrder: 57 + required: + - login + - url + - id + - node-id + - repos-url + - events-url + - hooks-url + - issues-url + - members-url + - public-members-url + - avatar-url + - description + - html-url + - has-organization-projects + - has-repository-projects + - public-repos + - public-gists + - followers + - following + - type + - created-at + - updated-at + - archived-at + title: Organization Full + format: object # type: object + pageOptions: + title: Page Options + description: Options for listing pages. + required: [] + page: + default: 1 + description: Page number of the results to fetch. Default is 1. + uiOrder: 100 + title: Page + acceptFormats: + - integer + format: integer + per-page: + default: 30 + description: Number of results to fetch per page. Default is 30. + uiOrder: 101 + title: Per Page + acceptFormats: + - integer + format: integer + format: object pullRequest: description: A pull request object. properties: @@ -62,81 +513,13 @@ $defs: uiOrder: 13 title: Commits items: - $ref: '#/$defs/commit' + $ref: "#/$defs/commit" required: [] description: A commit in the PR. format: array required: [] title: Pull Request format: object - commit: - description: A commit object. - properties: - sha: - description: SHA of the commit. - uiOrder: 1 - title: Commit SHA - format: string - message: - description: Message of the commit. - uiOrder: 2 - title: Commit message - format: string - stats: - uiOrder: 3 - $ref: '#/$defs/commitStats' - required: [] - files: - description: Files in the commit. - uiOrder: 4 - title: Files - items: - $ref: '#/$defs/commitFile' - required: [] - description: A file in the commit. - format: array - required: [] - title: Commit - format: object - commitStats: - description: Stats of changes. - uiOrder: 1 - properties: - additions: - description: Number of additions in the commit. - uiOrder: 1 - title: Additions - format: integer - deletions: - description: Number of deletions in the commit. - uiOrder: 2 - title: Deletions - format: integer - changes: - description: Total number of changes in the commit. - uiOrder: 3 - title: Total changes - format: integer - required: [] - title: Commit stats - format: object - commitFile: - description: A commit file object. - properties: - filename: - description: Name of the file. - uiOrder: 1 - title: File name - format: string - $ref: '#/$defs/commitStats/properties' - patch: - description: Patch of the file. - uiOrder: 3 - title: Patch - format: string - required: [] - title: Commit File - format: object repositoryInfo: owner: description: Owner of the repository. @@ -193,9 +576,9 @@ $defs: title: Comment start line format: integer side: - shortDescription: 'Side of the end line, can be one of: LEFT, RIGHT, side. Default is side.' - description: 'Side of the end line, can be one of: LEFT, RIGHT, side. LEFT is the left side of the diff (deletion), RIGHT is the right side of the - diff (addition), and side is the comment on the PR as a whole. Default is side.' + shortDescription: "Side of the end line, can be one of: LEFT, RIGHT, side. Default is side." + description: "Side of the end line, can be one of: LEFT, RIGHT, side. LEFT is the left side of the diff (deletion), RIGHT is the right side of the\ + \ diff (addition), and side is the comment on the PR as a whole. Default is side." default: side enum: - LEFT @@ -205,9 +588,9 @@ $defs: title: Comment end side format: string start-side: - shortDescription: 'Side of the start line, can be one of: LEFT, RIGHT, side. Default is side.' - description: 'Side of the start line, can be one of: LEFT, RIGHT, side. LEFT is the left side of the diff (deletion), RIGHT is the right side of - the diff (addition), and side is the comment on the PR as a whole. Default is side.' + shortDescription: "Side of the start line, can be one of: LEFT, RIGHT, side. Default is side." + description: "Side of the start line, can be one of: LEFT, RIGHT, side. LEFT is the left side of the diff (deletion), RIGHT is the right side of\ + \ the diff (addition), and side is the comment on the PR as a whole. Default is side." default: side enum: - LEFT @@ -217,7 +600,7 @@ $defs: title: Comment start side format: string subject-type: - description: 'Subject type of the comment, can be one of: line, file. Default is line.' + description: "Subject type of the comment, can be one of: line, file. Default is line." uiOrder: 10 title: Comment type default: line @@ -254,95 +637,433 @@ $defs: format: object title: Review Comment format: object - issue: - description: An issue object. - properties: - number: - description: Number of the issue. - uiOrder: 2 - title: Issue Number - format: integer - state: - description: State of the issue. - uiOrder: 3 - title: Issue state - format: string - title: - description: Title of the issue. - uiOrder: 4 - title: Issue title - format: string - body: - description: Body of the issue. - uiOrder: 5 - title: Issue body - format: string - assignee: - description: Assignee of the issue. - uiOrder: 6 - title: Assignee - format: string - assignees: - description: Assignees of the issue. - uiOrder: 7 - title: Assignees - items: - format: string - format: array - labels: - description: Labels of the issue. - uiOrder: 8 - title: Labels - items: - format: string - format: array - is-pull-request: - description: Whether the issue is a pull request. - uiOrder: 9 - title: Is Pull Request - format: boolean - title: Issue - format: object - pageOptions: - title: Page Options - description: Options for listing pages. - required: [] - page: - default: 1 - description: Page number of the results to fetch. Default is 1. - uiOrder: 100 - title: Page - acceptFormats: - - integer - format: integer - per-page: - default: 30 - description: Number of results to fetch per page. Default is 30. - uiOrder: 101 - title: Per Page - acceptFormats: - - integer - format: integer - format: object + user: + oneOf: + - title: Private User + description: Private User + format: object # type: object + properties: + login: + format: string # type: string + examples: [octocat] + uiOrder: 0 + id: + format: integer # type: integer, format: int64 + examples: [1] + uiOrder: 1 + user_view_type: + format: string # type: string + uiOrder: 2 + node_id: + format: string # type: string + examples: [MDQ6VXNlcjE=] + uiOrder: 3 + avatar_url: + format: string # type: string, format: uri + examples: ["https://github.com/images/error/octocat_happy.gif"] + uiOrder: 4 + gravatar_id: + format: string # type: [string, null] + examples: [41d064eb2195891e12d0413f63227ea7] + uiOrder: 5 + url: + format: string # type: string, format: uri + examples: ["https://api.github.com/users/octocat"] + uiOrder: 6 + html_url: + format: string # type: string, format: uri + examples: ["https://github.com/octocat"] + uiOrder: 7 + followers_url: + format: string # type: string, format: uri + examples: ["https://api.github.com/users/octocat/followers"] + uiOrder: 8 + following_url: + format: string # type: string + examples: ["https://api.github.com/users/octocat/following{/other_user}"] + uiOrder: 9 + gists_url: + format: string # type: string + examples: ["https://api.github.com/users/octocat/gists{/gist_id}"] + uiOrder: 10 + starred_url: + format: string # type: string + examples: ["https://api.github.com/users/octocat/starred{/owner}{/repo}"] + uiOrder: 11 + subscriptions_url: + format: string # type: string, format: uri + examples: ["https://api.github.com/users/octocat/subscriptions"] + uiOrder: 12 + organizations_url: + format: string # type: string, format: uri + examples: ["https://api.github.com/users/octocat/orgs"] + uiOrder: 13 + repos_url: + format: string # type: string, format: uri + examples: ["https://api.github.com/users/octocat/repos"] + uiOrder: 14 + events_url: + format: string # type: string + examples: ["https://api.github.com/users/octocat/events{/privacy}"] + uiOrder: 15 + received_events_url: + format: string # type: string, format: uri + examples: ["https://api.github.com/users/octocat/received_events"] + uiOrder: 16 + type: + format: string # type: string + examples: [User] + uiOrder: 17 + site_admin: + format: boolean # type: boolean + uiOrder: 18 + name: + format: string # type: [string, null] + examples: [monalisa octocat] + uiOrder: 19 + company: + format: string # type: [string, null] + examples: [GitHub] + uiOrder: 20 + blog: + format: string # type: [string, null] + examples: ["https://github.com/blog"] + uiOrder: 21 + location: + format: string # type: [string, null] + examples: [San Francisco] + uiOrder: 22 + email: + format: string # type: [string, null], format: email + examples: [octocat@github.com] + uiOrder: 23 + notification_email: + format: string # type: [string, null], format: email + examples: [octocat@github.com] + uiOrder: 24 + hireable: + format: boolean # type: [boolean, null] + uiOrder: 25 + bio: + format: string # type: [string, null] + examples: [There once was...] + uiOrder: 26 + twitter_username: + format: string # type: [string, null] + examples: [monalisa] + uiOrder: 27 + public_repos: + format: integer # type: integer + examples: [2] + uiOrder: 28 + public_gists: + format: integer # type: integer + examples: [1] + uiOrder: 29 + followers: + format: integer # type: integer + examples: [20] + uiOrder: 30 + following: + format: integer # type: integer + examples: [0] + uiOrder: 31 + created_at: + format: string # type: string, format: date-time + examples: ["2008-01-14T04:33:35Z"] + uiOrder: 32 + updated_at: + format: string # type: string, format: date-time + examples: ["2008-01-14T04:33:35Z"] + uiOrder: 33 + private_gists: + format: integer # type: integer + examples: [81] + uiOrder: 34 + total_private_repos: + format: integer # type: integer + examples: [100] + uiOrder: 35 + owned_private_repos: + format: integer # type: integer + examples: [100] + uiOrder: 36 + disk_usage: + format: integer # type: integer + examples: [10000] + uiOrder: 37 + collaborators: + format: integer # type: integer + examples: [8] + uiOrder: 38 + two_factor_authentication: + format: boolean # type: boolean + examples: [true] + uiOrder: 39 + plan: + format: object # type: object + properties: + collaborators: + format: integer # type: integer + uiOrder: 0 + name: + format: string # type: string + uiOrder: 1 + space: + format: integer # type: integer + uiOrder: 2 + private_repos: + format: integer # type: integer + uiOrder: 3 + required: + - collaborators + - name + - space + - private_repos + uiOrder: 40 + business_plus: + format: boolean # type: boolean + uiOrder: 41 + ldap_dn: + format: string # type: string + uiOrder: 42 + required: + - avatar_url + - events_url + - followers_url + - following_url + - gists_url + - gravatar_id + - html_url + - id + - node_id + - login + - organizations_url + - received_events_url + - repos_url + - site_admin + - starred_url + - subscriptions_url + - type + - url + - bio + - blog + - company + - email + - followers + - following + - hireable + - location + - name + - public_gists + - public_repos + - created_at + - updated_at + - collaborators + - disk_usage + - owned_private_repos + - private_gists + - total_private_repos + - two_factor_authentication + - title: Public User + description: Public User + format: object # type: object + properties: + login: + format: string # type: string + uiOrder: 0 + id: + format: integer # type: integer, format: int64 + uiOrder: 1 + user_view_type: + format: string # type: string + uiOrder: 2 + node_id: + format: string # type: string + uiOrder: 3 + avatar_url: + format: string # type: string, format: uri + uiOrder: 4 + gravatar_id: + format: string # type: [string, null] + uiOrder: 5 + url: + format: string # type: string, format: uri + uiOrder: 6 + html_url: + format: string # type: string, format: uri + uiOrder: 7 + followers_url: + format: string # type: string, format: uri + uiOrder: 8 + following_url: + format: string # type: string + uiOrder: 9 + gists_url: + format: string # type: string + uiOrder: 10 + starred_url: + format: string # type: string + uiOrder: 11 + subscriptions_url: + format: string # type: string, format: uri + uiOrder: 12 + organizations_url: + format: string # type: string, format: uri + uiOrder: 13 + repos_url: + format: string # type: string, format: uri + uiOrder: 14 + events_url: + format: string # type: string + uiOrder: 15 + received_events_url: + format: string # type: string, format: uri + uiOrder: 16 + type: + format: string # type: string + uiOrder: 17 + site_admin: + format: boolean # type: boolean + uiOrder: 18 + name: + format: string # type: [string, null] + uiOrder: 19 + company: + format: string # type: [string, null] + uiOrder: 20 + blog: + format: string # type: [string, null] + uiOrder: 21 + location: + format: string # type: [string, null] + uiOrder: 22 + email: + format: string # type: [string, null], format: email + uiOrder: 23 + notification_email: + format: string # type: [string, null], format: email + uiOrder: 24 + hireable: + format: boolean # type: [boolean, null] + uiOrder: 25 + bio: + format: string # type: [string, null] + uiOrder: 26 + twitter_username: + format: string # type: [string, null] + uiOrder: 27 + public_repos: + format: integer # type: integer + uiOrder: 28 + public_gists: + format: integer # type: integer + uiOrder: 29 + followers: + format: integer # type: integer + uiOrder: 30 + following: + format: integer # type: integer + uiOrder: 31 + created_at: + format: string # type: string, format: date-time + uiOrder: 32 + updated_at: + format: string # type: string, format: date-time + uiOrder: 33 + plan: + format: object # type: object + properties: + collaborators: + format: integer # type: integer + uiOrder: 0 + name: + format: string # type: string + uiOrder: 1 + space: + format: integer # type: integer + uiOrder: 2 + private_repos: + format: integer # type: integer + uiOrder: 3 + required: + - collaborators + - name + - space + - private_repos + uiOrder: 34 + private_gists: + format: integer # type: integer + examples: [1] + uiOrder: 35 + total_private_repos: + format: integer # type: integer + examples: [2] + uiOrder: 36 + owned_private_repos: + format: integer # type: integer + examples: [2] + uiOrder: 37 + disk_usage: + format: integer # type: integer + examples: [1] + uiOrder: 38 + collaborators: + format: integer # type: integer + examples: [3] + uiOrder: 39 + required: + - avatar_url + - events_url + - followers_url + - following_url + - gists_url + - gravatar_id + - html_url + - id + - node_id + - login + - organizations_url + - received_events_url + - repos_url + - site_admin + - starred_url + - subscriptions_url + - type + - url + - bio + - blog + - company + - email + - followers + - following + - hireable + - location + - name + - public_gists + - public_repos + - created_at + - updated_at + additionalProperties: false TASK_CREATE_ISSUE: description: Create an issue. input: description: Please input the repository name and owner. uiOrder: 0 properties: - $ref: '#/$defs/repositoryInfo' + $ref: "#/$defs/repositoryInfo" title: - $ref: '#/$defs/issue/properties/title' + $ref: "#/$defs/issue/properties/title" acceptFormats: - string uiOrder: 3 body: - $ref: '#/$defs/issue/properties/body' + $ref: "#/$defs/issue/properties/body" acceptFormats: - string uiOrder: 4 assignees: - $ref: '#/$defs/issue/properties/assignees' + $ref: "#/$defs/issue/properties/assignees" acceptFormats: - array items: @@ -350,7 +1071,7 @@ TASK_CREATE_ISSUE: title: Assignees uiOrder: 5 labels: - $ref: '#/$defs/issue/properties/labels' + $ref: "#/$defs/issue/properties/labels" acceptFormats: - array items: @@ -368,7 +1089,7 @@ TASK_CREATE_ISSUE: description: The created issue. uiOrder: 0 properties: - $ref: '#/$defs/issue/properties' + $ref: "#/$defs/issue/properties" required: [] title: Output format: object @@ -380,7 +1101,7 @@ TASK_CREATE_REVIEW_COMMENT: description: Please input the repository name and owner, and the PR number. uiOrder: 0 properties: - $ref: '#/$defs/repositoryInfo' + $ref: "#/$defs/repositoryInfo" pr-number: title: PR Number description: Number of the PR. @@ -394,42 +1115,42 @@ TASK_CREATE_REVIEW_COMMENT: uiOrder: 4 properties: commit-id: - $ref: '#/$defs/reviewComments/properties/commit-id' + $ref: "#/$defs/reviewComments/properties/commit-id" acceptFormats: - string uiOrder: 0 body: - $ref: '#/$defs/reviewComments/properties/body' + $ref: "#/$defs/reviewComments/properties/body" acceptFormats: - string uiOrder: 1 path: - $ref: '#/$defs/reviewComments/properties/path' + $ref: "#/$defs/reviewComments/properties/path" acceptFormats: - string uiOrder: 2 start-line: - $ref: '#/$defs/reviewComments/properties/start-line' + $ref: "#/$defs/reviewComments/properties/start-line" acceptFormats: - integer uiOrder: 3 line: - $ref: '#/$defs/reviewComments/properties/line' + $ref: "#/$defs/reviewComments/properties/line" acceptFormats: - integer uiOrder: 4 start-side: - $ref: '#/$defs/reviewComments/properties/start-side' + $ref: "#/$defs/reviewComments/properties/start-side" acceptFormats: - string uiOrder: 5 side: - $ref: '#/$defs/reviewComments/properties/side' + $ref: "#/$defs/reviewComments/properties/side" acceptFormats: - string uiOrder: 6 subject-type: - $ref: '#/$defs/reviewComments/properties/subject-type' + $ref: "#/$defs/reviewComments/properties/subject-type" acceptFormats: - string uiOrder: 7 @@ -449,7 +1170,7 @@ TASK_CREATE_REVIEW_COMMENT: description: The created comment. uiOrder: 0 properties: - $ref: '#/$defs/reviewComments/properties' + $ref: "#/$defs/reviewComments/properties" required: [] title: Output format: object @@ -459,7 +1180,7 @@ TASK_CREATE_WEBHOOK: description: Please input the repository name and owner. uiOrder: 0 properties: - $ref: '#/$defs/repositoryInfo' + $ref: "#/$defs/repositoryInfo" hook-url: title: Webhook URL description: URL to send the payload to. @@ -490,7 +1211,7 @@ TASK_CREATE_WEBHOOK: content-type: default: json title: Content Type - description: 'Content type of the webhook, can be one of: json, form. Default is json.' + description: "Content type of the webhook, can be one of: json, form. Default is json." enum: - json - form @@ -569,9 +1290,9 @@ TASK_GET_COMMIT: description: Please input the repository name and owner, and the commit SHA. uiOrder: 0 properties: - $ref: '#/$defs/repositoryInfo' + $ref: "#/$defs/repositoryInfo" sha: - $ref: '#/$defs/commit/properties/sha' + $ref: "#/$defs/commit/properties/sha" acceptFormats: - string uiOrder: 3 @@ -585,7 +1306,7 @@ TASK_GET_COMMIT: description: The specific commit in GitHub repository. uiOrder: 0 properties: - $ref: '#/$defs/commit/properties' + $ref: "#/$defs/commit/properties" required: [] title: Output format: object @@ -596,7 +1317,7 @@ TASK_GET_ISSUE: description: Please input the repository name and owner. uiOrder: 0 properties: - $ref: '#/$defs/repositoryInfo' + $ref: "#/$defs/repositoryInfo" issue-number: default: 0 title: Issue Number @@ -615,7 +1336,47 @@ TASK_GET_ISSUE: description: The specific issue in GitHub repository. uiOrder: 0 properties: - $ref: '#/$defs/issue/properties' + $ref: "#/$defs/issue/properties" + required: [] + title: Output + format: object +TASK_GET_ORGANIZATION: + description: Get an organization by name or ID. + shortDescription: Get an organization by name or ID. + input: + description: Please input either the organization name or organization ID. + editOnNodeFields: + - name + - id + properties: + name: + title: Name + description: The GitHub organization name. + format: string + acceptFormats: + - string + upstreamTypes: + - value + - reference + uiOrder: 0 + id: + title: ID + description: The GitHub organization ID. If both `name` and `id` are provided, `id` takes precedence. + format: integer + acceptFormats: + - integer + upstreamTypes: + - value + - reference + uiOrder: 1 + required: [] + title: Input + format: object + output: + description: The GitHub organization information. + properties: + organization: + $ref: "#/$defs/organization" required: [] title: Output format: object @@ -625,7 +1386,7 @@ TASK_GET_PULL_REQUEST: description: Please input the repository name and owner, and the PR number. uiOrder: 0 properties: - $ref: '#/$defs/repositoryInfo' + $ref: "#/$defs/repositoryInfo" pr-number: default: 0 title: PR Number @@ -642,7 +1403,47 @@ TASK_GET_PULL_REQUEST: output: description: The specific pr in GitHub repository. uiOrder: 0 - $ref: '#/$defs/pullRequest' + $ref: "#/$defs/pullRequest" + required: [] + title: Output + format: object +TASK_GET_USER: + description: Get a user by name or ID. + shortDescription: Get a user by name or ID. + input: + description: Please input either the username or user ID. + editOnNodeFields: + - name + - id + properties: + name: + title: Name + description: The GitHub username of the user. + format: string + acceptFormats: + - string + upstreamTypes: + - value + - reference + uiOrder: 0 + id: + title: ID + description: The GitHub user ID. If both `name` and `id` are provided, `id` takes precedence. + format: integer + acceptFormats: + - integer + upstreamTypes: + - value + - reference + uiOrder: 1 + required: [] + title: Input + format: object + output: + description: The GitHub user information. + properties: + user: + $ref: "#/$defs/user" required: [] title: Output format: object @@ -653,11 +1454,11 @@ TASK_LIST_ISSUES: description: Please input the repository name and owner. uiOrder: 0 properties: - $ref: '#/$defs/repositoryInfo' + $ref: "#/$defs/repositoryInfo" state: default: open title: State - description: 'State of the issues, can be one of: open, closed, all. Default is open.' + description: "State of the issues, can be one of: open, closed, all. Default is open." enum: - open - closed @@ -682,7 +1483,7 @@ TASK_LIST_ISSUES: direction: default: desc title: Direction - description: 'Direction of the sort, can be one of: asc, desc. Default is desc.' + description: "Direction of the sort, can be one of: asc, desc. Default is desc." enum: - asc - desc @@ -691,7 +1492,7 @@ TASK_LIST_ISSUES: uiOrder: 12 format: string since: - default: '2021-01-01' + default: "2021-01-01" title: Since description: Date (in `YYYY-MM-DD` format) from which issues will start to be fetched. The date will be in the UTC timezone. acceptFormats: @@ -736,7 +1537,7 @@ TASK_LIST_ISSUES: uiOrder: 1 title: Issues items: - $ref: '#/$defs/issue' + $ref: "#/$defs/issue" required: [] description: An issue in GitHub. format: array @@ -752,7 +1553,7 @@ TASK_LIST_PULL_REQUESTS: description: Please input the repository name and owner. uiOrder: 0 properties: - $ref: '#/$defs/repositoryInfo' + $ref: "#/$defs/repositoryInfo" state: default: open title: State @@ -819,7 +1620,7 @@ TASK_LIST_PULL_REQUESTS: title: Pull Requests uiOrder: 1 items: - $ref: '#/$defs/pullRequest' + $ref: "#/$defs/pullRequest" required: [] description: A pull request in GitHub. format: array @@ -834,7 +1635,7 @@ TASK_LIST_REVIEW_COMMENTS: description: Please input the repository name and owner, and the PR number. Set PR number as`0` to get all comments on all PRs in the repository. uiOrder: 0 properties: - $ref: '#/$defs/repositoryInfo' + $ref: "#/$defs/repositoryInfo" pr-number: default: 0 title: PR Number @@ -866,7 +1667,7 @@ TASK_LIST_REVIEW_COMMENTS: uiOrder: 12 format: string since: - default: '2021-01-01' + default: "2021-01-01" title: Since description: Date (in `YYYY-MM-DD` format) from which comments will start to be fetched. The date will be in the UTC timezone. acceptFormats: @@ -903,7 +1704,7 @@ TASK_LIST_REVIEW_COMMENTS: uiOrder: 0 title: Comments items: - $ref: '#/$defs/reviewComments' + $ref: "#/$defs/reviewComments" required: [] uiOrder: 1 description: Comments in the PR. diff --git a/pkg/component/application/github/v0/io.go b/pkg/component/application/github/v0/io.go index f60723c46..2e13ef560 100644 --- a/pkg/component/application/github/v0/io.go +++ b/pkg/component/application/github/v0/io.go @@ -53,6 +53,15 @@ type getIssueOutput struct { Issue } +type getOrganizationInput struct { + OrgName string `instill:"name"` + OrgID int64 `instill:"id"` +} + +type getOrganizationOutput struct { + Organization Organization `instill:"organization"` +} + type getPullRequestInput struct { RepoInfo PRNumber float64 `instill:"pr-number"` @@ -62,18 +71,28 @@ type getPullRequestOutput struct { PullRequest } +type getUserInput struct { + Username string `instill:"name"` + UserID int64 `instill:"id"` +} + +type getUserOutput struct { + User User `instill:"user"` +} + type listIssuesInput struct { + PageOptions RepoInfo - State string `instill:"state,default=open"` - Sort string `instill:"sort,default=created"` - Direction string `instill:"direction,default=desc"` + State string `instill:"state"` + Sort string `instill:"sort"` + Direction string `instill:"direction"` Since string `instill:"since"` NoPullRequest bool `instill:"no-pull-request"` - PageOptions } type listIssuesOutput struct { - Issues []Issue `instill:"issues"` + Issues []Issue `instill:"issues"` + Response *Response `instill:"response"` } type listPullRequestsInput struct { @@ -86,17 +105,19 @@ type listPullRequestsInput struct { type listPullRequestsOutput struct { PullRequests []PullRequest `instill:"pull-requests"` + Response *Response `instill:"response"` } type listReviewCommentsInput struct { + PageOptions RepoInfo - PRNumber int `instill:"pr-number,default=0"` - Sort string `instill:"sort,default=created"` - Direction string `instill:"direction,default=desc"` + PRNumber int `instill:"pr-number"` + Sort string `instill:"sort"` + Direction string `instill:"direction"` Since string `instill:"since"` - PageOptions } type listReviewCommentsOutput struct { ReviewComments []ReviewComment `instill:"comments"` + Response *Response `instill:"response"` } diff --git a/pkg/component/application/github/v0/issues.go b/pkg/component/application/github/v0/issues.go index 7b2669d87..cf69a9e46 100644 --- a/pkg/component/application/github/v0/issues.go +++ b/pkg/component/application/github/v0/issues.go @@ -6,13 +6,14 @@ import ( "github.com/google/go-github/v62/github" ) +// IssuesService is the interface for the GitHub issues service. type IssuesService interface { ListByRepo(context.Context, string, string, *github.IssueListByRepoOptions) ([]*github.Issue, *github.Response, error) Get(context.Context, string, string, int) (*github.Issue, *github.Response, error) Create(context.Context, string, string, *github.IssueRequest) (*github.Issue, *github.Response, error) - // Edit(context.Context, string, string, int, *github.IssueRequest) (*github.Issue, *github.Response, error) } +// Issue is the GitHub issue object. type Issue struct { Number int `instill:"number"` Title string `instill:"title"` @@ -53,13 +54,6 @@ func extractLabels(labels []*github.Label) []string { return labelList } -func (client *Client) getIssueFunc(ctx context.Context, owner, repository string, issueNumber int) (*github.Issue, error) { - issue, _, err := client.Issues.Get(ctx, owner, repository, issueNumber) - if err != nil { - return nil, addErrMsgToClientError(err) - } - return issue, nil -} func filterOutPullRequests(issues []Issue) []Issue { filteredIssues := make([]Issue, 0) for _, issue := range issues { diff --git a/pkg/component/application/github/v0/main.go b/pkg/component/application/github/v0/main.go index abd5f2a20..7767bf175 100644 --- a/pkg/component/application/github/v0/main.go +++ b/pkg/component/application/github/v0/main.go @@ -25,7 +25,9 @@ const ( taskCreateWebhook = "TASK_CREATE_WEBHOOK" taskGetCommit = "TASK_GET_COMMIT" taskGetIssue = "TASK_GET_ISSUE" + taskGetOrganization = "TASK_GET_ORGANIZATION" taskGetPullRequest = "TASK_GET_PULL_REQUEST" + taskGetUser = "TASK_GET_USER" taskListIssues = "TASK_LIST_ISSUES" taskListPullRequests = "TASK_LIST_PULL_REQUESTS" taskListReviewComments = "TASK_LIST_REVIEW_COMMENTS" @@ -91,8 +93,12 @@ func (c *component) CreateExecution(x base.ComponentExecution) (base.IExecution, e.execute = e.client.getCommit case taskGetIssue: e.execute = e.client.getIssue + case taskGetOrganization: + e.execute = e.client.getOrganization case taskGetPullRequest: e.execute = e.client.getPullRequest + case taskGetUser: + e.execute = e.client.getUser case taskListIssues: e.execute = e.client.listIssues case taskListPullRequests: @@ -122,8 +128,12 @@ func (e *execution) Execute(ctx context.Context, jobs []*base.Job) error { input = &getCommitInput{} case taskGetIssue: input = &getIssueInput{} + case taskGetOrganization: + input = &getOrganizationInput{} case taskGetPullRequest: input = &getPullRequestInput{} + case taskGetUser: + input = &getUserInput{} case taskListIssues: input = &listIssuesInput{} case taskListPullRequests: diff --git a/pkg/component/application/github/v0/mock.go b/pkg/component/application/github/v0/mock.go new file mode 100644 index 000000000..b6eede2df --- /dev/null +++ b/pkg/component/application/github/v0/mock.go @@ -0,0 +1,21 @@ +package github + +const ( + fakeHost = "https://fake-github.com" +) + +func middleWare(req string) int { + if req == "rate_limit" { + return 403 + } + if req == "not_found" { + return 404 + } + if req == "unprocessable_entity" { + return 422 + } + if req == "no_pr" { + return 201 + } + return 200 +} diff --git a/pkg/component/application/github/v0/mock_issue_service.go b/pkg/component/application/github/v0/mock_issue_service.go deleted file mode 100644 index fb43c2b72..000000000 --- a/pkg/component/application/github/v0/mock_issue_service.go +++ /dev/null @@ -1,114 +0,0 @@ -package github - -import ( - "context" - "fmt" - - "github.com/google/go-github/v62/github" -) - -type MockIssuesService struct{} - -func (m *MockIssuesService) ListByRepo(ctx context.Context, owner, repo string, opt *github.IssueListByRepoOptions) ([]*github.Issue, *github.Response, error) { - switch middleWare(owner) { - case 403: - return nil, nil, fmt.Errorf("403 API rate limit exceeded") - case 404: - return nil, nil, fmt.Errorf("404 Not Found") - case 201: - return []*github.Issue{}, nil, nil - } - - resp := &github.Response{} - issues := []*github.Issue{} - issues = append(issues, &github.Issue{ - ID: github.Int64(1), - Number: github.Int(1), - Title: github.String("This is a fake Issue"), - Body: github.String("Issue Body"), - State: github.String("open"), - Assignee: &github.User{ - Login: github.String("assignee"), - }, - Assignees: []*github.User{ - { - Login: github.String("assignee1"), - }, - { - Login: github.String("assignee2"), - }, - }, - Labels: []*github.Label{ - { - Name: github.String("label1"), - }, - { - Name: github.String("label2"), - }, - }, - PullRequestLinks: nil, - }) - return issues, resp, nil -} - -func (m *MockIssuesService) Get(ctx context.Context, owner, repo string, number int) (*github.Issue, *github.Response, error) { - switch middleWare(owner) { - case 403: - return nil, nil, fmt.Errorf("403 API rate limit exceeded") - case 404: - return nil, nil, fmt.Errorf("404 Not Found") - case 201: - return &github.Issue{}, nil, nil - } - resp := &github.Response{} - issue := &github.Issue{ - ID: github.Int64(1), - Number: github.Int(1), - Title: github.String("This is a fake Issue"), - Body: github.String("Issue Body"), - State: github.String("open"), - Assignee: &github.User{ - Login: github.String("assignee"), - }, - Assignees: []*github.User{ - { - Login: github.String("assignee1"), - }, - { - Login: github.String("assignee2"), - }, - }, - Labels: []*github.Label{ - { - Name: github.String("label1"), - }, - { - Name: github.String("label2"), - }, - }, - PullRequestLinks: nil, - } - return issue, resp, nil -} - -func (m *MockIssuesService) Create(ctx context.Context, owner, repo string, issue *github.IssueRequest) (*github.Issue, *github.Response, error) { - switch middleWare(owner) { - case 403: - return nil, nil, fmt.Errorf("403 API rate limit exceeded") - case 404: - return nil, nil, fmt.Errorf("404 Not Found") - case 201: - return &github.Issue{}, nil, nil - } - resp := &github.Response{} - - newIssue := &github.Issue{ - ID: github.Int64(1), - Number: github.Int(1), - Title: issue.Title, - Body: issue.Body, - State: github.String("open"), - PullRequestLinks: nil, - } - return newIssue, resp, nil -} diff --git a/pkg/component/application/github/v0/mock_issues_service.go b/pkg/component/application/github/v0/mock_issues_service.go new file mode 100644 index 000000000..46b9167a5 --- /dev/null +++ b/pkg/component/application/github/v0/mock_issues_service.go @@ -0,0 +1,184 @@ +package github + +import ( + "context" + "fmt" + + "github.com/google/go-github/v62/github" +) + +// MockIssuesService is a mock implementation of the IssuesService interface. +type MockIssuesService struct{} + +// ListByRepo is a mock implementation of the ListByRepo method for the IssuesService. +func (m *MockIssuesService) ListByRepo(ctx context.Context, owner, repo string, opt *github.IssueListByRepoOptions) ([]*github.Issue, *github.Response, error) { + switch middleWare(owner) { + case 403: + return nil, nil, fmt.Errorf("403 API rate limit exceeded") + case 404: + return nil, nil, fmt.Errorf("404 Not Found") + case 201: + return []*github.Issue{}, nil, nil + } + + resp := &github.Response{} + issues := []*github.Issue{} + + // Generate paginated issues + for i := 1; i <= 10; i++ { + issues = append(issues, &github.Issue{ + Number: github.Int(i), + Title: github.String(fmt.Sprintf("This is a fake Issue #%d", i)), + State: github.String("open"), + Body: github.String(fmt.Sprintf("Issue Body #%d", i)), + Assignee: &github.User{ + Login: github.String(fmt.Sprintf("assignee%d", i)), + }, + Assignees: []*github.User{ + {Login: github.String(fmt.Sprintf("assignee%d_1", i))}, + {Login: github.String(fmt.Sprintf("assignee%d_2", i))}, + }, + Labels: []*github.Label{ + {Name: github.String(fmt.Sprintf("label%d_1", i))}, + {Name: github.String(fmt.Sprintf("label%d_2", i))}, + }, + }) + } + + // Handle pagination if owner is "paginated" + if owner == "paginated" { + if opt == nil { + opt = &github.IssueListByRepoOptions{ + ListOptions: github.ListOptions{ + Page: 1, + PerPage: 2, // Default page size for tests + }, + } + } + if opt.Page == 0 { + opt.Page = 1 + } + if opt.PerPage == 0 { + opt.PerPage = 2 // Default page size for tests + } + + start := (opt.Page - 1) * opt.PerPage + end := start + opt.PerPage + if start >= len(issues) { + return []*github.Issue{}, resp, nil + } + if end > len(issues) { + end = len(issues) + } + + // Calculate total pages correctly + totalPages := (len(issues) + opt.PerPage - 1) / opt.PerPage + + // Set pagination information + resp.FirstPage = 1 + resp.LastPage = totalPages + + if opt.Page < totalPages { + resp.NextPage = opt.Page + 1 + resp.NextPageToken = fmt.Sprintf("page_%d", resp.NextPage) + } + + if opt.Page > 1 { + resp.PrevPage = opt.Page - 1 + } + + resp.Cursor = fmt.Sprintf("cursor_%d", opt.Page) + resp.Before = fmt.Sprintf("before_%d", opt.Page) + resp.After = fmt.Sprintf("after_%d", opt.Page) + + return issues[start:end], resp, nil + } + + // For non-paginated cases, return a single issue + issues = []*github.Issue{ + { + ID: github.Int64(1), + Number: github.Int(1), + Title: github.String("This is a fake Issue"), + Body: github.String("Issue Body"), + State: github.String("open"), + Assignee: &github.User{ + Login: github.String("assignee"), + }, + Assignees: []*github.User{ + {Login: github.String("assignee1")}, + {Login: github.String("assignee2")}, + }, + Labels: []*github.Label{ + {Name: github.String("label1")}, + {Name: github.String("label2")}, + }, + PullRequestLinks: nil, + }, + } + return issues, resp, nil +} + +// Get is a mock implementation of the Get method for the IssuesService. +func (m *MockIssuesService) Get(ctx context.Context, owner, repo string, number int) (*github.Issue, *github.Response, error) { + switch middleWare(owner) { + case 403: + return nil, nil, fmt.Errorf("403 API rate limit exceeded") + case 404: + return nil, nil, fmt.Errorf("404 Not Found") + case 201: + return &github.Issue{}, nil, nil + } + resp := &github.Response{} + issue := &github.Issue{ + ID: github.Int64(1), + Number: github.Int(1), + Title: github.String("This is a fake Issue"), + Body: github.String("Issue Body"), + State: github.String("open"), + Assignee: &github.User{ + Login: github.String("assignee"), + }, + Assignees: []*github.User{ + { + Login: github.String("assignee1"), + }, + { + Login: github.String("assignee2"), + }, + }, + Labels: []*github.Label{ + { + Name: github.String("label1"), + }, + { + Name: github.String("label2"), + }, + }, + PullRequestLinks: nil, + } + return issue, resp, nil +} + +// Create is a mock implementation of the Create method for the IssuesService. +func (m *MockIssuesService) Create(ctx context.Context, owner, repo string, issue *github.IssueRequest) (*github.Issue, *github.Response, error) { + switch middleWare(owner) { + case 403: + return nil, nil, fmt.Errorf("403 API rate limit exceeded") + case 404: + return nil, nil, fmt.Errorf("404 Not Found") + case 201: + return &github.Issue{}, nil, nil + } + resp := &github.Response{} + + newIssue := &github.Issue{ + ID: github.Int64(1), + Number: github.Int(1), + Title: issue.Title, + Body: issue.Body, + State: github.String("open"), + PullRequestLinks: nil, + } + return newIssue, resp, nil +} diff --git a/pkg/component/application/github/v0/mock_orgs_service.go b/pkg/component/application/github/v0/mock_orgs_service.go new file mode 100644 index 000000000..d734e0450 --- /dev/null +++ b/pkg/component/application/github/v0/mock_orgs_service.go @@ -0,0 +1,74 @@ +package github + +import ( + "context" + "fmt" + + "github.com/google/go-github/v62/github" +) + +// MockOrganizationsService is a mock implementation of the OrganizationService interface. +type MockOrganizationsService struct{} + +// Get is a mock implementation of the Get method for the OrganizationService. +func (m *MockOrganizationsService) Get(ctx context.Context, org string) (*github.Organization, *github.Response, error) { + switch middleWare(org) { + case 403: + return nil, nil, fmt.Errorf("403 API rate limit exceeded") + case 404: + return nil, nil, fmt.Errorf("404 Not Found") + } + + resp := &github.Response{} + organization := &github.Organization{ + Login: github.String("test-org"), + ID: github.Int64(1), + NodeID: github.String("node1"), + URL: github.String("https://api.github.com/orgs/test-org"), + ReposURL: github.String("https://api.github.com/orgs/test-org/repos"), + EventsURL: github.String("https://api.github.com/orgs/test-org/events"), + HooksURL: github.String("https://api.github.com/orgs/test-org/hooks"), + IssuesURL: github.String("https://api.github.com/orgs/test-org/issues"), + MembersURL: github.String("https://api.github.com/orgs/test-org/members{/member}"), + PublicMembersURL: github.String("https://api.github.com/orgs/test-org/public_members{/member}"), + AvatarURL: github.String("https://github.com/images/error/octocat_happy.gif"), + Description: github.String("A great organization"), + Name: github.String("test-org"), + Company: github.String("GitHub"), + Blog: github.String("https://github.com/blog"), + Location: github.String("San Francisco"), + Email: github.String("octocat@github.com"), + TwitterUsername: github.String("github"), + IsVerified: github.Bool(true), + HasOrganizationProjects: github.Bool(true), + HasRepositoryProjects: github.Bool(true), + PublicRepos: github.Int(2), + PublicGists: github.Int(1), + Followers: github.Int(20), + Following: github.Int(0), + HTMLURL: github.String("https://github.com/octocat"), + Type: github.String("Organization"), + CreatedAt: &github.Timestamp{}, + UpdatedAt: &github.Timestamp{}, + TotalPrivateRepos: github.Int64(50), + OwnedPrivateRepos: github.Int64(45), + PrivateGists: github.Int(10), + DiskUsage: github.Int(50000), + Collaborators: github.Int(25), + BillingEmail: github.String("billing@test-org.com"), + TwoFactorRequirementEnabled: github.Bool(true), + } + return organization, resp, nil +} + +// GetByID is a mock implementation of the GetByID method for the OrganizationService. +func (m *MockOrganizationsService) GetByID(ctx context.Context, id int64) (*github.Organization, *github.Response, error) { + if id == 403 { + return nil, nil, fmt.Errorf("403 API rate limit exceeded") + } + if id == 404 { + return nil, nil, fmt.Errorf("404 Not Found") + } + + return m.Get(ctx, "test-org") +} diff --git a/pkg/component/application/github/v0/mock_pull_request_service.go b/pkg/component/application/github/v0/mock_pull_request_service.go deleted file mode 100644 index b580a01f3..000000000 --- a/pkg/component/application/github/v0/mock_pull_request_service.go +++ /dev/null @@ -1,167 +0,0 @@ -package github - -import ( - "context" - "fmt" - - "github.com/google/go-github/v62/github" -) - -const ( - fakeHost = "https://fake-github.com" -) - -func middleWare(req string) int { - if req == "rate_limit" { - return 403 - } - if req == "not_found" { - return 404 - } - if req == "unprocessable_entity" { - return 422 - } - if req == "no_pr" { - return 201 - } - return 200 -} - -type MockPullRequestService struct { - Client *github.Client -} - -func (m *MockPullRequestService) List(ctx context.Context, owner, repo string, opts *github.PullRequestListOptions) ([]*github.PullRequest, *github.Response, error) { - switch middleWare(owner) { - case 403: - return nil, nil, fmt.Errorf("403 API rate limit exceeded") - case 404: - return nil, nil, fmt.Errorf("404 Not Found") - case 201: - return []*github.PullRequest{}, nil, nil - } - - resp := &github.Response{} - prs := []*github.PullRequest{} - diffURL := fmt.Sprintf("%v/%v/%v/pull/%v.diff", fakeHost, owner, repo, 1) - commitsURL := fmt.Sprintf("%v/%v/%v/pull/%v/commits", fakeHost, owner, repo, 1) - prs = append(prs, &github.PullRequest{ - ID: github.Int64(1), - Number: github.Int(1), - Title: github.String("This is a fake PR"), - Body: github.String("PR Body"), - DiffURL: github.String(diffURL), - Head: &github.PullRequestBranch{ - SHA: github.String("headSHA"), - }, - Base: &github.PullRequestBranch{ - SHA: github.String("baseSHA"), - }, - Comments: github.Int(0), - Commits: github.Int(1), - ReviewComments: github.Int(2), - State: github.String("open"), - CommitsURL: github.String(commitsURL), - }) - return prs, resp, nil -} -func (m *MockPullRequestService) Get(ctx context.Context, owner, repo string, number int) (*github.PullRequest, *github.Response, error) { - switch middleWare(owner) { - case 403: - return nil, nil, fmt.Errorf("403 API rate limit exceeded") - case 404: - return nil, nil, fmt.Errorf("404 Not Found") - case 201: - return nil, nil, nil - } - resp := &github.Response{} - diffURL := fmt.Sprintf("%v/%v/%v/pull/%v.diff", fakeHost, owner, repo, number) - commitsURL := fmt.Sprintf("%v/%v/%v/pull/%v/commits", fakeHost, owner, repo, number) - prs := &github.PullRequest{ - ID: github.Int64(1), - Number: github.Int(number), - Title: github.String("This is a fake PR"), - Body: github.String("PR Body"), - DiffURL: github.String(diffURL), - Head: &github.PullRequestBranch{ - SHA: github.String("headSHA"), - }, - Base: &github.PullRequestBranch{ - SHA: github.String("baseSHA"), - }, - Comments: github.Int(0), - Commits: github.Int(1), - ReviewComments: github.Int(2), - State: github.String("open"), - CommitsURL: github.String(commitsURL), - } - return prs, resp, nil -} -func (m *MockPullRequestService) ListComments(ctx context.Context, owner, repo string, number int, opts *github.PullRequestListCommentsOptions) ([]*github.PullRequestComment, *github.Response, error) { - switch middleWare(owner) { - case 403: - return nil, nil, fmt.Errorf("403 API rate limit exceeded") - case 404: - return nil, nil, fmt.Errorf("404 Not Found") - case 201: - return []*github.PullRequestComment{}, nil, nil - } - resp := &github.Response{} - comments := []*github.PullRequestComment{} - comments = append(comments, &github.PullRequestComment{ - ID: github.Int64(1), - Body: github.String("This is a fake comment"), - }) - return comments, resp, nil -} -func (m *MockPullRequestService) CreateComment(ctx context.Context, owner, repo string, number int, comment *github.PullRequestComment) (*github.PullRequestComment, *github.Response, error) { - switch middleWare(owner) { - case 403: - return nil, nil, fmt.Errorf("403 API rate limit exceeded") - case 404: - return nil, nil, fmt.Errorf("404 Not Found") - case 422: - return nil, nil, fmt.Errorf("422 Unprocessable Entity") - case 201: - return nil, nil, nil - } - if comment.StartLine != nil && *comment.Line <= *comment.StartLine { - return nil, nil, fmt.Errorf("422 Unprocessable Entity") - } - - resp := &github.Response{} - comment.ID = github.Int64(1) - return comment, resp, nil -} -func (m *MockPullRequestService) ListCommits(ctx context.Context, owner, repo string, number int, opts *github.ListOptions) ([]*github.RepositoryCommit, *github.Response, error) { - switch middleWare(owner) { - case 403: - return nil, nil, fmt.Errorf("403 API rate limit exceeded") - case 201: - return []*github.RepositoryCommit{}, nil, nil - } - resp := &github.Response{} - commits := []*github.RepositoryCommit{} - commits = append(commits, &github.RepositoryCommit{ - SHA: github.String("commitSHA"), - Commit: &github.Commit{ - Message: github.String("This is a fake commit"), - }, - Stats: &github.CommitStats{ - Additions: github.Int(1), - Deletions: github.Int(1), - Total: github.Int(2), - }, - Files: []*github.CommitFile{ - { - Filename: github.String("filename"), - Patch: github.String("patch"), - Additions: github.Int(1), - Deletions: github.Int(1), - Changes: github.Int(2), - }, - }, - }) - return commits, resp, nil - -} diff --git a/pkg/component/application/github/v0/mock_pull_requests_service.go b/pkg/component/application/github/v0/mock_pull_requests_service.go new file mode 100644 index 000000000..38f3c1766 --- /dev/null +++ b/pkg/component/application/github/v0/mock_pull_requests_service.go @@ -0,0 +1,303 @@ +package github + +import ( + "context" + "fmt" + + "github.com/google/go-github/v62/github" +) + +// MockPullRequestsService is a mock implementation of the PullRequestService interface. +type MockPullRequestsService struct { + Client *github.Client +} + +// List is a mock implementation of the List method for the PullRequestService. +func (m *MockPullRequestsService) List(ctx context.Context, owner, repo string, opts *github.PullRequestListOptions) ([]*github.PullRequest, *github.Response, error) { + switch middleWare(owner) { + case 403: + return nil, nil, fmt.Errorf("403 API rate limit exceeded") + case 404: + return nil, nil, fmt.Errorf("404 Not Found") + case 201: + return []*github.PullRequest{}, nil, nil + } + + resp := &github.Response{} + + // Return single PR for non-paginated case + if owner == "non-paginated" { + diffURL := fmt.Sprintf("%v/%v/%v/pull/%v.diff", fakeHost, owner, repo, 1) + commitsURL := fmt.Sprintf("%v/%v/%v/pull/%v/commits", fakeHost, owner, repo, 1) + return []*github.PullRequest{ + { + ID: github.Int64(1), + Number: github.Int(1), + Title: github.String("This is a fake PR"), + Body: github.String("PR Body"), + DiffURL: github.String(diffURL), + Head: &github.PullRequestBranch{ + SHA: github.String("headSHA"), + }, + Base: &github.PullRequestBranch{ + SHA: github.String("baseSHA"), + }, + Comments: github.Int(0), + Commits: github.Int(1), + ReviewComments: github.Int(2), + State: github.String("open"), + CommitsURL: github.String(commitsURL), + }, + }, resp, nil + } + + // Generate paginated PRs for "paginated" owner + prs := []*github.PullRequest{} + for i := 1; i <= 10; i++ { + diffURL := fmt.Sprintf("%v/%v/%v/pull/%v.diff", fakeHost, owner, repo, i) + commitsURL := fmt.Sprintf("%v/%v/%v/pull/%v/commits", fakeHost, owner, repo, i) + prs = append(prs, &github.PullRequest{ + ID: github.Int64(int64(i)), + Number: github.Int(i), + Title: github.String(fmt.Sprintf("This is a fake PR #%d", i)), + Body: github.String(fmt.Sprintf("PR Body #%d", i)), + DiffURL: github.String(diffURL), + Head: &github.PullRequestBranch{ + SHA: github.String(fmt.Sprintf("headSHA%d", i)), + }, + Base: &github.PullRequestBranch{ + SHA: github.String(fmt.Sprintf("baseSHA%d", i)), + }, + Comments: github.Int(i), + Commits: github.Int(i), + ReviewComments: github.Int(i * 2), + State: github.String("open"), + CommitsURL: github.String(commitsURL), + }) + } + + // Handle pagination if owner is "paginated" + if owner == "paginated" { + if opts == nil { + opts = &github.PullRequestListOptions{ + ListOptions: github.ListOptions{ + Page: 1, + PerPage: 30, // GitHub default + }, + } + } + if opts.Page == 0 { + opts.Page = 1 + } + if opts.PerPage == 0 { + opts.PerPage = 30 // GitHub default + } + + start := (opts.Page - 1) * opts.PerPage + end := start + opts.PerPage + if start >= len(prs) { + // Return empty slice when starting beyond available data + return []*github.PullRequest{}, resp, nil + } + if end > len(prs) { + end = len(prs) + } + + // Calculate total pages + totalPages := (len(prs) + opts.PerPage - 1) / opts.PerPage + + // Set pagination information + resp.FirstPage = 1 + resp.LastPage = totalPages + + if opts.Page < totalPages { + resp.NextPage = opts.Page + 1 + resp.NextPageToken = fmt.Sprintf("page_%d", resp.NextPage) + } + + if opts.Page > 1 { + resp.PrevPage = opts.Page - 1 + } + + resp.Cursor = fmt.Sprintf("cursor_%d", opts.Page) + resp.Before = fmt.Sprintf("before_%d", opts.Page) + resp.After = fmt.Sprintf("after_%d", opts.Page) + + return prs[start:end], resp, nil + } + + return prs, resp, nil +} + +// Get is a mock implementation of the Get method for the PullRequestService. +func (m *MockPullRequestsService) Get(ctx context.Context, owner, repo string, number int) (*github.PullRequest, *github.Response, error) { + switch middleWare(owner) { + case 403: + return nil, nil, fmt.Errorf("403 API rate limit exceeded") + case 404: + return nil, nil, fmt.Errorf("404 Not Found") + case 201: + return nil, nil, nil + } + resp := &github.Response{} + diffURL := fmt.Sprintf("%v/%v/%v/pull/%v.diff", fakeHost, owner, repo, number) + commitsURL := fmt.Sprintf("%v/%v/%v/pull/%v/commits", fakeHost, owner, repo, number) + prs := &github.PullRequest{ + ID: github.Int64(1), + Number: github.Int(number), + Title: github.String("This is a fake PR"), + Body: github.String("PR Body"), + DiffURL: github.String(diffURL), + Head: &github.PullRequestBranch{ + SHA: github.String("headSHA"), + }, + Base: &github.PullRequestBranch{ + SHA: github.String("baseSHA"), + }, + Comments: github.Int(0), + Commits: github.Int(1), + ReviewComments: github.Int(2), + State: github.String("open"), + CommitsURL: github.String(commitsURL), + } + return prs, resp, nil +} + +// ListComments is a mock implementation of the ListComments method for the PullRequestService. +func (m *MockPullRequestsService) ListComments(ctx context.Context, owner, repo string, number int, opts *github.PullRequestListCommentsOptions) ([]*github.PullRequestComment, *github.Response, error) { + switch middleWare(owner) { + case 403: + return nil, nil, fmt.Errorf("403 API rate limit exceeded") + case 404: + return nil, nil, fmt.Errorf("404 Not Found") + case 201: + return []*github.PullRequestComment{}, nil, nil + } + resp := &github.Response{} + + // Return single comment for non-paginated case + if owner == "non-paginated" { + return []*github.PullRequestComment{ + { + ID: github.Int64(1), + Body: github.String("This is a fake comment"), + }, + }, resp, nil + } + + // Generate paginated comments for "paginated" owner + comments := []*github.PullRequestComment{} + for i := 1; i <= 10; i++ { + comments = append(comments, &github.PullRequestComment{ + ID: github.Int64(int64(i)), + Body: github.String(fmt.Sprintf("This is a fake comment #%d", i)), + }) + } + + // Handle pagination if owner is "paginated" + if owner == "paginated" { + if opts == nil { + opts = &github.PullRequestListCommentsOptions{ + ListOptions: github.ListOptions{ + Page: 1, + PerPage: 30, // GitHub default + }, + } + } + if opts.Page == 0 { + opts.Page = 1 + } + if opts.PerPage == 0 { + opts.PerPage = 30 // GitHub default + } + + start := (opts.Page - 1) * opts.PerPage + end := start + opts.PerPage + if start >= len(comments) { + // Return empty slice when starting beyond available data + return []*github.PullRequestComment{}, resp, nil + } + if end > len(comments) { + end = len(comments) + } + + // Calculate total pages + totalPages := (len(comments) + opts.PerPage - 1) / opts.PerPage + + // Set pagination information + resp.FirstPage = 1 + resp.LastPage = totalPages + + if opts.Page < totalPages { + resp.NextPage = opts.Page + 1 + resp.NextPageToken = fmt.Sprintf("page_%d", resp.NextPage) + } + + if opts.Page > 1 { + resp.PrevPage = opts.Page - 1 + } + + resp.Cursor = fmt.Sprintf("cursor_%d", opts.Page) + resp.Before = fmt.Sprintf("before_%d", opts.Page) + resp.After = fmt.Sprintf("after_%d", opts.Page) + + return comments[start:end], resp, nil + } + + return comments, resp, nil +} + +// CreateComment is a mock implementation of the CreateComment method for the PullRequestService. +func (m *MockPullRequestsService) CreateComment(ctx context.Context, owner, repo string, number int, comment *github.PullRequestComment) (*github.PullRequestComment, *github.Response, error) { + switch middleWare(owner) { + case 403: + return nil, nil, fmt.Errorf("403 API rate limit exceeded") + case 404: + return nil, nil, fmt.Errorf("404 Not Found") + case 422: + return nil, nil, fmt.Errorf("422 Unprocessable Entity") + case 201: + return nil, nil, nil + } + if comment.StartLine != nil && *comment.Line <= *comment.StartLine { + return nil, nil, fmt.Errorf("422 Unprocessable Entity") + } + + resp := &github.Response{} + comment.ID = github.Int64(1) + return comment, resp, nil +} + +// ListCommits is a mock implementation of the ListCommits method for the PullRequestService. +func (m *MockPullRequestsService) ListCommits(ctx context.Context, owner, repo string, number int, opts *github.ListOptions) ([]*github.RepositoryCommit, *github.Response, error) { + switch middleWare(owner) { + case 403: + return nil, nil, fmt.Errorf("403 API rate limit exceeded") + case 201: + return []*github.RepositoryCommit{}, nil, nil + } + resp := &github.Response{} + commits := []*github.RepositoryCommit{} + commits = append(commits, &github.RepositoryCommit{ + SHA: github.String("commitSHA"), + Commit: &github.Commit{ + Message: github.String("This is a fake commit"), + }, + Stats: &github.CommitStats{ + Additions: github.Int(1), + Deletions: github.Int(1), + Total: github.Int(2), + }, + Files: []*github.CommitFile{ + { + Filename: github.String("filename"), + Patch: github.String("patch"), + Additions: github.Int(1), + Deletions: github.Int(1), + Changes: github.Int(2), + }, + }, + }) + return commits, resp, nil + +} diff --git a/pkg/component/application/github/v0/mock_repositories_service.go b/pkg/component/application/github/v0/mock_repositories_service.go index 27b8e3165..9cfc41296 100644 --- a/pkg/component/application/github/v0/mock_repositories_service.go +++ b/pkg/component/application/github/v0/mock_repositories_service.go @@ -7,8 +7,10 @@ import ( "github.com/google/go-github/v62/github" ) +// MockRepositoriesService is a mock implementation of the RepositoryService interface. type MockRepositoriesService struct{} +// GetCommit is a mock implementation of the GetCommit method for the RepositoryService. func (m *MockRepositoriesService) GetCommit(ctx context.Context, owner, repo, sha string, opts *github.ListOptions) (*github.RepositoryCommit, *github.Response, error) { switch middleWare(owner) { case 403: @@ -45,6 +47,7 @@ func (m *MockRepositoriesService) GetCommit(ctx context.Context, owner, repo, sh return commit, resp, nil } +// ListCommits is a mock implementation of the ListCommits method for the RepositoryService. func (m *MockRepositoriesService) ListCommits(ctx context.Context, owner, repo string, opts *github.CommitsListOptions) ([]*github.RepositoryCommit, *github.Response, error) { switch middleWare(owner) { case 403: diff --git a/pkg/component/application/github/v0/mock_users_service.go b/pkg/component/application/github/v0/mock_users_service.go new file mode 100644 index 000000000..6bd176cd7 --- /dev/null +++ b/pkg/component/application/github/v0/mock_users_service.go @@ -0,0 +1,91 @@ +package github + +import ( + "context" + "fmt" + + "github.com/google/go-github/v62/github" +) + +// MockUsersService is a mock implementation of the UsersService interface. +type MockUsersService struct{} + +// Get is a mock implementation of the Get method for the UsersService. +func (m *MockUsersService) Get(ctx context.Context, username string) (*github.User, *github.Response, error) { + switch middleWare(username) { + case 403: + return nil, nil, fmt.Errorf("403 API rate limit exceeded") + case 404: + return nil, nil, fmt.Errorf("404 Not Found") + } + + resp := &github.Response{} + user := &github.User{ + Login: github.String("test-user"), + ID: github.Int64(1), + NodeID: github.String("node1"), + AvatarURL: github.String("https://avatar.url"), + HTMLURL: github.String("https://github.com/test-user"), + GravatarID: github.String(""), + Name: github.String("Test User"), + Company: github.String("Test Company"), + Blog: github.String("https://blog.com"), + Location: github.String("Test Location"), + Email: github.String("test@example.com"), + Hireable: github.Bool(true), + Bio: github.String("Test Bio"), + TwitterUsername: github.String("testuser"), + PublicRepos: github.Int(10), + PublicGists: github.Int(5), + Followers: github.Int(100), + Following: github.Int(50), + CreatedAt: nil, + UpdatedAt: nil, + SuspendedAt: nil, + Type: github.String("User"), + SiteAdmin: github.Bool(false), + TotalPrivateRepos: github.Int64(2), + OwnedPrivateRepos: github.Int64(2), + PrivateGists: github.Int(1), + DiskUsage: github.Int(1000), + Collaborators: github.Int(3), + TwoFactorAuthentication: github.Bool(true), + Plan: &github.Plan{ + Name: github.String("pro"), + Space: github.Int(100), + Collaborators: github.Int(10), + PrivateRepos: github.Int64(50), + }, + LdapDn: github.String(""), + URL: github.String("https://api.github.com/users/test-user"), + EventsURL: github.String("https://api.github.com/users/test-user/events{/privacy}"), + FollowingURL: github.String("https://api.github.com/users/test-user/following{/other_user}"), + FollowersURL: github.String("https://api.github.com/users/test-user/followers"), + GistsURL: github.String("https://api.github.com/users/test-user/gists{/gist_id}"), + OrganizationsURL: github.String("https://api.github.com/users/test-user/orgs"), + ReceivedEventsURL: github.String("https://api.github.com/users/test-user/received_events"), + ReposURL: github.String("https://api.github.com/users/test-user/repos"), + StarredURL: github.String("https://api.github.com/users/test-user/starred{/owner}{/repo}"), + SubscriptionsURL: github.String("https://api.github.com/users/test-user/subscriptions"), + TextMatches: []*github.TextMatch{}, + Permissions: map[string]bool{ + "admin": true, + "push": true, + "pull": true, + }, + RoleName: github.String("admin"), + } + return user, resp, nil +} + +// GetByID is a mock implementation of the GetByID method for the UsersService. +func (m *MockUsersService) GetByID(ctx context.Context, id int64) (*github.User, *github.Response, error) { + if id == 403 { + return nil, nil, fmt.Errorf("403 API rate limit exceeded") + } + if id == 404 { + return nil, nil, fmt.Errorf("404 Not Found") + } + + return m.Get(ctx, "test-user") +} diff --git a/pkg/component/application/github/v0/orgs.go b/pkg/component/application/github/v0/orgs.go new file mode 100644 index 000000000..9fd6e7810 --- /dev/null +++ b/pkg/component/application/github/v0/orgs.go @@ -0,0 +1,165 @@ +package github + +import ( + "context" + "time" + + "github.com/google/go-github/v62/github" +) + +// OrganizationsService handles communication with the organization related methods of the GitHub API. +type OrganizationsService interface { + // Get fetches an organization by name. + Get(ctx context.Context, org string) (*github.Organization, *github.Response, error) + + // GetByID fetches an organization by ID. + GetByID(ctx context.Context, id int64) (*github.Organization, *github.Response, error) +} + +// Organization represents a GitHub organization. +type Organization struct { + Login string `json:"login"` + ID int64 `json:"id"` + NodeID string `json:"node-id"` + AvatarURL string `json:"avatar-url"` + HTMLURL string `json:"html-url"` + Name string `json:"name"` + Company string `json:"company"` + Blog string `json:"blog"` + Location string `json:"location"` + Email string `json:"email"` + TwitterUsername string `json:"twitter-username"` + Description string `json:"description"` + PublicRepos int `json:"public-repos"` + PublicGists int `json:"public-gists"` + Followers int `json:"followers"` + Following int `json:"following"` + CreatedAt time.Time `json:"created-at"` + UpdatedAt time.Time `json:"updated-at"` + TotalPrivateRepos int64 `json:"total-private-repos"` + OwnedPrivateRepos int64 `json:"owned-private-repos"` + PrivateGists int `json:"private-gists"` + DiskUsage int `json:"disk-usage"` + Collaborators int `json:"collaborators"` + BillingEmail string `json:"billing-email"` + Type string `json:"type"` + Plan OrganizationPlan `json:"plan"` + TwoFactorRequirementEnabled bool `json:"two-factor-requirement-enabled"` + IsVerified bool `json:"is-verified"` + HasOrganizationProjects bool `json:"has-organization-projects"` + HasRepositoryProjects bool `json:"has-repository-projects"` + DefaultRepoPermission string `json:"default-repository-permission"` + DefaultRepoSettings string `json:"default-repository-settings"` + MembersCanCreateRepos bool `json:"members-can-create-repositories"` + MembersCanCreatePublicRepos bool `json:"members-can-create-public-repositories"` + MembersCanCreatePrivateRepos bool `json:"members-can-create-private-repositories"` + MembersCanCreateInternalRepos bool `json:"members-can-create-internal-repositories"` + MembersCanForkPrivateRepos bool `json:"members-can-fork-private-repositories"` + MembersAllowedRepositoryCreationType string `json:"members-allowed-repository-creation-type"` + MembersCanCreatePages bool `json:"members-can-create-pages"` + MembersCanCreatePublicPages bool `json:"members-can-create-public-pages"` + MembersCanCreatePrivatePages bool `json:"members-can-create-private-pages"` + WebCommitSignoffRequired bool `json:"web-commit-signoff-required"` + AdvancedSecurityEnabledForNewRepos bool `json:"advanced-security-enabled-for-new-repositories"` + DependabotAlertsEnabledForNewRepos bool `json:"dependabot-alerts-enabled-for-new-repositories"` + DependabotSecurityUpdatesEnabledForNewRepos bool `json:"dependabot-security-updates-enabled-for-new-repositories"` + DependencyGraphEnabledForNewRepos bool `json:"dependency-graph-enabled-for-new-repositories"` + SecretScanningEnabledForNewRepos bool `json:"secret-scanning-enabled-for-new-repositories"` + SecretScanningPushProtectionEnabledForNewRepos bool `json:"secret-scanning-push-protection-enabled-for-new-repositories"` + SecretScanningValidityChecksEnabled bool `json:"secret-scanning-validity-checks-enabled"` + + // API URLs + URL string `json:"url"` + EventsURL string `json:"events-url"` + HooksURL string `json:"hooks-url"` + IssuesURL string `json:"issues-url"` + MembersURL string `json:"members-url"` + PublicMembersURL string `json:"public-members-url"` + ReposURL string `json:"repos-url"` +} + +// OrganizationPlan represents a GitHub organization's plan +type OrganizationPlan struct { + Name string `json:"name"` + Space int `json:"space"` + PrivateRepos int64 `json:"private-repos"` + FilledSeats int `json:"filled-seats"` + Seats int `json:"seats"` +} + +// extractOrganization extracts organization information from a GitHub organization object. +func (client *Client) extractOrganization(org *github.Organization) Organization { + organization := Organization{ + Login: getString(org.Login), + ID: getInt64(org.ID), + NodeID: getString(org.NodeID), + URL: getString(org.URL), + ReposURL: getString(org.ReposURL), + EventsURL: getString(org.EventsURL), + HooksURL: getString(org.HooksURL), + IssuesURL: getString(org.IssuesURL), + MembersURL: getString(org.MembersURL), + PublicMembersURL: getString(org.PublicMembersURL), + AvatarURL: getString(org.AvatarURL), + Description: getString(org.Description), + Name: getString(org.Name), + Company: getString(org.Company), + Blog: getString(org.Blog), + Location: getString(org.Location), + Email: getString(org.Email), + TwitterUsername: getString(org.TwitterUsername), + IsVerified: getBool(org.IsVerified), + HasOrganizationProjects: getBool(org.HasOrganizationProjects), + HasRepositoryProjects: getBool(org.HasRepositoryProjects), + PublicRepos: getInt(org.PublicRepos), + PublicGists: getInt(org.PublicGists), + Followers: getInt(org.Followers), + Following: getInt(org.Following), + HTMLURL: getString(org.HTMLURL), + Type: getString(org.Type), + TotalPrivateRepos: getInt64(org.TotalPrivateRepos), + OwnedPrivateRepos: getInt64(org.OwnedPrivateRepos), + PrivateGists: getInt(org.PrivateGists), + DiskUsage: getInt(org.DiskUsage), + Collaborators: getInt(org.Collaborators), + BillingEmail: getString(org.BillingEmail), + DefaultRepoPermission: getString(org.DefaultRepoPermission), + DefaultRepoSettings: getString(org.DefaultRepoSettings), + MembersCanCreateRepos: getBool(org.MembersCanCreateRepos), + MembersCanCreatePublicRepos: getBool(org.MembersCanCreatePublicRepos), + MembersCanCreatePrivateRepos: getBool(org.MembersCanCreatePrivateRepos), + MembersCanCreateInternalRepos: getBool(org.MembersCanCreateInternalRepos), + MembersCanForkPrivateRepos: getBool(org.MembersCanForkPrivateRepos), + MembersCanCreatePages: getBool(org.MembersCanCreatePages), + MembersCanCreatePublicPages: getBool(org.MembersCanCreatePublicPages), + MembersCanCreatePrivatePages: getBool(org.MembersCanCreatePrivatePages), + WebCommitSignoffRequired: getBool(org.WebCommitSignoffRequired), + AdvancedSecurityEnabledForNewRepos: getBool(org.AdvancedSecurityEnabledForNewRepos), + DependabotAlertsEnabledForNewRepos: getBool(org.DependabotAlertsEnabledForNewRepos), + DependabotSecurityUpdatesEnabledForNewRepos: getBool(org.DependabotSecurityUpdatesEnabledForNewRepos), + DependencyGraphEnabledForNewRepos: getBool(org.DependencyGraphEnabledForNewRepos), + SecretScanningEnabledForNewRepos: getBool(org.SecretScanningEnabledForNewRepos), + SecretScanningPushProtectionEnabledForNewRepos: getBool(org.SecretScanningPushProtectionEnabledForNewRepos), + SecretScanningValidityChecksEnabled: getBool(org.SecretScanningValidityChecksEnabled), + TwoFactorRequirementEnabled: getBool(org.TwoFactorRequirementEnabled), + } + + if org.CreatedAt != nil && org.CreatedAt.Time != (time.Time{}) { + organization.CreatedAt = org.CreatedAt.Time + } + if org.UpdatedAt != nil && org.UpdatedAt.Time != (time.Time{}) { + organization.UpdatedAt = org.UpdatedAt.Time + } + + if org.Plan != nil { + organization.Plan = OrganizationPlan{ + Name: getString(org.Plan.Name), + Space: getInt(org.Plan.Space), + PrivateRepos: getInt64(org.Plan.PrivateRepos), + FilledSeats: getInt(org.Plan.FilledSeats), + Seats: getInt(org.Plan.Seats), + } + } + + return organization +} diff --git a/pkg/component/application/github/v0/pull_request.go b/pkg/component/application/github/v0/pull_request.go index bdf2bdd87..e29a14d25 100644 --- a/pkg/component/application/github/v0/pull_request.go +++ b/pkg/component/application/github/v0/pull_request.go @@ -6,7 +6,8 @@ import ( "github.com/google/go-github/v62/github" ) -type PullRequestService interface { +// PullRequestsService is the interface for the GitHub pull request service. +type PullRequestsService interface { List(context.Context, string, string, *github.PullRequestListOptions) ([]*github.PullRequest, *github.Response, error) Get(context.Context, string, string, int) (*github.PullRequest, *github.Response, error) ListComments(context.Context, string, string, int, *github.PullRequestListCommentsOptions) ([]*github.PullRequestComment, *github.Response, error) @@ -14,6 +15,7 @@ type PullRequestService interface { ListCommits(context.Context, string, string, int, *github.ListOptions) ([]*github.RepositoryCommit, *github.Response, error) } +// PullRequest is the GitHub pull request object. type PullRequest struct { ID int64 `instill:"id"` Number int `instill:"number"` @@ -31,27 +33,27 @@ type PullRequest struct { } type PullRequestComment struct { - ID *int64 `instill:"id"` - NodeID *string `instill:"node-id"` - InReplyTo *int64 `instill:"in-reply-to-id"` - Body *string `instill:"body"` - Path *string `instill:"path"` - DiffHunk *string `instill:"diff-hunk"` - PullRequestReviewID *int64 `instill:"pull-request-review-id"` - Position *int `instill:"position"` - OriginalPosition *int `instill:"original-position"` - StartLine *int `instill:"start-line"` - Line *int `instill:"line"` - OriginalLine *int `instill:"original-line"` - OriginalStartLine *int `instill:"original-start-line"` - Side *string `instill:"side"` - StartSide *string `instill:"start-side"` - CommitID *string `instill:"commit-id"` - OriginalCommitID *string `instill:"original-commit-id"` - User *User `instill:"user"` - Reactions *Reactions `instill:"reactions"` - CreatedAt *Timestamp `instill:"created-at"` - UpdatedAt *Timestamp `instill:"updated-at"` + ID *int64 `instill:"id"` + NodeID *string `instill:"node-id"` + InReplyTo *int64 `instill:"in-reply-to-id"` + Body *string `instill:"body"` + Path *string `instill:"path"` + DiffHunk *string `instill:"diff-hunk"` + PullRequestReviewID *int64 `instill:"pull-request-review-id"` + Position *int `instill:"position"` + OriginalPosition *int `instill:"original-position"` + StartLine *int `instill:"start-line"` + Line *int `instill:"line"` + OriginalLine *int `instill:"original-line"` + OriginalStartLine *int `instill:"original-start-line"` + Side *string `instill:"side"` + StartSide *string `instill:"start-side"` + CommitID *string `instill:"commit-id"` + OriginalCommitID *string `instill:"original-commit-id"` + User *User `instill:"user"` + Reactions *Reactions `instill:"reactions"` + CreatedAt *github.Timestamp `instill:"created-at"` + UpdatedAt *github.Timestamp `instill:"updated-at"` // AuthorAssociation is the comment author's relationship to the pull request's repository. // Possible values are "COLLABORATOR", "CONTRIBUTOR", "FIRST_TIMER", "FIRST_TIME_CONTRIBUTOR", "MEMBER", "OWNER", or "NONE". AuthorAssociation *string `instill:"author-association"` @@ -83,19 +85,11 @@ func (client *Client) extractPullRequestInformation(ctx context.Context, owner s } resp.Commits = make([]Commit, len(commits)) for idx, commit := range commits { - resp.Commits[idx] = client.extractCommitInformation(ctx, owner, repository, commit, needCommitDetails) + resp.Commits[idx], err = client.extractCommitInformation(ctx, owner, repository, commit, needCommitDetails) + if err != nil { + return PullRequest{}, err + } } } return resp, nil } - -type ListPullRequestsInput struct { - RepoInfo - State string `instill:"state"` - Sort string `instill:"sort"` - Direction string `instill:"direction"` - PageOptions -} -type ListPullRequestsResp struct { - PullRequests []PullRequest `instill:"pull-requests"` -} diff --git a/pkg/component/application/github/v0/repositories.go b/pkg/component/application/github/v0/repositories.go new file mode 100644 index 000000000..ed89c469d --- /dev/null +++ b/pkg/component/application/github/v0/repositories.go @@ -0,0 +1,52 @@ +package github + +import ( + "context" + "fmt" + + "github.com/google/go-github/v62/github" + + "github.com/instill-ai/x/errmsg" +) + +// RepositoriesService is a wrapper around the github.RepositoriesService +type RepositoriesService interface { + GetCommit(ctx context.Context, owner string, repository string, sha string, opts *github.ListOptions) (*github.RepositoryCommit, *github.Response, error) + ListHooks(ctx context.Context, owner string, repository string, opts *github.ListOptions) ([]*github.Hook, *github.Response, error) + GetHook(ctx context.Context, owner string, repository string, id int64) (*github.Hook, *github.Response, error) + CreateHook(ctx context.Context, owner string, repository string, hook *github.Hook) (*github.Hook, *github.Response, error) + DeleteHook(ctx context.Context, owner string, repository string, id int64) (*github.Response, error) + EditHook(ctx context.Context, owner string, repository string, id int64, hook *github.Hook) (*github.Hook, *github.Response, error) +} + +// RepoInfoInterface is an interface for the RepoInfo struct +type RepoInfoInterface interface { + getOwner() (string, error) + getRepository() (string, error) +} + +// RepoInfo is a struct that contains the owner and repository of a GitHub repository +type RepoInfo struct { + Owner string `instill:"owner"` + Repository string `instill:"repository"` +} + +func (info RepoInfo) getOwner() (string, error) { + if info.Owner == "" { + return "", errmsg.AddMessage( + fmt.Errorf("owner not provided"), + "Owner not provided.", + ) + } + return info.Owner, nil +} + +func (info RepoInfo) getRepository() (string, error) { + if info.Repository == "" { + return "", errmsg.AddMessage( + fmt.Errorf("repository not provided"), + "Repository not provided.", + ) + } + return info.Repository, nil +} diff --git a/pkg/component/application/github/v0/response.go b/pkg/component/application/github/v0/response.go new file mode 100644 index 000000000..c35e47dda --- /dev/null +++ b/pkg/component/application/github/v0/response.go @@ -0,0 +1,32 @@ +package github + +import "github.com/google/go-github/v62/github" + +// Response represents pagination information from GitHub API responses +type Response struct { + NextPage int `instill:"next-page"` + PrevPage int `instill:"prev-page"` + FirstPage int `instill:"first-page"` + LastPage int `instill:"last-page"` + NextPageToken string `instill:"next-page-token"` + Cursor string `instill:"cursor"` + Before string `instill:"before"` + After string `instill:"after"` +} + +func (client *Client) extractResponse(oriResponse *github.Response) *Response { + if oriResponse == nil { + return nil + } + + return &Response{ + NextPage: oriResponse.NextPage, + PrevPage: oriResponse.PrevPage, + FirstPage: oriResponse.FirstPage, + LastPage: oriResponse.LastPage, + NextPageToken: oriResponse.NextPageToken, + Cursor: oriResponse.Cursor, + Before: oriResponse.Before, + After: oriResponse.After, + } +} diff --git a/pkg/component/application/github/v0/review_comment.go b/pkg/component/application/github/v0/review_comment.go index 970578408..5d428d01b 100644 --- a/pkg/component/application/github/v0/review_comment.go +++ b/pkg/component/application/github/v0/review_comment.go @@ -8,27 +8,27 @@ import ( // in the instill tags. type ReviewComment struct { github.PullRequestComment - ID *int64 `instill:"id"` - NodeID *string `instill:"node-id"` - InReplyTo *int64 `instill:"in-reply-to-id"` - Body *string `instill:"body"` - Path *string `instill:"path"` - DiffHunk *string `instill:"diff-hunk"` - PullRequestReviewID *int64 `instill:"pull-request-review-id"` - Position *int `instill:"position"` - OriginalPosition *int `instill:"original-position"` - StartLine *int `instill:"start-line"` - Line *int `instill:"line"` - OriginalLine *int `instill:"original-line"` - OriginalStartLine *int `instill:"original-start-line"` - Side *string `instill:"side"` - StartSide *string `instill:"start-side"` - CommitID *string `instill:"commit-id"` - OriginalCommitID *string `instill:"original-commit-id"` - User *User `instill:"user"` - Reactions *Reactions `instill:"reactions"` - CreatedAt *Timestamp `instill:"created-at"` - UpdatedAt *Timestamp `instill:"updated-at"` + ID *int64 `instill:"id"` + NodeID *string `instill:"node-id"` + InReplyTo *int64 `instill:"in-reply-to-id"` + Body *string `instill:"body"` + Path *string `instill:"path"` + DiffHunk *string `instill:"diff-hunk"` + PullRequestReviewID *int64 `instill:"pull-request-review-id"` + Position *int `instill:"position"` + OriginalPosition *int `instill:"original-position"` + StartLine *int `instill:"start-line"` + Line *int `instill:"line"` + OriginalLine *int `instill:"original-line"` + OriginalStartLine *int `instill:"original-start-line"` + Side *string `instill:"side"` + StartSide *string `instill:"start-side"` + CommitID *string `instill:"commit-id"` + OriginalCommitID *string `instill:"original-commit-id"` + User *User `instill:"user"` + Reactions *Reactions `instill:"reactions"` + CreatedAt *github.Timestamp `instill:"created-at"` + UpdatedAt *github.Timestamp `instill:"updated-at"` // AuthorAssociation is the comment author's relationship to the pull request's repository. // Possible values are "COLLABORATOR", "CONTRIBUTOR", "FIRST_TIMER", "FIRST_TIME_CONTRIBUTOR", "MEMBER", "OWNER", or "NONE". AuthorAssociation *string `instill:"author-association"` diff --git a/pkg/component/application/github/v0/task_create_issue_test.go b/pkg/component/application/github/v0/task_create_issue_test.go index fe9d1a8bf..d0b3ddca4 100644 --- a/pkg/component/application/github/v0/task_create_issue_test.go +++ b/pkg/component/application/github/v0/task_create_issue_test.go @@ -17,7 +17,7 @@ func TestComponent_CreateIssueTask(t *testing.T) { input: createIssueInput{ RepoInfo: RepoInfo{ Owner: "test_owner", - Repository: "test_repo", + Repository: "test-repo", }, Title: "This is a fake Issue", Body: "Issue Body", @@ -41,7 +41,7 @@ func TestComponent_CreateIssueTask(t *testing.T) { input: createIssueInput{ RepoInfo: RepoInfo{ Owner: "rate_limit", - Repository: "test_repo", + Repository: "test-repo", }, Title: "This is a fake Issue", Body: "Issue Body", @@ -54,7 +54,7 @@ func TestComponent_CreateIssueTask(t *testing.T) { input: createIssueInput{ RepoInfo: RepoInfo{ Owner: "not_found", - Repository: "test_repo", + Repository: "test-repo", }, Title: "This is a fake Issue", Body: "Issue Body", diff --git a/pkg/component/application/github/v0/task_create_review_comment_test.go b/pkg/component/application/github/v0/task_create_review_comment_test.go index aeaf14c03..0acec1757 100644 --- a/pkg/component/application/github/v0/task_create_review_comment_test.go +++ b/pkg/component/application/github/v0/task_create_review_comment_test.go @@ -18,7 +18,7 @@ func TestComponent_CreateReviewCommentTask(t *testing.T) { input: createReviewCommentInput{ RepoInfo: RepoInfo{ Owner: "test_owner", - Repository: "test_repo", + Repository: "test-repo", }, PRNumber: 1, Comment: PullRequestComment{ @@ -51,7 +51,7 @@ func TestComponent_CreateReviewCommentTask(t *testing.T) { input: createReviewCommentInput{ RepoInfo: RepoInfo{ Owner: "test_owner", - Repository: "test_repo", + Repository: "test-repo", }, PRNumber: 1, Comment: PullRequestComment{ @@ -83,7 +83,7 @@ func TestComponent_CreateReviewCommentTask(t *testing.T) { input: createReviewCommentInput{ RepoInfo: RepoInfo{ Owner: "rate_limit", - Repository: "test_repo", + Repository: "test-repo", }, PRNumber: 1, Comment: PullRequestComment{ @@ -103,7 +103,7 @@ func TestComponent_CreateReviewCommentTask(t *testing.T) { input: createReviewCommentInput{ RepoInfo: RepoInfo{ Owner: "not_found", - Repository: "test_repo", + Repository: "test-repo", }, PRNumber: 1, Comment: PullRequestComment{ @@ -123,7 +123,7 @@ func TestComponent_CreateReviewCommentTask(t *testing.T) { input: createReviewCommentInput{ RepoInfo: RepoInfo{ Owner: "unprocessable_entity", - Repository: "test_repo", + Repository: "test-repo", }, PRNumber: 1, Comment: PullRequestComment{ @@ -143,7 +143,7 @@ func TestComponent_CreateReviewCommentTask(t *testing.T) { input: createReviewCommentInput{ RepoInfo: RepoInfo{ Owner: "test_owner", - Repository: "test_repo", + Repository: "test-repo", }, PRNumber: 1, Comment: PullRequestComment{ diff --git a/pkg/component/application/github/v0/task_create_webhook_test.go b/pkg/component/application/github/v0/task_create_webhook_test.go index 849d87f2c..86bf94745 100644 --- a/pkg/component/application/github/v0/task_create_webhook_test.go +++ b/pkg/component/application/github/v0/task_create_webhook_test.go @@ -18,7 +18,7 @@ func TestComponent_CreateWebHook(t *testing.T) { input: createWebHookInput{ RepoInfo: RepoInfo{ Owner: "test_owner", - Repository: "test_repo", + Repository: "test-repo", }, Events: []string{"push"}, Active: *github.Bool(true), @@ -45,7 +45,7 @@ func TestComponent_CreateWebHook(t *testing.T) { input: createWebHookInput{ RepoInfo: RepoInfo{ Owner: "rate_limit", - Repository: "test_repo", + Repository: "test-repo", }, Events: []string{"push"}, Active: *github.Bool(true), @@ -60,7 +60,7 @@ func TestComponent_CreateWebHook(t *testing.T) { input: createWebHookInput{ RepoInfo: RepoInfo{ Owner: "not_found", - Repository: "test_repo", + Repository: "test-repo", }, Events: []string{"push"}, Active: *github.Bool(true), diff --git a/pkg/component/application/github/v0/task_get_commit.go b/pkg/component/application/github/v0/task_get_commit.go index c291490a6..ae318c568 100644 --- a/pkg/component/application/github/v0/task_get_commit.go +++ b/pkg/component/application/github/v0/task_get_commit.go @@ -17,12 +17,15 @@ func (client *Client) getCommit(ctx context.Context, job *base.Job) error { return err } sha := input.SHA - commit, err := client.getCommitFunc(ctx, owner, repository, sha) + commit, _, err := client.Repositories.GetCommit(ctx, owner, repository, sha, nil) if err != nil { - return err + return addErrMsgToClientError(err) } var output getCommitOutput - output.Commit = client.extractCommitInformation(ctx, owner, repository, commit, true) + output.Commit, err = client.extractCommitInformation(ctx, owner, repository, commit, true) + if err != nil { + return err + } if err := job.Output.WriteData(ctx, output); err != nil { return err } diff --git a/pkg/component/application/github/v0/task_get_commit_test.go b/pkg/component/application/github/v0/task_get_commit_test.go index 666e6b741..4a4be131d 100644 --- a/pkg/component/application/github/v0/task_get_commit_test.go +++ b/pkg/component/application/github/v0/task_get_commit_test.go @@ -17,7 +17,7 @@ func TestComponent_GetCommitTask(t *testing.T) { input: getCommitInput{ RepoInfo: RepoInfo{ Owner: "test_owner", - Repository: "test_repo", + Repository: "test-repo", }, SHA: "commitSHA", }, @@ -50,7 +50,7 @@ func TestComponent_GetCommitTask(t *testing.T) { input: getCommitInput{ RepoInfo: RepoInfo{ Owner: "rate_limit", - Repository: "test_repo", + Repository: "test-repo", }, SHA: "commitSHA", }, diff --git a/pkg/component/application/github/v0/task_get_issue.go b/pkg/component/application/github/v0/task_get_issue.go index b6e66345e..c1836cd68 100644 --- a/pkg/component/application/github/v0/task_get_issue.go +++ b/pkg/component/application/github/v0/task_get_issue.go @@ -18,9 +18,9 @@ func (client *Client) getIssue(ctx context.Context, job *base.Job) error { } issueNumber := input.IssueNumber - issue, err := client.getIssueFunc(ctx, owner, repository, issueNumber) + issue, _, err := client.Issues.Get(ctx, owner, repository, issueNumber) if err != nil { - return err + return addErrMsgToClientError(err) } var output getIssueOutput diff --git a/pkg/component/application/github/v0/task_get_issue_test.go b/pkg/component/application/github/v0/task_get_issue_test.go index 8e4865b5e..4fa0d9d56 100644 --- a/pkg/component/application/github/v0/task_get_issue_test.go +++ b/pkg/component/application/github/v0/task_get_issue_test.go @@ -17,7 +17,7 @@ func TestComponent_GetIssueTask(t *testing.T) { input: getIssueInput{ RepoInfo: RepoInfo{ Owner: "test_owner", - Repository: "test_repo", + Repository: "test-repo", }, IssueNumber: 1, }, @@ -40,7 +40,7 @@ func TestComponent_GetIssueTask(t *testing.T) { input: getIssueInput{ RepoInfo: RepoInfo{ Owner: "rate_limit", - Repository: "test_repo", + Repository: "test-repo", }, IssueNumber: 1, }, @@ -52,7 +52,7 @@ func TestComponent_GetIssueTask(t *testing.T) { input: getIssueInput{ RepoInfo: RepoInfo{ Owner: "not_found", - Repository: "test_repo", + Repository: "test-repo", }, IssueNumber: 1, }, diff --git a/pkg/component/application/github/v0/task_get_organization.go b/pkg/component/application/github/v0/task_get_organization.go new file mode 100644 index 000000000..3ddd66fd7 --- /dev/null +++ b/pkg/component/application/github/v0/task_get_organization.go @@ -0,0 +1,35 @@ +package github + +import ( + "context" + "fmt" + + "github.com/google/go-github/v62/github" + "github.com/instill-ai/pipeline-backend/pkg/component/base" +) + +func (client *Client) getOrganization(ctx context.Context, job *base.Job) error { + var input getOrganizationInput + if err := job.Input.ReadData(ctx, &input); err != nil { + return fmt.Errorf("reading input data: %w", err) + } + + var org *github.Organization + var err error + + if input.OrgID != 0 { + org, _, err = client.Organizations.GetByID(ctx, input.OrgID) + } else { + org, _, err = client.Organizations.Get(ctx, input.OrgName) + } + if err != nil { + return addErrMsgToClientError(err) + } + + var output getOrganizationOutput + output.Organization = client.extractOrganization(org) + if err := job.Output.WriteData(ctx, output); err != nil { + return err + } + return nil +} diff --git a/pkg/component/application/github/v0/task_get_organization_test.go b/pkg/component/application/github/v0/task_get_organization_test.go new file mode 100644 index 000000000..779202fa6 --- /dev/null +++ b/pkg/component/application/github/v0/task_get_organization_test.go @@ -0,0 +1,148 @@ +package github + +import ( + "testing" + "time" + + "go.uber.org/zap" + "google.golang.org/protobuf/types/known/structpb" + + "github.com/instill-ai/pipeline-backend/pkg/component/base" +) + +func TestComponent_GetOrganizationTask(t *testing.T) { + testCases := []TaskCase[getOrganizationInput, getOrganizationOutput]{ + { + _type: "ok", + name: "get organization by name", + input: getOrganizationInput{ + OrgName: "test-org", + }, + wantOutput: getOrganizationOutput{ + Organization: Organization{ + Login: "test-org", + ID: 1, + NodeID: "node1", + URL: "https://api.github.com/orgs/test-org", + ReposURL: "https://api.github.com/orgs/test-org/repos", + EventsURL: "https://api.github.com/orgs/test-org/events", + HooksURL: "https://api.github.com/orgs/test-org/hooks", + IssuesURL: "https://api.github.com/orgs/test-org/issues", + MembersURL: "https://api.github.com/orgs/test-org/members{/member}", + PublicMembersURL: "https://api.github.com/orgs/test-org/public_members{/member}", + AvatarURL: "https://github.com/images/error/octocat_happy.gif", + Description: "A great organization", + Name: "test-org", + Company: "GitHub", + Blog: "https://github.com/blog", + Location: "San Francisco", + Email: "octocat@github.com", + TwitterUsername: "github", + IsVerified: true, + HasOrganizationProjects: true, + HasRepositoryProjects: true, + PublicRepos: 2, + PublicGists: 1, + Followers: 20, + Following: 0, + HTMLURL: "https://github.com/octocat", + Type: "Organization", + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + TotalPrivateRepos: 50, + OwnedPrivateRepos: 45, + PrivateGists: 10, + DiskUsage: 50000, + Collaborators: 25, + BillingEmail: "billing@test-org.com", + TwoFactorRequirementEnabled: true, + }, + }, + }, + { + _type: "ok", + name: "get organization by ID", + input: getOrganizationInput{ + OrgID: 1, + }, + wantOutput: getOrganizationOutput{ + Organization: Organization{ + Login: "test-org", + ID: 1, + NodeID: "node1", + URL: "https://api.github.com/orgs/test-org", + ReposURL: "https://api.github.com/orgs/test-org/repos", + EventsURL: "https://api.github.com/orgs/test-org/events", + HooksURL: "https://api.github.com/orgs/test-org/hooks", + IssuesURL: "https://api.github.com/orgs/test-org/issues", + MembersURL: "https://api.github.com/orgs/test-org/members{/member}", + PublicMembersURL: "https://api.github.com/orgs/test-org/public_members{/member}", + AvatarURL: "https://github.com/images/error/octocat_happy.gif", + Description: "A great organization", + Name: "test-org", + Company: "GitHub", + Blog: "https://github.com/blog", + Location: "San Francisco", + Email: "octocat@github.com", + TwitterUsername: "github", + IsVerified: true, + HasOrganizationProjects: true, + HasRepositoryProjects: true, + PublicRepos: 2, + PublicGists: 1, + Followers: 20, + Following: 0, + HTMLURL: "https://github.com/octocat", + Type: "Organization", + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + TotalPrivateRepos: 50, + OwnedPrivateRepos: 45, + PrivateGists: 10, + DiskUsage: 50000, + Collaborators: 25, + BillingEmail: "billing@test-org.com", + TwoFactorRequirementEnabled: true, + }, + }, + }, + { + _type: "nok", + name: "403 API rate limit exceeded", + input: getOrganizationInput{ + OrgName: "rate_limit", + }, + wantErr: `403 API rate limit exceeded`, + }, + { + _type: "nok", + name: "404 Not Found", + input: getOrganizationInput{ + OrgName: "not_found", + }, + wantErr: `404 Not Found`, + }, + } + + e := &execution{ + client: *MockGithubClient, + ComponentExecution: base.ComponentExecution{ + Task: taskGetOrganization, + Component: Init(base.Component{Logger: zap.NewNop()}), + SystemVariables: nil, + Setup: func() *structpb.Struct { + setup, err := structpb.NewStruct(map[string]any{ + "token": token, + }) + if err != nil { + t.Fatalf("failed to create setup: %v", err) + } + return setup + }(), + }, + } + + e.execute = e.client.getOrganization + + taskTesting(testCases, e, t) +} diff --git a/pkg/component/application/github/v0/task_get_pull_request_test.go b/pkg/component/application/github/v0/task_get_pull_request_test.go index a39dcaadf..183610ffa 100644 --- a/pkg/component/application/github/v0/task_get_pull_request_test.go +++ b/pkg/component/application/github/v0/task_get_pull_request_test.go @@ -17,7 +17,7 @@ func TestComponent_GetPullRequestTask(t *testing.T) { input: getPullRequestInput{ RepoInfo: RepoInfo{ Owner: "test_owner", - Repository: "test_repo", + Repository: "test-repo", }, PRNumber: 1, }, @@ -47,7 +47,7 @@ func TestComponent_GetPullRequestTask(t *testing.T) { }, }, }, - DiffURL: "https://fake-github.com/test_owner/test_repo/pull/1.diff", + DiffURL: "https://fake-github.com/test_owner/test-repo/pull/1.diff", Head: "headSHA", ID: 1, Number: 1, @@ -65,7 +65,7 @@ func TestComponent_GetPullRequestTask(t *testing.T) { input: getPullRequestInput{ RepoInfo: RepoInfo{ Owner: "test_owner", - Repository: "test_repo", + Repository: "test-repo", }, PRNumber: 0, }, @@ -95,7 +95,7 @@ func TestComponent_GetPullRequestTask(t *testing.T) { }, }, }, - DiffURL: "https://fake-github.com/test_owner/test_repo/pull/1.diff", + DiffURL: "https://fake-github.com/test_owner/test-repo/pull/1.diff", Head: "headSHA", ID: 1, Number: 1, @@ -113,7 +113,7 @@ func TestComponent_GetPullRequestTask(t *testing.T) { input: getPullRequestInput{ RepoInfo: RepoInfo{ Owner: "rate_limit", - Repository: "test_repo", + Repository: "test-repo", }, PRNumber: 1, }, @@ -125,7 +125,7 @@ func TestComponent_GetPullRequestTask(t *testing.T) { input: getPullRequestInput{ RepoInfo: RepoInfo{ Owner: "not_found", - Repository: "test_repo", + Repository: "test-repo", }, PRNumber: 1, }, diff --git a/pkg/component/application/github/v0/task_get_user.go b/pkg/component/application/github/v0/task_get_user.go new file mode 100644 index 000000000..07780ca9d --- /dev/null +++ b/pkg/component/application/github/v0/task_get_user.go @@ -0,0 +1,36 @@ +package github + +import ( + "context" + "fmt" + + "github.com/google/go-github/v62/github" + + "github.com/instill-ai/pipeline-backend/pkg/component/base" +) + +func (client *Client) getUser(ctx context.Context, job *base.Job) error { + var input getUserInput + if err := job.Input.ReadData(ctx, &input); err != nil { + return fmt.Errorf("reading input data: %w", err) + } + + var user *github.User + var err error + + if input.UserID != 0 { + user, _, err = client.Users.GetByID(ctx, input.UserID) + } else { + user, _, err = client.Users.Get(ctx, input.Username) + } + if err != nil { + return addErrMsgToClientError(err) + } + + var output getUserOutput + output.User = client.extractUser(user) + if err := job.Output.WriteData(ctx, output); err != nil { + return err + } + return nil +} diff --git a/pkg/component/application/github/v0/task_get_user_test.go b/pkg/component/application/github/v0/task_get_user_test.go new file mode 100644 index 000000000..74258e577 --- /dev/null +++ b/pkg/component/application/github/v0/task_get_user_test.go @@ -0,0 +1,174 @@ +package github + +import ( + "testing" + + "go.uber.org/zap" + "google.golang.org/protobuf/types/known/structpb" + + "github.com/google/go-github/v62/github" + "github.com/instill-ai/pipeline-backend/pkg/component/base" +) + +func TestComponent_GetUserTask(t *testing.T) { + testCases := []TaskCase[getUserInput, getUserOutput]{ + { + _type: "ok", + name: "get user by username", + input: getUserInput{ + Username: "test-user", + }, + wantOutput: getUserOutput{ + User: User{ + Login: github.String("test-user"), + ID: github.Int64(1), + NodeID: github.String("node1"), + AvatarURL: github.String("https://avatar.url"), + HTMLURL: github.String("https://github.com/test-user"), + GravatarID: github.String(""), + Name: github.String("Test User"), + Company: github.String("Test Company"), + Blog: github.String("https://blog.com"), + Location: github.String("Test Location"), + Email: github.String("test@example.com"), + Hireable: github.Bool(true), + Bio: github.String("Test Bio"), + TwitterUsername: github.String("testuser"), + PublicRepos: github.Int(10), + PublicGists: github.Int(5), + Followers: github.Int(100), + Following: github.Int(50), + CreatedAt: &github.Timestamp{}, + UpdatedAt: &github.Timestamp{}, + SuspendedAt: &github.Timestamp{}, + Type: github.String("User"), + SiteAdmin: github.Bool(false), + TotalPrivateRepos: github.Int64(2), + OwnedPrivateRepos: github.Int64(2), + PrivateGists: github.Int(1), + DiskUsage: github.Int(1000), + Collaborators: github.Int(3), + TwoFactorAuthentication: github.Bool(true), + Plan: &github.Plan{ + Name: github.String("pro"), + Space: github.Int(100), + Collaborators: github.Int(10), + PrivateRepos: github.Int64(50), + }, + LdapDn: github.String(""), + URL: github.String("https://api.github.com/users/test-user"), + EventsURL: github.String("https://api.github.com/users/test-user/events{/privacy}"), + FollowingURL: github.String("https://api.github.com/users/test-user/following{/other_user}"), + FollowersURL: github.String("https://api.github.com/users/test-user/followers"), + GistsURL: github.String("https://api.github.com/users/test-user/gists{/gist_id}"), + OrganizationsURL: github.String("https://api.github.com/users/test-user/orgs"), + ReceivedEventsURL: github.String("https://api.github.com/users/test-user/received_events"), + ReposURL: github.String("https://api.github.com/users/test-user/repos"), + StarredURL: github.String("https://api.github.com/users/test-user/starred{/owner}{/repo}"), + SubscriptionsURL: github.String("https://api.github.com/users/test-user/subscriptions"), + TextMatches: nil, + Permissions: nil, + RoleName: nil, + }, + }, + }, + { + _type: "ok", + name: "get user by ID", + input: getUserInput{ + UserID: 1, + }, + wantOutput: getUserOutput{ + User: User{ + Login: github.String("test-user"), + ID: github.Int64(1), + NodeID: github.String("node1"), + AvatarURL: github.String("https://avatar.url"), + HTMLURL: github.String("https://github.com/test-user"), + GravatarID: github.String(""), + Name: github.String("Test User"), + Company: github.String("Test Company"), + Blog: github.String("https://blog.com"), + Location: github.String("Test Location"), + Email: github.String("test@example.com"), + Hireable: github.Bool(true), + Bio: github.String("Test Bio"), + TwitterUsername: github.String("testuser"), + PublicRepos: github.Int(10), + PublicGists: github.Int(5), + Followers: github.Int(100), + Following: github.Int(50), + CreatedAt: &github.Timestamp{}, + UpdatedAt: &github.Timestamp{}, + SuspendedAt: &github.Timestamp{}, + Type: github.String("User"), + SiteAdmin: github.Bool(false), + TotalPrivateRepos: github.Int64(2), + OwnedPrivateRepos: github.Int64(2), + PrivateGists: github.Int(1), + DiskUsage: github.Int(1000), + Collaborators: github.Int(3), + TwoFactorAuthentication: github.Bool(true), + Plan: &github.Plan{ + Name: github.String("pro"), + Space: github.Int(100), + Collaborators: github.Int(10), + PrivateRepos: github.Int64(50), + }, + LdapDn: github.String(""), + URL: github.String("https://api.github.com/users/test-user"), + EventsURL: github.String("https://api.github.com/users/test-user/events{/privacy}"), + FollowingURL: github.String("https://api.github.com/users/test-user/following{/other_user}"), + FollowersURL: github.String("https://api.github.com/users/test-user/followers"), + GistsURL: github.String("https://api.github.com/users/test-user/gists{/gist_id}"), + OrganizationsURL: github.String("https://api.github.com/users/test-user/orgs"), + ReceivedEventsURL: github.String("https://api.github.com/users/test-user/received_events"), + ReposURL: github.String("https://api.github.com/users/test-user/repos"), + StarredURL: github.String("https://api.github.com/users/test-user/starred{/owner}{/repo}"), + SubscriptionsURL: github.String("https://api.github.com/users/test-user/subscriptions"), + TextMatches: nil, + Permissions: nil, + RoleName: nil, + }, + }, + }, + { + _type: "nok", + name: "403 API rate limit exceeded", + input: getUserInput{ + Username: "rate_limit", + }, + wantErr: `403 API rate limit exceeded`, + }, + { + _type: "nok", + name: "404 Not Found", + input: getUserInput{ + Username: "not_found", + }, + wantErr: `404 Not Found`, + }, + } + + e := &execution{ + client: *MockGithubClient, + ComponentExecution: base.ComponentExecution{ + Task: taskGetUser, + Component: Init(base.Component{Logger: zap.NewNop()}), + SystemVariables: nil, + Setup: func() *structpb.Struct { + setup, err := structpb.NewStruct(map[string]any{ + "token": token, + }) + if err != nil { + t.Fatalf("failed to create setup: %v", err) + } + return setup + }(), + }, + } + + e.execute = e.client.getUser + + taskTesting(testCases, e, t) +} diff --git a/pkg/component/application/github/v0/task_list_issues.go b/pkg/component/application/github/v0/task_list_issues.go index 3aee45cba..83c0a3fa8 100644 --- a/pkg/component/application/github/v0/task_list_issues.go +++ b/pkg/component/application/github/v0/task_list_issues.go @@ -15,6 +15,7 @@ func (client *Client) listIssues(ctx context.Context, job *base.Job) error { if err := job.Input.ReadData(ctx, &input); err != nil { return fmt.Errorf("reading input data: %w", err) } + owner, repository, err := parseTargetRepo(input) if err != nil { return err @@ -42,7 +43,7 @@ func (client *Client) listIssues(ctx context.Context, job *base.Job) error { opts.Mentioned = "" } - issues, _, err := client.Issues.ListByRepo(ctx, owner, repository, opts) + issues, resp, err := client.Issues.ListByRepo(ctx, owner, repository, opts) if err != nil { return addErrMsgToClientError(err) } @@ -56,8 +57,11 @@ func (client *Client) listIssues(ctx context.Context, job *base.Job) error { if input.NoPullRequest { issueList = filterOutPullRequests(issueList) } - var output listIssuesOutput - output.Issues = issueList + + output := listIssuesOutput{ + Issues: issueList, + Response: client.extractResponse(resp), + } if err := job.Output.WriteData(ctx, output); err != nil { return err } diff --git a/pkg/component/application/github/v0/task_list_issues_test.go b/pkg/component/application/github/v0/task_list_issues_test.go index a4b972736..b32e4ae5a 100644 --- a/pkg/component/application/github/v0/task_list_issues_test.go +++ b/pkg/component/application/github/v0/task_list_issues_test.go @@ -13,11 +13,11 @@ func TestComponent_ListIssuesTask(t *testing.T) { testCases := []TaskCase[listIssuesInput, listIssuesOutput]{ { _type: "ok", - name: "get all issues", + name: "list all issues", input: listIssuesInput{ RepoInfo: RepoInfo{ - Owner: "test_owner", - Repository: "test_repo", + Owner: "non-paginated", + Repository: "test-repo", }, State: "open", Direction: "asc", @@ -37,6 +37,107 @@ func TestComponent_ListIssuesTask(t *testing.T) { Labels: []string{"label1", "label2"}, }, }, + Response: &Response{}, + }, + }, + { + _type: "ok", + name: "paginated issues", + input: listIssuesInput{ + PageOptions: PageOptions{ + Page: 2, + PerPage: 2, + }, + RepoInfo: RepoInfo{ + Owner: "paginated", + Repository: "test-repo", + }, + State: "open", + Direction: "asc", + Sort: "created", + Since: "2021-01-01", + }, + wantOutput: listIssuesOutput{ + Issues: []Issue{ + { + Number: 3, + Title: "This is a fake Issue #3", + State: "open", + Body: "Issue Body #3", + Assignee: "assignee3", + Assignees: []string{"assignee3_1", "assignee3_2"}, + Labels: []string{"label3_1", "label3_2"}, + }, + { + Number: 4, + Title: "This is a fake Issue #4", + State: "open", + Body: "Issue Body #4", + Assignee: "assignee4", + Assignees: []string{"assignee4_1", "assignee4_2"}, + Labels: []string{"label4_1", "label4_2"}, + }, + }, + Response: &Response{ + NextPage: 3, + PrevPage: 1, + FirstPage: 1, + LastPage: 5, + NextPageToken: "page_3", + Cursor: "cursor_2", + Before: "before_2", + After: "after_2", + }, + }, + }, + { + _type: "ok", + name: "paginated issues last page", + input: listIssuesInput{ + PageOptions: PageOptions{ + Page: 5, + PerPage: 2, + }, + RepoInfo: RepoInfo{ + Owner: "paginated", + Repository: "test-repo", + }, + State: "open", + Direction: "asc", + Sort: "created", + Since: "2021-01-01", + }, + wantOutput: listIssuesOutput{ + Issues: []Issue{ + { + Number: 9, + Title: "This is a fake Issue #9", + State: "open", + Body: "Issue Body #9", + Assignee: "assignee9", + Assignees: []string{"assignee9_1", "assignee9_2"}, + Labels: []string{"label9_1", "label9_2"}, + }, + { + Number: 10, + Title: "This is a fake Issue #10", + State: "open", + Body: "Issue Body #10", + Assignee: "assignee10", + Assignees: []string{"assignee10_1", "assignee10_2"}, + Labels: []string{"label10_1", "label10_2"}, + }, + }, + Response: &Response{ + NextPage: 0, + PrevPage: 4, + FirstPage: 1, + LastPage: 5, + NextPageToken: "", + Cursor: "cursor_5", + Before: "before_5", + After: "after_5", + }, }, }, { @@ -45,7 +146,7 @@ func TestComponent_ListIssuesTask(t *testing.T) { input: listIssuesInput{ RepoInfo: RepoInfo{ Owner: "rate_limit", - Repository: "test_repo", + Repository: "test-repo", }, State: "open", Direction: "asc", @@ -61,7 +162,7 @@ func TestComponent_ListIssuesTask(t *testing.T) { input: listIssuesInput{ RepoInfo: RepoInfo{ Owner: "not_found", - Repository: "test_repo", + Repository: "test-repo", }, State: "open", Direction: "asc", @@ -77,7 +178,7 @@ func TestComponent_ListIssuesTask(t *testing.T) { input: listIssuesInput{ RepoInfo: RepoInfo{ Owner: "not_found", - Repository: "test_repo", + Repository: "test-repo", }, State: "open", Direction: "asc", diff --git a/pkg/component/application/github/v0/task_list_pull_requests.go b/pkg/component/application/github/v0/task_list_pull_requests.go index 5679395fa..b2b4ef06d 100644 --- a/pkg/component/application/github/v0/task_list_pull_requests.go +++ b/pkg/component/application/github/v0/task_list_pull_requests.go @@ -2,7 +2,6 @@ package github import ( "context" - "fmt" "github.com/google/go-github/v62/github" @@ -31,7 +30,7 @@ func (client *Client) listPullRequests(ctx context.Context, job *base.Job) error PerPage: min(input.PerPage, 100), // GitHub API only allows 100 per page }, } - prs, _, err := client.PullRequests.List(ctx, owner, repository, opts) + prs, resp, err := client.PullRequests.List(ctx, owner, repository, opts) if err != nil { return addErrMsgToClientError(err) } @@ -45,6 +44,7 @@ func (client *Client) listPullRequests(ctx context.Context, job *base.Job) error output := listPullRequestsOutput{ PullRequests: PullRequests, + Response: client.extractResponse(resp), } if err := job.Output.WriteData(ctx, output); err != nil { return err diff --git a/pkg/component/application/github/v0/task_list_pull_requests_test.go b/pkg/component/application/github/v0/task_list_pull_requests_test.go index 6c1970aa2..a089d7873 100644 --- a/pkg/component/application/github/v0/task_list_pull_requests_test.go +++ b/pkg/component/application/github/v0/task_list_pull_requests_test.go @@ -16,8 +16,8 @@ func TestComponent_ListPullRequestsTask(t *testing.T) { name: "list all pull requests", input: listPullRequestsInput{ RepoInfo: RepoInfo{ - Owner: "test_owner", - Repository: "test_repo", + Owner: "non-paginated", + Repository: "test-repo", }, State: "open", Direction: "asc", @@ -26,15 +26,9 @@ func TestComponent_ListPullRequestsTask(t *testing.T) { wantOutput: listPullRequestsOutput{ PullRequests: []PullRequest{ { - Base: "baseSHA", - Body: "PR Body", - Commits: []Commit{ - { - Message: "This is a fake commit", - SHA: "commitSHA", - }, - }, - DiffURL: "https://fake-github.com/test_owner/test_repo/pull/1.diff", + Base: "baseSHA", + Body: "PR Body", + DiffURL: "https://fake-github.com/non-paginated/test-repo/pull/1.diff", Head: "headSHA", ID: 1, Number: 1, @@ -43,7 +37,151 @@ func TestComponent_ListPullRequestsTask(t *testing.T) { ReviewCommentsNum: 2, State: "open", Title: "This is a fake PR", + Commits: []Commit{ + { + Message: "This is a fake commit", + SHA: "commitSHA", + }, + }, + }, + }, + Response: &Response{}, + }, + }, + { + _type: "ok", + name: "paginated pull requests", + input: listPullRequestsInput{ + RepoInfo: RepoInfo{ + Owner: "paginated", + Repository: "test-repo", + }, + State: "open", + Direction: "asc", + Sort: "created", + PageOptions: PageOptions{ + Page: 2, + PerPage: 2, + }, + }, + wantOutput: listPullRequestsOutput{ + PullRequests: []PullRequest{ + { + Base: "baseSHA3", + Body: "PR Body #3", + DiffURL: "https://fake-github.com/paginated/test-repo/pull/3.diff", + Head: "headSHA3", + ID: 3, + Number: 3, + CommentsNum: 3, + CommitsNum: 3, + ReviewCommentsNum: 6, + State: "open", + Title: "This is a fake PR #3", + Commits: []Commit{ + { + Message: "This is a fake commit", + SHA: "commitSHA", + }, + }, + }, + { + Base: "baseSHA4", + Body: "PR Body #4", + DiffURL: "https://fake-github.com/paginated/test-repo/pull/4.diff", + Head: "headSHA4", + ID: 4, + Number: 4, + CommentsNum: 4, + CommitsNum: 4, + ReviewCommentsNum: 8, + State: "open", + Title: "This is a fake PR #4", + Commits: []Commit{ + { + Message: "This is a fake commit", + SHA: "commitSHA", + }, + }, + }, + }, + Response: &Response{ + NextPage: 3, + PrevPage: 1, + FirstPage: 1, + LastPage: 5, + NextPageToken: "page_3", + Cursor: "cursor_2", + Before: "before_2", + After: "after_2", + }, + }, + }, + { + _type: "ok", + name: "paginated pull requests last page", + input: listPullRequestsInput{ + RepoInfo: RepoInfo{ + Owner: "paginated", + Repository: "test-repo", + }, + State: "open", + Direction: "asc", + Sort: "created", + PageOptions: PageOptions{ + Page: 5, + PerPage: 2, + }, + }, + wantOutput: listPullRequestsOutput{ + PullRequests: []PullRequest{ + { + Base: "baseSHA9", + Body: "PR Body #9", + DiffURL: "https://fake-github.com/paginated/test-repo/pull/9.diff", + Head: "headSHA9", + ID: 9, + Number: 9, + CommentsNum: 9, + CommitsNum: 9, + ReviewCommentsNum: 18, + State: "open", + Title: "This is a fake PR #9", + Commits: []Commit{ + { + Message: "This is a fake commit", + SHA: "commitSHA", + }, + }, }, + { + Base: "baseSHA10", + Body: "PR Body #10", + DiffURL: "https://fake-github.com/paginated/test-repo/pull/10.diff", + Head: "headSHA10", + ID: 10, + Number: 10, + CommentsNum: 10, + CommitsNum: 10, + ReviewCommentsNum: 20, + State: "open", + Title: "This is a fake PR #10", + Commits: []Commit{ + { + Message: "This is a fake commit", + SHA: "commitSHA", + }, + }, + }, + }, + Response: &Response{ + NextPage: 0, + PrevPage: 4, + FirstPage: 1, + LastPage: 5, + Cursor: "cursor_5", + Before: "before_5", + After: "after_5", }, }, }, @@ -53,7 +191,7 @@ func TestComponent_ListPullRequestsTask(t *testing.T) { input: listPullRequestsInput{ RepoInfo: RepoInfo{ Owner: "rate_limit", - Repository: "test_repo", + Repository: "test-repo", }, State: "open", Direction: "asc", @@ -67,7 +205,7 @@ func TestComponent_ListPullRequestsTask(t *testing.T) { input: listPullRequestsInput{ RepoInfo: RepoInfo{ Owner: "not_found", - Repository: "test_repo", + Repository: "test-repo", }, State: "open", Direction: "asc", @@ -81,7 +219,7 @@ func TestComponent_ListPullRequestsTask(t *testing.T) { input: listPullRequestsInput{ RepoInfo: RepoInfo{ Owner: "no_pr", - Repository: "test_repo", + Repository: "test-repo", }, State: "open", Direction: "asc", diff --git a/pkg/component/application/github/v0/task_list_review_comments.go b/pkg/component/application/github/v0/task_list_review_comments.go index d9ab13a3c..d0a5d6c17 100644 --- a/pkg/component/application/github/v0/task_list_review_comments.go +++ b/pkg/component/application/github/v0/task_list_review_comments.go @@ -15,7 +15,7 @@ import ( // // * This only works for public repositories. func (client *Client) listReviewComments(ctx context.Context, job *base.Job) error { - var input listReviewCommentsInput + var input listReviewCommentsInput if err := job.Input.ReadData(ctx, &input); err != nil { return fmt.Errorf("reading input data: %w", err) } @@ -41,7 +41,7 @@ func (client *Client) listReviewComments(ctx context.Context, job *base.Job) err opts.Since = sinceTime } number := input.PRNumber - comments, _, err := client.PullRequests.ListComments(ctx, owner, repository, number, opts) + comments, resp, err := client.PullRequests.ListComments(ctx, owner, repository, number, opts) if err != nil { return addErrMsgToClientError(err) } @@ -50,8 +50,10 @@ func (client *Client) listReviewComments(ctx context.Context, job *base.Job) err for i, comment := range comments { reviewComments[i] = extractReviewCommentInformation(comment) } - var output listReviewCommentsOutput - output.ReviewComments = reviewComments + output := listReviewCommentsOutput{ + ReviewComments: reviewComments, + Response: client.extractResponse(resp), + } if err := job.Output.WriteData(ctx, output); err != nil { return err } diff --git a/pkg/component/application/github/v0/task_list_review_comments_test.go b/pkg/component/application/github/v0/task_list_review_comments_test.go index 4566c0513..6f89de81d 100644 --- a/pkg/component/application/github/v0/task_list_review_comments_test.go +++ b/pkg/component/application/github/v0/task_list_review_comments_test.go @@ -14,11 +14,11 @@ func TestComponent_ListReviewCommentsTask(t *testing.T) { testCases := []TaskCase[listReviewCommentsInput, listReviewCommentsOutput]{ { _type: "ok", - name: "get review comments", + name: "list review comments", input: listReviewCommentsInput{ RepoInfo: RepoInfo{ - Owner: "test_owner", - Repository: "test_repo", + Owner: "non-paginated", + Repository: "test-repo", }, PRNumber: 1, Sort: "created", @@ -34,6 +34,119 @@ func TestComponent_ListReviewCommentsTask(t *testing.T) { }, }, }, + Response: &Response{}, + }, + }, + { + _type: "ok", + name: "non-paginated review comments", + input: listReviewCommentsInput{ + RepoInfo: RepoInfo{ + Owner: "non-paginated", + Repository: "test-repo", + }, + PRNumber: 1, + Sort: "created", + Direction: "asc", + Since: "2021-01-01", + }, + wantOutput: listReviewCommentsOutput{ + ReviewComments: []ReviewComment{ + { + PullRequestComment: github.PullRequestComment{ + Body: github.String("This is a fake comment"), + ID: github.Int64(1), + }, + }, + }, + Response: &Response{}, + }, + }, + { + _type: "ok", + name: "paginated review comments", + input: listReviewCommentsInput{ + RepoInfo: RepoInfo{ + Owner: "paginated", + Repository: "test-repo", + }, + PRNumber: 1, + Sort: "created", + Direction: "asc", + Since: "2021-01-01", + PageOptions: PageOptions{ + Page: 2, + PerPage: 2, + }, + }, + wantOutput: listReviewCommentsOutput{ + ReviewComments: []ReviewComment{ + { + PullRequestComment: github.PullRequestComment{ + Body: github.String("This is a fake comment #3"), + ID: github.Int64(3), + }, + }, + { + PullRequestComment: github.PullRequestComment{ + Body: github.String("This is a fake comment #4"), + ID: github.Int64(4), + }, + }, + }, + Response: &Response{ + NextPage: 3, + PrevPage: 1, + FirstPage: 1, + LastPage: 5, + NextPageToken: "page_3", + Cursor: "cursor_2", + Before: "before_2", + After: "after_2", + }, + }, + }, + { + _type: "ok", + name: "paginated review comments last page", + input: listReviewCommentsInput{ + RepoInfo: RepoInfo{ + Owner: "paginated", + Repository: "test-repo", + }, + PRNumber: 1, + Sort: "created", + Direction: "asc", + Since: "2021-01-01", + PageOptions: PageOptions{ + Page: 5, + PerPage: 2, + }, + }, + wantOutput: listReviewCommentsOutput{ + ReviewComments: []ReviewComment{ + { + PullRequestComment: github.PullRequestComment{ + Body: github.String("This is a fake comment #9"), + ID: github.Int64(9), + }, + }, + { + PullRequestComment: github.PullRequestComment{ + Body: github.String("This is a fake comment #10"), + ID: github.Int64(10), + }, + }, + }, + Response: &Response{ + NextPage: 0, + PrevPage: 4, + FirstPage: 1, + LastPage: 5, + Cursor: "cursor_5", + Before: "before_5", + After: "after_5", + }, }, }, { @@ -42,7 +155,7 @@ func TestComponent_ListReviewCommentsTask(t *testing.T) { input: listReviewCommentsInput{ RepoInfo: RepoInfo{ Owner: "rate_limit", - Repository: "test_repo", + Repository: "test-repo", }, PRNumber: 1, Sort: "created", @@ -57,7 +170,7 @@ func TestComponent_ListReviewCommentsTask(t *testing.T) { input: listReviewCommentsInput{ RepoInfo: RepoInfo{ Owner: "not_found", - Repository: "test_repo", + Repository: "test-repo", }, PRNumber: 1, Sort: "created", @@ -72,7 +185,7 @@ func TestComponent_ListReviewCommentsTask(t *testing.T) { input: listReviewCommentsInput{ RepoInfo: RepoInfo{ Owner: "not_found", - Repository: "test_repo", + Repository: "test-repo", }, PRNumber: 1, Sort: "created", diff --git a/pkg/component/application/github/v0/user.go b/pkg/component/application/github/v0/user.go deleted file mode 100644 index e842ff604..000000000 --- a/pkg/component/application/github/v0/user.go +++ /dev/null @@ -1,86 +0,0 @@ -package github - -import ( - "time" -) - -type User struct { - Login *string `instill:"login"` - ID *int64 `instill:"id"` - NodeID *string `instill:"node-id"` - AvatarURL *string `instill:"avatar-url"` - HTMLURL *string `instill:"html-url"` - GravatarID *string `instill:"gravatar-id"` - Name *string `instill:"name"` - Company *string `instill:"company"` - Blog *string `instill:"blog"` - Location *string `instill:"location"` - Email *string `instill:"email"` - Hireable *bool `instill:"hireable"` - Bio *string `instill:"bio"` - TwitterUsername *string `instill:"twitter-username"` - PublicRepos *int `instill:"public-repos"` - PublicGists *int `instill:"public-gists"` - Followers *int `instill:"followers"` - Following *int `instill:"following"` - CreatedAt *Timestamp `instill:"created-at"` - UpdatedAt *Timestamp `instill:"updated-at"` - SuspendedAt *Timestamp `instill:"suspended-at"` - Type *string `instill:"type"` - SiteAdmin *bool `instill:"site-admin"` - TotalPrivateRepos *int64 `instill:"total-private-repos"` - OwnedPrivateRepos *int64 `instill:"owned-private-repos"` - PrivateGists *int `instill:"private-gists"` - DiskUsage *int `instill:"disk-usage"` - Collaborators *int `instill:"collaborators"` - TwoFactorAuthentication *bool `instill:"two-factor-authentication"` - Plan *Plan `instill:"plan"` - LdapDn *string `instill:"ldap-dn"` - - // API URLs - URL *string `instill:"url"` - EventsURL *string `instill:"events-url"` - FollowingURL *string `instill:"following-url"` - FollowersURL *string `instill:"followers-url"` - GistsURL *string `instill:"gists-url"` - OrganizationsURL *string `instill:"organizations-url"` - ReceivedEventsURL *string `instill:"received-events-url"` - ReposURL *string `instill:"repos-url"` - StarredURL *string `instill:"starred-url"` - SubscriptionsURL *string `instill:"subscriptions-url"` - - // TextMatches is only populated from search results that request text matches - // See: search.go and https://docs.github.com/rest/search/#text-match-metadata - TextMatches []*TextMatch `instill:"text-matches"` - - // Permissions and RoleName identify the permissions and role that a user has on a given - // repository. These are only populated when calling Repositories.ListCollaborators. - Permissions map[string]bool `instill:"permissions"` - RoleName *string `instill:"role-name"` -} - -type Timestamp struct { - time.Time -} - -type Plan struct { - Name *string `instill:"name"` - Space *int `instill:"space"` - Collaborators *int `instill:"collaborators"` - PrivateRepos *int64 `instill:"private-repos"` - FilledSeats *int `instill:"filled-seats"` - Seats *int `instill:"seats"` -} - -type TextMatch struct { - ObjectURL *string `instill:"object-url"` - ObjectType *string `instill:"object-type"` - Property *string `instill:"property"` - Fragment *string `instill:"fragment"` - Matches []*Match `instill:"matches"` -} - -type Match struct { - Text *string `instill:"text"` - Indices []int `instill:"indices"` -} diff --git a/pkg/component/application/github/v0/users.go b/pkg/component/application/github/v0/users.go new file mode 100644 index 000000000..f4ba85c2c --- /dev/null +++ b/pkg/component/application/github/v0/users.go @@ -0,0 +1,117 @@ +package github + +import ( + "context" + + "github.com/google/go-github/v62/github" +) + +// UsersService handles communication with the user related methods of the GitHub API. +type UsersService interface { + // Get fetches a user by username. If username is empty, fetches the authenticated user. + Get(ctx context.Context, username string) (*github.User, *github.Response, error) + + // GetByID fetches a user by their numeric ID. + GetByID(ctx context.Context, id int64) (*github.User, *github.Response, error) +} + +// User represents a GitHub user (can be either private or public) +type User struct { + Login *string `json:"login,omitempty"` + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node-id,omitempty"` + AvatarURL *string `json:"avatar-url,omitempty"` + HTMLURL *string `json:"html-url,omitempty"` + GravatarID *string `json:"gravatar-id,omitempty"` + Name *string `json:"name,omitempty"` + Company *string `json:"company,omitempty"` + Blog *string `json:"blog,omitempty"` + Location *string `json:"location,omitempty"` + Email *string `json:"email,omitempty"` + Hireable *bool `json:"hireable,omitempty"` + Bio *string `json:"bio,omitempty"` + TwitterUsername *string `json:"twitter-username,omitempty"` + PublicRepos *int `json:"public-repos,omitempty"` + PublicGists *int `json:"public-gists,omitempty"` + Followers *int `json:"followers,omitempty"` + Following *int `json:"following,omitempty"` + CreatedAt *github.Timestamp `json:"created-at,omitempty"` + UpdatedAt *github.Timestamp `json:"updated-at,omitempty"` + SuspendedAt *github.Timestamp `json:"suspended-at,omitempty"` + Type *string `json:"type,omitempty"` + SiteAdmin *bool `json:"site-admin,omitempty"` + TotalPrivateRepos *int64 `json:"total-private-repos,omitempty"` + OwnedPrivateRepos *int64 `json:"owned-private-repos,omitempty"` + PrivateGists *int `json:"private-gists,omitempty"` + DiskUsage *int `json:"disk-usage,omitempty"` + Collaborators *int `json:"collaborators,omitempty"` + TwoFactorAuthentication *bool `json:"two-factor-authentication,omitempty"` + Plan *github.Plan `json:"plan,omitempty"` + LdapDn *string `json:"ldap-dn,omitempty"` + + // API URLs + URL *string `json:"url,omitempty"` + EventsURL *string `json:"events_url,omitempty"` + FollowingURL *string `json:"following-url,omitempty"` + FollowersURL *string `json:"followers-url,omitempty"` + GistsURL *string `json:"gists-url,omitempty"` + OrganizationsURL *string `json:"organizations-url,omitempty"` + ReceivedEventsURL *string `json:"received-events-url,omitempty"` + ReposURL *string `json:"repos-url,omitempty"` + StarredURL *string `json:"starred-url,omitempty"` + SubscriptionsURL *string `json:"subscriptions-url,omitempty"` + + TextMatches []*github.TextMatch `json:"text-matches,omitempty"` + + Permissions map[string]bool `json:"permissions,omitempty"` + RoleName *string `json:"role-name,omitempty"` +} + +func (client *Client) extractUser(user *github.User) User { + return User{ + Login: github.String(user.GetLogin()), + ID: github.Int64(user.GetID()), + NodeID: github.String(user.GetNodeID()), + AvatarURL: github.String(user.GetAvatarURL()), + HTMLURL: github.String(user.GetHTMLURL()), + GravatarID: github.String(user.GetGravatarID()), + Name: github.String(user.GetName()), + Company: github.String(user.GetCompany()), + Blog: github.String(user.GetBlog()), + Location: github.String(user.GetLocation()), + Email: github.String(user.GetEmail()), + Hireable: github.Bool(user.GetHireable()), + Bio: github.String(user.GetBio()), + TwitterUsername: github.String(user.GetTwitterUsername()), + PublicRepos: github.Int(user.GetPublicRepos()), + PublicGists: github.Int(user.GetPublicGists()), + Followers: github.Int(user.GetFollowers()), + Following: github.Int(user.GetFollowing()), + CreatedAt: &github.Timestamp{Time: user.GetCreatedAt().Time}, + UpdatedAt: &github.Timestamp{Time: user.GetUpdatedAt().Time}, + SuspendedAt: &github.Timestamp{Time: user.GetSuspendedAt().Time}, + Type: github.String(user.GetType()), + SiteAdmin: github.Bool(user.GetSiteAdmin()), + TotalPrivateRepos: github.Int64(user.GetTotalPrivateRepos()), + OwnedPrivateRepos: github.Int64(user.GetOwnedPrivateRepos()), + PrivateGists: github.Int(user.GetPrivateGists()), + DiskUsage: github.Int(user.GetDiskUsage()), + Collaborators: github.Int(user.GetCollaborators()), + TwoFactorAuthentication: github.Bool(user.GetTwoFactorAuthentication()), + Plan: user.Plan, + LdapDn: github.String(user.GetLdapDn()), + URL: github.String(user.GetURL()), + EventsURL: github.String(user.GetEventsURL()), + FollowingURL: github.String(user.GetFollowingURL()), + FollowersURL: github.String(user.GetFollowersURL()), + GistsURL: github.String(user.GetGistsURL()), + OrganizationsURL: github.String(user.GetOrganizationsURL()), + ReceivedEventsURL: github.String(user.GetReceivedEventsURL()), + ReposURL: github.String(user.GetReposURL()), + StarredURL: github.String(user.GetStarredURL()), + SubscriptionsURL: github.String(user.GetSubscriptionsURL()), + TextMatches: nil, + Permissions: nil, + RoleName: nil, + } +} diff --git a/pkg/component/application/github/v0/utils.go b/pkg/component/application/github/v0/utils.go index 59f680caf..965010a27 100644 --- a/pkg/component/application/github/v0/utils.go +++ b/pkg/component/application/github/v0/utils.go @@ -1,6 +1,39 @@ package github +// PageOptions represents the pagination options for a request type PageOptions struct { Page int `instill:"page"` PerPage int `instill:"per-page"` } + +// getString safely dereferences a string pointer and returns an empty string if nil +func getString(s *string) string { + if s != nil { + return *s + } + return "" +} + +// getInt safely dereferences an int pointer and returns 0 if nil +func getInt(i *int) int { + if i != nil { + return *i + } + return 0 +} + +// getInt64 safely dereferences an int64 pointer and returns 0 if nil +func getInt64(i *int64) int64 { + if i != nil { + return *i + } + return 0 +} + +// getBool safely dereferences a bool pointer and returns false if nil +func getBool(b *bool) bool { + if b != nil { + return *b + } + return false +} diff --git a/pkg/worker/io.go b/pkg/worker/io.go index 2fc7a6099..54649807b 100644 --- a/pkg/worker/io.go +++ b/pkg/worker/io.go @@ -101,6 +101,7 @@ func (i *inputReader) Read(ctx context.Context) (inputStruct *structpb.Struct, e return input.GetStructValue(), nil } +// ReadData reads the input data from the workflow memory and unmarshals it into the provided struct. func (i *inputReader) ReadData(ctx context.Context, input any) (err error) { inputVal, err := i.read(ctx) if err != nil { @@ -122,6 +123,7 @@ type outputWriter struct { streaming bool } +// NewOutputWriter creates a new output writer. func NewOutputWriter(wfm memory.WorkflowMemory, compID string, originalIdx int, streaming bool) *outputWriter { return &outputWriter{ compID: compID,