Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

Talk:Web services: Difference between revisions

From MoodleDocs
 
(8 intermediate revisions by 3 users not shown)
Line 104: Line 104:
Just curious what I missed - what was the reason for implementing a web services auth plugin? I agree with what Eloy said in chat - the problem with this approach is that you'd probably want to apply any restrictions that might be implemented in the plugin to be applied to users with traditional authentication? --[[User:Dan Poltawski|Dan Poltawski]] 01:32, 3 February 2009 (CST)
Just curious what I missed - what was the reason for implementing a web services auth plugin? I agree with what Eloy said in chat - the problem with this approach is that you'd probably want to apply any restrictions that might be implemented in the plugin to be applied to users with traditional authentication? --[[User:Dan Poltawski|Dan Poltawski]] 01:32, 3 February 2009 (CST)


== Design state at the 16 Feb 2009 ==
== About the "how it works" section ==
=== Core functions ===
Moodle web service API will call Moodle core functions. These functions are located in different lib.php. These functions are independent of the web service structure.
My opinion: all good. No need to change


=== Web service API ===
Just two suggestions:
The web service API is included into every external.php file. Every moodle folder (mod/forum, user, ...) can have its own external.php file.
==== General design ====
These external.php files contains a class that can't be extended. The class functions are static. The class functions call the core functions. The class functions check user capability.<br>
My opinion: all good.
==== Web service description====
These external.php contains a description array. This description describes web service parameters (mandatory, optional, return, types) for every web service functions.<br>
My opinion: if we use third-party clients/servers as SOAP/XMLRPC Zend we don't need it. These clients/servers check the phpdoc in order to know the web service description. <br>
However these description are mandatory if we do choose to implement our own client/server. They are also mandatory for some third party client/server as Zend_amf. This AMF client/server needs some value object class. In order to generate these classes,  Moodle needs to know the structure of parameters/returned objects. Note that we could use the phpdoc instead of the a description array. However the implementation seems much more complex. For example Zend framework use a Reflection class that is not easy to understand, even less to use. I could not find any explanation how to parse the phpdoc...


=== The clients/servers===
* In point 1: instead of sending login + password I'd recommend sending login + hash(login and password) or something like that, just prevent sending plain password (and easily identificable). Assuming that SSL isn't a MUST, of course.
==== REST ====
* In point 10: together with the final response to the client with the results I'd recommend to send a renewed token, avoiding to use the same token all the time. That way, possibilities to explode a stolen token are really minimised.  
REST has no defined W3C specification but an architecture. Many REST client/server implementation are not RESTful. By RESTful I mean that retrieving data should use GET, creating data POST, deleting data DELETE and updating data PUT.<br>
At this moment we don't have in HEAD any RESTful client/server. The one we created is not RESTful (use mainly POST) and the Zend one is going to be deprecated in the futur Zend framework release.<br>
My opinion: I had a look at Java REST implementation, and they use annotation on the function (@GET, @POST). That make sens that we need this kind of information about the web services functions. So if we want to build a RESTful server it should be into the description or into the phpdoc.<br>
Zend are writing some new RESTful functions but it seems to use intensively their other library. Wait to see.
I couldn't find a RESTful implementation.<br>
At this moment our own REST server cannot manage array as parameter. We cannot for example call function to delete multiple user yet.


==== SOAP ====
Just in case those haven't been considered. [[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 22:13, 2 September 2009 (UTC)
At this moment SOAP client/server, as XMLRPC client/server, using the Zend client/server are the close working client/server we have. Their limitation are that the client need to mention the path (moodle folder) of the external.php file.<br>
I created a SOAP client/server with WSDL generator. Then I discover that the Zend framework were working on the same, so I implemented another client/server using it.<br>
My opinion: I think it is better to use the Zend SOAP server as their community will work to improve it and fix it. It will be good also to have a Zend SOAP server that load all Moodle function into one WSDL (no need Moodle path). Then see how are the performance. The reason for that is that the client could request only one WSDL file. Zend are also working on a way to have multiple service into the WSDL, that would allow us to still use Moodle path (good performance) and to deliver only one WSDL file to the client.<br>
I don't think we should use nuSOAP as the project is for PHP4, and have less support these days.


==== XMLRPC ====
THe Zend client/server has good reputation, and it is easy to use. It's also using phpdoc.<br>
My opinion: we should go for it.


==== AMF ====
Zend and Adobe released a client/server for AMF. The main problem of this solution is that require PHP value object class in order to map the parameter (and returned object) to the Flex value object. Either we decide to write all these value object class for all web services functions either we generate them (need some description)


== Brainstorm for WS Roadmap ==


