Skip to content

Commit

Permalink
Test for API done
Browse files Browse the repository at this point in the history
Signed-off-by: David Saradini <david.saradini@me.com>
  • Loading branch information
dsaradini committed Jun 24, 2013
1 parent bb67260 commit a84cafa
Show file tree
Hide file tree
Showing 25 changed files with 476 additions and 15 deletions.
Empty file added facile_backlog/api/__init__.py
Empty file.
46 changes: 46 additions & 0 deletions facile_backlog/api/serializers.py
@@ -0,0 +1,46 @@
from rest_framework import serializers
from rest_framework.reverse import reverse

from ..backlog.models import Project, Backlog, UserStory


class ProjectSerializer(serializers.ModelSerializer):
url = serializers.SerializerMethodField('_url')

class Meta:
model = Project
fields = ('id', 'url', 'name', 'code', 'description')

def _url(self, obj):
return reverse("api_project_detail", args=[obj.pk],
request=self.context['request'])


class BacklogSerializer(serializers.ModelSerializer):
url = serializers.SerializerMethodField('_url')

class Meta:
model = Backlog
fields = ('id', 'url', 'name', 'description')

def _url(self, obj):
return reverse("api_backlog_detail", args=[obj.project_id, obj.pk],
request=self.context['request'])


class StorySerializer(serializers.ModelSerializer):
url = serializers.SerializerMethodField('_url')
create_date = serializers.DateTimeField(read_only=True)
code = serializers.FloatField(read_only=True, source='code')

class Meta:
model = UserStory
fields = ('id', 'code', 'url', 'as_a', 'i_want_to', 'so_i_can',
'color', 'comments',
'acceptances', 'points', 'create_date', 'theme',
'status')

def _url(self, obj):
return reverse("api_story_detail",
args=[obj.project_id, obj.backlog_id, obj.pk],
request=self.context['request'])
37 changes: 37 additions & 0 deletions facile_backlog/api/urls.py
@@ -0,0 +1,37 @@
from django.conf.urls import url, patterns, include

from .views import (home_view, project_list, project_detail,
backlog_list, backlog_detail, story_list, story_detail)

urlpatterns = patterns(
'',

url(r'^api-auth/', include('rest_framework.urls',
namespace='rest_framework')),

url(r'^api-token-auth/',
'rest_framework.authtoken.views.obtain_auth_token'),

# views
url(r'^projects/$',
project_list, name="api_project_list"),

url(r'^projects/(?P<project_id>[\w]+)/$',
project_detail, name="api_project_detail"),

url(r'^projects/(?P<project_id>[\w]+)/backlogs/$',
backlog_list, name="api_backlog_list"),

url(r'^projects/(?P<project_id>[\w]+)/backlogs/(?P<backlog_id>[\w]+)/$',
backlog_detail, name="api_backlog_detail"),

url(r'^projects/(?P<project_id>[\w]+)/backlogs/'
r'(?P<backlog_id>[\w]+)/stories/$',
story_list, name="api_story_list"),

url(r'^projects/(?P<project_id>[\w]+)/backlogs/(?P<backlog_id>[\w]+)/'
r'stories/(?P<story_id>[\w]+)/$',
story_detail, name="api_story_detail"),

url(r'^$', home_view, name="api_home"),
)
104 changes: 104 additions & 0 deletions facile_backlog/api/views.py
@@ -0,0 +1,104 @@
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.reverse import reverse

from .serializers import ProjectSerializer, BacklogSerializer, StorySerializer

from ..backlog.models import Project, Backlog, UserStory


class HomeView(viewsets.ViewSet):
_ignore_model_permissions = True

def list(self, request):
return Response({
'projects/': reverse('api_project_list', request=request),
'projects/<project_id>/':
reverse('api_project_detail',
request=request,
args=("project_id",)),
'projects/<project_id>/backlogs/':
reverse('api_backlog_list',
request=request,
args=("project_id",)),
'projects/<project_id>/backlogs/<backlog_id>/':
reverse('api_backlog_detail',
request=request,
args=("project_id", "backlog_id")),
})
home_view = HomeView.as_view({'get': 'list'})


class ProjectViewSet(viewsets.ModelViewSet):
pk_url_kwarg = "project_id"
serializer_class = ProjectSerializer
model = Project

def initial(self, request, *args, **kwargs):
self.queryset = Project.my_projects(request.user)
return super(ProjectViewSet, self).initial(request, *args, **kwargs)

project_list = ProjectViewSet.as_view({
'get': 'list',
})

project_detail = ProjectViewSet.as_view({
'get': 'retrieve'
})


