Skip to content

Commit c785e31

Browse files
author
guqiankun.gqk
committed
feat: 支持gitlink Api接口
1 parent 1d9f464 commit c785e31

10 files changed

Lines changed: 426 additions & 2 deletions

File tree

packages/alex/src/api/require.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ import * as WorkspaceEdit from '../modules/opensumi__ide-workspace-edit';
4747
// others
4848
import * as CommonDI from '../modules/opensumi__common-di';
4949
import * as AlexCore from '../modules/alipay__alex-core';
50+
import * as CodeService from '../modules/alipay__alex-code-service';
51+
import * as CodeApi from '../modules/alipay__alex-code-api';
5052

5153
// node
5254
import * as os from 'os';
@@ -98,6 +100,8 @@ export function requireModule(module: '@opensumi/ide-workspace-edit'): typeof Wo
98100

99101
export function requireModule(module: '@opensumi/di'): typeof CommonDI;
100102
export function requireModule(module: '@alipay/alex-core'): typeof AlexCore;
103+
export function requireModule(module: '@alipay/alex-code-api'): typeof CodeApi;
104+
export function requireModule(module: '@alipay/alex-code-service'): typeof CodeService;
101105

102106
export function requireModule(module: 'fs'): typeof AlexCore.fs;
103107
export function requireModule(module: 'fs-extra'): typeof AlexCore.fsExtra;
@@ -195,7 +199,10 @@ export function requireModule(module: string): any {
195199
return CommonDI;
196200
case '@alipay/alex-core':
197201
return AlexCore;
198-
202+
case '@alipay/alex-code-api':
203+
return CodeApi;
204+
case '@alipay/alex-code-service':
205+
return CodeService;
199206
case 'fs':
200207
return AlexCore.fs;
201208
case 'fs-extra':
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from '@alipay/alex-code-api';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from '@alipay/alex-code-service';

packages/code-api/src/code-api.provider.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { ICodePlatform, ICodeAPIProvider, ICodeAPIService, CodePlatform } from '
1212
import { AntCodeAPIService } from './antcode/antcode.service';
1313
import { GitHubAPIService } from './github/github.service';
1414
import { GitLabAPIService } from './gitlab/gitlab.service';
15+
import { GitLinkAPIService } from './gitlink/gitlink.service';
1516
import { GitHubView } from './github/github.view';
1617
import { GitLabView } from './gitlab/gitlab.view';
1718

@@ -82,6 +83,9 @@ export class CodeAPIProvider implements ICodeAPIProvider, ClientAppContribution
8283
});
8384
},
8485
});
86+
this.registerPlatformProvider(CodePlatform.gitlink, {
87+
provider: GitLinkAPIService,
88+
});
8589
}
8690

8791
registerPlatformProvider(
@@ -115,6 +119,10 @@ export class CodeAPIProvider implements ICodeAPIProvider, ClientAppContribution
115119
return this.asPlatform(CodePlatform.gitlab) as GitLabAPIService;
116120
}
117121

122+
get gitlink() {
123+
return this.asPlatform(CodePlatform.gitlink) as GitLabAPIService;
124+
}
125+
118126
onStart() {
119127
this.started.resolve();
120128
}

packages/code-api/src/common/config.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,29 @@ export const CODE_PLATFORM_CONFIG: Record<ICodePlatform, ICodePlatformConfig> =
8989
},
9090
},
9191
},
92+
[CodePlatform.gitlink]: {
93+
platform: CodePlatform.gitlab,
94+
hostname: ['www.gitlink.org.cn'],
95+
origin: 'https://www.gitlink.org.cn',
96+
endpoint: 'https://www.gitlink.org.cn',
97+
brand: 'GitLink',
98+
line: {
99+
parse(hash: string) {
100+
let matched: RegExpMatchArray | null = null;
101+
matched = hash.match(/^#L(\d+)(?:-(\d+))?$/);
102+
if (matched) {
103+
return [+matched[1], +(matched[2] || matched[1])];
104+
}
105+
return null;
106+
},
107+
format([startLineNumber, endLineNumber]) {
108+
if (startLineNumber === endLineNumber) {
109+
return `#L${startLineNumber}`;
110+
}
111+
return `#L${startLineNumber}-${endLineNumber}`;
112+
},
113+
},
114+
},
92115
};
93116