=== My overall opinion ===
Current
I think we should offer a SOAP and XMLRPC solution using Zend clients/servers.<br>
Then come the decision to keep the web service description arrays. Some kind of description are currently needed for REST and AMF. It could be by phpdoc but it's not easy to implement.
==== REST ====
Do we want a RESTful server or not ? A RESTful server is more difficult to implement, but most important it requires for additional description about the function (is it a GET, POST, DELETE, PUT?).


==== AMF ====
user:
decision can be delayed later on, Flex still should be able to communicate easily with SOAP.
 
    create_users()
    delete_users()
    update_users()
    get_users_by_id() - returns a list of full user objects specified by user ids (only useful for site admin accounts)
    get_course_participants_by_id() - returns a list of full user objects in specific courses (that you can see)
    get_users_by_courseid() - returns some subset of full user objects from a course (perhaps by group or capability)
 
    * get_completed_users_per_course()  ?
    * get_completed_courses_per_user()  ?
    * get_users (allows admin to search by various keys)
   
course:
 
    get_courses()
    create_courses()
 
    * update_courses()  - including moving categories
    * delete_courses()
    * get_course_contents()  - including sections, activities etc
    * get_recent_activity()  - also works for site course
 
group:
 
    create_groups()
    get_groups()
    get_course_groups()
    delete_groups()
    get_groupmembers()
    add_groupmembers()
    delete_groupmembers()
 
roles:
 
    role_assign()
    role_unassign()
 
enrol:
 
    get_enrolled_users() - get some minimal information about the users enrolled in a course (DEPRECATED)
    get_users_courses() - get list of course ids that a user is enrolled in (if you are allowed to see that)
 
 
enrol/manual:
 
    manual_enrol_users()
   
    * manual_unenrol_users()
    * manual_update_enrolment()
 
enrol/cohort
 
    * cohort_enrol_cohort()
    * cohort_unenrol_cohort()
 
enrol/self
 
    * self_enrol_users()
    * self_unenrol_users()
 
webservice:
 
    get_siteinfo_parameters()
 
message:
   
    send_instantmessages()
    * get_instantmessages() - params for unread etc
    * create_contacts()
    * delete_contacts()
 
notes:
    create_notes()
    * delete_notes()
   
events:
 
    * create_event() - triggers an event
 
calendar:
 
    * create_events()
    * update_events()
    * delete_events()
 
private files:
 
    get_files()
    upload()  - DEPRECATED
    * create_files() - can also do folders via a flag
    * update_files()
    * delete_files()
 
 
categories:
 
    * create_categories()
    * delete_categories()
    * update_categories()
    * get_categories()
 
grades:
 
    * get_grades()  - for user, course, activity etc
    * get_scales() 
    * get_outcomes()
    * update_grades()  - including feedback
    * create_grades()
 
ratings:
 
    * get_ratings()
    * create_ratings()
    * update_ratings()
    * delete_ratings()
 
comments:
 
    * get_comments()
    * create_comments()
    * update_comments()
    * delete_comments()
 
forum:
    * get_forums()
    * create_forums()
    * update_forums()
    * delete_forums()
    * get_discussions()
    * create_discussions()
    * update_discussions()
    * delete_discussions()
    * get_posts()
    * create_posts()
    * update_posts()
    * delete_posts()

Latest revision as of 09:25, 12 August 2011

general remarks about OUTPUT of operations:

Is true of false informative enough ? Should the API have some sort of "last_error" function giving back more details such as "non existing course, non existing user, duplicate entry ...) Shall we consider that affecting a role that already exist or removing role that do not exist as an error (output=false) or not (output=true)

yep, agree, I'd result always one well defined $result object, with his status, error code and error msg (and perhaps, the whole original WS request encapsulated for easier debugging).
Eloy Lafuente (stronk7) 19:55, 21 December 2007 (CST)

I agree but throught the WS we need to be be carefull with the feedback we give to errors, due to security issues. --Ludo (Marc Alier) 14:14, 22 December 2007 (CST)

some WS to be able to... build courses ?

Perhpas it would be a great idea to start thinking about some WS functionalities like this:

  • add_course, delete_course, reset_course
  • add_section, delete_section, show_section, hide_section
  • add_activity, delete_activity, show_activity, hide_activity

Apart from the WS utility itself: automated creation of courses... it would help (or force, as we prefer), to better encapsulate course/section/modules internals, 100% isolating the creation and configuration from the frontend (forms, WS...). It implies some decisions, like upwards compatibility of old modules (although I think it can be maintained)... and so on, you know.

