Files
dougal-software/lib/modules/@dougal/organisations/Organisations.js

226 lines
5.1 KiB
JavaScript
Raw Normal View History

const Organisation = require('./Organisation');
class Organisations {
#values = {}
#overlord
static entries (orgs) {
return orgs.names().map(name => [name, orgs.get(name)]);
}
constructor (data, overlord) {
if (data instanceof Organisations) {
for (const [name, value] of Organisations.entries(data)) {
this.set(name, new Organisation(value));
}
} else if (data instanceof Object) {
for (const [name, value] of Object.entries(data)) {
this.set(name, new Organisation(value));
}
} else if (data instanceof String) {
this.set(data, new Organisation());
} else if (typeof data !== "undefined") {
throw new Error("Invalid constructor argument");
}
if (overlord) {
this.#overlord = overlord;
}
}
get values () {
return this.#values;
}
get length () {
return this.names().length;
}
get overlord () {
return this.#overlord;
}
set overlord (v) {
this.#overlord = new Organisations(v);
}
/** Get the operations for `name`
*/
get (name) {
const key = Object.keys(this.values).find( k => k.toLowerCase() == name.toLowerCase() ) ?? name;
return this.values[key];
}
/** Set the operations for `name` to `value`
*
* If we have an overlord, ensure we cannot:
*
* 1. Add new organisations which the overlord
* is not a member of
* 2. Access operations that the overlord is not
* allowed to access
*/
set (name, value) {
name = String(name).trim();
const key = Object.keys(this.values).find( k => k.toLowerCase() == name.toLowerCase() ) ?? name;
const org = new Organisation(value);
if (this.overlord) {
const parent = this.overlord.get(key) ?? this.overlord.get("*");
if (parent) {
this.values[key] = parent.filter(org);
}
} else {
this.values[key] = new Organisation(value);
}
return this;
}
/** Enable the operation `op` in all organisations
*/
enableOperation (op) {
if (this.overlord) {
Object.keys(this.#values)
.filter( key => (this.overlord.get(key) ?? this.overlord.get("*"))?.[op] )
.forEach( key => this.#values[key][op] = true );
} else {
Object.values(this.#values).forEach( org => org[op] = true );
}
return this;
}
/** Disable the operation `op` in all organisations
*/
disableOperation (op) {
Object.values(this.#values).forEach( org => org[op] = false );
return this;
}
/** Create a new organisation object limited by the caller's rights
*
* The spawned Organisations instance will have the same organisations
* and rights as the caller minus the applied `mask`. With the default
* mask, the spawned object will inherit all rights except for `edit`
* rights.
*
* The "*" organisation must be explicitly assigned. It is not inherited.
*/
spawn (mask = {read: true, write: true, edit: false}) {
const parent = new Organisations();
const wildcard = this.get("*").edit; // If true, we can spawn everywhere
this.entries().forEach( ([k, v]) => {
// if (k != "*") { // This organisation is not inherited
if (v.edit || wildcard) { // We have the right to spawn in this organisation
const o = new Organisation({
read: v.read && mask.read,
write: v.write && mask.write,
edit: v.edit && mask.edit
});
parent.set(k, o);
}
// }
});
return new Organisations({}, parent);
}
remove (name) {
const key = Object.keys(this.values).find( k => k.toLowerCase() == name.toLowerCase() ) ?? name;
delete this.values[key];
}
/** Return the list of organisation names
*/
names () {
return Object.keys(this.values);
}
/** Same as this.get(name)
*/
value (name) {
return this.values[name];
}
/** Same as Object.prototype.entries
*/
entries () {
return this.names().map( name => [ name, this.value(name) ] );
}
/** Return true if the named organisation is present
*/
has (name) {
return Boolean(this.value(name));
}
/** Return only those of our organisations
* and operations present in `other`
*/
filter (other) {
const filteredOrganisations = new Organisations();
const wildcard = other.value("*");
for (const [name, org] of this.entries()) {
const ownOrg = other.value(name) ?? wildcard;
if (ownOrg) {
filteredOrganisations.set(name, org.filter(ownOrg))
}
}
return filteredOrganisations;
}
/** Return only those organisations
* that have access to the required
* operation
*/
accessToOperation (op) {
const filteredOrganisations = new Organisations();
for (const [name, org] of this.entries()) {
if (org[op]) {
filteredOrganisations.set(name, org);
}
}
return filteredOrganisations;
}
toJSON () {
const obj = {};
for (const key in this.values) {
obj[key] = this.values[key].toJSON();
}
return obj;
}
toString (replacer, space) {
return JSON.stringify(this.toJSON(), replacer, space);
}
*[Symbol.iterator] () {
for (const [name, operations] of this.entries()) {
yield {name, operations};
}
}
}
if (typeof module !== 'undefined' && module.exports) {
module.exports = Organisations; // CJS export
}
// ESM export
if (typeof exports !== 'undefined' && !exports.default) {
exports.default = Organisations; // ESM export
}