Permalink
Browse files

more work, more tests.

  • Loading branch information...
1 parent 3760c48 commit 9376d6fd7f78a1c42dc05cb7245cf2818b077c5d @robey committed Apr 12, 2013
@@ -21,6 +21,30 @@ requireOwner = (f) ->
@setAuth @ownerAuthentication.username, @ownerAuthentication.password
f.bind(@)(x...)
+requirePublisher = (f) ->
+ (x...) ->
+ if not @publisherKey?
+ return Q.reject(new FaunaError(error: "Requires authentication as publisher"))
+ @setAuth @publisherKey, ""
+ f.bind(@)(x...)
+
+requireTokenOrPublisher = (f) ->
+ (x...) ->
+ if not (@publisherKey? or @userToken?)
+ return Q.reject(new FaunaError(error: "Requires authentication as publisher or user"))
+ if @userToken?
+ @setAuth @userToken, ""
+ else
+ @setAuth @publisherKey, ""
+ f.bind(@)(x...)
+
+assertNamespace = (namespace) ->
+ (f) ->
+ (id, x...) ->
+ if id.indexOf(namespace + "/") != 0
+ return Q.reject(new FaunaError(error: "Object id must be in namespace '#{namespace}'"))
+ f.bind(@)(id, x...)
+
asEventArray = (f) ->
(x...) ->
f.bind(@)(x...).then (data) => @unpack(data).toArray()
@@ -50,10 +74,14 @@ class FaunaClient
# all js/json object transformations use the schema:
@schema = new Schema(@)
# hook up nested namespaces
- @publisherKeys = new _PublisherKeys
- for k, v of @publisherKeys then @publisherKeys[k] = v.bind(@)
- @clientKeys = new _ClientKeys
- for k, v of @clientKeys then @clientKeys[k] = v.bind(@)
+ plumb = (cls) =>
+ rv = new cls
+ for k, v of rv then rv[k] = v.bind(@)
+ rv
+ @publisherKeys = plumb _PublisherKeys
+ @clientKeys = plumb _ClientKeys
+ @classes = plumb _Classes
+ @_testing = plumb _Testing
debug: (message) -> Rest.debug(message)
@@ -78,6 +106,20 @@ class FaunaClient
@ownerAuthentication.username = username
@ownerAuthentication.password = password
+ setPublisherKey: (key) ->
+ ###
+ Set the publisher key to use for requests that require one.
+ ###
+ @publisherKey = key
+
+ setUserToken: (key) ->
+ ###
+ Set the user login token to use for requests. (This is tantamount to
+ logging in, without the verification step that ensures the token is
+ actually valid.)
+ ###
+ @userToken = key
+
deleteEverything: requireOwner asObject -> @rest("delete", "everything")
# tell fauna to create classes & event sets for us.
@@ -89,22 +131,36 @@ class FaunaClient
Q.all(for setName in metadata.eventSets then @createEventSet(name, setName)).then (sets) =>
{ "class": klass, "event_sets": sets }
)
+ # XXX
class _PublisherKeys
get: requireOwner asEventArray -> @rest("get", "keys/publisher")
create: requireOwner asObject -> @rest("post", "keys/publisher")
- getById: requireOwner asObject (id) -> @rest("get", "#{id}")
- deleteById: requireOwner (id) -> @rest("delete", "#{id}")
+ getById: requireOwner asObject assertNamespace("keys/publisher") (id) -> @rest("get", "#{id}")
+ deleteById: requireOwner assertNamespace("keys/publisher") (id) -> @rest("delete", "#{id}")
deleteAll: ->
@publisherKeys.get().then (keys) =>
Q.all(keys.map((key) => @publisherKeys.deleteById(key._fauna.id)))
getOrCreate: ->
@publisherKeys.get().then (keys) =>
if keys.length > 0 then keys[0] else @publisherKeys.create()
+ .then (key) =>
+ @setPublisherKey(key.key)
+ key
class _ClientKeys
get: requireOwner asEventArray -> @rest("get", "keys/client")
+ class _Classes
+ get: requirePublisher asEventArray -> @rest("get", "classes")
+ getByName: requireTokenOrPublisher asObject (name) -> @rest("get", "classes/#{name}/config")
+
+ # wrapper to allow easy testing of the decorators
+ class _Testing
+ requireOwner: requireOwner (x) -> Q(x)
+ requirePublisher: requirePublisher (x) -> Q(x)
+ requireTokenOrPublisher: requireTokenOrPublisher (x) -> Q(x)
+
# ----- internals
View
@@ -28,13 +28,17 @@ class Class
@getInstances = (fauna, params) ->
fauna.listInstancesOfClass(metadataForClass(@).getClassName(), params)
+ @native = ->
+ metadataForClass(@).native = true
+
exports.Class = Class
class ClassMetadata
constructor: (@jsClass) ->
@className = null
+ @native = false
@schema = {}
@eventSets = []
@@ -138,7 +142,7 @@ class Schema
inflate: (resource) ->
className = resource["class"]
- if "/" in className then className = resource["class"]?.split("/")[1]
+ if className[0...8] == "classes/" then className = className[8...]
fclass = @classes[className]
obj = if fclass? then new fclass() else new Object()
@setFaunaData(obj, className, resource)
View
@@ -0,0 +1,100 @@
+should = require 'should'
+Q = require 'q'
+util = require 'util'
+
+fauna = require("../lib/fauna")
+test_util = require("./test_util")
+withSuccessfulRequest = test_util.withSuccessfulRequest
+futureTest = test_util.futureTest
+
+describe "FaunaClient.classes", ->
+ it "get", futureTest ->
+ f = new fauna.FaunaClient()
+ f.setPublisherKey("qqq")
+ r = -> f.classes.get()
+ withSuccessfulRequest(JSON.parse(data1), r).then ([ resp, requests ]) ->
+ requests.length.should.eql(1)
+ requests[0].url.should.match(/\/classes/)
+ resp.map((c) -> c.name).should.eql [
+ "spells"
+ "magical_creatures"
+ ]
+
+ it "getByName", futureTest ->
+ f = new fauna.FaunaClient()
+ class ClassConfig extends fauna.Class
+ @faunaClass "config"
+ @field "desc"
+ f.addPrototypes ClassConfig
+ f.setUserToken("xxx")
+ r = -> f.classes.getByName("magical_creatures")
+ withSuccessfulRequest(JSON.parse(data2), r).then ([ resp, requests ]) ->
+ requests.length.should.eql(1)
+ requests[0].url.should.match(/\/classes\/magical_creatures\/config/)
+ resp.name.should.eql "magical_creatures"
+ resp.desc.should.eql "Any of many mysterious beasts."
+
+
+data1 = """
+{
+ "resource" : {
+ "ref" : "classes",
+ "class" : "sets",
+ "after" : 9223372036854775,
+ "creates" : 2,
+ "updates" : 1,
+ "deletes" : 0,
+ "events" : [
+ {
+ "ts" : 1365020937690000,
+ "action" : "update",
+ "resource" : "classes/spells/config",
+ "set" : "classes"
+ },
+ {
+ "ts" : 1365020937407001,
+ "action" : "create",
+ "resource" : "classes/spells/config",
+ "set" : "classes"
+ },
+ {
+ "ts" : 1365020937350004,
+ "action" : "create",
+ "resource" : "classes/magical_creatures/config",
+ "set" : "classes"
+ }
+ ]
+ },
+ "references" : {
+ "classes/spells/config" : {
+ "ref" : "classes/spells/config",
+ "class" : "classes/config",
+ "ts" : 1365020937690000,
+ "name" : "spells",
+ "data" : { },
+ "deleted" : false
+ },
+ "classes/magical_creatures/config" : {
+ "ref" : "classes/magical_creatures/config",
+ "class" : "classes/config",
+ "ts" : 1365020937350004,
+ "name" : "magical_creatures",
+ "data" : { "desc" : "Any of many mysterious beasts." },
+ "deleted" : false
+ }
+ }
+}
+"""
+
+data2 = """
+{
+ "resource" : {
+ "ref" : "classes/magical_creatures/config",
+ "class" : "classes/config",
+ "ts" : 1365020937350004,
+ "name" : "magical_creatures",
+ "data" : { "desc" : "Any of many mysterious beasts." },
+ "deleted" : false
+ }
+}
+"""
@@ -63,15 +63,43 @@ describe "FaunaClient", ->
es.events[0].resource.state = "Tennessee"
es.events[1].resource.friend.state.should.eql("Tennessee")
- it "requires auth sometimes", futureTest ->
- f = new fauna.FaunaClient()
- f.publisherKeys.get()
- .then (x) ->
- throw new Error("Should not succeed")
- .fail (error) ->
- error.message.should.match(/Requires authentication/)
+ describe "requires auth", ->
+ it "owner", futureTest ->
+ f = new fauna.FaunaClient()
+ f._testing.requireOwner("hello").fail (error) ->
+ error.message.should.eql("Requires authentication as owner")
+ .then ->
+ f.setOwnerAuth("u", "p")
+ f._testing.requireOwner("hello")
+ .then (x) ->
+ x.should.eql("hello")
+
+ it "publisher", futureTest ->
+ f = new fauna.FaunaClient()
+ f._testing.requirePublisher("hello").fail (error) ->
+ error.message.should.eql("Requires authentication as publisher")
+ .then ->
+ f.setPublisherKey("qqq")
+ f._testing.requirePublisher("hello")
+ .then (x) ->
+ x.should.eql("hello")
+
+ it "token or publisher", futureTest ->
+ f = new fauna.FaunaClient()
+ f._testing.requireTokenOrPublisher("hello").fail (error) ->
+ error.message.should.eql("Requires authentication as publisher or user")
+ .then ->
+ f.setPublisherKey("qqq")
+ f._testing.requireTokenOrPublisher("hello")
+ .then (x) ->
+ x.should.eql("hello")
+ f.setPublisherKey(null)
+ f.setUserToken("xxx")
+ f._testing.requireTokenOrPublisher("hello")
+ .then (x) ->
+ x.should.eql("hello")
- it "sets owner auth", futureTest ->
+ it "uses owner auth", futureTest ->
f = new fauna.FaunaClient()
f.setOwnerAuth("u", "p")
r = -> f.publisherKeys.deleteById("keys/publisher/z")
@@ -31,6 +31,12 @@ describe "FaunaClient.publisherKeys", ->
requests[0].url.should.match(/keys\/publisher/)
resp.key.should.eql "AQAAayWp97AEAQBrJamcsAABlnqXMsjdfw3kJU44o1dpDg"
+ it "namespace assertion", futureTest ->
+ f = new fauna.FaunaClient()
+ f.setOwnerAuth("u", "p")
+ f.publisherKeys.getById("users/30159234443248641").fail (error) ->
+ error.message.should.eql("Object id must be in namespace 'keys/publisher'")
+
it "getById", futureTest ->
f = new fauna.FaunaClient()
f.setOwnerAuth("u", "p")
@@ -89,6 +95,8 @@ describe "FaunaClient.publisherKeys", ->
requests.length.should.eql(1)
requests[0].method.should.eql("GET")
resp.key.should.eql "AQAAayWp_qAAAQBrJamcsAABzAFAUI2ckXGpAt2VjWsyiA"
+ .then ->
+ f.publisherKey.should.eql "AQAAayWp_qAAAQBrJamcsAABzAFAUI2ckXGpAt2VjWsyiA"
# test data from the fauna documentation for GET keys/publisher
View
@@ -85,6 +85,18 @@ describe "Schema", ->
obj._fauna.ts.should.eql(new Date(1))
obj._fauna.deleted.should.eql(true)
+ it "can distinguish fauna-native classes from custom classes", ->
+ class Cat extends fauna.Class
+ @field "name"
+ class User extends fauna.Class
+ @field "email"
+ @native()
+
+ s = new fauna.Schema()
+ s.addPrototypes Cat, User
+ s.metadataForClassName("cats").native.should.eql(false)
+ s.metadataForClassName("users").native.should.eql(true)
+
describe "EventSet", ->
it "can flatten events to an array", ->

0 comments on commit 9376d6f

Please sign in to comment.