@darwino/darwino
Version:
A set of Javascript classes and utilities
417 lines (382 loc) • 11.7 kB
JavaScript
/*!COPYRIGHT HEADER! - CONFIDENTIAL
*
* Darwino Inc Confidential.
*
* (c) Copyright Darwino Inc. 2014-2016.
*
* Notice: The information contained in the source code for these files is the property
* of Darwino Inc. which, with its licensors, if any, owns all the intellectual property
* rights, including all copyright rights thereto. Such information may only be used
* for debugging, troubleshooting and informational purposes. All other uses of this information,
* including any production or commercial uses, are prohibited.
*/
import Utils from "./Utils"
import {fetchJson,fetchText,makeUrl} from './Fetch';
// Inline image so we display it even if there is an authentication error with the server
// it makes the image always available
const ANONYMOUS_PHOTO = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAD0AAAA9CAIAAACRAPa+AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAWdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjA76PVpAAAESUlEQVRoQ+2ZaVfaQBSG/f9fW7e2WmRRNgHZFFBBUMqigiwmLAmboO2v6NvSpvbWJpOZQeW05zxfPAdzn0zunbk3WRnff1lG/ns/L3K8R7PPYDh90MYzfTIbfv8TkJ9JRMhbG08vm2q6UPZFUx/2wm8/et9s74F1p98ZiEVSuUK13urqi7gBTm8sbfGq4fBH13b8c9cnwZ1suoLBxLHSH45m9CIi8HjX2l0ssLG6LGy4Apnzcm90Ry7FjT1vPPF8uYYlJFos4D49oWRHH5Nr8mHDezB9OLmorDp8RMgWH30HDbVPrswBqzdW+rRYFZSes+2NqMKrzupdublddwaIATe74aRgrjN5K9rw/W6IxBYBuZ7Ol0T2R2tvXD1xck4Ci4MNVCTRrb2V/sh8k+bGG03hHCDhGLH2xrFH4skC+yn3klt4K9ronXufxJNIOl8mERmx8MZhLmXv+xvYzvmq08z7W0Weyq9IQrs3IHFZMPNG0ewdHJEw0kHjQOKyYOY9uLtHd0rCSCd+XCBxWTDz1if3a4tM7jm+WJrEZcHMG2MBibEIPOFDjtI080YLQWIsAk84Kdkbk+KqneGAD+9BisRlwaIu33tktlNPcpA+I3FZMPWePmBCIWGkky1ekrgsmHkj7WKZPAkjnRulR+KyYOYN8pWarfnXLmh++FpCC+9mR8coToJJJMZ16AALb6TK4o56tPXX7Q6JyIiFN6grvVXHQlLFvZ/QJjMSjhFrb+Rf6PCUhBQHZVOut0ksdqy9wY3Sl57lkXSO45g0YPIG2GUlDhAYF5T+kISwBas3siV2nJeyJ244A9zlaMDqDVBDgXhGUH3THRRJawMb3gDqyewFt/rWXuSyqYqktYE9b4CohUp9y+YchPnDG03diuX0Y2x7z0FVHeWKG2wvlF3BeKnW0u/uyUVE4PQGo9mX7nByfF5B448xFNU2b9aRRWs7PjQe2DSCiZPLhjKcSkgMAr+3AbYaRRuhrbtqqtXGLUSxXbS6usSvC38iwftF+Pe8kR790VTVx+3eoNnR0AvUb7u1n6Aba6h9ZAtSCGWgT2QWJbDnjU0QirnSdSSV2w0f7gRiW94ISnAdRenwPd7XUaNoUzddwQ+7YYc/6g4lcGalz0ooAG3M2QM+xtob0zFW9NN1M3KUhaLgefkWt+Tw4Z5PL6oo5d6Qs3bNvHE6op3CNvfOs5BXyXggeBSY5xuqRkJb8oQ3EhfJcJi9kPghyhw8BExVlXqbfeuk3kgJTAl8X1YFQUk4g/FCtc7SwPzy7ujj1NmnZ3iRaYk7lLxqqXjshtuf/PAuXjVxLJP/f0HWnf7wUbb/97RZQUXHJQ0E0sFSomV4Mm1WXPuJ1yk9B3PtWfmaSIMV8rvXSaZQHtz9lu7L4Y2jKn5yvnzec9L5krHJLJM3zldjpl4mb4AybX3/3rlk3sAfz6DVWz5vnOgYCJfPG+wEYl8BFq48VCWZ4dcAAAAASUVORK5CYII="
const ANONYMOUS_PHOTO_URL = "$darwino-social/users/images/anonymous-photo.png";
//
// User class
//
class User {
constructor(attrs,svc) {
this._attrs = attrs;
this._svc = svc;
}
isAnonymous() {
return this._attrs["dn"]=="anonymous";
}
getProvider() {
return this._provider;
}
exists() {
return !(this._loading || this._error);
}
isLoading() {
return !!this._loading;
}
getDn() {
return this._attrs["dn"];
}
getCn() {
return this._attrs["cn"];
}
getPhotoUrl() {
if(!this._svc) return ANONYMOUS_PHOTO
return this._svc.getUserPhotoUrl(this._attrs["dn"]);
}
getGroupCount() {
return this.getGroups().length;
}
getGroup(index) {
return this.getGroups()[index];
}
getGroups() {
return this._attrs._groups||[];
}
getRoleCount() {
return this.getRoles().length;
}
getRole(index) {
return this.getRoles()[index];
}
getRoles() {
return this._attrs._roles||[];
}
hasUserId(id) {
return this._attrs["dn"]==id;
}
hasGroup(id) {
var groups = this.getGroups();
for(var i=0; i<groups.length; i++) {
if(groups[i]==id) {
return true;
}
}
return false;
}
hasRole(id) {
var roles = this.getRoles();
for(var i=0; i<roles.length; i++) {
if(roles[i]==id) {
return true;
}
}
return false;
}
getAttribute(attrName) {
return this._attrs[attrName];
}
getAttributes() {
return this._attrs;
}
findAttribute(attrName) {
var v = this._attrs[attrName];
if(v!=undefined) {
return v;
}
if(this._attrs._providers) {
for(var p in this._attrs._providers) {
var v = this._attrs._providers[p][attrName];
if(v!=undefined) {
return v
}
}
}
return null;
}
getContentAsBinaryString(type,cb) {
return getContent(this,null,type,cb,null);
}
getContentAsBase64(type,cb) {
return getContent(this,null,type,cb,Utils.btoa);
}
getContentAsString(type,cb) {
return getContent(this,null,type,cb,Utils.fromUtf8);
}
getUserData(provider) {
var p = this._attrs._providers;
return p && p[provider] ? new UserData(provider,p[provider],this) : null;
}
}
class UserData {
constructor(user,name,attrs) {
this._user = user;
this._name = name;
this._attrs = attrs;
}
getAttribute(attrName) {
return this._attrs[attrName];
}
getAttributes() {
return this._attrs;
}
getContentAsBinaryString(type,cb) {
return getContent(this._user,this._name,type,cb,null);
}
getContentAsBase64(type,cb) {
return getContent(this._user,this._name,type,cb,Utils.btoa);
}
getContentAsString(type,cb) {
return getContent(this._user,this._name,type,cb,Utils.fromUtf8);
}
}
function getContent(user,provider,type,cb,process) {
if(user._svc) {
var p = provider ? {provider:provider} : null;
const url = makeUrl(this.baseUrl,["users",user.getDn(),"content",type],p);
return fetchText(url),then((text) => {
if(process) {
text=process(text)
}
if(cb) (cb.success||cb)(text)
}).catch((error) => {
if(cb&&cb.failure) cb.failure(error);
})
}
return null;
};
// Anonymous user
const ANONYMOUS_DN = "anonymous";
const ANONYMOUS_USER = new User({dn:ANONYMOUS_DN,cn:"Anonymous",photoUrl:ANONYMOUS_PHOTO});
// This could go to a redux store!!
const userMap = {}
//
// UserService
//
class UserService {
// Export
static ANONYMOUS_PHOTO = ANONYMOUS_PHOTO
static ANONYMOUS_PHOTO_URL = ANONYMOUS_PHOTO_URL
static ANONYMOUS_DN = ANONYMOUS_DN
static ANONYMOUS_USER = ANONYMOUS_USER
constructor() {
this.requests = {};
this.baseUrl = "$darwino-social/users"
this.users = userMap;
}
createUser(dn,cn) {
return new User({dn:dn,cn:cn||dn},this);
}
// The methods bellow return a user
// The calls are asynchronous, so if the user is not already available, it temporarily returns anonymous
getCurrentUser(cb) {
return this._findUser("@me",false,cb);
}
getUser(id,cb) {
return this._findUser(id,false,cb);
}
getUserByLoginId(id,cb) {
return this._findUser(id,true,cb);
}
_findUser(id,loginid,cb) {
var _this=this;
// An ID is case insensitive
if(id) {
var dn = id.toLowerCase();
if(dn!="anonymous") {
var u = this.users[dn];
// if the user exists and there is an ongoing request for, add the callback to the list and return the user
if(u) {
if(cb) {
if(this.requests[dn]) {
this.requests[dn].push(cb);
} else {
Utils.execAsyncCb(cb,u,false);
}
}
return u;
}
// The user is not yet loaded
// Create the temporary user object and add it to the cache
if(id=="@me") {
u = new User({...ANONYMOUS_USER._attrs},_this);
this.users["@me"] = u;
} else {
u = new User({dn:id,cn:id},_this);
this.users[dn] = this.users[id] = u;
}
u._loading = true;
// Create a list of callbacks for the request and eventually add the current one
this.requests[dn] = [];
if(cb) {
this.requests[dn].push(cb);
}
// Execute the remote service
var params = {};
if(loginid) params.loginid=true;
if(id=="@me") params.providers="" // No providers to make @me as fast as possible for now
fetchJson(makeUrl(this.baseUrl,["users",dn],params)).then(
function(json) {
delete u._loading;
if(json) {
u._attrs = json;
u._exists = true;
_this.users[dn] = u;
// If @me, then no providers are added => don't store the user in the main list
// An access by dn will later get all the providers, if any
if(id!="@me") {
_this.users[u.getDn().toLowerCase()] = u; // May already be there...
if(json._current) {
_this.users["@me"] = u;
}
} else {
if(!_this.users["@me"]) _this.users["@me"] = u; // Might have been updated by another request by Id (complete)
}
var req = _this.requests[dn];
if(req) {
for(var i=0; i<req.length; i++) {
(req[i].success||req[i])(u,true);
}
delete _this.requests[dn];
}
}
}).catch(
function(error) {
delete u._loading;
u._error = error;
var req = _this.requests[dn];
if(req) {
for(var i=0; i<req.length; i++) {
if(req[i].failure) {
req[i].failure(error);
}
}
delete _this.requests[dn];
}
}
)
// Return the default user
return u;
}
}
return this.ANONYMOUS_USER;
}
getUserFromCache(dn) {
return dn ? this.users[dn.toLowerCase()] : null;
}
preloadUsers(ids,loginid,cb) {
var _this=this;
if(ids) {
// Find the users that are not already in the cache
var dns = [];
for(var i=0; i<ids.length; i++) {
var id = ids[i];
if(id) {
var dn = id.toLowerCase();
if(!this.users[dn]) {
// Create a user being loaded
var u = new User({dn:id,cn:id},_this);
u._loading = true;
this.users[dn] = this.users[id] = u;
dns.push(dn);
}
}
}
if(dns.length) {
var params = {};
if(loginid) params.loginid=true;
const url = fetchJson(makeUrl(this.baseUrl,["users"], params), {
method: 'POST',
body: dns
}).then((array) => {
if(array) {
for(var i=0; i<array.length; i++) {
var u = _this.users[dns[i]];
delete u._loading;
if(array[i]) {
u._attrs = array[i];
u._exists = true;
_this.users[u.getDn().toLowerCase()] = u; // May already be there...
if(u._attrs._current) {
_this.users["@me"] = u;
}
} else {
u._error = "Unknown user";
}
}
}
if(cb) (cb.success||cb)();
}).catch((error) => {
for(var i=0; i<dns.length; i++) {
var u = _this.users[dns[i]];
delete u._loading;
u._error = error;
}
if(cb&&cb.failure) cb.failure(error);
})
}
}
}
getUserPhotoUrl(dn) {
return this.getUserDataUrl(dn,"photo");
}
getUserDataUrl(dn,type) {
if(!dn) {
dn = ANONYMOUS_DN;
}
if(type) {
if(type=="photo") {
if(dn==ANONYMOUS_DN) {
return ANONYMOUS_PHOTO;
}
let u = this.getUserFromCache(dn);
if(u) {
if(u._attrs.photoUrl) {
return u._attrs.photoUrl;
}
}
}
return makeUrl(this.baseUrl,["users",dn,"content",type]);
}
return null;
}
query(query,attributes,skip,limit,options,cb) {
var params = {};
if(query) params.query = query;
if(attributes) params.attributes = attributes.join(',');
if(skip) params.skip = skip;
if(limit) params.limit = limit;
if(options) params.options = Utils.toJson(options);
return fetchJson(makeUrl(this.baseUrl,["query"], params)).then((json) => {
if(cb) (cb.success||cb)(json)
}).catch((error) => {
if(cb&&cb.failure) cb.failure(error)
})
}
typeAhead(query,attributes,skip,limit,options,cb) {
var params = {};
if(query) params.query = query;
if(attributes) params.attributes = attributes.join(',');
if(skip) params.skip = skip;
if(limit) params.limit = limit;
if(options) params.options = Utils.toJson(options);
return fetchJson(makeUrl(this.baseUrl,["typeahead"], params)).then((json) => {
if(cb) (cb.success||cb)(json)
}).catch((error) => {
if(cb&&cb.failure) cb.failure(error)
})
}
}
export default UserService;