mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-14 06:03:45 +01:00
Pull in contacts from contacts api and send to tag-input for autocomplete
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
AuthenticationController = require "../Authentication/AuthenticationController"
|
||||
ContactManager = require "./ContactManager"
|
||||
UserGetter = require "../User/UserGetter"
|
||||
logger = require "logger-sharelatex"
|
||||
|
||||
module.exports = ContactsController =
|
||||
getContacts: (req, res, next) ->
|
||||
AuthenticationController.getLoggedInUserId req, (error, user_id) ->
|
||||
return next(error) if error?
|
||||
ContactManager.getContactIds user_id, {limit: 50}, (error, contact_ids) ->
|
||||
return next(error) if error?
|
||||
UserGetter.getUsers contact_ids, {
|
||||
email: 1, first_name: 1, last_name: 1, holdingAccount: 1
|
||||
}, (error, contacts) ->
|
||||
return next(error) if error?
|
||||
|
||||
# UserGetter.getUsers may not preserve order so put them back in order
|
||||
positions = {}
|
||||
for contact_id, i in contact_ids
|
||||
positions[contact_id] = i
|
||||
contacts.sort (a,b) -> positions[a._id?.toString()] - positions[b._id?.toString()]
|
||||
|
||||
# Don't count holding accounts to discourage users from repeating mistakes (mistyped or wrong emails, etc)
|
||||
contacts = contacts.filter (c) -> !c.holdingAccount
|
||||
|
||||
contacts = contacts.map(ContactsController._formatContact)
|
||||
res.send({
|
||||
contacts: contacts
|
||||
})
|
||||
|
||||
_formatContact: (contact) ->
|
||||
return {
|
||||
id: contact._id?.toString()
|
||||
email: contact.email
|
||||
first_name: contact.first_name
|
||||
last_name: contact.last_name
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
request = require "request"
|
||||
settings = require "settings-sharelatex"
|
||||
logger = require "logger-sharelatex"
|
||||
|
||||
module.exports = ContactManager =
|
||||
getContactIds: (user_id, options = { limits: 50 }, callback = (error, contacts) ->) ->
|
||||
logger.log {user_id}, "getting user contacts"
|
||||
url = "#{settings.apis.contacts.url}/user/#{user_id}/contacts"
|
||||
request.get {
|
||||
url: url
|
||||
qs: options
|
||||
json: true
|
||||
jar: false
|
||||
}, (error, res, data) ->
|
||||
return callback(error) if error?
|
||||
if 200 <= res.statusCode < 300
|
||||
callback(null, data?.contact_ids or [])
|
||||
else
|
||||
error = new Error("contacts api responded with non-success code: #{res.statusCode}")
|
||||
logger.error {err: error, user_id}, "error getting contacts for user"
|
||||
callback(error)
|
||||
|
||||
addContact: (user_id, contact_id, callback = (error) ->) ->
|
||||
logger.log {user_id, contact_id}, "add user contact"
|
||||
url = "#{settings.apis.contacts.url}/user/#{user_id}/contacts"
|
||||
request.post {
|
||||
url: url
|
||||
json: {
|
||||
contact_id: contact_id
|
||||
}
|
||||
jar: false
|
||||
}, (error, res, data) ->
|
||||
return callback(error) if error?
|
||||
if 200 <= res.statusCode < 300
|
||||
callback(null, data?.contact_ids or [])
|
||||
else
|
||||
error = new Error("contacts api responded with non-success code: #{res.statusCode}")
|
||||
logger.error {err: error, user_id, contact_id}, "error adding contact for user"
|
||||
callback(error)
|
||||
@@ -0,0 +1,9 @@
|
||||
AuthenticationController = require('../Authentication/AuthenticationController')
|
||||
ContactController = require "./ContactController"
|
||||
|
||||
module.exports =
|
||||
apply: (webRouter, apiRouter) ->
|
||||
webRouter.get '/user/contacts',
|
||||
AuthenticationController.requireLogin(),
|
||||
ContactController.getContacts
|
||||
|
||||
@@ -16,3 +16,11 @@ module.exports = UserGetter =
|
||||
query = _id: query
|
||||
|
||||
db.users.findOne query, projection, callback
|
||||
|
||||
getUsers: (user_ids, projection, callback = (error, users) ->) ->
|
||||
try
|
||||
user_ids = user_ids.map (u) -> ObjectId(u.toString())
|
||||
catch error
|
||||
return callback error
|
||||
|
||||
db.users.find { _id: { $in: user_ids} }, projection, callback
|
||||
@@ -36,6 +36,7 @@ Modules = require "./infrastructure/Modules"
|
||||
RateLimiterMiddlewear = require('./Features/Security/RateLimiterMiddlewear')
|
||||
RealTimeProxyRouter = require('./Features/RealTimeProxy/RealTimeProxyRouter')
|
||||
InactiveProjectController = require("./Features/InactiveData/InactiveProjectController")
|
||||
ContactRouter = require("./Features/Contacts/ContactRouter")
|
||||
|
||||
logger = require("logger-sharelatex")
|
||||
_ = require("underscore")
|
||||
@@ -65,6 +66,7 @@ module.exports = class Router
|
||||
PasswordResetRouter.apply(webRouter, apiRouter)
|
||||
StaticPagesRouter.apply(webRouter, apiRouter)
|
||||
RealTimeProxyRouter.apply(webRouter, apiRouter)
|
||||
ContactRouter.apply(webRouter, apiRouter)
|
||||
|
||||
Modules.applyRouter(webRouter, apiRouter)
|
||||
|
||||
|
||||
@@ -47,12 +47,21 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
|
||||
form(ng-show="canAddCollaborators")
|
||||
.small #{translate("share_with_your_collabs")}
|
||||
.form-group
|
||||
input.form-control(
|
||||
type="text"
|
||||
tags-input(
|
||||
template="shareTagTemplate"
|
||||
placeholder="joe@example.com, sue@example.com, ..."
|
||||
ng-model="inputs.email"
|
||||
ng-model="inputs.contacts"
|
||||
focus-on="open"
|
||||
display-property="email"
|
||||
key-property="id"
|
||||
add-on-paste="true"
|
||||
)
|
||||
auto-complete(
|
||||
source="filterAutocompleteUsers($query)"
|
||||
template="shareAutocompleteTemplate"
|
||||
display-property="email"
|
||||
min-length="0"
|
||||
)
|
||||
.form-group
|
||||
.pull-right
|
||||
select.privileges.form-control(
|
||||
@@ -123,3 +132,21 @@ script(type="text/ng-template", id="makePrivateModalTemplate")
|
||||
button.btn.btn-info(
|
||||
ng-click="makePrivate()"
|
||||
) #{translate("make_private")}
|
||||
|
||||
script(type="text/ng-template", id="shareTagTemplate")
|
||||
.tag-template
|
||||
span(ng-if="data.type")
|
||||
i.fa.fa-fw(ng-class="{'fa-user': data.type == 'user', 'fa-group': data.type == 'group'}")
|
||||
|
|
||||
span {{$getDisplayText()}}
|
||||
|
|
||||
a(href, ng-click="$removeTag()").remove-button
|
||||
i.fa.fa-fw.fa-close
|
||||
|
||||
script(type="text/ng-template", id="shareAutocompleteTemplate")
|
||||
.autocomplete-template
|
||||
i.fa(ng-class="{'fa-user': data.type == 'user', 'fa-group': data.type == 'group'}")
|
||||
|
|
||||
span(ng-bind-html="$highlight(data.email)")
|
||||
div.subdued.small(ng-show="data.name", ng-bind-html="$highlight(data.name)")
|
||||
div.subdued.small(ng-show="data.memberCount") {{ data.memberCount }} members
|
||||
|
||||
@@ -102,6 +102,8 @@ module.exports =
|
||||
url: "http://localhost:8080/json"
|
||||
realTime:
|
||||
url: "http://localhost:3026"
|
||||
contacts:
|
||||
url: "http://localhost:3036"
|
||||
|
||||
templates:
|
||||
user_id: process.env.TEMPLATES_USER_ID or "5395eb7aad1f29a88756c7f2"
|
||||
|
||||
@@ -15,6 +15,7 @@ define [
|
||||
"ipCookie"
|
||||
"ErrorCatcher"
|
||||
"localStorage"
|
||||
"ngTagsInput"
|
||||
])
|
||||
|
||||
return App
|
||||
|
||||
+45
-26
@@ -1,7 +1,7 @@
|
||||
define [
|
||||
"base"
|
||||
], (App) ->
|
||||
App.controller "ShareProjectModalController", ["$scope", "$modalInstance", "$timeout", "projectMembers", "$modal", ($scope, $modalInstance, $timeout, projectMembers, $modal) ->
|
||||
App.controller "ShareProjectModalController", ($scope, $modalInstance, $timeout, projectMembers, $modal, $http) ->
|
||||
$scope.inputs = {
|
||||
privileges: "readAndWrite"
|
||||
email: ""
|
||||
@@ -22,34 +22,54 @@ define [
|
||||
allowedNoOfMembers = $scope.project.features.collaborators
|
||||
$scope.canAddCollaborators = noOfMembers < allowedNoOfMembers or allowedNoOfMembers == INFINITE_COLLABORATORS
|
||||
|
||||
$scope.$watchCollection "inputs.contacts", (value) ->
|
||||
console.log "EMAILS", value
|
||||
|
||||
$scope.autocompleteContacts = []
|
||||
do loadAutocompleteUsers = () ->
|
||||
$http.get "/user/contacts"
|
||||
.success (data) ->
|
||||
console.log "Got contacts", data
|
||||
$scope.autocompleteContacts = data.contacts or []
|
||||
|
||||
$scope.filterAutocompleteUsers = ($query) ->
|
||||
return $scope.autocompleteContacts.filter (user) ->
|
||||
for text in [user.name, user.email]
|
||||
if text?.toLowerCase().indexOf($query.toLowerCase()) > -1
|
||||
return true
|
||||
return false
|
||||
|
||||
$scope.addMembers = () ->
|
||||
return if !$scope.inputs.email? or $scope.inputs.email == ""
|
||||
$timeout -> # Give email list a chance to update
|
||||
return if $scope.inputs.contacts.length == 0
|
||||
|
||||
emails = $scope.inputs.email.split(/,\s*/)
|
||||
$scope.inputs.email = ""
|
||||
$scope.state.error = null
|
||||
$scope.state.inflight = true
|
||||
|
||||
do addNextMember = () ->
|
||||
if emails.length == 0 or !$scope.canAddCollaborators
|
||||
$scope.state.inflight = false
|
||||
$scope.$apply()
|
||||
return
|
||||
emails = $scope.inputs.contacts.map (contact) -> contact.email
|
||||
$scope.inputs.contacts = []
|
||||
$scope.state.error = null
|
||||
$scope.state.inflight = true
|
||||
|
||||
email = emails.shift()
|
||||
projectMembers
|
||||
.addMember(email, $scope.inputs.privileges)
|
||||
.success (data) ->
|
||||
if data?.user # data.user is false if collaborator limit is hit.
|
||||
$scope.project.members.push data.user
|
||||
setTimeout () ->
|
||||
# Give $scope a chance to update $scope.canAddCollaborators
|
||||
# with new collaborator information.
|
||||
addNextMember()
|
||||
, 0
|
||||
.error () ->
|
||||
console.log "Adding emails", emails
|
||||
|
||||
do addNextMember = () ->
|
||||
if emails.length == 0 or !$scope.canAddCollaborators
|
||||
$scope.state.inflight = false
|
||||
$scope.state.error = "Sorry, something went wrong :("
|
||||
$scope.$apply()
|
||||
return
|
||||
|
||||
email = emails.shift()
|
||||
projectMembers
|
||||
.addMember(email, $scope.inputs.privileges)
|
||||
.success (data) ->
|
||||
if data?.user # data.user is false if collaborator limit is hit.
|
||||
$scope.project.members.push data.user
|
||||
setTimeout () ->
|
||||
# Give $scope a chance to update $scope.canAddCollaborators
|
||||
# with new collaborator information.
|
||||
addNextMember()
|
||||
, 0
|
||||
.error () ->
|
||||
$scope.state.inflight = false
|
||||
$scope.state.error = "Sorry, something went wrong :("
|
||||
|
||||
|
||||
$scope.removeMember = (member) ->
|
||||
@@ -85,7 +105,6 @@ define [
|
||||
|
||||
$scope.cancel = () ->
|
||||
$modalInstance.dismiss()
|
||||
]
|
||||
|
||||
App.controller "MakePublicModalController", ["$scope", "$modalInstance", "settings", ($scope, $modalInstance, settings) ->
|
||||
$scope.inputs = {
|
||||
|
||||
@@ -10,4 +10,5 @@ define [
|
||||
"libs/angular-sanitize-1.2.17"
|
||||
"libs/angular-cookie"
|
||||
"libs/passfield"
|
||||
"libs/ng-tags-input-3.0.0"
|
||||
], () ->
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,143 @@
|
||||
tags-input {
|
||||
display: block;
|
||||
}
|
||||
tags-input *, tags-input *:before, tags-input *:after {
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
tags-input .host {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
tags-input .host:active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
tags-input .tags {
|
||||
.form-control;
|
||||
-moz-appearance: textfield;
|
||||
-webkit-appearance: textfield;
|
||||
padding: 2px 5px;
|
||||
overflow: hidden;
|
||||
word-wrap: break-word;
|
||||
cursor: text;
|
||||
background-color: #fff;
|
||||
height: 100%;
|
||||
}
|
||||
tags-input .tags.focused {
|
||||
outline: none;
|
||||
-webkit-box-shadow: 0 0 3px 1px rgba(5, 139, 242, 0.6);
|
||||
-moz-box-shadow: 0 0 3px 1px rgba(5, 139, 242, 0.6);
|
||||
box-shadow: 0 0 3px 1px rgba(5, 139, 242, 0.6);
|
||||
}
|
||||
tags-input .tags .tag-list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
tags-input .tags .tag-item {
|
||||
margin: 2px;
|
||||
padding: 0 7px;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
height: 26px;
|
||||
line-height: 25px;
|
||||
border: 1px solid @gray-light;
|
||||
background-color: @gray-lightest;
|
||||
border-radius: 3px;
|
||||
}
|
||||
tags-input .tags .tag-item.selected {
|
||||
background: -webkit-linear-gradient(top, #febbbb 0%, #fe9090 45%, #ff5c5c 100%);
|
||||
background: linear-gradient(to bottom, #febbbb 0%, #fe9090 45%, #ff5c5c 100%);
|
||||
}
|
||||
tags-input .tags .tag-item .remove-button {
|
||||
color: @gray-light;
|
||||
text-decoration: none;
|
||||
}
|
||||
tags-input .tags .tag-item .remove-button:active {
|
||||
color: @brand-primary;
|
||||
}
|
||||
tags-input .tags .input {
|
||||
border: 0;
|
||||
outline: none;
|
||||
margin: 2px;
|
||||
padding: 0;
|
||||
padding-left: 5px;
|
||||
float: left;
|
||||
height: 26px;
|
||||
}
|
||||
tags-input .tags .input.invalid-tag {
|
||||
color: @brand-danger;
|
||||
}
|
||||
tags-input .tags .input::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
tags-input.ng-invalid .tags {
|
||||
-webkit-box-shadow: 0 0 3px 1px rgba(255, 0, 0, 0.6);
|
||||
-moz-box-shadow: 0 0 3px 1px rgba(255, 0, 0, 0.6);
|
||||
box-shadow: 0 0 3px 1px rgba(255, 0, 0, 0.6);
|
||||
}
|
||||
tags-input[disabled] .host:focus {
|
||||
outline: none;
|
||||
}
|
||||
tags-input[disabled] .tags {
|
||||
background-color: #eee;
|
||||
cursor: default;
|
||||
}
|
||||
tags-input[disabled] .tags .tag-item {
|
||||
opacity: 0.65;
|
||||
background: -webkit-linear-gradient(top, #f0f9ff 0%, rgba(203, 235, 255, 0.75) 47%, rgba(161, 219, 255, 0.62) 100%);
|
||||
background: linear-gradient(to bottom, #f0f9ff 0%, rgba(203, 235, 255, 0.75) 47%, rgba(161, 219, 255, 0.62) 100%);
|
||||
}
|
||||
tags-input[disabled] .tags .tag-item .remove-button {
|
||||
cursor: default;
|
||||
}
|
||||
tags-input[disabled] .tags .tag-item .remove-button:active {
|
||||
color: @brand-primary;
|
||||
}
|
||||
tags-input[disabled] .tags .input {
|
||||
background-color: #eee;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
tags-input .autocomplete {
|
||||
margin-top: 5px;
|
||||
position: absolute;
|
||||
padding: 5px 0;
|
||||
z-index: 999;
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
tags-input .autocomplete .suggestion-list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
max-height: 280px;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
}
|
||||
tags-input .autocomplete .suggestion-item {
|
||||
padding: 5px 10px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
tags-input .autocomplete .suggestion-item.selected {
|
||||
color: white;
|
||||
background-color: @brand-primary;
|
||||
.subdued {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
tags-input .autocomplete .suggestion-item em {
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=ng-tags-input.css.map */
|
||||
@@ -47,6 +47,9 @@
|
||||
@import "components/popovers.less";
|
||||
@import "components/carousel.less";
|
||||
|
||||
// ngTagsInput
|
||||
@import "components/tags-input.less";
|
||||
|
||||
// Utility classes
|
||||
@import "core/utilities.less";
|
||||
@import "core/responsive-utilities.less";
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
sinon = require('sinon')
|
||||
chai = require('chai')
|
||||
should = chai.should()
|
||||
assert = chai.assert
|
||||
expect = chai.expect
|
||||
modulePath = "../../../../app/js/Features/Contacts/ContactController.js"
|
||||
SandboxedModule = require('sandboxed-module')
|
||||
|
||||
describe "ContactController", ->
|
||||
beforeEach ->
|
||||
@ContactController = SandboxedModule.require modulePath, requires:
|
||||
"logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() }
|
||||
"../User/UserGetter": @UserGetter = {}
|
||||
"./ContactManager": @ContactManager = {}
|
||||
"../Authentication/AuthenticationController": @AuthenticationController = {}
|
||||
|
||||
@next = sinon.stub()
|
||||
@req = {}
|
||||
@res = {}
|
||||
@res.status = sinon.stub().returns @req
|
||||
@res.send = sinon.stub()
|
||||
|
||||
describe "getContacts", ->
|
||||
beforeEach ->
|
||||
@user_id = "mock-user-id"
|
||||
@contact_ids = ["contact-1", "contact-2", "contact-3"]
|
||||
@contacts = [
|
||||
{ _id: "contact-1", email: "joe@example.com", first_name: "Joe", last_name: "Example", unsued: "foo" }
|
||||
{ _id: "contact-2", email: "jane@example.com", first_name: "Jane", last_name: "Example", unsued: "foo", holdingAccount: true }
|
||||
{ _id: "contact-3", email: "jim@example.com", first_name: "Jim", last_name: "Example", unsued: "foo" }
|
||||
]
|
||||
@AuthenticationController.getLoggedInUserId = sinon.stub().callsArgWith(1, null, @user_id)
|
||||
@ContactManager.getContactIds = sinon.stub().callsArgWith(2, null, @contact_ids)
|
||||
@UserGetter.getUsers = sinon.stub().callsArgWith(2, null, @contacts)
|
||||
|
||||
@ContactController.getContacts @req, @res, @next
|
||||
|
||||
it "should look up the logged in user id", ->
|
||||
@AuthenticationController.getLoggedInUserId
|
||||
.calledWith(@req)
|
||||
.should.equal true
|
||||
|
||||
it "should get the users contact ids", ->
|
||||
@ContactManager.getContactIds
|
||||
.calledWith(@user_id, { limit: 50 })
|
||||
.should.equal true
|
||||
|
||||
it "should populate the users contacts ids", ->
|
||||
@UserGetter.getUsers
|
||||
.calledWith(@contact_ids, { email: 1, first_name: 1, last_name: 1, holdingAccount: 1 })
|
||||
.should.equal true
|
||||
|
||||
it "should return a formatted list of contacts in contact list order, without holding accounts", ->
|
||||
@res.send.args[0][0].contacts.should.deep.equal [
|
||||
{ id: "contact-1", email: "joe@example.com", first_name: "Joe", last_name: "Example" }
|
||||
{ id: "contact-3", email: "jim@example.com", first_name: "Jim", last_name: "Example" }
|
||||
]
|
||||
@@ -0,0 +1,91 @@
|
||||
chai = require('chai')
|
||||
chai.should()
|
||||
sinon = require("sinon")
|
||||
modulePath = "../../../../app/js/Features/Contacts/ContactManager"
|
||||
SandboxedModule = require('sandboxed-module')
|
||||
|
||||
describe "ContactManager", ->
|
||||
beforeEach ->
|
||||
@ContactManager = SandboxedModule.require modulePath, requires:
|
||||
"request" : @request = sinon.stub()
|
||||
"settings-sharelatex": @settings =
|
||||
apis:
|
||||
contacts:
|
||||
url: "contacts.sharelatex.com"
|
||||
"logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub(), err:->}
|
||||
|
||||
@user_id = "user-id-123"
|
||||
@contact_id = "contact-id-123"
|
||||
@callback = sinon.stub()
|
||||
|
||||
describe "getContacts", ->
|
||||
describe "with a successful response code", ->
|
||||
beforeEach ->
|
||||
@request.get = sinon.stub().callsArgWith(1, null, statusCode: 204, { contact_ids: @contact_ids = ["mock", "contact_ids"]})
|
||||
@ContactManager.getContactIds @user_id, { limit: 42 }, @callback
|
||||
|
||||
it "should get the contacts from the contacts api", ->
|
||||
@request.get
|
||||
.calledWith({
|
||||
url: "#{@settings.apis.contacts.url}/user/#{@user_id}/contacts"
|
||||
qs: { limit: 42 }
|
||||
json: true
|
||||
jar: false
|
||||
})
|
||||
.should.equal true
|
||||
|
||||
it "should call the callback with the contatcs", ->
|
||||
@callback.calledWith(null, @contact_ids).should.equal true
|
||||
|
||||
describe "with a failed response code", ->
|
||||
beforeEach ->
|
||||
@request.get = sinon.stub().callsArgWith(1, null, statusCode: 500, null)
|
||||
@ContactManager.getContactIds @user_id, { limit: 42 }, @callback
|
||||
|
||||
it "should call the callback with an error", ->
|
||||
@callback.calledWith(new Error("contacts api responded with non-success code: 500")).should.equal true
|
||||
|
||||
it "should log the error", ->
|
||||
@logger.error
|
||||
.calledWith({
|
||||
err: new Error("contacts api responded with a non-success code: 500")
|
||||
user_id: @user_id
|
||||
}, "error getting contacts for user")
|
||||
.should.equal true
|
||||
|
||||
describe "addContact", ->
|
||||
describe "with a successful response code", ->
|
||||
beforeEach ->
|
||||
@request.post = sinon.stub().callsArgWith(1, null, statusCode: 200, null)
|
||||
@ContactManager.addContact @user_id, @contact_id, @callback
|
||||
|
||||
it "should add the contacts for the user in the contacts api", ->
|
||||
@request.post
|
||||
.calledWith({
|
||||
url: "#{@settings.apis.contacts.url}/user/#{@user_id}/contacts"
|
||||
json: {
|
||||
contact_id: @contact_id
|
||||
}
|
||||
jar: false
|
||||
})
|
||||
.should.equal true
|
||||
|
||||
it "should call the callback", ->
|
||||
@callback.called.should.equal true
|
||||
|
||||
describe "with a failed response code", ->
|
||||
beforeEach ->
|
||||
@request.post = sinon.stub().callsArgWith(1, null, statusCode: 500, null)
|
||||
@ContactManager.addContact @user_id, @contact_id, @callback
|
||||
|
||||
it "should call the callback with an error", ->
|
||||
@callback.calledWith(new Error("contacts api responded with non-success code: 500")).should.equal true
|
||||
|
||||
it "should log the error", ->
|
||||
@logger.error
|
||||
.calledWith({
|
||||
err: new Error("contacts api responded with a non-success code: 500")
|
||||
user_id: @user_id
|
||||
contact_id: @contact_id
|
||||
}, "error adding contact for user")
|
||||
.should.equal true
|
||||
Reference in New Issue
Block a user