[copr] master: Update the API code (9b617e5)
by bkabrda@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit 9b617e528be131db0d95db7373a578466e8f390c
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Mon Jan 28 09:10:17 2013 +0100
Update the API code
Now the API makes use of the form to validate the input when the request uses POST
for GET requests though, we need to check the argument given to the request.
>---------------------------------------------------------------
coprs_frontend/coprs/views/api_ns/api_general.py | 129 ++++++++++++++-------
1 files changed, 86 insertions(+), 43 deletions(-)
diff --git a/coprs_frontend/coprs/views/api_ns/api_general.py b/coprs_frontend/coprs/views/api_ns/api_general.py
index a6a6c94..b41ec07 100644
--- a/coprs_frontend/coprs/views/api_ns/api_general.py
+++ b/coprs_frontend/coprs/views/api_ns/api_general.py
@@ -1,9 +1,14 @@
+import datetime
+import random
+import string
import time
import flask
+from coprs import app
from coprs import db
from coprs import exceptions
+from coprs import forms
from coprs.views.misc import login_required
@@ -13,12 +18,24 @@ from coprs.logic import builds_logic
from coprs.logic import coprs_logic
+def generate_api_token(size=30):
+ """ Generate a random string used as token to access the API
+ remotely.
+
+ :kwarg: size, the size of the token to generate, defaults to 30
+ chars.
+ :return: a string, the API token for the user.
+
+ """
+ return ''.join(random.choice(string.ascii_lowercase) for x in range(size))
+
+
@api_ns.route('/')
def api_home():
""" Renders the home page of the api.
This page provides information on how to call/use the API.
"""
- return flask.render_template('coprs/api.html')
+ return flask.render_template('api.html')
@api_ns.route('/new/', methods = ["GET", "POST"])
@@ -33,16 +50,12 @@ def api_new_token():
db.session.add(user)
db.session.commit()
flask.g.user = user
- return flask.redirect(flask.url_for('misc.api'))
+ return flask.redirect(flask.url_for('api_ns.api_home'))
-(a)api_ns.route('/add/<name>/<chroots>/',
- defaults={'repos':"", 'initial_pkgs':""})
-(a)api_ns.route('/add/<name>/<chroots>/<repos>/',
- defaults={'initial_pkgs':None})
-(a)api_ns.route('/add/<name>/<chroots>/<repos>/<initial_pkgs>/')
+(a)api_ns.route('/copr/new/', methods=['POST'])
@login_required
-def api_add_copr(name, chroots, repos="", initial_pkgs=""):
+def api_new_copr():
""" Receive information from the user on how to create its new copr,
check their validity and create the corresponding copr.
@@ -54,43 +67,73 @@ def api_add_copr(name, chroots, repos="", initial_pkgs=""):
build in this new copr
"""
- infos = []
- copr = coprs_logic.CoprsLogic.add_coprs(
- name=name.strip(),
- repos=" ".join([repo.strip() for repo in repos.split(',')]),
- owner=flask.g.user,
- selected_chroots=[chroot.strip()
- for chroot in chroots.split(',')]
- )
- infos.append('New copr was successfully created.')
-
- if initial_pkgs:
- builds_logic.BuildsLogic.add_build(
- pkgs=" ".join([pkg.strip() for pkg in initial_pkgs.split(',')]),
- copr=copr,
- owner=flask.g.user)
- infos.append('Initial packages were successfully submitted '
- 'for building.')
- return '{"output" : "%s"}' % ("\n".join(infos))
-
-
-(a)api_ns.route('/list/<username>/', methods=['GET'])
-def api_list_copr(username):
+ form = forms.CoprFormFactory.create_form_cls()(csrf_enabled=False)
+ if form.validate_on_submit():
+ infos = []
+ try:
+ copr = coprs_logic.CoprsLogic.add(
+ name=form.name.data.strip(),
+ repos=" ".join([repo.strip()
+ for repo in form.repos.data.split(',')]),
+ user=flask.g.user,
+ selected_chroots=form.selected_chroots,
+ description=form.description.data,
+ instructions=form.instructions.data,
+ check_for_duplicates=True,
+ )
+ infos.append('New copr was successfully created.')
+
+ if form.initial_pkgs.data:
+ builds_logic.BuildsLogic.add_build(
+ pkgs=" ".join([pkg.strip()
+ for pkg in form.initial_pkgs.data.split(',')]),
+ copr=copr,
+ owner=flask.g.user)
+ infos.append('Initial packages were successfully submitted '
+ 'for building.')
+
+ output = '{"output" : "ok", "message" : "%s"}' % ("\n".join(infos))
+ db.session.commit()
+ except exceptions.DuplicateCoprNameException, err:
+ output = '{"output": "notok", "error": "%s"}' % err
+ db.session.rollback()
+
+ else:
+ errormsg = "\n".join(form.errors['name'])
+ errormsg = errormsg.replace('"', "'")
+ output = '{"output": "notok", "error": "%s"}' % errormsg
+
+ return flask.Response(output, mimetype='application/json')
+
+
+(a)api_ns.route('/owned/')
+def api_list_copr():
""" Return the list of coprs owned by the given user.
:arg username: the username of the person one would like to the
coprs of.
+
"""
- query = coprs_logic.CoprsLogic.get_multiple(flask.g.user,
- user_relation = 'owned', username = username)
- output = '{"repos":[\n'
- repos = query.all()
- cnt = 0
- for repo in repos:
- output += '{"name" : "%s", "repos" : "%s"}\n' % (
- repo.name, repo.repos)
- cnt = cnt + 1
- if cnt < len(repos):
- output += ','
- output += "]}\n"
- return output
+ if 'username' in flask.request.args:
+ username = flask.request.args['username']
+ query = coprs_logic.CoprsLogic.get_multiple(flask.g.user,
+ user_relation = 'owned', username = username)
+ repos = query.all()
+ output = '{"output": "ok",\n"repos":[\n'
+ cnt = 0
+ for repo in repos:
+ output += '{'\
+ '"name" : "%s", '\
+ '"repos" : "%s", '\
+ '"description": "%s", '\
+ '"instructions": "%s" '\
+ '}\n' % (repo.name, repo.repos, repo.description,
+ repo.instructions)
+ cnt = cnt + 1
+ if cnt < len(repos):
+ output += ','
+ output += "]}\n"
+ else:
+ output = '{"output": "notok", "error": "Invalid request"}'
+
+ return flask.Response(output, mimetype='application/json')
11 years, 2 months
[copr] master: Update the logic (0de746e)
by bkabrda@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit 0de746efc3b5fa66a522fc76ee7ff90fd7e94214
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Mon Jan 28 09:06:23 2013 +0100
Update the logic
- The check_for_duplicates should be a parameter since the logic can be called from
the API where the form validation is different
- When raising an error, give an error message, this way when the API calls the function
and catch the error, it can make something of it.
>---------------------------------------------------------------
coprs_frontend/coprs/config.py | 1 +
coprs_frontend/coprs/logic/coprs_logic.py | 15 +++++++++------
2 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/coprs_frontend/coprs/config.py b/coprs_frontend/coprs/config.py
index 6218ea9..3ab0410 100644
--- a/coprs_frontend/coprs/config.py
+++ b/coprs_frontend/coprs/config.py
@@ -24,6 +24,7 @@ class Config(object):
class ProductionConfig(Config):
DEBUG = False
+ SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://bkabrda:pass@/copr'
class DevelopmentConfig(Config):
DEBUG = True
diff --git a/coprs_frontend/coprs/logic/coprs_logic.py b/coprs_frontend/coprs/logic/coprs_logic.py
index a862055..a27668e 100644
--- a/coprs_frontend/coprs/logic/coprs_logic.py
+++ b/coprs_frontend/coprs/logic/coprs_logic.py
@@ -47,7 +47,8 @@ class CoprsLogic(object):
return query
@classmethod
- def add(cls, user, name, repos, selected_chroots, description, instructions):
+ def add(cls, user, name, repos, selected_chroots, description,
+ instructions, check_for_duplicates=False):
copr = models.Copr(name=name,
repos=repos,
owner=user,
@@ -55,21 +56,23 @@ class CoprsLogic(object):
instructions=instructions,
created_on=int(time.time()))
CoprsLogic.new(user, copr,
- check_for_duplicates=False) # form validation checks for duplicates
+ check_for_duplicates=check_for_duplicates) # form validation checks for duplicates
CoprChrootsLogic.new_from_names(user, copr,
selected_chroots)
return copr
@classmethod
def new(cls, user, copr, check_for_duplicates = True):
- if check_for_duplicates and cls.exists_for_current_user(user, copr.name):
- raise exceptions.DuplicateCoprNameException
+ if check_for_duplicates and cls.exists_for_current_user(user, copr.name).all():
+ raise exceptions.DuplicateCoprNameException(
+ 'Copr: "{0}" already exists'.format(copr.name))
db.session.add(copr)
@classmethod
def update(cls, user, copr, check_for_duplicates = True):
- if check_for_duplicates and cls.exists_for_current_user(user, copr.name):
- raise exceptions.DuplicateCoprNameException
+ if check_for_duplicates and cls.exists_for_current_user(user, copr.name).all():
+ raise exceptions.DuplicateCoprNameException(
+ 'Copr: "{0}" already exists'.format(copr.name))
db.session.add(copr)
@classmethod
11 years, 2 months
[copr] master: Remove un-needed code from misc (400e8e0)
by bkabrda@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit 400e8e08b75fa11f7b5526c657775d5af9d3c121
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Mon Jan 28 09:05:46 2013 +0100
Remove un-needed code from misc
>---------------------------------------------------------------
coprs_frontend/coprs/views/misc.py | 13 -------------
1 files changed, 0 insertions(+), 13 deletions(-)
diff --git a/coprs_frontend/coprs/views/misc.py b/coprs_frontend/coprs/views/misc.py
index c5cffc1..e6a3f8a 100644
--- a/coprs_frontend/coprs/views/misc.py
+++ b/coprs_frontend/coprs/views/misc.py
@@ -1,7 +1,5 @@
import datetime
import functools
-import random
-import string
import flask
@@ -26,17 +24,6 @@ def page_not_found(message):
misc = flask.Blueprint('misc', __name__)
-def generate_api_token(size=30):
- """ Generate a random string used as token to access the API
- remotely.
-
- :kwarg: size, the size of the token to generate, defaults to 30
- chars.
- :return: a string, the API token for the user.
-
- """
- return ''.join(random.choice(string.ascii_lowercase) for x in range(size))
-
@misc.route('/login/', methods=['GET', 'POST'])
@oid.loginhandler
11 years, 2 months
[copr] master: Fix the api template (cbc35b8)
by bkabrda@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit cbc35b849d30276ef9b976e639ec855a2db4a259
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Mon Jan 28 09:05:11 2013 +0100
Fix the api template
>---------------------------------------------------------------
coprs_frontend/coprs/templates/api.html | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/coprs_frontend/coprs/templates/api.html b/coprs_frontend/coprs/templates/api.html
index 2800fd3..0de97b2 100644
--- a/coprs_frontend/coprs/templates/api.html
+++ b/coprs_frontend/coprs/templates/api.html
@@ -17,14 +17,14 @@
in order to ensure you are you every 30 days.
</p>
- {% if user %}
+ {% if g.user %}
<p>Your information:</p>
<pre style="font-size:120%">
- API token : {{ user.api_token }}
- Expiration date : {{ user.api_token_expiration }}
+ API token : {{ g.user.api_token }}
+ Expiration date : {{ g.user.api_token_expiration }}
</pre>
- <a href="{{ url_for('misc.api_new_token') }}">
+ <a href="{{ url_for('api_ns.api_new_token') }}">
<input type="button" value="Generate a new token" />
</a>
{% else %}
11 years, 2 months
[copr] master: Create the API namespace (b6c2504)
by bkabrda@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit b6c2504faaf150a669823b739fe966333c960a3e
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Mon Jan 28 09:04:24 2013 +0100
Create the API namespace
This is the namespace that we will use to describe and expose the copr API.
This commit introduce it, move some code from misc to the api_general and
start with two API functions to list someone's copr and add a new one.
These two functions are documented in the /api/ (ie in the api.html template).
>---------------------------------------------------------------
coprs_frontend/coprs/__init__.py | 3 +
coprs_frontend/coprs/templates/api.html | 42 ++++++++++
coprs_frontend/coprs/views/api_ns/__init__.py | 3 +
coprs_frontend/coprs/views/api_ns/api_general.py | 96 ++++++++++++++++++++++
coprs_frontend/coprs/views/misc.py | 19 ----
5 files changed, 144 insertions(+), 19 deletions(-)
diff --git a/coprs_frontend/coprs/__init__.py b/coprs_frontend/coprs/__init__.py
index d5d732a..f239b65 100644
--- a/coprs_frontend/coprs/__init__.py
+++ b/coprs_frontend/coprs/__init__.py
@@ -22,6 +22,8 @@ import coprs.filters
import coprs.log
import coprs.models
+from coprs.views import api_ns
+from coprs.views.api_ns import api_general
from coprs.views import coprs_ns
from coprs.views.coprs_ns import coprs_builds
from coprs.views.coprs_ns import coprs_general
@@ -29,6 +31,7 @@ from coprs.views import backend_ns
from coprs.views.backend_ns import backend_general
from coprs.views import misc
+app.register_blueprint(api_ns.api_ns)
app.register_blueprint(coprs_ns.coprs_ns)
app.register_blueprint(misc.misc)
app.register_blueprint(backend_ns.backend_ns)
diff --git a/coprs_frontend/coprs/templates/api.html b/coprs_frontend/coprs/templates/api.html
index fb20f0d..2800fd3 100644
--- a/coprs_frontend/coprs/templates/api.html
+++ b/coprs_frontend/coprs/templates/api.html
@@ -33,6 +33,48 @@
<h3>The API</h3>
+ <h4>List someone's copr</h4>
+
+ <p>To list the coprs owned by someone one argument is required:</p>
+ <table>
+ <tr>
+ <th>name</th> <td>The name of the user you would to list the
+ copr of.</td>
+ </tr>
+ </table>
+ <p>URL will therefore look like:</p>
+ <pre style="font-size:120%">
+ /list/<username>/
+ </pre>
+
+ <h4>Add a new copr</h4>
+
+ <p><span style="font-style:italic">Login required</span></p>
+
+ <p>To add a new copr some informations are required:</p>
+ <table>
+ <tr>
+ <th>name</th> <td>The name of the copr to create.</td>
+ </tr><tr>
+ <th>chroots</th> <td>A comma separated list of chroots to
+ use in the copr.</td>
+ </tr><tr>
+ <th>repos</th> <td>A comma separated list of repositories
+ that this new copr should have access to.</td>
+ </tr><tr>
+ <th>initial_pkgs</th> <td>A comma separated list of initial
+ packages to build in this new copr.</td>
+ </tr>
+ </table>
+
+ <p>URL will therefore look like:</p>
+ <pre style="font-size:120%">
+ /add/<name>/<chroots>/
+ /add/<name>/<chroots>/<repos>/
+ /add/<name>/<chroots>/<repos>/<initial_pkgs>/
+ </pre>
+
+
<p>To come here, more information about the API itself.</p>
</div>
diff --git a/coprs_frontend/coprs/views/api_ns/__init__.py b/coprs_frontend/coprs/views/api_ns/__init__.py
new file mode 100644
index 0000000..ce13abc
--- /dev/null
+++ b/coprs_frontend/coprs/views/api_ns/__init__.py
@@ -0,0 +1,3 @@
+import flask
+
+api_ns = flask.Blueprint('api_ns', __name__, url_prefix = '/api')
diff --git a/coprs_frontend/coprs/views/api_ns/api_general.py b/coprs_frontend/coprs/views/api_ns/api_general.py
new file mode 100644
index 0000000..a6a6c94
--- /dev/null
+++ b/coprs_frontend/coprs/views/api_ns/api_general.py
@@ -0,0 +1,96 @@
+import time
+
+import flask
+
+from coprs import db
+from coprs import exceptions
+
+from coprs.views.misc import login_required
+
+from coprs.views.api_ns import api_ns
+
+from coprs.logic import builds_logic
+from coprs.logic import coprs_logic
+
+
+(a)api_ns.route('/')
+def api_home():
+ """ Renders the home page of the api.
+ This page provides information on how to call/use the API.
+ """
+ return flask.render_template('coprs/api.html')
+
+
+(a)api_ns.route('/new/', methods = ["GET", "POST"])
+@login_required
+def api_new_token():
+ """ Method use to generate a new API token for the current user.
+ """
+ user = flask.g.user
+ user.api_token = generate_api_token(app.config['API_TOKEN_LENGTH'])
+ user.api_token_expiration = datetime.date.today() \
+ + datetime.timedelta(days=30)
+ db.session.add(user)
+ db.session.commit()
+ flask.g.user = user
+ return flask.redirect(flask.url_for('misc.api'))
+
+
+(a)api_ns.route('/add/<name>/<chroots>/',
+ defaults={'repos':"", 'initial_pkgs':""})
+(a)api_ns.route('/add/<name>/<chroots>/<repos>/',
+ defaults={'initial_pkgs':None})
+(a)api_ns.route('/add/<name>/<chroots>/<repos>/<initial_pkgs>/')
+@login_required
+def api_add_copr(name, chroots, repos="", initial_pkgs=""):
+ """ Receive information from the user on how to create its new copr,
+ check their validity and create the corresponding copr.
+
+ :arg name: the name of the copr to add
+ :arg chroots: a comma separated list of chroots to use
+ :kwarg repos: a comma separated list of repository that this copr
+ can use.
+ :kwarg initial_pkgs: a comma separated list of initial packages to
+ build in this new copr
+
+ """
+ infos = []
+ copr = coprs_logic.CoprsLogic.add_coprs(
+ name=name.strip(),
+ repos=" ".join([repo.strip() for repo in repos.split(',')]),
+ owner=flask.g.user,
+ selected_chroots=[chroot.strip()
+ for chroot in chroots.split(',')]
+ )
+ infos.append('New copr was successfully created.')
+
+ if initial_pkgs:
+ builds_logic.BuildsLogic.add_build(
+ pkgs=" ".join([pkg.strip() for pkg in initial_pkgs.split(',')]),
+ copr=copr,
+ owner=flask.g.user)
+ infos.append('Initial packages were successfully submitted '
+ 'for building.')
+ return '{"output" : "%s"}' % ("\n".join(infos))
+
+
+(a)api_ns.route('/list/<username>/', methods=['GET'])
+def api_list_copr(username):
+ """ Return the list of coprs owned by the given user.
+
+ :arg username: the username of the person one would like to the
+ coprs of.
+ """
+ query = coprs_logic.CoprsLogic.get_multiple(flask.g.user,
+ user_relation = 'owned', username = username)
+ output = '{"repos":[\n'
+ repos = query.all()
+ cnt = 0
+ for repo in repos:
+ output += '{"name" : "%s", "repos" : "%s"}\n' % (
+ repo.name, repo.repos)
+ cnt = cnt + 1
+ if cnt < len(repos):
+ output += ','
+ output += "]}\n"
+ return output
diff --git a/coprs_frontend/coprs/views/misc.py b/coprs_frontend/coprs/views/misc.py
index e10b07f..c5cffc1 100644
--- a/coprs_frontend/coprs/views/misc.py
+++ b/coprs_frontend/coprs/views/misc.py
@@ -119,22 +119,3 @@ def backend_authenticated(f):
return 'You have to provide the correct password', 401
return f(*args, **kwargs)
return decorated_function
-
-
-(a)misc.route('/api/')
-def api():
- return flask.render_template('api.html',
- user=flask.g.user)
-
-
-(a)misc.route('/api/new/', methods = ["GET", "POST"])
-@login_required
-def api_new_token():
- user = flask.g.user
- user.api_token = generate_api_token(app.config['API_TOKEN_LENGTH'])
- user.api_token_expiration = datetime.date.today() \
- + datetime.timedelta(days=30)
- db.session.add(user)
- db.session.commit()
- flask.g.user = user
- return flask.redirect(flask.url_for('misc.api'))
11 years, 2 months
[copr] master: Update the .gitignore file (b058329)
by Pierre-YvesChibon
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit b058329aad9de925c3cda49d56b6864cd195166c
Author: Pierre-Yves Chibon <pingou(a)pingoured.fr>
Date: Sun Jan 27 16:46:19 2013 +0100
Update the .gitignore file
>---------------------------------------------------------------
.gitignore | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
index 0c750bb..359cb92 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,7 @@
*.pyc
*.pyo
*.swp
+data/
+dist/
+build/
+*.egg-info
11 years, 2 months
[PATCH] First line of code on the CLI for copr
by Pierre-Yves Chibon
With this version of Copr CLI one can list someone's coprs and create a new one
for himself.
This relies on cliff and requests to perform its task.
---
Ok, so here is the first draft/lines of code for the copr CLI.
Comments welcome :)
cli_requirements.txt | 4 ++
copr_cli/__init__.py | 2 +
copr_cli/main.log | 5 +++
copr_cli/main.py | 69 ++++++++++++++++++++++++++++++
copr_cli/subcommands.py | 109 ++++++++++++++++++++++++++++++++++++++++++++++++
coprcli-setup.py | 69 ++++++++++++++++++++++++++++++
6 files changed, 258 insertions(+)
create mode 100644 cli_requirements.txt
create mode 100644 copr_cli/__init__.py
create mode 100644 copr_cli/main.log
create mode 100644 copr_cli/main.py
create mode 100644 copr_cli/subcommands.py
create mode 100644 coprcli-setup.py
diff --git a/cli_requirements.txt b/cli_requirements.txt
new file mode 100644
index 0000000..7d3bfe6
--- /dev/null
+++ b/cli_requirements.txt
@@ -0,0 +1,4 @@
+# Used for when working from a virtualenv.
+# Use this file by running "$ pip install -r cli_requirements.txt"
+cliff
+requests
diff --git a/copr_cli/__init__.py b/copr_cli/__init__.py
new file mode 100644
index 0000000..17d0be3
--- /dev/null
+++ b/copr_cli/__init__.py
@@ -0,0 +1,2 @@
+#!/usr/bin/python -tt
+#-*- coding: UTF-8 -*-
diff --git a/copr_cli/main.log b/copr_cli/main.log
new file mode 100644
index 0000000..e98f62a
--- /dev/null
+++ b/copr_cli/main.log
@@ -0,0 +1,5 @@
+[2013-01-27 14:15:46,649] DEBUG __main__ initialize_app
+[2013-01-27 14:16:02,242] DEBUG __main__ initialize_app
+[2013-01-27 14:16:33,725] DEBUG __main__ initialize_app
+[2013-01-27 14:16:57,768] DEBUG __main__ initialize_app
+[2013-01-27 14:30:07,034] DEBUG __main__ initialize_app
diff --git a/copr_cli/main.py b/copr_cli/main.py
new file mode 100644
index 0000000..557d144
--- /dev/null
+++ b/copr_cli/main.py
@@ -0,0 +1,69 @@
+#!/usr/bin/python -tt
+#-*- coding: UTF-8 -*-
+
+import logging
+import sys
+
+
+import cliff.app
+import cliff.commandmanager
+from cliff.commandmanager import CommandManager
+
+__version__ = '0.1.0'
+__description__ = "CLI tool to run copr"
+
+copr_url = 'http://localhost:5000'
+copr_api_url = '{0}/api'.format(copr_url)
+
+
+class CoprCli(cliff.app.App):
+
+ log = logging.getLogger(__name__)
+
+ def __init__(self):
+ manager = cliff.commandmanager.CommandManager('copr_cli.subcommands')
+ super(CoprCli, self).__init__(
+ description=__description__,
+ version=__version__,
+ command_manager=manager,
+ )
+ requests_log = logging.getLogger("requests")
+ requests_log.setLevel(logging.WARN)
+
+ def initialize_app(self, argv):
+ self.log.debug('initialize_app')
+
+ def prepare_to_run_command(self, cmd):
+ self.log.debug('prepare_to_run_command %s', cmd.__class__.__name__)
+
+ def clean_up(self, cmd, result, err):
+ self.log.debug('clean_up %s', cmd.__class__.__name__)
+ if err:
+ self.log.debug('got an error: %s', err)
+
+ # Overload run_subcommand to gracefully handle unknown commands.
+ def run_subcommand(self, argv):
+ try:
+ self.command_manager.find_command(argv)
+ except ValueError as e:
+ if "Unknown command" in str(e):
+ print "%r is an unknown command" % ' '.join(argv)
+ print "Try \"copr -h\""
+ sys.exit(1)
+ else:
+ raise
+
+ return super(CoprCli, self).run_subcommand(argv)
+
+
+def main(argv=sys.argv[1:]):
+ """ Main function """
+ myapp = CoprCli()
+ return myapp.run(argv)
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
+ #add_copr('test2', 'fedora-rawhide-x86_64',
+ #description='Test repos #2')
+ #list_copr()
diff --git a/copr_cli/subcommands.py b/copr_cli/subcommands.py
new file mode 100644
index 0000000..1231208
--- /dev/null
+++ b/copr_cli/subcommands.py
@@ -0,0 +1,109 @@
+#-*- coding: UTF-8 -*-
+
+import ConfigParser
+import json
+import logging
+import requests
+import os
+import sys
+
+import cliff.lister
+import cliff.show
+
+from cliff.command import Command
+
+from main import copr_api_url
+
+
+def set_user():
+ """ Retrieve the user information from the config file. """
+ config = ConfigParser.ConfigParser()
+ config.read(os.path.join(os.path.expanduser('~'), '.config',
+ 'copr'))
+ username = config.get('copr-cli', 'username', None)
+ token = config.get('copr-cli', 'token', None)
+ return {'username': username, 'token': token}
+
+
+class List(cliff.lister.Lister):
+ """ List all the copr of a user. """
+
+ log = logging.getLogger(__name__)
+
+ def get_parser(self, prog_name):
+ parser = super(type(self), self).get_parser(prog_name)
+ parser.add_argument("username", nargs='?')
+ return parser
+
+ def take_action(self, args):
+ user = set_user()
+
+ if args.username:
+ user['username'] = args.username
+ URL = '{0}/owned/'.format(copr_api_url)
+ req = requests.get(URL, params=user)
+ output = json.loads(req.text)
+ if 'repos' in output:
+ if output['repos']:
+ columns = ['name', 'description', 'repos', 'instructions']
+ values = []
+ for entry in output['repos']:
+ values.append([entry[key] for key in columns])
+ return (columns, values)
+ else:
+ columns = ['output']
+ values = ['No copr retrieved for user: "{0}"'.format(
+ user['username'])]
+ return (columns, [values])
+ else:
+ columns = ['output']
+ values = ['Wrong output format returned by the server']
+ return (columns, [values])
+
+
+class AddCopr(Command):
+ """ Create a new copr. """
+
+ log = logging.getLogger(__name__)
+
+ def get_parser(self, prog_name):
+ parser = super(type(self), self).get_parser(prog_name)
+ parser.add_argument("name")
+ parser.add_argument("--chroot", dest="chroots", action='append',
+ help="")
+ parser.add_argument('--repo', dest='repos', action='append',
+ help="")
+ parser.add_argument('--initial-pkgs', dest='initial_pkgs',
+ action='append',
+ help="")
+ parser.add_argument('--description',
+ help="")
+ parser.add_argument('--instructions',
+ help="")
+ return parser
+
+ def take_action(self, args):
+ user = set_user()
+ URL = '{0}/copr/new/'.format(copr_api_url)
+
+ repos = None
+ if args.repos:
+ repos = ",".join(args.repos)
+ initial_pkgs = None
+ if args.initial_pkgs:
+ initial_pkgs = ",".join(args.initial_pkgs)
+ data = {'name': args.name,
+ 'repos': repos,
+ 'initial_pkgs': initial_pkgs,
+ 'description': args.description,
+ 'instructions': args.instructions
+ }
+ for chroot in args.chroots:
+ data[chroot] = 'y'
+
+ req = requests.post(URL, params=user, data=data)
+ output = json.loads(req.text)
+ if output['output'] == 'ok':
+ print output['message']
+ else:
+ print 'Something went wrong:\n {0}'.format(output['error'])
diff --git a/coprcli-setup.py b/coprcli-setup.py
new file mode 100644
index 0000000..a7a40d3
--- /dev/null
+++ b/coprcli-setup.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+
+try:
+ from setuptools import setup
+except ImportError:
+ from ez_setup import use_setuptools
+ use_setuptools()
+ from setuptools import setup
+
+import sys
+
+f = open('README')
+long_description = f.read().strip()
+f.close()
+
+# Ridiculous as it may seem, we need to import multiprocessing and
+# logging here in order to get tests to pass smoothly on python 2.7.
+try:
+ import multiprocessing
+ import logging
+except ImportError:
+ pass
+
+from copr_cli.main import __description__, __version__
+
+requires = [
+ 'cliff',
+]
+
+subcommands = [
+ 'list = copr_cli.subcommands:List',
+ 'add-copr = copr_cli.subcommands:AddCopr',
+]
+
+__name__ = 'copr-cli'
+__version__ = __version__
+__description__ = __description__
+__author__ = "Pierre-Yves Chibon"
+__author_email__ = "pingou(a)pingoured.fr"
+__url__ = "http://fedorahosted.org/copr/"
+
+setup(
+ name=__name__,
+ version=__version__,
+ description=__description__,
+ long_description=long_description,
+ author=__author__,
+ author_email=__author_email__,
+ url=__url__,
+ license='GPLv2+',
+ classifiers=[
+ "License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)",
+ "Programming Language :: Python :: 2",
+ "Programming Language :: Python :: 2.7",
+ "Topic :: System :: Archiving :: Packaging",
+ "Development Status :: 1 - Alpha",
+ ],
+ install_requires=requires,
+ packages=['copr_cli'],
+ namespace_packages=['copr_cli'],
+ include_package_data=True,
+ zip_safe=False,
+ entry_points={
+ 'console_scripts': [
+ 'copr-cli = copr_cli.main:main'
+ ],
+ 'copr_cli.subcommands': subcommands,
+ },
+)
--
1.8.1
11 years, 2 months
[PATCH 1/5] Create the API namespace
by Pierre-Yves Chibon
This is the namespace that we will use to describe and expose the copr API.
This commit introduce it, move some code from misc to the api_general and
start with two API functions to list someone's copr and add a new one.
These two functions are documented in the /api/ (ie in the api.html template).
---
coprs_frontend/coprs/__init__.py | 3 +
coprs_frontend/coprs/templates/api.html | 42 +++++++++++
coprs_frontend/coprs/views/api_ns/__init__.py | 3 +
coprs_frontend/coprs/views/api_ns/api_general.py | 96 ++++++++++++++++++++++++
coprs_frontend/coprs/views/misc.py | 19 -----
5 files changed, 144 insertions(+), 19 deletions(-)
create mode 100644 coprs_frontend/coprs/views/api_ns/__init__.py
create mode 100644 coprs_frontend/coprs/views/api_ns/api_general.py
diff --git a/coprs_frontend/coprs/__init__.py b/coprs_frontend/coprs/__init__.py
index d5d732a..f239b65 100644
--- a/coprs_frontend/coprs/__init__.py
+++ b/coprs_frontend/coprs/__init__.py
@@ -22,6 +22,8 @@ import coprs.filters
import coprs.log
import coprs.models
+from coprs.views import api_ns
+from coprs.views.api_ns import api_general
from coprs.views import coprs_ns
from coprs.views.coprs_ns import coprs_builds
from coprs.views.coprs_ns import coprs_general
@@ -29,6 +31,7 @@ from coprs.views import backend_ns
from coprs.views.backend_ns import backend_general
from coprs.views import misc
+app.register_blueprint(api_ns.api_ns)
app.register_blueprint(coprs_ns.coprs_ns)
app.register_blueprint(misc.misc)
app.register_blueprint(backend_ns.backend_ns)
diff --git a/coprs_frontend/coprs/templates/api.html b/coprs_frontend/coprs/templates/api.html
index fb20f0d..2800fd3 100644
--- a/coprs_frontend/coprs/templates/api.html
+++ b/coprs_frontend/coprs/templates/api.html
@@ -33,6 +33,48 @@
<h3>The API</h3>
+ <h4>List someone's copr</h4>
+
+ <p>To list the coprs owned by someone one argument is required:</p>
+ <table>
+ <tr>
+ <th>name</th> <td>The name of the user you would to list the
+ copr of.</td>
+ </tr>
+ </table>
+ <p>URL will therefore look like:</p>
+ <pre style="font-size:120%">
+ /list/<username>/
+ </pre>
+
+ <h4>Add a new copr</h4>
+
+ <p><span style="font-style:italic">Login required</span></p>
+
+ <p>To add a new copr some informations are required:</p>
+ <table>
+ <tr>
+ <th>name</th> <td>The name of the copr to create.</td>
+ </tr><tr>
+ <th>chroots</th> <td>A comma separated list of chroots to
+ use in the copr.</td>
+ </tr><tr>
+ <th>repos</th> <td>A comma separated list of repositories
+ that this new copr should have access to.</td>
+ </tr><tr>
+ <th>initial_pkgs</th> <td>A comma separated list of initial
+ packages to build in this new copr.</td>
+ </tr>
+ </table>
+
+ <p>URL will therefore look like:</p>
+ <pre style="font-size:120%">
+ /add/<name>/<chroots>/
+ /add/<name>/<chroots>/<repos>/
+ /add/<name>/<chroots>/<repos>/<initial_pkgs>/
+ </pre>
+
+
<p>To come here, more information about the API itself.</p>
</div>
diff --git a/coprs_frontend/coprs/views/api_ns/__init__.py b/coprs_frontend/coprs/views/api_ns/__init__.py
new file mode 100644
index 0000000..ce13abc
--- /dev/null
+++ b/coprs_frontend/coprs/views/api_ns/__init__.py
@@ -0,0 +1,3 @@
+import flask
+
+api_ns = flask.Blueprint('api_ns', __name__, url_prefix = '/api')
diff --git a/coprs_frontend/coprs/views/api_ns/api_general.py b/coprs_frontend/coprs/views/api_ns/api_general.py
new file mode 100644
index 0000000..adc851d
--- /dev/null
+++ b/coprs_frontend/coprs/views/api_ns/api_general.py
@@ -0,0 +1,96 @@
+import time
+
+import flask
+
+from coprs import db
+from coprs import exceptions
+
+from coprs.views.misc import login_required
+
+from coprs.views.api_ns import api_ns
+
+from coprs.logic import builds_logic
+from coprs.logic import coprs_logic
+
+
+(a)api_ns.route('/')
+def api_home():
+ """ Renders the home page of the api.
+ This page provides information on how to call/use the API.
+ """
+ return flask.render_template('coprs/api.html')
+
+
+(a)api_ns.route('/new/', methods = ["GET", "POST"])
+@login_required
+def api_new_token():
+ """ Method use to generate a new API token for the current user.
+ """
+ user = flask.g.user
+ user.api_token = generate_api_token(app.config['API_TOKEN_LENGTH'])
+ user.api_token_expiration = datetime.date.today() \
+ + datetime.timedelta(days=30)
+ db.session.add(user)
+ db.session.commit()
+ flask.g.user = user
+ return flask.redirect(flask.url_for('misc.api'))
+
+
+(a)api_ns.route('/add/<name>/<chroots>/',
+ defaults={'repos':"", 'initial_pkgs':""})
+(a)api_ns.route('/add/<name>/<chroots>/<repos>/',
+ defaults={'initial_pkgs':None})
+(a)api_ns.route('/add/<name>/<chroots>/<repos>/<initial_pkgs>/')
+@login_required
+def api_add_copr(name, chroots, repos="", initial_pkgs=""):
+ """ Receive information from the user on how to create its new copr,
+ check their validity and create the corresponding copr.
+
+ :arg name: the name of the copr to add
+ :arg chroots: a comma separated list of chroots to use
+ :kwarg repos: a comma separated list of repository that this copr
+ can use.
+ :kwarg initial_pkgs: a comma separated list of initial packages to
+ build in this new copr
+
+ """
+ infos = []
+ copr = coprs_logic.CoprsLogic.add_coprs(
+ name=name.strip(),
+ repos=" ".join([repo.strip() for repo in repos.split(',')]),
+ owner=flask.g.user,
+ selected_chroots=[chroot.strip()
+ for chroot in chroots.split(',')]
+ )
+ infos.append('New copr was successfully created.')
+
+ if initial_pkgs:
+ builds_logic.BuildsLogic.add_build(
+ pkgs=" ".join([pkg.strip() for pkg in initial_pkgs.split(',')]),
+ copr=copr,
+ owner=flask.g.user)
+ infos.append('Initial packages were successfully submitted '
+ 'for building.')
+ return '{"output" : "%s"}' % ("\n".join(infos))
+
+
+(a)api_ns.route('/list/<username>/', methods=['GET'])
+def api_list_copr(username):
+ """ Return the list of coprs owned by the given user.
+
+ :arg username: the username of the person one would like to the
+ coprs of.
+ """
+ query = coprs_logic.CoprsLogic.get_multiple(flask.g.user,
+ user_relation = 'owned', username = username)
+ output = '{"repos":[\n'
+ repos = query.all()
+ cnt = 0
+ for repo in repos:
+ output += '{"name" : "%s", "repos" : "%s"}\n' % (
+ repo.name, repo.repos)
+ cnt = cnt + 1
+ if cnt < len(repos):
+ output += ','
+ output += "]}\n"
+ return output
diff --git a/coprs_frontend/coprs/views/misc.py b/coprs_frontend/coprs/views/misc.py
index e10b07f..c5cffc1 100644
--- a/coprs_frontend/coprs/views/misc.py
+++ b/coprs_frontend/coprs/views/misc.py
@@ -119,22 +119,3 @@ def backend_authenticated(f):
return 'You have to provide the correct password', 401
return f(*args, **kwargs)
return decorated_function
-
-
-(a)misc.route('/api/')
-def api():
- return flask.render_template('api.html',
- user=flask.g.user)
-
-
-(a)misc.route('/api/new/', methods = ["GET", "POST"])
-@login_required
-def api_new_token():
- user = flask.g.user
- user.api_token = generate_api_token(app.config['API_TOKEN_LENGTH'])
- user.api_token_expiration = datetime.date.today() \
- + datetime.timedelta(days=30)
- db.session.add(user)
- db.session.commit()
- flask.g.user = user
- return flask.redirect(flask.url_for('misc.api'))
--
1.8.1
11 years, 2 months
Copr add use
by Pierre-Yves Chibon
Hi,
I am making progress on the API part of copr, I have the /api/owned part working
fine and I am trying to get the /api/copr/new working.
I was wondering if we shouldn't use a SelectMultipleField for the chroots
instead of the current list of checkbox.
Any thoughts on this?
Pierre
11 years, 2 months
[copr] master: Use proper template variable to check build count otherwise a new query is triggered (4393bd7)
by bkabrda@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit 4393bd70cfdb61a727a4cfbd71dc45b915c4cbfd
Author: Bohuslav Kabrda <bkabrda(a)redhat.com>
Date: Fri Jan 25 13:44:49 2013 +0100
Use proper template variable to check build count otherwise a new query is triggered
>---------------------------------------------------------------
.../coprs/templates/coprs/detail/builds.html | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/coprs_frontend/coprs/templates/coprs/detail/builds.html b/coprs_frontend/coprs/templates/coprs/detail/builds.html
index 8e193af..2746554 100644
--- a/coprs_frontend/coprs/templates/coprs/detail/builds.html
+++ b/coprs_frontend/coprs/templates/coprs/detail/builds.html
@@ -5,7 +5,7 @@
{% from "coprs/detail/_builds_table.html" import builds_table with context %}
{% block detail_body %}
- {% if copr.builds %}
+ {% if builds %}
{{ builds_table(builds) }}
<div class="pagination">
{{ render_pagination(request, paginator) }}
11 years, 2 months