(It’s been a very long time since this RSS feed has had an update. Um. Suprise!)
At work, as a Lunch Time Project™, myself, Ed and David have designed an ultra-ultra-lightweight protocol for exposing a few methods returning simple structures, with the aim of creating rapid AJAX prototypes for internal and “fun” projects.
(In the spirit of internal and fun project, Pembo developed a system for tracking coffee output, Neil built a system for managing our lunch orders, and a starting goal for this new system is tracking our office currency; owed cups of tea.)
The basic premise is that there exists a central registry of thing that can be called, indexed by name and version. The registry knows about the URLs that these services live at, but keeps them a secret so that all calls pass through the proxy.
The proxy receives a request at the wellknown URI “/discovery/1/discover” and receives a response like
[['discovery', 1], ['trac', 1], ['time', 1], ['auth', 1]]
representing each of the services available, and their maximum version available.
Requests to http://[proxy]/[service]/[version]/[method] get passed verbatim through, meaning you could implement the proxy as a mod_rewrite / mod_proxy pair in Apache. (At least, at the moment, if we don’t build in more smarts).
The basic architecture is:
Building a service is truly a doddle. Each service must provide a “listMethods” method which returns a JSON list of all of the methods available. You could, in PHP, just place some files in a directory, turn on Apache MultiViews, and be at it. That’s what our test service does.
Now that we have a service (we have two actually, because the registry is, itself, defined as a service, to be as meta as possible) we should build a client. Obviously, you can just go to http://[proxy]/time/1/getTime and get the result, which is nice.
Anyway, my first draft at a code-generating Python script is made up of a template (which has some global variables added) and a script to do a bare minimum of introspection:
import json import urllib class Call: def __init__(self, method): self.method = method def __call__(self, **kwargs): u = urllib.urlopen("%s/%s?%s" % (base_url, self.method, urllib.urlencode(kwargs))) body = u.read() return json.read(body) class O: pass o = O() for method in methods: setattr(o, method, Call(method))
import urllib import json import sys base = "http://[proxy address]/" template = "template.py" def main(name): u = urllib.urlopen("%s/discovery/1/discover" % base) body = u.read().strip() j = eval(body) f = open("x_%s.py" % name, 'w') for item in j: if item['name'] == name: version = item['version'] service_url = '%s/%s/%s' % (base, name, version) lm_contents = eval(urllib.urlopen('%s/listMethods' % service_url).read()) print >>f, "base_url = %s" % repr(service_url) print >>f, "methods = %s" % repr(lm_contents) print >>f, open(template).read() f.close() if __name__ == '__main__': main(*sys.argv[1:])
I would like to move the Call object out to a module that just gets imported, and then add the method to the module namespace itself, but I can’t figure out how to do that (the methods in the namespace; I know how to move the code).
At the moment to use it I must do this:
>>> from x_time import o as time >>> dir(time) ['__doc__', '__module__', 'getTime', 'listMethods'] >>> time.listMethods() ['listMethods', 'getAllTickets']
That import hackery, well, it rubs me up wrong.