Just one idea to analyse. But with benefits in the end, IMO. Eloy Lafuente (stronk7) 20:02, 21 December 2007 (CST)

I do agree with this ; this would also allow creation of specific courses, customized for some users, depending of their "performances" in external evaluations methods ; when the ePortfolio API will be finalized in Moodle core's, accessing the portfolio of any user would be needed. Patrick Pollet 04:21, 30 December 2007 (CST)

the problem of ID of entity to fetch:

In the list of API calls defined in this page, Moodle entity to process (user,course,grade,event ...) is identified by an ID, that is implicitly the Moodle's internal id field used in the database. This raises a problem since it is very likely that in the "external SIS" talking to Moodle across the Web Service, the entity will be identified by another attribute such as an idNumber (user,course), a short name (course) or even an username (login of user).

So either we must provide :

  • extra API calls to convert external identifiers to Moodle's internal id, such as user_id_from_idnumber, user_id_from_username, course_id_from_shortname ..., at the cost of extra calls before the real one ; three calls would be needed to enrol student "CS2121212", to course "JAVA_101".
  • extra API functions such as delete_user_byusername, get_course_by_idnumber , enrol_student_byidnumber_tocourse_byshortname ....
  • or add parameters to the current API calls specifiying what identifier we are using such as delete_user(id,idField) with idField being a string that could be "id","idnumber","username","email" ... or enrol_student(sid,"idnumber",cid,"shortname") . This is the approach in the current SOAP implementation (with some calls of the previous type (get_user_byusername()...)

Patrick Pollet 04:21, 30 December 2007 (CST)

output of operations: adding URLs?

In addition, I suggest to return the full URL towards every Moodle entity, either added or got.

Today, in my uPortal installation, I request for Moodle database and display a list of courses in which the connected user is enrolled. Of course I provide links to these courses so that he can directly access. In addition I display a list of the last changes in these courses, again with links to the resources/activities.

If the URLs are returned by the WS then the calling service has nothing to know about Moodle URLs. Else it has to build every URL by aggregating Moodle alias + adequate PHP script + entity ID

Implementation feedback

[Feedback] Speaking as someone who will be using this API to integrate with another line of business application I would recommend the following changes.

The add_user method should really be a 'SetUser' ie it will create the record if it does not exist or update it it if it does. The ID that is passed into the method would be *our* primary key which Moodle would use to recognise the user record within Moodle. We would not normally need to know anything about the Moodle primary key.

One option to make the web services maintainable is to accept XML as a parameter and not a structure or custom object. That way you can define an XSD which will define required\optional parameters. Equally if you include a version number you can change the XML to add new features without having to redefine the web service signature itself (and hence clients will not have to recompile their code). So you may receive a SetUser call with XML V1.0 and another with XML V2.0 on the same webservice but from two different clients supporting different versions of the API.

The method *must* accept an array of users - it's a little trickier to set this up in the client code but is much more scalable (if there's some good sample code then this is easier anyway). The latency (round trip time) with web service calls is high so you want to do as much as you can in a single call. We have a customer who wants us to deploy Moodle to 40,000 users - we're not going to do that using individual calls.

Would it be possible to expand on the existing web service work rather than produce something completely new? We're investing a lot of effort with these services and we wouldn't like to have to re-do that work. [/Feedback]

Using web services to allow client side code such as js, Flash/Flex or java to communicate with Moodle

I understand these web services should also allow client side code to communicate with Moodle.

I imagined that web services used by client side code should be authenticated with a 'session key'. When the server outputs a page with such an object in it then a random session key string should be created and passed into the client side code through the html that embeds/outputs the object in a page. I think the session key should be associated with a user and probably a Moodle context. Then the context can be used within block related code or activity module related code to know when for example the client side code passes a log message back to Moodle or when it passes a grade back to Moodle that the grade is for such and such an activity. For web services for Flash I thought an appropriate way to do this would be to have an array of objects of session keys and there associated context/other data in the $SESSION object.

Config settings

This are the main switches / global config settings to control what's available:

  • WS Global switch:
    • Check for global$CFG->enablewebservices properly (outer layer).
  • WS Capability:
    • Create the 'moodle/site:use webservices' capability and check for it in medium layer globally (after auth). Default to admin role only.
  • WS Protocol/Function switches:
    • Create new setting $CFG->enabledwsprotocols, with list of enabled protocols (default to none) and check request against that (outer layer)
    • Create new setting $CFG->enabledwsfunctions, with list of enabled functions (default to none) and check request against that (on function invocation)
  • WS Function capabilities?? Not sure (overlay with normal capabilities). -1
  • Other restrictions (fixed list of users, IP, SSL required...)

