Skip to content

Core implementation

Aravind edited this page Jun 10, 2013 · 2 revisions

This needs cleanup and updating.

Scrollback Design Document

Release 5, target 30 June 2013

Contents

  • Code structure
  • URL path scheme
  • Core module API
  • JS model schema
  • Neo4j schema
  • Gateway module API
  • REST API

Note: Module APIs are what is exposed through Node require();

Code structure

  • scrollback/
    • scrollback.js - requires and initializes everything (parts of app.js)
    • core/
      • queries/ - cypher queries
        • room.get.cypher
        • room.create.cypher
        • (other cypher files)
      • core.js - formerly warehouse.js; brings other core APIs together
      • room/
        • room.js - new file, combines and simplified topic and discussion
      • user/
        • user.js - parts of existing user.js
      • message/
        • message.js - new file, parts of discussion.js
      • account/
        • account.js - new file, parts of user.js
    • gateways/
      • http/
        • http.js - new file which initializes express (parts of app.js)
        • session.js - session related stuff from sdk.js and api.js
        • auth.js - authentication related stuff.
        • api.js - the rest of api.js
        • wdg.js - formerly wdg.js
        • socket.js - formerly gateway/web.js
        • img/ - image files go here
        • sdk/ - currently js/sdk
          • lib/
          • sdk.js
          • sdk.min.js - autogenerated file
        • js/ - currently js/
        • jade/
          • layout.jade - currently known as widget.jade
          • room.jade - enhanced version of discuss.jade
          • (other jade files)
        • css/
        • res/ - other static resources go here
      • email/
        • email.js - new file
      • xmpp/
        • xmpp.js - formerly xmpp.js
      • irc/
        • irc.js - new file
      • apn/
        • apn.js - apple push notification service
      • gcm/
        • gcm.js - google cloud messaging for android
      • wns/
        • wns.js - windows notification service
    • apps/
      • wordpress/
      • joomla/
      • drupal/
      • magento/
      • phonegap/
      • chrome/
      • firefox/
    • lib/
    • log/
    • tools/ - currently init
    • tests/

URL path scheme

  • /a/ - APIs
  • /w/ - Widgets
  • /j/ - Client-side JS files
  • /i/ - Images
  • /s/ - Static resources
  • / - rotates beteen landing pages in /s/landing
  • /me - user's "inbox" (using Room Widget)
  • /me/edit - edit own profiles, add/remove accounts
  • /:id - Room widget or User profile widget; If it doesn't exist, create a room with no owner.
  • /:id/edit - edit rooms (owned by current user) If the room has no owner, allow editing and make the current user owner on save.

Core Module API

In any file, you get a "core" object during initialization, which contains "builder" functions for room, user, message and account. Use these functions to access model objects.

For example, to load a single room with id "sandbox", call:

core.room('sandbox').then(function(sandbox) {
	...
});

while to search for

and to create a new room

core.room({id: 'sandbox', name: 'Sandbox', owner: 'aravind', ...}).
then(function(sandbox) {
	...
});

Where an object has references to other objects, for example room.owner above, the string ID of an existing user can be provided to the function. The returned sandbox object will have the actual user object loaded from the database.

Resource objects also have some common properties and methods:

  • saved - Boolean property indicating whether the object exists in the DB.
  • save() - method that saves the current state of the object to the DB.
  • delete()

This would be used like:

sandbox.name = 'Alternate Sandbox';
sandbox.save();

Similarly, for every resource (room, user, message, account) there are common cypher files (parameters supplied to the query at execution time are given in parantheses):

queries/<res>.get.cypher		(id: String)
queries/<res>.list.cypher		(<params>)
queries/<res>.create.cypher		(<writeable properties>)
queries/<res>.udpate.cypher		(<writeable properties>)
queries/<res>.delete.cypher		(id: String)

As these are common, they are not listed in the schemas below.

JS Model Object Schema

