Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A brand new API - pyteamcity.future #37

Merged
merged 36 commits into from
Aug 10, 2016
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
96fedf4
Create the future
msabramo Aug 3, 2016
edf9af3
_pyteamcity.v2 => pyteamcity.future
msabramo Aug 3, 2016
551203e
Delete todo comment re: removing surveymonkey refs
msabramo Aug 4, 2016
9dc8d8e
v2 => future
msabramo Aug 4, 2016
673565a
test_builds.py => test_build.py
msabramo Aug 4, 2016
a621969
Add VCSRoot support
msabramo Aug 4, 2016
3dfd45e
Split into separate modules
msabramo Aug 4, 2016
47d44a2
Delete todo about opening WIP PR
msabramo Aug 4, 2016
da0e73d
Delete todo about adding note to README
msabramo Aug 4, 2016
66129b1
Add UserGroup model
msabramo Aug 4, 2016
265675c
queryset fixes
msabramo Aug 4, 2016
b09f039
flake8 fixes
msabramo Aug 4, 2016
ac4a8ef
Finish implementing QueuedBuild
msabramo Aug 4, 2016
8d5ccb5
Add flake8 and py35 tox targets
msabramo Aug 4, 2016
e0de037
Move py.test options from tox.ini to pytest.ini
msabramo Aug 4, 2016
83b226d
Add unit tests for queued_builds
msabramo Aug 4, 2016
f443fb5
Move legacy stuff to its own dir
msabramo Aug 4, 2016
ab63bfd
Add TeamCity.server_info
msabramo Aug 4, 2016
765cf22
.travis.yml: Post coverage to couverture.io (#38)
msabramo Aug 4, 2016
459c7d8
Put comment in pyteamcity/future/core/__init__.py (#39)
msabramo Aug 4, 2016
681a8dc
use long option name
aconrad Aug 5, 2016
0d1cbde
Add support for getting artifacts (#40)
msabramo Aug 8, 2016
88ee9e4
.travis.yml: Skip couverture if no coverage.xml
msabramo Aug 8, 2016
054f4ba
Add message about future to legacy.py
msabramo Aug 8, 2016
d9967bb
Nix raise_for_status; raise our own exceptions
msabramo Aug 8, 2016
e2254eb
Fix flake8 warning
msabramo Aug 8, 2016
4a37502
Add _metadata_url property to Artifact
msabramo Aug 8, 2016
4b9be0e
Reimplement get_artifact_by_path
msabramo Aug 8, 2016
0bb5708
Add ArtifactNotFound exception
msabramo Aug 8, 2016
53b4b95
Allow specifying TEAMCITY_PROTO env var
msabramo Aug 9, 2016
6dd1852
Add create and delete project
msabramo Aug 9, 2016
66f7c4a
Implement create/delete build type
msabramo Aug 9, 2016
251fa92
Add BuildType.set_paused
msabramo Aug 10, 2016
607962f
Add BuildType.reset_build_counter
msabramo Aug 10, 2016
9a62689
Add QueuedBuild.trigger_build
msabramo Aug 10, 2016
d50f9ec
Make queued_build.build_type.project work
msabramo Aug 10, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
13 changes: 13 additions & 0 deletions pyteamcity/future/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""
@todo: Allow creating projects
@todo: Allow deleting projects
@todo: Allow creating build types
@todo: Allow deleting build types
@todo: Allow pausing build types
@todo: Allow triggering builds
@todo: Allow canceling builds
@todo: docstrings for classes
"""

from .page_joiner import PageJoiner # noqa
from .teamcity import TeamCity # noqa
133 changes: 133 additions & 0 deletions pyteamcity/future/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import requests

from .core.parameter import Parameter
from .core.queryset import QuerySet

from .agent_pool import AgentPool, AgentPoolQuerySet


class Agent(object):
"""
(Pdb++) agent._data_dict.keys()
[u'typeId', u'name', u'ip', u'enabled', u'properties',
u'uptodate', u'href', u'connected', u'authorized',
u'id', u'pool']
"""

def __init__(self, id, href, name, type_id, ip,
enabled, connected, authorized,
pool_id,
query_set, data_dict=None):
self.id = id
self.href = href
self.name = name
self.type_id = type_id
self.ip = ip
self.enabled = enabled
self.connected = connected
self.authorized = authorized
self.pool_id = pool_id
self.query_set = query_set
self._data_dict = data_dict

def __repr__(self):
return '<%s.%s: id=%r name=%r>' % (
self.__module__,
self.__class__.__name__,
self.id,
self.name)

@classmethod
def from_dict(cls, d, query_set=None):
return cls(
id=d.get('id'),
href=d.get('href'),
name=d.get('name'),
type_id=d.get('typeId'),
ip=d.get('ip'),
enabled=d.get('enabled'),
connected=d.get('connected'),
authorized=d.get('authorized'),
pool_id=d.get('pool', {}).get('id'),
query_set=query_set,
data_dict=d)

@property
def pool(self):
teamcity = self.query_set.teamcity
if 'agentPool' in self._data_dict:
agent_pool = AgentPool.from_dict(self._data_dict.get('pool'))
else:
agent_pool = AgentPoolQuerySet(teamcity).get(id=self.pool_id)
return agent_pool

@property
def parameters_dict(self):
d = {}

for param in self._data_dict['properties']['property']:
param_obj = Parameter()
if 'value' in param:
param_obj.value = param['value']
if 'type' in param:
param_obj.ptype = param['type']
d[param['name']] = param_obj

return d

@property
def teamcity(self):
return self.query_set.teamcity

def set_enabled(self, enabled_str, dry_run=False):
extra_headers = {'Content-Type': 'text/plain',
'Accept': 'text/plain'}
req = self._put_request('enabled', data=enabled_str,
extra_headers=extra_headers)
if dry_run:
return req
return self.teamcity.session.send(req)

def _put_request(self, relative_uri, data, extra_headers):
url = self._get_url() + '/' + relative_uri
headers = dict(self.teamcity.session.headers)
headers.update(extra_headers)
req = requests.Request(
method='PUT',
url=url,
data=data,
headers=headers)
prepped = self.teamcity.session.prepare_request(req)
return prepped

def _get_url(self):
return AgentQuerySet(self.teamcity).get(id=self.id, just_url=True)

def enable(self, dry_run=False):
return self.set_enabled('true', dry_run=dry_run)

def disable(self, dry_run=False):
return self.set_enabled('false', dry_run=dry_run)


class AgentQuerySet(QuerySet):
uri = '/app/rest/agents/'
_entity_factory = Agent

def filter(self, id=None, name=None,
connected=None, authorized=None, enabled=None):
if id is not None:
self._add_pred('id', id)
if name is not None:
self._add_pred('name', name)
if connected is not None:
self._add_pred('connected', connected)
if authorized is not None:
self._add_pred('authorized', authorized)
if enabled is not None:
self._add_pred('enabled', enabled)
return self

def __iter__(self):
return (self._entity_factory.from_dict(d, self)
for d in self._data()['agent'])
60 changes: 60 additions & 0 deletions pyteamcity/future/agent_pool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from .core.queryset import QuerySet
from .project import Project


class AgentPool(object):
def __init__(self, id, href, name,
query_set, data_dict=None):
self.id = id
self.href = href
self.name = name
self.query_set = query_set
self._data_dict = data_dict

def __repr__(self):
return '<%s.%s: id=%r name=%r>' % (
self.__module__,
self.__class__.__name__,
self.id,
self.name)

@classmethod
def from_dict(cls, d, query_set=None):
return cls(
id=d.get('id'),
href=d.get('href'),
name=d.get('name'),
query_set=query_set,
data_dict=d)

@property
def agents(self):
from .agent import Agent

ret = []
for agent in self._data_dict['agents']['agent']:
ret.append(Agent.from_dict(agent))
return ret

@property
def projects(self):
ret = []
for project in self._data_dict['projects']['project']:
ret.append(Project.from_dict(project))
return ret


class AgentPoolQuerySet(QuerySet):
uri = '/app/rest/agentPools/'
_entity_factory = AgentPool

def filter(self, id=None, name=None):
if id is not None:
self._add_pred('id', id)
if name is not None:
self._add_pred('name', name)
return self

def __iter__(self):
return (self._entity_factory.from_dict(d, self)
for d in self._data()['agentPool'])
173 changes: 173 additions & 0 deletions pyteamcity/future/build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
from six.moves.urllib.parse import quote

from .core.parameter import Parameter
from .core.queryset import QuerySet
from .core.utils import parse_date_string

from .agent import Agent
from .build_type import BuildType, BuildTypeQuerySet
from .user import User


class Build(object):
def __init__(self, id, number,
build_type_id,
queued_date_string, start_date_string, finish_date_string,
state, status, branch_name, href,
build_query_set, data_dict=None):
self.id = id
self.number = number
self.queued_date_string = queued_date_string
self.start_date_string = start_date_string
self.finish_date_string = finish_date_string
self.build_type_id = build_type_id
self.state = state
self.status = status
self.branch_name = branch_name
self.href = href
self.build_query_set = build_query_set
self._data_dict = data_dict

@property
def user(self):
if 'user' in self._data_dict.get('triggered', {}):
return User.from_dict(self._data_dict['triggered']['user'])

@property
def queued_date(self):
return parse_date_string(self.queued_date_string)

@property
def start_date(self):
return parse_date_string(self.start_date_string)

@property
def finish_date(self):
return parse_date_string(self.finish_date_string)

@property
def agent(self):
return Agent.from_dict(self._data_dict.get('agent'))

@property
def build_type(self):
teamcity = self.build_query_set.teamcity
if 'buildType' in self._data_dict:
build_type = BuildType.from_dict(self._data_dict.get('buildType'))
elif 'buildTypeId' in self._data_dict:
build_type_id = self._data_dict['buildTypeId']
build_type = BuildTypeQuerySet(teamcity).get(id=build_type_id)

return build_type

def __repr__(self):
return '<%s.%s: id=%r build_type_id=%r number=%r>' % (
self.__module__,
self.__class__.__name__,
self.id,
self.build_type_id,
self.number)

@classmethod
def from_dict(cls, d, build_query_set):
return Build(
id=d.get('id'),
number=d.get('number'),
queued_date_string=d.get('queuedDate'),
start_date_string=d.get('startDate'),
finish_date_string=d.get('finishDate'),
build_type_id=d.get('buildTypeId'),
state=d.get('state'),
status=d.get('status'),
branch_name=d.get('branchName'),
href=d.get('href'),
build_query_set=build_query_set,
data_dict=d)

@property
def parameters_dict(self):
d = {}

for param in self._data_dict['properties']['property']:
param_obj = Parameter()
if 'value' in param:
param_obj.value = param['value']
if 'type' in param:
param_obj.ptype = param['type']
d[param['name']] = param_obj

return d


class BuildQuerySet(QuerySet):
uri = '/app/rest/builds/'
_entity_factory = Build

def filter(self,
id=None,
project=None, affected_project=None,
build_type=None, number=None, branch=None, user=None,
tags=None, pinned=None,
since_build=None, since_date=None, status=None,
agent_name=None, personal=None,
canceled=None, failed_to_start=None, running=None,
start=None, count=None, lookup_limit=None):
if id is not None:
self._add_pred('id', id)
if project is not None:
self._add_pred('project', '(%s)' % project)
if affected_project is not None:
self._add_pred('affectedProject', '(%s)' % affected_project)
if build_type is not None:
self._add_pred('buildType', build_type)
if number is not None:
self._add_pred('number', number)
if branch is not None:
self._add_pred('branch', branch)
if user is not None:
self._add_pred('user', '(%s)' % user)
if tags is not None:
if not hasattr(tags, 'split'):
tags = ','.join(tags)
self._add_pred('tags', tags)
if pinned is not None:
self._add_pred('pinned', pinned)
if since_build is not None:
self._add_pred('sinceBuild', '(%s)' % since_build)
if since_date is not None:
since_date = self._get_since_date(since_date)
self._add_pred('sinceDate', since_date)
if status is not None:
self._add_pred('status', status)
if agent_name is not None:
self._add_pred('agentName', agent_name)
if personal is not None:
self._add_pred('personal', personal)
if canceled is not None:
self._add_pred('canceled', canceled)
if failed_to_start is not None:
self._add_pred('failedToStart', failed_to_start)
if running is not None:
self._add_pred('running', running)
if start is not None:
self._add_pred('start', start)
if count is not None:
self._add_pred('count', count)
if lookup_limit is not None:
self._add_pred('lookupLimit', lookup_limit)
return self

def _get_since_date(self, since_date):
if hasattr(since_date, 'strftime'):
since_date = since_date.strftime('%Y%m%dT%H%M%S%z')

# If there's no timezone, assume UTC
if '+' not in since_date:
since_date += '+0000'

since_date = quote(since_date)
return since_date

def __iter__(self):
return (Build.from_dict(d, self)
for d in self._data()['build'])
Loading