Skip to content
This repository has been archived by the owner on Sep 20, 2022. It is now read-only.

Application Specs

Brian Moeskau edited this page Jul 23, 2014 · 12 revisions

Groops Specs [DRAFT]

"Groops" is a sample application used for comparing different web app frameworks. It is a simple virtual group Q&A application.

Technical Goals

Each implementation should demonstrate framework best practices (when applicable) or generally accepted patterns for all of the following:

  • Dependency / package management
  • Application structure (MVC, MVVM, etc.)
  • Request routing
  • Server UI templates (loosely coupled)
  • Data model / ORM / persistence layer
  • REST-like API support
  • Validation
  • Unit tests

Specs

The app will be implemented in multiple technologies, and the details are left up to each developer. However, as a minimum baseline, the following specifications should be fully implemented.

UI "Views" (Use cases)

Views may be implemented as server templates or client templates. These are really UI use cases that may be combined or composed in various ways, so long as the basic view behavior is implemented somehow.

Views should function on both web and mobile browsers.

  • View user profile
  • List user's created and/or joined rooms
  • Create a room
  • Join a room
  • View a room
  • Compose a message

API

Should be implemented as a REST-like interface separate from the UI and persistence layers. It should be executable from any client that supports REST, e.g. cURL. For this sample app, the API will not be secured, although implementing some form of API security in production apps is always highly recommended.

Users

Users can only create, update or delete their own profiles. A user can view any profile.

POST   /api/user
GET    /api/user/:id
PUT    /api/user/:id
DELETE /api/user/:id

Rooms

Any user can create a room, and any user can join any available room. Each room name must be unique (case-insensitive).

Rooms are intended to be ephemeral. A room only exists while one or more users are actively joined. After the last user has left the room and no one has joined for 5 minutes the room may be removed by the system and all associated messages deleted.

GET    /api/rooms
POST   /api/room
GET    /api/room/:id
PUT    /api/room/:id
DELETE /api/room/:id

Messages

Messages can only be created within a room, and are visible to all members in the room. Messages may not be edited or deleted, and are stored only for the lifetime of the room itself.

POST   /api/room/:id/message
GET    /api/room/:id/messages (Get all room messages)
GET    /api/message/:id (Get specific message)
DELETE /api/message/:id

Data

This is a guideline more than a strict schema, and does not imply that these must be implemented as "models" per se. It is merely an outline of the attributes required to be persisted, organized as entities. It is left to the developer to decide how best to structure and normalize these attributes based on the selected database. The relationships between "entities" are loosely implied below, but may be modeled as needed.

User

  • ID (unique)
  • Name (String)
  • Email (String)
  • TwitterName (String)
  • Rooms (0 or more Room entities)

Room

  • ID (unique)
  • Name (String)
  • Users (0 or more User entities)
  • Messages (0 or more Message entities)

Message

  • Content (String)
  • Created (Date)
  • Author (Object)
    • id (String)
    • img (String)

Websocket API (Event Notifications)

The websocket API is provided by socket.io 1.0. As such, the UI uses the socket.io client library.

The UI will automatically connect to the server, but the developer must explicitly join a room. This is accomplished by emitting a join event with the room and user ID (see example). Once the client has joined a room, it can listen for events. The following events should be handled in the UI:

joined

This event is fired when the client has successfully joined a room. It is only fired once, and there is no payload. It is only used to indicate that the system is now ready to participate in room activities.

message

The message event sends a data payload with an action and the message.

{
  "action": "create|delete",
  "message": {
    "id": "message ID",
    "content": "string",
    "created": Date
  }
}

If the action is delete, the message attribute will be a string containing the message ID.

room

The room event is only fired when a room is closed. The data payload is a simple string containing the ID of the room that closed.

user

The user event is fired when a user joins or leaves a room. The data payload is a JSON object that looks like:

{
  "action": "join|leave",
  "user": {
    "id": "userid",
    "name": "Real Name",
    "email": "user@domain.com",
    "twitter": "handle",
    "profilepic": "http://domain.com/image.png"
  }
}

The profilepic attribute is a gravatar image.

exception

The exception event is an error event. It is called "exception" in order to not conflict with any error handling. The payload is a string containing the error message.

Example Implementation

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io();

  // Join a room
  socket.emit('join',{
    room: 'roomid',
    user: 'userid'
  });

  // Once the user has joined a room, setup a proper communication channel
  socket.on('joined',function(){

    // When a message changes state, update the UI accordingly
    socket.on('message',function(data){
      if (data.action === 'create'){
        console.log('Message created',data);
      } else {
        console.log('Message removed',data);
      }
    });

    /**
     * Right now this will only fire when a room closes. "data" 
     * will just be a string with the ID of the room.
     */
    socket.on('room',function(data){
      console.log('Room closed',data);
    });
        
    // When a user's status changes, update the UI accordingly
    socket.on('user', function(data){
      if (data.action === 'join'){
        console.log('A new user joined the room',data);
      } else {
        console.log('A user left the room',data);
      }
    });

  });

  // If there's an error, let the user know
  socket.on('exception',function(msg){
    alert(msg);
  });

</script>

Testing

[TBD - requirements for validating API implementations]