提交 18307069 编写于 作者: T Tomas Vik

Merge branch '253-use-graphql-for-discussions' into 'main'

perf(webview): use GraphQL to load MR/Issue discussions

See merge request gitlab-org/gitlab-vscode-extension!150
......@@ -59,6 +59,13 @@ test-integration:
- /usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
- npm run test-integration
test-webview:
stage: test
script:
- cd src/webview
- npm ci
- npm run test
package:
stage: package
script:
......
......@@ -8,5 +8,6 @@ module.exports = {
coverageProvider: 'v8',
coverageReporters: ['lcov'],
roots: ['src'],
testPathIgnorePatterns: ['/node_modules/', '/src/webview/'],
testEnvironment: 'node',
};
import * as vscode from 'vscode';
import { GitLabNewService, GraphQLSnippet, GraphQLBlob } from '../gitlab/gitlab_new_service';
import { GitLabNewService, GqlSnippet, GqlBlob } from '../gitlab/gitlab_new_service';
import { createGitService } from '../git_service_factory';
import { getCurrentWorkspaceFolderOrSelectOne } from '../services/workspace_service';
const pickSnippet = async (snippets: GraphQLSnippet[]) => {
const pickSnippet = async (snippets: GqlSnippet[]) => {
const quickPickItems = snippets.map(s => ({
label: s.title,
description: s.description,
......@@ -13,7 +13,7 @@ const pickSnippet = async (snippets: GraphQLSnippet[]) => {
return vscode.window.showQuickPick(quickPickItems);
};
const pickBlob = async (blobs: GraphQLBlob[]) => {
const pickBlob = async (blobs: GqlBlob[]) => {
const quickPickItems = blobs.map(b => ({
label: b.name,
original: b,
......
......@@ -3,34 +3,70 @@ import { GraphQLClient, gql } from 'graphql-request';
import crossFetch from 'cross-fetch';
import { URL } from 'url';
import * as createHttpProxyAgent from 'https-proxy-agent';
import * as assert from 'assert';
import { tokenService } from '../services/token_service';
import { FetchError } from '../errors/fetch_error';
import { getUserAgentHeader } from '../utils/get_user_agent_header';
interface Node<T> {
pageInfo?: {
hasNextPage: boolean;
endCursor: string;
};
nodes: T[];
}
interface GraphQLProjectResult {
project?: {
id: string;
snippets: Node<GraphQLSnippet>;
};
interface GqlProjectResult<T> {
project?: T;
}
export interface GraphQLSnippet {
interface GqlSnippetProject {
id: string;
snippets: Node<GqlSnippet>;
}
export interface GqlSnippet {
id: string;
projectId: string;
title: string;
description: string;
blobs: Node<GraphQLBlob>;
blobs: Node<GqlBlob>;
}
export interface GraphQLBlob {
export interface GqlBlob {
name: string;
path: string;
}
interface GqlNoteAuthor {
avatarUrl: string;
name: string;
username: string;
webUrl: string;
}
interface GqlNote {
id: string;
author: GqlNoteAuthor;
createdAt: string;
system: boolean;
body: string; // TODO: remove this once the SystemNote.vue doesn't require plain text body
bodyHtml: string;
}
export interface GqlDiscussion {
replyId: string;
createdAt: string;
notes: Node<GqlNote>;
}
interface GqlDiscussionsProject {
mergeRequest?: {
discussions: Node<GqlDiscussion>;
};
issue?: {
discussions: Node<GqlDiscussion>;
};
}
const queryGetSnippets = gql`
query GetSnippets($projectPath: ID!) {
project(fullPath: $projectPath) {
......@@ -52,6 +88,65 @@ const queryGetSnippets = gql`
}
`;
const discussionsFragment = gql`
fragment discussions on DiscussionConnection {
pageInfo {
hasNextPage
endCursor
}
nodes {
replyId
createdAt
notes {
pageInfo {
hasNextPage
endCursor
}
nodes {
id
createdAt
system
author {
avatarUrl
name
username
webUrl
}
body
bodyHtml
}
}
}
}
`;
const queryGetMrDiscussions = gql`
${discussionsFragment}
query GetMrDiscussions($projectPath: ID!, $iid: String!, $afterCursor: String) {
project(fullPath: $projectPath) {
id
mergeRequest(iid: $iid) {
discussions(after: $afterCursor) {
...discussions
}
}
}
}
`;
const queryGetIssueDiscussions = gql`
${discussionsFragment}
query GetIssueDiscussions($projectPath: ID!, $iid: String!, $afterCursor: String) {
project(fullPath: $projectPath) {
id
issue(iid: $iid) {
discussions(after: $afterCursor) {
...discussions
}
}
}
}
`;
export class GitLabNewService {
client: GraphQLClient;
......@@ -76,10 +171,13 @@ export class GitLabNewService {
};
}
async getSnippets(projectPath: string): Promise<GraphQLSnippet[]> {
const result = await this.client.request<GraphQLProjectResult>(queryGetSnippets, {
projectPath,
});
async getSnippets(projectPath: string): Promise<GqlSnippet[]> {
const result = await this.client.request<GqlProjectResult<GqlSnippetProject>>(
queryGetSnippets,
{
projectPath,
},
);
const { project } = result;
// this can mean three things: project doesn't exist, user doesn't have access, or user credentials are wrong
......@@ -98,7 +196,7 @@ export class GitLabNewService {
}
// TODO change this method to use GraphQL when https://gitlab.com/gitlab-org/gitlab/-/issues/260316 is done
async getSnippetContent(snippet: GraphQLSnippet, blob: GraphQLBlob): Promise<string> {
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 url = `${this.instanceUrl}/api/v4/projects/${projectId}/snippets/${snippetId}/files/master/${blob.path}/raw`;
......@@ -136,4 +234,42 @@ export class GitLabNewService {
}
return fileResult.text();
}
/*
The GraphQL endpoint sends us the note.htmlBody with links that start with `/`.
This works well for the the GitLab webapp, but in VS Code we need to add the full host.
*/
private addHostToUrl(discussion: GqlDiscussion): GqlDiscussion {
const prependHost = (note: GqlNote): GqlNote => ({
...note,
bodyHtml: note.bodyHtml.replace(/href="\//, `href="${this.instanceUrl}/`),
});
return {
...discussion,
notes: {
...discussion.notes,
nodes: discussion.notes.nodes.map(prependHost),
},
};
}
async getDiscussions(issuable: RestIssuable, endCursor?: string): Promise<GqlDiscussion[]> {
const [projectPath] = issuable.references.full.split(/[#!]/);
const query = issuable.sha ? queryGetMrDiscussions : queryGetIssueDiscussions;
const result = await this.client.request<GqlProjectResult<GqlDiscussionsProject>>(query, {
projectPath,
iid: String(issuable.iid),
endCursor,
});
assert(result.project, `Project ${projectPath} was not found.`);
const discussions =
result.project.issue?.discussions || result.project.mergeRequest?.discussions;
assert(discussions, `Discussions for issuable ${projectPath}#!${issuable.iid} were not found.`);
if (discussions.pageInfo?.hasNextPage) {
assert(discussions.pageInfo.endCursor);
const remainingPages = await this.getDiscussions(issuable, discussions.pageInfo.endCursor);
return [...discussions.nodes, ...remainingPages];
}
return discussions.nodes.map(n => this.addHostToUrl(n));
}
}
......@@ -11,6 +11,7 @@ import { handleError, logError } from './log';
import { getUserAgentHeader } from './utils/get_user_agent_header';
import { CustomQueryType } from './gitlab/custom_query_type';
import { CustomQuery } from './gitlab/custom_query';
import { GitLabNewService, GqlDiscussion } from './gitlab/gitlab_new_service';
interface GitLabProject {
id: number;
......@@ -517,34 +518,6 @@ interface Discussion {
}[];
}
export async function fetchDiscussions(issuable: RestIssuable, page = 1): Promise<Discussion[]> {
let discussions: Discussion[] = [];
try {
const type = issuable.sha ? 'merge_requests' : 'issues';
const { response, headers } = await fetch(
`/projects/${issuable.project_id}/${type}/${issuable.iid}/discussions?sort=asc&per_page=5&page=${page}`,
);
discussions = response;
if (page === 1 && headers['x-next-page'] !== '') {
const pages = [];
// Temporarily disable eslint to be able to start enforcing stricter rules
// eslint-disable-next-line no-plusplus
for (let i = 2; i <= headers['x-total-pages']; i++) {
pages.push(fetchDiscussions(issuable, i));
}
const results = await Promise.all(pages);
results.forEach(result => {
discussions = discussions.concat(result);
});
}
} catch (e) {
handleError(new UserFriendlyError('Failed to fetch discussions for this issuable.', e));
}
return discussions;
}
export async function renderMarkdown(markdown: string, workspaceFolder: string) {
let rendered = { html: markdown };
const version = await fetchVersion();
......@@ -594,22 +567,27 @@ export async function saveNote(params: {
return { success: false };
}
type note = Discussion | LabelEvent;
type note = GqlDiscussion | LabelEvent;
function isLabelEvent(object: any): object is LabelEvent {
return Boolean(object.label);
}
export async function fetchDiscussionsAndLabelEvents(issuable: RestIssuable): Promise<note[]> {
// obtaining GitLabNewService in GitLabService is temporary and will be removed in this or the next MR
const instanceUrl = await createGitService(
(await getCurrentWorkspaceFolder()) || '',
).fetchCurrentInstanceUrl();
const gitlabNewService = new GitLabNewService(instanceUrl);
const [discussions, labelEvents] = await Promise.all([
fetchDiscussions(issuable),
gitlabNewService.getDiscussions(issuable),
fetchLabelEvents(issuable),
]);
const combinedEvents: note[] = [...discussions, ...labelEvents];
combinedEvents.sort((a: note, b: note) => {
const aCreatedAt = isLabelEvent(a) ? a.created_at : a.notes[0].created_at;
const bCreatedAt = isLabelEvent(b) ? b.created_at : b.notes[0].created_at;
const aCreatedAt = isLabelEvent(a) ? a.created_at : a.createdAt;
const bCreatedAt = isLabelEvent(b) ? b.created_at : b.createdAt;
return aCreatedAt < bCreatedAt ? -1 : 1;
});
......
......@@ -11,6 +11,9 @@ export const issuable: RestIssuable = {
'https://secure.gravatar.com/avatar/6042a9152ada74d9fb6a0cdce895337e?s=80&d=identicon',
name: 'Tomas Vik',
},
references: {
full: 'gitlab-org/gitlab#1000',
},
};
export const diffFile: RestDiffFile = {
......
......@@ -15,6 +15,9 @@ interface RestIssuable {
web_url: string;
author: { name: string; avatar_url: string };
sha?: string; // only present in MR, legacy logic uses the presence to decide issuable type
references: {
full: string; // e.g. "gitlab-org/gitlab#219925"
};
}
interface RestMrVersion {
......
module.exports = {
moduleFileExtensions: ['js', 'json', 'vue'],
transform: {
'.*\\.(vue)$': 'vue-jest',
'.*\\.(js)$': 'babel-jest',
},
};
此差异已折叠。
......@@ -6,7 +6,7 @@
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"watch": "vue-cli-service build --watch",
"test:unit": "vue-cli-service test:unit",
"test": "vue-cli-service test:unit",
"lint": "eslint --ext .js --ext .vue .",
"autofix": "eslint --ext .js --ext .vue --fix ."
},
......@@ -19,9 +19,9 @@
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.5.7",
"@vue/cli-plugin-unit-mocha": "^4.5.7",
"@vue/cli-plugin-unit-jest": "^4.5.9",
"@vue/cli-service": "^4.4.6",
"@vue/test-utils": "^1.1.0",
"@vue/test-utils": "^1.1.2",
"chai": "^4.2.0",
"eslint": "^7.13.0",
"eslint-config-airbnb-base": "^14.2.0",
......@@ -30,6 +30,7 @@
"eslint-plugin-vue": "^6.2.2",
"node-sass": "^4.14.1",
"sass-loader": "^8.0.2",
"vue-jest": "^3.0.7",
"vue-template-compiler": "^2.6.11"
}
}
......@@ -20,10 +20,10 @@ export default {
},
computed: {
initialDiscussion() {
return this.noteable.notes[0];
return this.noteable.notes.nodes[0];
},
replies() {
return this.noteable.notes.slice(1);
return this.noteable.notes.nodes.slice(1);
},
hasReplies() {
return this.replies.length > 0;
......
import { mount } from '@vue/test-utils';
import IssuableDiscussions from './IssuableDiscussions';
const discussionsResponse = require('../../../../test/integration/fixtures/graphql/discussions.json');
describe('IssuableDiscussions', () => {
beforeEach(() => {
window.vsCodeApi = { postMessage: jest.fn() };
});
it('renders', () => {
const wrapper = mount(IssuableDiscussions, {
propsData: {
discussions: discussionsResponse.project.issue.discussions.nodes,
},
stubs: {
date: true,
},
});
expect(wrapper.element).toMatchSnapshot();
});
});
......@@ -4,6 +4,8 @@ import Discussion from './Discussion';
import SystemNote from './SystemNote';
import LabelNote from './LabelNote';
const hasSingleNote = discussion => discussion.notes && discussion.notes.nodes.length === 1;
export default {
props: {
discussions: {
......@@ -19,8 +21,8 @@ export default {
},
methods: {
getComponentName(discussion) {
if (discussion.individual_note) {
if (discussion.notes[0].system) {
if (hasSingleNote(discussion)) {
if (discussion.notes.nodes[0].system) {
return SystemNote;
}
......@@ -36,7 +38,7 @@ export default {
if (discussion.label) {
return discussion;
}
return discussion.individual_note ? discussion.notes[0] : discussion;
return hasSingleNote(discussion) ? discussion.notes.nodes[0] : discussion;
},
},
};
......
<script>
import UserAvatar from './UserAvatar';
import NoteBody from './NoteBody';
import Date from './Date';
export default {
......@@ -13,7 +12,6 @@ export default {
},
components: {
UserAvatar,
NoteBody,
Date,
},
computed: {
......@@ -34,9 +32,11 @@ export default {
<div class="note-header">
<user-avatar :user="author" :size="40" :show-avatar="false" style="margin-right: 2px;" />
·
<date :date="noteable.created_at" style="margin-left: 2px;" />
<date :date="noteable.createdAt" style="margin-left: 2px;" />
</div>
<div class="note-body">
<div class="body" v-html="noteable.bodyHtml" />
</div>
<note-body :note="noteable" />
</div>
</div>
</li>
......
......@@ -124,7 +124,7 @@ export default {
<div class="timelineContent" v-if="multiLine">
<div class="note-header">
<user-avatar :user="author" :show-avatar="false" style="margin-right: 2px;" />
{{ firstLine }} <date :date="noteable.created_at" style="margin-left: 2px;" />
{{ firstLine }} <date :date="noteable.createdAt" style="margin-left: 2px;" />
</div>
<note-body :note="noteable" style="margin-left: 25px;" />
</div>
......@@ -132,7 +132,7 @@ export default {
<div class="note-header">
<user-avatar :user="author" :show-avatar="false" style="margin-right: 2px;" />
<note-body :note="noteable" style="margin-right: 2px;" /> ·
<date :date="noteable.created_at" style="margin-left: 2px;" />
<date :date="noteable.createdAt" style="margin-left: 2px;" />
</div>
</div>
</div>
......
......@@ -35,14 +35,17 @@ export default {
sizeClass() {
return `s${this.size}`;
},
userUrl() {
return this.user.webUrl || this.user.web_url; // labels contain snake_case variables
},
},
};
</script>
<template>
<span>
<component :is="showLink ? 'a' : 'span'" :href="user.web_url" target="_blank">
<img v-if="showAvatar" :src="user.avatar_url" :class="sizeClass" class="avatar" />
<component :is="showLink ? 'a' : 'span'" :href="userUrl" target="_blank">
<img v-if="showAvatar" :src="user.avatarUrl" :class="sizeClass" class="avatar" />
<span v-if="showUsername" class="author">
<strong> {{ user.name }}</strong>
<span v-if="showHandle"> @{{ user.username }}</span>
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`IssuableDiscussions renders 1`] = `
<ul
class="issuable-discussions"
>
<li
class="note system-note"
>
<div
class="timeline-entry-inner"
>
<div
class="timelineIcon"
>
<span>
<svg
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.8759,8.99237 C11.4346,10.7215 9.86658,12 8,12 C6.13342,12 4.56545,10.7215 4.12406,8.99238 C4.08342,8.99741 4.04201,9 4,9 L1,9 C0.447715,9 0,8.55228 0,8 C0,7.44771 0.447715,7 1,7 L4,7 C4.04201,7 4.08342,7.00259 4.12406,7.00762 C4.56545,5.27853 6.13342,4 8,4 C9.86658,4 11.4346,5.27853 11.8759,7.00763 C11.9166,7.00259 11.958,7 12,7 L15,7 C15.5523,7 16,7.44772 16,8 C16,8.55228 15.5523,9 15,9 L12,9 C11.958,9 11.9166,8.9974 11.8759,8.99237 Z M8,10 C9.10457,10 10,9.10457 10,8 C10,6.89543 9.10457,6 8,6 C6.89543,6 6,6.89543 6,8 C6,9.10457 6.89543,10 8,10 Z"
fill="currentColor"
fill-rule="evenodd"
/>
</svg>
</span>
</div>
<div
class="timelineContent"
>
<div
class="note-header"
>
<span
style="margin-right: 2px;"
>
<a
href="https://gitlab.com/vymarkov"
target="_blank"
>
<!---->
<span
class="author"
>
<strong>
Vitaly Markov
</strong>
<span>
@vymarkov
</span>
</span>
</a>
</span>
added 2 commits
<date-stub
date="2020-12-02T09:44:11Z"
style="margin-left: 2px;"
/>
</div>
<div
class="note-body"
style="margin-left: 25px;"
>
<div
class="body"
>
<p>
&lt;ul&gt;&lt;li&gt;5b63a281 - fix(lint): Fix linter errors&lt;/li&gt;&lt;li&gt;618c91eb - docs: Update CONTRIBUTING.md&lt;/li&gt;&lt;/ul&gt;
</p>
<p>
<a
href="/gitlab-org/gitlab-vscode-extension/-/merge_requests/130/diffs?diff_id=128320927&start_sha=2a7d1c93417adaafaee85b0345fdf8ea3f28c847"
>
Compare with previous version
</a>
</p>
</div>
</div>
</div>
<!---->
</div>
</li>
<li
class="note"
>
<div
class="timeline-entry-inner"
>
<div
class="timelineIcon"
>
<span>
<a
href="https://gitlab.com/viktomas"
target="_blank"
>
<img
class="avatar s40"
src="https://secure.gravatar.com/avatar/6042a9152ada74d9fb6a0cdce895337e?s=80&d=identicon"
/>
<!---->
</a>
</span>
</div>
<div
class="timelineContent"
>
<div
class="note-header"
>
<span
style="margin-right: 2px;"
>
<a
href="https://gitlab.com/viktomas"
target="_blank"
>
<!---->
<span
class="author"
>
<strong>
Tomas Vik
</strong>
<span>
@viktomas
</span>
</span>
</a>
</span>
·
<date-stub
date="2020-12-02T17:00:04Z"
style="margin-left: 2px;"
/>
</div>
<div
class="note-body"
>
<div
class="body"
>
<p
data-sourcepos="1:1-1:59"
dir="auto"
>
<a
class="gfm gfm-project_member js-user-link"
data-container="body"
data-placement="top"
data-reference-type="user"
data-user="359491"
href="/vymarkov"
title="Vitaly Markov"
>
@vymarkov
</a>
Thank you for fixing the
<code>
CONTRIBUTING.md
</code>
<gl-emoji
data-name="tada"
data-unicode-version="6.0"
title="party popper"
>
🎉
</gl-emoji>
</p>
<p
data-sourcepos="3:1-3:182"
dir="auto"
>
Regarding the change in
<code>
src/gitlab_service.ts
</code>
, I think we should still show some message to the user when they try to run
<code>
GitLab: *
</code>
command and the extension fails to execute it.
</p>
<p
data-sourcepos="5:1-5:217"
dir="auto"
>
We had a lot of troubles in the past when we were swallowing errors and then we weren't able to help the extension users debug any potential issues. (
<a
class="gfm gfm-issue has-tooltip"
data-container="body"
data-issue="30087182"
data-link="false"
data-link-reference="true"
data-original="https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/issues/145"
data-placement="top"
data-project="278964"
data-reference-type="issue"
href="https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/issues/145"
title="Error reporting to VSCode OutputChannel"
>
#145 (closed)
</a>
)
</p>
</div>
</div>
</div>
</div>
</li>
<li
class="note system-note"
>
<div
class="timeline-entry-inner"
>
<div
class="timelineIcon"
>
<span>
<svg
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.325 7.225a4 4 0 1 0-6.65 0A4.002 4.002 0 0 0 2 11v4h12v-4a4.002 4.002 0 0 0-2.675-3.775zM10 5a2 2 0 1 1-4 0 2 2 0 0 1 4 0zM6 9a2 2 0 0 0-2 2v2h8v-2a2 2 0 0 0-2-2H6z"
fill="currentColor"
/>
</svg>
</span>
</div>
<!---->
<div
class="timelineContent"
>
<div
class="note-header"
>
<span
style="margin-right: 2px;"
>
<a
href="https://gitlab.com/viktomas"
target="_blank"
>
<!---->
<span
class="author"
>
<strong>
Tomas Vik
</strong>
<span>
@viktomas
</span>
</span>
</a>
</span>
<div
class="note-body"
style="margin-right: 2px;"
>
<div
class="body"
>
<p>
assigned to @viktomas
</p>
</div>
</div>
·
<date-stub
date="2020-12-02T17:00:15Z"
style="margin-left: 2px;"
/>
</div>
</div>
</div>
</li>
<li
class="note system-note"
>
<div
class="timeline-entry-inner"
>
<div
class="timelineIcon"
>
<span>
<svg
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.325 7.225a4 4 0 1 0-6.65 0A4.002 4.002 0 0 0 2 11v4h12v-4a4.002 4.002 0 0 0-2.675-3.775zM10 5a2 2 0 1 1-4 0 2 2 0 0 1 4 0zM6 9a2 2 0 0 0-2 2v2h8v-2a2 2 0 0 0-2-2H6z"
fill="currentColor"
/>
</svg>
</span>
</div>
<!---->
<div
class="timelineContent"
>
<div
class="note-header"
>
<span
style="margin-right: 2px;"
>
<a
href="https://gitlab.com/viktomas"
target="_blank"
>
<!---->
<span
class="author"
>
<strong>
Tomas Vik
</strong>
<span>
@viktomas
</span>
</span>
</a>
</span>
<div
class="note-body"
style="margin-right: 2px;"
>
<div
class="body"
>
<p>
assigned to @vymarkov
</p>
</div>
</div>
·
<date-stub
date="2020-12-02T17:00:22Z"
style="margin-left: 2px;"
/>
</div>
</div>
</div>
</li>
<li
class="note system-note"
>
<div
class="timeline-entry-inner"
>
<div
class="timelineIcon"
>
<span>
<svg
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 1a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H4.71L1.5 14.855A1 1 0 0 1 0 13.99V3a2 2 0 0 1 2-2h12zm0 2H2v9.257L4.175 11H14V3zM5 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm3 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm3 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"
fill="currentColor"
/>
</svg>
</span>
</div>
<!---->
<div
class="timelineContent"
>
<div
class="note-header"
>
<span
style="margin-right: 2px;"
>
<a
href="https://gitlab.com/gitlab-bot"
target="_blank"
>
<!---->
<span
class="author"
>
<strong>
🤖 GitLab Bot 🤖
</strong>
<span>
@gitlab-bot
</span>
</span>
</a>
</span>
<div
class="note-body"
style="margin-right: 2px;"
>
<div
class="body"
>
<p>
mentioned in issue gitlab-org/quality/triage-reports#1062
</p>
</div>
</div>
·
<date-stub
date="2020-12-03T00:11:43Z"
style="margin-left: 2px;"
/>
</div>
</div>
</div>
</li>
<li
class="note"
>
<div
class="timeline-entry-inner"
>
<div
class="timelineIcon"
>
<span>
<a
href="https://gitlab.com/vymarkov"
target="_blank"
>
<img
class="avatar s40"
src="https://secure.gravatar.com/avatar/24d3a696fcc9592d18db6e623c0e654e?s=80&d=identicon"
/>
<!---->
</a>
</span>
</div>
<div
class="timelineContent"
>
<div
class="note-header"
>
<span
style="margin-right: 2px;"
>
<a
href="https://gitlab.com/vymarkov"
target="_blank"
>
<!---->
<span
class="author"
>
<strong>
Vitaly Markov
</strong>
<span>
@vymarkov
</span>
</span>
</a>
</span>
·
<date-stub
date="2020-12-03T10:58:37Z"
style="margin-left: 2px;"
/>
</div>
<div
class="note-body"
>
<div
class="body"
>
<blockquote
data-sourcepos="1:1-1:135"
dir="auto"
>
<p
data-sourcepos="1:3-1:135"
>
I think we should still show some message to the user when they try to run
<code>
GitLab: *
</code>
command and the extension fails to execute it.
</p>
</blockquote>
<p
data-sourcepos="3:1-3:64"
dir="auto"
>
Will it be enough to log the error message
<code>
project not found
</code>
?
</p>
<p
data-sourcepos="5:1-5:129"
dir="auto"
>
It's critical issue, I can't use ext cause I have on my workspace some repo from Github, it lead to issue what I'm trying to fix.
</p>
</div>
</div>
</div>
</div>
</li>
<li
class="note system-note"
>
<div
class="timeline-entry-inner"
>
<div
class="timelineIcon"
>
<span>
<svg
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 1a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H4.71L1.5 14.855A1 1 0 0 1 0 13.99V3a2 2 0 0 1 2-2h12zm0 2H2v9.257L4.175 11H14V3zM5 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm3 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm3 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"
fill="currentColor"
/>
</svg>
</span>
</div>
<!---->
<div
class="timelineContent"
>
<div
class="note-header"
>
<span
style="margin-right: 2px;"
>
<a
href="https://gitlab.com/viktomas"
target="_blank"
>
<!---->
<span
class="author"
>
<strong>
Tomas Vik
</strong>
<span>
@viktomas
</span>
</span>
</a>
</span>
<div
class="note-body"
style="margin-right: 2px;"
>
<div
class="body"
>
<p>
mentioned in issue #275
</p>
</div>
</div>
·
<date-stub
date="2020-12-03T12:50:21Z"
style="margin-left: 2px;"
/>
</div>
</div>
</div>
</li>
</ul>
`;
{
"project": {
"id": "gid://gitlab/Project/278964",
"issue": {
"discussions": {
"pageInfo": {
"hasNextPage": false,
"endCursor": "Nw"
},
"nodes": [
{
"replyId": "gid://gitlab/IndividualNoteDiscussion/3e120050b42400665aa728f283cfa167d800f03e",
"createdAt": "2020-12-02T09:44:11Z",
"notes": {
"pageInfo": {
"hasNextPage": false,
"endCursor": "MQ"
},
"nodes": [
{
"id": "gid://gitlab/Note/458662425",
"createdAt": "2020-12-02T09:44:11Z",
"system": true,
"author": {
"avatarUrl": "https://secure.gravatar.com/avatar/24d3a696fcc9592d18db6e623c0e654e?s=80&d=identicon",
"name": "Vitaly Markov",
"username": "vymarkov",
"webUrl": "https://gitlab.com/vymarkov"
},
"body": "added 2 commits\n\n<ul><li>5b63a281 - fix(lint): Fix linter errors</li><li>618c91eb - docs: Update CONTRIBUTING.md</li></ul>\n\n[Compare with previous version](/gitlab-org/gitlab-vscode-extension/-/merge_requests/130/diffs?diff_id=128320927&start_sha=2a7d1c93417adaafaee85b0345fdf8ea3f28c847)",
"bodyHtml": "<p data-sourcepos=\"1:1-1:15\" dir=\"auto\">added 2 commits</p>&#x000A;<ul dir=\"auto\">&#x000A;<li>&#x000A;<a href=\"/gitlab-org/gitlab-vscode-extension/-/merge_requests/130/diffs?commit_id=5b63a28151c5028e6bc0758ec1c28d4c36256a58\" data-original=\"5b63a281\" data-link=\"false\" data-link-reference=\"false\" data-project=\"278964\" data-commit=\"5b63a28151c5028e6bc0758ec1c28d4c36256a58\" data-reference-type=\"commit\" data-container=\"body\" data-placement=\"top\" title=\"fix(lint): Fix linter errors\" class=\"gfm gfm-commit has-tooltip\">5b63a281</a> - fix(lint): Fix linter errors</li>&#x000A;<li>&#x000A;<a href=\"/gitlab-org/gitlab-vscode-extension/-/merge_requests/130/diffs?commit_id=618c91ebd1527deef3740f66d8950ecc85032782\" data-original=\"618c91eb\" data-link=\"false\" data-link-reference=\"false\" data-project=\"278964\" data-commit=\"618c91ebd1527deef3740f66d8950ecc85032782\" data-reference-type=\"commit\" data-container=\"body\" data-placement=\"top\" title=\"docs: Update CONTRIBUTING.md\" class=\"gfm gfm-commit has-tooltip\">618c91eb</a> - docs: Update CONTRIBUTING.md</li>&#x000A;</ul>&#x000A;<p data-sourcepos=\"5:1-5:164\" dir=\"auto\"><a href=\"/gitlab-org/gitlab-vscode-extension/-/merge_requests/130/diffs?diff_id=128320927&amp;start_sha=2a7d1c93417adaafaee85b0345fdf8ea3f28c847\">Compare with previous version</a></p>"
}
]
}
},
{
"replyId": "gid://gitlab/IndividualNoteDiscussion/afbf8f461a773fc130aa8091c6636f22efb5f4c5",
"createdAt": "2020-12-02T17:00:04Z",
"notes": {
"pageInfo": {
"hasNextPage": false,
"endCursor": "MQ"
},
"nodes": [
{
"id": "gid://gitlab/Note/459020558",
"createdAt": "2020-12-02T17:00:04Z",
"system": false,
"author": {
"avatarUrl": "https://secure.gravatar.com/avatar/6042a9152ada74d9fb6a0cdce895337e?s=80&d=identicon",
"name": "Tomas Vik",
"username": "viktomas",
"webUrl": "https://gitlab.com/viktomas"
},
"body": "@vymarkov Thank you for fixing the `CONTRIBUTING.md` :tada:\n\nRegarding the change in `src/gitlab_service.ts`, I think we should still show some message to the user when they try to run `GitLab: *` command and the extension fails to execute it.\n\nWe had a lot of troubles in the past when we were swallowing errors and then we weren't able to help the extension users debug any potential issues. (https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/issues/145)",
"bodyHtml": "<p data-sourcepos=\"1:1-1:59\" dir=\"auto\"><a href=\"/vymarkov\" data-user=\"359491\" data-reference-type=\"user\" data-container=\"body\" data-placement=\"top\" class=\"gfm gfm-project_member js-user-link\" title=\"Vitaly Markov\">@vymarkov</a> Thank you for fixing the <code>CONTRIBUTING.md</code> <gl-emoji title=\"party popper\" data-name=\"tada\" data-unicode-version=\"6.0\">🎉</gl-emoji></p>&#x000A;<p data-sourcepos=\"3:1-3:182\" dir=\"auto\">Regarding the change in <code>src/gitlab_service.ts</code>, I think we should still show some message to the user when they try to run <code>GitLab: *</code> command and the extension fails to execute it.</p>&#x000A;<p data-sourcepos=\"5:1-5:217\" dir=\"auto\">We had a lot of troubles in the past when we were swallowing errors and then we weren't able to help the extension users debug any potential issues. (<a href=\"https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/issues/145\" data-original=\"https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/issues/145\" data-link=\"false\" data-link-reference=\"true\" data-project=\"278964\" data-issue=\"30087182\" data-reference-type=\"issue\" data-container=\"body\" data-placement=\"top\" title=\"Error reporting to VSCode OutputChannel\" class=\"gfm gfm-issue has-tooltip\">#145 (closed)</a>)</p>"
}
]
}
},
{
"replyId": "gid://gitlab/IndividualNoteDiscussion/27801445b1ed5c1344c3d7f2354cd6144046cd31",
"createdAt": "2020-12-02T17:00:15Z",
"notes": {
"pageInfo": {
"hasNextPage": false,
"endCursor": "MQ"
},
"nodes": [
{
"id": "gid://gitlab/Note/459020697",
"createdAt": "2020-12-02T17:00:15Z",
"system": true,
"author": {
"avatarUrl": "https://secure.gravatar.com/avatar/6042a9152ada74d9fb6a0cdce895337e?s=80&d=identicon",
"name": "Tomas Vik",
"username": "viktomas",
"webUrl": "https://gitlab.com/viktomas"
},
"body": "assigned to @viktomas",
"bodyHtml": "<p data-sourcepos=\"1:1-1:21\" dir=\"auto\">assigned to <a href=\"/viktomas\" data-user=\"3457201\" data-reference-type=\"user\" data-container=\"body\" data-placement=\"top\" class=\"gfm gfm-project_member js-user-link\" title=\"Tomas Vik\">@viktomas</a></p>"
}
]
}
},
{
"replyId": "gid://gitlab/IndividualNoteDiscussion/34493f511812d89dd5992fea24d52764aaf89096",
"createdAt": "2020-12-02T17:00:22Z",
"notes": {
"pageInfo": {
"hasNextPage": false,
"endCursor": "MQ"
},
"nodes": [
{
"id": "gid://gitlab/Note/459020793",
"createdAt": "2020-12-02T17:00:22Z",
"system": true,
"author": {
"avatarUrl": "https://secure.gravatar.com/avatar/6042a9152ada74d9fb6a0cdce895337e?s=80&d=identicon",
"name": "Tomas Vik",
"username": "viktomas",
"webUrl": "https://gitlab.com/viktomas"
},
"body": "assigned to @vymarkov",
"bodyHtml": "<p data-sourcepos=\"1:1-1:21\" dir=\"auto\">assigned to <a href=\"/vymarkov\" data-user=\"359491\" data-reference-type=\"user\" data-container=\"body\" data-placement=\"top\" class=\"gfm gfm-project_member js-user-link\" title=\"Vitaly Markov\">@vymarkov</a></p>"
}
]
}
},
{
"replyId": "gid://gitlab/IndividualNoteDiscussion/590262a4335578f03bbf725a6ee01345b20c3acf",
"createdAt": "2020-12-03T00:11:43Z",
"notes": {
"pageInfo": {
"hasNextPage": false,
"endCursor": "MQ"
},
"nodes": [
{
"id": "gid://gitlab/Note/459233800",
"createdAt": "2020-12-03T00:11:43Z",
"system": true,
"author": {
"avatarUrl": "/uploads/-/system/user/avatar/1786152/avatar.png",
"name": "🤖 GitLab Bot 🤖",
"username": "gitlab-bot",
"webUrl": "https://gitlab.com/gitlab-bot"
},
"body": "mentioned in issue gitlab-org/quality/triage-reports#1062",
"bodyHtml": "<p data-sourcepos=\"1:1-1:57\" dir=\"auto\">mentioned in issue <a href=\"/gitlab-org/quality/triage-reports/-/issues/1062\" data-original=\"gitlab-org/quality/triage-reports#1062\" data-link=\"false\" data-link-reference=\"false\" data-project=\"16233736\" data-issue=\"75514620\" data-reference-type=\"issue\" data-container=\"body\" data-placement=\"top\" title=\"2020-12-03 Untriaged community merge requests requiring initial triage\" class=\"gfm gfm-issue has-tooltip\">gitlab-org/quality/triage-reports#1062 (closed)</a></p>"
}
]
}
},
{
"replyId": "gid://gitlab/IndividualNoteDiscussion/f5677c52b69037d2311aa0d4d842b434df5c5ae6",
"createdAt": "2020-12-03T10:58:37Z",
"notes": {
"pageInfo": {
"hasNextPage": false,
"endCursor": "MQ"
},
"nodes": [
{
"id": "gid://gitlab/Note/459646571",
"createdAt": "2020-12-03T10:58:37Z",
"system": false,
"author": {
"avatarUrl": "https://secure.gravatar.com/avatar/24d3a696fcc9592d18db6e623c0e654e?s=80&d=identicon",
"name": "Vitaly Markov",
"username": "vymarkov",
"webUrl": "https://gitlab.com/vymarkov"
},
"body": "> I think we should still show some message to the user when they try to run `GitLab: *` command and the extension fails to execute it.\n\nWill it be enough to log the error message `project not found`? \n\nIt's critical issue, I can't use ext cause I have on my workspace some repo from Github, it lead to issue what I'm trying to fix.",
"bodyHtml": "<blockquote data-sourcepos=\"1:1-1:135\" dir=\"auto\">&#x000A;<p data-sourcepos=\"1:3-1:135\">I think we should still show some message to the user when they try to run <code>GitLab: *</code> command and the extension fails to execute it.</p>&#x000A;</blockquote>&#x000A;<p data-sourcepos=\"3:1-3:64\" dir=\"auto\">Will it be enough to log the error message <code>project not found</code>?</p>&#x000A;<p data-sourcepos=\"5:1-5:129\" dir=\"auto\">It's critical issue, I can't use ext cause I have on my workspace some repo from Github, it lead to issue what I'm trying to fix.</p>"
}
]
}
},
{
"replyId": "gid://gitlab/IndividualNoteDiscussion/ffd1e603c157acaa58f1bdeb45e03fbc47450c57",
"createdAt": "2020-12-03T12:50:21Z",
"notes": {
"pageInfo": {
"hasNextPage": false,
"endCursor": "MQ"
},
"nodes": [
{
"id": "gid://gitlab/Note/459784823",
"createdAt": "2020-12-03T12:50:21Z",
"system": true,
"author": {
"avatarUrl": "https://secure.gravatar.com/avatar/6042a9152ada74d9fb6a0cdce895337e?s=80&d=identicon",
"name": "Tomas Vik",
"username": "viktomas",
"webUrl": "https://gitlab.com/viktomas"
},
"body": "mentioned in issue #275",
"bodyHtml": "<p data-sourcepos=\"1:1-1:23\" dir=\"auto\">mentioned in issue <a href=\"/gitlab-org/gitlab-vscode-extension/-/issues/275\" data-original=\"#275\" data-link=\"false\" data-link-reference=\"false\" data-project=\"278964\" data-issue=\"75467458\" data-reference-type=\"issue\" data-container=\"body\" data-placement=\"top\" title=\"API request failed when trying to get current project because: Error: 404 Project Not Found\" class=\"gfm gfm-issue has-tooltip\">#275</a></p>"
}
]
}
}
]
}
}
}
}
......@@ -2,9 +2,11 @@ const assert = require('assert');
const vscode = require('vscode');
const sinon = require('sinon');
const EventEmitter = require('events');
const { graphql } = require('msw');
const webviewController = require('../../src/webview_controller');
const { tokenService } = require('../../src/services/token_service');
const openIssueResponse = require('./fixtures/rest/open_issue.json');
const discussionsResponse = require('./fixtures/graphql/discussions.json');
const {
getServer,
......@@ -29,10 +31,11 @@ describe('GitLab webview', () => {
before(async () => {
server = getServer([
createJsonEndpoint(
`/projects/${openIssueResponse.project_id}/issues/${openIssueResponse.iid}/discussions`,
[],
),
graphql.query('GetIssueDiscussions', (req, res, ctx) => {
if (req.variables.projectPath === 'gitlab-org/gitlab')
return res(ctx.data(discussionsResponse));
return res(ctx.data({ project: null }));
}),
createJsonEndpoint(
`/projects/${openIssueResponse.project_id}/issues/${openIssueResponse.iid}/resource_label_events`,
[],
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册