At the recent Data Architecture Design Sprint, participants outlined a development path in which fields can be local or external. A given entity instance (node, user, etc.) can be fully local, fully external, or a combination of the two. For example, a node can have several fields stored and handled in a local SQL database and other fields both loaded from and written to an external SOAP service.
As steps on this path, we outlined (a) short term changes to CCK, (b) a minimal patch to integrate CCK into core, and (c) some follow-up steps, like deciding on a consistent data structure for all entity types, to be followed by a unified set of methods for all entity types (e.g, drupal_save($entity_type, $entity)
).
Are there short term steps we can (should?) take now toward external data source support? Here is a rough, conceptual attempt to sketch out such steps in the form of a proposed contrib module, Web Service Clients, taking the existing Services module as a model to work from.
Services module: a model for external data source support
In D5 contrib, a services module was developed to enable standardized development of Drupal as a web services platform. Documentation: http://drupal.org/node/109782, project: http://drupal.org/project/services. Services implements a pluggable architecture in which various types of servers (XMLRPC, SOAP) can be implemented, drawing on a common set of services. Services here are sets of available methods, e.g., node load and user save. Through services, a Drupal site can respond to external requests, providing read/write access to locally stored data.
Clients module: web service clients modelled on Services
Services provides external read/write access to locally stored data. We need the parallel set of implementations: local read/write access to external data sources (via web services).
Parallel to the Services module's pluggable server, we need a set of pluggable server types--but as clients rather than servers. In short, we need a Web Service Clients module (Clients for short).
A minimal initial target spec for this module might be:
- Using XMLRPC (the one server plugin bundled with Services module; there are others in contrib), implement a client such that a Drupal site running Clients can meet the following use cases:
- User can create and update a node that has two fields from an external site running Services' XMLRPC server. On create and update, those fields are not saved locally but instead are saved to the remote service. On load, fields are loaded from the remote service.
- Whenever a user record is updated locally, a call is sent to a remote service notifying of the update operation. This enables a remote service to store/track a user's ID and associated email address on the Drupal instance.
Solution components:
- Generic handlers (in clients.module): shared methods
- Pluggable services: services supported, e.g., XMLRPC
- Pluggable methods: local methods for handling data operations
- Client instances: data unique to specific client instances
Generic handlers
Generic handlers and methods, including:
- Connecting to external services.
- UI for admin-defined client instances.
Pluggable services
Example: generic SOAP support.
Each service includes methods for:
- authentication
- deriving schema information about external data sources
Pluggable methods
Define in metadata form a common set of local methods and their expected data formats and return values. Examples:
- entity_find: given an array of properties and values, return an array of matching entity IDs.
- entity_load: given an entity id, return an entity instance
- entity_save: given an entity instance, save to the remote service and return a result code
- field_load: given an entity id and a field name, return the value of that field for the given entity
- field_save: given an entity id, field name, and field value, save the field to that entity and return a result code
Alternately, we could implement the same set of methods as are available in Services module (see the services plugins), but these may tend to be too specific to the Drupal environment and so to map poorly to external web services.
Client instances
Example: SOAP client to a particular SOAP service.
A client instance may override generic methods for its service type.
Each client instance includes handling and methods for:
- Mapping available external request methods to the set of local methods. For example, fed a field_load call, translate this into a call to save data to a remote SOAP service. (Each client instance will implement only a subset of the available local methods.)
Nodeapi and CCK integration
To locally present and edit data from external sources, we need the ability to represent and integrate those data locally.
CCK provides our richest set of applicable tools, and is in any case our proposed long term solution. How therefore can we leverage CCK fields in 5.x or 6.x to provide handling for external fields?
Proposed implementation:
One-to-one mapping between external data fields and local CCK fields, with the CCK field handling overridden for CRUD operations.
- For each external field, site admin defines a CCK field and attaches it to one or more content types.
- Clients module provides additional UI element in field definition to select external field to map this field to.
- In
hook_nodeapi()
load op, Clients module overrides CCK field handling, inserting externally-derived data in place of local ones (which will be empty, as there are no locally stored data). - In
hook_nodeapi()
insert, update, and delete ops, Clients module dispatches calls to external web service for fields it is responsible for.
Actions
As well as field-level handling, we need methods to invoke actions based on given state changes. In the above use case #2, this included responding to a user save operation by sending a notification to an external service.
Here we can build off of Actions, particularly the hook operation support in D6.
- Client method implementations (particular local methods, e.g., entity_save, that are implemented by a specific client, e.g., a Gazetteer SOAP service) may declare the 'hooks' operations they support, using the same format as used for actions metadata.
- These methods are programmatically exposed as actions with associated hooks, to be called as appropriate, receiving in argument form the relevant data structure (e.g., a user object).
Summary
By developing genericized, fields-aware web service client support, we can both provide short term functionality for D5 and D6 and begin concretely to map out long term solutions for core.
After getting our minimal CCK fields into core, we could turn to minimally bringing the Services module and (assuming some solid development by then) accompanying Clients module into core as well.
Does this approach seem to make sense? What considerations are missing? Please comment on or edit up this Wiki page.
[Note: I'm hoping to be able to begin coding a draft Clients module within a week or two for an upcoming client contract.--Nedjo]
Comments
Comments now enabled
Comments are now enabled on this page (thanks Angie!).
Wow sounds great! I really
Wow sounds great! I really love the abstraction I think that is a great leap to take. The external fields sound really interesting too, could you post some use-case scenarios for this?
vision media
350designs
Tj Holowaychuk
Vision Media - Victoria BC Web Design
Victoria British Columbia Web Design School
Have you seen what is going
Have you seen what is going on at http://groups.drupal.org/node/8930 and http://groups.drupal.org/node/9010?
Yes, interesting
I'm not sure as yet what the relationship is. I'll study the RDF work a bit more....
Services and Drupal 7
Here are some things to think about when implementing Services in Drupal 7:
Do you have the list of
Do you have the list of current drupal modules that can be seen as kind of "web service client" ?
load a node from a web service
Hello to every body.
My name is Alessandro Mascherpa and I´m just starting to build a site with drupal 6 that will need to load data from a soap web service; and then administer the data backwards with rpc methods.
When I first find this information about clients module I thougt It was going to feet perfectly my needs and the job was going to be "easy". Then I found that clients module seems to be build directly for drupal 7, and I am steel searching for a solution to my problem.
The I saw a post from Robert Douglass in "http://www.mattfarina.com/2008/01/08/drupal-without-nodes" talking about the "proxy-node pattern where you generated nodes to represent the legacy data". It sounds like it could solve part of my problem.
I´ll be very pleased if somebody could give me more information about this topic (or give some orientation). Or if you now other ways to load data from a web service into a Drupal node.
Thanks in advance!
Not sure what you need to do
Please clarify a couple things you're trying to do:
Hi Robert, thanks for
Hi Robert,
thanks for replying so quickly.
To better explain my needs can be done wiyh the following phrase from the upper clients module specification "We need the parallel set of implementations: local read/write access to external data sources (via web services)."
We allready found how to contact the web service with the soap client module to retrieve data and invoque rpc. The problem now is to administer the web service data from the Drupal UI and load data from the web service into durpal nodes.
I'm not sure if we really need nodes, and with some data I'm sure that it's not going to be needed. But for the main data, which are hotels, we need to show them in page and in teaser mode, for that I thougt that this could be done easier using nodes, becouse their native functionality and that well made easier to administer this data.
For other data I found the lullabot scaffolding module which will do very well.
I other words, I don´t know the better way to load data in nodes. I know that it can be done by hook nodeapi. But, what if I don't have nid?
Besides my problem, I will be thankfull if you can explain something more about "proxy-node pattern where you generated nodes to represent the legacy data".
Thank you in advance, and forgive my english, I know it can be making it a bit tough.
Alessandro Mascherpa.
Creating nodes
Hi Alessandro,
read here for a guide for creating nodes programmatically: http://civicactions.com/blog/cck_import_and_update
The "proxy node" pattern simply means that you have a node for every foreign object. You might have to have an extra table to keep track of the foreign object's primary id. It would be possible to use CCK for this and fill it in using the node creation technique above. Then you can use hook_nodeapi to detect changes in the node and send them to the foreign database via SOAP.
You usually only make nodes if you need some of the things that nodes can do: comments, revision control, CCK fields, Views integration.
In the right way
Hi Robert,
I really think, and hope not being mistaken, that I'm in the rigth way to solve my problem.
I found your reference a bit difficult to understand, but I'll give it a bit of time get it better. Througt it I found the following article by Jeff Eaton: http://www.lullabot.com/articles/quick_and_dirty_cck_imports
With it and your last post I think to be able to solve my problem. I promise to come back when it's done (if everything went rigth) so my experience can serve to somebody else (if somebody else find himself in such a weird situation).
I'm so grateful for all your help, and I just would like to give it some day in return.
Regards,
Alessandro Mascherpa.