Eloy Lafuente (stronk7) 08:44, 27 January 2009 (CST)

IP restriction

We'd like a feature to restrict web services to a list of specified IP addresses for additional security. On our services we just have like this:

$allowedaddresses=preg_split('/[,\s]+/',$CFG->privilegedserverips,-1,PREG_SPLIT_NO_EMPTY);
if(!in_array($_SERVER['REMOTE_ADDR'],$allowedaddresses,true)) {
   error('You are not permitted to access this page.');
}

(note I think REMOTE_ADDR may not be reliable on all systems? eh whatever)

so we use a comma-separated list of IP addresses that are permitted. Something similar would be good for this.

Maybe it should really go into the user section ie a way to restrict a specific user to given IPs? Of course ideally it would then support ranges/wildcards as well, etc, but then it becomes a lot of work instead of a simple option to the webservice stuff...

Sam marshall 08:52, 27 January 2009 (CST)

There is already a nice address_in_subnet function in lib/moodlelib.php, which gives you a flexible way to check IP addresses. Normally used in combination with getremoteaddr(), also in moodlelib.--Tim Hunt 20:40, 27 January 2009 (CST)

Why the Auth Plugin?

Just curious what I missed - what was the reason for implementing a web services auth plugin? I agree with what Eloy said in chat - the problem with this approach is that you'd probably want to apply any restrictions that might be implemented in the plugin to be applied to users with traditional authentication? --Dan Poltawski 01:32, 3 February 2009 (CST)

About the "how it works" section

Just two suggestions:

  • In point 1: instead of sending login + password I'd recommend sending login + hash(login and password) or something like that, just prevent sending plain password (and easily identificable). Assuming that SSL isn't a MUST, of course.
  • In point 10: together with the final response to the client with the results I'd recommend to send a renewed token, avoiding to use the same token all the time. That way, possibilities to explode a stolen token are really minimised.

Just in case those haven't been considered. Eloy Lafuente (stronk7) 22:13, 2 September 2009 (UTC)


Brainstorm for WS Roadmap

Current

user:

   create_users()
   delete_users()
   update_users()
   get_users_by_id() - returns a list of full user objects specified by user ids (only useful for site admin accounts)
   get_course_participants_by_id() - returns a list of full user objects in specific courses (that you can see)
   get_users_by_courseid() - returns some subset of full user objects from a course (perhaps by group or capability) 
   * get_completed_users_per_course()  ?
   * get_completed_courses_per_user()  ?
   * get_users (allows admin to search by various keys)
   

course:

   get_courses()
   create_courses() 
   * update_courses()  - including moving categories
   * delete_courses()
   * get_course_contents()  - including sections, activities etc 
   * get_recent_activity()  - also works for site course

group:

   create_groups()
   get_groups()
   get_course_groups()
   delete_groups()
   get_groupmembers()
   add_groupmembers()
   delete_groupmembers() 

roles:

   role_assign()
   role_unassign() 

enrol:

   get_enrolled_users() - get some minimal information about the users enrolled in a course (DEPRECATED)
   get_users_courses() - get list of course ids that a user is enrolled in (if you are allowed to see that)


enrol/manual:

   manual_enrol_users() 
   
   * manual_unenrol_users()
   * manual_update_enrolment()

enrol/cohort

   * cohort_enrol_cohort()
   * cohort_unenrol_cohort()

enrol/self

   * self_enrol_users()
   * self_unenrol_users()

webservice:

   get_siteinfo_parameters() 

message:

   send_instantmessages() 
   * get_instantmessages() - params for unread etc
   * create_contacts()
   * delete_contacts()

notes:

   create_notes()
   * delete_notes()
   

events:

   * create_event() - triggers an event

calendar:

   * create_events()
   * update_events()
   * delete_events()

private files:

   get_files()
   upload()  - DEPRECATED
   * create_files() - can also do folders via a flag
   * update_files()
   * delete_files()


categories:

   * create_categories()
   * delete_categories()
   * update_categories()
   * get_categories()
  

grades:

   * get_grades()  - for user, course, activity etc 
   * get_scales()  
   * get_outcomes()
   * update_grades()  - including feedback
   * create_grades()

ratings:

   * get_ratings()
   * create_ratings()
   * update_ratings()
   * delete_ratings()

comments:

   * get_comments()
   * create_comments()
   * update_comments()
   * delete_comments()

forum:

   * get_forums()
   * create_forums()
   * update_forums()
   * delete_forums()
   * get_discussions()
   * create_discussions()
   * update_discussions()
   * delete_discussions()
   * get_posts()
   * create_posts()
   * update_posts()
   * delete_posts()