<repo_name>VickyMutai/chat-app<file_sep>/README.md
# Chat App api

### Prerequisites

###### Requirements

Python 3.6.3


### Installing
Create virtual
- python3.6 -m venv virtual
Activate virtual
- source/bin/activate

#### Install all the requirements
- pip install -r requirements.txt

#### Databases
for postgresql users;
  * Go to settings;
    - Change user,password, name(database name) on the database settings.
for sqlite users;
  * Go to settings;
    - replace the database section with the following;
        - DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
      

### Make migrations
- python manage.py makemigrations
- python manage.py migrate

#### Run application
- python manage.py runserver

#### Testing API endpoints
##### Authentication

For this functionality we used [Djoser](http://djoser.readthedocs.io/en/latest/introduction.html) - REST implementation of Django authentication system. It handles all the auth/ endpoints.
Example

Creating a new user
```
$ curl -X POST http://127.0.0.1:8000/auth/users/create/ --data 'username=mike&password=<PASSWORD>'
{"email": "", "username": "mike", "id": 1}
```

To access the user details
```
$ curl -X GET http://127.0.0.1:8000/auth/me/
{"detail": "Authentication credentials were not provided."}
```
The error is because we didn't provide the token which is required for all app/ endpoints. To generate a token we do

```
$ curl -X POST http://127.0.0.1:8088/auth/token/create/ --data 'username=mike&password=<PASSWORD>'
{"auth_token":"<KEY>"}
```
We can now use the token for all the other endpoints. Running the same request again: 

```
$ curl -X GET http://127.0.0.1:8000/auth/me/ -H 'Authorization: Token <KEY>'
{"email":"","id":4,"username":"mike"}
```
##### Chat
Aside from the auth/ endpoints which handle authentification. We have two main API views
- ChatView. Accepts POST and PATCH requests. For creating chat groups/rooms
- ChatMessageView. Accepts GET and POST requests. For sending and getting messages to/from a chat group.

Using the user we created and token given we can create a chat group and send messages to it.
Creating a chat group:

```
curl -X POST http://127.0.0.1:8000/app/chats/ -H 'Authorization: Token <KEY>'
{"message":"New room created","uri":"bf9116670fae470","status":"SUCCESS"}
```
The uri is a random string generated that uniquely identifies each chat room. To get messages from the chat room

```
curl -X GET http://127.0.0.1:8000/app/chats/bf9116670fae470/messages -H 'Authorization: Token <KEY>'
```
It doesn't return anything since no messages have been sent to that particular chat room. To send a message

```
$ curl -X POST http://127.0.0.1:8000/app/chats/bf9116670fae470/messages/ -H 'Authorization: Token bc968ac5c0410b3e83b81805d804438a5c2425d3' --data message="The force is strong"
{"message":"The force is strong","user":{"id":4,"username":"mike","email":""},"uri":"bf9116670fae470","status":"SUCCESS"}
```
And another one

```
curl -X POST http://127.0.0.1:8000/app/chats/bf9116670fae470/messages/ -H 'Authorization: Token <KEY>' --data message="Phantom Menace was the best"
{"message":"Phantom Menace was the best","user":{"id":4,"username":"mike","email":""},"uri":"bf9116670fae470","status":"SUCCESS"}
```
Using the uri for the chat room we can get all the messages sent there like so

```
curl -X GET http://127.0.0.1:8000/app/chats/bf9116670fae470/messages -H 'Authorization: Token <KEY>'
{"messages":[{"user":{"id":4,"username":"mike","email":""},"message":"The force is strong"},{"user":{"id":4,"username":"mike","email":""},"message":"Phantom Menace was the best"}],"id":10,"uri":"bf9116670fae470"}
```
<file_sep>/app/views.py
from django.shortcuts import render
from django.contrib.auth import get_user_model
from .models import Chat, ChatMember, ChatMessage, deserialize_user
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import permissions

# Create your views here.
class ChatView(APIView):
    """Manage Chat rooms."""
    permission_classes = (permissions.IsAuthenticated,)
    def post(self, request, *args, **kwargs):
        """create a new chat session."""
        user = request.user
        chat = Chat.objects.create(admin=user)

        context = {
            'status':'SUCCESS',
            'uri':chat.uri,
            'message':'New room created'
        }
        return Response(context)

    def patch(self,request,*args,**kwargs):
        """Add user to chat session."""
        User = get_user_model()
        uri = kwargs['uri']
        username = request.data['username']
        user = User.objects.get(username=username)

        chat=Chat.objects.get(uri=uri)
        admin = chat.admin

        if admin != user:
            chat.members.get_or_create(user=user,chat=chat)

        admin = deserialize_user(admin)
        members = [ deserialize_user(chat.user) for chat in chat.members.all()]
        members.insert(0,admin)

        context = {
            'status':'SUCCESS',
            'members':members,
            'message':'%s joined that chat' %user.username,
            'user':deserialize_user(user)
        }

        return Response(context)

class ChatMessageView(APIView):
    """Create/Get Chat session messages."""
    permission_classes = (permissions.IsAuthenticated,)
    def get(self,request,*args,**kwargs):
        """"Gets messages from a chat session"""
        uri = kwargs['uri']
        chat = Chat.objects.get(uri=uri)
        messages = [chat_message.convert() for chat_message in chat.messages.all()]
        context = {
            'id':chat.id,
            'uri':chat.uri,
            'messages':messages,
        }
        return Response(context)

    def post(self,request,*args,**kwargs):
        """create a new message in a chat session."""
        uri = kwargs['uri']
        message = request.data['message']
        user = request.user
        chat= Chat.objects.get(uri=uri)
        ChatMessage.objects.create(user=user, chat=chat, message=message)

        context={
            'status': 'SUCCESS',
            'uri': chat.uri, 
            'message': message,
            'user': deserialize_user(user)
        }
        return Response (context)
<file_sep>/app/urls.py
from django.conf import settings
from django.conf.urls import url
from django.conf.urls.static import static
from . import views

urlpatterns=[
    url(r'chats/$', views.ChatView.as_view()),
    url(r'chats/(?P<uri>\w+)/$', views.ChatView.as_view()),
    url(r'chats/(?P<uri>\w+)/messages/$', views.ChatMessageView.as_view()),
]<file_sep>/app/migrations/0002_auto_20180430_1530.py
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-30 12:30
from __future__ import unicode_literals

import app.models
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
        ('app', '0001_initial'),
    ]

    operations = [
        migrations.CreateModel(
            name='Chat',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('create', models.DateTimeField(auto_now_add=True)),
                ('update', models.DateTimeField(auto_now=True)),
                ('uri', models.URLField(default=app.models._generate_unique_uri)),
                ('admin', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
            ],
            options={
                'abstract': False,
            },
        ),
        migrations.RenameModel(
            old_name='ChatSessionMember',
            new_name='ChatMember',
        ),
        migrations.RenameModel(
            old_name='ChatSessionMessage',
            new_name='ChatMessage',
        ),
        migrations.RemoveField(
            model_name='chatsession',
            name='user',
        ),
        migrations.RemoveField(
            model_name='chatmember',
            name='chat_session',
        ),
        migrations.RemoveField(
            model_name='chatmessage',
            name='chat_session',
        ),
        migrations.DeleteModel(
            name='ChatSession',
        ),
        migrations.AddField(
            model_name='chatmember',
            name='chat',
            field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, related_name='members', to='app.Chat'),
            preserve_default=False,
        ),
        migrations.AddField(
            model_name='chatmessage',
            name='chat',
            field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, related_name='messages', to='app.Chat'),
            preserve_default=False,
        ),
    ]
