From 9779d77eb11b28f6579de4da8076a9ce939ee91b Mon Sep 17 00:00:00 2001
From: Lukas Eipert <leipert@users.noreply.github.com>
Date: Mon, 8 Jul 2019 16:43:39 +0200
Subject: [PATCH] feat(gitlab): Improve performance for projects with a lot of
 branches (#3936)

1. `findPr` also includes the `source_branch` API parameter which
shortens execution time considerably
2. `getPrList` also filters `author_id` for the renovate's user
---
 lib/platform/common.ts             |  1 +
 lib/platform/gitlab/index.ts       | 55 ++++++++++++++++++------------
 test/platform/gitlab/index.spec.ts | 36 ++++++++++++-------
 3 files changed, 58 insertions(+), 34 deletions(-)

diff --git a/lib/platform/common.ts b/lib/platform/common.ts
index a8703c70df..512ac0e8d9 100644
--- a/lib/platform/common.ts
+++ b/lib/platform/common.ts
@@ -4,6 +4,7 @@ export interface IGotApiOptions {
   useCache?: boolean;
   hostType?: string;
   body?: any;
+  query?: string | { [key: string]: string | number } | URLSearchParams;
 }
 
 export interface IGotApi<TOptions extends object = any> {
diff --git a/lib/platform/gitlab/index.ts b/lib/platform/gitlab/index.ts
index 18ae6192ae..1308558f48 100644
--- a/lib/platform/gitlab/index.ts
+++ b/lib/platform/gitlab/index.ts
@@ -1,4 +1,4 @@
-import URL from 'url';
+import URL, { URLSearchParams } from 'url';
 import is from '@sindresorhus/is';
 
 import api from './gl-got-wrapper';
@@ -22,6 +22,8 @@ const defaults = {
   endpoint: 'https://gitlab.com/api/v4/',
 };
 
+let authorId: number;
+
 export async function initPlatform({
   endpoint,
   token,
@@ -42,7 +44,9 @@ export async function initPlatform({
     logger.info('Using default GitLab endpoint: ' + res.endpoint);
   }
   try {
-    res.gitAuthor = (await api.get(`user`, { token })).body.email;
+    const user = (await api.get(`user`, { token })).body;
+    res.gitAuthor = user.email;
+    authorId = user.id;
   } catch (err) {
     logger.info(
       { err },
@@ -203,8 +207,13 @@ export async function getBranchPr(branchName: string) {
   if (!(await branchExists(branchName))) {
     return null;
   }
-  const urlString = `projects/${config.repository}/merge_requests?state=opened&per_page=100`;
-  const res = await api.get(urlString, { paginate: true });
+  const query = new URLSearchParams({
+    per_page: '100',
+    state: 'opened',
+    source_branch: branchName,
+  });
+  const urlString = `projects/${config.repository}/merge_requests`;
+  const res = await api.get(urlString, { query, paginate: true });
   logger.debug(`Got res with ${res.body.length} results`);
   let pr: any = null;
   res.body.forEach((result: { source_branch: string }) => {
@@ -590,25 +599,29 @@ export async function ensureCommentRemoval(issueNo: number, topic: string) {
   }
 }
 
+const mapPullRequests = (pr: {
+  iid: number;
+  source_branch: string;
+  title: string;
+  state: string;
+  created_at: string;
+}) => ({
+  number: pr.iid,
+  branchName: pr.source_branch,
+  title: pr.title,
+  state: pr.state === 'opened' ? 'open' : pr.state,
+  createdAt: pr.created_at,
+});
+
 export async function getPrList() {
   if (!config.prList) {
-    const urlString = `projects/${config.repository}/merge_requests?per_page=100`;
-    const res = await api.get(urlString, { paginate: true });
-    config.prList = res.body.map(
-      (pr: {
-        iid: number;
-        source_branch: string;
-        title: string;
-        state: string;
-        created_at: string;
-      }) => ({
-        number: pr.iid,
-        branchName: pr.source_branch,
-        title: pr.title,
-        state: pr.state === 'opened' ? 'open' : pr.state,
-        createdAt: pr.created_at,
-      })
-    );
+    const query = new URLSearchParams({
+      per_page: '100',
+      author_id: `${authorId}`,
+    });
+    const urlString = `projects/${config.repository}/merge_requests`;
+    const res = await api.get(urlString, { query, paginate: true });
+    config.prList = res.body.map(mapPullRequests);
   }
   return config.prList;
 }
diff --git a/test/platform/gitlab/index.spec.ts b/test/platform/gitlab/index.spec.ts
index 71485c73a6..104212f266 100644
--- a/test/platform/gitlab/index.spec.ts
+++ b/test/platform/gitlab/index.spec.ts
@@ -260,13 +260,13 @@ describe('platform/gitlab', () => {
         // branchExists
         body: [],
       } as any);
-      const pr = await gitlab.getBranchPr('somebranch');
+      const pr = await gitlab.getBranchPr('some-branch');
       expect(pr).toBeNull();
     });
     it('should return the PR object', async () => {
       await initRepo();
       api.get.mockReturnValueOnce({
-        body: [{ number: 91, source_branch: 'somebranch' }],
+        body: [{ iid: 91, source_branch: 'some-branch', state: 'opened' }],
       } as any);
       api.get.mockReturnValueOnce({
         body: {
@@ -284,7 +284,7 @@ describe('platform/gitlab', () => {
       api.get.mockReturnValueOnce({ body: [] } as any); // get branch commit
       api.get.mockReturnValueOnce({ body: [{ status: 'success' }] } as any); // get commit statuses
       api.get.mockReturnValueOnce({ body: 'foo' } as any);
-      const pr = await gitlab.getBranchPr('somebranch');
+      const pr = await gitlab.getBranchPr('some-branch');
       expect(pr).toMatchSnapshot();
     });
   });
@@ -633,7 +633,7 @@ describe('platform/gitlab', () => {
       api.get.mockResolvedValueOnce({
         body: [
           {
-            number: 1,
+            iid: 1,
             source_branch: 'branch-a',
             title: 'branch a pr',
             state: 'opened',
@@ -647,7 +647,7 @@ describe('platform/gitlab', () => {
       api.get.mockReturnValueOnce({
         body: [
           {
-            number: 1,
+            iid: 1,
             source_branch: 'branch-a',
             title: 'branch a pr',
             state: 'merged',
@@ -657,25 +657,35 @@ describe('platform/gitlab', () => {
       const res = await gitlab.findPr('branch-a', null, '!open');
       expect(res).toBeDefined();
     });
-    it('caches pr list', async () => {
+
+    it('returns true if open and with title', async () => {
       api.get.mockReturnValueOnce({
         body: [
           {
-            number: 1,
+            iid: 1,
             source_branch: 'branch-a',
             title: 'branch a pr',
             state: 'opened',
           },
         ],
       } as any);
-      let res = await gitlab.findPr('branch-a', null);
-      expect(res).toBeDefined();
-      res = await gitlab.findPr('branch-a', 'branch a pr');
+      const res = await gitlab.findPr('branch-a', 'branch a pr', 'open');
       expect(res).toBeDefined();
-      res = await gitlab.findPr('branch-a', 'branch a pr', 'open');
+    });
+
+    it('returns true with title', async () => {
+      api.get.mockReturnValueOnce({
+        body: [
+          {
+            iid: 1,
+            source_branch: 'branch-a',
+            title: 'branch a pr',
+            state: 'opened',
+          },
+        ],
+      } as any);
+      const res = await gitlab.findPr('branch-a', 'branch a pr');
       expect(res).toBeDefined();
-      res = await gitlab.findPr('branch-b');
-      expect(res).not.toBeDefined();
     });
   });
   describe('createPr(branchName, title, body)', () => {
-- 
GitLab