blob: 8d46952e93e0ce3784b43bc5d8d198232568bf3a [file] [log] [blame]
'use strict'
var urljoin = require('url-join'),
config = require('./config'),
helpers = require('./'),
UsergridQuery = require('../lib/query'),
UsergridEntity = require('../lib/entity'),
UsergridAuth = require('../lib/auth'),
util = require('util'),
version = require('../package.json').version,
ok = require('objectkit'),
_ = require('lodash')
module.exports = {
uri: function(client, options) {
return urljoin(
client.baseUrl,
client.orgId,
client.appId,
options.path || options.type,
_.isString(options.uuidOrName) ? options.uuidOrName : ""
)
},
headers: function(client, auth) {
var headers = {
'User-Agent': util.format("usergrid-nodejs/v%s", version)
}
if (ok(auth).getIfExists('isValid')) {
// checks if an auth param was passed to the request and uses the token if applicable
_.assign(headers, {
authorization: util.format("Bearer %s", auth.token)
})
} else if (ok(client).getIfExists('authFallback') === UsergridAuth.AuthFallback.APP && ok(client).getIfExists('appAuth.isValid')) {
// if auth-fallback is set to APP, this request will make a call using the application token
_.assign(headers, {
authorization: util.format("Bearer %s", client.appAuth.token)
})
} else if (ok(client).getIfExists('currentUser.auth.isValid')) {
// defaults to using the current user's token
_.assign(headers, {
authorization: util.format("Bearer %s", client.currentUser.auth.token)
})
}
return headers
},
userLoginBody: function(options) {
var body = {
grant_type: 'password',
password: options.password
}
if (options.tokenTtl) {
body.ttl = options.tokenTtl
}
body[(options.username) ? "username" : "email"] = (options.username) ? options.username : options.email
return body
},
appLoginBody: function(options) {
var body = {
grant_type: 'client_credentials',
client_id: options.clientId,
client_secret: options.clientSecret
}
if (options.tokenTtl) {
body.ttl = options.tokenTtl
}
return body
},
GET: function(client, args) {
/* GET supports the following constructor patterns:
client.GET('type', 'uuidOrName', optionalCallback)
client.GET('type', optionalCallback)
client.GET(query, optionalCallback)
client.GET({
query: query, // takes precedence
type: type, // required if query not defined
uuid: uuid, // will be set to nameOrUuid on init (priority)
name: name, // will be set to nameOrUuid on init (if no uuid specified)
nameOrUuid: nameOrUuid // the definitive key for name or uuid
}, optionalCallback)
*/
var options = {
client: client,
method: 'GET',
callback: helpers.cb(args)
}
// if a preformatted options argument passed, assign it to options
if (_.isObject(args[0]) && !_.isFunction(args[0]) && args.length <= 2) {
_.assign(options, args[0])
}
options.type = _.first([options.type, ok(args).getIfExists('0._type'), args[0]].filter(_.isString))
options.query = _.first([options.query, args[0]].filter(function(property) {
return (property instanceof UsergridQuery)
}))
options.uuidOrName = _.last([options.uuidOrName, options.uuid, options.name, args[1]].filter(function(property) {
return (property)
}))
options.entity = _.first([options.entity, args[0]].filter(function(property) {
return (property instanceof UsergridEntity)
}))
return options
},
PUT: function(client, args) {
/* PUT supports the following constructor patterns:
client.PUT('type', 'uuidOrName', bodyObject, optionalCallback)
client.PUT('type', bodyObject, optionalCallback) // if no uuid, will create a new record
client.PUT(bodyObjectOrEntity, optionalCallback) // if no uuid, will create a new record; must include type
client.PUT(query, bodyObjectOrEntity, optionalCallback) // will update all entities matching query
client.PUT(entity, optionalCallback)
client.PUT({
*entity = alias to body*
query: query, // takes precedence over type/body
type: type, // required if query not defined
body: bodyObject or bodyObjectOrEntity, // if includes type, type will be inferred from body
*uuid, name* = alias to nameOrUuid*
nameOrUuid: nameOrUuid // the definitive key for name or uuid
}, optionalCallback)
*/
var options = {
client: client,
method: 'PUT',
callback: helpers.cb(args)
}
// if a preformatted options argument passed, assign it to options
if (_.isObject(args[0]) && !_.isFunction(args[0]) && args.length <= 2) {
_.assign(options, args[0])
}
options.body = _.first([options.entity, options.body, args[2], args[1], args[0]].filter(function(property) {
return _.isObject(property) && !_.isFunction(property) && !(property instanceof UsergridQuery)
}))
if (typeof options.body !== 'object') {
throw new Error('"body" parameter is required when making a PUT request')
}
options.uuidOrName = _.first([options.nameOrUuid, options.uuid, options.name, options.body.uuid, args[2], args[1], args[0]].filter(_.isString))
options.type = _.first([options.type, args[0]._type, options.body.type, args[0]].filter(_.isString))
options.query = _.first([options.query, args[0]].filter(function(property) {
return (property instanceof UsergridQuery)
}))
options.entity = _.first([options.entity, args[0]].filter(function(property) {
return (property instanceof UsergridEntity)
}))
return options
},
POST: function(client, args) {
/* POST supports the following constructor patterns:
client.POST('type', bodyObjectOrArray, optionalCallback)
client.POST(bodyObjectOrArray, optionalCallback) // must include type in body
client.POST(entityOrEntities, optionalCallback)
client.POST({
*entity, entities = alias to body*
type: type, // required
body: bodyObjectOrArray or entityOrEntities, // if the first entity includes type, type will be inferred from body
}, optionalCallback)
*/
var options = {
client: client,
method: 'POST',
callback: helpers.cb(args)
}
// if a preformatted options argument passed, assign it to options
if (_.isObject(args[0]) && !_.isFunction(args[0]) && args.length <= 2) {
_.assign(options, args[0])
}
options.body = _.first([options.entities, options.entity, options.body, args[1], args[0]].filter(function(property) {
return _.isArray(property) && _.isObject(property[0]) && !_.isFunction(property[0]) || _.isObject(property) && !_.isFunction(property)
}))
if (typeof options.body !== 'object') {
throw new Error('"body" parameter is required when making a POST request')
}
options.type = _.first([options.type, args[0], ok(options).getIfExists('body.0.type'), options.body.type].filter(_.isString))
return options
},
DELETE: function(client, args) {
/* DELETE supports the following constructor patterns:
client.DELETE('type', 'uuidOrName', optionalCallback)
client.DELETE(entity, optionalCallback) // must include type in body
client.DELETE(query, optionalCallback)
client.DELETE({
*uuid, name* = alias to nameOrUuid*
uuidOrName: uuidOrName,
type: type, // required if query not defined
query: query // takes precedence over type/uuid
}, optionalCallback)
*/
var options = {
client: client,
method: 'DELETE',
callback: helpers.cb(args)
}
// if a preformatted options argument passed, assign it to options
if (_.isObject(args[0]) && !_.isFunction(args[0]) && args.length <= 2) {
_.assign(options, args[0])
}
options.type = _.first([options.type, ok(options).getIfExists('entity.type'), args[0]._type, args[0]].filter(_.isString))
options.entity = _.first([options.entity, args[0]].filter(function(property) {
return (property instanceof UsergridEntity)
}))
options.query = _.first([options.query, args[0]].filter(function(property) {
return (property instanceof UsergridQuery)
}))
options.uuidOrName = _.first([options.uuidOrName, options.uuid, options.name, ok(options).getIfExists('entity.uuid'), args[1]].filter(_.isString))
if (!_.isString(options.uuidOrName) && options.query === undefined) {
throw new Error('"uuidOrName" parameter or "query" is required when making a DELETE request')
}
return options
},
connection: function(client, method, args) {
/* connect supports the following constructor patterns:
client.connect(entity, "relationship", toEntity);
// POST entity.type/entity.uuid/"relationship"/toEntity.uuid
client.connect("type", <uuidOrName>, "relationship", <toUuid>);
// POST type/uuidOrName/relationship/toUuid
client.connect("type", <uuidOrName>, "relationship", "toType", "toName");
// POST type/uuidOrName/relationship/toType/toName
client.connect({
entity: { // or UsergridEntity
type: "type",
uuidOrName: <uuidOrName>
},
relationship: "likes",
to: { // or UsergridEntity
"type": "(required if not using uuid)",
"uuidOrName": <uuidOrName>,
"name": "alias to uuidOrName" // if uuid not specified, requires "type"
"uuid": "alias to uuidOrName"
}
);
disconnect supports the identical patters, but uses DELETE instead of POST; it is therefore a reference to this function
*/
var options = {
client: client,
method: method,
entity: {},
to: {},
callback: helpers.cb(args)
}
// if a preformatted options argument passed, assign it to options
if (_.isObject(args[0]) && !_.isFunction(args[0]) && args.length <= 2) {
_.assign(options, args[0])
}
// handle DELETE using "from" preposition
if (_.isObject(options.from)) {
options.to = options.from
}
// if an entity object or UsergridEntity instance is the first argument (source)
if (_.isObject(args[0]) && !_.isFunction(args[0]) && _.isString(args[1])) {
_.assign(options.entity, args[0])
options.relationship = _.first([options.relationship, args[1]].filter(_.isString))
}
// if an entity object or UsergridEntity instance is the third argument (target)
if (_.isObject(args[2]) && !_.isFunction(args[2])) {
_.assign(options.to, args[2])
}
options.entity.uuidOrName = _.first([options.entity.uuidOrName, options.entity.uuid, options.entity.name, args[1]].filter(_.isString))
if (!options.entity.type) {
options.entity.type = _.first([options.entity.type, args[0]].filter(_.isString))
}
options.relationship = _.first([options.relationship, args[2]].filter(_.isString))
if (_.isString(args[3]) && !_.isUuid(args[3]) && _.isString(args[4])) {
options.to.type = args[3]
} else if (_.isString(args[2]) && !_.isUuid(args[2]) && _.isString(args[3]) && _.isObject(args[0]) && !_.isFunction(args[0])) {
options.to.type = args[2]
}
options.to.uuidOrName = _.first([options.to.uuidOrName, options.to.uuid, options.to.name, args[4], args[3], args[2]].filter(function(u) {
return (_.isString(options.to.type) && _.isString(u) || _.isUuid(u))
}))
if (!_.isString(options.entity.uuidOrName)) {
throw new Error('source entity "uuidOrName" is required when connecting or disconnecting entities')
}
if (!_.isString(options.to.uuidOrName)) {
throw new Error('target entity "uuidOrName" is required when connecting or disconnecting entities')
}
if (!_.isString(options.to.type) && !_.isUuid(options.to.uuidOrName)) {
throw new Error('target "type" (collection name) parameter is required connecting or disconnecting entities by name')
}
options.uri = urljoin(
config.baseUrl,
client.orgId,
client.appId,
_.isString(options.entity.type) ? options.entity.type : "",
_.isString(options.entity.uuidOrName) ? options.entity.uuidOrName : "",
options.relationship,
_.isString(options.to.type) ? options.to.type : "",
_.isString(options.to.uuidOrName) ? options.to.uuidOrName : ""
)
return options
},
getConnections: function(client, args) {
/* getConnections supports the following constructor patterns:
client.getConnections(direction, entity, "relationship");
// GET OUT: /entity.type/entity.uuid/connections/relationship
// GET IN: /entity.type/entity.uuid/connecting/relationship
client.getConnections(direction, "type", "<uuidOrName>", "relationship");
// GET OUT: /type/uuidOrName/connections/relationship
// GET IN: /type/uuidOrName/connecting/relationship
client.getConnections({
type: "type", // or inferred, if second argument is an entity
uuidOrName: "<uuidOrName>" // if entity not specified
relationship: "relationship",
direction: OUT or IN
);
// GET OUT: /entity.type/entity.uuid/connections/relationship
// GET IN: /entity.type/entity.uuid/connecting/relationship
*/
var options = {
client: client,
method: 'GET',
callback: helpers.cb(args)
}
// if a preformatted options argument passed, assign it to options
if (_.isObject(args[0]) && !_.isFunction(args[0]) && args.length <= 2) {
_.assign(options, args[0])
} else if (_.isObject(args[1]) && !_.isFunction(args[1])) {
_.assign(options, args[1])
}
options.direction = _.first([options.direction, args[0]].filter(function(d) {
return (d === "IN" || d === "OUT")
}))
options.relationship = _.first([options.relationship, args[3], args[2]].filter(_.isString))
options.uuidOrName = _.first([options.uuidOrName, options.uuid, options.name, args[2]].filter(_.isString))
options.type = _.first([options.type, args[1]].filter(_.isString))
if (!_.isString(options.type)) {
throw new Error('"type" (collection name) parameter is required when retrieving connections')
}
if (!_.isString(options.uuidOrName)) {
throw new Error('target entity "uuidOrName" is required when retrieving connections')
}
options.uri = urljoin(
config.baseUrl,
client.orgId,
client.appId,
_.isString(options.type) ? options.type : "",
_.isString(options.uuidOrName) ? options.uuidOrName : "",
options.direction === "IN" ? "connecting" : "connections",
options.relationship
)
return options
}
}