The .get.cypher query returns the properties of the object listed here. Some of them (e.g. room#owner) is not a property in Neo4j but a separate node that is linked through a relationship. The get.cypher will traverse and get those as well.

.list.cypher takes a 'params' object like:

{ "where": { "key1": "value", "key2": ["values", "values"], "key3": {"lt": 45} },
  "order": {"key1": "asc", "key2": "desc"},
  "limit": [0, 100] }

Room

Read/write properties

id					String	//  Room ID's cannot be less than 4 characters long.
name				String
type				String	// topic, discussion, user
picture				String
description			String
owner				User	// loaded up from a Neo4j relationship.
web.allowedUrls		String	// regex that matches allowed referrer URLs

Read-only properties

present				Boolean // am i present? (do i have at least 1 present listener)?
listenerCount		Integer // rooms listening to me
announcerCount		Integer // rooms i'm listening to
presentCount		Integer	// number of listeners who are "present"
messageCount			Integer
createdAt			Timestamp
lastMessageAt			Timestamp

Getter methods

getListeners()		(params)->[Room]	// room.getListeners.cypher
getAnnouncers()		(params)->[Room]	// room.getSpeakers.cypher
getMessages()			(params)->[Message]	// room.getMessagess.cypher

Setter methods

startListening()	(toId)->null		// room.startListening.cypher
stopListening()		(toId)->null		// room.stopListening.cypher

User

User is a subclass of room and inherits all its properties. In addition, it has the getter methods:

getOwnRooms()			(params)->[Room]
getOwnAccounts()		(params)->[Account]

Message

Note: All the routing magic happens when save() is called on a message object. It is implemented in message.save.cypher. It creates new 'reaches' relationships between the message and all the rooms and accounts that the message "has reached", starting with its from and to.

All properties are read-write.

id					String		// auto-generated GUID
type				String		// text, invite, join, part, away, back
text				Text
time				Timestamp
from				Room
to					Room
account				Account		// from which this message was sent
resource			String		// account-specific resource identifier
about				String		// optional: id of room invitation is for

getRooms()			(params)->[Room]	// rooms this has been sent to.
getAccounts()		(params)->[Account]	// message.getAccounts.cypher

Account

All properties are read-write.

id				String		// auto-generated GUID
belongs				User
listens				Room
gateway				String
identifier			String
present				Boolean		// account available in real time?
params				Text/Json

Neo4j schema

All nodes are indexed on 'id'. There are no relationship indexes. The properties of each node are listed.

Room nodes

id				String
name				String
type				String
picture				String
description			String
allowedUrls			String

Account nodes

id				String
gateway				String
identifier			String
present				Boolean
params				Text/Json

Message nodes

id				String
type				String
text				Text
time				Timestamp
fromResource			String
about				String

listens relationships

listener -> announcer
Room     -> Room
Account  -> Room

belongs relationships

object  -> owner
Room    -> Room[type=user]
Account -> Room[type=user]

from, to, reaches relationships

Message -[:from]-> Account
Message -[:from]-> Room
Message -[:to]-> Room
Message -[:reaches]-> Room
Message -[:reaches]-> Account

Gateway Module API

All the methods are optional.

init(core, config)

Initialize the gateway. Make a local reference to the core object.

send({id, type, text, time, from, to}, [identifier]) -> ..Boolean

"From" is the room of the user the message was sent from; "To" is the room it was sent to; "Type" is text, away/back, invite, join/part This function should only be called ONCE per message per gateway, with all the recipients' account objects in the second parameter.

Returns a promise that resolves true or false depending on sending success.

auth(identifier, token) -> ..Boolean

Returns a promise which resolves to a boolean to indicate if the token correctly authenticates the account. For example, if an fb account and access token are passed, it will verify it with fb and resolve to true or false.

getProfile(identifier) -> ..String

For gateways like facebook, returns a URL with the profile page of the user on that network.

getPicture(identifier) -> ..String

For gateways like facebook, returns a URL with the picture/avatar of the user on that network.

Cypher Queries

room.get
room.create
room.update
room.delete

room.getListeners
room.getAnnouncers
room.getMessages

room.startListening
room.stopListening

user.getOwnRooms
user.getOwnAccounts

message.get
message.create
message.update
message.delete

message.getRooms
message.getAccounts

account.get
account.create
account.update
account.delete