class BacklogViewSet(viewsets.ModelViewSet):
pk_url_kwarg = "backlog_id"
serializer_class = BacklogSerializer
model = Backlog

def initial(self, request, *args, **kwargs):
project_id = kwargs.pop("project_id")
projects = Project.my_projects(request.user)
try:
self.project = projects.get(pk=project_id)
self.queryset = self.project.backlogs.all()
except Project.DoesNotExist:
self.project = None
self.queryset = Backlog.objects.none()
return super(BacklogViewSet, self).initial(request, *args, **kwargs)

backlog_list = BacklogViewSet.as_view({
'get': 'list'
})

backlog_detail = BacklogViewSet.as_view({
'get': 'retrieve'
})


class StoryViewSet(viewsets.ModelViewSet):
pk_url_kwarg = "story_id"
serializer_class = StorySerializer
model = UserStory

def initial(self, request, *args, **kwargs):
project_id = kwargs.pop("project_id")
backlog_id = kwargs.pop("backlog_id")
projects = Project.my_projects(request.user)
try:
self.project = projects.get(pk=project_id)
self.backlog = self.project.backlogs.get(pk=backlog_id)
self.queryset = self.backlog.ordered_stories
except Project.DoesNotExist, Backlog.DoesNotExist:
self.project = None
self.backlog = None
self.queryset = UserStory.objects.none()
return super(StoryViewSet, self).initial(request, *args, **kwargs)

story_list = StoryViewSet.as_view({
'get': 'list',
'post': 'create'
})

story_detail = StoryViewSet.as_view({
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy'
})
4 changes: 2 additions & 2 deletions facile_backlog/backlog/admin.py
Expand Up @@ -30,7 +30,8 @@ class EventAdmin(admin.ModelAdmin):

def get_readonly_fields(self, request, obj=None):
if obj:
self.readonly_fields = [field.name for field in obj.__class__._meta.fields]
self.readonly_fields = [
field.name for field in obj.__class__._meta.fields]
return self.readonly_fields
return self.readonly_fields

Expand All @@ -47,4 +48,3 @@ def queryset(self, request):
admin.site.register(UserStory, UserStoryAdmin)
admin.site.register(AuthorizationAssociation, AuthorizationAssociationAdmin)
admin.site.register(Event, EventAdmin)

2 changes: 1 addition & 1 deletion facile_backlog/backlog/forms.py
@@ -1,7 +1,7 @@
from django.forms.fields import CharField
from django.forms.models import ModelForm
from django.forms import Form
from django.forms import EmailField, BooleanField, ChoiceField, CharField
from django.forms import EmailField, BooleanField, ChoiceField
from django.utils.translation import ugettext as _

from .models import Project, Backlog, UserStory
Expand Down
4 changes: 3 additions & 1 deletion facile_backlog/backlog/models.py
Expand Up @@ -99,6 +99,8 @@ def __unicode__(self):
@classmethod
def my_projects(cls, user):
""" Return all project user accepted the invitation """
if not user.is_authenticated():
return Project.objects.none()
return user.projects.filter(
authorizationassociation__is_active=True
)
Expand Down Expand Up @@ -381,7 +383,7 @@ def build_event_kwargs(values, **kwargs):
return values


def create_event(user, project, text, backlog=None, story=None ):
def create_event(user, project, text, backlog=None, story=None):
kwargs = {
'text': text
}
Expand Down
2 changes: 1 addition & 1 deletion facile_backlog/backlog/views.py
Expand Up @@ -44,7 +44,7 @@ class ProjectList(generic.ListView):
paginate_by = 10

def dispatch(self, request, *args, **kwargs):
self.query = request.GET.get("q","").replace("+", " ")
self.query = request.GET.get("q", "").replace("+", " ")
return super(ProjectList, self).dispatch(request, *args, **kwargs)

def get_queryset(self):
Expand Down
2 changes: 1 addition & 1 deletion facile_backlog/core/admin.py
Expand Up @@ -25,7 +25,7 @@ class UserAdmin(UserAdmin):
form = UserChangeForm
add_form = UserCreationForm
list_display = ['email', 'full_name', 'is_active', 'is_staff',
'is_superuser']
'is_superuser', 'auth_token']
list_filter = ['is_staff', 'is_superuser', 'is_active']
search_fields = ['email', 'full_name']
ordering = ['email']
Expand Down
16 changes: 16 additions & 0 deletions facile_backlog/core/forms.py
Expand Up @@ -4,6 +4,8 @@
from django.core.validators import validate_email
from django.utils.translation import ugettext_lazy as _

from rest_framework.authtoken.models import Token

from password_reset.forms import (PasswordRecoveryForm as BaseRecovery,
PasswordResetForm as BaseReset)

