提交 5c4e6138 编写于 作者: T Tomas Vik

fix: some self-managed GitLab deployments not handling project URLs

上级 d19fb6a6
......@@ -38,7 +38,8 @@
"import/no-extraneous-dependencies": [
"error",
{ "devDependencies": ["**/*.test.ts", "test/**/*"] }
]
],
"no-useless-constructor": "off"
},
"settings": {
"import/resolver": {
......
......@@ -54,7 +54,7 @@ async function uploadSnippet(project, editor, visibility, context) {
data.content = content;
if (project) {
data.id = project.id;
data.id = project.restId;
}
const snippet = await gitLabService.createSnippet(data);
......
......@@ -10,6 +10,8 @@ import { FetchError } from '../errors/fetch_error';
import { getUserAgentHeader } from '../utils/get_user_agent_header';
import { getAvatarUrl } from '../utils/get_avatar_url';
import { getHttpAgentOptions } from '../utils/get_http_agent_options';
import { GitLabProject, GqlProject } from './gitlab_project';
import { getRestIdFromGraphQLId } from '../utils/get_rest_id_from_graphql_id';
interface Node<T> {
pageInfo?: {
......@@ -124,6 +126,19 @@ const queryGetSnippets = gql`
}
`;
const queryGetProject = gql`
query GetProject($projectPath: ID!) {
project(fullPath: $projectPath) {
id
name
fullPath
group {
id
}
}
}
`;
const positionFragment = gql`
fragment position on Note {
position {
......@@ -238,6 +253,13 @@ export class GitLabNewService {
};
}
async getProject(projectPath: string): Promise<GitLabProject | undefined> {
const result = await this.client.request<GqlProjectResult<GqlProject>>(queryGetProject, {
projectPath,
});
return result.project && new GitLabProject(result.project);
}
async getSnippets(projectPath: string): Promise<GqlSnippet[]> {
const result = await this.client.request<GqlProjectResult<GqlSnippetProject>>(
queryGetSnippets,
......@@ -264,8 +286,8 @@ export class GitLabNewService {
// TODO change this method to use GraphQL when https://gitlab.com/gitlab-org/gitlab/-/issues/260316 is done
async getSnippetContent(snippet: GqlSnippet, blob: GqlBlob): Promise<string> {
const projectId = snippet.projectId.replace('gid://gitlab/Project/', '');
const snippetId = snippet.id.replace('gid://gitlab/ProjectSnippet/', '');
const projectId = getRestIdFromGraphQLId(snippet.projectId);
const snippetId = getRestIdFromGraphQLId(snippet.id);
const url = `${this.instanceUrl}/api/v4/projects/${projectId}/snippets/${snippetId}/files/master/${blob.path}/raw`;
const result = await crossFetch(url, this.fetchOptions);
if (!result.ok) {
......
import { getRestIdFromGraphQLId } from '../utils/get_rest_id_from_graphql_id';
interface GqlGroup {
id: string;
}
export interface GqlProject {
id: string;
name: string;
fullPath: string;
group?: GqlGroup;
}
export class GitLabProject {
constructor(private readonly gqlProject: GqlProject) {}
get gqlId(): string {
return this.gqlProject.id;
}
get restId(): number {
return getRestIdFromGraphQLId(this.gqlProject.id);
}
get name(): string {
return this.gqlProject.name;
}
get fullPath(): string {
return this.gqlProject.fullPath;
}
get groupRestId(): number | undefined {
return this.gqlProject.group && getRestIdFromGraphQLId(this.gqlProject.group.id);
}
}
......@@ -5,7 +5,7 @@ import { tokenService } from './services/token_service';
import { UserFriendlyError } from './errors/user_friendly_error';
import { ApiError } from './errors/api_error';
import { getCurrentWorkspaceFolder } from './services/workspace_service';
import { createGitService } from './service_factory';
import { createGitLabNewService, createGitService } from './service_factory';
import { GitRemote } from './git/git_remote_parser';
import { handleError, logError } from './log';
import { getUserAgentHeader } from './utils/get_user_agent_header';
......@@ -14,17 +14,7 @@ import { CustomQuery } from './gitlab/custom_query';
import { getAvatarUrl } from './utils/get_avatar_url';
import { getHttpAgentOptions } from './utils/get_http_agent_options';
import { getInstanceUrl as getInstanceUrlUtil } from './utils/get_instance_url';
interface GitLabProject {
id: number;
name: string;
namespace: {
id: number;
kind: string;
};
// eslint-disable-next-line camelcase
path_with_namespace: string;
}
import { GitLabProject } from './gitlab/gitlab_project';
interface GitLabPipeline {
id: number;
......@@ -100,13 +90,15 @@ async function fetch(path: string, method = 'GET', data?: Record<string, unknown
return await request(`${apiRoot}${path}`, config);
}
async function fetchProjectData(remote: GitRemote | null) {
async function fetchProjectData(remote: GitRemote | null, workspaceFolder: string) {
if (remote) {
if (!(`${remote.namespace}_${remote.project}` in projectCache)) {
const { namespace, project } = remote;
const { response } = await fetch(`/projects/${namespace.replace(/\//g, '%2F')}%2F${project}`);
const projectData = response;
projectCache[`${remote.namespace}_${remote.project}`] = projectData;
const gitlabNewService = await createGitLabNewService(workspaceFolder);
const projectData = await gitlabNewService.getProject(`${namespace}/${project}`);
if (projectData) {
projectCache[`${remote.namespace}_${remote.project}`] = projectData;
}
}
return projectCache[`${remote.namespace}_${remote.project}`] || null;
}
......@@ -118,7 +110,7 @@ export async function fetchCurrentProject(workspaceFolder: string): Promise<GitL
try {
const remote = await createGitService(workspaceFolder).fetchGitRemote();
return await fetchProjectData(remote);
return await fetchProjectData(remote, workspaceFolder);
} catch (e) {
throw new ApiError(e, 'get current project');
}
......@@ -137,7 +129,7 @@ export async function fetchCurrentPipelineProject(workspaceFolder: string) {
try {
const remote = await createGitService(workspaceFolder).fetchGitRemotePipeline();
return await fetchProjectData(remote);
return await fetchProjectData(remote, workspaceFolder);
} catch (e) {
logError(e);
return null;
......@@ -195,7 +187,7 @@ export async function fetchLastPipelineForCurrentBranch(workspaceFolder: string)
if (project) {
const branchName = await createGitService(workspaceFolder).fetchTrackingBranchName();
const pipelinesRootPath = `/projects/${project.id}/pipelines`;
const pipelinesRootPath = `/projects/${project.restId}/pipelines`;
const { response } = await fetch(`${pipelinesRootPath}?ref=${branchName}`);
const pipelines = response;
......@@ -246,15 +238,15 @@ export async function fetchIssuables(params: CustomQuery, workspaceFolder: strin
let path = '';
if (config.type === 'epics') {
if (project.namespace.kind === 'group') {
path = `/groups/${project.namespace.id}/${config.type}?include_ancestor_groups=true&state=${config.state}`;
if (project.groupRestId) {
path = `/groups/${project.groupRestId}/${config.type}?include_ancestor_groups=true&state=${config.state}`;
} else {
return [];
}
} else {
const searchKind =
config.type === CustomQueryType.VULNERABILITY ? 'vulnerability_findings' : config.type;
path = `/projects/${project.id}/${searchKind}?scope=${config.scope}&state=${config.state}`;
path = `/projects/${project.restId}/${searchKind}?scope=${config.scope}&state=${config.state}`;
}
if (config.type === 'issues') {
if (author) {
......@@ -344,7 +336,7 @@ export async function fetchLastJobsForCurrentBranch(
) {
const project = await fetchCurrentPipelineProject(workspaceFolder);
if (project) {
const { response } = await fetch(`/projects/${project.id}/pipelines/${pipeline.id}/jobs`);
const { response } = await fetch(`/projects/${project.restId}/pipelines/${pipeline.id}/jobs`);
let jobs: GitLabJob[] = response;
// Gitlab return multiple jobs if you retry the pipeline we filter to keep only the last
......@@ -368,7 +360,7 @@ export async function fetchOpenMergeRequestForCurrentBranch(workspaceFolder: str
const project = await fetchCurrentProjectSwallowError(workspaceFolder);
const branchName = await createGitService(workspaceFolder).fetchTrackingBranchName();
const path = `/projects/${project?.id}/merge_requests?state=opened&source_branch=${branchName}`;
const path = `/projects/${project?.restId}/merge_requests?state=opened&source_branch=${branchName}`;
const { response } = await fetch(path);
const mrs = response;
......@@ -389,11 +381,11 @@ export async function handlePipelineAction(action: string, workspaceFolder: stri
const project = await fetchCurrentProjectSwallowError(workspaceFolder);
if (pipeline && project) {
let endpoint = `/projects/${project.id}/pipelines/${pipeline.id}/${action}`;
let endpoint = `/projects/${project.restId}/pipelines/${pipeline.id}/${action}`;
if (action === 'create') {
const branchName = await createGitService(workspaceFolder).fetchTrackingBranchName();
endpoint = `/projects/${project.id}/pipeline?ref=${branchName}`;
endpoint = `/projects/${project.restId}/pipeline?ref=${branchName}`;
}
try {
......@@ -415,7 +407,7 @@ export async function fetchMRIssues(mrId: number, workspaceFolder: string) {
if (project) {
try {
const { response } = await fetch(
`/projects/${project.id}/merge_requests/${mrId}/closes_issues`,
`/projects/${project.restId}/merge_requests/${mrId}/closes_issues`,
);
issues = response;
} catch (e) {
......@@ -480,8 +472,7 @@ export async function renderMarkdown(markdown: string, workspaceFolder: string)
const project = await fetchCurrentProject(workspaceFolder);
const { response } = await fetch('/markdown', 'POST', {
text: markdown,
// eslint-disable-next-line camelcase
project: project?.path_with_namespace,
project: project?.fullPath,
gfm: 'true', // Needs to be a string for the API
});
rendered = response;
......
import * as assert from 'assert';
// copied from the gitlab-org/gitlab project
// https://gitlab.com/gitlab-org/gitlab/-/blob/a4b939809c68c066e358a280491bf4ec2ff439a2/app/assets/javascripts/graphql_shared/utils.js#L9-10
export const getRestIdFromGraphQLId = (gid: string): number => {
const result = parseInt(gid.replace(/gid:\/\/gitlab\/.*\//g, ''), 10);
assert(result, `the gid ${gid} can't be parsed into REST id`);
return result;
};
{
"project": {
"id": "gid://gitlab/Project/278964",
"name": "GitLab",
"fullPath": "gitlab-org/gitlab",
"group": {
"id": "gid://gitlab/Group/9970"
}
}
}
{
"avatar_url": "https://assets.gitlab-static.net/uploads/-/system/project/avatar/278964/logo-extra-whitespace.png",
"created_at": "2015-05-20T10:47:11.949Z",
"default_branch": "master",
"description": "GitLab is an open source end-to-end software development platform with built-in version control, issue tracking, code review, CI/CD, and more. Self-host GitLab on your own servers, in a container, or on a cloud provider.",
"forks_count": 2193,
"http_url_to_repo": "https://gitlab.com/gitlab-org/gitlab.git",
"id": 278964,
"last_activity_at": "2020-07-23T12:59:24.905Z",
"name": "GitLab",
"name_with_namespace": "GitLab.org / GitLab",
"namespace": {
"avatar_url": "/uploads/-/system/group/avatar/9970/logo-extra-whitespace.png",
"full_path": "gitlab-org",
"id": 9970,
"kind": "group",
"name": "GitLab.org",
"parent_id": null,
"path": "gitlab-org",
"web_url": "https://gitlab.com/groups/gitlab-org"
},
"path": "gitlab",
"path_with_namespace": "gitlab-org/gitlab",
"readme_url": "https://gitlab.com/gitlab-org/gitlab/-/blob/master/README.md",
"ssh_url_to_repo": "git@gitlab.com:gitlab-org/gitlab.git",
"star_count": 1974,
"tag_list": [],
"web_url": "https://gitlab.com/gitlab-org/gitlab"
}
const { setupServer } = require('msw/node');
const { rest } = require('msw');
const { rest, graphql } = require('msw');
const { API_URL_PREFIX } = require('./constants');
const projectResponse = require('../fixtures/rest/project.json');
const projectResponse = require('../fixtures/graphql/project.json');
const versionResponse = require('../fixtures/rest/version.json');
const createJsonEndpoint = (path, response) =>
......@@ -46,7 +46,10 @@ const notFoundByDefault = rest.get(/.*/, (req, res, ctx) => {
const getServer = (handlers = []) => {
const server = setupServer(
createJsonEndpoint('/projects/gitlab-org%2Fgitlab', projectResponse),
graphql.query('GetProject', (req, res, ctx) => {
if (req.variables.projectPath === 'gitlab-org/gitlab') return res(ctx.data(projectResponse));
return res(ctx.data({ project: null }));
}),
createJsonEndpoint('/version', versionResponse),
...handlers,
notFoundByDefault,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册