Closed (fixed)
Project:
Services Tools
Version:
7.x-3.x-dev
Component:
Code
Priority:
Normal
Category:
Feature request
Assigned:
Reporter:
Created:
1 Apr 2011 at 22:04 UTC
Updated:
23 Apr 2011 at 09:41 UTC
Jump to comment: Most recent file
Comments
Comment #1
gddI have not actually done this, but it should work.
1) Define two resources. 'foo_1.0' and 'foo_1.1'. This can be in two modules, or separately defined in one.
2) Define the callbacks in each resource. For the ones that stay the same, call the same callbacks. For different ones, call different callbacks.
3) Define two endpoints. '/api/1.0' and '/api/1.1'
4) In the 1.0 endpoint, enable the 'foo 1.0' resource, and alias it to foo.
5) In the 1.1 endpoint, enable the 'foo 1.1' resource, and alias it to foo.
6) Profit
This is probably worth getting detailed documentation about into the handbook. Let us know if you have problems.
Comment #2
boombatower commentedThat makes sense. I think I'll set it up with the active endpoint/api in the main module and a historical module for the backwards compatible endpoints.
I should be implementing this in the next week or so and I will report back how it turned out.
Comment #3
boombatower commentedSeems to work fairly well. I tried to setup a nice clean way of storing the historical api sets of resources and endpoints in a separate module. It might be something that services could support either as a sub module or maybe I should make a separate project.
Still not sure whether it is best to store full copies of the resources and endpoints or just make "diffs" like update functions.
Example usecase:
1. Release api as 1.0.
2. Change the signature of a method and release as 1.1.
3. Create historical module to provide 1.0.
4. New API release 1.2.
5. Historical 1.1.
The historical module then could either contain two sets of copies of resources and endpoints (4 total) or simply a copy of the 1.0 that it uses as a basis. The 1.0 resources and endpoints would simply override the necessary information and provide wrapper methods that call the current API. When 1.1 becomes historical those wrappers may need to be updated to point to 1.1 historical which points to 1.2 (current) API. The 1.1 resource and endpoint definitions would then start with 1.0 as a basis and modify again what is needed.
Just trying to make sure this is setup is such a way that it is easy to maintain. So lets look at a detailed example (obviously more can change then signature, but should follow same rules).
Base services API:
-- 1.0 --
api/foo(int, string)
api/bar(struct)
Changes (per release):
-- 1.1 --
api/foo(string, string)
api/bar(struct)
-- 1.2 --
api/foo(string, string)
api/bar(struct)
api/rawr()
-- 1.3 --
api/foo(string, string)
api/bar(struct)
api/rawr(int)
-- 1.4 --
api/foo(string, string)
api/bar(struct)
api/rawr()
The historical module will then alter the base module endpoint to make it whatever the most current version number is (which should be a constant or calculated value in historical module). The historical module then needs to provide 1.0 - 1.3.
-- 1.0 --
So the 1.0 structure is exact copy of what was in 1.0 release except that foo points to foo_1_0() and bar is left alone (aka points to current) since 1.0 differs from current in that it's foo has a different signature.
-- 1.1 --
Points to all current, just missing rawr() like 1.0.
-- 1.2 --
Points to all current.
-- 1.3 --
rawr points to rawr_1_3().
-- 1.4 --
Not present in historical module since base module provides it as active api. The historical module just sets it as 1.4.
If you have more complex setups you may need to update something like rawr_1_3($int) to call rawr_1_4() which then points to current (1.5, rawr()).
I think this makes more sense then having straight copies of the resources and endpoints and then I guess just tweaking the methods in them? and/or having entire copies of the methods? That seems to have little flexibility if you were to change the code inside those methods it would be a mess. I think stepping it up like this resembles updates system and makes sense.
So I ended up creating some helper functions that convert a previous version resource/endpoint to the version specified so you can setup a bunch of "diff" functions that start out by call previous function (or base) and then running the helper function to update name, title, version, array keys ect then overriding anything that is necessary. So the historical module simply needs to implement the services hooks and then auto call all these diff functions to generate a set of historical resources/endpoints.
I get the feeling that although this is a good practice (like writing update hooks) not everyone will do this so probably just start a new project to maintain a minimal sub-services API that provides the helper functions and update_N() style hooks for each version the historical module provides.
Thoughts?
Comment #4
boombatower commentedCreated temporary sandbox project to build an API to make historical services APIs simpler http://drupal.org/sandbox/boombatower/1117838.
I'll post patches here.
Comment #5
boombatower commentedTest locally and seems to work well.
http://i.imgur.com/0rrqD.png
The latest shows up on top since the key is just conduit and the others are all suffixed with the version. I figured that is best since it is the only one you should modify normally and if any integration modules rely on the key they won't be broken when the historical information is added.
Comment #6
voxpelli commentedSubscribe
Comment #7
boombatower commentedFor example, in my not yet released module I implement the following so that the current/initial api shows up as 1.0 when my conduit_historical module is enabled.
Then it shows up as 1.0 so clients can start using as 1.0. Once I release a 1.1 I simply copy the 1.0 (from vcs) into the base endpoint hook and create a 1_0 update hook with the 1.0 resources and update the info() hook version to 1.1. (you may also need to make wrapper callbacks depending on the changes)
Next time I just add a update_1_1() and make the "diff" changes.
I spent a few hours going over a number of possibilities and I think this hits the most cases while making it quite simple to use, anyone with a more complex case can simply use hook_services_..._alter() to change the endpoints or resources further since they will all have nice keys. In my example the conduit endpoint would be conduit_1_0, conduit_1_1 and the resources will be the same with the version suffixed. (suppose I could add another drupal_alter() for endpoints and resources with the $version passed)
Side note, in my default install profile I then change the paths (which are defined to be conduit, conduit/1.0, etc) to api/... as follows.
I think that creates a really slick setup that is quite configurable based on which modules (or profile) are enabled (three layers: conduit, conduit_historical, profile).
Comment #8
boombatower commentedLastly, make sure you read through .api.php for details.
Comment #9
boombatower commentedAdded a few extra lines of documentation.
Comment #10
boombatower commentedStraight diff one can use with drush make.
Comment #11
pcarman commentedsubscribing
Comment #12
boombatower commentedIt was decided to maintained as separate project for now. Since the code is functional I'm marking this as fixed, but I'll fix up a few typos and what not before making a release.
Comment #13
boombatower commentedDecided to include a few tools like this in one project.