We're developing a Tincan LRS module for Drupal, and working to use the Services module as the backbone for the REST server.
Tincan REST API spec: https://github.com/adlnet/xAPI-Spec/blob/master/xAPI.md#datatransfer
Prototype module code: https://github.com/jackrabbithanna/tincan_lrs

Forgive me if this question has been asked, I could not find the information I was looking for via Google.

I'm hoping someone can provide me with an approach vector for what we're trying to accomplish.

We need different endpoints/resources that share some path components.

We've got a prototypical Statement API working, using Services, which basically provides the CRUD+I resources for a Statement entity type. Even works with CORS requests from embedded javascript packages in a page. Super so far.

So via configuration I create an xAPI endpoint with path 'xAPI', then the module code provides resource 'statement', and the CRUD+I callbacks.

so that
GET "/xAPI/statements" returns all statements
GET "/xAPI/statements/1234" would return the statement with id 1234
PUT "/xAPI/statements/1234" would create the statement with id 1234 using the request content body
POST "/xAPI/statements" would create statements based on the json in the request content body
POST "/xAPI/statements?method=PUT" used for CORS requests, the method parameter tell HTTP request type to use, and we are able to handle this fine.

Now there are several other resources that need to be created according to spec, as seen here:
https://github.com/adlnet/xAPI-Spec/blob/master/xAPI.md#docapis

Resource "activities"
http://example.com/xAPI/activities
This would be used for GET requests, to retrieve activity entities

http://example.com/xAPI/activities/1234
This would GET the activity entity with id 1234

Resource "activities/profile"
This would be used for GET,PUT,POST,DELETE requests to create/update/delete/index activity profile entities...

So I really want 2 separate resources, but the resources share the same first path component? Is this possible?

Since targeted actions, and actions both only take POST requests (or so I've read), then it doesn't seem like those are the way to go.

I'm not worried about Services actually fetching the entities or manipulating them in any way, I just need the right callbacks to fire and I can take it from there.

In testing/playing, it seems I can have one or the other, but not both at the same time.

Any criticism, help, ideas, are appreciated.

Comments

kylebrowning’s picture

Can you provide some insight on why /profiles/1234 would not be suitable to your needs? Why does it need to be prefixed with activities?

If profiles have some sort of activity relationship, why not just add an argument to pass in an activity?

kylebrowning’s picture

Or better yet, pass a type to profile of 'activity', or 'agent'

markusa’s picture

Well the module needs to conform to the TinCan 1.0 API spec,
the endpoint or resource paths which should be available are listed here:
https://github.com/adlnet/xAPI-Spec/blob/master/xAPI.md#AppendixF

So to conform, those paths need to produce the specified behavior, just trying to figure how it might be done.

markusa’s picture

As I'm coding it right now, the State, Activity Profile, and Agent Profiles are separate entities, although the data structures are not very dissimilar and could be adapted to be one entity. I'd "like" to be able to do it with the 3 separate entities though.

Activities need to be separate.

markusa’s picture

Ok, lots of experimentation later, I think I can handle almost all the situations by creating an get handler for 'activities', and just parsing the first path component, and if 'state' or 'profile' is the value of the first path component, ship the data and params off to an appropriate processor.

I'm thinking that very much the same thing could be done for PUT and POST.

Now, a point I'm stuck at is being able to have PUT requests that have no additional path components after the resource.

when I look at the code:
http://www.drupalcontrib.org/api/drupal/contributions!services!servers!r...

I see that PUT handling mandates at least one path component.

So I'm wondering....
Is there a hook that fires before the controller tries to map out which function should be called in
protected function resolveController($resource, &$operation) {

I'm not seeing a call to module_invoke() in that function....

Anyway we can get a hook in there that allows altering of the canonical path components before they are processed?

In this way a dummy value could be created for a path component, and PUT calls would fire the 'update' callback.

Are you inclined for the inclusion of such a hook in Services Rest Server?

The only other way I see doing this is extending the RestServer class and overriding the method
resolveController()

I could basically copy the code for the RestServer portion, alter the thing a bit, change the names, and a 'Tincan Rest Server'....this may be the way to go in the long run for the Tincan LRS project...would allow for further customization later.

Thoughts?

markusa’s picture

So what i ended up doing is just making a "Tincan REST Server" module that is basically the minimum amount of code copied from the rest_server module, and I extend the provided classes to override the method:
protected function resolveController($resource, &$operation) {

I only needed to extend the Factory class, and the RESTServer class, override the method and boom I can change whatever I want.

If you are interesting code to increase the flexibility of the resolveController method, like by adding a hook invocation in there, I'm game to code it and include a patch to the rest_server module, but if not and you are happy with what is there, i can develop independently along lines required specifically for a Tincan LRS.

Great module design, god bless OOP.

markusa’s picture

Status: Active » Postponed
kylebrowning’s picture

Would love to see some alter code!

markusa’s picture

This doesn't help my original question yet, but I'm still working on that....will be looking into the resource selection process.

After the resource has been determined, it would be flexible to be be able to use alternative rules to choose the operation

So around line 590 services/servers/rest_server/includes/RESTServer.inc
class RESTServer
method resolveController
inserting some code to invoke any module's implementation of a hook_rest_server_operation_alter()

// insert this
    foreach (module_implements('rest_server_operation_alter') as $module) {
      $function = $module . '_rest_server_operation_alter';
      $function($request_method, $canonical_path_array, $this, $resource, $operation_type, $operation);
    }
 // end insert   
    if (empty($operation_type) || empty($operation) || empty($resource[$operation_type][$operation])) {
      return FALSE;
    }
    

You may or may not want to include the RESTServer object in the hook calls, but this would allow a knowledgeable user the ability to choose their operation based on all the information available.

So a implemented a hook in a custom module:

function YOUR_MODULE_NAME_rest_server_operation_alter($request_method, $canonical_path_array, $RESTServer, $resource, &$operation_type, &$operation) {
 
  // allow put requests with no path parameter count limit to still go through to the operations update callback  
  if($request_method == 'PUT'  && isset($resource['operations']['update']))
  {
    $operation_type = 'operations';
    $operation = 'update';
  }
}
kylebrowning’s picture

Status: Postponed » Closed (fixed)