mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-15 01:55:29 +00:00
Merge branch 'master' into cmg-share-modal
This commit is contained in:
commit
6334066d2d
21 changed files with 291 additions and 63 deletions
|
@ -11,6 +11,7 @@ UserHandler = require("../User/UserHandler")
|
|||
UserSessionsManager = require("../User/UserSessionsManager")
|
||||
Analytics = require "../Analytics/AnalyticsManager"
|
||||
passport = require 'passport'
|
||||
NotificationsBuilder = require("../Notifications/NotificationsBuilder")
|
||||
|
||||
module.exports = AuthenticationController =
|
||||
|
||||
|
@ -112,6 +113,7 @@ module.exports = AuthenticationController =
|
|||
UserHandler.setupLoginData(user, ()->)
|
||||
LoginRateLimiter.recordSuccessfulLogin(user.email)
|
||||
AuthenticationController._recordSuccessfulLogin(user._id)
|
||||
AuthenticationController.ipMatchCheck(req, user)
|
||||
Analytics.recordEvent(user._id, "user-logged-in", {ip:req.ip})
|
||||
Analytics.identifyUser(user._id, req.sessionID)
|
||||
logger.log email: user.email, user_id: user._id.toString(), "successful log in"
|
||||
|
@ -119,6 +121,13 @@ module.exports = AuthenticationController =
|
|||
# capture the request ip for use when creating the session
|
||||
user._login_req_ip = req.ip
|
||||
|
||||
ipMatchCheck: (req, user) ->
|
||||
if req.ip != user.lastLoginIp
|
||||
NotificationsBuilder.ipMatcherAffiliation(user._id, req.ip).create()
|
||||
UserUpdater.updateUser user._id.toString(), {
|
||||
$set: { "lastLoginIp": req.ip }
|
||||
}
|
||||
|
||||
setInSessionUser: (req, props) ->
|
||||
for key, value of props
|
||||
if req?.session?.passport?.user?
|
||||
|
|
|
@ -31,56 +31,56 @@ UnsupportedFileTypeError = (message) ->
|
|||
error.name = "UnsupportedFileTypeError"
|
||||
error.__proto__ = UnsupportedFileTypeError.prototype
|
||||
return error
|
||||
UnsupportedFileTypeError.prototype.__proto___ = Error.prototype
|
||||
UnsupportedFileTypeError.prototype.__proto__ = Error.prototype
|
||||
|
||||
UnsupportedBrandError = (message) ->
|
||||
error = new Error(message)
|
||||
error.name = "UnsupportedBrandError"
|
||||
error.__proto__ = UnsupportedBrandError.prototype
|
||||
return error
|
||||
UnsupportedBrandError.prototype.__proto___ = Error.prototype
|
||||
UnsupportedBrandError.prototype.__proto__ = Error.prototype
|
||||
|
||||
UnsupportedExportRecordsError = (message) ->
|
||||
error = new Error(message)
|
||||
error.name = "UnsupportedExportRecordsError"
|
||||
error.__proto__ = UnsupportedExportRecordsError.prototype
|
||||
return error
|
||||
UnsupportedExportRecordsError.prototype.__proto___ = Error.prototype
|
||||
UnsupportedExportRecordsError.prototype.__proto__ = Error.prototype
|
||||
|
||||
V1HistoryNotSyncedError = (message) ->
|
||||
error = new Error(message)
|
||||
error.name = "V1HistoryNotSyncedError"
|
||||
error.__proto__ = V1HistoryNotSyncedError.prototype
|
||||
return error
|
||||
V1HistoryNotSyncedError.prototype.__proto___ = Error.prototype
|
||||
V1HistoryNotSyncedError.prototype.__proto__ = Error.prototype
|
||||
|
||||
ProjectHistoryDisabledError = (message) ->
|
||||
error = new Error(message)
|
||||
error.name = "ProjectHistoryDisabledError"
|
||||
error.__proto__ = ProjectHistoryDisabledError.prototype
|
||||
return error
|
||||
ProjectHistoryDisabledError.prototype.__proto___ = Error.prototype
|
||||
ProjectHistoryDisabledError.prototype.__proto__ = Error.prototype
|
||||
|
||||
V1ConnectionError = (message) ->
|
||||
error = new Error(message)
|
||||
error.name = "V1ConnectionError"
|
||||
error.__proto__ = V1ConnectionError.prototype
|
||||
return error
|
||||
V1ConnectionError.prototype.__proto___ = Error.prototype
|
||||
V1ConnectionError.prototype.__proto__ = Error.prototype
|
||||
|
||||
UnconfirmedEmailError = (message) ->
|
||||
error = new Error(message)
|
||||
error.name = "UnconfirmedEmailError"
|
||||
error.__proto__ = UnconfirmedEmailError.prototype
|
||||
return error
|
||||
UnconfirmedEmailError.prototype.__proto___ = Error.prototype
|
||||
UnconfirmedEmailError.prototype.__proto__ = Error.prototype
|
||||
|
||||
EmailExistsError = (message) ->
|
||||
error = new Error(message)
|
||||
error.name = "EmailExistsError"
|
||||
error.__proto__ = EmailExistsError.prototype
|
||||
return error
|
||||
EmailExistsError.prototype.__proto___ = Error.prototype
|
||||
EmailExistsError.prototype.__proto__ = Error.prototype
|
||||
|
||||
module.exports = Errors =
|
||||
NotFoundError: NotFoundError
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
logger = require("logger-sharelatex")
|
||||
NotificationsHandler = require("./NotificationsHandler")
|
||||
request = require "request"
|
||||
settings = require "settings-sharelatex"
|
||||
|
||||
module.exports =
|
||||
|
||||
|
@ -29,3 +31,29 @@ module.exports =
|
|||
NotificationsHandler.createNotification user._id, @key, "notification_project_invite", messageOpts, invite.expires, callback
|
||||
read: (callback=()->) ->
|
||||
NotificationsHandler.markAsReadByKeyOnly @key, callback
|
||||
|
||||
ipMatcherAffiliation: (userId, ip) ->
|
||||
key: "ip-matched-affiliation-#{ip}"
|
||||
create: (callback=()->) ->
|
||||
return null unless settings?.apis?.v1?.url # service is not configured
|
||||
_key = @key
|
||||
request {
|
||||
method: 'GET'
|
||||
url: "#{settings.apis.v1.url}/api/v2/users/#{userId}/ip_matcher"
|
||||
auth: { user: settings.apis.v1.user, pass: settings.apis.v1.pass }
|
||||
body: { ip: ip }
|
||||
json: true
|
||||
timeout: 20 * 1000
|
||||
}, (error, response, body) ->
|
||||
return error if error?
|
||||
return null unless response.statusCode == 200
|
||||
|
||||
messageOpts =
|
||||
university_id: body.id
|
||||
university_name: body.name
|
||||
content: body.enrolment_ad_html
|
||||
logger.log user_id:userId, key:_key, "creating notification key for user"
|
||||
NotificationsHandler.createNotification userId, _key, "notification_ip_matched_affiliation", messageOpts, null, false, callback
|
||||
|
||||
read: (callback = ->)->
|
||||
NotificationsHandler.markAsReadWithKey userId, @key, callback
|
||||
|
|
|
@ -29,12 +29,15 @@ module.exports =
|
|||
unreadNotifications = []
|
||||
callback(null, unreadNotifications)
|
||||
|
||||
createNotification: (user_id, key, templateKey, messageOpts, expiryDateTime, callback)->
|
||||
createNotification: (user_id, key, templateKey, messageOpts, expiryDateTime, forceCreate, callback)->
|
||||
if !callback
|
||||
callback = forceCreate
|
||||
forceCreate = true
|
||||
payload = {
|
||||
key:key
|
||||
messageOpts:messageOpts
|
||||
templateKey:templateKey
|
||||
forceCreate: true
|
||||
forceCreate:forceCreate
|
||||
}
|
||||
if expiryDateTime?
|
||||
payload.expires = expiryDateTime
|
||||
|
|
|
@ -26,6 +26,8 @@ TokenAccessHandler = require '../TokenAccess/TokenAccessHandler'
|
|||
CollaboratorsHandler = require '../Collaborators/CollaboratorsHandler'
|
||||
Modules = require '../../infrastructure/Modules'
|
||||
ProjectEntityHandler = require './ProjectEntityHandler'
|
||||
UserGetter = require("../User/UserGetter")
|
||||
NotificationsBuilder = require("../Notifications/NotificationsBuilder")
|
||||
crypto = require 'crypto'
|
||||
{ V1ConnectionError } = require '../Errors/Errors'
|
||||
Features = require('../../infrastructure/Features')
|
||||
|
@ -209,6 +211,11 @@ module.exports = ProjectController =
|
|||
user = results.user
|
||||
warnings = ProjectController._buildWarningsList results.v1Projects
|
||||
|
||||
# in v2 add notifications for matching university IPs
|
||||
if Settings.overleaf?
|
||||
UserGetter.getUser user_id, { 'lastLoginIp': 1 }, (error, user) ->
|
||||
if req.ip != user.lastLoginIp
|
||||
NotificationsBuilder.ipMatcherAffiliation(user._id, req.ip).create()
|
||||
|
||||
ProjectController._injectProjectOwners projects, (error, projects) ->
|
||||
return next(error) if error?
|
||||
|
|
|
@ -7,14 +7,20 @@ module.exports = ProxyManager =
|
|||
apply: (publicApiRouter) ->
|
||||
for proxyUrl, target of settings.proxyUrls
|
||||
do (target) ->
|
||||
publicApiRouter.get proxyUrl, ProxyManager.createProxy(target)
|
||||
method = target.options?.method || 'get'
|
||||
publicApiRouter[method] proxyUrl, ProxyManager.createProxy(target)
|
||||
|
||||
createProxy: (target) ->
|
||||
(req, res, next) ->
|
||||
targetUrl = makeTargetUrl(target, req)
|
||||
logger.log targetUrl: targetUrl, reqUrl: req.url, "proxying url"
|
||||
|
||||
upstream = request(targetUrl)
|
||||
options =
|
||||
url: targetUrl
|
||||
options.headers = { Cookie: req.headers.cookie } if req.headers?.cookie
|
||||
Object.assign(options, target.options) if target?.options?
|
||||
options.form = req.body if options.method in ['post', 'put']
|
||||
upstream = request(options)
|
||||
upstream.on "error", (error) ->
|
||||
logger.error err: error, "error in ProxyManager"
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ UserSchema = new Schema
|
|||
confirmed : {type : Boolean, default : false}
|
||||
signUpDate : {type : Date, default: () -> new Date() }
|
||||
lastLoggedIn : {type : Date}
|
||||
lastLoginIp : {type : String, default : ''}
|
||||
loginCount : {type : Number, default: 0}
|
||||
holdingAccount : {type : Boolean, default: false}
|
||||
ace : {
|
||||
|
|
|
@ -60,6 +60,21 @@ span(ng-controller="NotificationsController").userNotifications
|
|||
button(ng-click="dismiss(notification)").close.pull-right
|
||||
span(aria-hidden="true") ×
|
||||
span.sr-only #{translate("close")}
|
||||
.alert.alert-info(ng-switch-when="notification_ip_matched_affiliation")
|
||||
div.notification_inner
|
||||
.notification_body
|
||||
| It looks like you're at
|
||||
strong {{ notification.messageOpts.university_name }}! <br/>
|
||||
| Did you know that {{notification.messageOpts.university_name}} is providing
|
||||
strong free Overleaf Professional accounts
|
||||
| to everyone at {{notification.messageOpts.university_name}}? <br/>
|
||||
| Add an institutional email address to claim your account.
|
||||
a.pull-right.btn.btn-sm.btn-info(href="/user/settings")
|
||||
| Add Affiliation
|
||||
span().notification_close
|
||||
button(ng-click="dismiss(notification)").close.pull-right
|
||||
span(aria-hidden="true") ×
|
||||
span.sr-only #{translate("close")}
|
||||
.alert.alert-info(ng-switch-default)
|
||||
div.notification_inner
|
||||
span(ng-bind-html="notification.html").notification_body
|
||||
|
|
|
@ -516,6 +516,7 @@ define [
|
|||
|
||||
scope.$on '$destroy', () ->
|
||||
if scope.sharejsDoc?
|
||||
scope.$broadcast('changeEditor')
|
||||
tearDownSpellCheck()
|
||||
tearDownCursorPosition()
|
||||
detachFromAce(scope.sharejsDoc)
|
||||
|
|
|
@ -3,12 +3,12 @@ define [], () ->
|
|||
constructor: (@$scope, @adapter, @localStorage) ->
|
||||
@$scope.$on 'editorInit', @jumpToPositionInNewDoc
|
||||
|
||||
@$scope.$on 'beforeChangeDocument', () =>
|
||||
@storeCursorPosition()
|
||||
@storeFirstVisibleLine()
|
||||
@$scope.$on 'beforeChangeDocument', @storePositionAndLine
|
||||
|
||||
@$scope.$on 'afterChangeDocument', @jumpToPositionInNewDoc
|
||||
|
||||
@$scope.$on 'changeEditor', @storePositionAndLine
|
||||
|
||||
@$scope.$on "#{@$scope.name}:gotoLine", (e, line, column) =>
|
||||
if line?
|
||||
setTimeout () =>
|
||||
|
@ -24,6 +24,10 @@ define [], () ->
|
|||
@$scope.$on "#{@$scope.name}:clearSelection", (e) =>
|
||||
@adapter.clearSelection()
|
||||
|
||||
storePositionAndLine: () =>
|
||||
@storeCursorPosition()
|
||||
@storeFirstVisibleLine()
|
||||
|
||||
jumpToPositionInNewDoc: () =>
|
||||
@doc_id = @$scope.sharejsDoc?.doc_id
|
||||
setTimeout () =>
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
including About and Blog
|
||||
*/
|
||||
.cms-page {
|
||||
img {
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
.btn-description {
|
||||
margin-right: @margin-sm;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,32 @@
|
|||
.modal-body-publish {
|
||||
.form-control-box {
|
||||
margin-bottom: 1.5ex;
|
||||
margin-left: 1.0em;
|
||||
label {
|
||||
display: inline-block;
|
||||
width: 10em;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
.form-control {
|
||||
display: inline-block;
|
||||
width: 60%;
|
||||
}
|
||||
input[type="checkbox"] {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
textarea {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
select {
|
||||
padding-top: 1ex;
|
||||
padding-bottom: 1ex;
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
option {
|
||||
margin-left: -4px;
|
||||
}
|
||||
}
|
||||
#search-input-container {
|
||||
overflow: hidden;
|
||||
margin: 5px 0 10px;
|
||||
|
|
|
@ -100,52 +100,10 @@
|
|||
}
|
||||
// End Print
|
||||
|
||||
/*
|
||||
Begin Tabs
|
||||
*/
|
||||
.nav-tabs {
|
||||
// Overrides for nav.less
|
||||
background-color: @ol-blue-gray-0;
|
||||
border: 0!important;
|
||||
margin-bottom: @margin-md;
|
||||
margin-top: -@line-height-computed; //- adjusted for portal-name
|
||||
padding: @padding-lg 0 @padding-md;
|
||||
text-align: center;
|
||||
|
||||
a {
|
||||
color: @link-color;
|
||||
&:hover {
|
||||
background-color: transparent!important;
|
||||
border: 0!important;
|
||||
color: @link-hover-color!important;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
float: none;
|
||||
a {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
li.active > a {
|
||||
background-color: transparent!important;
|
||||
border: 0;
|
||||
border-bottom: 1px solid @accent-color-secondary!important;
|
||||
color: @accent-color-secondary;
|
||||
&:hover {
|
||||
color: @accent-color-secondary!important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tab-content:extend(.container) {
|
||||
background-color: transparent!important;
|
||||
border: none!important;
|
||||
}
|
||||
// End Tabs
|
||||
|
||||
@media (max-width: @screen-size-sm-max) {
|
||||
.content-pull {
|
||||
padding: 0;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
}
|
||||
|
||||
img {
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
|
|
43
services/web/public/stylesheets/components/tabs.less
Normal file
43
services/web/public/stylesheets/components/tabs.less
Normal file
|
@ -0,0 +1,43 @@
|
|||
.ol-tabs {
|
||||
// Overrides for nav.less
|
||||
.nav-tabs {
|
||||
border: 0!important;
|
||||
margin-bottom: @margin-md;
|
||||
margin-top: -@line-height-computed; //- adjusted for portal-name
|
||||
padding: @padding-lg 0 @padding-md;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
a {
|
||||
color: @link-color;
|
||||
&:hover {
|
||||
background-color: transparent!important;
|
||||
border: 0!important;
|
||||
color: @link-hover-color!important;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
float: none;
|
||||
a {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
li.active > a {
|
||||
background-color: transparent!important;
|
||||
border: 0!important;
|
||||
border-bottom: 1px solid @accent-color-secondary!important;
|
||||
color: @accent-color-secondary!important;
|
||||
&:hover {
|
||||
color: @accent-color-secondary!important;
|
||||
}
|
||||
}
|
||||
.tab-content:extend(.container) {
|
||||
background-color: transparent!important;
|
||||
border: none!important;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -10,6 +10,7 @@
|
|||
@import "components/icons.less";
|
||||
@import "components/navs-ol.less";
|
||||
@import "components/pagination.less";
|
||||
@import "components/tabs.less";
|
||||
|
||||
// Pages
|
||||
@import "app/about.less";
|
||||
|
|
|
@ -80,6 +80,9 @@ class User
|
|||
update["features.#{key}"] = value
|
||||
UserModel.update { _id: @id }, update, callback
|
||||
|
||||
setOverleafId: (overleaf_id, callback = (error) ->) ->
|
||||
UserModel.update { _id: @id }, { 'overleaf.id': overleaf_id }, callback
|
||||
|
||||
logout: (callback = (error) ->) ->
|
||||
@getCsrfToken (error) =>
|
||||
return callback(error) if error?
|
||||
|
|
|
@ -15,7 +15,7 @@ describe "AuthenticationController", ->
|
|||
tk.freeze(Date.now())
|
||||
@AuthenticationController = SandboxedModule.require modulePath, requires:
|
||||
"./AuthenticationManager": @AuthenticationManager = {}
|
||||
"../User/UserUpdater" : @UserUpdater = {}
|
||||
"../User/UserUpdater" : @UserUpdater = {updateUser:sinon.stub()}
|
||||
"metrics-sharelatex": @Metrics = { inc: sinon.stub() }
|
||||
"../Security/LoginRateLimiter": @LoginRateLimiter = { processLoginRequest:sinon.stub(), recordSuccessfulLogin:sinon.stub() }
|
||||
"../User/UserHandler": @UserHandler = {setupLoginData:sinon.stub()}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
SandboxedModule = require('sandboxed-module')
|
||||
assert = require('chai').assert
|
||||
require('chai').should()
|
||||
sinon = require('sinon')
|
||||
modulePath = require('path').join __dirname, '../../../../app/js/Features/Notifications/NotificationsBuilder.js'
|
||||
|
||||
describe 'NotificationsBuilder', ->
|
||||
user_id = "123nd3ijdks"
|
||||
|
||||
beforeEach ->
|
||||
@handler =
|
||||
createNotification: sinon.stub().callsArgWith(6)
|
||||
|
||||
@settings = apis: { v1: { url: 'v1.url', user: '', pass: '' } }
|
||||
@body = {id: 1, name: 'stanford', enrolment_ad_html: 'v1 ad content'}
|
||||
response = {statusCode: 200}
|
||||
@request = sinon.stub().returns(@stubResponse).callsArgWith(1, null, response, @body)
|
||||
@controller = SandboxedModule.require modulePath, requires:
|
||||
"./NotificationsHandler":@handler
|
||||
"settings-sharelatex":@settings
|
||||
'request': @request
|
||||
"logger-sharelatex":
|
||||
log:->
|
||||
err:->
|
||||
|
||||
it 'should call v1 and create affiliation notifications', (done)->
|
||||
ip = '192.168.0.1'
|
||||
@controller.ipMatcherAffiliation(user_id, ip).create (callback)=>
|
||||
@request.calledOnce.should.equal true
|
||||
expectedOpts =
|
||||
university_id: @body.id
|
||||
university_name: @body.name
|
||||
content: @body.enrolment_ad_html
|
||||
@handler.createNotification.calledWith(
|
||||
user_id,
|
||||
"ip-matched-affiliation-#{ip}",
|
||||
"notification_ip_matched_affiliation",
|
||||
expectedOpts
|
||||
).should.equal true
|
||||
done()
|
|
@ -69,6 +69,10 @@ describe "ProjectController", ->
|
|||
@CollaboratorsHandler =
|
||||
userIsTokenMember: sinon.stub().callsArgWith(2, null, false)
|
||||
@ProjectEntityHandler = {}
|
||||
@NotificationBuilder =
|
||||
ipMatcherAffiliation: sinon.stub().returns({create: sinon.stub()})
|
||||
@UserGetter =
|
||||
getUser: sinon.stub().callsArgWith 2, null, {lastLoginIp: '192.170.18.2'}
|
||||
@Modules =
|
||||
hooks:
|
||||
fire: sinon.stub()
|
||||
|
@ -105,11 +109,16 @@ describe "ProjectController", ->
|
|||
"./ProjectEntityHandler": @ProjectEntityHandler
|
||||
"../Errors/Errors": Errors
|
||||
"../../infrastructure/Features": @Features
|
||||
"../Notifications/NotificationsBuilder":@NotificationBuilder
|
||||
"../User/UserGetter": @UserGetter
|
||||
|
||||
@projectName = "£12321jkj9ujkljds"
|
||||
@req =
|
||||
params:
|
||||
Project_id: @project_id
|
||||
headers: {}
|
||||
connection:
|
||||
remoteAddress: "192.170.18.1"
|
||||
session:
|
||||
user: @user
|
||||
body:
|
||||
|
@ -301,6 +310,13 @@ describe "ProjectController", ->
|
|||
done()
|
||||
@ProjectController.projectListPage @req, @res
|
||||
|
||||
it "should create trigger ip matcher notifications", (done)->
|
||||
@settings.overleaf = true
|
||||
@res.render = (pageName, opts)=>
|
||||
@NotificationBuilder.ipMatcherAffiliation.called.should.equal true
|
||||
done()
|
||||
@ProjectController.projectListPage @req, @res
|
||||
|
||||
it "should send the projects", (done)->
|
||||
@res.render = (pageName, opts)=>
|
||||
opts.projects.length.should.equal (@projects.length + @collabertions.length + @readOnly.length + @tokenReadAndWrite.length + @tokenReadOnly.length)
|
||||
|
|
|
@ -35,11 +35,26 @@ describe "ProxyManager", ->
|
|||
assertCalledWith(@router.get, '/foo/bar')
|
||||
assertCalledWith(@router.get, '/foo/:id')
|
||||
|
||||
it 'applies methods other than get', ->
|
||||
@router =
|
||||
post: sinon.stub()
|
||||
put: sinon.stub()
|
||||
@settings.proxyUrls =
|
||||
'/foo/bar': {options: {method: 'post'}}
|
||||
'/foo/:id': {options: {method: 'put'}}
|
||||
@proxyManager.apply(@router)
|
||||
sinon.assert.calledOnce(@router.post)
|
||||
sinon.assert.calledOnce(@router.put)
|
||||
assertCalledWith(@router.post, '/foo/bar')
|
||||
assertCalledWith(@router.put, '/foo/:id')
|
||||
|
||||
describe 'createProxy', ->
|
||||
beforeEach ->
|
||||
@req.url = @proxyPath
|
||||
@req.route.path = @proxyPath
|
||||
@req.query = {}
|
||||
@req.params = {}
|
||||
@req.headers = {}
|
||||
@settings.proxyUrls = {}
|
||||
|
||||
afterEach ->
|
||||
|
@ -57,7 +72,7 @@ describe "ProxyManager", ->
|
|||
targetUrl = 'https://user:pass@foo.bar:123/pa/th.ext?query#hash'
|
||||
@settings.proxyUrls[@proxyPath] = targetUrl
|
||||
@proxyManager.createProxy(targetUrl)(@req)
|
||||
assertCalledWith(@request, targetUrl)
|
||||
assertCalledWith(@request, {url: targetUrl})
|
||||
|
||||
it 'overwrite query', ->
|
||||
targetUrl = 'foo.bar/baz?query'
|
||||
|
@ -65,19 +80,19 @@ describe "ProxyManager", ->
|
|||
@settings.proxyUrls[@proxyPath] = targetUrl
|
||||
@proxyManager.createProxy(targetUrl)(@req)
|
||||
newTargetUrl = 'foo.bar/baz?requestQuery=important'
|
||||
assertCalledWith(@request, newTargetUrl)
|
||||
assertCalledWith(@request, {url: newTargetUrl})
|
||||
|
||||
it 'handles target objects', ->
|
||||
target = { baseUrl: 'api.v1', path: '/pa/th'}
|
||||
@settings.proxyUrls[@proxyPath] = target
|
||||
@proxyManager.createProxy(target)(@req, @res, @next)
|
||||
assertCalledWith(@request, 'api.v1/pa/th')
|
||||
assertCalledWith(@request, {url: 'api.v1/pa/th'})
|
||||
|
||||
it 'handles missing baseUrl', ->
|
||||
target = { path: '/pa/th'}
|
||||
@settings.proxyUrls[@proxyPath] = target
|
||||
@proxyManager.createProxy(target)(@req, @res, @next)
|
||||
assertCalledWith(@request, 'undefined/pa/th')
|
||||
assertCalledWith(@request, {url: 'undefined/pa/th'})
|
||||
|
||||
it 'handles dynamic path', ->
|
||||
target = baseUrl: 'api.v1', path: (params) -> "/resource/#{params.id}"
|
||||
|
@ -86,4 +101,49 @@ describe "ProxyManager", ->
|
|||
@req.route.path = '/res/:id'
|
||||
@req.params = id: 123
|
||||
@proxyManager.createProxy(target)(@req, @res, @next)
|
||||
assertCalledWith(@request, 'api.v1/resource/123')
|
||||
assertCalledWith(@request, {url: 'api.v1/resource/123'})
|
||||
|
||||
it 'set arbitrary options on request', ->
|
||||
target = baseUrl: 'api.v1', path: '/foo', options: foo: 'bar'
|
||||
@req.url = '/foo'
|
||||
@req.route.path = '/foo'
|
||||
@proxyManager.createProxy(target)(@req, @res, @next)
|
||||
assertCalledWith(@request,
|
||||
foo: 'bar'
|
||||
url: 'api.v1/foo'
|
||||
)
|
||||
|
||||
it 'passes cookies', ->
|
||||
target = baseUrl: 'api.v1', path: '/foo'
|
||||
@req.url = '/foo'
|
||||
@req.route.path = '/foo'
|
||||
@req.headers = cookie: 'cookie'
|
||||
@proxyManager.createProxy(target)(@req, @res, @next)
|
||||
assertCalledWith(@request,
|
||||
headers: Cookie: 'cookie'
|
||||
url: 'api.v1/foo'
|
||||
)
|
||||
|
||||
it 'passes body for post', ->
|
||||
target = baseUrl: 'api.v1', path: '/foo', options: method: 'post'
|
||||
@req.url = '/foo'
|
||||
@req.route.path = '/foo'
|
||||
@req.body = foo: 'bar'
|
||||
@proxyManager.createProxy(target)(@req, @res, @next)
|
||||
assertCalledWith(@request,
|
||||
form: foo: 'bar'
|
||||
method: 'post'
|
||||
url: 'api.v1/foo'
|
||||
)
|
||||
|
||||
it 'passes body for put', ->
|
||||
target = baseUrl: 'api.v1', path: '/foo', options: method: 'put'
|
||||
@req.url = '/foo'
|
||||
@req.route.path = '/foo'
|
||||
@req.body = foo: 'bar'
|
||||
@proxyManager.createProxy(target)(@req, @res, @next)
|
||||
assertCalledWith(@request,
|
||||
form: foo: 'bar'
|
||||
method: 'put'
|
||||
url: 'api.v1/foo'
|
||||
)
|
Loading…
Add table
Reference in a new issue