Extension interfaces in lazr.restful

What we'd like here is to allow for some interfaces to be marked as extending others. For example, we could have an IBugTarget interface which extends IProduct, so that webservice clients would see all of IBugTarget fields/operations on IProduct. In order for that to work, though, the webservice-generated adapters would need to know which fields/operations come from IBugTarget so that it could adapt IProduct into an IBugTarget before accessing them.

How lazr.restful works

The lazr.restful decorators just mark what entries/collections/fields/operations to export, as well as annotating them with webservice-specific details. Those annotations are later picked up by register_webservice() (which is called by webservice:register in zcml), which generates IEntry/ICollection/IResource*Operation adapters.

For every exported entry, lazr.restful generates one adapter (subclassing BaseResourceOperationAdapter or BaseFactoryResourceOperationAdapter) for each of its operations (i.e. exported methods) and a single adapter (subclassing Entry) for all fields (i.e. attributes) of that interface.

Collections have a single method/property representing its default contents and one or more operations, so there's an ICollection adapter generated for collections, which uses BaseCollectionAdapter.

What happens in the case of attribute access

The IEntry generated adapters use lazr.delegates._delegates.Passthrough or PropertyWithMutator when accessing fields, so we need to either extend them to adapt the context when needed or create new versions that always do so and are used only for fields that come from extension interfaces.

What happens for collections

The adapters generated for collections subclass BaseCollectionAdapter, which does a getattr(content_obj, method_name). I don't think we need to worry about this, though, as collections don't have attributes so it doesn't make much sense to have fields in extension interfaces for collections.

I thought this could be needed for scoped collections, but we don't seem to generate ICollection adapters for scoped collections.

  • class IProduct:
    • export_as_entry()
    • ..
    class IHasBugs:

What happens for operations

Here it's BaseResourceOperationAdapter.call() which does the dispatching to the content class so we'll need to either extend it to adapt the content class or create a new one which does that. (BaseFactoryResourceOperationAdapter may also be used)

That is the base class used for the operation adapters (generate_operation_adapter), so we could pass an extra Interface there to have the content class adapted to before it does the getattr(). Should be easy.

How can we support extension interfaces?

I basically see two ways of doing this.

  1. Combining the webservice-generated adapter of our content interface (e.g. IProduct) with the webservice-generated adapter of the extension (e.g. IBugTarget)
  2. Combining our content interface with the extension interface right before passing them to lazr.restful for the creation of the adapter

Combining adapters

This consists of letting lazr.restful treat our extension interfaces in the same way it treats regular exported interfaces, and once we have adapters for everything we generate new ones combining the one for the content interface with the one for the extension interface.

To generate new adapters we'd just create new classes that inherit from the content/extension adapters (the new adapters would also need new schemas, but we can merge the content/extension schemas using the same technique).

We would also need some changes to the adapter-generation code of lazr.restful so that the adapters of extension interfaces adapt their context (e.g. adapt IProduct into IBugTarget) before doing anything.

Pros:

  1. Minimal changes needed to have lazr.restful generate adapters for extension interfaces

Cons:

  1. Need to make sure that, for every content/extension pair, we combine the adapters of all webservice versions

Combining interfaces

Before generating any adapters, laz.restful would combine extension interfaces with the interfaces they extend into new interfaces and then use these new interfaces to generate the adapters.

Cons:

  1. Probably more complicated to do the adapter-generation as we'd need to figure out, for each field/operation, if it comes from the content or the extension interface -- and that's not trivial because after we combine the interfaces we no longer know which is the content and which is the extension interface.

internal/archive/Platform/Infrastructure/LazrRestfulExtensionInterfaces (last modified 2013-08-23 12:11:37)