/duckduckhack/spice/spice_advanced_backend.md

https://github.com/DavidMascio/duckduckgo-documentation · Markdown · 150 lines · 95 code · 55 blank · 0 comment · 0 complexity · 6d677ee37989ea25b2d0bc8b41fa481f MD5 · raw file

  1. ##Spice Handlers
  2. - [Multiple Placeholders in Spice To URL](http://duck.co/duckduckhack/spice_advanced_backend#Multiple-Placeholders-in-Spice-To-URL)
  3. - [Returning Multiple Values (to Spice From)](http://duck.co/duckduckhack/spice_advanced_backend#Returning-Multiple-Values-to-Spice-From)
  4. - [API Keys](http://duck.co/duckduckhack/spice_advanced_backend#api-keys)
  5. - [JSON -> JSONP](http://duck.co/duckduckhack/spice_advanced_backend#json-gt-jsonp)
  6. - [Pure JS functions](http://duck.co/duckduckhack/spice_advanced_backend#pure-js-functions)
  7. - [Caching API Responses](http://duck.co/duckduckhack/spice_advanced_backend#caching-api-responses)
  8. - [Caching API Calls](http://duck.co/duckduckhack/spice_advanced_backend#caching-api-calls)
  9. ## Multiple Placeholders in Spice To URL
  10. If you need to substitute multiple parameters into the API call like how the [RandWord Spice](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Spice/RandWord.pm) uses two numbers to specify the min and max length of the random word, you can use the **Spice from** keyword:
  11. ```perl
  12. spice from => '(?:([0-9]+)\-([0-9]+)|)';
  13. ```
  14. Whatever you return from the handle function gets sent to this **spice from** regexp, which then gets fed into the **spice to** API:
  15. For example, if your `handle` function looked like this:
  16. ```perl
  17. handle remainder => sub {
  18. ...
  19. if ( $foo ){
  20. my $minMax = "10-100"
  21. return $minMax;
  22. }
  23. return;
  24. }
  25. ```
  26. <!-- /summary -->
  27. Then the the string `10-100` would be sent to the `spice from` regexp, which would capture the two numbers into `$1` and `$2`. These two placeholders are then used to replace `$1` and `$2` in the `spice to` URL:
  28. ```perl
  29. spice to => 'http://api.wordnik.com/v4/words.json/randomWord?minLength=$1&maxLength=$2&api_key={{ENV{DDG_SPICE_RANDWORD_APIKEY}}}&callback={{callback}}';
  30. ```
  31. **\*\*Note:** The reason why you do not need to specify a **from** keyword by default, is that the default value of `spice from` is **(.*)**, which means whatever you return gets gets captured into `$1`.
  32. ## Returning Multiple Values (to Spice From)
  33. You can have multiple return values in your handle function like the [AlternativeTo Spice](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Spice/AlternativeTo.pm).
  34. ```perl
  35. return $prog, $platform, $license;
  36. ```
  37. In this case they are URL encoded and joined together with '/' chars, e.g., in this case **$prog/$platform/$license**. Then that full string is fed into the **spice from** regexp.
  38. ```perl
  39. spice from => '([^/]+)/?(?:([^/]+)/?(?:([^/]+)|)|)';
  40. ```
  41. ## API Keys
  42. Some APIs require API keys to function properly like in the [RandWord Spice](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Spice/RandWord.pm). You can insert an API key for testing in the callback function and replace it with a variable reference when submitting.
  43. ```perl
  44. spice to => 'http://api.wordnik.com/v4/words.json/randomWord?minLength=$1&maxLength=$2&api_key={{ENV{DDG_SPICE_RANDWORD_APIKEY}}}&callback={{callback}}';
  45. ```
  46. You can set the variable when you start DuckPAN server like this:
  47. ```bash
  48. DDG_SPICE_RANDWORD_APIKEY=xyz duckpan server
  49. ```
  50. ## JSON -> JSONP
  51. Some APIs don't do JSONP by default, i.e. don't have the ability to return the JSON object to a callback function. In this case, first you should try to contact the API provider and see if it can be added. Where it cannot, you can tell us to wrap the JSON object return in a callback function like in the [XKCD Spice](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Spice/Xkcd.pm).
  52. ```perl
  53. spice wrap_jsonp_callback => 1;
  54. ```
  55. ## Pure JS functions
  56. Sometimes no external API is necessary to deliver the instant answer like how the [Flash Version Spice](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Spice/FlashVersion.pm) just prints out your [Flash Player version](https://duckduckgo.com/?q=flash+version) using an [internal call](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/share/spice/flash_version/spice.js).
  57. In cases like these you can define a **spice\_call\_type** as 'self' like this:
  58. ```perl
  59. spice call_type => 'self';
  60. ```
  61. Then in the handle function you can return call, e.g.:
  62. ```perl
  63. return $_ eq 'flash version' ? call : ();
  64. ```
  65. The return of **call** will run whatever is in the **call\_type** setting. **self** is a special keyword to just run the callback function directly, in this case **ddg\_spice\_flash_version()**.
  66. ## Caching
  67. Spice instant answers have two forms of caching: API Response caching (remembers the JSON returned from the API) and API Call caching (remembers the API call URL created for a given query). Both of these will be explained with examples.
  68. <!-- /summary -->
  69. ### Caching API Responses
  70. By default, we cache API responses for for **24 hours**. We use [nginx](https://duckduckgo.com/?q=nginx) and get this functionality by using the [proxy_cache_valid](http://wiki.nginx.org/HttpProxyModule#proxy_cache_valid) directive. You can override our default behavior by setting your own `spice proxy_cache_valid` directive like in the [RandWord Spice](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Spice/RandWord.pm):
  71. ```perl
  72. spice proxy_cache_valid => "200 304 1d";
  73. ```
  74. This will cache any HTTP 200 and 304 responses for 1 day. You can also force API responses to **not** be cached like so:
  75. ```perl
  76. spice proxy_cache_valid => "418 1d";
  77. ```
  78. This is a special declaration that will only cache [418 HTTP](https://duckduckgo.com/?q=HTTP+418) return values for 1 day. Since regular return codes are [200](https://duckduckgo.com/?q=HTTP+200) and [304](https://duckduckgo.com/?q=HTTP+304), nothing will get cached.
  79. If you expect API response to change very frequently you should lower the caching time. As well, if your API is supposed to return random results (such as the RandWord spice) it makes sense to prevent all caching so every time the spice is trigger a new result will be returned.
  80. ### Caching API Calls
  81. When a Spice triggers, its Perl code is used to construct the URL for the API call. It's likely that a given query will always map to the same API call so by default we cache the API calls for a given query for **1 hour**:
  82. ```
  83. # This query will always make the same API call
  84. "random word" => http://api.wordnik.com/v4/words.json/randomWord
  85. ```
  86. Sometimes, a given query won't always require the same API call. This scenario generally arises when a Spice instant answer uses the Location API and uses it to append the user's location to the API call:
  87. ```
  88. # This query will NEVER make the same API call, because the location is dynamic
  89. "weather" => http://forecast.io/ddg?q=<user_location>
  90. ```
  91. To **turn off** API Call caching, you must set `spice is_cached` to `0` as we do in the [Forecast](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Spice/Forecast.pm) instant answer:
  92. ```perl
  93. spice is_cached => 0;
  94. ```
  95. This way, every time the Forecast instant answer is triggered, **Forecast.pm** will be run, so the correct URL will be built and the current user's location will be used for the API call.