Add support for Job Folders (CloudBees Folders Plugin).
authorJoao Vale <jpvale@gmail.com>
Fri, 24 Jan 2014 20:53:46 +0000 (20:53 +0000)
committerJoao Vale <jpvale@gmail.com>
Mon, 27 Jan 2014 12:46:10 +0000 (12:46 +0000)
jenkinsapi/jenkinsbase.py
jenkinsapi_tests/unittests/test_job_folders.py [new file with mode: 0644]
jenkinsapi_tests/unittests/test_queue.py

index bc5624ba776e93714220c639f96654f4fec4aa61..8b6f321cd184dbc8d7faf4b0a98cde639fd32477 100644 (file)
@@ -53,6 +53,8 @@ class JenkinsBase(object):
 
     def poll(self):
         self._data = self._poll()
+        if 'jobs' in self._data:
+            self._data['jobs'] = self.resolve_job_folders(self._data['jobs'])
 
     def _poll(self):
         url = self.python_api_url(self.baseurl)
@@ -67,6 +69,26 @@ class JenkinsBase(object):
             log.exception('Inappropriate content found at %s', url)
             raise JenkinsAPIException('Cannot parse %s' % response.content)
 
+    def resolve_job_folders(self, jobs):
+        for job in jobs:
+            if 'color' not in job.keys():
+                jobs.remove(job)
+                jobs += self.process_job_folder(job)
+
+        return jobs
+
+    def process_job_folder(self, folder):
+        data = self.get_data(self.python_api_url(folder['url']))
+        result = []
+
+        for job in data.get('jobs', []):
+            if 'color' not in job.keys():
+                result += self.process_job_folder(job)
+            else:
+                result.append(job)
+
+        return result
+
     @classmethod
     def python_api_url(cls, url):
         if url.endswith(config.JENKINS_API):