Expand Down Expand Up @@ -97,3 +99,17 @@ class PasswordResetForm(BaseReset):
def save(self):
self.user.set_password(self.cleaned_data['password1'])
self.user.save(update_fields=['password'])


class ChangeApiKeyForm(forms.Form):

def __init__(self, user, *args, **kwargs):
super(ChangeApiKeyForm, self).__init__(*args, **kwargs)
self.user = user

def change_or_create(self):
token, created = Token.objects.get_or_create(user=self.user)
if token:
token.delete()
Token.objects.get_or_create(user=self.user)
return created
2 changes: 1 addition & 1 deletion facile_backlog/core/static/core/css/main.css

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions facile_backlog/core/templates/profile/api_key_form.html
@@ -0,0 +1,21 @@
{% extends "base.html" %}

{% block title %}{% trans "Change API key" %}{% endblock %}

{% block content %}
<h1>{% trans "Change API key" %}</h1>
<p class="description">
{% if has_token %}
{% blocktrans %}You're about to request a new API key, your previous key will be revoked. Are you sure your want to do that? {% endblocktrans %}
{% else %}
{% blocktrans %}You're about to create an API key for your account.{% endblocktrans %}
{% endif %}
</p>
<form method="post" action="{% url "change_api_key" %}">
{% include "form.html" %}
<div class="submit">
<input class="button" type="submit" value="{% if has_token %}{% trans "Change key" %}{% else %}{% trans "Create key" %}{% endif %}">
<a class="alt-button" href="{% url "auth_profile" %}">{% trans "cancel" %}</a>
</div>
</form>
{% endblock %}
25 changes: 22 additions & 3 deletions facile_backlog/core/templates/profile/user_form.html
Expand Up @@ -6,13 +6,32 @@
<h1>{% trans "Update your profile" %}</h1>

<form method="post" action="{% url "auth_profile" %}">


<div class="field">
<label>{% trans "Your API key:" %}</label>
{% if request.user.auth_token %}
<span class="read-only-field"><pre>{{ request.user.auth_token.key }}</pre>
<a class="alt-button" href="{% url "change_api_key" %}">{% trans "Change API key" %}</a>
</span>
{% else %}
<span class="read-only-field">
{% trans "You don't have any API key yet" %}
<a class="alt-button" href="{% url "change_api_key" %}">{% trans "Create one" %}</a>
</span>
{% endif %}
</div>

<div class="field">
<label> </label>
<span>{{ user.email }}</span>
<label>{% trans "Login email" %}</label>
<span class="read-only-field">{{ user.email }}</span>
</div>
{% include "form.html" %}
<div class="submit">
<input class="button" type="submit" value="{% trans "Save changes" %}">
<input class="button" type="submit" value="{% trans "Update profile" %}">
</div>
</form>

<h1>{% trans "API" %}</h1>
<a href="{% url "api_home" %}">{% trans "API Help" %}</a>
{% endblock %}
3 changes: 3 additions & 0 deletions facile_backlog/core/urls.py
Expand Up @@ -34,4 +34,7 @@

url(r'^reset/(?P<token>[\w:-]+)/$', views.reset,
name='password_reset_reset'),

url(r'^change_api_key/$', views.change_api_key,
name='change_api_key'),
)
29 changes: 28 additions & 1 deletion facile_backlog/core/views.py
Expand Up @@ -18,7 +18,7 @@
from ratelimitbackend.views import login as do_login

from .forms import (ProfileEditionForm, RegistrationForm, PasswordRecoveryForm,
PasswordResetForm)
PasswordResetForm, ChangeApiKeyForm)


def login(request):
Expand Down Expand Up @@ -168,3 +168,30 @@ def form_valid(self, form):
_('Your password was successfully reset.'))
return redirect(self.get_success_url())
reset = Reset.as_view()


class ChangeAPIKey(generic.FormView):
form_class = ChangeApiKeyForm
template_name = 'profile/api_key_form.html'
success_url = reverse_lazy('auth_profile')

def get_form_kwargs(self):
kwargs = super(ChangeAPIKey, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs

def get_context_data(self, **kwargs):
data = super(ChangeAPIKey, self).get_context_data(**kwargs)
data['has_token'] = hasattr(self.request.user, "auth_token")
return data

def form_valid(self, form):
created = form.change_or_create()
if created:
messages.success(self.request,
_('API key successfully created.'))
else:
messages.success(self.request,
_('API key successfully changed.'))
return redirect(self.get_success_url())
change_api_key = login_required(ChangeAPIKey.as_view())

0 comments on commit a84cafa

Please sign in to comment.