pwix:tenants-manager
What is it ?
A try to mutualize and factorize the most common part of a multi-tenants management system:
-
defines a basic schema and provides client and server check functions
-
provides components to list and edit tenants.
Our tenants are defined in the common acceptance of the term as distinct organizations which are managed in a same software space. They can have validity periods. A tenant administrator can be defined as a scoped role.
Storage considerations
When an application makes use of this package to manage several tenants, two tenants_e
and tenants_r
collections are created which gathers defined tenants. That's all, and, in particular, this doesn't create for the application any assumption about the way the application tenants data will be themselves stored (in distinct databases, in distinct collections, or so).
-
the
tenants_e
collection contains datas which are common to all validity periods, which default to only be a label -
the
tenants_r
collection contains datas which are tied to a particular validity period.
Provides
TenantsManager
The exported TenantsManager
global object provides following items:
Functions
TenantsManager.configure( o<Object> )
See below
TenantsManager.getScopes()
An async function which returns an array of known tenants identifier, and their closest label.
This function is not reactive.
If reactivity is desired, the caller should prefer the pwix_tenants_manager_tenants_get_scopes
publication.
TenantsManager.i18n.namespace()
Returns the i18n namespace used by the package. Used to add translations at runtime.
Available both on the client and the server.
Blaze components
TenantEditPanel
A tabbed editing panel to be run inside of a page or of a modal. This is a two-levels Tabbed
component with:
-
the first level manages the entity, and contains a sub-
Tabbed
component for the records and anentity_notes_tab
pane for the entity notes -
each validity period is in second-level tab, with record properties and notes panes.
When run from TenantsList
, it is run in a modal to edit the current item.
The TenantEditPanel
component accepts a data context as:
-
item
: the item to be edited, or null (or unset) -
entityTabs
: a list of tabs to be inserted before the 'notes' tabs of the entity -
entityTabsAfter
: a list of tabs to be inserted after the 'notes' tabs of the entity, i.e. at the end -
recordTabs
: a list of tabs to be inserted before the 'notes' tabs of the record -
recordTabsAfter
: a list of tabs to be inserted after the 'notes' tabs of the record, i.e. at the end
TenantNewButton
A PlusButton
component customized to create a new tenant.
It takes itself care of checking the permissions of the user, and, depending of its runtime parameters, either is disabled, or doesn't display at all if the user is not allowed.
It takes the very same data context than below TenantEditPanel
.
TenantsList
The component list the defined tenants as a pwix:tabular_ext
table, with standard 'Informations', 'Edit' and 'Delete' buttons.
Each rendered line of the table displays an entity, and the closest values for each column.
It takes the very same data context than below TenantEditPanel
.
Events emitter
On server side, TenantsManager
is an event emitter though the TenantsManager.s.eventEmitter
object.
Following events are sent:
-
added
, from thetenantsAll
publication, with arguments asid<String>, object<Object>
-
changed
, from thetenantsAll
publication, with arguments asid<String>, object<Object>
-
removed
, from thetenantsAll
publication, with arguments asid<String>
-
item-update
, from theTenantsManager.list
object, with arguments asobject<Object>
This event is sent when the list has changed with each list item as argument.
So, if an item has been removed from the list, the event is sent on each remaining item, but not on the removed one.
Permissions management
This package can take advantage of pwix:permissions
package to manage the user permissions.
It defines following tasks:
-
at the user interface level
pwix.tenants_manager.feat.delete
, with argsitem<Object>
: delete a tenantpwix.tenants_manager.feat.edit
, with argsitem<Object>
: edit a tenantpwix.tenants_manager.feat.new
: create a new tenant
-
at the server level
pwix.tenants_manager.entities.fn.get_by
, with argsselector<Object>
: get entities from a Mongo selectorpwix.tenants_manager.entities.fn.upsert
, with argsitem<Object>
: upsert an entitypwix.tenants_manager.records.fn.get_by
, with argsselector<Object>
: get records from a Mongo selectorpwix.tenants_manager.records.fn.upsert
, with argsitem<Object>
: upsert a recordpwix.tenants_manager.fn.delete_tenant
, with argsentity<String>
: delete a tenantpwix.tenants_manager.fn.get_scopes
: provides a list of known scopespwix.tenants_manager.fn.set_managers
, with argsitem<Object>
: set managerspwix.tenants_manager.fn.upsert
, with argsitem<Object>
: upsert a tenant
-
on publications
pwix.tenants_manager.pub.list_all
: list all tenants and their contentspwix.tenants_manager.pub.closests
: list the closest record of each tenant, a tabular display requisitepwix.tenants_manager.pub.tabular
: a tabular-aware publicationpwix.tenants_manager.pub.known_scopes
: publishes a list of the known scopes to be used as a reference when editing scoped roles
Configuration
The package's behavior can be configured through a call to the TenantsManager.configure()
method, with just a single javascript object argument, which itself should only contains the options you want override.
Known configuration options are:
-
allowFn
An async function which will be called with an action string identifier, and must return whether the current user is allowed to do the specified action.
If the function is not provided, then the default is to deny all actions.
allowFn
prototype is:async allowFn( action<String> [, ...<Any> ] ): Boolean
-
classes
Let the application provides some classes to add to the display.
Defauts to nothing.
-
entityFields
Let the application extends the Entities default schema by providing additional fields as an array of
Field.Set.extend()
-valid definitions, or as a function which returns such an array ofField.Set.extend()
-valid definitions.Defauts to nothing.
Example:
1 TenantsManager.configure({ 2 entityFields: [ 3 { 4 before: 'notes', 5 fields: [ 6 { 7 name: 'label', 8 type: String, 9 optional: true 10 } 11 ] 12 } 13 ] 14 });
Default Entities fields are: - `notes`: common notes - `createdAt`, `createdBy`, `updatedAt`, `updatedBy`: timestampable behaviour.
-
hideDisabled
Whether to hide disabled actions instead of displaying the disabled state.
Defaults to
true
: disabled actions are hidden. -
recordFields
Let the application extends the Records default schema by providing additional fields as an array of
Field.Set.extend()
-valid definitions, or as a function which returns such an array ofField.Set.extend()
-valid definitions.Defauts to nothing.
Example:
1 TenantsManager.configure({ 2 recordFields: [ 3 { 4 before: 'label', 5 fields: [ 6 { 7 name: 'secondaryLabel', 8 type: String, 9 optional: true 10 } 11 ] 12 } 13 ] 14 });
Default Records fields are: - `label`: mandatory unique label - `pdmpUrl`: personal data management policy page url - `gtuUrl`: general terms of use page url - `legalsUrl`: legal terms page url - `homeUrl`: home page url - `supportUrl`: support page url - `contactUrl`: contact page url - `supportEmail`: support email address - `contactEmail`: contact email address - `logoUrl`: logo url - `logoImage`: logo image - `createdAt`, `createdBy`, `updatedAt`, `updatedBy`: timestampable behaviour.
-
roles
Let the application provides the permissions required to perform CRUD operations on the Tenants collection. This is an object with following keys:
list
: defaulting tonull
(allowed to all)create
: defaulting tonull
(allowed to all)edit
: defaulting tonull
(allowed to all)delete
: defaulting tonull
(allowed to all)
-
scopedManagerRole
The name of the role which holds the scoped management for a tenant, defaulting to
SCOPED_TENANT_MANAGER
. -
serverAllExtend
A server-side function which comes to extend the content of the 'tenantsAll' publication.
The function get the current entity item as its unique argument and returns a Promise when finished with its job.
Defaults to
null
. -
serverTabularExtend
A server-side function which comes to extend the content of the dataset published for the tabular display.
The function get the current entity item as its unique argument and returns a Promise when finished with its job.
Defaults to
null
. -
tenantButtons
Let the application extends the Tenants default tabular display by providing additional buttons as an array of template names, or as a function which returns such an array.
The template will be called with the current row item as its data context.
Defauts to nothing.
Example:
1 TenantsManager.configure({ 2 tenantButtons: [ 3 { 4 where: Tabular.C.Where.AFTER, 5 buttons: [ 6 'my_template' 7 ] 8 } 9 ] 10 });
-
tenantFields
Let the application extends the Tenants default tabular display by providing additional fields as an array of
Field.Set.extend()
-valid definitions, or as a function which returns such an array ofField.Set.extend()
-valid definitions.Defauts to nothing.
Example:
1 TenantsManager.configure({ 2 tenantFields: [ 3 { 4 before: 'label', 5 fields: [ 6 { 7 name: 'column', 8 schema: false, 9 dt_title: 'My title', 10 dt_className: 'dt-center' 11 } 12 ] 13 } 14 ] 15 });
-
tenantsCollection
The radical of the names of the tenants Mongo collections.
Defaults to
tenants
. -
verbosity
The verbosity level as:
TenantsManager.C.Verbose.NONE
or an OR-ed value of integer constants:
-
TenantsManager.C.Verbose.CONFIGURE
Trace configuration operations
-
TenantsManager.C.Verbose.FUNCTIONS
Trace all function calls
-
TenantsManager.C.Verbose.ATTACHSCHEMA
Trace the schemas attachement operations
Defaults to
TenantsManager.C.Verbose.CONFIGURE
. -
withValidities
Whether we want manage validity periods for the tenants, defaulting to
true
.
A function can be provided by the application for each of these parameters. The function will be called without argument and must return a suitable value.
Please note that TenantsManager.configure()
method should be called in the same terms both in client and server sides.
Remind too that Meteor packages are instanciated at application level. They are so only configurable once, or, in other words, only one instance has to be or can be configured. Addtionnal calls to TenantsManager.configure()
will just override the previous one. You have been warned: only the application should configure a package.
NPM peer dependencies
Starting with v 1.0.0, and in accordance with advices from the Meteor Guide, we no more hardcode NPM dependencies in the Npm.depends
clause of the package.js
.
Instead we check npm versions of installed packages at runtime, on server startup, in development environment.
Dependencies as of v 1.3.0:
1 'email-validator': '^2.0.4', 2 'lodash': '^4.17.0', 3 'valid-url': '^1.0.9'
Each of these dependencies should be installed at application level:
meteor npm install <package> --save
Translations
New and updated translations are willingly accepted, and more than welcome. Just be kind enough to submit a PR on the Github repository.
Cookies and comparable technologies
None at the moment.
Issues & help
In case of support or error, please report your issue request to our Issues tracker.
P. Wieser
- Last updated on 2024, Oct. 4th