UPDATE Oct 22, 2018: This post describes an early version of both the Army Knife app and the ActingWeb library. It is still a good source and introduction to the basics of using ActingWeb to build a bot, but the current version of Army Knife is not available in code form.
UPDATE July 7, 2016: To simplify getting the Spark Army Knife code, I have established a new code repository that will always mirror what is running at https://spark-army-knife.appspot.com. You can thus easily download the entire Army Knife app, including the right version of the ActingWeb library, from the download section of the repository.
UPDATE June 26, 2016: Spark just turned off the practice of accepting json encoded body for token requests (the standard specified url/form encoded). I have committed a new version of the ActingWeb library that is a drop-in replacement of the previous version (note though the new template aw-actor-www-trust.html. It is not needed for Spark Army Knife, but is there to support ActingWeb trust relationships between actors.)
In my earlier post on the Cisco Spark app “Spark Army Knife” (https://spark-army-knife.appspot.com), I introduced what it does and how to sign up to use it, and I promised to share the code. After some re-factoring and getting approval for open sourcing the code, I here share the code in two posts. In this first post, I cover the ActingWeb library and how it creates the framework or plumbing, if you like, for the Spark Army Knife. The next post covers the Spark-specific code. If you haven’t read my earlier post or signed up to Spark Army Knife, I recommend doing that before reading on, so you know what the whole thing is about.
The ActingWeb library offers the developer the following when implementing a Spark app:
- A very simple way of signing up new users and creating a personal cloud “actor” for the user
- A way to easily tie this actor in the cloud to the user’s Spark account using OAuth authorisation
- A framework for storing personal data isolated from all other users
- The luxury of coding without having to think about how to handle more than one user at the time
- Some simple tools to create personalised html pages for the user (like for the /makepublic feature)
- A trusted way of interacting with other users’ actors through a simple REST API
In the Spark Army Knife, I have not used #6 at all, so I will not touch that part of the ActingWeb here.
The most important way ActingWeb accomplishes this user-centric approach is that ALL functionality is exposed through a REST API that is personalised to that specific user (but standardised). This is very different from most APIs that expose a REST API where the user typically is a parameter in the request to identify the user.
For example, to get somebody’s Spark rooms (not implemented here, though possible), you could do https://spark-army-knife.appspot.com/2de42dab3232bdfbb532adeb/methods/getRooms where 2de42dab3232bdfbb532adeb is the identity of an ActingWeb actor representing one and only one Spark user. When you do /myurl in the Army Knife Control room, you will get your actor’s root URL with /www appended (to give you the web landing page of the actor). You can also do an https DELETE to that root URL (i.e. minus the /www part) to delete your account (or use the root URL and add ?_method=DELETE to the end of the URL).
Why is this approach a good thing? Mainly because as a developer, when you get the request, the ActingWeb library has already set up your environment with all you need for that particular user, you just have to think about the code and Spark API requests you need for the functionality you want. Of course, this is absolutely not good if you are making a traditional web application with users interacting, and looking at, and sharing content you create, but when you only want to interact with an OAuth-enabled service, it is hard to make it simpler and more powerful! (the ActingWeb trust and interaction model allows creation of more complex features like your Spark actor using your Box actor to pull files in and out of Spark, but that is for later!)
If you are eager to get started, you may want to jump to the end of this post where you will find a Quick Start guide, and you can come back later and read more about the details of the ActingWeb library and how it works later.
The ActingWeb Library
Now you want more of the specifics?! Great! The ActingWeb python library offers the following code elements:
- A set of .py files serving the root paths for ActingWeb (those used by Spark Army Knife are: /, the root factory for actor creation, /oauth for binding the actor to Spark, /callbacks to receive webhooks from Spark, and /www to serve the web page to add yourself to a public room)
- The app.yaml and index.yaml files used by Google App Engine to register the paths and index the data
- A set of webapp2 html templates to serve the sign up page and the /www web pages
- The ActingWeb library and storage module to persist data for the actor
Before we go more into the specifics, here is the code repository (note that the code is licensed as Apache 2.0), so you can see the details or check out the code yourself. This is the code that is running live on https://actingwebdemo.appspot.com/. You can get details of how to set up your own instance of the service in my demo app post. If you just want to download the code (and not check it out to get updates), there is a Download link at the left-hand side in the navigation bar.
I have not done any unit tests, but the library is fully tested through online tests implemented using Runscope. The snapshot below gives you an idea of how that works. Looking at the tests is a great way of understanding how the ActingWeb API works (but I have not found a way to give Runscope read-only access yet).
This is the way the ActingWeb framework is built up:
- The library’s actingweb/config.py file is the most important file as this is the only file you need to touch to set up your own actingwebdemo service. Here you set all the configurations, including whether you want to turn on the /www user interface (or only allow REST API requests). OAuth settings (e.g. for Spark) can also be found here
- You use the root factory, aw-root-factory.py (found at https://actingwebdemo.appspot.com/) to create a new actor, either using a form and POST variables or by adding a json request body. The demo (and the Spark Army Knife) uses a simple form where you can sign up and POST directly to the API
- As actingwebdemo does not use OAuth, it will just return a simple page with information about the new actor. In the Spark Army Knife app, you need to connect the new actor to your Spark user. This is accomplished by going to the /oauth path of the actor. If you did not use json, the root factory assumes a human is in front of a browser (as opposed to this being an API request with json) and will forward you to /www
- For actingwebdemo, you are now done, but if OAuth is turned on, like for Spark Army Knife, /www will check if you are authenticated (checking a cookie) and since you are not, you will be forwarded to the actor’s /oauth path:
- this will start the Spark OAuth process
- and once you have granted Spark Army Knife access to your Spark account, you will be redirected back to your actor’s /www path as that is where you came from
As part of the OAuth authorisation and at other locations in the ActingWeb code, there are “hooks” in the code to execute application-specific code. The directory on_aw/ contains the .py files where you can add such handlers. For Spark Army Knife, there are four handlers: to process callbacks from the Spark service (primarily messages), initialisation when OAuth access is granted (create the Army Knife Control room), processing of /www requests to create pages like the sign-up form for public rooms, as well as a handler to clean up when an actor is deleted (delete all webhooks in the Spark platform and clean up data).
The ActingWeb library for Google AppEngine is a library you can just drop into your app directory (as explained in my Spark demo app post), but you can run it as an AppEngine app as is. This makes it very easy to extend.
Here is an example of how the ActingWeb library initialises the environment (you don’t need to do this yourself, so this is just to understand how it works):
Config = config.config() if not Config.ui: self.response.set_status(404, "Web interface is not enabled") return myself = actor.actor(id) if not myself.id: self.response.set_status(404, "Actor not found") return check = auth.auth(id, type=Config.www_auth) if not check.checkAuth(self, '/www/' + path): return
The config is instantiated and this snippet is taken from the /www path handler, so the Config.ui setting is checked to verify that the web interface should be served. Then, an actor is instantiated using id. The id variable was passed into the GET, POST, and DELETE handlers from the webapp2 framework, so it will be the long actor unique id of the URL path. If the actor was not found, there is nothing to do. However, if found and since this is a web page to be served, authentication and authorisation must be done. auth.auth is instantiated with the actor’s id and the auth type set to the app’s configured www_auth (oauth or basic auth using the actor’s passphrase). Then the checkAuth() method is called with the webapp2 (http request object) and the requested path as arguments. If basic authentication is set, then the creator user and passphrase are used to grant access, if oauth is set, a cookie is checked to see if this browser earlier has been used with OAuth to authorise access. If the cookie is not set, an OAuth process is started with the user having to approve access to the app.
Through the on_aw/ hooks, you can execute code on important events. You will see that the on_ow/*.py files have empty functions in the actingwebdemo code. In the Spark Army Knife, these functions (+ a Spark API library) are really the only Spark specific code necessary to implement the Army Knife functionality.
An important event is the callback (or webhooks in Spark platform terminology). A webhook callback will already have an actor (myself) when the on_aw_* functions are called, and the http request with headers, body, etc (req), and information about the path (name or path) is also passed in. Once you have the actor, you can get an oauth object to use through this instantiation as on_aw functions always will be called after OAuth has happened: my_oauth = oauth.oauth(token=myself.getProperty(‘oauth_token’).value)
For callbacks, a callback should always be registered to the /{actorid}/callbacks path (look at the on_aw_callbacks.py file). As an example for actor 2de42dab3232bdfbb532adeb, the Spark Army Knife registers callbacks to /2de42dab3232bdfbb532adeb/callbacks/room?id={roomid}. Since the actor is already available and the roomid gives the context, all necessary information is thus available right there in the request. The actor object myself has two simple methods to store and retrieve information for the actor: getProperty() and setProperty(). getProperty() returns an object with name and value. When a user signs up, a part of the initialisation is to create the Army Knife Control Spark room. The id of this room is stored as an actor property and is retrieved like this:
chatRoomId = myself.getProperty('chatRoomId').value
Similarly, information can be stored as properties through a request to myself.setProperty(‘propertyname’, value).
With these simple elements, you have what you need to implement an app interacting with any OAuth-enabled API service, and you can move on my post on the Spark specific part of the code.
Quick Start Guide
Here is the QuickStart guide for how to use the ActingWeb library:
- Register a new Spark app at http://developer.ciscospark.com
- Start a new Google AppEngine project and drop the ActingWeb library code into the project directory
- Edit actingweb/config.py file with meta-information about my ActingWeb app (create a new urn that is unique, see config.py), and set the Spark API OAuth information (from step 1). Set www_auth either to basic or oauth (use basic if you want to use creator and passphrase; oauth if you want to do an OAuth authorisation to get access to the /www and other paths of the actor)
- Edit the templates/*.html you want to change
- Create a new actor by browsing to the app root (where you deploy the Google AppEngine app), then go to actorurl/oauth to verify that you can bind the new actor to Spark by granting access to your app
- Add your code to on_aw/*.py to do the stuff you want to do with the OAuth service