├── sjs-lib-index.txt ├── test ├── run.html ├── yql-tests.sjs └── google-tests.sjs ├── freebase.sjs ├── lastfm.sjs ├── yql.sjs ├── google.sjs └── asana.sjs /sjs-lib-index.txt: -------------------------------------------------------------------------------- 1 | @lib SJS Web API Modules 2 | @summary Bindings to popular web apis 3 | 4 | @module asana 5 | @module freebase 6 | @module google 7 | @module lastfm 8 | @module yql 9 | -------------------------------------------------------------------------------- /test/run.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | sjs-webapi tests 5 | 6 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/yql-tests.sjs: -------------------------------------------------------------------------------- 1 | var yql=require('../yql'); 2 | var {test, context, assert} = require('sjs:test/suite'); 3 | 4 | context {|| 5 | test("query") {|| 6 | var rv = yql.query("select * from html where url=@url and xpath='//h1'", 7 | {url:"http://www.stratifiedjs.org"}); 8 | rv.results.h1.content .. assert.eq("Stratified"); 9 | } 10 | 11 | test("getFile") {|| 12 | var file = yql.getFile("http://stratifiedjs.org/presentations/OSCON2010/"); 13 | file.indexOf("Alexander Fritze") .. assert.notEq(-1); 14 | } 15 | 16 | }.ignoreLeaks('_oni_jsonpcb'); 17 | -------------------------------------------------------------------------------- /test/google-tests.sjs: -------------------------------------------------------------------------------- 1 | var {test, assert, context} = require('sjs:test/suite'); 2 | var g = require('../google'); 3 | var logging = require('sjs:logging'); 4 | 5 | context {|| 6 | test('search') {|| 7 | var results = g.search("croczilla"); 8 | results.responseData.results[0].url .. assert.ok(); 9 | } 10 | 11 | test('search(., {start:4})') {|| 12 | var results = g.search("croczilla", {start:4}); 13 | results.responseData.results[0].url .. assert.ok(); 14 | } 15 | 16 | test('siteSearch(., {start:4})') {|| 17 | var results = g.siteSearch("news", "http://cnn.com", {start:4}); 18 | logging.info(results); 19 | results.responseData.results[0].url .. assert.ok(); 20 | } 21 | 22 | test('translate') {|| 23 | var response = g.translate("hello", "de"); 24 | if (!response.responseData) return response.responseDetails; 25 | response.responseData.translatedText .. assert.eq("Hallo"); 26 | }.skip("translate is now a paid api"); 27 | 28 | test('BROKEN: load') {|| 29 | g.load("language", "1"); 30 | google.language.isFontRenderingSupported("hi") .. assert.ok(); 31 | }.skip(); 32 | 33 | }.ignoreLeaks('_oni_jsonpcb'); 34 | -------------------------------------------------------------------------------- /freebase.sjs: -------------------------------------------------------------------------------- 1 | /* 2 | * 'freebase' module 3 | * Bindings to the Freebase API 4 | * 5 | * (c) 2011 Oni Labs, http://onilabs.com 6 | * 7 | * This file is licensed under the terms of the MIT License: 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | /** 29 | @module freebase 30 | @summary Bindings to the [Freebase](http://freebase.com) API 31 | @home github:onilabs/sjs-webapi/master/freebase 32 | */ 33 | var http = require("sjs:http"); 34 | var sys = require("builtin:apollo-sys"); 35 | 36 | var api_base = "http://api.freebase.com/api/service/"; // XXX do we want https? 37 | 38 | /** 39 | @function mqlread 40 | @summary See [freebase mqlread docs](http://freebase.com/docs/web_services/mqlread) 41 | @param {Object} [query] MQL query object 42 | @param {optional Object} [envelope_props] Hash of envelope parameters 43 | @return {Object} 44 | */ 45 | function mqlread(query, envelope_props) { 46 | return http.jsonp([api_base, "mqlread", 47 | {query: JSON.stringify(sys.extendObject({query:query}, 48 | envelope_props))}]); 49 | } 50 | exports.mqlread = mqlread; 51 | 52 | /** 53 | @function search 54 | @summary See [freebase api/service/search](http://freebase.com/docs/web_services/search) 55 | @param {String} [query] Search string 56 | @param {optional Object} [props] Hash of additional query parameters 57 | @return {Object} 58 | */ 59 | function search(query, props) { 60 | return http.jsonp([api_base,"search",{query:query},props]); 61 | } 62 | exports.search = search; 63 | 64 | -------------------------------------------------------------------------------- /lastfm.sjs: -------------------------------------------------------------------------------- 1 | /* 2 | * 'lastfm' module 3 | * Bindings to the lastfm API 4 | * 5 | * (c) 2010-2011 Oni Labs, http://onilabs.com 6 | * 7 | * This file is licensed under the terms of the MIT License: 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | 29 | /** 30 | @module lastfm 31 | @summary A wrapper around the Last.fm API 32 | @home github:onilabs/sjs-webapi/master/lastfm 33 | @desc 34 | This module uses the JSONP interface of [the Last.fm API](http://www.last.fm/api). 35 | 36 | var lastfm = require("sjs:webapi/lastfm"); 37 | lastfm.key = "somekey..."; 38 | var tracks = lastfm.get({ 39 | method: "user.getrecenttracks", 40 | user: "rj" 41 | }).track; 42 | for (var i = 0; i < tracks.length; i++) { 43 | console.log(tracks[i].name); 44 | } 45 | 46 | */ 47 | var http = require("sjs:http"); 48 | var defaultKey = "b25b959554ed76058ac220b7b2e0a026"; 49 | 50 | /** 51 | @variable key 52 | @summary A string containing the API key. 53 | By default it will use Last.fm's own demo key (which should not be used in production). 54 | */ 55 | exports.key = defaultKey; 56 | 57 | /** 58 | @function get 59 | @summary Execute a remote method on the Last.fm API. 60 | @param {optional String} [method] A string defining the remote method you want to call. 61 | @param {optional Object} [params] Object with key/value pairs describing the request parameters. 62 | @return {Object} 63 | @desc 64 | ###Example 65 | 66 | var name = require("sjs:webapi/lastfm").get({ 67 | method: "user.getinfo", 68 | user: "rj" 69 | }).realname; 70 | */ 71 | exports.get = function () { 72 | if (!exports.key) { 73 | throw new Error("No Last.fm API key supplied"); 74 | } 75 | if (exports.key == defaultKey) { 76 | //require("../xbrowser/console").warn() ?? 77 | //using lastfm's demo key 78 | } 79 | if (typeof arguments[0] == "string") { 80 | var params = arguments[1] || {}; 81 | params.method = arguments[0]; 82 | } 83 | else { 84 | var params = arguments[0] || {}; 85 | } 86 | var rv = http.jsonp(["http://ws.audioscrobbler.com/2.0/", 87 | { 88 | api_key: exports.key, 89 | format: "json", 90 | cb: Math.random() 91 | }, 92 | params]); 93 | if (rv.error) { 94 | var e = new Error(rv.message); 95 | e.code = rv.error; 96 | throw e; 97 | } 98 | // prettify lastfm's xml->json conversion 99 | var count = 0; 100 | var first; 101 | for (first in rv) { if (++count > 1) break; } 102 | if (count == 1) return rv[first]; 103 | return rv; 104 | }; 105 | /* 106 | exports.getRecentTracks = function (user, limit) { 107 | limit = limit || 0; 108 | return exports.get({method: "user.getrecenttracks", user: user, limit: limit}).recenttracks.track; 109 | }; 110 | 111 | exports.getFriends = function (user, recenttracks, limit) { 112 | limit = limit || 0; 113 | recenttracks = recenttracks || false; 114 | return exports.get({ 115 | method: "user.getfriends", 116 | user: user, 117 | limit: limit, 118 | recenttracks: recenttracks 119 | }).friends.user; 120 | }; 121 | 122 | exports.getTopArtists = function (user, period) { 123 | period = period || "overall"; 124 | var rv = exports.get({ 125 | method: "user.gettopartists", 126 | user: user, 127 | period: period 128 | }).topartists.artist; 129 | return (rv && rv.hasOwnProperty("length")) ? rv : [rv]; 130 | //return toString.call(rv) === "[object Array]" ? rv : [rv]; // XXX doesn't work in Fx 131 | } 132 | */ 133 | 134 | -------------------------------------------------------------------------------- /yql.sjs: -------------------------------------------------------------------------------- 1 | /* 2 | * 'yql' module 3 | * Stratified wrapper for the YQL web service 4 | * 5 | * (c) 2010-2011 Oni Labs, http://onilabs.com 6 | * 7 | * This file is licensed under the terms of the MIT License: 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | /** 29 | @module yql 30 | @summary A stratified wrapper for the [Yahoo! Query Language](http://developer.yahoo.com/yql/) (YQL) Web Service, 31 | which enables you to access Internet data with SQL-like commands. 32 | @home github:onilabs/sjs-webapi/master/yql 33 | @desc 34 | 35 | var yql = require("sjs:webapi/yql"); 36 | var q = "select * from html where url=@url and xpath='//h1'"; 37 | var rv = yql.query(q, {url:"http://www.onilabs.com"}); 38 | 39 | See the [::query] function for more examples. 40 | */ 41 | 42 | var http = require("sjs:http"); 43 | 44 | /** 45 | @function query 46 | @summary Execute a [YQL query](http://developer.yahoo.com/yql/guide/yql_overview_guide.html) on the Yahoo Web Service. 47 | @param {String} [statement] YQL query. 48 | @param {optional Object} [parameters] Key-value hash of parameters for the query. 49 | @return {Object} The query result. 50 | @desc 51 | The Yahoo! Query Language is an expressive SQL-like language that lets you query, filter, and join data across Web services. 52 | Debug your YQL queries in the [YQL Console](http://developer.yahoo.com/yql/console) 53 | 54 | ### HTML selector Example 55 | 56 | var yql = require("sjs:webapi/yql"); 57 | var q = "select * from html where url=@url and xpath='//h1'"; 58 | var rv = yql.query(q, {url:"http://www.onilabs.com"}); 59 | c.log(rv.results.h1); 60 | 61 | ### Cross-domain XML Example 62 | 63 | var yql = require("sjs:webapi/yql"); 64 | var q = "select * from xml where url=@url"; 65 | var rv = yql.query(q, { 66 | url:"http://www.weather.gov/xml/current_obs/OOUH1.xml" 67 | }); 68 | c.log(rv.results.current_observation.temp_c) 69 | */ 70 | exports.query = function (statement, params) { 71 | var url = "http://query.yahooapis.com/v1/public/yql"; 72 | 73 | params = params || {}; 74 | if (params.communitytables) { 75 | delete params.communitytables; 76 | params.env = "store://datatables.org/alltableswithkeys"; 77 | } 78 | var rv = http.jsonp([url, { q: statement, format: "json"}, params]); 79 | if (rv["error"]) { 80 | throw new Error(rv.error.description); // syntax error 81 | } 82 | if (rv.query && rv.query.results && rv.query.results.error) { 83 | var error = rv.query.results.error; 84 | throw new Error(error.description || error); // content error 85 | } 86 | return rv.query; 87 | }; 88 | 89 | /** 90 | @function getFeed 91 | @summary Load a feed through the Yahoo Web Service. 92 | @shortcut query 93 | @param {String} [url] A string containing the URL of the requested Atom feed. 94 | @return {Array} An array of Atom entries 95 | @desc 96 | This is a convenience wrapper for [the feed table](http://developer.yahoo.com/yql/console/#h=desc%20feed). 97 | 98 | var yql = require("sjs:webapi/yql"); 99 | var rv = yql.getFeed("http://planet.mozilla.org/atom.xml"}); 100 | console.log(rv[0].title);` 101 | */ 102 | exports.getFeed = function(url) { 103 | var q = "select * from atom where url=@url"; 104 | return exports.query(q, {url:url}).results.item; 105 | }; 106 | 107 | /* 108 | @function getXML 109 | @summary Loads an XML document through the Yahoo Web Service. 110 | @shortcut query 111 | @desc 112 | This is a convenience wrapper for [the xml table](http://developer.yahoo.com/yql/console/#h=desc%20xml). 113 | `var yql = require("sjs:webapi/yql"); 114 | var xmlUrl = "http://www.weather.gov/xml/current_obs/OOUH1.xml"; 115 | var honoluluWeather = yql.getXML(xmlUrl).current_observation; 116 | console.log(honoluluWeather.temp_c);` 117 | @param {String} [url] A string containing the URL of the requested XML document. 118 | @return {Object} An object describing the XML document. 119 | exports.getXML = function(url) { 120 | var q = "select * from xml where url=@url"; 121 | return exports.query(q, {url:url}).results; 122 | }; 123 | */ 124 | 125 | /* 126 | @function getDataURI 127 | @summary Returns any file smaller than 25kb through the Yahoo Web Service as a data URI. 128 | @shortcut query 129 | @param {String} [url] A string containing the URL of the requested file. 130 | @return {Array} An array of Atom entries 131 | */ 132 | exports.getDataURI = function(url) { 133 | var q = "select * from data.uri where url=@url"; 134 | return exports.query(q, {url:url}).results.url; 135 | }; 136 | 137 | /** 138 | @function getFile 139 | @summary Returns any file smaller than 25kb through the Yahoo Web Service as a string. 140 | @shortcut query 141 | @desc 142 | This is a convenience wrapper for [the data.uri table](http://developer.yahoo.com/yql/console/#h=desc%20data.uri). 143 | @param {String} [url] A string containing the URL of the requested file. 144 | @return {String} 145 | */ 146 | exports.getFile = function(url) { 147 | var str = require("sjs:string"); 148 | return str.utf8ToUtf16(str.base64ToOctets(exports.getDataURI(url).split("base64,")[1])); 149 | }; 150 | 151 | /* 152 | var title = y.cssGet("http://www.croczilla.com/stratified", "h1").h1.content; 153 | 154 | exports.cssGet = function() { 155 | var rv = exports.query("select * from data.html.cssselect where url=@url and css=@css", 156 | {url:arguments[0],css:arguments[1]}, {communitytables:true}).results; 157 | return rv ? rv.results : null; 158 | }; 159 | */ -------------------------------------------------------------------------------- /google.sjs: -------------------------------------------------------------------------------- 1 | /* 2 | * 'google' module 3 | * Bindings to various Google webservices and APIs 4 | * 5 | * (c) 2010-2011 Oni Labs, http://onilabs.com 6 | * 7 | * This file is licensed under the terms of the MIT License: 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | /** 29 | @module google 30 | @summary Bindings to various Google webservices and APIs 31 | @home github:onilabs/sjs-webapi/master/google 32 | */ 33 | var http = require("sjs:http"); 34 | 35 | /** 36 | @function search 37 | @summary Performs a Google web search query 38 | @param {String} [query] The search query to execute. 39 | @param {optional Object} [settings] Hash of additional 40 | key/value query parameters. 41 | @return {Object} The query result. 42 | @desc 43 | Uses the RESTful Google search API. 44 | See . 45 | 46 | **Example:** 47 | 48 | var s = require("sjs:webapi/google").search("Onilabs", {start:4}); 49 | console.log(s.responseData.results[0].url); // first result 50 | */ 51 | function search(q, settings) { 52 | return http.jsonp(["http://ajax.googleapis.com/ajax/services/search/web", {v: "1.0", q : q }, settings]); 53 | }; 54 | exports.search = search; 55 | 56 | /** 57 | @function siteSearch 58 | @summary Performs a web search query limited to a particular site. 59 | @param {String} [query] The search query to execute. 60 | @param {String} [site] URL of site to limit the search to. 61 | @param {optional Object} [settings] Hash of additional 62 | key/value query parameters. 63 | @return {Object} The query result. 64 | @desc 65 | See [::search]. 66 | */ 67 | exports.siteSearch = function (q, site, settings) { 68 | q = q || ""; 69 | q += " site:" + site; 70 | return search(q, settings); 71 | }; 72 | 73 | /** 74 | @function translate 75 | @summary Translates a string of text using the Google Translation webservice. 76 | @param {String|Array} [text] A string containing the text to translate or an array specifying several strings for translation. 77 | @param {String|Array} [to] A string specifying the target language or an array specifying several target languages. 78 | @param {optional String} [from] An optional string specifying the source language. 79 | @param {optional Object} [extra] A hash of key/value pairs to append to the request. 80 | @return {Object} A ['Language Detection Result'](http://code.google.com/apis/ajaxlanguage/documentation/reference.html#detectResult) 81 | @desc 82 | Uses the RESTful Google Translation API v2 83 | See 84 | 85 | Note that as of August 2011, the Google Translate API is a paid service, so it 86 | requires an API key. 87 | 88 | Note that Google places a limit of ~2000 characters on request 89 | URIs. This limits the number of characters that can be translated by 90 | the API in one go. 91 | 92 | **Example:** 93 | 94 | var t = google.translate("hello", "de"); 95 | console.log(t.translation, t.detectedSourceLanguage); // hallo, en 96 | 97 | */ 98 | exports.translate = function(text, to, /* [opt] */ from, /* [opt] */ extra) { 99 | from = from || ""; // "" == autodetect 100 | var langpair; 101 | if (require('builtin:apollo-sys').isArrayLike(to)) { 102 | langpair = []; 103 | for (var i=0; i 191 | 192 | **Example:** 193 | 194 | require('sjs:webapi/google').load("language", "1"); 195 | if (google.language.isFontRenderingSupported("hi")) 196 | ... 197 | */ 198 | exports.load = function(moduleName, moduleVersion, settings) { 199 | ensureAPI(); 200 | waitfor() { 201 | settings = settings || {}; 202 | settings.callback = resume; // XXX we should really take a copy of settings 203 | google.load(moduleName, moduleVersion, settings); 204 | } 205 | return google[moduleName]; 206 | }; 207 | 208 | -------------------------------------------------------------------------------- /asana.sjs: -------------------------------------------------------------------------------- 1 | /* 2 | * 'asana' module 3 | * Bindings to the Asana API 4 | * 5 | * (c) 2012 Oni Labs, http://onilabs.com 6 | * 7 | * This file is licensed under the terms of the MIT License: 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | /** 29 | @module asana 30 | @summary Bindings to the [Asana](http://asana.com) API 31 | @home github:onilabs/sjs-webapi/master/asana 32 | @hostenv nodejs 33 | @desc 34 | For more information about the Asana API, see http://developer.asana.com/documentation/ 35 | 36 | ### Limitations: 37 | 38 | Because of Asana cross-domain policies, this module doesn't work in 39 | the xbrowser host environment (yet). 40 | */ 41 | 42 | var http = require("sjs:http"); 43 | var { merge } = require("sjs:object"); 44 | 45 | /** 46 | @class Client 47 | @summary Asana API client 48 | @constructor Client 49 | @param {String} [apiKey] [Asana API Key](http://developer.asana.com/documentation/#api_keys) 50 | */ 51 | function Client(apiKey) { 52 | this.apiKey = apiKey; 53 | } 54 | exports.Client = Client; 55 | /** 56 | @function makeClient 57 | @summary Shorthand for "`new Client(.)`". 58 | @param {String} [apiKey] [Asana API Key](http://developer.asana.com/documentation/#api_keys) 59 | */ 60 | exports.makeClient = function(apiKey) { return new Client(apiKey); }; 61 | 62 | Client.prototype = {}; 63 | 64 | Client.prototype.request = function(method, path, opts) { 65 | opts = merge( 66 | { 67 | username: this.apiKey, 68 | password: "", 69 | method: method 70 | }, 71 | opts); 72 | try { 73 | var rv = http.request(["https://app.asana.com/api/1.0/", path], opts); 74 | } 75 | catch (e){ 76 | throw new Error("Asana API: "+e.status+" "+e.data); 77 | } 78 | return JSON.parse(rv).data; 79 | }; 80 | 81 | //---------------------------------------------------------------------- 82 | // Workspaces 83 | 84 | /** 85 | @function Client.listWorkspaces 86 | @summary Retrieve list of all reachable workspaces 87 | @return {Array} Array of `{id,name}` objects 88 | @desc 89 | Throws in error case. 90 | */ 91 | Client.prototype.listWorkspaces = function() { 92 | return this.request("GET", "workspaces"); 93 | }; 94 | 95 | //---------------------------------------------------------------------- 96 | // Projects 97 | 98 | /** 99 | @function Client.listProjects 100 | @summary Retrieve list of projects 101 | @param {optional Object} [settings] Hash of settings 102 | @setting {Boolean} [archived] If provided, this parameter will 103 | filter on projects whose `archived` field takes on the specified value. 104 | @setting {String|Integer} [workspace] If provided, this parameter will filter on projects 105 | which belong in the workspace with the specified id. 106 | @return {Array} Array of `{id,name}` objects 107 | @desc 108 | Throws in error case. 109 | */ 110 | Client.prototype.listProjects = function(settings) { 111 | var params = {}; 112 | if (settings) { 113 | if (typeof settings.archived != 'undefined') params.archived = settings.archived; 114 | if (typeof settings.workspace != 'undefined') params.workspace = settings.workspace; 115 | } 116 | 117 | return this.request("GET", ["projects", params]); 118 | }; 119 | 120 | /** 121 | @function Client.getProject 122 | @summary Retrieve full record of project with given id 123 | @param {String|Integer} [id] Project ID 124 | @return {Object} [ ] [Project record](http://developer.asana.com/documentation/#projects) 125 | @desc 126 | Throws in error case. 127 | */ 128 | Client.prototype.getProject = function(id) { 129 | return this.request("GET", ["projects", ""+id]); 130 | }; 131 | 132 | /** 133 | @function Client.modifyProject 134 | @summary Modify project with given id 135 | @param {String|Integer} [id] Project ID 136 | @param {Object} [data] Object with data to modify (see http://developer.asana.com/documentation/#projects) 137 | @return {Object} Updated [project record](http://developer.asana.com/documentation/#projects) 138 | */ 139 | Client.prototype.modifyProject = function(id, data) { 140 | return this.request("PUT", ["projects", ""+id], {body:JSON.stringify({data:data})}); 141 | }; 142 | 143 | //---------------------------------------------------------------------- 144 | // Tasks 145 | 146 | /** 147 | @function Client.listTasks 148 | @summary Retrieve list of tasks 149 | @param {optional Object} [settings] Hash of settings 150 | @setting {String|Integer} [assignee] The ID of the assignee to filter tasks on. 151 | Only unarchived tasks in the assignee's list will be returned. **Note:** 152 | If you specify an `assignee`, you must also specify a `workspace` to 153 | filter on. 154 | @setting {String|Integer} [workspace] The ID of the workspace to filter tasks on. 155 | **Note:** If you specify a `workspace`, you must also specify an `assignee` 156 | to filter on. 157 | @setting {String|Integer} [project] The ID of the project to get tasks from. 158 | Only unarchived tasks in the project will be returned. 159 | @return {Array} Array of `{id,name}` objects 160 | @desc 161 | Throws in error case. 162 | */ 163 | Client.prototype.listTasks = function(settings) { 164 | var params = {}; 165 | if (settings) { 166 | for (var p in {'assignee':1, 'workspace':1, 'project':1}) { 167 | if (typeof settings[p] != 'undefined') 168 | params[p] = settings[p]; 169 | } 170 | } 171 | return this.request("GET", ["tasks", params]); 172 | }; 173 | 174 | /** 175 | @function Client.getTask 176 | @summary Retrieve full record of task with given id 177 | @param {String|Integer} [id] Task ID 178 | @return {Object} [ ] [Task record](http://developer.asana.com/documentation/#tasks) 179 | @desc 180 | Throws in error case. 181 | */ 182 | Client.prototype.getTask = function(id) { 183 | return this.request("GET", ["tasks", ""+id]); 184 | }; 185 | 186 | /** 187 | @function Client.modifyTask 188 | @summary Modify task with given id 189 | @param {String|Integer} [id] Task ID 190 | @param {Object} [data] Object with data to modify (see http://developer.asana.com/documentation/#tasks) 191 | @return {Object} Updated [task record](http://developer.asana.com/documentation/#tasks) 192 | */ 193 | Client.prototype.modifyTask = function(id, data) { 194 | return this.request("PUT", ["tasks", ""+id], {body:JSON.stringify({data:data})}); 195 | }; 196 | 197 | /** 198 | @function Client.createTask 199 | @summary Create a new task 200 | @param {Object|Integer} [workspace_id] ID of workspace to create task in 201 | @param {Object} [data] Object with task data (see http://developer.asana.com/documentation/#tasks) 202 | @return {Object} [ ] [Task record](http://developer.asana.com/documentation/#tasks) 203 | */ 204 | Client.prototype.createTask = function(workspace_id, data) { 205 | return this.request("POST", ["tasks",{workspace:""+workspace_id}], {body:JSON.stringify({data:data})}); 206 | }; 207 | 208 | /** 209 | @function Client.addTaskToProject 210 | @summary Associate a task with a project 211 | @param {Object|Integer} [task_id] ID of task 212 | @param {Object|Integer} [project_id] ID of project 213 | */ 214 | Client.prototype.addTaskToProject = function(task_id, project_id) { 215 | return this.request("POST", ["tasks",""+task_id,"addProject"],{body:JSON.stringify({data:{project:""+project_id}})}); 216 | }; 217 | 218 | /** 219 | @function Client.removeTaskFromProject 220 | @summary Dissociate a task from a project 221 | @param {Object|Integer} [task_id] ID of task 222 | @param {Object|Integer} [project_id] ID of project 223 | */ 224 | Client.prototype.removeTaskFromProject = function(task_id, project_id) { 225 | return this.request("POST", ["tasks",""+task_id,"removeProject"],{body:JSON.stringify({data:{project:""+project_id}})}); 226 | }; 227 | 228 | 229 | //---------------------------------------------------------------------- 230 | // Users 231 | 232 | /** 233 | @function Client.listUsers 234 | @summary Retrieve list of users 235 | @param {optional Object} [settings] Hash of settings 236 | @setting {String|Integer} [workspace] If provided, this parameter will 237 | filter on users in the specified workspace. 238 | @return {Array} Array of `{id,name}` objects 239 | @desc 240 | Throws in error case. 241 | */ 242 | Client.prototype.listUsers = function(settings) { 243 | var params = {}; 244 | if (settings) { 245 | if (typeof settings.workspace != 'undefined') params.workspace = settings.workspace; 246 | } 247 | 248 | return this.request("GET", ["users", params]); 249 | }; 250 | 251 | 252 | /** 253 | @function Client.getUser 254 | @summary Retrieve full record of user with given id 255 | @param {String|Integer} [id] User ID 256 | @return {Object} [ ] [User record](http://developer.asana.com/documentation/#users) 257 | @desc 258 | Throws in error case. 259 | */ 260 | Client.prototype.getUser = function(id) { 261 | return this.request("GET", ["users", ""+id]); 262 | }; 263 | 264 | //---------------------------------------------------------------------- 265 | // Stories 266 | 267 | /** 268 | @function Client.listStories 269 | @summary Retrieve list of stories for a task 270 | @param {String|Integer} [task_id] ID of task 271 | @return {Array} Array of (compact) [story objects](http://developer.asana.com/documentation/#stories) 272 | @desc 273 | Throws in error case. 274 | */ 275 | Client.prototype.listStories = function(task_id) { 276 | return this.request("GET", ["tasks", ""+task_id,"stories"]); 277 | }; 278 | 279 | /** 280 | @function Client.getStory 281 | @summary Retrieve full record of story with given id 282 | @param {String|Integer} [id] Story ID 283 | @return {Object} [ ] [Story record](http://developer.asana.com/documentation/#stories) 284 | @desc 285 | Throws in error case. 286 | */ 287 | Client.prototype.getStory = function(id) { 288 | return this.request("GET", ["stories", ""+id]); 289 | }; 290 | 291 | /** 292 | @function Client.comment 293 | @summary Adds a comment to a task 294 | @param {String|Integer} [id] Task ID 295 | @param {String} [text] Comment text 296 | @return {Object} [ ] [Story record](http://developer.asana.com/documentation/#stories) 297 | @desc 298 | Throws in error case. 299 | */ 300 | Client.prototype.comment = function(id, text) { 301 | return this.request("POST", ["tasks", ""+id, "stories"],{body:JSON.stringify({data:{text:""+text}})}); 302 | }; 303 | --------------------------------------------------------------------------------