google-admin-sdk
Version:
node.js library that wraps the Directory and Groups APIs in the Google Admin SDK
163 lines (147 loc) • 7.89 kB
text/coffeescript
GoogleAPIAdminSDK = require "#{__dirname}/google_api_admin_sdk"
_ = require 'underscore'
utils = require "#{__dirname}/utils"
{Query} = require "#{__dirname}/query.coffee"
qs = require 'qs'
sanitize = require 'sanitize-arguments'
async = require 'async'
class OrgUnit extends GoogleAPIAdminSDK
# TODO: possibly make customer_id an option of OrgUnit
# customer_id, org_unit_path required
# if no patch_body is provided, update behaves like get
# fields returns only selected properties of an OrgUnit object
patch: (customer_id, org_unit_path, patch_body, fields, cb) =>
arglist = sanitize arguments, OrgUnit.patch, [String, String, Object, String, Function]
args = _.object ['customer_id', 'org_unit_path', 'patch_body', 'fields', 'cb'], arglist
die = utils.die_fn args.cb
if not args.customer_id? or not args.org_unit_path?
return die "OrgUnit::patch expected (String customer_id, String org_unit_path, [Object patch_body, String fields, callback])"
uri = "https://www.googleapis.com/admin/directory/v1/customer/#{args.customer_id}/orgunits/#{args.org_unit_path}"
uri += "?#{qs.stringify {fields: args.fields}}" if args.fields?
opts = { method: 'patch', uri: uri, json: (args.patch_body or true) }
q = new Query @, opts
return q unless args.cb?
q.exec args.cb
insert: (customer_id, properties, fields, cb) =>
arglist = sanitize arguments, OrgUnit.insert, [String, Object, String, Function]
args = _.object ['customer_id', 'properties', 'fields', 'cb'], arglist
die = utils.die_fn args.cb
if not args.customer_id? or not args.properties?
return die "OrgUnit::insert expected (String customer_id, Object properties[, String fields, callback])"
uri = "https://www.googleapis.com/admin/directory/v1/customer/#{args.customer_id}/orgunits"
uri += "?#{qs.stringify {fields: args.fields}}" if args.fields?
opts = { method: 'post', uri: uri, json: properties }
q = new Query @, opts
return q unless args.cb?
q.exec args.cb
delete: (customer_id, org_unit_path, cb) =>
arglist = sanitize arguments, OrgUnit.delete, [String, String, Function]
args = _.object ['customer_id', 'org_unit_path', 'cb'], arglist
die = utils.die_fn args.cb
if not args.customer_id? or not args.org_unit_path?
return die "OrgUnit::delete expected (String customer_id, String org_unit_path[, callback])"
uri = "https://www.googleapis.com/admin/directory/v1/customer/#{args.customer_id}/orgunits/#{args.org_unit_path}"
opts = { method: 'delete', uri: uri, json: true }
q = new Query @, opts
return q unless args.cb?
q.exec args.cb
list: (customer_id, params, cb) =>
arglist = sanitize arguments, OrgUnit.list, [String, Object, Function]
args = _.object ['customer_id', 'params', 'cb'], arglist
die = utils.die_fn args.cb
return die "OrgUnit::list expected (String customer_id[, Object params, callback])" if not args.customer_id?
opts = { json: true }
opts.uri = "https://www.googleapis.com/admin/directory/v1/customer/#{args.customer_id}/orgunits"
opts.uri += "?#{qs.stringify args.params}" if args.params?
q = new Query @, opts
return q unless args.cb?
q.exec args.cb
get: (customer_id, org_unit_path, fields, cb) =>
arglist = sanitize arguments, OrgUnit.get, [String, String, String, Function]
args = _.object ['customer_id', 'org_unit_path', 'fields', 'cb'], arglist
die = utils.die_fn args.cb
if not args.customer_id? or not args.org_unit_path?
return die "OrgUnit::get expected (String customer_id, String org_unit_path, [, String fields, callback])"
opts = { json: true }
opts.uri = "https://www.googleapis.com/admin/directory/v1/customer/#{args.customer_id}/orgunits/#{args.org_unit_path}"
opts.uri += "?#{qs.stringify {fields: args.fields}}" if args.fields?
q = new Query @, opts
return q unless args.cb?
q.exec args.cb
# get_children takes in a customer_id and org_unit_path and returns the children of that org unit, or an
# error if that org unit cannot be found.
# Reference: https://developers.google.com/admin-sdk/directory/v1/guides/manage-org-units#get_all_ou
get_children: (customer_id, org_unit_path, cb) =>
arglist = sanitize arguments, OrgUnit.get_children, [String, String, Function]
args = _.object ['customer_id', 'org_unit_path', 'cb'], arglist
die = utils.die_fn args.cb
if not args.customer_id? or not args.org_unit_path?
return die "OrgUnit::get_children expected (String customer_id, String org_unit_path, [, callback])"
opts =
json: true
uri: "https://www.googleapis.com/admin/directory/v1/customer/#{args.customer_id}/orgunits"
qs:
orgUnitPath: args.org_unit_path
type: "children"
q = new Query @, opts
return q unless args.cb?
q.exec args.cb
# takes customer_id, array of orgunit levels eg. ['CleverStudents', 'Schoolname', ...], and optional cache, and callback
# returns callback w/ args orgunit string '/Students/Schoolname' and cache of orgunits created '/', '/Students', '/Students/Schoolname'
findOrCreate: (customer_id, org_unit, cache, cb) =>
if _(cache).isFunction()
cb = cache
cache = {}
arglist = sanitize arguments, OrgUnit.findOrCreate, [String, Array, Function]
args = _.object ['customer_id', 'org_unit', 'cb'], arglist
die = utils.die_fn args.cb
if not args.customer_id? or not args.org_unit?
return die "OrgUnit::findOrCreate expected (String customer_id, Array org_unit, [callback])"
parent = '/'
# for each level in the OU, find that OU or create it and store the response in the cache
async.eachSeries args.org_unit, (level, cb_es) =>
full_path = if parent is '/' then "/#{level}" else "#{parent}/#{level}"
if cache[full_path]?
parent = full_path
return cb_es()
@atomic_get_or_create full_path, args.customer_id, level, parent, @, (err, results) ->
return cb_es err if err?
cache[full_path] = results.body
parent = results.parent
cb_es err, results.body
, (err) ->
return die err if err?
cb null, parent, cache
# use async.memoize for two reasons:
# 1. Ensure that the actual find-or-create action is executed at most once.
# This avoids a race condition in which the consumer of this library
# calls findOrCreate with multiple concurrency
# 2. Increases performance
atomic_get_or_create: async.memoize (full_path, customer_id, level, parent, that, cb) =>
async.waterfall [
(cb_wf) =>
# Make a request to find this OU first. Only make an insert request if the OU doesn't
# already exist.
that.get customer_id, full_path[1..], (err, body) ->
if err?
if err.error?.code is 404 and err.error?.message is "Org unit not found"
# Not found; pass through to create the orgunit
return cb_wf null, null
else
# A valid error was returned. Return the error and skip the insert.
return cb_wf err
# If no error, we found the orgunit. Cache it and skip insert because it already exists.
# cache[full_path] = body
parent = full_path
return cb_wf null, { body, parent }
(body, cb_wf) =>
# No need to insert because the org unit was found
return cb_wf null, body if body?
# Google interface: `name` requires no slash, parentOrgUnitPath requires a slash
that.insert customer_id, { name: level, parentOrgUnitPath: parent }, (err, body) =>
return cb_wf "Unable to create org unit #{full_path}: #{JSON.stringify err}" if err?
# cache[full_path] = body
parent = full_path
cb_wf null, { body, parent }
], cb
module.exports = OrgUnit