@gitkraken/core-gitlens 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/CHANGELOG.md +13 -1
  2. package/README.md +13 -10
  3. package/dist/git/context.d.ts +4 -3
  4. package/dist/git/context.d.ts.map +1 -1
  5. package/dist/git/context.js.map +1 -1
  6. package/dist/git/providers/operations.d.ts +1 -0
  7. package/dist/git/providers/operations.d.ts.map +1 -1
  8. package/dist/git/providers/worktrees.d.ts +2 -0
  9. package/dist/git/providers/worktrees.d.ts.map +1 -1
  10. package/dist/git/utils/conflictResolution.utils.d.ts +32 -0
  11. package/dist/git/utils/conflictResolution.utils.d.ts.map +1 -1
  12. package/dist/git/utils/conflictResolution.utils.js +95 -0
  13. package/dist/git/utils/conflictResolution.utils.js.map +1 -1
  14. package/dist/git-cli/providers/operations.d.ts +1 -0
  15. package/dist/git-cli/providers/operations.d.ts.map +1 -1
  16. package/dist/git-cli/providers/operations.js +22 -1
  17. package/dist/git-cli/providers/operations.js.map +1 -1
  18. package/dist/git-cli/providers/worktrees.d.ts +2 -0
  19. package/dist/git-cli/providers/worktrees.d.ts.map +1 -1
  20. package/dist/git-cli/providers/worktrees.js +3 -0
  21. package/dist/git-cli/providers/worktrees.js.map +1 -1
  22. package/dist/plus/ai/utils/ai.utils.js +5 -5
  23. package/dist/plus/ai/utils/ai.utils.js.map +1 -1
  24. package/dist/plus/git-github/api/github.d.ts +6 -13
  25. package/dist/plus/git-github/api/github.d.ts.map +1 -1
  26. package/dist/plus/git-github/api/github.js +5 -10
  27. package/dist/plus/git-github/api/github.js.map +1 -1
  28. package/dist/plus/integrations/authentication/cloudIntegrationService.d.ts +11 -0
  29. package/dist/plus/integrations/authentication/cloudIntegrationService.d.ts.map +1 -0
  30. package/dist/plus/integrations/authentication/cloudIntegrationService.js +90 -0
  31. package/dist/plus/integrations/authentication/cloudIntegrationService.js.map +1 -0
  32. package/dist/plus/integrations/authentication/configuredIntegrationService.d.ts +67 -0
  33. package/dist/plus/integrations/authentication/configuredIntegrationService.d.ts.map +1 -0
  34. package/dist/plus/integrations/authentication/configuredIntegrationService.js +295 -0
  35. package/dist/plus/integrations/authentication/configuredIntegrationService.js.map +1 -0
  36. package/dist/plus/integrations/authentication/integrationAuthenticationProvider.d.ts +92 -0
  37. package/dist/plus/integrations/authentication/integrationAuthenticationProvider.d.ts.map +1 -0
  38. package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js +185 -0
  39. package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js.map +1 -0
  40. package/dist/plus/integrations/authentication/integrationAuthenticationService.d.ts +62 -0
  41. package/dist/plus/integrations/authentication/integrationAuthenticationService.d.ts.map +1 -0
  42. package/dist/plus/integrations/authentication/integrationAuthenticationService.js +122 -0
  43. package/dist/plus/integrations/authentication/integrationAuthenticationService.js.map +1 -0
  44. package/dist/plus/integrations/authentication/manualTokenProvider.d.ts +15 -0
  45. package/dist/plus/integrations/authentication/manualTokenProvider.d.ts.map +1 -0
  46. package/dist/plus/integrations/authentication/manualTokenProvider.js +49 -0
  47. package/dist/plus/integrations/authentication/manualTokenProvider.js.map +1 -0
  48. package/dist/plus/integrations/authentication/models.d.ts +87 -0
  49. package/dist/plus/integrations/authentication/models.d.ts.map +1 -0
  50. package/dist/plus/integrations/authentication/models.js +64 -0
  51. package/dist/plus/integrations/authentication/models.js.map +1 -0
  52. package/dist/plus/integrations/constants.d.ts +47 -0
  53. package/dist/plus/integrations/constants.d.ts.map +1 -0
  54. package/dist/plus/integrations/constants.js +128 -0
  55. package/dist/plus/integrations/constants.js.map +1 -0
  56. package/dist/plus/integrations/context.d.ts +354 -0
  57. package/dist/plus/integrations/context.d.ts.map +1 -0
  58. package/dist/plus/integrations/context.js +2 -0
  59. package/dist/plus/integrations/context.js.map +1 -0
  60. package/dist/plus/integrations/errors.d.ts +14 -0
  61. package/dist/plus/integrations/errors.d.ts.map +1 -0
  62. package/dist/plus/integrations/errors.js +19 -0
  63. package/dist/plus/integrations/errors.js.map +1 -0
  64. package/dist/plus/integrations/index.d.ts +32 -0
  65. package/dist/plus/integrations/index.d.ts.map +1 -0
  66. package/dist/plus/integrations/index.js +62 -0
  67. package/dist/plus/integrations/index.js.map +1 -0
  68. package/dist/plus/integrations/integrationService.d.ts +98 -0
  69. package/dist/plus/integrations/integrationService.d.ts.map +1 -0
  70. package/dist/plus/integrations/integrationService.js +627 -0
  71. package/dist/plus/integrations/integrationService.js.map +1 -0
  72. package/dist/plus/integrations/models/gitHostIntegration.d.ts +82 -0
  73. package/dist/plus/integrations/models/gitHostIntegration.d.ts.map +1 -0
  74. package/dist/plus/integrations/models/gitHostIntegration.js +522 -0
  75. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -0
  76. package/dist/plus/integrations/models/integration.d.ts +127 -0
  77. package/dist/plus/integrations/models/integration.d.ts.map +1 -0
  78. package/dist/plus/integrations/models/integration.js +456 -0
  79. package/dist/plus/integrations/models/integration.js.map +1 -0
  80. package/dist/plus/integrations/models/issuesIntegration.d.ts +28 -0
  81. package/dist/plus/integrations/models/issuesIntegration.d.ts.map +1 -0
  82. package/dist/plus/integrations/models/issuesIntegration.js +101 -0
  83. package/dist/plus/integrations/models/issuesIntegration.js.map +1 -0
  84. package/dist/plus/integrations/providers/apiClients.d.ts +25 -0
  85. package/dist/plus/integrations/providers/apiClients.d.ts.map +1 -0
  86. package/dist/plus/integrations/providers/apiClients.js +39 -0
  87. package/dist/plus/integrations/providers/apiClients.js.map +1 -0
  88. package/dist/plus/integrations/providers/azure/azure.d.ts +47 -0
  89. package/dist/plus/integrations/providers/azure/azure.d.ts.map +1 -0
  90. package/dist/plus/integrations/providers/azure/azure.js +444 -0
  91. package/dist/plus/integrations/providers/azure/azure.js.map +1 -0
  92. package/dist/plus/integrations/providers/azure/models.d.ts +286 -0
  93. package/dist/plus/integrations/providers/azure/models.d.ts.map +1 -0
  94. package/dist/plus/integrations/providers/azure/models.js +203 -0
  95. package/dist/plus/integrations/providers/azure/models.js.map +1 -0
  96. package/dist/plus/integrations/providers/azureDevOps.d.ts +95 -0
  97. package/dist/plus/integrations/providers/azureDevOps.d.ts.map +1 -0
  98. package/dist/plus/integrations/providers/azureDevOps.js +432 -0
  99. package/dist/plus/integrations/providers/azureDevOps.js.map +1 -0
  100. package/dist/plus/integrations/providers/bitbucket/bitbucket.d.ts +42 -0
  101. package/dist/plus/integrations/providers/bitbucket/bitbucket.d.ts.map +1 -0
  102. package/dist/plus/integrations/providers/bitbucket/bitbucket.js +513 -0
  103. package/dist/plus/integrations/providers/bitbucket/bitbucket.js.map +1 -0
  104. package/dist/plus/integrations/providers/bitbucket/models.d.ts +222 -0
  105. package/dist/plus/integrations/providers/bitbucket/models.d.ts.map +1 -0
  106. package/dist/plus/integrations/providers/bitbucket/models.js +158 -0
  107. package/dist/plus/integrations/providers/bitbucket/models.js.map +1 -0
  108. package/dist/plus/integrations/providers/bitbucket-server/models.d.ts +120 -0
  109. package/dist/plus/integrations/providers/bitbucket-server/models.d.ts.map +1 -0
  110. package/dist/plus/integrations/providers/bitbucket-server/models.js +111 -0
  111. package/dist/plus/integrations/providers/bitbucket-server/models.js.map +1 -0
  112. package/dist/plus/integrations/providers/bitbucket-server.d.ts +60 -0
  113. package/dist/plus/integrations/providers/bitbucket-server.d.ts.map +1 -0
  114. package/dist/plus/integrations/providers/bitbucket-server.js +138 -0
  115. package/dist/plus/integrations/providers/bitbucket-server.js.map +1 -0
  116. package/dist/plus/integrations/providers/bitbucket.d.ts +50 -0
  117. package/dist/plus/integrations/providers/bitbucket.d.ts.map +1 -0
  118. package/dist/plus/integrations/providers/bitbucket.js +181 -0
  119. package/dist/plus/integrations/providers/bitbucket.js.map +1 -0
  120. package/dist/plus/integrations/providers/github/github.d.ts +5 -0
  121. package/dist/plus/integrations/providers/github/github.d.ts.map +1 -0
  122. package/dist/plus/integrations/providers/github/github.js +47 -0
  123. package/dist/plus/integrations/providers/github/github.js.map +1 -0
  124. package/dist/plus/integrations/providers/github/github.utils.d.ts +11 -0
  125. package/dist/plus/integrations/providers/github/github.utils.d.ts.map +1 -0
  126. package/dist/plus/integrations/providers/github/github.utils.js +6 -0
  127. package/dist/plus/integrations/providers/github/github.utils.js.map +1 -0
  128. package/dist/plus/integrations/providers/github/models.d.ts +3 -0
  129. package/dist/plus/integrations/providers/github/models.d.ts.map +1 -0
  130. package/dist/plus/integrations/providers/github/models.js +2 -0
  131. package/dist/plus/integrations/providers/github/models.js.map +1 -0
  132. package/dist/plus/integrations/providers/github.d.ts +73 -0
  133. package/dist/plus/integrations/providers/github.d.ts.map +1 -0
  134. package/dist/plus/integrations/providers/github.js +160 -0
  135. package/dist/plus/integrations/providers/github.js.map +1 -0
  136. package/dist/plus/integrations/providers/gitlab/gitlab.d.ts +63 -0
  137. package/dist/plus/integrations/providers/gitlab/gitlab.d.ts.map +1 -0
  138. package/dist/plus/integrations/providers/gitlab/gitlab.js +828 -0
  139. package/dist/plus/integrations/providers/gitlab/gitlab.js.map +1 -0
  140. package/dist/plus/integrations/providers/gitlab/gitlab.utils.d.ts +21 -0
  141. package/dist/plus/integrations/providers/gitlab/gitlab.utils.d.ts.map +1 -0
  142. package/dist/plus/integrations/providers/gitlab/gitlab.utils.js +45 -0
  143. package/dist/plus/integrations/providers/gitlab/gitlab.utils.js.map +1 -0
  144. package/dist/plus/integrations/providers/gitlab/models.d.ts +128 -0
  145. package/dist/plus/integrations/providers/gitlab/models.d.ts.map +1 -0
  146. package/dist/plus/integrations/providers/gitlab/models.js +96 -0
  147. package/dist/plus/integrations/providers/gitlab/models.js.map +1 -0
  148. package/dist/plus/integrations/providers/gitlab.d.ts +79 -0
  149. package/dist/plus/integrations/providers/gitlab.d.ts.map +1 -0
  150. package/dist/plus/integrations/providers/gitlab.js +253 -0
  151. package/dist/plus/integrations/providers/gitlab.js.map +1 -0
  152. package/dist/plus/integrations/providers/jira.d.ts +46 -0
  153. package/dist/plus/integrations/providers/jira.d.ts.map +1 -0
  154. package/dist/plus/integrations/providers/jira.js +255 -0
  155. package/dist/plus/integrations/providers/jira.js.map +1 -0
  156. package/dist/plus/integrations/providers/linear.d.ts +47 -0
  157. package/dist/plus/integrations/providers/linear.d.ts.map +1 -0
  158. package/dist/plus/integrations/providers/linear.js +198 -0
  159. package/dist/plus/integrations/providers/linear.js.map +1 -0
  160. package/dist/plus/integrations/providers/models.d.ts +433 -0
  161. package/dist/plus/integrations/providers/models.d.ts.map +1 -0
  162. package/dist/plus/integrations/providers/models.js +602 -0
  163. package/dist/plus/integrations/providers/models.js.map +1 -0
  164. package/dist/plus/integrations/providers/providersApi.d.ts +129 -0
  165. package/dist/plus/integrations/providers/providersApi.d.ts.map +1 -0
  166. package/dist/plus/integrations/providers/providersApi.js +602 -0
  167. package/dist/plus/integrations/providers/providersApi.js.map +1 -0
  168. package/dist/plus/integrations/providers/utils.d.ts +54 -0
  169. package/dist/plus/integrations/providers/utils.d.ts.map +1 -0
  170. package/dist/plus/integrations/providers/utils.js +262 -0
  171. package/dist/plus/integrations/providers/utils.js.map +1 -0
  172. package/dist/plus/integrations/telemetry.d.ts +26 -0
  173. package/dist/plus/integrations/telemetry.d.ts.map +1 -0
  174. package/dist/plus/integrations/telemetry.js +11 -0
  175. package/dist/plus/integrations/telemetry.js.map +1 -0
  176. package/dist/plus/integrations/utils/integration.utils.d.ts +14 -0
  177. package/dist/plus/integrations/utils/integration.utils.d.ts.map +1 -0
  178. package/dist/plus/integrations/utils/integration.utils.js +111 -0
  179. package/dist/plus/integrations/utils/integration.utils.js.map +1 -0
  180. package/dist/utils/disposable.d.ts +15 -2
  181. package/dist/utils/disposable.d.ts.map +1 -1
  182. package/dist/utils/disposable.js.map +1 -1
  183. package/package.json +43 -2
  184. package/src/git/context.ts +4 -3
  185. package/src/git/providers/operations.ts +1 -1
  186. package/src/git/providers/worktrees.ts +14 -2
  187. package/src/git/utils/conflictResolution.utils.ts +126 -0
  188. package/src/git-cli/providers/operations.ts +22 -2
  189. package/src/git-cli/providers/worktrees.ts +17 -2
  190. package/src/plus/ai/utils/ai.utils.ts +5 -5
  191. package/src/plus/git-github/api/github.ts +13 -25
  192. package/src/plus/integrations/authentication/cloudIntegrationService.ts +120 -0
  193. package/src/plus/integrations/authentication/configuredIntegrationService.ts +414 -0
  194. package/src/plus/integrations/authentication/integrationAuthenticationProvider.ts +289 -0
  195. package/src/plus/integrations/authentication/integrationAuthenticationService.ts +144 -0
  196. package/src/plus/integrations/authentication/manualTokenProvider.ts +72 -0
  197. package/src/plus/integrations/authentication/models.ts +168 -0
  198. package/src/plus/integrations/constants.ts +169 -0
  199. package/src/plus/integrations/context.ts +383 -0
  200. package/src/plus/integrations/errors.ts +35 -0
  201. package/src/plus/integrations/index.ts +119 -0
  202. package/src/plus/integrations/integrationService.ts +924 -0
  203. package/src/plus/integrations/models/gitHostIntegration.ts +738 -0
  204. package/src/plus/integrations/models/integration.ts +657 -0
  205. package/src/plus/integrations/models/issuesIntegration.ts +124 -0
  206. package/src/plus/integrations/providers/apiClients.ts +72 -0
  207. package/src/plus/integrations/providers/azure/azure.ts +691 -0
  208. package/src/plus/integrations/providers/azure/models.ts +605 -0
  209. package/src/plus/integrations/providers/azureDevOps.ts +677 -0
  210. package/src/plus/integrations/providers/bitbucket/bitbucket.ts +774 -0
  211. package/src/plus/integrations/providers/bitbucket/models.ts +445 -0
  212. package/src/plus/integrations/providers/bitbucket-server/models.ts +252 -0
  213. package/src/plus/integrations/providers/bitbucket-server.ts +282 -0
  214. package/src/plus/integrations/providers/bitbucket.ts +350 -0
  215. package/src/plus/integrations/providers/github/github.ts +64 -0
  216. package/src/plus/integrations/providers/github/github.utils.ts +23 -0
  217. package/src/plus/integrations/providers/github/models.ts +29 -0
  218. package/src/plus/integrations/providers/github.ts +366 -0
  219. package/src/plus/integrations/providers/gitlab/gitlab.ts +1210 -0
  220. package/src/plus/integrations/providers/gitlab/gitlab.utils.ts +71 -0
  221. package/src/plus/integrations/providers/gitlab/models.ts +257 -0
  222. package/src/plus/integrations/providers/gitlab.ts +473 -0
  223. package/src/plus/integrations/providers/jira.ts +359 -0
  224. package/src/plus/integrations/providers/linear.ts +275 -0
  225. package/src/plus/integrations/providers/models.ts +1114 -0
  226. package/src/plus/integrations/providers/providersApi.ts +1126 -0
  227. package/src/plus/integrations/providers/utils.ts +359 -0
  228. package/src/plus/integrations/telemetry.ts +29 -0
  229. package/src/plus/integrations/utils/integration.utils.ts +138 -0
  230. package/src/utils/disposable.ts +15 -2
