Set up beginnings of getContacts end point

This commit is contained in:
James Allen 2015-10-06 17:22:11 +01:00
parent 9d99b12ed5
commit ededf6f6b9
4 changed files with 184 additions and 4 deletions

View file

@ -16,3 +16,15 @@ module.exports = ContactManager =
}, update, {
upsert: true
}, callback)
getContacts: (user_id, callback = (error) ->) ->
try
user_id = ObjectId(user_id.toString())
catch error
return callback error
db.contacts.findOne {
user_id: user_id
}, (error, user) ->
return callback(error) if error?
callback null, user?.contacts

View file

@ -1,5 +1,7 @@
ContactManager = require "./ContactManager"
WebApiManager = require "./WebApiManager"
logger = require "logger-sharelatex"
async = require "async"
module.exports = HttpController =
addContact: (req, res, next) ->
@ -18,5 +20,63 @@ module.exports = HttpController =
return next(error) if error?
res.status(204).end()
getUserContacts: (req, res, next) ->
CONTACT_LIMIT: 50
getContacts: (req, res, next) ->
{user_id} = req.params
if req.query?.limit?
limit = parseInt(req.query.limit, 10)
else
limit = HttpController.CONTACT_LIMIT
limit = Math.min(limit, HttpController.CONTACT_LIMIT)
logger.log {user_id}, "getting contacts"
ContactManager.getContacts user_id, (error, contact_dict) ->
return next(error) if error?
contacts = []
for user_id, data of (contact_dict or {})
contacts.push {
user_id: user_id
n: data.n
ts: data.ts
}
HttpController._sortContacts contacts
contacts = contacts.slice(0, limit)
async.mapLimit contacts, 5,
(contact, cb) ->
WebApiManager.getUserDetails contact.user_id, (error, user) ->
return cb(error) if error?
cb null, HttpController._formatUser user
(error, users) ->
return next(error) if error?
res.status(200).send({
contacts: users
})
_sortContacts: (contacts) ->
contacts.sort (a, b) ->
# Sort by decreasing count, descreasing timestamp.
# I.e. biggest count, and most recent at front.
if a.n > b.n
return -1
else if a.n < b.n
return 1
else
if a.ts > b.ts
return -1
else if a.ts < b.ts
return 1
else
return 0
_formatUser: (user) ->
return {
id: user._id
email: user.email
first_name: user.first_name
last_name: user.last_name
}

View file

@ -35,13 +35,14 @@ describe "ContactManager", ->
.calledWith({
user_id: ObjectId(@user_id)
}, {
$set:
$inc:
"contacts.mock_contact.n": 1
$set:
"contacts.mock_contact.ts": new Date()
}, {
upsert: true
})
.should.equal true
it "should call the callback", ->
@callback.called.should.equal true
@ -52,3 +53,32 @@ describe "ContactManager", ->
it "should call the callback with an error", ->
@callback.calledWith(new Error()).should.equal true
describe "getContacts", ->
beforeEach ->
@user = {
contacts: ["mock", "contacts"]
}
@db.contacts.findOne = sinon.stub().callsArgWith(1, null, @user)
describe "with a valid user_id", ->
beforeEach ->
@ContactManager.getContacts @user_id, @callback
it "should find the user's contacts", ->
@db.contacts.findOne
.calledWith({
user_id: ObjectId(@user_id)
})
.should.equal true
it "should call the callback with the contacts", ->
@callback.calledWith(null, @user.contacts).should.equal true
describe "with an invalid user id", ->
beforeEach ->
@ContactManager.getContacts "not-valid-object-id", @callback
it "should call the callback with an error", ->
@callback.calledWith(new Error()).should.equal true

View file

@ -9,6 +9,7 @@ describe "HttpController", ->
beforeEach ->
@HttpController = SandboxedModule.require modulePath, requires:
"./ContactManager": @ContactManager = {}
"./WebApiManager": @WebApiManager = {}
"logger-sharelatex": @logger = { log: sinon.stub() }
@user_id = "mock-user-id"
@contact_id = "mock-contact-id"
@ -55,3 +56,80 @@ describe "HttpController", ->
it "should return 400, Bad Request", ->
@res.status.calledWith(400).should.equal true
@res.send.calledWith("contact_id should be a non-blank string").should.equal true
describe "getContacts", ->
beforeEach ->
@req.params =
user_id: @user_id
now = Date.now()
@contacts = {
"user-id-1": { n: 2, ts: new Date(now) }
"user-id-2": { n: 4, ts: new Date(now) }
"user-id-3": { n: 2, ts: new Date(now - 1000) }
}
@user_details = {
"user-id-1": { _id: "user-id-1", email: "joe@example.com", first_name: "Joe", last_name: "Example", extra: "foo" }
"user-id-2": { _id: "user-id-2", email: "jane@example.com", first_name: "Sarah", last_name: "Example", extra: "foo" }
"user-id-3": { _id: "user-id-3", email: "sam@example.com", first_name: "Sam", last_name: "Example", extra: "foo" }
}
@ContactManager.getContacts = sinon.stub().callsArgWith(1, null, @contacts)
@WebApiManager.getUserDetails = (user_id, callback = (error, user) ->) =>
callback null, @user_details[user_id]
sinon.spy @WebApiManager, "getUserDetails"
describe "normally", ->
beforeEach ->
@HttpController.getContacts @req, @res, @next
it "should look up the contacts in mongo", ->
@ContactManager.getContacts
.calledWith(@user_id)
.should.equal true
it "should look up each contact in web for their details", ->
for user_id, data of @contacts
@WebApiManager.getUserDetails
.calledWith(user_id)
.should.equal true
it "should return a sorted list of contacts by count and timestamp", ->
@res.send
.calledWith({
contacts: [
{ id: "user-id-2", email: "jane@example.com", first_name: "Sarah", last_name: "Example" }
{ id: "user-id-1", email: "joe@example.com", first_name: "Joe", last_name: "Example" }
{ id: "user-id-3", email: "sam@example.com", first_name: "Sam", last_name: "Example" }
]
})
.should.equal true
describe "with more contacts than the limit", ->
beforeEach ->
@req.query =
limit: 2
@HttpController.getContacts @req, @res, @next
it "should return the most commonly used contacts up to the limit", ->
@res.send
.calledWith({
contacts: [
{ id: "user-id-2", email: "jane@example.com", first_name: "Sarah", last_name: "Example" }
{ id: "user-id-1", email: "joe@example.com", first_name: "Joe", last_name: "Example" }
]
})
.should.equal true
describe "without a contact list", ->
beforeEach ->
@ContactManager.getContacts = sinon.stub().callsArgWith(1, null, null)
@HttpController.getContacts @req, @res, @next
it "should return an empty list", ->
@res.send
.calledWith({
contacts: []
})
.should.equal true
describe "with a holding account", ->
it "should not return holding accounts"