This step requires a few things:
1. Creation of a new database
2. Loading a schema
3. Loading templates
4. Creating an initial user
The fourth step is a problem (the others are encapsulated well in LedgerSMB::Database). This is only 1 occurrence of a much broader problem: our Moose objects basically expect to be instantiated within the context of our web application, including the existence of an HTTP request:
my $user = LedgerSMB::Entity::User->new(%$request);
Even though that works while we're in the web application, when I want to use the Moose objects as a scripting API (as I do for the testing framework), this doesn't work for a number of reasons:
1. There *is* no request object
2. The request object passes "internal values" into each object (i.e. _DBH; maybe others?)
3. The script doesn't have "global state" as normally stored in App_State
I'm thinking that having an API with the following properties would greatly help my coding for the test scripts (and possibly others to code against our company databases?):
The entire API would be keyed off a LedgerSMB::API::Connection (or other) object, which encapsulates all state specific to one user (normal or super-user) being connected to a company. Such an object will need to be instantiated for every web request in the same way that we currently instantiate a LedgerSMB object, but it would be ignorant of whether it's being instantiated from a web-request or from a Perl script with the correct credentials.
Everything in the company (if access rules allow it) is accessible through methods from this class either directly or indirectly. Methods I envision this class to have:
- users: access to (non-super-users) with access to the
- roles: access to roles and/or role groups available in this company
- contacts: access to the "entity" subsystem (vendors/customers/employees)
(I have thought of many different names for the "Connection" class, all of which I found confusing:
- Connection -- we already have a "database connection" (DBI object)
- Session -- we already have a "User/Browser logged in session"
- Company -- we already have a "Company as a contact" object
My idea is that if I want to create a new user - which inherently needs database access -
I can simply instantiate a new user object and "create that into the company" like this:
my $new_user = LedgerSMB::API::User->new(username=>'test');
That is, the user is "created into the company". At that point, any internal administration can be "fixed up" on the user object before the actual database entity is created, including, but not limited to, passing a database connection (DBI) object.
Some existing entities (e.g. roles) are currently directly queried on the database. These should become accessible through the collection accessors introduced at the beginning.
Wondering what to do about entity cross references here: I think API users would expect to be able to request a customer object from an invoice object. In Weasel, I solved this problem by maintaining a reference to the central Session, which allows objects which are related to that session to query attributes of other entities in the session. A similar pattern would probably work for the company as well.
Even though we're not even close to a model like this, I think it's good to have a target model planned so we can start migrating our API to such a model.
Comments? Remarks? Better ideas?
Robust and Flexible. No vendor lock-in.