tripal_ws.rest.inc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. <?php
  2. /**
  3. *
  4. */
  5. function tripal_ws_rest() {
  6. global $base_url;
  7. $ws_args = func_get_args();
  8. // The web services should never be cached.
  9. drupal_page_is_cacheable(FALSE);
  10. // Set some initial variables.
  11. $response = array();
  12. $status = 'success';
  13. $version = 'v0.1';
  14. $message = '';
  15. $api_url = $base_url . '/ws/' . $version;
  16. $page_limit = 25;
  17. $pager_id = 0;
  18. // Set some defaults for the response.
  19. $response['@context'] = array();
  20. // Lump everything ito a try block so that if there is a problem we can
  21. // throw an error and have that returned in the response.
  22. try {
  23. // The services is the first argument
  24. $service = (count($ws_args) > 0) ? $ws_args[0] : '';
  25. switch ($service) {
  26. case 'doc':
  27. tripal_ws_handle_doc_service($api_url, $response);
  28. break;
  29. case 'content':
  30. tripal_ws_handle_content_service($api_url, $response, $ws_args);
  31. break;
  32. case 'vocab':
  33. tripal_ws_handle_vocab_service($api_url, $response, $ws_args);
  34. break;
  35. default:
  36. tripal_ws_handle_no_service($api_url, $response);
  37. }
  38. }
  39. catch (Exception $e) {
  40. watchdog('tripal_ws', $e->getMessage(), array(), WATCHDOG_ERROR);
  41. $message = $e->getMessage();
  42. $status = 'error';
  43. }
  44. // The responses follow a similar format as the AGAVE API with a
  45. // status, message, version and all data in the 'result' object.
  46. /* $response['status'] = $status;
  47. $response['message'] = $message;
  48. $response['api_version'] = $version;
  49. $response['source'] = array(
  50. 'site_name' => variable_get('site_name', 'Unspecified'),
  51. 'site_url' => $base_url,
  52. 'site_slogan' => variable_get('site_slogan', 'Unspecified'),
  53. 'site_email' => variable_get('site_mail', 'Unspecified'),
  54. ); */
  55. // Rather than use the drupal_json_output() funciton we manually specify
  56. // content type because we want it to be 'ld+json'.
  57. drupal_add_http_header('Content-Type', 'application/ld+json');
  58. print drupal_json_encode($response);
  59. }
  60. /**
  61. *
  62. * @param $api_url
  63. * @param $response
  64. * @param $ws_args
  65. */
  66. function tripal_ws_handle_content_service($api_url, &$response, $ws_args) {
  67. // Get the content type.
  68. $ctype = (count($ws_args) > 1) ? $ws_args[1] : '';
  69. $entity_id = (count($ws_args) > 2) ? $ws_args[2] : '';
  70. // If we have no content type then list all of the available content types.
  71. if (!$ctype) {
  72. tripal_ws_get_content_types($api_url, $response);
  73. }
  74. // If we don't have an entity ID then show a paged list of entities with
  75. // the given type.
  76. else if ($ctype and !$entity_id) {
  77. tripal_ws_get_content_type($api_url, $response, $ws_args, $ctype);
  78. }
  79. // If we have a content type and an entity ID then show the entity
  80. else {
  81. tripal_ws_get_content($api_url, $response, $ws_args, $ctype, $entity_id);
  82. }
  83. }
  84. /**
  85. *
  86. * @param $api_url
  87. * @param $response
  88. * @param $ws_args
  89. */
  90. function tripal_ws_handle_vocab_service($api_url, &$response, $ws_args) {
  91. // Get the vocab name.
  92. $namespace = (count($ws_args) > 1) ? $ws_args[1] : '';
  93. $accession = (count($ws_args) > 2) ? $ws_args[2] : '';
  94. // If we have no $namespace type then list all of the available vocabs.
  95. if (!$namespace) {
  96. tripal_ws_get_vocabs($api_url, $response);
  97. }
  98. // If we don't have a $namespace then show a paged list of terms.
  99. else if ($namespace and !$accession) {
  100. }
  101. // If we have a content type and an entity ID then show the entity
  102. else {
  103. }
  104. }
  105. /**
  106. *
  107. * @param $api_url
  108. * @param $response
  109. */
  110. function tripal_ws_get_vocabs($api_url, &$response) {
  111. // First, add the vocabularies used into the @context section.
  112. $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
  113. $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
  114. // Next add in the ID for tihs resource.
  115. $response['@id'] = $api_url . '/vocab';
  116. // Start the list.
  117. $response['@type'] = 'Collection';
  118. $response['totalItems'] = 0;
  119. $response['label'] = 'Content Types';
  120. $response['member'] = array();
  121. // TODO: determine how to get the list of in-house terms that are used
  122. // on the site. This should really only include terms that are used
  123. // as TripalEntity bundle types and that aren't part of another published
  124. // vocabulary.
  125. //$response['totalItems'] = $i;
  126. // Lastly, add in the terms used into the @context section.
  127. $response['@context']['Collection'] = 'hydra:Collection';
  128. $response['@context']['totalItems'] = 'hydra:totalItems';
  129. $response['@context']['member'] = 'hydra:member';
  130. $response['@context']['label'] = 'rdfs:label';
  131. $response['@context']['description'] = 'hydra:description';
  132. }
  133. /**
  134. * Provides a collection (list) of all of the content types.
  135. *
  136. * @param $api_url
  137. * @param $response
  138. */
  139. function tripal_ws_get_content_types($api_url, &$response) {
  140. // First, add the vocabularies used into the @context section.
  141. $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
  142. $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
  143. // Next add in the ID for tihs resource.
  144. $response['@id'] = $api_url . '/content';
  145. // Start the list.
  146. $response['@type'] = 'Collection';
  147. $response['totalItems'] = 0;
  148. $response['label'] = 'Content Types';
  149. $response['member'] = array();
  150. // Get the list of published terms (these are the bundle IDs)
  151. $bundles = db_select('tripal_bundle', 'tb')
  152. ->fields('tb')
  153. ->orderBy('tb.label', 'ASC')
  154. ->execute();
  155. $terms = array();
  156. // Iterate through the terms and add an entry in the collection.
  157. $i = 0;
  158. while ($bundle = $bundles->fetchObject()) {
  159. $entity = entity_load('TripalTerm', array('id' => $bundle->term_id));
  160. $term = reset($entity);
  161. $vocab = $term->vocab;
  162. if (!array_key_exists($vocab->namespace, $response['@context'])) {
  163. // If there is no URL prefix then use this API's vocabulary API
  164. if ($term->urlprefix) {
  165. $response['@context'][$vocab->namespace] = $term->urlprefix;
  166. }
  167. else {
  168. $response['@context'][$vocab->namespace] = $api_url . '/vocab/' . $vocab->namespace . '/';
  169. }
  170. }
  171. // Get the bundle description. If no description is provided then
  172. // use the term definition
  173. $description = tripal_get_bundle_variable('description', $bundle->id);
  174. if (!$description) {
  175. $description = $term->definition;
  176. }
  177. // Add the bundle as a content type.
  178. $response['member'][] = array(
  179. '@id' => $api_url . '/content/' . $bundle->label,
  180. '@type' => $vocab->namespace . ':' . $term->accession,
  181. 'label' => $bundle->label,
  182. 'description' => $description,
  183. );
  184. $i++;
  185. }
  186. $response['totalItems'] = $i;
  187. // Lastly, add in the terms used into the @context section.
  188. $response['@context']['Collection'] = 'hydra:Collection';
  189. $response['@context']['totalItems'] = 'hydra:totalItems';
  190. $response['@context']['member'] = 'hydra:member';
  191. $response['@context']['label'] = 'rdfs:label';
  192. $response['@context']['description'] = 'hydra:description';
  193. }
  194. /**
  195. *
  196. * @param $api_url
  197. * @param $response
  198. * @param $ws_args
  199. */
  200. function tripal_ws_get_content_type($api_url, &$response, $ws_args, $ctype) {
  201. // First, add the vocabularies used into the @context section.
  202. $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
  203. $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
  204. $response['@context']['schema'] = 'https://schema.org/';
  205. // Next add in the ID for tihs resource.
  206. $response['@id'] = $api_url . '/content/' . $ctype;
  207. // Get the TripalBundle, TripalTerm and TripalVocab type for this type.
  208. $bundle = tripal_load_bundle_entity(array('label' => $ctype));
  209. $term = entity_load('TripalTerm', array('id' => $bundle->term_id));
  210. $term = reset($term);
  211. $vocab = $term->vocab;
  212. if (!array_key_exists($vocab->namespace, $response['@context'])) {
  213. // If there is no URL prefix then use this API's vocabulary API
  214. if ($term->urlprefix) {
  215. $response['@context'][$vocab->namespace] = $term->urlprefix;
  216. }
  217. else {
  218. $response['@context'][$vocab->namespace] = $api_url . '/vocab/' . $vocab->namespace . '/';
  219. }
  220. }
  221. // Start the list.
  222. $response['@type'] = 'Collection';
  223. $response['totalItems'] = 0;
  224. $response['label'] = $bundle->label . " collection";
  225. // Get the list of entities for this bundle.
  226. $query = new EntityFieldQuery;
  227. $query->entityCondition('entity_type', 'TripalEntity')
  228. ->entityCondition('bundle', $bundle->name)
  229. ->propertyOrderBy('title', 'DESC')
  230. ->pager(10);
  231. // Iterate through the entities and add them to the list.
  232. $results = $query->execute();
  233. $i = 0;
  234. if (isset($results['TripalEntity'])) {
  235. $entities = entity_load('TripalEntity', array_keys($results['TripalEntity']));
  236. foreach ($entities as $entity) {
  237. $response['member'][] = array(
  238. '@id' => $api_url . '/content/' . $ctype . '/' . $entity->id,
  239. '@type' => $vocab->namespace . ':' . $term->accession,
  240. 'label' => $entity->title,
  241. 'itemPage' => url('/bio-data/' . $entity->id, array('absolute' => TRUE)),
  242. );
  243. $i++;
  244. }
  245. }
  246. $response['totalItems'] = $i;
  247. // Lastly, add in the terms used into the @context section.
  248. $response['@context']['Collection'] = 'hydra:Collection';
  249. $response['@context']['totalItems'] = 'hydra:totalItems';
  250. $response['@context']['member'] = 'hydra:member';
  251. $response['@context']['label'] = 'rdfs:label';
  252. $response['@context']['itemPage'] = 'schema:itemPage';
  253. }
  254. /**
  255. *
  256. * @param $api_url
  257. * @param $response
  258. * @param $ws_args
  259. */
  260. function tripal_ws_get_content($api_url, &$response, $ws_args, $ctype, $entity_id) {
  261. // First, add the vocabularies used into the @context section.
  262. $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
  263. $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
  264. $response['@context']['schema'] = 'https://schema.org/';
  265. // Get the TripalBundle, TripalTerm and TripalVocab type for this type.
  266. $bundle = tripal_load_bundle_entity(array('label' => $ctype));
  267. $term = entity_load('TripalTerm', array('id' => $bundle->term_id));
  268. $term = reset($term);
  269. $vocab = $term->vocab;
  270. if (!array_key_exists($vocab->namespace, $response['@context'])) {
  271. // If there is no URL prefix then use this API's vocabulary API
  272. if ($term->urlprefix) {
  273. $response['@context'][$vocab->namespace] = $term->urlprefix;
  274. }
  275. else {
  276. $response['@context'][$vocab->namespace] = $api_url . '/vocab/' . $vocab->namespace . '/';
  277. }
  278. }
  279. // Get the TripalEntity and attach all the fields.
  280. $entity = entity_load('TripalEntity', array('id' => $entity_id));
  281. field_attach_load('TripalEntity', $entity);
  282. $entity = reset($entity);
  283. // Next add in the ID and Type for this resources.
  284. $response['@id'] = $api_url . '/content/' . $ctype . '/' . $entity_id;
  285. $response['@type'] = $vocab->namespace . ':' . $term->accession;
  286. $response['label'] = $entity->title;
  287. $response['itemPage'] = url('/bio-data/' . $bundle->id, array('absolute' => TRUE));
  288. // Get information about the fields attached to this bundle and sort them
  289. // in the order they were set for the display.
  290. // TODO: should we allow for custom ordering of fields for web services
  291. // or use the default display ordering?
  292. $fields = field_info_instances('TripalEntity', $bundle->name);
  293. uasort($fields, function($a, $b) {
  294. $a_weight = (is_array($a) && isset($a['display']['default']['weight'])) ? $a['display']['default']['weight'] : 0;
  295. $b_weight = (is_array($b) && isset($b['display']['default']['weight'])) ? $b['display']['default']['weight'] : 0;
  296. if ($a_weight == $b_weight) {
  297. return 0;
  298. }
  299. return ($a_weight < $b_weight) ? -1 : 1;
  300. });
  301. // Iterate throught the fields and add each value to the response.
  302. //$response['fields'] = $fields;
  303. foreach ($fields as $field_name => $field) {
  304. $field_value = $entity->$field_name;
  305. // Get the semantic web settings for this field
  306. $field_type = '';
  307. if (array_key_exists('semantic_web', $field['settings'])) {
  308. $field_type = $field['settings']['semantic_web']['type'];
  309. if ($field_type) {
  310. $ns = $field['settings']['semantic_web']['ns'];
  311. $nsurl = $field['settings']['semantic_web']['nsurl'];
  312. $response['@context'][$ns] = $nsurl;
  313. $response['@context'][$field['label']] = $ns . ':' .$field_type;
  314. }
  315. }
  316. // TODO: need a way to hide fields.
  317. // Get the values based on cardinality
  318. $items = field_get_items('TripalEntity', $entity, $field_name);
  319. $values = '';
  320. if (array_key_exists('und', $field_value) and count($field_value['und']) == 1) {
  321. $values = $field_value['und'][0]['value'];
  322. }
  323. // If cardinality is greater than 1 then the value should be an array
  324. else {
  325. $values = array();
  326. for ($i = 0; $i < count($field_value); $i++) {
  327. $values[] = $field_value['und'][$i]['value'];
  328. }
  329. }
  330. $response[$field['label']] = $values;
  331. }
  332. //$response['fields'] = $fields;
  333. // Lastly, add in the terms used into the @context section.
  334. $response['@context']['label'] = 'rdfs:label';
  335. $response['@context']['itemPage'] = 'schema:itemPage';
  336. }
  337. /**
  338. * Provides the Hydra compatible apiDocumentation page that describes this API.
  339. *
  340. * @param $api_url
  341. * @param $response
  342. */
  343. function tripal_ws_handle_doc_service($api_url, &$response) {
  344. // First, add the vocabularies used into the @context section.
  345. $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
  346. $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
  347. // Next add in the ID for tihs resource.
  348. $site_name = variable_get('site_name', '');
  349. $response['@id'] = $api_url . '/doc/';
  350. $response['title'] = $site_name . ": RESTful Web Services API";
  351. $response['entrypoint'] = $api_url;
  352. $response['description'] = "A fully queryable REST API using JSON-LD and " .
  353. "discoverable using the WC3 Hydra specification.";
  354. // Lastly, add in the terms used into the @context section.
  355. $response['@context']['title'] = 'hydra:title';
  356. $response['@context']['entrypoint'] = array(
  357. "@id" => "hydra:entrypoint",
  358. "@type" => "@id",
  359. );
  360. $response['@context']['description'] = 'hydra:description';
  361. }
  362. /**
  363. * This function specifies the types of resources avaiable via the API.
  364. *
  365. * @param $api_url
  366. * @param $response
  367. * @param $ws_args
  368. */
  369. function tripal_ws_handle_no_service($api_url, &$response) {
  370. // First, add the vocabularies used into the @context section.
  371. $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
  372. $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
  373. $response['@context']['dc'] = 'http://purl.org/dc/dcmitype/';
  374. $response['@context']['schema'] = 'https://schema.org/';
  375. // Next add in the ID for tihs resource.
  376. $response['@id'] = $api_url;
  377. // Start the list.
  378. $response['@type'] = 'Collection';
  379. $response['totalItems'] = 0;
  380. $response['label'] = 'Services';
  381. $response['member'] = array();
  382. // Start the list.
  383. $response['member'][] = array(
  384. '@id' => $api_url . '/content/',
  385. '@type' => 'Service',
  386. 'label' => 'Content Types',
  387. 'description' => 'Provides acesss to the biological and ' .
  388. 'ancilliary data available on this site. Each content type ' .
  389. 'represents biological data that is defined in a controlled vocabulary '.
  390. '(e.g. Sequence Ontology term: gene (SO:0000704)).',
  391. );
  392. $response['member'][] = array(
  393. '@id' => $api_url . '/doc/',
  394. '@type' => 'Service',
  395. 'label' => 'API Documentation',
  396. 'description' => 'The WC3 Hydra compatible documentation for this API.',
  397. );
  398. $response['member'][] = array(
  399. '@id' => $api_url . '/vocab/',
  400. '@type' => 'Service',
  401. 'label' => 'Vocabulary',
  402. 'description' => 'Defines in-house locally defined vocabulary terms that ' .
  403. 'have been added specifically for this site. These terms are typically ' .
  404. 'added because no other appropriate term exists in another community-vetted '.
  405. 'controlled vocabulary.',
  406. );
  407. $response['totalItems'] = count($response['member']);
  408. $response['@context']['Collection'] = 'hydra:Collection';
  409. $response['@context']['totalItems'] = 'hydra:totalItems';
  410. $response['@context']['member'] = 'hydra:member';
  411. $response['@context']['Service'] = 'dc:Service';
  412. $response['@context']['label'] = 'rdfs:label';
  413. $response['@context']['description'] = 'hydra:description';
  414. }