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)
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):
--- /dev/null
+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'),
+ ]
+ )