94117
export const extendPlatformConfig = (

packages/code-api/src/common/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ export enum CodePlatform {
44
antcode = 'antcode',
55
github = 'github',
66
gitlab = 'gitlab',
7+
gitlink = 'gitlink',
78
}
89

9-
export type ICodePlatform = 'antcode' | 'github' | 'gitlab';
10+
export type ICodePlatform = 'antcode' | 'github' | 'gitlab' | 'gitlink';
1011

1112
export type EntryFileType = 'commit' | 'tree' | 'blob';
1213

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
import { Injectable, Autowired } from '@opensumi/di';
2+
import { localize, IReporterService, formatLocalize, MessageType } from '@opensumi/ide-core-common';
3+
import { request, RequestOptions } from '@alipay/alex-shared';
4+
import { API } from './types';
5+
import { HelperService } from '../common/service';
6+
import { CODE_PLATFORM_CONFIG } from '../common/config';
7+
import type {
8+
TreeEntry,
9+
EntryParam,
10+
ICodeAPIService,
11+
IRepositoryModel,
12+
BranchOrTag,
13+
CommitParams,
14+
Branch,
15+
Project,
16+
} from '../common/types';
17+
import { CodePlatform, CommitFileStatus } from '../common/types';
18+
19+
const toType = (d: API.ResponseCommitFileChange) => {
20+
if (d.new_file) return CommitFileStatus.Added;
21+
else if (d.deleted_file) return CommitFileStatus.Deleted;
22+
else if (d.renamed_file) return CommitFileStatus.Renamed;
23+
return CommitFileStatus.Modified;
24+
};
25+
const toChangeLines = (diff: string) => {
26+
const diffLines = diff ? diff.split('\n') : [];
27+
return diffLines.reduce(
28+
(obj, line) => {
29+
// +++,--- 在首行为文件名
30+
if (line.startsWith('+') && !line.startsWith('+++')) {
31+
obj.additions += 1;
32+
} else if (line.startsWith('-') && !line.startsWith('---')) {
33+
obj.deletions += 1;
34+
}
35+
return obj;
36+
},
37+
{ additions: 0, deletions: 0 }
38+
);
39+
};
40+
/*
41+
* 为保证生产测试一致 暂时使用的是gitlink的生产区接口 https://www.gitlink.org.cn/docs/api
42+
* TODO 待后续测试区接口上线 https://testgitea2.trustie.net/api/swagger#/
43+
*/
44+
45+
@Injectable()
46+
export class GitLinkAPIService implements ICodeAPIService {
47+
@Autowired(IReporterService)
48+
reporter: IReporterService;
49+
50+
@Autowired()
51+
helper: HelperService;
52+
53+
private config = CODE_PLATFORM_CONFIG[CodePlatform.gitlink];
54+
55+
private _PRIVATE_TOKEN: string | null;
56+
57+
shouldShowView = false;
58+
59+
get PRIVATE_TOKEN() {
60+
return this._PRIVATE_TOKEN;
61+
}
62+
63+
constructor() {
64+
this._PRIVATE_TOKEN = this.config.token || '';
65+
}
66+
getUser(repo: IRepositoryModel): Promise<Branch> {
67+
throw new Error('Method not implemented.');
68+
}
69+
70+
async available() {
71+
return true;
72+
}
73+
74+
private showErrorMessage(symbol: string, message?: string, status?: number) {
75+
this.helper.showMessage(CodePlatform.gitlink, {
76+
type: MessageType.Error,
77+
status,
78+
symbol,
79+
message,
80+
});
81+
}
82+
83+
private projectIdMap = new Map<string, Promise<string>>();
84+
85+
async getProjectId(repo: IRepositoryModel) {
86+
const projectPath = this.getProjectPath(repo);
87+
if (!this.projectIdMap.has(projectPath)) {
88+
this.projectIdMap.set(
89+
projectPath,
90+
this.getProject(repo).then(({ id }) => id)
91+
);
92+
}
93+
return this.projectIdMap.get(projectPath)!;
94+
}
95+
96+
private getProjectPath(repo: IRepositoryModel) {
97+
return `${repo.owner}/${repo.name}`;
98+
}
99+
100+
// 静态资源路径
101+
transformStaticResource(repo: IRepositoryModel, path: string) {
102+
return `${this.config.origin}/${this.getProjectPath(repo)}/raw/${repo.commit}/${path}`;
103+
}
104+
105+
protected async request<T>(path: string, options?: RequestOptions): Promise<T> {
106+
try {
107+
const { headers, ...rest } = options || {};
108+
const privateToken = this.PRIVATE_TOKEN;
109+
return await request(path, {
110+
baseURL: this.config.endpoint,
111+
responseType: 'json',
112+
headers: {
113+
...(privateToken
114+
? {
115+
'PRIVATE-TOKEN': privateToken,
116+
}
117+
: {}),
118+
...headers,
119+
},
120+
...rest,
121+
});
122+
} catch (err: any) {
123+
const status = err.response?.status;
124+
let messageKey = 'error.request';
125+
if (status === 401) {
126+
messageKey = 'gitlink.unauthorized';
127+
} else if (status === 404) {
128+
messageKey = 'error.resource-not-found';
129+
}
130+
this.showErrorMessage(messageKey, status);
131+
throw err;
132+
}
133+
}
134+
135+
async getProject(repo: IRepositoryModel) {
136+
const data = await this.request<API.Project>(`/api/${this.getProjectPath(repo)}/detail.json`);
137+
if (!data?.identifier) {
138+
const message = formatLocalize('api.response.project-not-found', this.getProjectPath(repo));
139+
this.showErrorMessage('', message, 404);
140+
throw new Error(message);
141+
}
142+
return {
143+
...data,
144+
id: data.identifier,
145+
};
146+
}
147+
148+
async getCommit(repo: IRepositoryModel, ref: string) {
149+
return (
150+
await this.request<API.ResponseGetCommit>(
151+
`/api/${this.getProjectPath(repo)}/commits/${encodeURIComponent(ref)}`
152+
)
153+
).commit.sha;
154+
}
155+
156+
// getTree getBlob getBlobByCommitPath 暂时都使用同一接口
157+
async getTree(repo: IRepositoryModel, path: string) {
158+
const data = await this.request<API.ResponseGetTree>(
159+
`/api/${this.getProjectPath(repo)}/sub_entries.json`,
160+
{
161+
params: {
162+
ref: repo.commit,
163+
filepath: path,
164+
type: 'dir',
165+
},
166+
}
167+
);
168+
if (data.entries) {
169+
return data.entries.map<TreeEntry>((item) => {
170+
const entry: TreeEntry = {
171+
...item,
172+
// gitlink 文件类型暂时只有 dir 和 file
173+
type: item.type === 'dir' ? 'tree' : 'blob',
174+
};
175+
return entry;
176+
});
177+
} else {
178+
throw new Error('[can not find entries]');
179+
}
180+
}
181+
182+
async getBlob(repo: IRepositoryModel, entry: EntryParam) {
183+
const buf = await this.request<API.ResponseGetSubTree>(
184+
`/api/${this.getProjectPath(repo)}/sub_entries.json`,
185+
{
186+
params: {
187+
filepath: entry.path,
188+
ref: repo.commit,
189+
type: 'file',
190+
},
191+
}
192+
);
193+
let content = (buf.entries as API.Entry).content;
194+
return Buffer.from(content);
195+
}
196+
197+
async getBlobByCommitPath(repo: IRepositoryModel, commit: string, path: string) {
198+
const buf = await this.request<API.ResponseGetSubTree>(
199+
`/api/${this.getProjectPath(repo)}/sub_entries.json`,
200+
{
201+
params: {
202+
filepath: path,
203+
ref: commit,
204+
type: 'file',
205+
},
206+
}
207+
);
208+
let content = (buf.entries as API.Entry).content;
209+
return Buffer.from(content);
210+
}
211+
212+
// 此处应该使用后续的api/v1 里的branch接口
213+
async getBranches(repo: IRepositoryModel): Promise<BranchOrTag[]> {
214+
const branchSlice = await this.request<API.BranchSlice>(
215+
`/api/${this.getProjectPath(repo)}/branches_slice.json`
216+
);
217+
const branches: BranchOrTag[] = [];
218+
branchSlice.forEach((slices) => {
219+
slices.list.forEach((branch) => {
220+
branches.push({
221+
commit: {
222+
id: branch.last_commit.sha,
223+
},
224+
name: branch.name,
225+
});
226+
});
227+
});
228+
return branches;
229+
}
230+
231+
async getTags(repo: IRepositoryModel): Promise<BranchOrTag[]> {
232+
const tags = await this.request<API.ResponseGetRefs>(
233+
`/api/${this.getProjectPath(repo)}/tags.json`
234+
);
235+
return tags.map((tag) => ({
236+
commit: {
237+
id: tag.id,
238+
},
239+
name: tag.name,
240+
}));
241+
}
242+
243+
/**
244+
* gitlab 接口不支持搜索
245+
*/
246+
async searchContent() {
247+
return [];
248+
}
249+
250+
async searchFile() {
251+
return [];
252+
}
253+
254+
// 不支持
255+
async getFileBlame() {
256+
return Uint8Array.from([]);
257+
}
258+
259+
async getCommits(repo: IRepositoryModel, params: CommitParams) {
260+
return [];
261+
}
262+
263+
async getCommitDiff(repo: IRepositoryModel, sha: string) {
264+
return [];
265+
}
266+
267+
async getCommitCompare(repo: IRepositoryModel, from: string, to: string) {
268+
return [];
269+
}
270+
271+
async bulkChangeFiles() {
272+
return [];
273+
}
274+
275+
async getFiles() {
276+
return [];
277+
}
278+
createBranch(repo: IRepositoryModel, newBranch: string): Promise<any> {
279+
throw new Error('Method not implemented.');
280+
}
281+
}

0 commit comments

Comments
 (0)