PageRenderTime 20ms CodeModel.GetById 1ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 0ms

/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
  3- [Multiple Placeholders in Spice To URL](http://duck.co/duckduckhack/spice_advanced_backend#Multiple-Placeholders-in-Spice-To-URL)
  4
  5- [Returning Multiple Values (to Spice From)](http://duck.co/duckduckhack/spice_advanced_backend#Returning-Multiple-Values-to-Spice-From)
  6
  7- [API Keys](http://duck.co/duckduckhack/spice_advanced_backend#api-keys)
  8
  9- [JSON -> JSONP](http://duck.co/duckduckhack/spice_advanced_backend#json-gt-jsonp)
 10
 11- [Pure JS functions](http://duck.co/duckduckhack/spice_advanced_backend#pure-js-functions)
 12
 13- [Caching API Responses](http://duck.co/duckduckhack/spice_advanced_backend#caching-api-responses)
 14
 15- [Caching API Calls](http://duck.co/duckduckhack/spice_advanced_backend#caching-api-calls)
 16
 17## Multiple Placeholders in Spice To URL
 18
 19If 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:
 20
 21```perl
 22spice from => '(?:([0-9]+)\-([0-9]+)|)';
 23```
 24
 25Whatever you return from the handle function gets sent to this **spice from** regexp, which then gets fed into the **spice to** API:
 26
 27For example, if your `handle` function looked like this:
 28
 29```perl
 30handle remainder => sub {
 31  ...
 32  if ( $foo ){
 33    my $minMax = "10-100"
 34    return $minMax;
 35  }
 36  return;
 37}
 38```
 39
 40<!-- /summary -->
 41
 42Then 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:
 43
 44```perl
 45spice to => 'http://api.wordnik.com/v4/words.json/randomWord?minLength=$1&maxLength=$2&api_key={{ENV{DDG_SPICE_RANDWORD_APIKEY}}}&callback={{callback}}';
 46```
 47
 48**\*\*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`.
 49
 50## Returning Multiple Values (to Spice From)
 51
 52You 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).
 53
 54```perl
 55return $prog, $platform, $license;
 56```
 57
 58In 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.
 59
 60```perl
 61spice from => '([^/]+)/?(?:([^/]+)/?(?:([^/]+)|)|)';
 62```
 63
 64## API Keys
 65
 66Some 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.
 67
 68```perl
 69spice to => 'http://api.wordnik.com/v4/words.json/randomWord?minLength=$1&maxLength=$2&api_key={{ENV{DDG_SPICE_RANDWORD_APIKEY}}}&callback={{callback}}';
 70```
 71
 72You can set the variable when you start DuckPAN server like this:
 73
 74```bash
 75DDG_SPICE_RANDWORD_APIKEY=xyz duckpan server
 76```
 77
 78## JSON -> JSONP
 79
 80Some 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).
 81
 82```perl
 83spice wrap_jsonp_callback => 1;
 84```
 85
 86## Pure JS functions
 87
 88Sometimes 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).
 89
 90In cases like these you can define a **spice\_call\_type** as 'self' like this:
 91
 92```perl
 93spice call_type => 'self';
 94```
 95
 96Then in the handle function you can return call, e.g.:
 97
 98```perl
 99return $_ eq 'flash version' ? call : ();
100```
101
102The 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()**.
103
104## Caching
105
106Spice 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.
107
108<!-- /summary -->
109
110### Caching API Responses
111
112By 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):
113
114```perl
115spice proxy_cache_valid => "200 304 1d";
116```
117
118This will cache any HTTP 200 and 304 responses for 1 day. You can also force API responses to **not** be cached like so:
119
120```perl
121spice proxy_cache_valid => "418 1d";
122```
123
124This 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.
125
126If 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.
127
128### Caching API Calls
129
130When 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**:
131
132```
133# This query will always make the same API call
134"random word" => http://api.wordnik.com/v4/words.json/randomWord
135```
136
137Sometimes, 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:
138
139```
140# This query will NEVER make the same API call, because the location is dynamic
141"weather" => http://forecast.io/ddg?q=<user_location>
142```
143
144To **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:
145
146```perl
147spice is_cached => 0;
148```
149
150This 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.