@@ -0,0 +1,1126 @@
1
+ import ProviderApis from '@gitkraken/provider-apis';
2
+ import type { PullRequest, PullRequestMergeMethod } from '../../../git/models/pullRequest.js';
3
+ import { base64 } from '../../../utils/base64.js';
4
+ import type { PagedResult } from '../../../utils/paging.js';
5
+ import type { IntegrationAuthenticationService } from '../authentication/integrationAuthenticationService.js';
6
+ import type { TokenOptInfo, TokenWithInfo } from '../authentication/models.js';
7
+ import { toTokenWithInfo } from '../authentication/models.js';
8
+ import type { IntegrationIds } from '../constants.js';
9
+ import {
10
+ GitCloudHostIntegrationId,
11
+ GitSelfManagedHostIntegrationId,
12
+ IssuesCloudHostIntegrationId,
13
+ } from '../constants.js';
14
+ import {
15
+ AuthenticationError,
16
+ AuthenticationErrorReason,
17
+ RequestClientError,
18
+ RequestNotFoundError,
19
+ RequestRateLimitError,
20
+ } from '../errors.js';
21
+ import type {
22
+ GetIssueFn,
23
+ GetIssuesForReposFn,
24
+ GetIssuesOptions,
25
+ GetPullRequestsForRepoFn,
26
+ GetPullRequestsForReposFn,
27
+ GetPullRequestsForUserFn,
28
+ GetPullRequestsForUserOptions,
29
+ GetPullRequestsOptions,
30
+ GetReposOptions,
31
+ IssueFilter,
32
+ PageInfo,
33
+ PagingInput,
34
+ PagingMode,
35
+ ProviderAccount,
36
+ ProviderAzureProject,
37
+ ProviderAzureResource,
38
+ ProviderBitbucketResource,
39
+ ProviderInfo,
40
+ ProviderIssue,
41
+ ProviderJiraProject,
42
+ ProviderJiraResource,
43
+ ProviderLinearOrganization,
44
+ ProviderLinearTeam,
45
+ ProviderPullRequest,
46
+ ProviderRepoInput,
47
+ ProviderReposInput,
48
+ ProviderRepository,
49
+ ProviderRequestFunction,
50
+ ProviderRequestOptions,
51
+ ProviderRequestResponse,
52
+ Providers,
53
+ PullRequestFilter,
54
+ } from './models.js';
55
+ import { providersMetadata } from './models.js';
56
+
57
+ // `@gitkraken/provider-apis` is published as CommonJS with its factory on the `default` export.
58
+ // How that surfaces depends on the consuming bundler's CJS->ESM interop: esbuild yields the
59
+ // callable factory directly, while webpack and Node surface the module namespace (with the
60
+ // factory under `.default`). Normalize to the callable factory so every consumer resolves it.
61
+ type ProviderApisFactory = typeof ProviderApis;
62
+ const createProviderApis: ProviderApisFactory =
63
+ (ProviderApis as ProviderApisFactory & { default?: ProviderApisFactory }).default ?? ProviderApis;
64
+
65
+ export class ProvidersApi {
66
+ private readonly providers: Providers;
67
+
68
+ constructor(private readonly authenticationService: IntegrationAuthenticationService) {
69
+ const http = authenticationService.ctx.http;
70
+ const userAgent = http.userAgent;
71
+ const customFetch: ProviderRequestFunction = async <T>({
72
+ url,
73
+ ...options
74
+ }: ProviderRequestOptions): Promise<ProviderRequestResponse<T>> => {
75
+ const response = await http.fetch(url, {
76
+ ...options,
77
+ headers: {
78
+ 'User-Agent': userAgent,
79
+ ...options.headers,
80
+ },
81
+ });
82
+
83
+ return parseFetchResponseForApi<T>(response);
84
+ };
85
+ const providerApis = createProviderApis({ request: customFetch });
86
+ this.providers = {
87
+ [GitCloudHostIntegrationId.GitHub]: {
88
+ ...providersMetadata[GitCloudHostIntegrationId.GitHub],
89
+ provider: providerApis.github,
90
+ getCurrentUserFn: providerApis.github.getCurrentUser.bind(providerApis.github),
91
+ getPullRequestsForReposFn: providerApis.github.getPullRequestsForRepos.bind(
92
+ providerApis.github,
93
+ ) as GetPullRequestsForReposFn,
94
+ getPullRequestsForUserFn: providerApis.github.getPullRequestsAssociatedWithUser.bind(
95
+ providerApis.github,
96
+ ) as GetPullRequestsForUserFn,
97
+ getIssuesForReposFn: providerApis.github.getIssuesForRepos.bind(
98
+ providerApis.github,
99
+ ) as GetIssuesForReposFn,
100
+ },
101
+ [GitSelfManagedHostIntegrationId.CloudGitHubEnterprise]: {
102
+ ...providersMetadata[GitSelfManagedHostIntegrationId.CloudGitHubEnterprise],
103
+ provider: providerApis.github,
104
+ getCurrentUserFn: providerApis.github.getCurrentUser.bind(providerApis.github),
105
+ getPullRequestsForReposFn: providerApis.github.getPullRequestsForRepos.bind(
106
+ providerApis.github,
107
+ ) as GetPullRequestsForReposFn,
108
+ getPullRequestsForUserFn: providerApis.github.getPullRequestsAssociatedWithUser.bind(
109
+ providerApis.github,
110
+ ) as GetPullRequestsForUserFn,
111
+ getIssuesForReposFn: providerApis.github.getIssuesForRepos.bind(
112
+ providerApis.github,
113
+ ) as GetIssuesForReposFn,
114
+ },
115
+ [GitCloudHostIntegrationId.GitLab]: {
116
+ ...providersMetadata[GitCloudHostIntegrationId.GitLab],
117
+ provider: providerApis.gitlab,
118
+ getRepoFn: providerApis.gitlab.getRepo.bind(providerApis.gitlab),
119
+ getCurrentUserFn: providerApis.gitlab.getCurrentUser.bind(providerApis.gitlab),
120
+ getPullRequestsForReposFn: providerApis.gitlab.getPullRequestsForRepos.bind(
121
+ providerApis.gitlab,
122
+ ) as GetPullRequestsForReposFn,
123
+ getPullRequestsForRepoFn: providerApis.gitlab.getPullRequestsForRepo.bind(
124
+ providerApis.gitlab,
125
+ ) as GetPullRequestsForRepoFn,
126
+ getPullRequestsForUserFn: providerApis.gitlab.getPullRequestsAssociatedWithUser.bind(
127
+ providerApis.gitlab,
128
+ ) as GetPullRequestsForUserFn,
129
+ getIssueFn: providerApis.gitlab.getIssue.bind(providerApis.gitlab) as GetIssueFn,
130
+ getIssuesForReposFn: providerApis.gitlab.getIssuesForRepos.bind(
131
+ providerApis.gitlab,
132
+ ) as GetIssuesForReposFn,
133
+ getIssuesForRepoFn: providerApis.gitlab.getIssuesForRepo.bind(providerApis.gitlab),
134
+ mergePullRequestFn: providerApis.gitlab.mergePullRequest.bind(providerApis.gitlab),
135
+ },
136
+ [GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted]: {
137
+ ...providersMetadata[GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted],
138
+ provider: providerApis.gitlab,
139
+ getCurrentUserFn: providerApis.gitlab.getCurrentUser.bind(providerApis.gitlab),
140
+ getPullRequestsForReposFn: providerApis.gitlab.getPullRequestsForRepos.bind(
141
+ providerApis.gitlab,
142
+ ) as GetPullRequestsForReposFn,
143
+ getPullRequestsForRepoFn: providerApis.gitlab.getPullRequestsForRepo.bind(
144
+ providerApis.gitlab,
145
+ ) as GetPullRequestsForRepoFn,
146
+ getPullRequestsForUserFn: providerApis.gitlab.getPullRequestsAssociatedWithUser.bind(
147
+ providerApis.gitlab,
148
+ ) as GetPullRequestsForUserFn,
149
+ getIssueFn: providerApis.gitlab.getIssue.bind(providerApis.gitlab) as GetIssueFn,
150
+ getIssuesForReposFn: providerApis.gitlab.getIssuesForRepos.bind(
151
+ providerApis.gitlab,
152
+ ) as GetIssuesForReposFn,
153
+ getIssuesForRepoFn: providerApis.gitlab.getIssuesForRepo.bind(providerApis.gitlab),
154
+ mergePullRequestFn: providerApis.gitlab.mergePullRequest.bind(providerApis.gitlab),
155
+ },
156
+ [GitCloudHostIntegrationId.Bitbucket]: {
157
+ ...providersMetadata[GitCloudHostIntegrationId.Bitbucket],
158
+ provider: providerApis.bitbucket,
159
+ getCurrentUserFn: providerApis.bitbucket.getCurrentUser.bind(providerApis.bitbucket),
160
+ getBitbucketResourcesForCurrentUserFn: providerApis.bitbucket.getWorkspacesForCurrentUser.bind(
161
+ providerApis.bitbucket,
162
+ ),
163
+ getBitbucketPullRequestsAuthoredByUserForWorkspaceFn:
164
+ providerApis.bitbucket.getPullRequestsForUserAndWorkspace.bind(providerApis.bitbucket),
165
+ getPullRequestsForReposFn: providerApis.bitbucket.getPullRequestsForRepos.bind(
166
+ providerApis.bitbucket,
167
+ ) as GetPullRequestsForReposFn,
168
+ getPullRequestsForRepoFn: providerApis.bitbucket.getPullRequestsForRepo.bind(providerApis.bitbucket),
169
+ mergePullRequestFn: providerApis.bitbucket.mergePullRequest.bind(providerApis.bitbucket),
170
+ },
171
+ [GitSelfManagedHostIntegrationId.BitbucketServer]: {
172
+ ...providersMetadata[GitSelfManagedHostIntegrationId.BitbucketServer],
173
+ provider: providerApis.bitbucketServer,
174
+ getRepoFn: providerApis.bitbucketServer.getRepo.bind(providerApis.bitbucketServer),
175
+ getCurrentUserFn: providerApis.bitbucketServer.getCurrentUser.bind(providerApis.bitbucketServer),
176
+ getBitbucketServerPullRequestsForCurrentUserFn:
177
+ providerApis.bitbucketServer.getPullRequestsForCurrentUser.bind(providerApis.bitbucketServer),
178
+ getPullRequestsForReposFn: providerApis.bitbucketServer.getPullRequestsForRepos.bind(
179
+ providerApis.bitbucketServer,
180
+ ) as GetPullRequestsForReposFn,
181
+ getPullRequestsForRepoFn: providerApis.bitbucketServer.getPullRequestsForRepo.bind(
182
+ providerApis.bitbucketServer,
183
+ ),
184
+ mergePullRequestFn: providerApis.bitbucketServer.mergePullRequest.bind(providerApis.bitbucketServer),
185
+ },
186
+ [GitCloudHostIntegrationId.AzureDevOps]: {
187
+ ...providersMetadata[GitCloudHostIntegrationId.AzureDevOps],
188
+ provider: providerApis.azureDevOps,
189
+ getRepoOfProjectFn: providerApis.azureDevOps.getRepo.bind(providerApis.azureDevOps),
190
+ getCurrentUserFn: providerApis.azureDevOps.getCurrentUser.bind(providerApis.azureDevOps),
191
+ getCurrentUserForInstanceFn: providerApis.azureDevOps.getCurrentUserForInstance.bind(
192
+ providerApis.azureDevOps,
193
+ ),
194
+ getAzureResourcesForUserFn: providerApis.azureDevOps.getOrgsForUser.bind(providerApis.azureDevOps),
195
+ getAzureProjectsForResourceFn: providerApis.azureDevOps.getAzureProjects.bind(providerApis.azureDevOps),
196
+ getPullRequestsForReposFn: providerApis.azureDevOps.getPullRequestsForRepos.bind(
197
+ providerApis.azureDevOps,
198
+ ) as GetPullRequestsForReposFn,
199
+ getPullRequestsForRepoFn: providerApis.azureDevOps.getPullRequestsForRepo.bind(
200
+ providerApis.azureDevOps,
201
+ ),
202
+ getPullRequestsForAzureProjectsFn: providerApis.azureDevOps.getPullRequestsForProjects.bind(
203
+ providerApis.azureDevOps,
204
+ ),
205
+ getIssuesForAzureProjectFn: providerApis.azureDevOps.getIssuesForAzureProject.bind(
206
+ providerApis.azureDevOps,
207
+ ),
208
+ getReposForAzureProjectFn: providerApis.azureDevOps.getReposForAzureProject.bind(
209
+ providerApis.azureDevOps,
210
+ ),
211
+ mergePullRequestFn: providerApis.azureDevOps.mergePullRequest.bind(providerApis.azureDevOps),
212
+ },
213
+ [GitSelfManagedHostIntegrationId.AzureDevOpsServer]: {
214
+ ...providersMetadata[GitSelfManagedHostIntegrationId.AzureDevOpsServer],
215
+ provider: providerApis.azureDevOps,
216
+ getRepoOfProjectFn: providerApis.azureDevOps.getRepo.bind(providerApis.azureDevOps),
217
+ getCurrentUserFn: providerApis.azureDevOps.getCurrentUser.bind(providerApis.azureDevOps),
218
+ getCurrentUserForInstanceFn: providerApis.azureDevOps.getCurrentUserForInstance.bind(
219
+ providerApis.azureDevOps,
220
+ ),
221
+ getAzureResourcesForUserFn: providerApis.azureDevOps.getCollectionsForUser.bind(
222
+ providerApis.azureDevOps,
223
+ ),
224
+ getAzureProjectsForResourceFn: providerApis.azureDevOps.getAzureProjects.bind(providerApis.azureDevOps),
225
+ getPullRequestsForReposFn: providerApis.azureDevOps.getPullRequestsForRepos.bind(
226
+ providerApis.azureDevOps,
227
+ ) as GetPullRequestsForReposFn,
228
+ getPullRequestsForRepoFn: providerApis.azureDevOps.getPullRequestsForRepo.bind(
229
+ providerApis.azureDevOps,
230
+ ),
231
+ getPullRequestsForAzureProjectsFn: providerApis.azureDevOps.getPullRequestsForProjects.bind(
232
+ providerApis.azureDevOps,
233
+ ),
234
+ getIssuesForAzureProjectFn: providerApis.azureDevOps.getIssuesForAzureProject.bind(
235
+ providerApis.azureDevOps,
236
+ ),
237
+ getReposForAzureProjectFn: providerApis.azureDevOps.getReposForAzureProject.bind(
238
+ providerApis.azureDevOps,
239
+ ),
240
+ mergePullRequestFn: providerApis.azureDevOps.mergePullRequest.bind(providerApis.azureDevOps),
241
+ },
242
+ [IssuesCloudHostIntegrationId.Jira]: {
243
+ ...providersMetadata[IssuesCloudHostIntegrationId.Jira],
244
+ provider: providerApis.jira,
245
+ getCurrentUserForResourceFn: providerApis.jira.getCurrentUserForResource.bind(providerApis.jira),
246
+ getJiraResourcesForCurrentUserFn: providerApis.jira.getJiraResourcesForCurrentUser.bind(
247
+ providerApis.jira,
248
+ ),
249
+ getJiraProjectsForResourcesFn: providerApis.jira.getJiraProjectsForResources.bind(providerApis.jira),
250
+ getIssueFn: providerApis.jira.getIssue.bind(providerApis.jira) as GetIssueFn,
251
+ getIssuesForProjectFn: providerApis.jira.getIssuesForProject.bind(providerApis.jira),
252
+ getIssuesForResourceForCurrentUserFn: providerApis.jira.getIssuesForResourceForCurrentUser.bind(
253
+ providerApis.jira,
254
+ ),
255
+ },
256
+ [IssuesCloudHostIntegrationId.Linear]: {
257
+ ...providersMetadata[IssuesCloudHostIntegrationId.Linear],
258
+ provider: providerApis.linear,
259
+ getIssueFn: providerApis.linear.getIssue.bind(providerApis.linear),
260
+ getIssuesForCurrentUserFn: providerApis.linear.getIssuesForCurrentUser.bind(providerApis.linear),
261
+ getLinearOrganizationFn: providerApis.linear.getLinearOrganization.bind(providerApis.linear),
262
+ getLinearTeamsForCurrentUserFn: providerApis.linear.getTeamsForCurrentUser.bind(providerApis.linear),
263
+ },
264
+ [IssuesCloudHostIntegrationId.Trello]: {
265
+ ...providersMetadata[IssuesCloudHostIntegrationId.Trello],
266
+ provider: providerApis.trello,
267
+ },
268
+ };
269
+ }
270
+
271
+ getScopesForProvider(providerId: IntegrationIds): string[] | undefined {
272
+ return this.providers[providerId]?.scopes;
273
+ }
274
+
275
+ getProviderDomain(providerId: IntegrationIds): string | undefined {
276
+ return this.providers[providerId]?.domain;
277
+ }
278
+
279
+ getProviderPullRequestsPagingMode(providerId: IntegrationIds): PagingMode | undefined {
280
+ return this.providers[providerId]?.pullRequestsPagingMode;
281
+ }
282
+
283
+ getProviderIssuesPagingMode(providerId: IntegrationIds): PagingMode | undefined {
284
+ return this.providers[providerId]?.issuesPagingMode;
285
+ }
286
+
287
+ providerSupportsPullRequestFilters(providerId: IntegrationIds, filters: PullRequestFilter[]): boolean {
288
+ return (
289
+ this.providers[providerId]?.supportedPullRequestFilters != null &&
290
+ filters.every(filter => this.providers[providerId]?.supportedPullRequestFilters?.includes(filter))
291
+ );
292
+ }
293
+
294
+ providerSupportsIssueFilters(providerId: IntegrationIds, filters: IssueFilter[]): boolean {
295
+ return (
296
+ this.providers[providerId]?.supportedIssueFilters != null &&
297
+ filters.every(filter => this.providers[providerId]?.supportedIssueFilters?.includes(filter))
298
+ );
299
+ }
300
+
301
+ isRepoIdsInput(input: any): input is (string | number)[] {
302
+ return (
303
+ input != null &&
304
+ Array.isArray(input) &&
305
+ input.every((id: any) => typeof id === 'string' || typeof id === 'number')
306
+ );
307
+ }
308
+
309
+ private async getProviderToken<T extends IntegrationIds>(
310
+ provider: ProviderInfo & { id: T },
311
+ options?: { createSessionIfNeeded?: boolean },
312
+ ): Promise<TokenWithInfo<T> | undefined> {
313
+ const providerDescriptor = { domain: provider.domain, scopes: provider.scopes };
314
+ try {
315
+ const authProvider = await this.authenticationService.get(provider.id);
316
+ const session = await authProvider.getSession(providerDescriptor, {
317
+ createIfNeeded: options?.createSessionIfNeeded,
318
+ });
319
+ if (session == null) {
320
+ return undefined;
321
+ }
322
+ return toTokenWithInfo(provider.id, session);
323
+ } catch {
324
+ return undefined;
325
+ }
326
+ }
327
+
328
+ private getAzurePATForOAuthToken(oauthToken: string) {
329
+ return base64(`PAT:${oauthToken}`);
330
+ }
331
+
332
+ private async ensureProviderTokenAndFunction<T extends IntegrationIds>(
333
+ tokenOptInfo: TokenOptInfo<T>,
334
+ providerFn: keyof ProviderInfo,
335
+ ): Promise<{ provider: ProviderInfo; tokenWithInfo: TokenWithInfo<T> }> {
336
+ const providerId = tokenOptInfo.providerId;
337
+ const provider = this.providers[providerId];
338
+ if (provider == null) {
339
+ throw new Error(`Provider with id ${providerId} not registered`);
340
+ }
341
+
342
+ if (providerId !== provider.id) {
343
+ throw new Error(`Provider id mismatch: expected ${providerId} but got ${provider.id}`);
344
+ }
345
+
346
+ const tokenWithInfo = tokenOptInfo?.accessToken
347
+ ? tokenOptInfo
348
+ : await this.getProviderToken<T>(provider as ProviderInfo & { id: T });
349
+ if (tokenWithInfo == null) {
350
+ throw new Error(`Not connected to provider ${providerId}`);
351
+ }
352
+
353
+ if (provider[providerFn] == null) {
354
+ throw new Error(`Provider with id ${providerId} does not support function: ${providerFn}`);
355
+ }
356
+
357
+ return { provider: provider, tokenWithInfo: tokenWithInfo };
358
+ }
359
+
360
+ private handleProviderError<T>(tokenWithInfo: TokenWithInfo, error: any): T {
361
+ const { accessToken: token, ...tokenInfo } = tokenWithInfo;
362
+ const providerId = tokenWithInfo.providerId;
363
+ const provider = this.providers[providerId];
364
+ if (provider == null) {
365
+ throw new Error(`Provider with id ${providerId} not registered`);
366
+ }
367
+
368
+ if (error?.response?.status != null) {
369
+ switch (error.response.status) {
370
+ case 404: // Not found
371
+ case 410: // Gone
372
+ case 422: // Unprocessable Entity
373
+ throw new RequestNotFoundError(error);
374
+ case 401: // Unauthorized
375
+ if (error.message?.includes('rate limit')) {
376
+ let resetAt: number | undefined;
377
+
378
+ const reset = error.response?.headers?.['x-ratelimit-reset'];
379
+ if (reset != null) {
380
+ resetAt = parseInt(reset, 10);
381
+ if (Number.isNaN(resetAt)) {
382
+ resetAt = undefined;
383
+ }
384
+ }
385
+
386
+ throw new RequestRateLimitError(error, token, resetAt);
387
+ }
388
+ throw new AuthenticationError(tokenInfo, AuthenticationErrorReason.Unauthorized, error);
389
+ case 403: // Forbidden
390
+ throw new AuthenticationError(tokenInfo, AuthenticationErrorReason.Forbidden, error);
391
+ case 429: {
392
+ // Too Many Requests
393
+ let resetAt: number | undefined;
394
+
395
+ const reset = error.response.headers?.['x-ratelimit-reset'];
396
+ if (reset != null) {
397
+ resetAt = parseInt(reset, 10);
398
+ if (Number.isNaN(resetAt)) {
399
+ resetAt = undefined;
400
+ }
401
+ }
402
+
403
+ throw new RequestRateLimitError(error, token, resetAt);
404
+ }
405
+ default:
406
+ if (error.response.status >= 400 && error.response.status < 500) {
407
+ throw new RequestClientError(error);
408
+ }
409
+ }
410
+ }
411
+
412
+ throw error;
413
+ }
414
+
415
+ async getPagedResult<T>(
416
+ args: any,
417
+ providerFn:
418
+ | ((
419
+ input: any,
420
+ options?: { token?: string; isPAT?: boolean; baseUrl?: string },
421
+ ) => Promise<{ data: NonNullable<T>[]; pageInfo?: PageInfo }>)
422
+ | undefined,
423
+ tokenWithInfo: TokenWithInfo,
424
+ cursor: string = '{}',
425
+ isPAT: boolean = false,
426
+ baseUrl?: string,
427
+ ): Promise<PagedResult<T>> {
428
+ let cursorInfo;
429
+ try {
430
+ cursorInfo = JSON.parse(cursor);
431
+ } catch {
432
+ cursorInfo = {};
433
+ }
434
+ const cursorValue = cursorInfo.value;
435
+ const cursorType = cursorInfo.type;
436
+ let cursorOrPage = {};
437
+ if (cursorType === 'page') {
438
+ cursorOrPage = { page: cursorValue };
439
+ } else if (cursorType === 'cursor') {
440
+ cursorOrPage = { cursor: cursorValue };
441
+ }
442
+
443
+ const input = {
444
+ ...args,
445
+ ...cursorOrPage,
446
+ };
447
+
448
+ try {
449
+ const result = await providerFn?.(input, {
450
+ token: tokenWithInfo.accessToken,
451
+ isPAT: isPAT,
452
+ baseUrl: baseUrl,
453
+ });
454
+ if (result == null) {
455
+ return { values: [] };
456
+ }
457
+
458
+ const hasMore = result.pageInfo?.hasNextPage ?? false;
459
+
460
+ let nextCursor = '{}';
461
+ if (result.pageInfo?.endCursor != null) {
462
+ nextCursor = JSON.stringify({ value: result.pageInfo?.endCursor, type: 'cursor' });
463
+ } else if (result.pageInfo?.nextPage != null) {
464
+ nextCursor = JSON.stringify({ value: result.pageInfo?.nextPage, type: 'page' });
465
+ }
466
+
467
+ return {
468
+ values: result.data,
469
+ paging: {
470
+ cursor: nextCursor,
471
+ more: hasMore,
472
+ },
473
+ };
474
+ } catch (e) {
475
+ return this.handleProviderError<PagedResult<T>>(tokenWithInfo, e);
476
+ }
477
+ }
478
+
479
+ async getRepo(
480
+ tokenOptInfo: TokenOptInfo,
481
+ owner: string,
482
+ name: string,
483
+ project?: string,
484
+ options?: { isPAT?: boolean; baseUrl?: string },
485
+ ): Promise<ProviderRepository | undefined> {
486
+ const providerId = tokenOptInfo.providerId;
487
+ if (providerId === GitCloudHostIntegrationId.AzureDevOps && project != null) {
488
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
489
+ tokenOptInfo,
490
+ 'getRepoOfProjectFn',
491
+ );
492
+ const token = tokenWithInfo.accessToken;
493
+
494
+ try {
495
+ const result = await provider['getRepoOfProjectFn']?.(
496
+ { namespace: owner, name: name, project: project },
497
+ { token: token, isPAT: options?.isPAT, baseUrl: options?.baseUrl },
498
+ );
499
+ return result?.data;
500
+ } catch (e) {
501
+ return this.handleProviderError<ProviderRepository>(tokenWithInfo, e);
502
+ }
503
+ } else {
504
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(tokenOptInfo, 'getRepoFn');
505
+ const token = tokenWithInfo.accessToken;
506
+
507
+ try {
508
+ const result = await provider['getRepoFn']?.(
509
+ { namespace: owner, name: name, project: project },
510
+ { token: token, isPAT: options?.isPAT, baseUrl: options?.baseUrl },
511
+ );
512
+ return result?.data;
513
+ } catch (e) {
514
+ return this.handleProviderError<ProviderRepository>(tokenWithInfo, e);
515
+ }
516
+ }
517
+ }
518
+
519
+ async getCurrentUser(
520
+ tokenOptInfo: TokenOptInfo,
521
+ options?: { isPAT?: boolean; baseUrl?: string },
522
+ ): Promise<ProviderAccount | undefined> {
523
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(tokenOptInfo, 'getCurrentUserFn');
524
+ const token = tokenWithInfo.accessToken;
525
+
526
+ try {
527
+ return (
528
+ await provider.getCurrentUserFn?.(
529
+ {},
530
+ { token: token, isPAT: options?.isPAT, baseUrl: options?.baseUrl },
531
+ )
532
+ )?.data;
533
+ } catch (e) {
534
+ return this.handleProviderError<ProviderAccount>(tokenWithInfo, e);
535
+ }
536
+ }
537
+
538
+ async getCurrentUserForInstance(
539
+ tokenOptInfo: TokenOptInfo,
540
+ namespace: string,
541
+ options?: { isPAT?: boolean; baseUrl?: string },
542
+ ): Promise<ProviderAccount | undefined> {
543
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
544
+ tokenOptInfo,
545
+ 'getCurrentUserForInstanceFn',
546
+ );
547
+ const token = tokenWithInfo.accessToken;
548
+
549
+ return (
550
+ await provider.getCurrentUserForInstanceFn?.(
551
+ { namespace: namespace },
552
+ { token: token, isPAT: options?.isPAT, baseUrl: options?.baseUrl },
553
+ )
554
+ )?.data;
555
+ }
556
+
557
+ async getCurrentUserForResource(
558
+ tokenOptInfo: TokenWithInfo,
559
+ resourceId: string,
560
+ options?: { isPAT?: boolean; baseUrl?: string },
561
+ ): Promise<ProviderAccount | undefined> {
562
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
563
+ tokenOptInfo,
564
+ 'getCurrentUserForResourceFn',
565
+ );
566
+ const token = tokenWithInfo.accessToken;
567
+
568
+ try {
569
+ return (
570
+ await provider.getCurrentUserForResourceFn?.(
571
+ { resourceId: resourceId },
572
+ { token: token, isPAT: options?.isPAT, baseUrl: options?.baseUrl },
573
+ )
574
+ )?.data;
575
+ } catch (e) {
576
+ return this.handleProviderError<ProviderAccount>(tokenWithInfo, e);
577
+ }
578
+ }
579
+
580
+ async getJiraResourcesForCurrentUser(
581
+ tokenOptInfo: TokenWithInfo<IssuesCloudHostIntegrationId.Jira>,
582
+ ): Promise<ProviderJiraResource[] | undefined> {
583
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
584
+ tokenOptInfo,
585
+ 'getJiraResourcesForCurrentUserFn',
586
+ );
587
+ const token = tokenWithInfo.accessToken;
588
+
589
+ try {
590
+ return (await provider.getJiraResourcesForCurrentUserFn?.({ token: token }))?.data;
591
+ } catch (e) {
592
+ return this.handleProviderError<ProviderJiraResource[] | undefined>(tokenWithInfo, e);
593
+ }
594
+ }
595
+
596
+ async getLinearOrganization(
597
+ tokenOptInfo: TokenWithInfo<IssuesCloudHostIntegrationId.Linear>,
598
+ ): Promise<ProviderLinearOrganization | undefined> {
599
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
600
+ tokenOptInfo,
601
+ 'getLinearOrganizationFn',
602
+ );
603
+ const token = tokenWithInfo.accessToken;
604
+
605
+ try {
606
+ const x = await provider.getLinearOrganizationFn?.({ token: token });
607
+ const y = x?.data;
608
+ return y;
609
+ } catch (e) {
610
+ return this.handleProviderError<ProviderLinearOrganization | undefined>(tokenWithInfo, e);
611
+ }
612
+ }
613
+
614
+ async getLinearTeamsForCurrentUser(
615
+ tokenOptInfo: TokenWithInfo<IssuesCloudHostIntegrationId.Linear>,
616
+ ): Promise<ProviderLinearTeam[] | undefined> {
617
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
618
+ tokenOptInfo,
619
+ 'getLinearTeamsForCurrentUserFn',
620
+ );
621
+ const token = tokenWithInfo.accessToken;
622
+
623
+ try {
624
+ return (await provider.getLinearTeamsForCurrentUserFn?.({ token: token }))?.data;
625
+ } catch (e) {
626
+ return this.handleProviderError<ProviderLinearTeam[] | undefined>(tokenWithInfo, e);
627
+ }
628
+ }
629
+
630
+ async getAzureResourcesForUser(
631
+ tokenOptInfo: TokenWithInfo<
632
+ GitCloudHostIntegrationId.AzureDevOps | GitSelfManagedHostIntegrationId.AzureDevOpsServer
633
+ >,
634
+ userId: string,
635
+ options?: { isPAT?: boolean; baseUrl?: string },
636
+ ): Promise<ProviderAzureResource[] | undefined> {
637
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
638
+ tokenOptInfo,
639
+ 'getAzureResourcesForUserFn',
640
+ );
641
+ const token = tokenWithInfo.accessToken;
642
+
643
+ try {
644
+ return (
645
+ await provider.getAzureResourcesForUserFn?.(
646
+ { userId: userId },
647
+ { token: token, isPAT: options?.isPAT, baseUrl: options?.baseUrl },
648
+ )
649
+ )?.data;
650
+ } catch (e) {
651
+ return this.handleProviderError<ProviderAzureResource[] | undefined>(tokenWithInfo, e);
652
+ }
653
+ }
654
+
655
+ async getBitbucketResourcesForCurrentUser(
656
+ tokenOptInfo: TokenWithInfo<GitCloudHostIntegrationId.Bitbucket>,
657
+ ): Promise<ProviderBitbucketResource[] | undefined> {
658
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
659
+ tokenOptInfo,
660
+ 'getBitbucketResourcesForCurrentUserFn',
661
+ );
662
+ const token = tokenWithInfo.accessToken;
663
+
664
+ try {
665
+ return (await provider.getBitbucketResourcesForCurrentUserFn?.({}, { token: token }))?.data;
666
+ } catch (e) {
667
+ return this.handleProviderError<ProviderBitbucketResource[] | undefined>(tokenWithInfo, e);
668
+ }
669
+ }
670
+
671
+ async getBitbucketPullRequestsAuthoredByUserForWorkspace(
672
+ tokenOptInfo: TokenWithInfo<GitCloudHostIntegrationId.Bitbucket>,
673
+ userId: string,
674
+ workspaceSlug: string,
675
+ ): Promise<ProviderPullRequest[] | undefined> {
676
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
677
+ tokenOptInfo,
678
+ 'getBitbucketPullRequestsAuthoredByUserForWorkspaceFn',
679
+ );
680
+ const token = tokenWithInfo.accessToken;
681
+
682
+ try {
683
+ return (
684
+ await provider.getBitbucketPullRequestsAuthoredByUserForWorkspaceFn?.(
685
+ { userId: userId, workspaceSlug: workspaceSlug },
686
+ { token: token },
687
+ )
688
+ )?.data;
689
+ } catch (e) {
690
+ return this.handleProviderError(tokenWithInfo, e);
691
+ }
692
+ }
693
+
694
+ async getBitbucketServerPullRequestsForCurrentUser(
695
+ tokenOptInfo: TokenWithInfo<GitSelfManagedHostIntegrationId.BitbucketServer>,
696
+ baseUrl: string,
697
+ ): Promise<ProviderPullRequest[] | undefined> {
698
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
699
+ tokenOptInfo,
700
+ 'getBitbucketServerPullRequestsForCurrentUserFn',
701
+ );
702
+ const token = tokenWithInfo.accessToken;
703
+ try {
704
+ return (
705
+ await provider.getBitbucketServerPullRequestsForCurrentUserFn?.({}, { token: token, baseUrl: baseUrl })
706
+ )?.data;
707
+ } catch (e) {
708
+ return this.handleProviderError(tokenWithInfo, e);
709
+ }
710
+ }
711
+
712
+ async getJiraProjectsForResources(
713
+ tokenOptInfo: TokenWithInfo<IssuesCloudHostIntegrationId.Jira>,
714
+ resourceIds: string[],
715
+ ): Promise<ProviderJiraProject[] | undefined> {
716
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
717
+ tokenOptInfo,
718
+ 'getJiraProjectsForResourcesFn',
719
+ );
720
+ const token = tokenWithInfo.accessToken;
721
+
722
+ try {
723
+ return (await provider.getJiraProjectsForResourcesFn?.({ resourceIds: resourceIds }, { token: token }))
724
+ ?.data;
725
+ } catch (e) {
726
+ return this.handleProviderError<ProviderJiraProject[] | undefined>(tokenWithInfo, e);
727
+ }
728
+ }
729
+
730
+ async getAzureProjectsForResource(
731
+ tokenOptInfo: TokenWithInfo<
732
+ GitCloudHostIntegrationId.AzureDevOps | GitSelfManagedHostIntegrationId.AzureDevOpsServer
733
+ >,
734
+ namespace: string,
735
+ options?: { cursor?: string; isPAT?: boolean; baseUrl?: string },
736
+ ): Promise<PagedResult<ProviderAzureProject>> {
737
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
738
+ tokenOptInfo,
739
+ 'getAzureProjectsForResourceFn',
740
+ );
741
+ const token = tokenWithInfo.accessToken;
742
+
743
+ // Azure only supports PAT for this call
744
+ const azureToken = options?.isPAT ? token : this.getAzurePATForOAuthToken(token);
745
+
746
+ try {
747
+ return await this.getPagedResult<ProviderAzureProject>(
748
+ { namespace: namespace, ...options },
749
+ provider.getAzureProjectsForResourceFn,
750
+ { ...tokenWithInfo, accessToken: azureToken },
751
+ options?.cursor,
752
+ options?.isPAT,
753
+ options?.baseUrl,
754
+ );
755
+ } catch (e) {
756
+ return this.handleProviderError<PagedResult<ProviderAzureProject>>(tokenWithInfo, e);
757
+ }
758
+ }
759
+
760
+ async getReposForAzureProject(
761
+ tokenOptInfo: TokenWithInfo<
762
+ GitCloudHostIntegrationId.AzureDevOps | GitSelfManagedHostIntegrationId.AzureDevOpsServer
763
+ >,
764
+ namespace: string,
765
+ project: string,
766
+ options?: GetReposOptions & { isPAT?: boolean; baseUrl?: string },
767
+ ): Promise<PagedResult<ProviderRepository>> {
768
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
769
+ tokenOptInfo,
770
+ 'getReposForAzureProjectFn',
771
+ );
772
+
773
+ return this.getPagedResult<ProviderRepository>(
774
+ { namespace: namespace, project: project, ...options },
775
+ provider.getReposForAzureProjectFn,
776
+ tokenWithInfo,
777
+ options?.cursor,
778
+ options?.isPAT,
779
+ options?.baseUrl,
780
+ );
781
+ }
782
+
783
+ async getPullRequestsForRepos(
784
+ tokenOptInfo: TokenOptInfo,
785
+ reposOrIds: ProviderReposInput,
786
+ options?: GetPullRequestsOptions & { isPAT?: boolean; baseUrl?: string },
787
+ ): Promise<PagedResult<ProviderPullRequest>> {
788
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
789
+ tokenOptInfo,
790
+ 'getPullRequestsForReposFn',
791
+ );
792
+
793
+ return this.getPagedResult<ProviderPullRequest>(
794
+ {
795
+ ...(this.isRepoIdsInput(reposOrIds) ? { repoIds: reposOrIds } : { repos: reposOrIds }),
796
+ ...options,
797
+ },
798
+ provider.getPullRequestsForReposFn,
799
+ tokenWithInfo,
800
+ options?.cursor,
801
+ options?.isPAT,
802
+ options?.baseUrl,
803
+ );
804
+ }
805
+
806
+ async getPullRequestsForRepo(
807
+ tokenOptInfo: TokenOptInfo,
808
+ repo: ProviderRepoInput,
809
+ options?: GetPullRequestsOptions & { isPAT?: boolean; baseUrl?: string },
810
+ ): Promise<PagedResult<ProviderPullRequest>> {
811
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
812
+ tokenOptInfo,
813
+ 'getPullRequestsForRepoFn',
814
+ );
815
+
816
+ return this.getPagedResult<ProviderPullRequest>(
817
+ { repo: repo, ...options },
818
+ provider.getPullRequestsForRepoFn,
819
+ tokenWithInfo,
820
+ options?.cursor,
821
+ options?.isPAT,
822
+ options?.baseUrl,
823
+ );
824
+ }
825
+
826
+ async getPullRequestsForUser(
827
+ tokenWithInfo: TokenWithInfo<GitCloudHostIntegrationId.Bitbucket>,
828
+ userId: string,
829
+ options?: { isPAT?: boolean } & GetPullRequestsForUserOptions,
830
+ ): Promise<PagedResult<ProviderPullRequest>>;
831
+ async getPullRequestsForUser(
832
+ tokenWithInfo: TokenWithInfo<Exclude<IntegrationIds, GitCloudHostIntegrationId.Bitbucket>>,
833
+ username: string,
834
+ options?: { isPAT?: boolean } & GetPullRequestsForUserOptions,
835
+ ): Promise<PagedResult<ProviderPullRequest>>;
836
+ async getPullRequestsForUser(
837
+ tokenOptInfo: TokenWithInfo,
838
+ usernameOrId: string,
839
+ options?: { isPAT?: boolean } & GetPullRequestsForUserOptions,
840
+ ): Promise<PagedResult<ProviderPullRequest>> {
841
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
842
+ tokenOptInfo,
843
+ 'getPullRequestsForUserFn',
844
+ );
845
+
846
+ return this.getPagedResult<ProviderPullRequest>(
847
+ {
848
+ ...(tokenWithInfo.providerId === GitCloudHostIntegrationId.Bitbucket
849
+ ? { userId: usernameOrId }
850
+ : { username: usernameOrId }),
851
+ ...options,
852
+ },
853
+ provider.getPullRequestsForUserFn,
854
+ tokenWithInfo,
855
+ options?.cursor,
856
+ options?.isPAT,
857
+ options?.baseUrl,
858
+ );
859
+ }
860
+
861
+ async getPullRequestsForAzureProjects(
862
+ tokenOptInfo: TokenWithInfo<
863
+ GitCloudHostIntegrationId.AzureDevOps | GitSelfManagedHostIntegrationId.AzureDevOpsServer
864
+ >,
865
+ projects: { namespace: string; project: string }[],
866
+ options?: {
867
+ authorLogin?: string;
868
+ assigneeLogins?: string[];
869
+ isPAT?: boolean;
870
+ baseUrl?: string;
871
+ },
872
+ ): Promise<ProviderPullRequest[] | undefined> {
873
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
874
+ tokenOptInfo,
875
+ 'getPullRequestsForAzureProjectsFn',
876
+ );
877
+ const token = tokenWithInfo.accessToken;
878
+
879
+ // Azure only supports PAT for this call
880
+ const azureToken = options?.isPAT ? token : this.getAzurePATForOAuthToken(token);
881
+
882
+ try {
883
+ return (
884
+ await provider.getPullRequestsForAzureProjectsFn?.(
885
+ { projects: projects, ...options },
886
+ { token: azureToken, isPAT: options?.isPAT, baseUrl: options?.baseUrl },
887
+ )
888
+ )?.data;
889
+ } catch (e) {
890
+ return this.handleProviderError<ProviderPullRequest[]>(tokenWithInfo, e);
891
+ }
892
+ }
893
+
894
+ async mergePullRequest(
895
+ tokenOptInfo: TokenWithInfo,
896
+ pr: PullRequest,
897
+ options?: {
898
+ mergeMethod?: PullRequestMergeMethod;
899
+ isPAT?: boolean;
900
+ baseUrl?: string;
901
+ },
902
+ ): Promise<boolean> {
903
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
904
+ tokenOptInfo,
905
+ 'mergePullRequestFn',
906
+ );
907
+ const token = tokenWithInfo.accessToken;
908
+ const headRef = pr.refs?.head;
909
+ if (headRef == null) return false;
910
+
911
+ if (provider.id === GitCloudHostIntegrationId.AzureDevOps && pr.project == null) {
912
+ return false;
913
+ }
914
+
915
+ try {
916
+ await provider.mergePullRequestFn?.(
917
+ {
918
+ pullRequest: {
919
+ headRef: { oid: headRef.sha },
920
+ id: pr.id,
921
+ number: Number.parseInt(pr.id, 10),
922
+ repository: {
923
+ id: pr.repository.repo,
924
+ name: pr.repository.repo,
925
+ project: pr.project?.name ?? '',
926
+ owner: {
927
+ login: pr.repository.owner,
928
+ },
929
+ },
930
+ version: pr.version,
931
+ },
932
+ ...options,
933
+ },
934
+ { token: token, isPAT: options?.isPAT, baseUrl: options?.baseUrl },
935
+ );
936
+ return true;
937
+ } catch (e) {
938
+ return this.handleProviderError<boolean>(tokenWithInfo, e);
939
+ }
940
+ }
941
+
942
+ async getIssuesForRepos(
943
+ tokenOptInfo: TokenOptInfo,
944
+ reposOrIds: ProviderReposInput,
945
+ options?: GetIssuesOptions & { isPAT?: boolean; baseUrl?: string },
946
+ ): Promise<PagedResult<ProviderIssue>> {
947
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
948
+ tokenOptInfo,
949
+ 'getIssuesForReposFn',
950
+ );
951
+
952
+ return this.getPagedResult<ProviderIssue>(
953
+ {
954
+ ...(this.isRepoIdsInput(reposOrIds) ? { repoIds: reposOrIds } : { repos: reposOrIds }),
955
+ ...options,
956
+ },
957
+ provider.getIssuesForReposFn,
958
+ tokenWithInfo,
959
+ options?.cursor,
960
+ options?.isPAT,
961
+ options?.baseUrl,
962
+ );
963
+ }
964
+
965
+ async getIssuesForRepo(
966
+ tokenOptInfo: TokenOptInfo,
967
+ repo: ProviderRepoInput,
968
+ options?: GetIssuesOptions & { isPAT?: boolean; baseUrl?: string },
969
+ ): Promise<PagedResult<ProviderIssue>> {
970
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
971
+ tokenOptInfo,
972
+ 'getIssuesForRepoFn',
973
+ );
974
+
975
+ return this.getPagedResult<ProviderIssue>(
976
+ { repo: repo, ...options },
977
+ provider.getIssuesForRepoFn,
978
+ tokenWithInfo,
979
+ options?.cursor,
980
+ options?.isPAT,
981
+ options?.baseUrl,
982
+ );
983
+ }
984
+
985
+ async getIssuesForCurrentUser(
986
+ tokenOptInfo: TokenWithInfo,
987
+ options?: PagingInput & { isPAT?: boolean; baseUrl?: string },
988
+ ): Promise<PagedResult<ProviderIssue>> {
989
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
990
+ tokenOptInfo,
991
+ 'getIssuesForCurrentUserFn',
992
+ );
993
+ return this.getPagedResult<ProviderIssue>(
994
+ options,
995
+ provider.getIssuesForCurrentUserFn,
996
+ tokenWithInfo,
997
+ options?.cursor ?? undefined,
998
+ options?.isPAT,
999
+ options?.baseUrl,
1000
+ );
1001
+ }
1002
+
1003
+ async getIssuesForAzureProject(
1004
+ tokenOptInfo: TokenOptInfo<
1005
+ GitCloudHostIntegrationId.AzureDevOps | GitSelfManagedHostIntegrationId.AzureDevOpsServer
1006
+ >,
1007
+ namespace: string,
1008
+ project: string,
1009
+ options?: GetIssuesOptions & { isPAT?: boolean; baseUrl?: string },
1010
+ ): Promise<PagedResult<ProviderIssue>> {
1011
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
1012
+ tokenOptInfo,
1013
+ 'getIssuesForAzureProjectFn',
1014
+ );
1015
+
1016
+ return this.getPagedResult<ProviderIssue>(
1017
+ { namespace: namespace, project: project, ...options },
1018
+ provider.getIssuesForAzureProjectFn,
1019
+ tokenWithInfo,
1020
+ options?.cursor,
1021
+ options?.isPAT,
1022
+ options?.baseUrl,
1023
+ );
1024
+ }
1025
+
1026
+ async getIssuesForProject(
1027
+ tokenOptInfo: TokenWithInfo,
1028
+ project: string,
1029
+ resourceId: string,
1030
+ options?: GetIssuesOptions,
1031
+ ): Promise<ProviderIssue[] | undefined> {
1032
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
1033
+ tokenOptInfo,
1034
+ 'getIssuesForProjectFn',
1035
+ );
1036
+ const token = tokenWithInfo.accessToken;
1037
+
1038
+ try {
1039
+ const result = await provider.getIssuesForProjectFn?.(
1040
+ { projectKey: project, resourceId: resourceId, ...options },
1041
+ { token: token },
1042
+ );
1043
+
1044
+ return result?.data;
1045
+ } catch (e) {
1046
+ return this.handleProviderError<ProviderIssue[] | undefined>(tokenWithInfo, e);
1047
+ }
1048
+ }
1049
+
1050
+ async getIssuesForResourceForCurrentUser(
1051
+ tokenOptInfo: TokenWithInfo,
1052
+ resourceId: string,
1053
+ options?: { cursor?: string; isPAT?: boolean; baseUrl?: string },
1054
+ ): Promise<PagedResult<ProviderIssue>> {
1055
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(
1056
+ tokenOptInfo,
1057
+ 'getIssuesForResourceForCurrentUserFn',
1058
+ );
1059
+
1060
+ return this.getPagedResult<ProviderIssue>(
1061
+ { resourceId: resourceId },
1062
+ provider.getIssuesForResourceForCurrentUserFn,
1063
+ tokenWithInfo,
1064
+ options?.cursor,
1065
+ options?.isPAT,
1066
+ options?.baseUrl,
1067
+ );
1068
+ }
1069
+
1070
+ async getIssue(
1071
+ tokenOptInfo: TokenWithInfo,
1072
+ input: { resourceId: string; number: string } | { namespace: string; name: string; number: string },
1073
+ options?: { isPAT?: boolean; baseUrl?: string },
1074
+ ): Promise<ProviderIssue | undefined> {
1075
+ const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(tokenOptInfo, 'getIssueFn');
1076
+ const token = tokenWithInfo.accessToken;
1077
+
1078
+ try {
1079
+ const result = await provider.getIssueFn?.(input, {
1080
+ token: token,
1081
+ isPAT: options?.isPAT,
1082
+ baseUrl: options?.baseUrl,
1083
+ });
1084
+
1085
+ return result?.data;
1086
+ } catch (e) {
1087
+ return this.handleProviderError<ProviderIssue | undefined>(tokenWithInfo, e);
1088
+ }
1089
+ }
1090
+ }
1091
+
1092
+ // This is copied over from the shared provider library because the current version is not respecting the "forceIsFetch: true"
1093
+ // option in the config and our custom fetch function isn't being wrapped by the necessary fetch wrapper. Remove this once the library
1094
+ // properly wraps our custom fetch and use `forceIsFetch: true` in the config.
1095
+ async function parseFetchResponseForApi<T>(response: Response): Promise<ProviderRequestResponse<T>> {
1096
+ const contentType = response.headers.get('content-type') || '';
1097
+ let body;
1098
+
1099
+ // parse the response body
1100
+ if (contentType.startsWith('application/json')) {
1101
+ const text = await response.text();
1102
+ body = text.trim().length > 0 ? JSON.parse(text) : null;
1103
+ } else if (contentType.startsWith('text/') || contentType === '') {
1104
+ body = await response.text();
1105
+ } else if (contentType.startsWith('application/vnd.github.raw+json')) {
1106
+ body = await response.arrayBuffer();
1107
+ } else {
1108
+ throw new Error(`Unsupported content-type: ${contentType}`);
1109
+ }
1110
+
1111
+ const result = {
1112
+ body: body,
1113
+ headers: Object.fromEntries(response.headers.entries()),
1114
+ status: response.status,
1115
+ statusText: response.statusText,
1116
+ };
1117
+
1118
+ // throw an error if the response is not ok
1119
+ if (!response.ok) {
1120
+ const error = new Error(response.statusText);
1121
+ Object.assign(error, { response: result });
1122
+ throw error;
1123
+ }
1124
+
1125
+ return result;
1126
+ }