gitlab_service.js 5.1 KB
Newer Older
1
const vscode = require('vscode');
F
Fatih Acet 已提交
2
const opn = require('opn');
3 4
const request = require('request-promise');
const gitService = require('./git_service');
F
Fatih Acet 已提交
5
const statusBar = require('./status_bar');
F
Fatih Acet 已提交
6

F
Fatih Acet 已提交
7
let glToken = null;
F
Fatih Acet 已提交
8
let branchMR = null;
9

F
Fatih Acet 已提交
10
async function fetch(path, method = 'GET') {
11 12 13
  const { instanceUrl } = vscode.workspace.getConfiguration('gitlab');
  const apiRoot = `${instanceUrl}/api/v4`;

F
Fatih Acet 已提交
14
  if (!glToken) {
15
    return vscode.window.showInformationMessage('GitLab Workflow: Cannot make request. No token found.');
F
Fatih Acet 已提交
16 17
  }

18 19
  const config = {
    url: `${apiRoot}${path}`,
F
Fatih Acet 已提交
20
    method,
21
    headers: {
F
Fatih Acet 已提交
22
      'PRIVATE-TOKEN': glToken,
23 24 25 26 27 28 29 30
    }
  };

  const response = await request(config);

  try {
    return JSON.parse(response);
  } catch (e) {
31
    vscode.window.showInformationMessage('GitLab Workflow: Failed to perform your operation.');
F
Fatih Acet 已提交
32
    console.log('Failed to execute fetch', e);
33 34 35 36
    return { error: e };
  }
}

F
Fatih Acet 已提交
37
async function fetchUser(userName) {
38
  try {
F
Fatih Acet 已提交
39 40 41
    const path = userName ? `/user?search=${userName}`: '/user';

    return await fetch(path);
42
  } catch (e) {
F
Fatih Acet 已提交
43 44 45 46 47 48 49
    let message = 'GitLab Workflow: GitLab user not found.'

    if (!userName) {
      message += ' Check your Personal Access Token.';
    }

    vscode.window.showInformationMessage(message);
50 51 52
  }
}

53
async function fetchMyOpenMergeRequests() {
F
Fatih Acet 已提交
54 55 56 57 58 59
  const project = await fetchCurrentProject();

  if (project) {
    return await fetch(`/projects/${project.id}/merge_requests?scope=created-by-me&state=opened`);
  }

60
  return [];
61 62 63
}

async function fetchLastPipelineForCurrentBranch() {
F
Fatih Acet 已提交
64
  const project = await fetchCurrentProject();
65

F
Fatih Acet 已提交
66 67 68 69
  if (project) {
    const branchName = await gitService.fetchTrackingBranchName();
    const pipelinesRootPath = `/projects/${project.id}/pipelines`;
    const pipelines = await fetch(`${pipelinesRootPath}?ref=${branchName}`);
70 71

    if (pipelines.length) {
F
Fatih Acet 已提交
72
      return await fetch(`${pipelinesRootPath}/${pipelines[0].id}`);
73 74 75 76 77 78 79 80
    }

    return null;
  }

  return null;
}

F
Fatih Acet 已提交
81 82 83 84 85 86 87 88 89 90 91 92 93
async function fetchCurrentProject() {
  const remote = await gitService.fetchGitRemote();

  if (remote) {
    const { namespace, project } = remote;
    const projectData = await fetch(`/projects/${namespace}%2F${project}`);

    return projectData || null;
  }

  return null;
}

F
Fatih Acet 已提交
94 95 96 97 98
/**
 * GitLab API doesn't support getting open MR by commit ID or branch name.
 * Using this recursive fetcher method, we fetch 100 MRs at a time and do pagination
 * until we find the MR for current branch. This method will retry max 5 times.
 */
F
Fatih Acet 已提交
99
async function fetchOpenMergeRequestForCurrentBranch() {
F
Fatih Acet 已提交
100 101 102 103
  if (branchMR) {
    return branchMR;
  }

F
Fatih Acet 已提交
104 105 106 107
  const project = await fetchCurrentProject();
  const branchName = await gitService.fetchTrackingBranchName();
  let page = 1;

F
Fatih Acet 已提交
108
  // Recursive fetcher method to find the branch MR in MR list.
F
Fatih Acet 已提交
109 110 111 112 113 114 115 116
  async function fetcher() {
    const mrs = await fetch(`/projects/${project.id}/merge_requests?state=opened&per_page=100&page=${page}`);

    const [mr] = mrs.filter((mr) => {
      return mr.source_branch === branchName;
    });

    if (mr) {
F
Fatih Acet 已提交
117 118 119 120
      if (page > 1) {  // Cache only if we need to do pagination.
        branchMR = mr;
      }

F
Fatih Acet 已提交
121 122 123
      return mr;
    }

124
    if (page <= 5 && mrs.length === 100) { // Retry max 5 times.
F
Fatih Acet 已提交
125 126 127 128 129 130 131 132
      page = page + 1;
      return await fetcher();
    }
  }

  return project ? await fetcher() : null;
}

F
Fatih Acet 已提交
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
/**
 * Cancels or retries last pipeline or creates a new pipeline for current branch.
 *
 * @param {string} action create|retry|cancel
 */
async function handlePipelineAction(action) {
  const pipeline = await fetchLastPipelineForCurrentBranch();
  const project = await fetchCurrentProject();

  if (pipeline && project) {
    let endpoint = `/projects/${project.id}/pipelines/${pipeline.id}/${action}`;
    let newPipeline = null;

    if (action === 'create') {
      const branchName = await gitService.fetchTrackingBranchName();
      endpoint = `/projects/${project.id}/pipeline?ref=${branchName}`;
    }

    try {
      newPipeline = await fetch(endpoint, 'POST');
    } catch (e) {
      vscode.window.showErrorMessage(`GitLab Workflow: Failed to ${action} pipeline.`);
    }

    if (newPipeline) {
      opn(`${project.web_url}/pipelines/${newPipeline.id}`);
      statusBar.refreshPipelines();
    }
  } else {
    vscode.window.showErrorMessage('GitLab Workflow: No project or pipeline found.');
  }
}

F
Fatih Acet 已提交
166 167 168 169 170 171 172 173
async function fetchMRIssues(mrId) {
  const project = await fetchCurrentProject();
  let issues = [];

  if (project) {
    try {
      issues = await fetch(`/projects/${project.id}/merge_requests/${mrId}/closes_issues`);
    } catch (e) {
F
Fatih Acet 已提交
174
      console.log('Failed to execute fetchMRIssue', e);
F
Fatih Acet 已提交
175 176 177 178 179 180
    }
  }

  return issues;
};

F
Fatih Acet 已提交
181 182 183 184
/**
 * @private
 * @param {string} token GL PAT
 */
F
Fatih Acet 已提交
185 186 187 188
const _setGLToken = (token) => {
  glToken = token;
}

189
exports.fetchUser = fetchUser;
190 191 192
exports.fetchMyOpenMergeRequests = fetchMyOpenMergeRequests;
exports.fetchOpenMergeRequestForCurrentBranch = fetchOpenMergeRequestForCurrentBranch;
exports.fetchLastPipelineForCurrentBranch = fetchLastPipelineForCurrentBranch;
F
Fatih Acet 已提交
193
exports.fetchCurrentProject = fetchCurrentProject;
F
Fatih Acet 已提交
194
exports.handlePipelineAction = handlePipelineAction;
F
Fatih Acet 已提交
195
exports.fetchMRIssues = fetchMRIssues;
F
Fatih Acet 已提交
196
exports._setGLToken = _setGLToken;