Skip to content

Commit 4609219

Browse files
committed
Add linear provider and implement getting linear teams
(#4543, #4579)
1 parent 3e3d673 commit 4609219

File tree

15 files changed

+220
-29
lines changed

15 files changed

+220
-29
lines changed

docs/telemetry-events.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,7 @@ void
630630
```typescript
631631
{
632632
'hostingProvider.key': string,
633-
'hostingProvider.provider': 'github' | 'gitlab' | 'bitbucket' | 'azureDevOps' | 'bitbucket-server' | 'github-enterprise' | 'cloud-github-enterprise' | 'gitlab-self-hosted' | 'cloud-gitlab-self-hosted' | 'azure-devops-server' | 'jira' | 'trello'
633+
'hostingProvider.provider': 'github' | 'gitlab' | 'bitbucket' | 'azureDevOps' | 'bitbucket-server' | 'github-enterprise' | 'cloud-github-enterprise' | 'gitlab-self-hosted' | 'cloud-gitlab-self-hosted' | 'azure-devops-server' | 'jira' | 'linear' | 'trello'
634634
}
635635
```
636636

@@ -641,7 +641,7 @@ void
641641
```typescript
642642
{
643643
'hostingProvider.key': string,
644-
'hostingProvider.provider': 'github' | 'gitlab' | 'bitbucket' | 'azureDevOps' | 'bitbucket-server' | 'github-enterprise' | 'cloud-github-enterprise' | 'gitlab-self-hosted' | 'cloud-gitlab-self-hosted' | 'azure-devops-server' | 'jira' | 'trello'
644+
'hostingProvider.provider': 'github' | 'gitlab' | 'bitbucket' | 'azureDevOps' | 'bitbucket-server' | 'github-enterprise' | 'cloud-github-enterprise' | 'gitlab-self-hosted' | 'cloud-gitlab-self-hosted' | 'azure-devops-server' | 'jira' | 'linear' | 'trello'
645645
}
646646
```
647647

@@ -652,7 +652,7 @@ void
652652
```typescript
653653
{
654654
'issueProvider.key': string,
655-
'issueProvider.provider': 'github' | 'gitlab' | 'bitbucket' | 'azureDevOps' | 'bitbucket-server' | 'github-enterprise' | 'cloud-github-enterprise' | 'gitlab-self-hosted' | 'cloud-gitlab-self-hosted' | 'azure-devops-server' | 'jira' | 'trello'
655+
'issueProvider.provider': 'github' | 'gitlab' | 'bitbucket' | 'azureDevOps' | 'bitbucket-server' | 'github-enterprise' | 'cloud-github-enterprise' | 'gitlab-self-hosted' | 'cloud-gitlab-self-hosted' | 'azure-devops-server' | 'jira' | 'linear' | 'trello'
656656
}
657657
```
658658

@@ -663,7 +663,7 @@ void
663663
```typescript
664664
{
665665
'issueProvider.key': string,
666-
'issueProvider.provider': 'github' | 'gitlab' | 'bitbucket' | 'azureDevOps' | 'bitbucket-server' | 'github-enterprise' | 'cloud-github-enterprise' | 'gitlab-self-hosted' | 'cloud-gitlab-self-hosted' | 'azure-devops-server' | 'jira' | 'trello'
666+
'issueProvider.provider': 'github' | 'gitlab' | 'bitbucket' | 'azureDevOps' | 'bitbucket-server' | 'github-enterprise' | 'cloud-github-enterprise' | 'gitlab-self-hosted' | 'cloud-gitlab-self-hosted' | 'azure-devops-server' | 'jira' | 'linear' | 'trello'
667667
}
668668
```
669669

@@ -697,7 +697,7 @@ or when connection refresh is skipped due to being a non-cloud session
697697
698698
```typescript
699699
{
700-
'integration.id': 'github' | 'gitlab' | 'bitbucket' | 'azureDevOps' | 'bitbucket-server' | 'github-enterprise' | 'cloud-github-enterprise' | 'gitlab-self-hosted' | 'cloud-gitlab-self-hosted' | 'azure-devops-server' | 'jira' | 'trello'
700+
'integration.id': 'github' | 'gitlab' | 'bitbucket' | 'azureDevOps' | 'bitbucket-server' | 'github-enterprise' | 'cloud-github-enterprise' | 'gitlab-self-hosted' | 'cloud-gitlab-self-hosted' | 'azure-devops-server' | 'jira' | 'linear' | 'trello'
701701
}
702702
```
703703

@@ -2819,7 +2819,7 @@ void
28192819
```typescript
28202820
{
28212821
'hostingProvider.key': string,
2822-
'hostingProvider.provider': 'github' | 'gitlab' | 'bitbucket' | 'azureDevOps' | 'bitbucket-server' | 'github-enterprise' | 'cloud-github-enterprise' | 'gitlab-self-hosted' | 'cloud-gitlab-self-hosted' | 'azure-devops-server' | 'jira' | 'trello',
2822+
'hostingProvider.provider': 'github' | 'gitlab' | 'bitbucket' | 'azureDevOps' | 'bitbucket-server' | 'github-enterprise' | 'cloud-github-enterprise' | 'gitlab-self-hosted' | 'cloud-gitlab-self-hosted' | 'azure-devops-server' | 'jira' | 'linear' | 'trello',
28232823
// @deprecated: true
28242824
'remoteProviders.key': string
28252825
}
@@ -2832,7 +2832,7 @@ void
28322832
```typescript
28332833
{
28342834
'hostingProvider.key': string,
2835-
'hostingProvider.provider': 'github' | 'gitlab' | 'bitbucket' | 'azureDevOps' | 'bitbucket-server' | 'github-enterprise' | 'cloud-github-enterprise' | 'gitlab-self-hosted' | 'cloud-gitlab-self-hosted' | 'azure-devops-server' | 'jira' | 'trello',
2835+
'hostingProvider.provider': 'github' | 'gitlab' | 'bitbucket' | 'azureDevOps' | 'bitbucket-server' | 'github-enterprise' | 'cloud-github-enterprise' | 'gitlab-self-hosted' | 'cloud-gitlab-self-hosted' | 'azure-devops-server' | 'jira' | 'linear' | 'trello',
28362836
// @deprecated: true
28372837
'remoteProviders.key': string
28382838
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25009,7 +25009,7 @@
2500925009
},
2501025010
"dependencies": {
2501125011
"@gitkraken/gitkraken-components": "13.0.0-vnext.8",
25012-
"@gitkraken/provider-apis": "0.29.6",
25012+
"@gitkraken/provider-apis": "../../../provider-apis-package-js.repo",
2501325013
"@gitkraken/shared-web-components": "0.1.1-rc.15",
2501425014
"@gk-nzaytsev/fast-string-truncated-width": "1.1.0",
2501525015
"@lit-labs/signals": "0.1.3",

pnpm-lock.yaml

Lines changed: 2 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/constants.integrations.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export enum GitSelfManagedHostIntegrationId {
1616

1717
export enum IssuesCloudHostIntegrationId {
1818
Jira = 'jira',
19+
Linear = 'linear',
1920
Trello = 'trello',
2021
}
2122

src/plus/integrations/authentication/integrationAuthenticationService.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,11 @@ export class IntegrationAuthenticationService implements Disposable {
148148
await import(/* webpackChunkName: "integrations" */ './jira')
149149
).JiraAuthenticationProvider(this.container, this, this.configuredIntegrationService);
150150
break;
151+
case IssuesCloudHostIntegrationId.Linear:
152+
provider = new (
153+
await import(/* webpackChunkName: "integrations" */ './linear')
154+
).LinearAuthenticationProvider();
155+
break;
151156
default:
152157
provider = new BuiltInAuthenticationProvider(
153158
this.container,
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import type { Disposable, Event } from 'vscode';
2+
import type { Sources } from '../../../constants.telemetry';
3+
import { configuration } from '../../../system/-webview/configuration';
4+
import type {
5+
IntegrationAuthenticationProvider,
6+
IntegrationAuthenticationSessionDescriptor,
7+
} from './integrationAuthenticationProvider';
8+
import type { ProviderAuthenticationSession } from './models';
9+
10+
export class LinearAuthenticationProvider implements IntegrationAuthenticationProvider {
11+
// I want to read the token from the config "ololo-linear-config":
12+
private currentToken: string | undefined = (configuration.get('ololo-linear-token') as string) ?? undefined;
13+
14+
deleteSession(_descriptor: IntegrationAuthenticationSessionDescriptor): Promise<void> {
15+
//throw new Error('Method not implemented.');
16+
this.currentToken = undefined;
17+
return Promise.resolve();
18+
}
19+
deleteAllSessions(): Promise<void> {
20+
//throw new Error('Method not implemented.');
21+
this.currentToken = undefined;
22+
return Promise.resolve();
23+
}
24+
getSession(
25+
_descriptor: IntegrationAuthenticationSessionDescriptor,
26+
_options?:
27+
| { createIfNeeded?: boolean; forceNewSession?: boolean; sync?: never; source?: Sources }
28+
| { createIfNeeded?: never; forceNewSession?: never; sync: boolean; source?: Sources },
29+
): Promise<ProviderAuthenticationSession | undefined> {
30+
return Promise.resolve(
31+
this.currentToken
32+
? {
33+
accessToken: this.currentToken,
34+
id: 'linear',
35+
account: {
36+
id: 'linear',
37+
label: 'Linear',
38+
},
39+
scopes: ['read'],
40+
cloud: true,
41+
expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
42+
domain: 'linear.app',
43+
}
44+
: undefined,
45+
);
46+
}
47+
get onDidChange(): Event<void> {
48+
return (_listener: (e: void) => any, _thisArgs?: any, _disposables?: Disposable[]): Disposable => {
49+
return { dispose: () => {} };
50+
};
51+
}
52+
dispose(): void {}
53+
}

src/plus/integrations/authentication/models.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export interface CloudIntegrationConnection {
4444

4545
export type CloudIntegrationType =
4646
| 'jira'
47+
| 'linear'
4748
| 'trello'
4849
| 'gitlab'
4950
| 'github'
@@ -70,6 +71,7 @@ export function isSupportedCloudIntegrationId(id: string): id is SupportedCloudI
7071

7172
export const toIntegrationId: { [key in CloudIntegrationType]: IntegrationIds } = {
7273
jira: IssuesCloudHostIntegrationId.Jira,
74+
linear: IssuesCloudHostIntegrationId.Linear,
7375
trello: IssuesCloudHostIntegrationId.Trello,
7476
gitlab: GitCloudHostIntegrationId.GitLab,
7577
github: GitCloudHostIntegrationId.GitHub,
@@ -83,6 +85,7 @@ export const toIntegrationId: { [key in CloudIntegrationType]: IntegrationIds }
8385

8486
export const toCloudIntegrationType: { [key in IntegrationIds]: CloudIntegrationType | undefined } = {
8587
[IssuesCloudHostIntegrationId.Jira]: 'jira',
88+
[IssuesCloudHostIntegrationId.Linear]: 'linear',
8689
[IssuesCloudHostIntegrationId.Trello]: 'trello',
8790
[GitCloudHostIntegrationId.GitLab]: 'gitlab',
8891
[GitCloudHostIntegrationId.GitHub]: 'github',

src/plus/integrations/integrationService.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,16 @@ export class IntegrationService implements Disposable {
537537
) as IssuesIntegration as IntegrationById<T>;
538538
break;
539539

540+
case IssuesCloudHostIntegrationId.Linear:
541+
integration = new (
542+
await import(/* webpackChunkName: "integrations" */ './providers/linear')
543+
).LinearIntegration(
544+
this.container,
545+
this.authenticationService,
546+
this.getProvidersApi.bind(this),
547+
this._onDidChangeIntegrationConnection,
548+
) as IssuesIntegration as IntegrationById<T>;
549+
break;
540550
default:
541551
throw new Error(`Integration with '${id}' is not supported`);
542552
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import type { CancellationToken } from 'vscode';
2+
import { IssuesCloudHostIntegrationId } from '../../../constants.integrations';
3+
import type { Account } from '../../../git/models/author';
4+
import type { Issue, IssueShape } from '../../../git/models/issue';
5+
import type { IssueOrPullRequest, IssueOrPullRequestType } from '../../../git/models/issueOrPullRequest';
6+
import type { IssueResourceDescriptor, ResourceDescriptor } from '../../../git/models/resourceDescriptor';
7+
import type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider';
8+
import type { ProviderAuthenticationSession } from '../authentication/models';
9+
import { IssuesIntegration } from '../models/issuesIntegration';
10+
import type { IssueFilter } from './models';
11+
import { providersMetadata, toIssueShape } from './models';
12+
13+
const metadata = providersMetadata[IssuesCloudHostIntegrationId.Linear];
14+
const authProvider = Object.freeze({ id: metadata.id, scopes: metadata.scopes });
15+
const maxPagesPerRequest = 10;
16+
17+
export interface LinearTeamDescriptor extends IssueResourceDescriptor {
18+
url: string;
19+
}
20+
21+
export interface LinearProjectDescriptor extends IssueResourceDescriptor {}
22+
23+
export class LinearIntegration extends IssuesIntegration<IssuesCloudHostIntegrationId.Linear> {
24+
protected override getProviderResourcesForUser(
25+
_session: ProviderAuthenticationSession,
26+
): Promise<ResourceDescriptor[] | undefined> {
27+
throw new Error('Method not implemented.');
28+
}
29+
protected override getProviderProjectsForResources(
30+
_session: ProviderAuthenticationSession,
31+
_resources: ResourceDescriptor[],
32+
): Promise<ResourceDescriptor[] | undefined> {
33+
throw new Error('Method not implemented.');
34+
}
35+
readonly authProvider: IntegrationAuthenticationProviderDescriptor = authProvider;
36+
37+
protected override getProviderAccountForResource(
38+
_session: ProviderAuthenticationSession,
39+
_resource: ResourceDescriptor,
40+
): Promise<Account | undefined> {
41+
throw new Error('Method not implemented.');
42+
}
43+
44+
protected override getProviderIssuesForProject(
45+
_session: ProviderAuthenticationSession,
46+
_project: ResourceDescriptor,
47+
_options?: { user?: string; filters?: IssueFilter[] },
48+
): Promise<IssueShape[] | undefined> {
49+
throw new Error('Method not implemented.');
50+
}
51+
52+
override get id(): IssuesCloudHostIntegrationId.Linear {
53+
return IssuesCloudHostIntegrationId.Linear;
54+
}
55+
protected override get key(): 'linear' {
56+
return 'linear';
57+
}
58+
override get name(): string {
59+
return metadata.name;
60+
}
61+
override get domain(): string {
62+
return metadata.domain;
63+
}
64+
protected override async searchProviderMyIssues(
65+
_session: ProviderAuthenticationSession,
66+
_resources?: ResourceDescriptor[],
67+
_cancellation?: CancellationToken,
68+
): Promise<IssueShape[] | undefined> {
69+
return Promise.resolve(undefined);
70+
}
71+
protected override getProviderIssueOrPullRequest(
72+
_session: ProviderAuthenticationSession,
73+
_resource: ResourceDescriptor,
74+
_id: string,
75+
_type: undefined | IssueOrPullRequestType,
76+
): Promise<IssueOrPullRequest | undefined> {
77+
throw new Error('Method not implemented.');
78+
}
79+
protected override getProviderIssue(
80+
_session: ProviderAuthenticationSession,
81+
_resource: ResourceDescriptor,
82+
_id: string,
83+
): Promise<Issue | undefined> {
84+
throw new Error('Method not implemented.');
85+
}
86+
}

src/plus/integrations/providers/models.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import type {
1919
Jira,
2020
JiraProject,
2121
JiraResource,
22+
Linear,
2223
NumberedPageInput,
2324
Issue as ProviderApiIssue,
2425
PullRequestWithUniqueID,
@@ -355,7 +356,7 @@ export type GetIssuesForResourceForCurrentUserFn = (
355356
) => Promise<{ data: ProviderIssue[] }>;
356357

357358
export interface ProviderInfo extends ProviderMetadata {
358-
provider: GitHub | GitLab | Bitbucket | BitbucketServer | Jira | Trello | AzureDevOps;
359+
provider: GitHub | GitLab | Bitbucket | BitbucketServer | Jira | Linear | Trello | AzureDevOps;
359360
getRepoFn?: GetRepoFn;
360361
getRepoOfProjectFn?: GetRepoOfProjectFn;
361362
getPullRequestsForReposFn?: GetPullRequestsForReposFn;
@@ -602,6 +603,14 @@ export const providersMetadata: ProvidersMetadata = {
602603
],
603604
supportedIssueFilters: [IssueFilter.Author, IssueFilter.Assignee, IssueFilter.Mention],
604605
},
606+
[IssuesCloudHostIntegrationId.Linear]: {
607+
domain: 'linear.app',
608+
id: IssuesCloudHostIntegrationId.Linear,
609+
name: 'Linear',
610+
type: 'issues',
611+
iconKey: IssuesCloudHostIntegrationId.Linear,
612+
scopes: [],
613+
},
605614
[IssuesCloudHostIntegrationId.Trello]: {
606615
domain: 'trello.com',
607616
id: IssuesCloudHostIntegrationId.Trello,

0 commit comments

Comments
 (0)