diff --git a/jenkinsapi_tests/unittests/test_job_folders.py b/jenkinsapi_tests/unittests/test_job_folders.py
new file mode 100644 (file)
index 0000000..a23c4d8
--- /dev/null
@@ -0,0 +1,191 @@
+import mock
+import unittest
+
+from jenkinsapi.jenkins import JenkinsBase
+
+
+class TestJobFolders(unittest.TestCase):
+    def setUp(self):
+        self.jb = JenkinsBase('http://localhost:8080/', poll=False)
+
+    @mock.patch('jenkinsapi.jenkins.JenkinsBase.resolve_job_folders')
+    @mock.patch('jenkinsapi.jenkins.JenkinsBase._poll')
+    def test_called_in__poll(self, _poll_mock, resolve_job_folders_mock):
+        _poll_mock.return_value = {
+            'description': "My jobs",
+            'jobs': [
+                {
+                    'name': "Foo",
+                    'url': "http://localhost:8080/job/Foo",
+                    'color': "blue",
+                },
+            ],
+            'name': "All",
+            'property': [],
+            'url': "http://localhost:8080/view/All/",
+        }
+
+        self.jb.poll()
+
+        resolve_job_folders_mock.assert_called_once_with(
+            [
+                {
+                    'name': "Foo",
+                    'url': "http://localhost:8080/job/Foo",
+                    'color': "blue",
+                },
+            ],
+        )
+
+    def test_no_folders(self):
+        jobs = [
+            {
+                'name': "Foo",
+                'url': "http://localhost:8080/job/Foo",
+                'color': "blue",
+            },
+            {
+                'name': "Bar",
+                'url': "http://localhost:8080/job/Bar",
+                'color': "disabled",
+            },
+        ]
+
+        self.assertEquals(
+            self.jb.resolve_job_folders(jobs),
+            [
+                {
+                    'name': "Foo",
+                    'url': "http://localhost:8080/job/Foo",
+                    'color': "blue",
+                },
+                {
+                    'name': "Bar",
+                    'url': "http://localhost:8080/job/Bar",
+                    'color': "disabled",
+                },
+            ]
+        )
+
+    @mock.patch('jenkinsapi.jenkins.JenkinsBase.get_data')
+    def test_empty_folder(self, get_data_mock):
+        get_data_mock.return_value = {'jobs': []}
+        jobs = [
+            {
+                'name': "Folder1",
+                'url': "http://localhost:8080/job/Folder1",
+            },
+        ]
+
+        self.assertEquals(self.jb.resolve_job_folders(jobs), [])
+        get_data_mock.assert_called_once_with('http://localhost:8080/job/Folder1/api/python')
+
+    @mock.patch('jenkinsapi.jenkins.JenkinsBase.get_data')
+    def test_folder_job_mix(self, get_data_mock):
+        get_data_mock.return_value = {'jobs': [
+                {
+                    'name': "Bar",
+                    'url': "http://localhost:8080/job/Folder1/job/Bar",
+                    'color': "disabled",
+                },
+            ]
+        }
+        jobs = [
+            {
+                'name': "Foo",
+                'url': "http://localhost:8080/job/Foo",
+                'color': "blue",
+            },
+            {
+                'name': "Folder1",
+                'url': "http://localhost:8080/job/Folder1",
+            },
+        ]
+
+        self.assertEquals(
+            self.jb.resolve_job_folders(jobs),
+            [
+                {
+                    'name': "Foo",
+                    'url': "http://localhost:8080/job/Foo",
+                    'color': "blue",
+                },
+                {
+                    'name': "Bar",
+                    'url': "http://localhost:8080/job/Folder1/job/Bar",
+                    'color': "disabled",
+                },
+            ]
+        )
+        get_data_mock.assert_called_once_with('http://localhost:8080/job/Folder1/api/python')
+
+    @mock.patch('jenkinsapi.jenkins.JenkinsBase.get_data')
+    def test_multiple_folder_levels(self, get_data_mock):
+        get_data_mock.side_effect = [
+            # first call
+            {
+                'jobs': [
+                    {
+                        'name': "Bar",
+                        'url': "http://localhost:8080/job/Folder1/job/Bar",
+                        'color': "disabled",
+                    },
+                    {
+                        'name': "Folder2",
+                        'url': "http://localhost:8080/job/Folder1/job/Folder2",
+                    },
+                ]
+            },
+
+            # second call
+            {
+                'jobs': [
+                    {
+                        'name': "Baz",
+                        'url': "http://localhost:8080/job/Folder1/job/Folder2/job/Baz",
+                        'color': "disabled",
+                    },
+                ]
+            },
+        ]
+
+        jobs = [
+            {
+                'name': "Foo",
+                'url': "http://localhost:8080/job/Foo",
+                'color': "blue",
+            },
+            {
+                'name': "Folder1",
+                'url': "http://localhost:8080/job/Folder1",
+            },
+        ]
+
+        self.assertEquals(
+            self.jb.resolve_job_folders(jobs),
+            [
+                {
+                    'name': "Foo",
+                    'url': "http://localhost:8080/job/Foo",
+                    'color': "blue",
+                },
+                {
+                    'name': "Bar",
+                    'url': "http://localhost:8080/job/Folder1/job/Bar",
+                    'color': "disabled",
+                },
+                {
+                    'name': "Baz",
+                    'url': "http://localhost:8080/job/Folder1/job/Folder2/job/Baz",
+                    'color': "disabled",
+                },
+            ]
+        )
+
+        self.assertEquals(
+            get_data_mock.call_args_list,
+            [
+                mock.call('http://localhost:8080/job/Folder1/api/python'),
+                mock.call('http://localhost:8080/job/Folder1/job/Folder2/api/python'),
+            ]
+        )
index ad0da6cf85caed782f0243e457e44f18bcaf27c8..8afaea1e9135da03bf1e50bf20ec52c2ff675ad6 100644 (file)
@@ -29,6 +29,7 @@ class TestQueue(unittest.TestCase):
         'jobs': [
             {
                 'name': 'utmebvpxrw',
+                'color': 'blue',
                 'url': 'http://localhost/job/utmebvpxrw'
             }
         ]