snapframework / cufp2011

Code for the CUFP 2011 Snap Framework Tutorial
BSD 3-Clause "New" or "Revised" License
21 stars 1 forks source link

CUFP 2011 Tutorial: Web programming in Haskell with the Snap Framework

This repository contains code for the CUFP 2011 tutorial The Snap Framework for web applications in Haskell. You will be building a simplified web application implementing multi-user chat.

Getting started

From the project root directory, running cabal install should build the snap-chat and snap-chat-sample executables. You can find these executables in the dist/build/ subdirectory. To run the sample application (to see what the result should look like), run dist/build/snap-chat-sample/snap-chat-sample and point your browser to http://localhost:8000/.

The sample implementation of the code you're expected to provide can be found in the sample-implementation/ directory; however please don't peek unless you get really stuck. We'll be working through the code together.

Test code (so you can test your implementation against the expected result) can be found in the test/ directory; to run it:

cd test
cabal install
./runTestsAndCoverage.sh

Specifications

Snap Chat is split into three functional components:

For brevity and to bound the amount of work students are expected to do in the short tutorial, most of the code is already provided, including all of the JavaScript (this is a Haskell tutorial!), the data model code, and all of the datatypes. More advanced students who quickly breeze through the small amount of work provided can check out the "extra credit" section at the end of this document.

API documentation

Each of the four API calls (/api/join, /api/leave, /api/fetch, /api/write) share a similar structure: they all respond only to POST requests containing a UTF8-encoded JSON document as the request body; i.e. the Content-Type of the input request is application/json, and they all produce a JSON document as the result. The output responses have the following common structure: either they succeed, producing a document like the following:

{
  "status": "ok",
  "session": "DF1642....038A=",
  "response": { ...some json object... }
}

When they fail, the output document looks like this:

{
  "status": "failure",
  "statusCode": "some_failure",
  "reason": "blah blah blah blah."
}

The "session" variable above deserves some special mention: upon successfully joining the chat room, the user will receive an encrypted session token, which will be used on subsequent requests to re-authenticate the user with the chat room. Upon each response, a fresh session token will be generated. The contents of the session token are opaque to the API user, but can be decrypted on the server-side.

The data type for the encoded session looks like this (see src/Snap/Chat/Internal/API/Types.hs):

data EncodedSession = EncodedSession {
      _sessionToken :: UserToken
    , _sessionTime  :: EpochTime
    , _apiUser      :: UserName
    }

A session will only be considered valid if:

/api/join

The "join" command is responsible for connecting a user to the chat room with a given user name.

Example request:

{ "desiredUserName": "george" }

Example successful response:

{
  "status": "ok",
  "session": "abc.....def",
  "response": {}
}

Example unsuccessful response:

{
  "status": "failure",
  "statusCode": "user_already_exists",
  "reason": "Cannot log in; a user with that name is already connected \
             to the channel."
}

/api/leave

The "leave" command logs the user out of the chat room.

Example request:

{
  "session": "abc.....def",
  "requestData": {}
}

Example successful response:

{
  "status": "ok",
  "session": "abc.....def",
  "response": {}
}

/api/fetch

The "fetch" command gets new messages from the chat room, blocking for up to 50 seconds before it returns with a list of new messages, possibly empty.

Example request:

{
  "session": "abc.....def",
  "requestData": {}
}

Example successful response:

{
  "status": "ok",
  "session": "abc.....def",
  "response": { "messages": [ ...messages... ] }
}

The JSON type of messages is as follows:

{ "contents": {
    "type": <<one of "talk", "action", "join", "leave">>,
    "text": "message text"
    },
  "user": "fred",
  "time": <<posix timestamp>>
}

/api/write

The "write" command writes a message to the chat room.

Example request:

{
  "session": "abc.....def",
  "requestData": {
      "type": <<one of "talk", "action">>,
      "text": "message text"
    }
}

Example successful response:

{
  "status": "ok",
  "session": "abc.....def",
  "response": {}
}

What students must implement

Several functions and instances in the source tree have been marked as "toBeImplemented". Tutorial attendees must implement:

You'll need to use the functions from src/Snap/Chat/ChatRoom.hs, which contains all of the "business logic" for the chat rooms.

Extra credit

If you finish early and get bored, here are some ideas for "extra-credit" assignments: