gitlab_service.js 4.5 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.');
32 33 34 35
    return { error: e };
  }
}

36 37 38 39 40 41 42 43
async function fetchUser() {
  try {
    return await fetch('/user');
  } catch (e) {
    vscode.window.showInformationMessage('GitLab Workflow: GitLab user not found. Check your Personal Access Token.');
  }
}

44
async function fetchMyOpenMergeRequests() {
F
Fatih Acet 已提交
45 46 47 48 49 50
  const project = await fetchCurrentProject();

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

51
  return [];
52 53 54
}

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

F
Fatih Acet 已提交
57 58 59 60
  if (project) {
    const branchName = await gitService.fetchTrackingBranchName();
    const pipelinesRootPath = `/projects/${project.id}/pipelines`;
    const pipelines = await fetch(`${pipelinesRootPath}?ref=${branchName}`);
61 62

    if (pipelines.length) {
F
Fatih Acet 已提交
63
      return await fetch(`${pipelinesRootPath}/${pipelines[0].id}`);
64 65 66 67 68 69 70 71
    }

    return null;
  }

  return null;
}

F
Fatih Acet 已提交
72 73 74 75 76 77 78 79 80 81 82 83 84
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 已提交
85 86 87 88 89
/**
 * 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 已提交
90
async function fetchOpenMergeRequestForCurrentBranch() {
F
Fatih Acet 已提交
91 92 93 94
  if (branchMR) {
    return branchMR;
  }

F
Fatih Acet 已提交
95 96 97 98
  const project = await fetchCurrentProject();
  const branchName = await gitService.fetchTrackingBranchName();
  let page = 1;

F
Fatih Acet 已提交
99
  // Recursive fetcher method to find the branch MR in MR list.
F
Fatih Acet 已提交
100 101 102 103 104 105 106 107
  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 已提交
108 109 110 111
      if (page > 1) {  // Cache only if we need to do pagination.
        branchMR = mr;
      }

F
Fatih Acet 已提交
112 113 114
      return mr;
    }

F
Fatih Acet 已提交
115
    if (page <= 5) { // Retry max 5 times.
F
Fatih Acet 已提交
116 117 118 119 120 121 122 123
      page = page + 1;
      return await fetcher();
    }
  }

  return project ? await fetcher() : null;
}

F
Fatih Acet 已提交
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
/**
 * 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 已提交
157 158 159 160
/**
 * @private
 * @param {string} token GL PAT
 */
F
Fatih Acet 已提交
161 162 163 164
const _setGLToken = (token) => {
  glToken = token;
}

165
exports.fetchUser = fetchUser;
166 167 168
exports.fetchMyOpenMergeRequests = fetchMyOpenMergeRequests;
exports.fetchOpenMergeRequestForCurrentBranch = fetchOpenMergeRequestForCurrentBranch;
exports.fetchLastPipelineForCurrentBranch = fetchLastPipelineForCurrentBranch;
F
Fatih Acet 已提交
169
exports.fetchCurrentProject = fetchCurrentProject;
F
Fatih Acet 已提交
170
exports.handlePipelineAction = handlePipelineAction;
F
Fatih Acet 已提交
171
exports._setGLToken = _setGLToken;