├── alpaca ├── format_blueprint.go ├── error.go ├── template.go ├── langs_node.go ├── langs_python.go ├── langs_ruby.go ├── langs_php.go ├── library.go ├── alpaca.go ├── utils_test.go └── utils.go ├── templates ├── php │ ├── gitignore │ ├── lib │ │ ├── Exception │ │ │ ├── ExceptionInterface.php │ │ │ └── ClientException.php │ │ ├── HttpClient │ │ │ ├── Response.php │ │ │ ├── ResponseHandler.php │ │ │ ├── RequestHandler.php │ │ │ ├── ErrorHandler.php │ │ │ ├── AuthHandler.php │ │ │ └── HttpClient.php │ │ ├── Client.php │ │ └── Api │ │ │ └── Api.php │ ├── composer.json │ └── readme.md ├── node │ ├── gitignore │ ├── lib │ │ ├── error │ │ │ ├── index.js │ │ │ └── client_error.js │ │ ├── http_client │ │ │ ├── response.js │ │ │ ├── response_handler.js │ │ │ ├── request_handler.js │ │ │ ├── error_handler.js │ │ │ ├── auth_handler.js │ │ │ └── index.js │ │ ├── index.js │ │ ├── client.js │ │ └── api │ │ │ └── api.js │ ├── package.json │ └── readme.md ├── python │ ├── lib │ │ ├── __init__.py │ │ ├── error │ │ │ ├── __init__.py │ │ │ └── client_error.py │ │ ├── api │ │ │ ├── __init__.py │ │ │ └── api.py │ │ ├── http_client │ │ │ ├── response.py │ │ │ ├── response_handler.py │ │ │ ├── error_handler.py │ │ │ ├── request_handler.py │ │ │ ├── auth_handler.py │ │ │ └── __init__.py │ │ └── client.py │ ├── gitignore │ ├── setup.py │ └── readme.md └── ruby │ ├── lib │ ├── version.rb │ ├── error.rb │ ├── name.rb │ ├── error │ │ └── client_error.rb │ ├── http_client │ │ ├── response.rb │ │ ├── response_handler.rb │ │ ├── request_handler.rb │ │ ├── error_handler.rb │ │ └── auth_handler.rb │ ├── client.rb │ ├── api │ │ └── api.rb │ └── http_client.rb │ ├── gitignore │ ├── gemspec │ └── readme.md ├── .travis.yml ├── .gitignore ├── Godeps ├── examples ├── buffer │ ├── pkg.json │ ├── api.json │ └── doc.json └── helpful │ ├── pkg.json │ ├── api.json │ └── doc.json ├── CHANGELOG.md ├── main.go ├── Makefile ├── README.md └── LICENSE /alpaca/format_blueprint.go: -------------------------------------------------------------------------------- 1 | package alpaca 2 | -------------------------------------------------------------------------------- /templates/php/gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | composer.lock 3 | -------------------------------------------------------------------------------- /templates/node/gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /templates/python/lib/__init__.py: -------------------------------------------------------------------------------- 1 | from .client import Client 2 | -------------------------------------------------------------------------------- /templates/python/lib/error/__init__.py: -------------------------------------------------------------------------------- 1 | from .client_error import ClientError 2 | -------------------------------------------------------------------------------- /templates/ruby/lib/version.rb: -------------------------------------------------------------------------------- 1 | module {{call .Fnc.camelize .Pkg.Name}} 2 | VERSION = "{{.Pkg.Version}}" 3 | end 4 | -------------------------------------------------------------------------------- /templates/node/lib/error/index.js: -------------------------------------------------------------------------------- 1 | var error = module.exports; 2 | 3 | error.ClientError = require('./client_error'); 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.9.x 5 | - 1.10.x 6 | - 1.11.x 7 | 8 | install: make 9 | script: make test 10 | -------------------------------------------------------------------------------- /templates/php/lib/Exception/ExceptionInterface.php: -------------------------------------------------------------------------------- 1 | body = $body; 12 | $this->code = $code; 13 | $this->headers = $headers; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /templates/ruby/lib/http_client/response.rb: -------------------------------------------------------------------------------- 1 | module {{call .Fnc.camelize .Pkg.Name}} 2 | 3 | module HttpClient 4 | 5 | # Response object contains the response returned by the client 6 | class Response 7 | 8 | attr_accessor :body, :code, :headers 9 | 10 | def initialize(body, code, headers) 11 | @body = body 12 | @code = code 13 | @headers = headers 14 | end 15 | 16 | end 17 | 18 | end 19 | 20 | end 21 | -------------------------------------------------------------------------------- /templates/node/lib/index.js: -------------------------------------------------------------------------------- 1 | var Client = require('./{{call .Fnc.underscore .Pkg.Name}}/client'); 2 | 3 | // Export module 4 | var {{call .Fnc.camelizeDownFirst .Pkg.Name}} = module.exports; 5 | 6 | /** 7 | * This file contains the global namespace for the module 8 | */ 9 | {{call .Fnc.camelizeDownFirst .Pkg.Name}}.client = function({{if .Api.BaseAsArg}}baseUrl, {{end}}auth, options) { 10 | return new Client({{if .Api.BaseAsArg}}baseUrl, {{end}}auth, options); 11 | }; 12 | -------------------------------------------------------------------------------- /templates/php/lib/Exception/ClientException.php: -------------------------------------------------------------------------------- 1 | code = $code; 15 | parent::__construct($message); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /templates/python/lib/http_client/response_handler.py: -------------------------------------------------------------------------------- 1 | class ResponseHandler(object): 2 | 3 | """ResponseHandler takes care of decoding the response body into suitable type""" 4 | 5 | @staticmethod 6 | def get_body(response): 7 | typ = response.headers.get('content-type', '') 8 | body = response.text 9 | {{if .Api.Response.Formats.Json}} 10 | # Response body is in JSON 11 | if typ.find('json') != -1: 12 | body = response.json() 13 | {{end}} 14 | return body 15 | -------------------------------------------------------------------------------- /examples/buffer/pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Buffer", 3 | "package": "buffer-alpaca", 4 | "version": "0.1.0", 5 | "url": "https://bufferapp.com", 6 | "keywords": ["alpaca", "buffer", "api", "client", "library"], 7 | "official": false, 8 | "author": { 9 | "name": "Pavan Kumar Sunkara", 10 | "email": "pavan.sss1991@gmail.com", 11 | "url": "http://github.com/pksunkara" 12 | }, 13 | "git": { 14 | "site": "github.com", 15 | "user": "alpaca-api", 16 | "name": "buffer" 17 | }, 18 | "license": "MIT", 19 | "php": { 20 | "vendor": "pksunkara" 21 | }, 22 | "python": { 23 | "license": "MIT License" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /templates/node/lib/http_client/response_handler.js: -------------------------------------------------------------------------------- 1 | var response = module.exports; 2 | 3 | /** 4 | * This module takes care of decoding the response body into suitable type 5 | */ 6 | response.getBody = function(res, body, callback) { 7 | var type = res.headers['content-type'], error = null; 8 | {{if .Api.Response.Formats.Json}} 9 | // Response body is in JSON 10 | if (type.indexOf('json') !== -1 && typeof(body) !== 'object') { 11 | try { 12 | body = JSON.parse(body || '{}'); 13 | } catch (err) { 14 | error = err; 15 | } 16 | } 17 | {{end}} 18 | return callback(error, res, body); 19 | }; 20 | -------------------------------------------------------------------------------- /examples/helpful/pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Helpful", 3 | "package": "helpful-client", 4 | "version": "1.0.0", 5 | "url": "https://helpful.io", 6 | "keywords": ["alpaca", "helpful", "assemblymade", "api", "client", "library"], 7 | "official": true, 8 | "author": { 9 | "name": "Pavan Kumar Sunkara", 10 | "email": "pavan.sss1991@gmail.com", 11 | "url": "http://assemblymade.com/helpful" 12 | }, 13 | "git": { 14 | "site": "github.com", 15 | "user": "asm-helpful", 16 | "name": "helpful" 17 | }, 18 | "license": "MIT", 19 | "php": { 20 | "vendor": "helpful" 21 | }, 22 | "python": { 23 | "license": "MIT License" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /templates/ruby/gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | Gem::Specification.new do |gem| 4 | gem.name = "{{.Pkg.Package}}" 5 | gem.version = "{{.Pkg.Version}}" 6 | gem.description = "{{if .Pkg.Official}}Official {{end}}{{.Pkg.Name}} API library client for ruby" 7 | gem.summary = "{{if .Pkg.Official}}Official {{end}}{{.Pkg.Name}} API library client for ruby" 8 | 9 | gem.author = "{{.Pkg.Author.Name}}" 10 | gem.email = "{{.Pkg.Author.Email}}" 11 | gem.homepage = "{{.Pkg.Url}}" 12 | gem.license = "{{.Pkg.License}}" 13 | 14 | gem.require_paths = ['lib'] 15 | 16 | gem.files = Dir["lib/**/*"] 17 | 18 | gem.add_dependency "faraday", "~> 0.9", ">= 0.9.0" 19 | gem.add_dependency "json", "~> 1.7", ">= 1.7.7" 20 | end 21 | -------------------------------------------------------------------------------- /templates/ruby/lib/http_client/response_handler.rb: -------------------------------------------------------------------------------- 1 | module {{call .Fnc.camelize .Pkg.Name}} 2 | 3 | module HttpClient 4 | 5 | # ResponseHandler takes care of decoding the response body into suitable type 6 | class ResponseHandler 7 | 8 | def self.get_body(response) 9 | type = response.headers["content-type"] 10 | body = response.body 11 | {{if .Api.Response.Formats.Json}} 12 | # Response body is in JSON 13 | if type and type.include?("json") 14 | begin 15 | body = JSON.parse body 16 | rescue JSON::ParserError 17 | return body 18 | end 19 | end 20 | {{end}} 21 | return body 22 | end 23 | 24 | end 25 | 26 | end 27 | 28 | end 29 | -------------------------------------------------------------------------------- /templates/php/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{.Pkg.Php.Vendor}}/{{.Pkg.Package}}", 3 | "version": "{{.Pkg.Version}}", 4 | "description": "{{if .Pkg.Official}}Official {{end}}{{.Pkg.Name}} API library client for PHP", 5 | "homepage": "{{.Pkg.Url}}", 6 | "authors": [ 7 | { 8 | "name": "{{.Pkg.Author.Name}}", 9 | "email": "{{.Pkg.Author.Email}}", 10 | "homepage": "{{.Pkg.Author.Url}}" 11 | } 12 | ], 13 | "keywords": ["{{call .Fnc.join .Pkg.Keywords "\", \""}}"], 14 | "autoload": { 15 | "psr-0": { 16 | "{{.Pkg.Name}}\\": "lib/" 17 | } 18 | }, 19 | "require": { 20 | "ext-json": "*", 21 | "guzzlehttp/guzzle": "3.7.*" 22 | }, 23 | "support": { 24 | "issues": "https://{{.Pkg.Git.Site}}/{{.Pkg.Git.User}}/{{.Pkg.Git.Name}}-php/issues" 25 | }, 26 | "license": "{{.Pkg.License}}" 27 | } 28 | -------------------------------------------------------------------------------- /templates/php/lib/HttpClient/ResponseHandler.php: -------------------------------------------------------------------------------- 1 | getBody(true); 15 | {{if .Api.Response.Formats.Json}} 16 | // Response body is in JSON 17 | if ($response->isContentType('json')) { 18 | $tmp = json_decode($body, true); 19 | 20 | if (JSON_ERROR_NONE === json_last_error()) { 21 | $body = $tmp; 22 | } 23 | } 24 | {{end}} 25 | return $body; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /templates/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{.Pkg.Package}}", 3 | "version": "{{.Pkg.Version}}", 4 | "description": "{{if .Pkg.Official}}Official {{end}}{{.Pkg.Name}} API library client for node.js", 5 | "author": "{{.Pkg.Author.Name}} <{{.Pkg.Author.Email}}> ({{.Pkg.Author.Url}})", 6 | "homepage": "{{.Pkg.Url}}", 7 | "keywords": ["{{call .Fnc.join .Pkg.Keywords "\", \""}}"], 8 | "repository": { 9 | "type": "git", 10 | "url": "https://{{.Pkg.Git.Site}}/{{.Pkg.Git.User}}/{{.Pkg.Git.Name}}-node" 11 | }, 12 | "main": "./lib/index.js", 13 | "dependencies": { 14 | "request": "2.x.x" 15 | }, 16 | "bugs": { 17 | "url": "https://{{.Pkg.Git.Site}}/{{.Pkg.Git.User}}/{{.Pkg.Git.Name}}-node/issues" 18 | }, 19 | "license": "{{.Pkg.License}}", 20 | "engines": { 21 | "node": ">= 0.8.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /alpaca/template.go: -------------------------------------------------------------------------------- 1 | package alpaca 2 | 3 | import ( 4 | "github.com/GeertJohan/go.rice" 5 | "os" 6 | "path" 7 | "text/template" 8 | ) 9 | 10 | func ReadTemplate(name string) *template.Template { 11 | templateBox := rice.MustFindBox("../templates") 12 | 13 | temp, err := templateBox.String(name) 14 | HandleError(err) 15 | 16 | return template.Must(template.New(name).Parse(temp)) 17 | } 18 | 19 | func WriteTemplate(temp *template.Template, out string, data interface{}) { 20 | file, err := os.Create(path.Clean(out)) 21 | defer file.Close() 22 | HandleError(err) 23 | 24 | HandleError(temp.Execute(file, data)) 25 | } 26 | 27 | func ChooseTemplate(template string) func(string, string, interface{}) { 28 | return func(name string, out string, data interface{}) { 29 | temp := ReadTemplate(path.Join(template, name)) 30 | WriteTemplate(temp, out, data) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /templates/node/lib/client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Main client for the module 3 | */ 4 | var Client = function({{if .Api.BaseAsArg}}baseUrl, {{end}}auth, options) { 5 | this.httpClient = new (require('./http_client').HttpClient)({{if .Api.BaseAsArg}}baseUrl, {{end}}auth, options); 6 | 7 | return this; 8 | }; 9 | {{with $data := .}}{{range .Api.Classes}} 10 | /** 11 | * {{(index $data.Doc .Name).Desc}}{{with .Args}} 12 | *{{end}}{{with $class := .}}{{range .Args}} 13 | * @param "{{.}}" {{(index ((index $data.Doc $class.Name).Args) .).Desc}}{{end}}{{end}} 14 | */ 15 | Client.prototype.{{call $data.Fnc.camelizeDownFirst .Name}} = function ({{call $data.Fnc.args.node .Args true}}) { 16 | return new (require('./api/{{call $data.Fnc.underscore .Name}}'))({{call $data.Fnc.args.node .Args}}this.httpClient); 17 | }; 18 | {{end}}{{end}} 19 | // Export module 20 | module.exports = Client; 21 | -------------------------------------------------------------------------------- /templates/node/lib/http_client/request_handler.js: -------------------------------------------------------------------------------- 1 | var request = module.exports; 2 | 3 | /** 4 | * This module takes care of encoding the request body into format given by options 5 | */ 6 | request.setBody = function(reqobj, body, options) { 7 | var type = (options.request_type ? options.request_type : '{{or .Api.Request.Formats.Default "raw"}}'); 8 | 9 | {{if .Api.Request.Formats.Json}} 10 | // Encoding body into JSON format 11 | if (type === 'json') { 12 | reqobj.json = body; 13 | } 14 | {{end}}{{if .Api.Request.Formats.Form}} 15 | // Encoding body into form-urlencoded format 16 | if (type === 'form') { 17 | reqobj.form = body; 18 | } 19 | {{end}} 20 | // Raw body 21 | if (type === 'raw') { 22 | reqobj.body = body; 23 | 24 | if (typeof reqobj.body === 'object') { 25 | delete reqobj.body; 26 | } 27 | } 28 | 29 | return reqobj; 30 | }; 31 | -------------------------------------------------------------------------------- /templates/python/lib/client.py: -------------------------------------------------------------------------------- 1 | from .http_client import HttpClient 2 | 3 | # Assign all the api classes{{with $data := .}}{{range .Api.Classes}} 4 | from .api.{{call $data.Fnc.underscore .Name}} import {{call $data.Fnc.camelize .Name}}{{end}}{{end}} 5 | 6 | 7 | class Client(object): 8 | 9 | def __init__(self, {{if .Api.BaseAsArg}}base_url, {{end}}auth={}, options={}): 10 | self.http_client = HttpClient({{if .Api.BaseAsArg}}base_url, {{end}}auth, options) 11 | {{with $data := .}}{{range .Api.Classes}} 12 | def {{call $data.Fnc.underscore .Name}}(self{{call $data.Fnc.args.python .Args true true}}): 13 | """{{(index $data.Doc .Name).Desc}}{{with .Args}} 14 | 15 | Args:{{end}}{{with $class := .}}{{range .Args}} 16 | {{.}}: {{(index ((index $data.Doc $class.Name).Args) .).Desc}}{{end}}{{end}} 17 | """ 18 | return {{call $data.Fnc.camelize .Name}}({{call $data.Fnc.args.python .Args}}self.http_client) 19 | {{end}}{{end}} 20 | -------------------------------------------------------------------------------- /templates/php/lib/Client.php: -------------------------------------------------------------------------------- 1 | httpClient = new HttpClient({{if .Api.BaseAsArg}}$baseUrl, {{end}}$auth, $options); 14 | } 15 | {{with $data := .}}{{range .Api.Classes}} 16 | /** 17 | * {{(index $data.Doc .Name).Desc}}{{with .Args}} 18 | *{{end}}{{with $class := .}}{{range .Args}} 19 | * @param ${{.}} {{(index ((index $data.Doc $class.Name).Args) .).Desc}}{{end}}{{end}} 20 | */ 21 | public function {{call $data.Fnc.camelizeDownFirst .Name}}({{call $data.Fnc.args.php .Args true}}) 22 | { 23 | return new Api\{{call $data.Fnc.camelize .Name}}({{call $data.Fnc.args.php .Args}}$this->httpClient); 24 | } 25 | {{end}}{{end}} 26 | } 27 | -------------------------------------------------------------------------------- /templates/ruby/lib/client.rb: -------------------------------------------------------------------------------- 1 | require "faraday" 2 | require "json" 3 | {{with $data := .}}{{range .Api.Classes}} 4 | require "{{call $data.Fnc.underscore $data.Pkg.Name}}/api/{{call $data.Fnc.underscore .Name}}"{{end}}{{end}} 5 | 6 | module {{call .Fnc.camelize .Pkg.Name}} 7 | 8 | class Client 9 | 10 | def initialize({{if .Api.BaseAsArg}}base_url, {{end}}auth = {}, options = {}) 11 | @http_client = {{call .Fnc.camelize .Pkg.Name}}::HttpClient::HttpClient.new({{if .Api.BaseAsArg}}base_url, {{end}}auth, options) 12 | end 13 | {{with $data := .}}{{range .Api.Classes}} 14 | # {{(index $data.Doc .Name).Desc}}{{with .Args}} 15 | #{{end}}{{with $class := .}}{{range .Args}} 16 | # {{.}} - {{(index ((index $data.Doc $class.Name).Args) .).Desc}}{{end}}{{end}} 17 | def {{call $data.Fnc.underscore .Name}}({{call $data.Fnc.args.ruby .Args true}}) 18 | {{call $data.Fnc.camelize $data.Pkg.Name}}::Api::{{call $data.Fnc.camelize .Name}}.new({{call $data.Fnc.args.ruby .Args}}@http_client) 19 | end 20 | {{end}}{{end}} 21 | end 22 | 23 | end 24 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.2.1 2 | 3 | Using Mozilla 2.0 license from now on. 4 | 5 | Features: 6 | 7 | - Support `authorization.header_prefix` for authorization headers (#6) 8 | - Can now make authentication compulsory using `authorizaiton.need_auth` 9 | - Default value is `false` for `response.formats.html` 10 | - Default value is `false` for `request.formats.form` 11 | - Allow API `base_url` to be an argument (#30) 12 | - Parameters needed for method URL can be defined in the method 13 | 14 | Bugfixes: 15 | 16 | - Helpful error when missing language specific fields in `pkg.json` 17 | - Better building of binary (#22) 18 | - Python style fixes (#26) 19 | - Comments in generated code are now params/args aware 20 | - Fix bug with JSON parsing response in node.js 21 | - Ruby style fixes 22 | 23 | ## 0.2.0 24 | 25 | Features: 26 | 27 | - Added Makefile (#7) 28 | - Added support for `no_verify_ssl` in **api.json** (#10) 29 | - Made `params` in **api.json** into an array of hashes (#12) 30 | 31 | Bugfixes: 32 | 33 | - MakeStringArrayInterface and ArrayStringInterface supports nil (#14) 34 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/jessevdk/go-flags" 6 | "github.com/pksunkara/alpaca/alpaca" 7 | "os" 8 | ) 9 | 10 | func main() { 11 | 12 | // Options for flags package 13 | var opts struct { 14 | Version bool `short:"v" long:"version" description:"Show version information"` 15 | 16 | Format string `short:"f" long:"format" description:"API description format" value-name:"FORMAT"` 17 | 18 | Langs alpaca.LanguageOptions `group:"Language Options"` 19 | } 20 | 21 | // Build the parser 22 | parser := flags.NewParser(&opts, flags.Default) 23 | 24 | // Set usage string 25 | parser.Usage = "[options] " 26 | 27 | // Parse the arguments 28 | args, err := parser.Parse() 29 | 30 | if err != nil { 31 | os.Exit(0) 32 | } 33 | 34 | // Print version and exit 35 | if opts.Version { 36 | fmt.Println(alpaca.Version) 37 | os.Exit(0) 38 | } 39 | 40 | // If no argument is given 41 | if len(args) == 0 { 42 | parser.WriteHelp(os.Stdout) 43 | os.Exit(0) 44 | } 45 | 46 | alpaca.LoadLibraryPath(args[0]) 47 | 48 | if opts.Format != "" { 49 | alpaca.ConvertFormat(opts.Format) 50 | } 51 | 52 | alpaca.WriteLibraries(&opts.Langs) 53 | } 54 | -------------------------------------------------------------------------------- /templates/php/lib/HttpClient/RequestHandler.php: -------------------------------------------------------------------------------- 1 | setBody($body, 'application/json'); 21 | } 22 | {{end}}{{if .Api.Request.Formats.Form}} 23 | if ($type == 'form') { 24 | // Encoding body into form-urlencoded format 25 | return $request->addPostFields($body); 26 | } 27 | {{end}} 28 | if ($type == 'raw') { 29 | // Raw body 30 | return $request->setBody($body, $header); 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /templates/ruby/lib/http_client/request_handler.rb: -------------------------------------------------------------------------------- 1 | module {{call .Fnc.camelize .Pkg.Name}} 2 | 3 | module HttpClient 4 | 5 | # RequestHandler takes care of encoding the request body into format given by options 6 | class RequestHandler 7 | 8 | def self.set_body(options) 9 | type = options.fetch(:request_type, "{{or .Api.Request.Formats.Default "raw"}}") 10 | {{if .Api.Request.Formats.Json}} 11 | # Encoding request body into JSON format 12 | if type == "json" 13 | options[:body] = options[:body].to_json 14 | options[:headers]["content-type"] = "application/json" 15 | end 16 | {{end}}{{if .Api.Request.Formats.Form}} 17 | # Encoding body into form-urlencoded format 18 | if type == "form" 19 | options[:body] = Faraday::Utils::ParamsHash[options[:body]].to_query 20 | options[:headers]["content-type"] = "application/x-www-form-urlencoded" 21 | end 22 | {{end}} 23 | # Raw body 24 | if type == "raw" 25 | options[:body] = options[:body].is_a?(Hash) ? "" : options[:body] 26 | options[:headers].delete("content-type") 27 | end 28 | 29 | return options 30 | end 31 | 32 | end 33 | 34 | end 35 | 36 | end 37 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION = 0.2.1 2 | 3 | GO_RICE = rice 4 | GO_FMT = gofmt -w 5 | GO_XC = goxc 6 | 7 | GOXC_FILE = .goxc.local.json 8 | 9 | DEPS = \ 10 | github.com/GeertJohan/go.rice/rice \ 11 | github.com/robertkrimen/terst \ 12 | github.com/jessevdk/go-flags \ 13 | github.com/kardianos/osext \ 14 | bitbucket.org/pkg/inflect 15 | 16 | all:deps templates 17 | 18 | templates:clean 19 | $(GO_RICE) --import-path github.com/pksunkara/alpaca/alpaca embed 20 | 21 | compile:templates goxc 22 | 23 | goxc: 24 | $(shell echo '{\n "ArtifactsDest": "build",\n "ConfigVersion": "0.9",' > $(GOXC_FILE)) 25 | $(shell echo ' "PackageVersion": "$(VERSION)",\n "TaskSettings": {' >> $(GOXC_FILE)) 26 | $(shell echo ' "bintray": {\n "apikey": "",\n "package": "alpaca",' >> $(GOXC_FILE)) 27 | $(shell echo ' "repository": "utils",\n "subject": "pksunkara"' >> $(GOXC_FILE)) 28 | $(shell echo ' }\n }\n}' >> $(GOXC_FILE)) 29 | $(GO_XC) 30 | 31 | bintray: 32 | $(GO_XC) bintray 33 | 34 | test: 35 | go test -v ./... 36 | 37 | test-cover: 38 | go test -coverprofile=coverage.out github.com/pksunkara/alpaca/alpaca 39 | go tool cover -html=coverage.out 40 | 41 | install: 42 | go install -a github.com/pksunkara/alpaca 43 | 44 | deps: 45 | go get -u $(DEPS) 46 | 47 | clean: 48 | $(GO_RICE) --import-path github.com/pksunkara/alpaca/alpaca clean 49 | -------------------------------------------------------------------------------- /templates/node/lib/http_client/error_handler.js: -------------------------------------------------------------------------------- 1 | var errors = require('../error'); 2 | 3 | /** 4 | * ErrorHanlder takes care of selecting the error message from response body 5 | */ 6 | module.exports = function(response, body, callback) { 7 | var code = response.statusCode 8 | , message = '' 9 | , type = response.headers['content-type']; 10 | 11 | if (Math.floor(code/100) === 5) { 12 | return callback(new errors.ClientError('Error ' + code, code)); 13 | } 14 | 15 | if (Math.floor(code/100) === 4) { 16 | // If HTML, whole body is taken 17 | if (typeof body === 'string') { 18 | message = body; 19 | } 20 | {{if .Api.Response.Formats.Json}} 21 | // If JSON, a particular field is taken and used 22 | if (type.indexOf('json') !== -1 && typeof body === 'object') { 23 | if (body['{{.Api.Error.Message}}']) { 24 | message = body['{{.Api.Error.Message}}']; 25 | } else { 26 | message = 'Unable to select error message from json returned by request responsible for error'; 27 | } 28 | } 29 | {{end}} 30 | if (message === '') { 31 | message = 'Unable to understand the content type of response returned by request responsible for error'; 32 | } 33 | 34 | return callback(new errors.ClientError(message, code)); 35 | } 36 | 37 | return callback(null, response, body); 38 | }; 39 | -------------------------------------------------------------------------------- /templates/python/setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | try: 5 | from setuptools import setup 6 | except ImportError: 7 | from distutils.core import setup 8 | 9 | setup( 10 | name='{{.Pkg.Package}}', 11 | version='{{.Pkg.Version}}', 12 | description='{{if .Pkg.Official}}Official {{end}}{{.Pkg.Name}} API library client for python', 13 | author='{{.Pkg.Author.Name}}', 14 | author_email='{{.Pkg.Author.Email}}', 15 | url='{{.Pkg.Url}}', 16 | license='{{.Pkg.License}}', 17 | install_requires=[ 18 | 'requests >= 2.1.0' 19 | ], 20 | packages=[ 21 | '{{call .Fnc.underscore .Pkg.Name}}', 22 | '{{call .Fnc.underscore .Pkg.Name}}.api', 23 | '{{call .Fnc.underscore .Pkg.Name}}.error', 24 | '{{call .Fnc.underscore .Pkg.Name}}.http_client' 25 | ], 26 | classifiers=[ 27 | 'Development Status :: 5 - Production/Stable', 28 | 'Intended Audience :: Developers',{{if .Pkg.Python.License}} 29 | 'License :: OSI Approved :: {{.Pkg.Python.License}}',{{end}} 30 | 'Operating System :: OS Independent', 31 | 'Programming Language :: Python :: 2.6', 32 | 'Programming Language :: Python :: 2.7', 33 | 'Programming Language :: Python :: 3.2', 34 | 'Programming Language :: Python :: 3.3', 35 | 'Topic :: Software Development :: Libraries :: Python Modules', 36 | ] 37 | ) 38 | -------------------------------------------------------------------------------- /templates/python/lib/http_client/error_handler.py: -------------------------------------------------------------------------------- 1 | from ..error import ClientError 2 | from .response_handler import ResponseHandler 3 | 4 | 5 | class ErrorHandler(object): 6 | 7 | """ErrorHandler takes care of getting the error message from response body""" 8 | 9 | @staticmethod 10 | def check_error(response, *args, **kwargs): 11 | code = response.status_code 12 | typ = response.headers.get('content-type', '') 13 | 14 | if code in range(500, 600): 15 | raise ClientError('Error ' + str(code), code) 16 | elif code in range(400, 500): 17 | body = ResponseHandler.get_body(response) 18 | message = '' 19 | 20 | # If HTML, whole body is taken 21 | if isinstance(body, str): 22 | message = body 23 | {{if .Api.Response.Formats.Json}} 24 | # If JSON, a particular field is taken and used 25 | if typ.find('json') != -1 and isinstance(body, dict): 26 | if '{{.Api.Error.Message}}' in body: 27 | message = body['{{.Api.Error.Message}}'] 28 | else: 29 | message = 'Unable to select error message from json returned by request responsible for error' 30 | {{end}} 31 | if message == '': 32 | message = 'Unable to understand the content type of response returned by request responsible for error' 33 | 34 | raise ClientError(message, code) 35 | -------------------------------------------------------------------------------- /templates/ruby/lib/api/api.rb: -------------------------------------------------------------------------------- 1 | module {{call .Fnc.camelize .Pkg.Name}} 2 | {{define "boq"}}{{if (eq (or .Method "get") "get")}}query{{else}}body{{end}}{{end}} 3 | module Api 4 | 5 | # {{(index .Doc .Active.Name).Desc}}{{with (index .Doc .Active.Name).Args}} 6 | #{{end}}{{with $data := .}}{{range .Active.Args}} 7 | # {{.}} - {{(index ((index $data.Doc $data.Active.Name).Args) .).Desc}}{{end}}{{end}} 8 | class {{call .Fnc.camelize .Active.Name}} 9 | 10 | def initialize({{call .Fnc.args.ruby .Active.Args}}client) 11 | {{range .Active.Args}} @{{.}} = {{.}} 12 | {{end}} @client = client 13 | end 14 | {{with $data := .}}{{range .Active.Functions}} 15 | # {{(index ((index $data.Doc $data.Active.Name).Functions) .Name).Desc}} 16 | # 17 | # '{{.Path}}' {{call $data.Fnc.upper (or .Method "get")}}{{with .Params}} 18 | #{{end}}{{with $method := .}}{{range .Params}}{{if .Required}} 19 | # {{.Name}} - {{(index ((index ((index $data.Doc $data.Active.Name).Functions) $method.Name).Params) .Name).Desc}}{{end}}{{end}}{{end}} 20 | def {{call $data.Fnc.underscore .Name}}({{call $data.Fnc.args.ruby .Params}}options = {}) 21 | body = options.fetch(:{{template "boq" .}}, {}){{range .Params}}{{if .Required}}{{if (not .UrlUse)}} 22 | body[:{{.Name}}] = {{.Name}}{{end}}{{end}}{{end}} 23 | 24 | @client.{{or .Method "get"}}("{{call $data.Fnc.path.ruby .Path $data.Active.Args .Params}}", body, options) 25 | end 26 | {{end}}{{end}} 27 | end 28 | 29 | end 30 | 31 | end 32 | -------------------------------------------------------------------------------- /templates/python/lib/api/api.py: -------------------------------------------------------------------------------- 1 | {{define "boq"}}{{if (eq (or .Method "get") "get")}}query{{else}}body{{end}}{{end}}class {{call .Fnc.camelize .Active.Name}}(object): 2 | 3 | """{{(index .Doc .Active.Name).Desc}}{{with (index .Doc .Active.Name).Args}} 4 | 5 | Args:{{end}}{{with $data := .}}{{range .Active.Args}} 6 | {{.}}: {{(index ((index $data.Doc $data.Active.Name).Args) .).Desc}}{{end}}{{end}} 7 | """ 8 | 9 | def __init__(self, {{call .Fnc.args.python .Active.Args}}client):{{range .Active.Args}} 10 | self.{{.}} = {{.}}{{end}} 11 | self.client = client 12 | {{with $data := .}}{{range .Active.Functions}} 13 | def {{call $data.Fnc.underscore .Name}}(self, {{call $data.Fnc.args.python .Params}}options={}): 14 | """{{(index ((index $data.Doc $data.Active.Name).Functions) .Name).Desc}} 15 | 16 | '{{.Path}}' {{call $data.Fnc.upper (or .Method "get")}}{{with .Params}} 17 | 18 | Args:{{end}}{{with $method := .}}{{range .Params}}{{if .Required}} 19 | {{.Name}}: {{(index ((index ((index $data.Doc $data.Active.Name).Functions) $method.Name).Params) .Name).Desc}}{{end}}{{end}}{{end}} 20 | """ 21 | body = options['{{template "boq" .}}'] if '{{template "boq" .}}' in options else {}{{range .Params}}{{if .Required}}{{if (not .UrlUse)}} 22 | body['{{.Name}}'] = {{.Name}}{{end}}{{end}}{{end}} 23 | 24 | response = self.client.{{or .Method "get"}}('{{call $data.Fnc.path.python .Path $data.Active.Args .Params}}', body, options) 25 | 26 | return response 27 | {{end}}{{end}} 28 | -------------------------------------------------------------------------------- /templates/ruby/lib/http_client/error_handler.rb: -------------------------------------------------------------------------------- 1 | module {{call .Fnc.camelize .Pkg.Name}} 2 | 3 | module HttpClient 4 | 5 | # ErrorHanlder takes care of selecting the error message from response body 6 | class ErrorHandler < Faraday::Middleware 7 | 8 | def initialize(app) 9 | super(app) 10 | end 11 | 12 | def call(env) 13 | @app.call(env).on_complete do |env| 14 | code = env.status 15 | type = env.response_headers["content-type"] 16 | 17 | case code 18 | when 500...599 19 | raise {{call .Fnc.camelize .Pkg.Name}}::Error::ClientError.new("Error #{code}", code) 20 | when 400...499 21 | body = {{call .Fnc.camelize .Pkg.Name}}::HttpClient::ResponseHandler.get_body(env) 22 | message = "" 23 | 24 | # If HTML, whole body is taken 25 | if body.is_a?(String) 26 | message = body 27 | end 28 | {{if .Api.Response.Formats.Json}} 29 | # If JSON, a particular field is taken and used 30 | if type and type.include?("json") and body.is_a?(Hash) 31 | if body.has_key?("{{.Api.Error.Message}}") 32 | message = body["{{.Api.Error.Message}}"] 33 | else 34 | message = "Unable to select error message from json returned by request responsible for error" 35 | end 36 | end 37 | {{end}} 38 | if message == "" 39 | message = "Unable to understand the content type of response returned by request responsible for error" 40 | end 41 | 42 | raise {{call .Fnc.camelize .Pkg.Name}}::Error::ClientError.new message, code 43 | end 44 | end 45 | end 46 | 47 | end 48 | 49 | end 50 | 51 | end 52 | -------------------------------------------------------------------------------- /templates/node/lib/api/api.js: -------------------------------------------------------------------------------- 1 | {{define "boq"}}{{if (eq (or .Method "get") "get")}}query{{else}}body{{end}}{{end}}/** 2 | * {{(index .Doc .Active.Name).Desc}}{{with (index .Doc .Active.Name).Args}} 3 | *{{end}}{{with $data := .}}{{range .Active.Args}} 4 | * @param "{{.}}" {{(index ((index $data.Doc $data.Active.Name).Args) .).Desc}}{{end}}{{end}} 5 | */ 6 | var {{call .Fnc.camelize .Active.Name}} = function({{call .Fnc.args.node .Active.Args}}client) { 7 | {{range .Active.Args}} this.{{.}} = {{.}}; 8 | {{end}} this.client = client; 9 | 10 | return this; 11 | }; 12 | {{with $data := .}}{{range .Active.Functions}} 13 | /** 14 | * {{(index ((index $data.Doc $data.Active.Name).Functions) .Name).Desc}} 15 | * 16 | * '{{.Path}}' {{call $data.Fnc.upper (or .Method "get")}}{{with .Params}} 17 | *{{end}}{{with $method := .}}{{range .Params}}{{if .Required}} 18 | * @param "{{.Name}}" {{(index ((index ((index $data.Doc $data.Active.Name).Functions) $method.Name).Params) .Name).Desc}}{{end}}{{end}}{{end}} 19 | */ 20 | {{call $data.Fnc.camelize $data.Active.Name}}.prototype.{{call $data.Fnc.camelizeDownFirst .Name}} = function ({{call $data.Fnc.args.node .Params}}options, callback) { 21 | if (typeof options === 'function') { 22 | callback = options; 23 | options = {}; 24 | } 25 | 26 | var body = (options.{{template "boq" .}} ? options.{{template "boq" .}} : {});{{range .Params}}{{if .Required}}{{if (not .UrlUse)}} 27 | body['{{.Name}}'] = {{.Name}};{{end}}{{end}}{{end}} 28 | 29 | this.client.{{or .Method "get"}}('{{call $data.Fnc.path.node .Path $data.Active.Args .Params}}', body, options, function(err, response) { 30 | if (err) { 31 | return callback(err); 32 | } 33 | 34 | callback(null, response); 35 | }); 36 | }; 37 | {{end}}{{end}} 38 | // Export module 39 | module.exports = {{call .Fnc.camelize .Active.Name}}; 40 | -------------------------------------------------------------------------------- /templates/php/lib/HttpClient/ErrorHandler.php: -------------------------------------------------------------------------------- 1 | getResponse(); 20 | 21 | $message = null; 22 | $code = $response->getStatusCode(); 23 | 24 | if ($response->isServerError()) { 25 | throw new ClientException('Error '.$code, $code); 26 | } 27 | 28 | if ($response->isClientError()) { 29 | $body = ResponseHandler::getBody($response); 30 | 31 | // If HTML, whole body is taken 32 | if (gettype($body) == 'string') { 33 | $message = $body; 34 | } 35 | {{if .Api.Response.Formats.Json}} 36 | // If JSON, a particular field is taken and used 37 | if ($response->isContentType('json') && is_array($body)) { 38 | if (isset($body['{{.Api.Error.Message}}'])) { 39 | $message = $body['{{.Api.Error.Message}}']; 40 | } else { 41 | $message = 'Unable to select error message from json returned by request responsible for error'; 42 | } 43 | } 44 | {{end}} 45 | if (empty($message)) { 46 | $message = 'Unable to understand the content type of response returned by request responsible for error'; 47 | } 48 | 49 | throw new ClientException($message, $code); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /templates/php/lib/Api/Api.php: -------------------------------------------------------------------------------- 1 | {{.}} = ${{.}}; 21 | {{end}} $this->client = $client; 22 | } 23 | {{with $data := .}}{{range .Active.Functions}} 24 | /** 25 | * {{(index ((index $data.Doc $data.Active.Name).Functions) .Name).Desc}} 26 | * 27 | * '{{.Path}}' {{call $data.Fnc.upper (or .Method "get")}}{{with .Params}} 28 | *{{end}}{{with $method := .}}{{range .Params}}{{if .Required}} 29 | * @param ${{.Name}} {{(index ((index ((index $data.Doc $data.Active.Name).Functions) $method.Name).Params) .Name).Desc}}{{end}}{{end}}{{end}} 30 | */ 31 | public function {{call $data.Fnc.camelizeDownFirst .Name}}({{call $data.Fnc.args.php .Params}}array $options = array()) 32 | { 33 | $body = (isset($options['{{template "boq" .}}']) ? $options['{{template "boq" .}}'] : array());{{range .Params}}{{if .Required}}{{if (not .UrlUse)}} 34 | $body['{{.Name}}'] = ${{.Name}};{{end}}{{end}}{{end}} 35 | 36 | $response = $this->client->{{or .Method "get"}}('{{call $data.Fnc.path.php .Path $data.Active.Args .Params}}', $body, $options); 37 | 38 | return $response; 39 | } 40 | {{end}}{{end}} 41 | } 42 | -------------------------------------------------------------------------------- /alpaca/langs_node.go: -------------------------------------------------------------------------------- 1 | package alpaca 2 | 3 | import ( 4 | "bitbucket.org/pkg/inflect" 5 | ) 6 | 7 | func WriteNode(data *Data) { 8 | MakeLibraryDir("node") 9 | RunTemplate := ChooseTemplate("node") 10 | 11 | RunTemplate("gitignore", ".gitignore", data) 12 | RunTemplate("package.json", "package.json", data) 13 | RunTemplate("readme.md", "README.md", data) 14 | 15 | MakeDir("lib") 16 | RunTemplate("lib/index.js", "index.js", data) 17 | 18 | MakeDir(inflect.Underscore(data.Pkg.Name)) 19 | RunTemplate("lib/client.js", "client.js", data) 20 | 21 | MakeDir("error") 22 | RunTemplate("lib/error/index.js", "index.js", data) 23 | RunTemplate("lib/error/client_error.js", "client_error.js", data) 24 | MoveDir("..") 25 | 26 | MakeDir("http_client") 27 | RunTemplate("lib/http_client/index.js", "index.js", data) 28 | RunTemplate("lib/http_client/auth_handler.js", "auth_handler.js", data) 29 | RunTemplate("lib/http_client/error_handler.js", "error_handler.js", data) 30 | RunTemplate("lib/http_client/request_handler.js", "request_handler.js", data) 31 | RunTemplate("lib/http_client/response.js", "response.js", data) 32 | RunTemplate("lib/http_client/response_handler.js", "response_handler.js", data) 33 | MoveDir("..") 34 | 35 | MakeDir("api") 36 | 37 | for _, v := range data.Api.Classes { 38 | data.Active = &v 39 | RunTemplate("lib/api/api.js", inflect.Underscore(v.Name)+".js", data) 40 | data.Active = nil 41 | } 42 | } 43 | 44 | func FunctionsNode(fnc map[string]interface{}) { 45 | args := fnc["args"].(map[string]interface{}) 46 | path := fnc["path"].(map[string]interface{}) 47 | prnt := fnc["prnt"].(map[string]interface{}) 48 | 49 | args["node"] = ArgsFunctionMaker("", ", ") 50 | path["node"] = PathFunctionMaker("' + ", "this.", " + '") 51 | prnt["node"] = PrntFunctionMaker(false, " ", "\"", "\"", "[", "]", "{", "}", "", ": ") 52 | } 53 | 54 | func CheckNode(data *Data) error { 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /alpaca/langs_python.go: -------------------------------------------------------------------------------- 1 | package alpaca 2 | 3 | import ( 4 | "bitbucket.org/pkg/inflect" 5 | ) 6 | 7 | func WritePython(data *Data) { 8 | MakeLibraryDir("python") 9 | RunTemplate := ChooseTemplate("python") 10 | 11 | RunTemplate("gitignore", ".gitignore", data) 12 | RunTemplate("setup.py", "setup.py", data) 13 | RunTemplate("readme.md", "README.md", data) 14 | 15 | MakeDir(inflect.Underscore(data.Pkg.Name)) 16 | RunTemplate("lib/__init__.py", "__init__.py", data) 17 | RunTemplate("lib/client.py", "client.py", data) 18 | 19 | MakeDir("error") 20 | RunTemplate("lib/error/__init__.py", "__init__.py", data) 21 | RunTemplate("lib/error/client_error.py", "client_error.py", data) 22 | MoveDir("..") 23 | 24 | MakeDir("http_client") 25 | RunTemplate("lib/http_client/__init__.py", "__init__.py", data) 26 | RunTemplate("lib/http_client/auth_handler.py", "auth_handler.py", data) 27 | RunTemplate("lib/http_client/error_handler.py", "error_handler.py", data) 28 | RunTemplate("lib/http_client/request_handler.py", "request_handler.py", data) 29 | RunTemplate("lib/http_client/response.py", "response.py", data) 30 | RunTemplate("lib/http_client/response_handler.py", "response_handler.py", data) 31 | MoveDir("..") 32 | 33 | MakeDir("api") 34 | RunTemplate("lib/api/__init__.py", "__init__.py", data) 35 | 36 | for _, v := range data.Api.Classes { 37 | data.Active = &v 38 | RunTemplate("lib/api/api.py", inflect.Underscore(v.Name)+".py", data) 39 | data.Active = nil 40 | } 41 | } 42 | 43 | func FunctionsPython(fnc map[string]interface{}) { 44 | args := fnc["args"].(map[string]interface{}) 45 | path := fnc["path"].(map[string]interface{}) 46 | prnt := fnc["prnt"].(map[string]interface{}) 47 | 48 | args["python"] = ArgsFunctionMaker("", ", ") 49 | path["python"] = PathFunctionMaker("' + ", "self.", " + '") 50 | prnt["python"] = PrntFunctionMaker(true, " ", "\"", "\"", "[", "]", "{", "}", "'", "': ") 51 | } 52 | 53 | func CheckPython(data *Data) error { 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /alpaca/langs_ruby.go: -------------------------------------------------------------------------------- 1 | package alpaca 2 | 3 | import ( 4 | "bitbucket.org/pkg/inflect" 5 | ) 6 | 7 | func WriteRuby(data *Data) { 8 | MakeLibraryDir("ruby") 9 | RunTemplate := ChooseTemplate("ruby") 10 | 11 | RunTemplate("gitignore", ".gitignore", data) 12 | RunTemplate("gemspec", data.Pkg.Package+".gemspec", data) 13 | RunTemplate("readme.md", "README.md", data) 14 | 15 | MakeDir("lib") 16 | RunTemplate("lib/name.rb", data.Pkg.Package+".rb", data) 17 | 18 | MakeDir(inflect.Underscore(data.Pkg.Name)) 19 | RunTemplate("lib/client.rb", "client.rb", data) 20 | RunTemplate("lib/http_client.rb", "http_client.rb", data) 21 | RunTemplate("lib/error.rb", "error.rb", data) 22 | RunTemplate("lib/version.rb", "version.rb", data) 23 | 24 | MakeDir("error") 25 | RunTemplate("lib/error/client_error.rb", "client_error.rb", data) 26 | MoveDir("..") 27 | 28 | MakeDir("http_client") 29 | RunTemplate("lib/http_client/auth_handler.rb", "auth_handler.rb", data) 30 | RunTemplate("lib/http_client/error_handler.rb", "error_handler.rb", data) 31 | RunTemplate("lib/http_client/request_handler.rb", "request_handler.rb", data) 32 | RunTemplate("lib/http_client/response.rb", "response.rb", data) 33 | RunTemplate("lib/http_client/response_handler.rb", "response_handler.rb", data) 34 | MoveDir("..") 35 | 36 | MakeDir("api") 37 | 38 | for _, v := range data.Api.Classes { 39 | data.Active = &v 40 | RunTemplate("lib/api/api.rb", inflect.Underscore(v.Name)+".rb", data) 41 | data.Active = nil 42 | } 43 | } 44 | 45 | func FunctionsRuby(fnc map[string]interface{}) { 46 | args := fnc["args"].(map[string]interface{}) 47 | path := fnc["path"].(map[string]interface{}) 48 | prnt := fnc["prnt"].(map[string]interface{}) 49 | 50 | args["ruby"] = ArgsFunctionMaker("", ", ") 51 | path["ruby"] = PathFunctionMaker("#{", "@", "}") 52 | prnt["ruby"] = PrntFunctionMaker(false, " ", "\"", "\"", "[", "]", "{", "}", ":", " => ") 53 | } 54 | 55 | func CheckRuby(data *Data) error { 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /alpaca/langs_php.go: -------------------------------------------------------------------------------- 1 | package alpaca 2 | 3 | import ( 4 | "bitbucket.org/pkg/inflect" 5 | "errors" 6 | ) 7 | 8 | func WritePhp(data *Data) { 9 | MakeLibraryDir("php") 10 | RunTemplate := ChooseTemplate("php") 11 | 12 | RunTemplate("gitignore", ".gitignore", data) 13 | RunTemplate("composer.json", "composer.json", data) 14 | RunTemplate("readme.md", "README.md", data) 15 | 16 | MakeDir("lib") 17 | 18 | MakeDir(inflect.Camelize(data.Pkg.Name)) 19 | RunTemplate("lib/Client.php", "Client.php", data) 20 | 21 | MakeDir("Exception") 22 | RunTemplate("lib/Exception/ExceptionInterface.php", "ExceptionInterface.php", data) 23 | RunTemplate("lib/Exception/ClientException.php", "ClientException.php", data) 24 | MoveDir("..") 25 | 26 | MakeDir("HttpClient") 27 | RunTemplate("lib/HttpClient/HttpClient.php", "HttpClient.php", data) 28 | RunTemplate("lib/HttpClient/AuthHandler.php", "AuthHandler.php", data) 29 | RunTemplate("lib/HttpClient/ErrorHandler.php", "ErrorHandler.php", data) 30 | RunTemplate("lib/HttpClient/RequestHandler.php", "RequestHandler.php", data) 31 | RunTemplate("lib/HttpClient/Response.php", "Response.php", data) 32 | RunTemplate("lib/HttpClient/ResponseHandler.php", "ResponseHandler.php", data) 33 | MoveDir("..") 34 | 35 | MakeDir("Api") 36 | 37 | for _, v := range data.Api.Classes { 38 | data.Active = &v 39 | RunTemplate("lib/Api/Api.php", inflect.Camelize(v.Name)+".php", data) 40 | data.Active = nil 41 | } 42 | } 43 | 44 | func FunctionsPhp(fnc map[string]interface{}) { 45 | args := fnc["args"].(map[string]interface{}) 46 | path := fnc["path"].(map[string]interface{}) 47 | prnt := fnc["prnt"].(map[string]interface{}) 48 | 49 | args["php"] = ArgsFunctionMaker("$", ", ") 50 | path["php"] = PathFunctionMaker("'.rawurlencode($$", "this->", ").'") 51 | prnt["php"] = PrntFunctionMaker(false, " ", "\"", "\"", "array(", ")", "array(", ")", "'", "' => ") 52 | } 53 | 54 | func CheckPhp(data *Data) error { 55 | if data.Pkg.Php.Vendor == "" { 56 | return errors.New("php.vendor is needed in pkg.json for generating php library") 57 | } 58 | 59 | return nil 60 | } 61 | -------------------------------------------------------------------------------- /templates/python/lib/http_client/request_handler.py: -------------------------------------------------------------------------------- 1 | import urllib 2 | import json 3 | 4 | 5 | class RequestHandler(object): 6 | 7 | """RequestHandler takes care of encoding the request body into format given by options""" 8 | 9 | @staticmethod 10 | def render_key(parents): 11 | depth, new = 0, '' 12 | 13 | for x in parents: 14 | old = '[%s]' if depth > 0 else '%s' 15 | new += old % x 16 | depth += 1 17 | 18 | return new 19 | 20 | @staticmethod 21 | def urlencode(data, parents=None, pairs=None): 22 | if pairs is None: 23 | pairs = {} 24 | 25 | if parents is None: 26 | parents = [] 27 | 28 | if isinstance(data, dict): 29 | for key, value in data.items(): 30 | RequestHandler.urlencode(value, parents + [key], pairs) 31 | elif isinstance(data, list): 32 | for key, value in enumerate(data): 33 | RequestHandler.urlencode(value, parents + [key], pairs) 34 | else: 35 | pairs[RequestHandler.render_key(parents)] = data 36 | 37 | return pairs 38 | 39 | @staticmethod 40 | def set_body(request): 41 | typ = request['request_type'] if 'request_type' in request else '{{or .Api.Request.Formats.Default "raw"}}' 42 | {{if .Api.Request.Formats.Json}} 43 | # Encoding request body into JSON format 44 | if typ == 'json': 45 | request['data'] = json.dumps(request['data']) 46 | request['headers']['content-type'] = 'application/json' 47 | {{end}}{{if .Api.Request.Formats.Form}} 48 | # Encoding body into form-urlencoded format 49 | if typ == 'form': 50 | request['data'] = RequestHandler.urlencode(request['data']) 51 | request['headers']['content-type'] = 'application/x-www-form-urlencoded' 52 | {{end}} 53 | if typ == 'raw': 54 | if 'content-type' in request['headers']: 55 | del request['headers']['content-type'] 56 | 57 | if 'request_type' in request: 58 | del request['request_type'] 59 | 60 | return request 61 | -------------------------------------------------------------------------------- /alpaca/library.go: -------------------------------------------------------------------------------- 1 | package alpaca 2 | 3 | import ( 4 | "os" 5 | "path" 6 | ) 7 | 8 | type PkgStruct struct { 9 | Name string 10 | Package string 11 | Version string 12 | Url string 13 | 14 | Keywords []string 15 | Official bool 16 | License string 17 | 18 | Author struct { 19 | Name string 20 | Email string 21 | Url string 22 | } 23 | 24 | Git struct { 25 | Site string 26 | User string 27 | Name string 28 | } 29 | 30 | Php struct { 31 | Vendor string 32 | } 33 | 34 | Python struct { 35 | License string 36 | } 37 | } 38 | 39 | type ApiParam struct { 40 | Name string 41 | Required bool 42 | UrlUse bool `json:"url_use"` 43 | } 44 | 45 | type ApiFunction struct { 46 | Name string 47 | Path string 48 | Method string 49 | 50 | Params []ApiParam 51 | } 52 | 53 | type ApiClass struct { 54 | Name string 55 | Args []string 56 | 57 | Functions []ApiFunction 58 | } 59 | 60 | type ApiStruct struct { 61 | Version string 62 | Base string 63 | 64 | BaseAsArg bool `json:"base_as_arg"` 65 | NoVerifySSL bool `json:"no_verify_ssl"` 66 | 67 | Authorization struct { 68 | Basic bool 69 | Oauth bool 70 | Header bool 71 | 72 | HeaderPrefix string `json:"header_prefix"` 73 | NeedAuth bool `json:"need_auth"` 74 | } 75 | 76 | Request struct { 77 | Formats struct { 78 | Default string 79 | 80 | Form bool 81 | Json bool 82 | } 83 | } 84 | 85 | Response struct { 86 | Suffix bool 87 | 88 | Formats struct { 89 | Default string 90 | 91 | Html bool 92 | Json bool 93 | } 94 | } 95 | 96 | Error struct { 97 | Message string 98 | } 99 | 100 | Classes []ApiClass 101 | } 102 | 103 | type DocParam struct { 104 | Desc string 105 | Value interface{} 106 | } 107 | 108 | type DocFunction struct { 109 | Title string 110 | Desc string 111 | 112 | Params map[string]DocParam 113 | } 114 | 115 | type DocClass struct { 116 | Title string 117 | Desc string 118 | 119 | Args map[string]DocParam 120 | Functions map[string]DocFunction 121 | } 122 | 123 | func MakeLibraryDir(name string) { 124 | name = path.Join(LibraryRoot, name) 125 | 126 | HandleError(os.RemoveAll(name)) 127 | MakeDir(name) 128 | } 129 | -------------------------------------------------------------------------------- /examples/helpful/api.json: -------------------------------------------------------------------------------- 1 | { 2 | "base": "https://helpful.io", 3 | "version": "api", 4 | "authorization": { 5 | "need_auth": true, 6 | "basic": true, 7 | "oauth": true 8 | }, 9 | "request": { 10 | "formats": { 11 | "default": "json", 12 | "json": true 13 | } 14 | }, 15 | "response": { 16 | "formats": { 17 | "default": "json", 18 | "json": true 19 | } 20 | }, 21 | "error": { 22 | "message": "error" 23 | }, 24 | "classes": [ 25 | { 26 | "name": "accounts", 27 | "functions": [ 28 | { 29 | "name": "all", 30 | "path": "/accounts" 31 | }, 32 | { 33 | "name": "get", 34 | "path": "/accounts/:account_id", 35 | "params": [ 36 | { 37 | "name": "account_id", 38 | "required": true, 39 | "url_use": true 40 | } 41 | ] 42 | }, 43 | { 44 | "name": "update", 45 | "path": "/accounts/:account_id", 46 | "method": "patch", 47 | "params": [ 48 | { 49 | "name": "account_id", 50 | "required": true, 51 | "url_use": true 52 | } 53 | ] 54 | } 55 | ] 56 | }, 57 | { 58 | "name": "people", 59 | "functions": [ 60 | { 61 | "name": "all", 62 | "path": "/accounts/:account_id/people", 63 | "params": [ 64 | { 65 | "name": "account_id", 66 | "required": true, 67 | "url_use": true 68 | } 69 | ] 70 | } 71 | ] 72 | }, 73 | { 74 | "name": "conversations", 75 | "functions": [ 76 | { 77 | "name": "all", 78 | "path": "/accounts/:account_id/conversations", 79 | "params": [ 80 | { 81 | "name": "account_id", 82 | "required": true, 83 | "url_use": true 84 | }, 85 | { 86 | "name": "archived" 87 | } 88 | ] 89 | }, 90 | { 91 | "name": "create", 92 | "path": "/accounts/:account_id/conversations", 93 | "method": "post", 94 | "params": [ 95 | { 96 | "name": "account_id", 97 | "required": true, 98 | "url_use": true 99 | } 100 | ] 101 | }, 102 | { 103 | "name": "get", 104 | "path": "/conversations/:conversation_id", 105 | "params": [ 106 | { 107 | "name": "conversation_id", 108 | "required": true, 109 | "url_use": true 110 | } 111 | ] 112 | } 113 | ] 114 | }, 115 | { 116 | "name": "messages", 117 | "functions": [ 118 | { 119 | "name": "get", 120 | "path": "/messages/:message_id", 121 | "params": [ 122 | { 123 | "name": "message_id", 124 | "required": true, 125 | "url_use": true 126 | } 127 | ] 128 | } 129 | ] 130 | } 131 | ] 132 | } 133 | -------------------------------------------------------------------------------- /alpaca/alpaca.go: -------------------------------------------------------------------------------- 1 | package alpaca 2 | 3 | import ( 4 | "bitbucket.org/pkg/inflect" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | ) 10 | 11 | const ( 12 | Version = "0.2.1" 13 | ) 14 | 15 | var ( 16 | LibraryRoot string 17 | FormatList []string 18 | ) 19 | 20 | type Data struct { 21 | Pkg PkgStruct 22 | Api ApiStruct 23 | Doc map[string]DocClass 24 | 25 | Fnc map[string]interface{} 26 | 27 | Version string /* Alpaca version to be used in user_agent */ 28 | Active *ApiClass /* Current class info needed to keep context */ 29 | } 30 | 31 | type LanguageOptions struct { 32 | Php bool `long:"no-php" description:"Do not write php library"` 33 | Python bool `long:"no-python" description:"Do not write python library"` 34 | Ruby bool `long:"no-ruby" description:"Do not write ruby library"` 35 | Node bool `long:"no-node" description:"Do not write node library"` 36 | } 37 | 38 | func LoadLibraryPath(directory string) { 39 | var err error 40 | 41 | LibraryRoot, err = filepath.Abs(directory) 42 | HandleError(err) 43 | } 44 | 45 | func ConvertFormat(format string) { 46 | acceptable := false 47 | 48 | FormatList = []string{} 49 | 50 | for _, v := range FormatList { 51 | if v == format { 52 | acceptable = true 53 | } 54 | } 55 | 56 | if !acceptable { 57 | fmt.Println("The given format is not allowed. Please choose one from the following:") 58 | fmt.Println() 59 | fmt.Println(strings.Join(FormatList, ", ") + "\n") 60 | os.Exit(0) 61 | } 62 | } 63 | 64 | func WriteLibraries(opts *LanguageOptions) { 65 | data := ReadData() 66 | ModifyData(data) 67 | 68 | if !opts.Php { 69 | HandleError(CheckPhp(data)) 70 | FunctionsPhp(data.Fnc) 71 | WritePhp(data) 72 | } 73 | 74 | if !opts.Python { 75 | HandleError(CheckPython(data)) 76 | FunctionsPython(data.Fnc) 77 | WritePython(data) 78 | } 79 | 80 | if !opts.Ruby { 81 | HandleError(CheckRuby(data)) 82 | FunctionsRuby(data.Fnc) 83 | WriteRuby(data) 84 | } 85 | 86 | if !opts.Node { 87 | HandleError(CheckNode(data)) 88 | FunctionsNode(data.Fnc) 89 | WriteNode(data) 90 | } 91 | } 92 | 93 | func ReadData() *Data { 94 | var pkg PkgStruct 95 | var api ApiStruct 96 | var doc map[string]DocClass 97 | 98 | ReadJSON("pkg.json", &pkg) 99 | ReadJSON("api.json", &api) 100 | ReadJSON("doc.json", &doc) 101 | 102 | return &Data{pkg, api, doc, make(map[string]interface{}), Version, nil} 103 | } 104 | 105 | func ModifyData(data *Data) { 106 | data.Fnc["join"] = strings.Join 107 | data.Fnc["upper"] = strings.ToUpper 108 | 109 | data.Fnc["camelize"] = inflect.Camelize 110 | data.Fnc["camelizeDownFirst"] = inflect.CamelizeDownFirst 111 | data.Fnc["underscore"] = inflect.Underscore 112 | 113 | data.Fnc["args"] = make(map[string]interface{}) 114 | data.Fnc["path"] = make(map[string]interface{}) 115 | data.Fnc["prnt"] = make(map[string]interface{}) 116 | } 117 | -------------------------------------------------------------------------------- /examples/helpful/doc.json: -------------------------------------------------------------------------------- 1 | { 2 | "accounts": { 3 | "title": "Accounts", 4 | "desc": "These are like organizations which use Helpful.", 5 | "functions": { 6 | "all": { 7 | "title": "List all accounts", 8 | "desc": "All the accounts the user has access to" 9 | }, 10 | "get": { 11 | "title": "Retrieve an account", 12 | "desc": "Get an account the user has access to", 13 | "params": { 14 | "account_id": { 15 | "desc": "Identifier of the account", 16 | "value": "b3ebe711-755e-4a91-b8d2-0cc028827bcf" 17 | } 18 | } 19 | }, 20 | "update": { 21 | "title": "Update an account", 22 | "desc": "Update an account the user has access to", 23 | "params": { 24 | "account_id": { 25 | "desc": "Identifier of the account", 26 | "value": "b3ebe711-755e-4a91-b8d2-0cc028827bcf" 27 | } 28 | } 29 | } 30 | } 31 | }, 32 | "people": { 33 | "title": "People", 34 | "desc": "People who operate or interacted with the account", 35 | "functions": { 36 | "all": { 37 | "title": "List all people", 38 | "desc": "List all people in the account the user has access to", 39 | "params": { 40 | "account_id": { 41 | "desc": "Identifier of the account", 42 | "value": "b3ebe711-755e-4a91-b8d2-0cc028827bcf" 43 | } 44 | } 45 | } 46 | } 47 | }, 48 | "conversations": { 49 | "title": "Conversations", 50 | "desc": "Conversations in an account", 51 | "functions": { 52 | "all": { 53 | "title": "List all conversations", 54 | "desc": "List all conversations in an account the user has access to", 55 | "params": { 56 | "account_id": { 57 | "desc": "Identifier of the account", 58 | "value": "b3ebe711-755e-4a91-b8d2-0cc028827bcf" 59 | }, 60 | "archived": { 61 | "desc": "Archived if true, not if false", 62 | "value": "false" 63 | } 64 | } 65 | }, 66 | "create": { 67 | "title": "Create a conversation", 68 | "desc": "Create an empty conversation in account the user has access to", 69 | "params": { 70 | "account_id": { 71 | "desc": "Identifier of the account", 72 | "value": "b3ebe711-755e-4a91-b8d2-0cc028827bcf" 73 | } 74 | } 75 | }, 76 | "get": { 77 | "title": "Retrieve a conversation", 78 | "desc": "Get a conversation the user has access to", 79 | "params": { 80 | "conversation_id": { 81 | "desc": "Identifier of the conversation", 82 | "value": "10ce24de-23f6-433f-9a71-255cffffa96f" 83 | } 84 | } 85 | } 86 | } 87 | }, 88 | "messages": { 89 | "title": "Messages", 90 | "desc": "Messages in a conversation", 91 | "functions": { 92 | "get": { 93 | "title": "Retrieve a message", 94 | "desc": "Get a message the user has access to", 95 | "params": { 96 | "message_id": { 97 | "desc": "Identifier of the message", 98 | "value": "33314e4e-baf5-4b33-be72-112ed86701fd" 99 | } 100 | } 101 | } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /templates/python/lib/http_client/auth_handler.py: -------------------------------------------------------------------------------- 1 | class AuthHandler(object): 2 | 3 | """AuthHandler takes care of devising the auth type and using it""" 4 | {{if .Api.Authorization.Basic}} 5 | HTTP_PASSWORD = 0 6 | {{end}}{{if .Api.Authorization.Header}} 7 | HTTP_HEADER = 1 8 | {{end}}{{if .Api.Authorization.Oauth}} 9 | URL_SECRET = 2 10 | URL_TOKEN = 3 11 | {{end}} 12 | def __init__(self, auth): 13 | self.auth = auth 14 | 15 | def get_auth_type(self): 16 | """Calculating the Authentication Type""" 17 | {{if .Api.Authorization.Basic}} 18 | if 'username' in self.auth and 'password' in self.auth: 19 | return self.HTTP_PASSWORD 20 | {{end}}{{if .Api.Authorization.Header}} 21 | if 'http_header' in self.auth: 22 | return self.HTTP_HEADER 23 | {{end}}{{if .Api.Authorization.Oauth}} 24 | if 'client_id' in self.auth and 'client_secret' in self.auth: 25 | return self.URL_SECRET 26 | 27 | if 'access_token' in self.auth: 28 | return self.URL_TOKEN 29 | {{end}} 30 | return -1 31 | 32 | def set(self, request): 33 | if len(self.auth.keys()) == 0:{{if .Api.Authorization.NeedAuth}} 34 | raise StandardError("Server requires authentication to proceed further. Please check"){{else}} 35 | return request{{end}} 36 | 37 | auth = self.get_auth_type() 38 | flag = False 39 | {{if .Api.Authorization.Basic}} 40 | if auth == self.HTTP_PASSWORD: 41 | request = self.http_password(request) 42 | flag = True 43 | {{end}}{{if .Api.Authorization.Header}} 44 | if auth == self.HTTP_HEADER: 45 | request = self.http_header(request) 46 | flag = True 47 | {{end}}{{if .Api.Authorization.Oauth}} 48 | if auth == self.URL_SECRET: 49 | request = self.url_secret(request) 50 | flag = True 51 | 52 | if auth == self.URL_TOKEN: 53 | request = self.url_token(request) 54 | flag = True 55 | {{end}} 56 | if not flag: 57 | raise StandardError("Unable to calculate authorization method. Please check") 58 | 59 | return request 60 | {{if .Api.Authorization.Basic}} 61 | def http_password(self, request): 62 | """Basic Authorization with username and password""" 63 | request['auth'] = (self.auth['username'], self.auth['password']) 64 | return request 65 | {{end}}{{if .Api.Authorization.Header}} 66 | def http_header(self, request): 67 | """Authorization with HTTP header""" 68 | request['headers']['Authorization'] = '{{or .Api.Authorization.HeaderPrefix "token"}} ' + self.auth['http_header'] 69 | return request 70 | {{end}}{{if .Api.Authorization.Oauth}} 71 | def url_secret(self, request): 72 | """OAUTH2 Authorization with client secret""" 73 | request['params']['client_id'] = self.auth['client_id'] 74 | request['params']['client_secret'] = self.auth['client_secret'] 75 | return request 76 | 77 | def url_token(self, request): 78 | """OAUTH2 Authorization with access token""" 79 | request['params']['access_token'] = self.auth['access_token'] 80 | return request 81 | {{end}} 82 | -------------------------------------------------------------------------------- /templates/node/lib/http_client/auth_handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module takes care of devising the auth type and using it 3 | */ 4 | var Auth = function(auth) { 5 | this.auth = auth; 6 | {{if .Api.Authorization.Basic}} 7 | this.HTTP_PASSWORD = 0; 8 | {{end}}{{if .Api.Authorization.Header}} 9 | this.HTTP_HEADER = 1; 10 | {{end}}{{if .Api.Authorization.Oauth}} 11 | this.URL_SECRET = 2; 12 | this.URL_TOKEN = 3; 13 | {{end}} 14 | return this; 15 | }; 16 | 17 | /** 18 | * Calculating the type of authentication 19 | */ 20 | Auth.prototype.getAuthType = function () { 21 | {{if .Api.Authorization.Basic}} 22 | if (this.auth.username && this.auth.password) { 23 | return this.HTTP_PASSWORD; 24 | } 25 | {{end}}{{if .Api.Authorization.Header}} 26 | if (this.auth.http_header) { 27 | return this.HTTP_HEADER; 28 | } 29 | {{end}}{{if .Api.Authorization.Oauth}} 30 | if (this.auth.client_id && this.auth.client_secret) { 31 | return this.URL_SECRET; 32 | } 33 | 34 | if (this.auth.access_token) { 35 | return this.URL_TOKEN; 36 | } 37 | {{end}} 38 | return -1; 39 | }; 40 | 41 | /** 42 | * Set authentication for the request 43 | * 44 | * This should throw error because this should be caught while in development 45 | */ 46 | Auth.prototype.set = function (request, callback) { 47 | if (Object.keys(this.auth).length === 0) { 48 | return callback({{if .Api.Authorization.NeedAuth}}new Error('Server requires authentication to proceed further. Please check')); 49 | {{else}}null, request); 50 | {{end}}} 51 | 52 | var auth = this.getAuthType(), flag = false; 53 | {{if .Api.Authorization.Basic}} 54 | if (auth === this.HTTP_PASSWORD) { 55 | request = this.httpPassword(request); 56 | flag = true; 57 | } 58 | {{end}}{{if .Api.Authorization.Header}} 59 | if (auth == this.HTTP_HEADER) { 60 | request = this.httpHeader(request); 61 | flag = true; 62 | } 63 | {{end}}{{if .Api.Authorization.Oauth}} 64 | if (auth == this.URL_SECRET) { 65 | request = this.urlSecret(request); 66 | flag = true; 67 | } 68 | 69 | if (auth == this.URL_TOKEN) { 70 | request = this.urlToken(request); 71 | flag = true; 72 | } 73 | {{end}} 74 | if (!flag) { 75 | return callback(new Error('Unable to calculate authorization method. Please check.')); 76 | } 77 | 78 | callback(null, request); 79 | }; 80 | {{if .Api.Authorization.Basic}} 81 | /** 82 | * Basic Authorization with username and password 83 | */ 84 | Auth.prototype.httpPassword = function(request) { 85 | request.auth = this.auth; 86 | 87 | return request; 88 | }; 89 | {{end}}{{if .Api.Authorization.Header}} 90 | /** 91 | * Authorization with HTTP header 92 | */ 93 | Auth.prototype.httpHeader = function(request) { 94 | request['headers']['Authorization'] = '{{or .Api.Authorization.HeaderPrefix "token"}} ' + this.auth['http_header']; 95 | 96 | return request; 97 | }; 98 | {{end}}{{if .Api.Authorization.Oauth}} 99 | /** 100 | * OAUTH2 Authorization with client secret 101 | */ 102 | Auth.prototype.urlSecret = function(request) { 103 | request['qs']['client_id'] = this.auth['client_id']; 104 | request['qs']['client_secret'] = this.auth['client_secret']; 105 | 106 | return request; 107 | }; 108 | 109 | /** 110 | * OAUTH2 Authorization with access token 111 | */ 112 | Auth.prototype.urlToken = function(request) { 113 | request['qs']['access_token'] = this.auth['access_token']; 114 | 115 | return request; 116 | }; 117 | {{end}} 118 | // Export module 119 | module.exports = Auth; 120 | -------------------------------------------------------------------------------- /examples/buffer/api.json: -------------------------------------------------------------------------------- 1 | { 2 | "base": "https://api.bufferapp.com", 3 | "version": "1", 4 | "authorization": { 5 | "need_auth": true, 6 | "oauth": true 7 | }, 8 | "request": { 9 | "formats": { 10 | "default": "form", 11 | "form": true 12 | } 13 | }, 14 | "response": { 15 | "formats": { 16 | "default": "json", 17 | "json": true 18 | }, 19 | "suffix": true 20 | }, 21 | "error": { 22 | "message": "error" 23 | }, 24 | "classes": [ 25 | { 26 | "name": "info", 27 | "functions": [ 28 | { 29 | "name": "show", 30 | "path": "/info/configuration" 31 | } 32 | ] 33 | }, 34 | { 35 | "name": "user", 36 | "functions": [ 37 | { 38 | "name": "show", 39 | "path": "/user" 40 | }, 41 | { 42 | "name": "profiles", 43 | "path": "/profiles" 44 | }, 45 | { 46 | "name": "create_update", 47 | "path": "/updates/create", 48 | "method": "post", 49 | "params": [ 50 | { 51 | "name": "text", 52 | "required": true 53 | }, 54 | { 55 | "name": "profile_ids", 56 | "required": true 57 | } 58 | ] 59 | } 60 | ] 61 | }, 62 | { 63 | "name": "link", 64 | "functions": [ 65 | { 66 | "name": "shares", 67 | "path": "/link/shares", 68 | "params": [ 69 | { 70 | "name": "url", 71 | "required": true 72 | } 73 | ] 74 | } 75 | ] 76 | }, 77 | { 78 | "name": "profile", 79 | "args": ["id"], 80 | "functions": [ 81 | { 82 | "name": "show", 83 | "path": "/profiles/:id" 84 | }, 85 | { 86 | "name": "pending", 87 | "path": "/profiles/:id/updates/pending", 88 | "paginate": true 89 | }, 90 | { 91 | "name": "sent", 92 | "path": "/profiles/:id/updates/sent", 93 | "paginate": true 94 | }, 95 | { 96 | "name": "reorder", 97 | "path": "/profiles/:id/updates/reorder", 98 | "method": "post", 99 | "params": [ 100 | { 101 | "name": "order", 102 | "required": true 103 | } 104 | ] 105 | }, 106 | { 107 | "name": "shuffle", 108 | "path": "/profiles/:id/updates/shuffle", 109 | "method": "post" 110 | } 111 | ] 112 | }, 113 | { 114 | "name": "schedule", 115 | "args": ["id"], 116 | "functions": [ 117 | { 118 | "name": "list", 119 | "path": "/profiles/:id/schedules" 120 | }, 121 | { 122 | "name": "update", 123 | "path": "/profiles/:id/schedules/update", 124 | "method": "post", 125 | "params": [ 126 | { 127 | "name": "schedules", 128 | "required": true 129 | } 130 | ] 131 | } 132 | ] 133 | }, 134 | { 135 | "name": "update", 136 | "args": ["id"], 137 | "functions": [ 138 | { 139 | "name": "show", 140 | "path": "/updates/:id" 141 | }, 142 | { 143 | "name": "interactions", 144 | "path": "/updates/:id/interactions", 145 | "paginate": true 146 | }, 147 | { 148 | "name": "update", 149 | "path": "/updates/:id/update", 150 | "method": "post", 151 | "params": [ 152 | { 153 | "name": "text", 154 | "required": true 155 | } 156 | ] 157 | }, 158 | { 159 | "name": "share", 160 | "path": "/updates/:id/share", 161 | "method": "post" 162 | }, 163 | { 164 | "name": "destroy", 165 | "path": "/updates/:id/destroy", 166 | "method": "post" 167 | }, 168 | { 169 | "name": "top", 170 | "path": "/updates/:id/move_to_top", 171 | "method": "post" 172 | } 173 | ] 174 | } 175 | ] 176 | } 177 | -------------------------------------------------------------------------------- /alpaca/utils_test.go: -------------------------------------------------------------------------------- 1 | package alpaca 2 | 3 | import ( 4 | "github.com/robertkrimen/terst" 5 | "testing" 6 | ) 7 | 8 | func TestArgsFunctionMaker(t *testing.T) { 9 | terst.Terst(t) 10 | 11 | f := ArgsFunctionMaker("$", ", ").(func(interface{}, ...bool) string) 12 | 13 | args := []string{"id", "url"} 14 | params := make([]ApiParam, 2) 15 | 16 | nulArgs := []string{} 17 | nulParams := make([]ApiParam, 0) 18 | 19 | params[0] = ApiParam{"id", true, false} 20 | params[1] = ApiParam{"url", false, false} 21 | 22 | terst.Is(f(args), "$id, $url, ") 23 | terst.Is(f(args, false), "$id, $url, ") 24 | terst.Is(f(args, true), "$id, $url") 25 | terst.Is(f(args, true, false), "$id, $url") 26 | terst.Is(f(args, true, true), ", $id, $url") 27 | terst.Is(f(args, false, true), ", $id, $url, ") 28 | 29 | terst.Is(f(params), "$id, ") 30 | terst.Is(f(params, true), "$id") 31 | terst.Is(f(params, false, true), ", $id, ") 32 | 33 | terst.Is(f(nulArgs), "") 34 | terst.Is(f(nulArgs, true), "") 35 | terst.Is(f(nulArgs, false, true), "") 36 | 37 | terst.Is(f(nulParams), "") 38 | terst.Is(f(nulParams, true), "") 39 | terst.Is(f(nulParams, false, true), "") 40 | } 41 | 42 | func TestPathFunctionMaker(t *testing.T) { 43 | terst.Terst(t) 44 | 45 | f := PathFunctionMaker("\"+", "@", "+\"").(func(string, []string, []ApiParam) string) 46 | 47 | cargs := []string{"id", "url"} 48 | margs := make([]ApiParam, 1) 49 | 50 | margs[0] = ApiParam{"one", false, false} 51 | 52 | terst.Is(f("/user/:id/:not/:url/wow:one", cargs, margs), "/user/\"+@id+\"/:not/\"+@url+\"/wow\"+one+\"") 53 | } 54 | 55 | func TestPrntFunctionMaker(t *testing.T) { 56 | terst.Terst(t) 57 | 58 | f := PrntFunctionMaker(true, " ", "'", "'", "[", "]", "{", "}", ":", " => ").(func(interface{}, map[string]DocParam, string, bool) string) 59 | 60 | apis := []string{"id"} 61 | apip := make([]ApiParam, 2) 62 | docs := make(map[string]DocParam) 63 | vals := make(map[string]interface{}) 64 | orgs := make([]interface{}, 3) 65 | null := make(map[string]string) 66 | 67 | orgs[0] = false 68 | orgs[1] = "alpaca-api" 69 | orgs[2] = 00 70 | 71 | apip[0] = ApiParam{"id", true, false} 72 | apip[1] = ApiParam{"flag", false, false} 73 | 74 | docs["flag"] = DocParam{"", false} 75 | 76 | terst.Is(f([]string{}, make(map[string]DocParam), ", ", true), "") 77 | terst.Is(f([]ApiParam{}, make(map[string]DocParam), ", ", true), "") 78 | terst.Is(f([]int{}, make(map[string]DocParam), ", ", true), "") 79 | 80 | vals["key"] = 3737 81 | docs["id"] = DocParam{"", vals} 82 | terst.Is(f(apis, docs, ", ", true), "{\n :key => 3737\n}, ") 83 | terst.Is(f(apip, docs, ", ", true), "{\n :key => 3737\n}, ") 84 | 85 | vals["key"] = 1.99 86 | docs["id"] = DocParam{"", vals} 87 | terst.Is(f(apis, docs, ", ", false), "{\n :key => 1.99\n}") 88 | terst.Is(f(apip, docs, ", ", false), "{\n :key => 1.99\n}") 89 | 90 | vals["key"] = "pksunkara" 91 | docs["id"] = DocParam{"", vals} 92 | terst.Is(f(apis, docs, ", ", false), "{\n :key => 'pksunkara'\n}") 93 | terst.Is(f(apip, docs, ", ", false), "{\n :key => 'pksunkara'\n}") 94 | 95 | vals["key"] = true 96 | docs["id"] = DocParam{"", vals} 97 | terst.Is(f(apis, docs, ", ", false), "{\n :key => True\n}") 98 | terst.Is(f(apip, docs, ", ", false), "{\n :key => True\n}") 99 | 100 | vals["key"] = orgs 101 | docs["id"] = DocParam{"", vals} 102 | terst.Is(f(apis, docs, ", ", false), "{\n :key => [\n False,\n 'alpaca-api',\n 0\n ]\n}") 103 | terst.Is(f(apip, docs, ", ", false), "{\n :key => [\n False,\n 'alpaca-api',\n 0\n ]\n}") 104 | 105 | vals["key"] = null 106 | docs["id"] = DocParam{"", vals} 107 | terst.Is(f(apis, docs, ", ", false), "{\n :key => \n}") 108 | terst.Is(f(apip, docs, ", ", false), "{\n :key => \n}") 109 | } 110 | -------------------------------------------------------------------------------- /templates/php/lib/HttpClient/AuthHandler.php: -------------------------------------------------------------------------------- 1 | auth = $auth; 24 | } 25 | 26 | /** 27 | * Calculating the Authentication Type 28 | */ 29 | public function getAuthType() 30 | { 31 | {{if .Api.Authorization.Basic}} 32 | if (isset($this->auth['username']) && isset($this->auth['password'])) { 33 | return self::HTTP_PASSWORD; 34 | } 35 | {{end}}{{if .Api.Authorization.Header}} 36 | if (isset($this->auth['http_header'])) { 37 | return self::HTTP_HEADER; 38 | } 39 | {{end}}{{if .Api.Authorization.Oauth}} 40 | if (isset($this->auth['client_id']) && isset($this->auth['client_secret'])) { 41 | return self::URL_SECRET; 42 | } 43 | 44 | if (isset($this->auth['access_token'])) { 45 | return self::URL_TOKEN; 46 | } 47 | {{end}} 48 | return -1; 49 | } 50 | 51 | public function onRequestBeforeSend(Event $event) 52 | { 53 | if (empty($this->auth)) { 54 | {{if .Api.Authorization.NeedAuth}} throw new \ErrorException('Server requires authentication to proceed further. Please check'); 55 | {{else}} return; 56 | {{end}}} 57 | 58 | $auth = $this->getAuthType(); 59 | $flag = false; 60 | {{if .Api.Authorization.Basic}} 61 | if ($auth == self::HTTP_PASSWORD) { 62 | $this->httpPassword($event); 63 | $flag = true; 64 | } 65 | {{end}}{{if .Api.Authorization.Header}} 66 | if ($auth == self::HTTP_HEADER) { 67 | $this->httpHeader($event); 68 | $flag = true; 69 | } 70 | {{end}}{{if .Api.Authorization.Oauth}} 71 | if ($auth == self::URL_SECRET) { 72 | $this->urlSecret($event); 73 | $flag = true; 74 | } 75 | 76 | if ($auth == self::URL_TOKEN) { 77 | $this->urlToken($event); 78 | $flag = true; 79 | } 80 | {{end}} 81 | if (!$flag) { 82 | throw new \ErrorException('Unable to calculate authorization method. Please check.'); 83 | } 84 | } 85 | {{if .Api.Authorization.Basic}} 86 | /** 87 | * Basic Authorization with username and password 88 | */ 89 | public function httpPassword(Event $event) 90 | { 91 | $event['request']->setHeader('Authorization', sprintf('Basic %s', base64_encode($this->auth['username'] . ':' . $this->auth['password']))); 92 | } 93 | {{end}}{{if .Api.Authorization.Header}} 94 | /** 95 | * Authorization with HTTP header 96 | */ 97 | public function httpHeader(Event $event) 98 | { 99 | $event['request']->setHeader('Authorization', sprintf('{{or .Api.Authorization.HeaderPrefix "token"}} %s', $this->auth['http_header'])); 100 | } 101 | {{end}}{{if .Api.Authorization.Oauth}} 102 | /** 103 | * OAUTH2 Authorization with client secret 104 | */ 105 | public function urlSecret(Event $event) 106 | { 107 | $query = $event['request']->getQuery(); 108 | 109 | $query->set('client_id', $this->auth['client_id']); 110 | $query->set('client_secret', $this->auth['client_secret']); 111 | } 112 | 113 | /** 114 | * OAUTH2 Authorization with access token 115 | */ 116 | public function urlToken(Event $event) 117 | { 118 | $query = $event['request']->getQuery(); 119 | 120 | $query->set('access_token', $this->auth['access_token']); 121 | } 122 | {{end}} 123 | } 124 | -------------------------------------------------------------------------------- /templates/ruby/lib/http_client/auth_handler.rb: -------------------------------------------------------------------------------- 1 | require "base64" 2 | 3 | module {{call .Fnc.camelize .Pkg.Name}} 4 | 5 | module HttpClient 6 | 7 | # AuthHandler takes care of devising the auth type and using it 8 | class AuthHandler < Faraday::Middleware 9 | {{if .Api.Authorization.Basic}} 10 | HTTP_PASSWORD = 0 11 | {{end}}{{if .Api.Authorization.Header}} 12 | HTTP_HEADER = 1 13 | {{end}}{{if .Api.Authorization.Oauth}} 14 | URL_SECRET = 2 15 | URL_TOKEN = 3 16 | {{end}} 17 | def initialize(app, auth = {}, options = {}) 18 | @auth = auth 19 | super(app) 20 | end 21 | 22 | def call(env) 23 | if !@auth.empty? 24 | auth = get_auth_type 25 | flag = false 26 | {{if .Api.Authorization.Basic}} 27 | if auth == HTTP_PASSWORD 28 | env = http_password(env) 29 | flag = true 30 | end 31 | {{end}}{{if .Api.Authorization.Header}} 32 | if auth == HTTP_HEADER 33 | env = http_header(env) 34 | flag = true 35 | end 36 | {{end}}{{if .Api.Authorization.Oauth}} 37 | if auth == URL_SECRET 38 | env = url_secret(env) 39 | flag = true 40 | end 41 | 42 | if auth == URL_TOKEN 43 | env = url_token(env) 44 | flag = true 45 | end 46 | {{end}} 47 | if !flag 48 | raise StandardError.new "Unable to calculate authorization method. Please check" 49 | end{{if .Api.Authorization.NeedAuth}} 50 | else 51 | raise StandardError.new "Server requires authentication to proceed further. Please check"{{end}} 52 | end 53 | 54 | @app.call(env) 55 | end 56 | 57 | # Calculating the Authentication Type 58 | def get_auth_type() 59 | {{if .Api.Authorization.Basic}} 60 | if @auth.has_key?(:username) and @auth.has_key?(:password) 61 | return HTTP_PASSWORD 62 | end 63 | {{end}}{{if .Api.Authorization.Header}} 64 | if @auth.has_key?(:http_header) 65 | return HTTP_HEADER 66 | end 67 | {{end}}{{if .Api.Authorization.Oauth}} 68 | if @auth.has_key?(:client_id) and @auth.has_key?(:client_secret) 69 | return URL_SECRET 70 | end 71 | 72 | if @auth.has_key?(:access_token) 73 | return URL_TOKEN 74 | end 75 | {{end}} 76 | return -1 77 | end 78 | {{if .Api.Authorization.Basic}} 79 | # Basic Authorization with username and password 80 | def http_password(env) 81 | code = Base64.strict_encode64 "#{@auth[:username]}:#{@auth[:password]}" 82 | 83 | env[:request_headers]["Authorization"] = "Basic #{code}" 84 | 85 | return env 86 | end 87 | {{end}}{{if .Api.Authorization.Header}} 88 | # Authorization with HTTP header 89 | def http_header(env) 90 | env[:request_headers]["Authorization"] = "{{or .Api.Authorization.HeaderPrefix "token"}} #{@auth[:http_header]}" 91 | 92 | return env 93 | end 94 | {{end}}{{if .Api.Authorization.Oauth}} 95 | # OAUTH2 Authorization with client secret 96 | def url_secret(env) 97 | query = { 98 | :client_id => @auth[:client_id], 99 | :client_secret => @auth[:client_secret] 100 | } 101 | 102 | merge_query(env, query) 103 | end 104 | 105 | # OAUTH2 Authorization with access token 106 | def url_token(env) 107 | query = { :access_token => @auth[:access_token] } 108 | 109 | merge_query(env, query) 110 | end 111 | {{end}} 112 | def query_params(url) 113 | if url.query.nil? or url.query.empty? 114 | {} 115 | else 116 | Faraday::Utils.parse_query(url.query) 117 | end 118 | end 119 | 120 | def merge_query(env, query) 121 | query = query.update query_params(env[:url]) 122 | 123 | env[:url].query = Faraday::Utils.build_query(query) 124 | 125 | return env 126 | end 127 | end 128 | 129 | end 130 | 131 | end 132 | -------------------------------------------------------------------------------- /alpaca/utils.go: -------------------------------------------------------------------------------- 1 | package alpaca 2 | 3 | import ( 4 | "bitbucket.org/pkg/inflect" 5 | "encoding/json" 6 | "os" 7 | "path" 8 | "reflect" 9 | "regexp" 10 | "strconv" 11 | ) 12 | 13 | func ReadJSON(name string, v interface{}) { 14 | file, err := os.Open(path.Join(LibraryRoot, name)) 15 | HandleError(err) 16 | 17 | HandleError(json.NewDecoder(file).Decode(v)) 18 | } 19 | 20 | func MakeDir(name string) { 21 | HandleError(os.Mkdir(name, 0755)) 22 | MoveDir(name) 23 | } 24 | 25 | func MoveDir(name string) { 26 | HandleError(os.Chdir(name)) 27 | } 28 | 29 | func ArgsFunctionMaker(before, after string) interface{} { 30 | return func(args interface{}, options ...bool) string { 31 | str := "" 32 | 33 | if reflect.TypeOf(args).String() == "[]string" { 34 | for _, v := range args.([]string) { 35 | str += before + v + after 36 | } 37 | } else { 38 | for _, v := range args.([]ApiParam) { 39 | if v.Required { 40 | str += before + v.Name + after 41 | } 42 | } 43 | } 44 | 45 | if str != "" { 46 | if len(options) > 0 && options[0] { 47 | str = str[0 : len(str)-len(after)] 48 | } 49 | 50 | if len(options) > 1 && options[1] { 51 | str = after + str 52 | } 53 | } 54 | 55 | return str 56 | } 57 | } 58 | 59 | func PathFunctionMaker(before, this, after string) interface{} { 60 | return func(path string, cargs []string, margs []ApiParam) string { 61 | for _, v := range cargs { 62 | reg := regexp.MustCompile(":(" + v + ")") 63 | path = reg.ReplaceAllString(path, before+this+"$1"+after) 64 | } 65 | 66 | for _, v := range margs { 67 | reg := regexp.MustCompile(":(" + v.Name + ")") 68 | path = reg.ReplaceAllString(path, before+"$1"+after) 69 | } 70 | 71 | return path 72 | } 73 | } 74 | 75 | func PrntFunctionMaker(boolcap bool, tab, strbeg, strend, arrbeg, arrend, objbeg, objend, keybeg, keyend string) interface{} { 76 | var vals func(interface{}, ...int) string 77 | var tabs func(int) string 78 | 79 | arrmid, objmid, newline := ",", ",", "\n" 80 | 81 | tabs = func(level int) string { 82 | str := "" 83 | 84 | for i := 0; i < level; i++ { 85 | str += tab 86 | } 87 | 88 | return str 89 | } 90 | 91 | vals = func(data interface{}, level ...int) string { 92 | typ, lev := reflect.TypeOf(data).String(), 1 93 | 94 | if len(level) == 1 { 95 | lev = level[0] 96 | } 97 | 98 | if typ == "bool" { 99 | str := strconv.FormatBool(data.(bool)) 100 | 101 | if boolcap { 102 | str = inflect.Capitalize(str) 103 | } 104 | 105 | return str 106 | } 107 | 108 | if typ == "string" { 109 | return strbeg + data.(string) + strend 110 | } 111 | 112 | if typ == "int" { 113 | return strconv.Itoa(data.(int)) 114 | } 115 | 116 | if typ == "float64" { 117 | return strconv.FormatFloat(data.(float64), 'f', -1, 64) 118 | } 119 | 120 | if typ == "[]interface {}" { 121 | str := arrbeg 122 | 123 | for _, v := range data.([]interface{}) { 124 | str += newline + tabs(lev) + vals(v, lev+1) + arrmid 125 | } 126 | 127 | return str[0:len(str)-len(arrmid)] + newline + tabs(lev-1) + arrend 128 | } 129 | 130 | if typ == "map[string]interface {}" { 131 | str := objbeg 132 | 133 | for k, v := range data.(map[string]interface{}) { 134 | str += newline + tabs(lev) + keybeg + k + keyend + vals(v, lev+1) + objmid 135 | } 136 | 137 | return str[0:len(str)-len(objmid)] + newline + tabs(lev-1) + objend 138 | } 139 | 140 | return "" 141 | } 142 | 143 | return func(api interface{}, doc map[string]DocParam, sep string, notLast bool) string { 144 | str, typ := "", reflect.TypeOf(api).String() 145 | 146 | if typ == "[]string" { 147 | for _, v := range api.([]string) { 148 | str += vals(doc[v].Value) + sep 149 | } 150 | } else if typ == "[]alpaca.ApiParam" { 151 | for _, v := range api.([]ApiParam) { 152 | if v.Required { 153 | str += vals(doc[v.Name].Value) + sep 154 | } 155 | } 156 | } else { 157 | return str 158 | } 159 | 160 | if str != "" && !notLast { 161 | return str[0 : len(str)-len(sep)] 162 | } 163 | 164 | return str 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /templates/ruby/lib/http_client.rb: -------------------------------------------------------------------------------- 1 | require "{{call .Fnc.underscore .Pkg.Name}}/http_client/auth_handler" 2 | require "{{call .Fnc.underscore .Pkg.Name}}/http_client/error_handler" 3 | require "{{call .Fnc.underscore .Pkg.Name}}/http_client/request_handler" 4 | require "{{call .Fnc.underscore .Pkg.Name}}/http_client/response" 5 | require "{{call .Fnc.underscore .Pkg.Name}}/http_client/response_handler" 6 | 7 | module {{call .Fnc.camelize .Pkg.Name}} 8 | 9 | module HttpClient 10 | 11 | # Main HttpClient which is used by Api classes 12 | class HttpClient 13 | 14 | attr_accessor :options, :headers 15 | 16 | def initialize({{if .Api.BaseAsArg}}base_url, {{end}}auth = {}, options = {}) 17 | {{if .Api.Authorization.Oauth}} 18 | if auth.is_a?(String) 19 | auth = { :access_token => auth } 20 | end 21 | {{else}}{{if .Api.Authorization.Header}} 22 | if auth.is_a?(String) 23 | auth = { :http_header => auth } 24 | end 25 | {{end}}{{end}} 26 | @options = { 27 | :base => {{if .Api.BaseAsArg}}base_url{{else}}"{{.Api.Base}}"{{end}},{{with .Api.Version}} 28 | :api_version => "{{.}}",{{end}} 29 | :user_agent => "alpaca/{{.Version}} (https://github.com/pksunkara/alpaca)" 30 | } 31 | 32 | @options.update(options) 33 | 34 | @headers = { 35 | "user-agent" => @options[:user_agent] 36 | } 37 | 38 | if @options.has_key?(:headers) 39 | @headers.update(Hash[@options[:headers].map { |k, v| [k.downcase, v] }]) 40 | @options.delete(:headers) 41 | end 42 | {{if .Api.NoVerifySSL}} 43 | @conn_options = { 44 | :ssl => { :verify => false } 45 | } 46 | 47 | @client = Faraday.new(@options[:base], @conn_options) do |conn| 48 | {{else}} 49 | @client = Faraday.new(@options[:base]) do |conn| 50 | {{end}} conn.use({{call .Fnc.camelize .Pkg.Name}}::HttpClient::AuthHandler, auth) 51 | conn.use({{call .Fnc.camelize .Pkg.Name}}::HttpClient::ErrorHandler) 52 | 53 | conn.adapter(Faraday.default_adapter) 54 | end 55 | end 56 | 57 | def get(path, params = {}, options = {}) 58 | request(path, nil, "get", options.merge({ :query => params })) 59 | end 60 | 61 | def post(path, body = {}, options = {}) 62 | request(path, body, "post", options) 63 | end 64 | 65 | def patch(path, body = {}, options = {}) 66 | request(path, body, "patch", options) 67 | end 68 | 69 | def delete(path, body = {}, options = {}) 70 | request(path, body, "delete", options) 71 | end 72 | 73 | def put(path, body = {}, options = {}) 74 | request(path, body, "put", options) 75 | end 76 | 77 | # Intermediate function which does three main things 78 | # 79 | # - Transforms the body of request into correct format 80 | # - Creates the requests with give parameters 81 | # - Returns response body after parsing it into correct format 82 | def request(path, body, method, options) 83 | options = @options.merge(options) 84 | 85 | options[:headers] = options[:headers] || {} 86 | options[:headers] = @headers.merge(Hash[options[:headers].map { |k, v| [k.downcase, v] }]) 87 | 88 | options[:body] = body 89 | 90 | if method != "get" 91 | options[:body] = options[:body] || {} 92 | options = set_body(options) 93 | end 94 | 95 | response = create_request(method, path, options) 96 | 97 | body = get_body(response) 98 | 99 | {{call .Fnc.camelize .Pkg.Name}}::HttpClient::Response.new(body, response.status, response.headers) 100 | end 101 | 102 | # Creating a request with the given arguments 103 | # 104 | # If api_version is set, appends it immediately after host 105 | def create_request(method, path, options) 106 | version = options.has_key?(:api_version) ? "/#{options[:api_version]}" : "" 107 | {{if .Api.Response.Suffix}} 108 | # Adds a suffix (ex: ".html", ".json") to url 109 | suffix = options.has_key?(:response_type) ? options[:response_type] : "{{.Api.Response.Formats.Default}}" 110 | path = "#{path}.#{suffix}" 111 | {{end}} 112 | path = "#{version}#{path}" 113 | 114 | instance_eval <<-RUBY, __FILE__, __LINE__ + 1 115 | @client.#{method}(path) do |req| 116 | req.body = options[:body] 117 | req.headers.update(options[:headers]) 118 | req.params.update(options[:query]) if options[:query] 119 | end 120 | RUBY 121 | end 122 | 123 | # Get response body in correct format 124 | def get_body(response) 125 | {{call .Fnc.camelize .Pkg.Name}}::HttpClient::ResponseHandler.get_body(response) 126 | end 127 | 128 | # Set request body in correct format 129 | def set_body(options) 130 | {{call .Fnc.camelize .Pkg.Name}}::HttpClient::RequestHandler.set_body(options) 131 | end 132 | 133 | end 134 | 135 | end 136 | 137 | end 138 | -------------------------------------------------------------------------------- /templates/python/lib/http_client/__init__.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import copy 3 | 4 | try: 5 | import urlparse 6 | except ImportError: 7 | import urllib.parse as urlparse 8 | 9 | from .auth_handler import AuthHandler 10 | from .error_handler import ErrorHandler 11 | from .request_handler import RequestHandler 12 | from .response import Response 13 | from .response_handler import ResponseHandler 14 | 15 | 16 | class HttpClient(object): 17 | 18 | """Main HttpClient which is used by API classes""" 19 | 20 | def __init__(self, {{if .Api.BaseAsArg}}base_url, {{end}}auth, options): 21 | {{if .Api.Authorization.Oauth}} 22 | if isinstance(auth, str): 23 | auth = {'access_token': auth} 24 | {{else}}{{if .Api.Authorization.Header}} 25 | if isinstance(auth, str): 26 | auth = {'http_header': auth} 27 | {{end}}{{end}} 28 | self.options = { 29 | 'base': {{if .Api.BaseAsArg}}base_url{{else}}'{{.Api.Base}}'{{end}},{{with .Api.Version}} 30 | 'api_version': '{{.}}',{{end}} 31 | 'user_agent': 'alpaca/{{.Version}} (https://github.com/pksunkara/alpaca)' 32 | } 33 | 34 | self.options.update(options) 35 | 36 | self.base = self.options['base'] 37 | 38 | self.headers = { 39 | 'user-agent': self.options['user_agent'] 40 | } 41 | 42 | if 'headers' in self.options: 43 | self.headers.update(self.dict_key_lower(self.options['headers'])) 44 | del self.options['headers'] 45 | 46 | self.auth = AuthHandler(auth) 47 | 48 | def get(self, path, params={}, options={}): 49 | options.update({'query': params}) 50 | return self.request(path, None, 'get', options) 51 | 52 | def post(self, path, body={}, options={}): 53 | return self.request(path, body, 'post', options) 54 | 55 | def patch(self, path, body={}, options={}): 56 | return self.request(path, body, 'patch', options) 57 | 58 | def delete(self, path, body={}, options={}): 59 | return self.request(path, body, 'delete', options) 60 | 61 | def put(self, path, body={}, options={}): 62 | return self.request(path, body, 'put', options) 63 | 64 | def request(self, path, body, method, options): 65 | """Intermediate function which does three main things 66 | 67 | - Transforms the body of request into correct format 68 | - Creates the requests with given parameters 69 | - Returns response body after parsing it into correct format 70 | """ 71 | kwargs = copy.deepcopy(self.options) 72 | kwargs.update(options) 73 | 74 | kwargs['headers'] = copy.deepcopy(self.headers) 75 | 76 | if 'headers' in options: 77 | kwargs['headers'].update(self.dict_key_lower(options['headers'])) 78 | 79 | kwargs['data'] = body 80 | kwargs['allow_redirects'] = True 81 | 82 | kwargs['params'] = kwargs['query'] if 'query' in kwargs else {} 83 | 84 | if 'query' in kwargs: 85 | del kwargs['query'] 86 | 87 | if 'body' in kwargs: 88 | del kwargs['body'] 89 | 90 | del kwargs['base'] 91 | del kwargs['user_agent'] 92 | {{if .Api.NoVerifySSL}} 93 | kwargs['verify'] = False 94 | {{end}} 95 | if method != 'get': 96 | kwargs = self.set_body(kwargs) 97 | 98 | kwargs['hooks'] = dict(response=ErrorHandler.check_error) 99 | 100 | kwargs = self.auth.set(kwargs) 101 | 102 | response = self.create_request(method, path, kwargs) 103 | 104 | return Response( 105 | self.get_body(response), response.status_code, response.headers 106 | ) 107 | 108 | def create_request(self, method, path, options): 109 | """Creating a request with the given arguments 110 | 111 | If api_version is set, appends it immediately after host 112 | """ 113 | version = '/' + options['api_version'] if 'api_version' in options else '' 114 | {{if .Api.Response.Suffix}} 115 | # Adds a suffix (ex: ".html", ".json") to url 116 | suffix = options['response_type'] if 'response_type' in options else '{{.Api.Response.Formats.Default}}' 117 | path = path + '.' + suffix 118 | {{end}} 119 | path = urlparse.urljoin(self.base, version + path) 120 | 121 | if 'api_version' in options: 122 | del options['api_version'] 123 | 124 | if 'response_type' in options: 125 | del options['response_type'] 126 | 127 | return requests.request(method, path, **options) 128 | 129 | def get_body(self, response): 130 | """Get response body in correct format""" 131 | return ResponseHandler.get_body(response) 132 | 133 | def set_body(self, request): 134 | """Set request body in correct format""" 135 | return RequestHandler.set_body(request) 136 | 137 | def dict_key_lower(self, dic): 138 | """Make dict keys all lower case""" 139 | return dict(zip(map(self.key_lower, dic.keys()), dic.values())) 140 | 141 | def key_lower(self, key): 142 | """Make a function for lower case""" 143 | return key.lower() 144 | -------------------------------------------------------------------------------- /templates/node/lib/http_client/index.js: -------------------------------------------------------------------------------- 1 | var url = require('url') 2 | , request = require('request'); 3 | 4 | var client = module.exports; 5 | 6 | client.AuthHandler = require('./auth_handler'); 7 | client.ErrorHandler = require('./error_handler'); 8 | 9 | client.RequestHandler = require('./request_handler'); 10 | client.ResponseHandler = require('./response_handler'); 11 | 12 | client.Response = require('./response.js'); 13 | 14 | /** 15 | * Main HttpClient which is used by Api classes 16 | */ 17 | client.HttpClient = function ({{if .Api.BaseAsArg}}baseUrl, {{end}}auth, options) { 18 | if (!options) { 19 | options = {}; 20 | } 21 | 22 | if (!auth) { 23 | auth = {}; 24 | } 25 | 26 | {{if .Api.Authorization.Oauth}} 27 | if (typeof auth === 'string') { 28 | auth = { 'access_token': auth }; 29 | } 30 | {{else}}{{if .Api.Authorization.Header}} 31 | if (typeof auth === 'string') { 32 | auth = { 'http_header': auth }; 33 | } 34 | {{end}}{{end}} 35 | this.options = { 36 | 'base': {{if .Api.BaseAsArg}}baseUrl{{else}}'{{.Api.Base}}'{{end}},{{with .Api.Version}} 37 | 'api_version': '{{.}}',{{end}} 38 | 'user_agent': 'alpaca/{{.Version}} (https://github.com/pksunkara/alpaca)' 39 | }; 40 | 41 | for (var key in options) { 42 | this.options[key] = options[key]; 43 | } 44 | 45 | this.base = this.options.base; 46 | 47 | this.headers = { 48 | 'user-agent': this.options.user_agent 49 | }; 50 | 51 | if (this.options.headers) { 52 | for (key in this.options.headers) { 53 | this.headers[key.toLowerCase()] = this.options.headers[key]; 54 | } 55 | 56 | delete this.options.headers; 57 | } 58 | 59 | this.auth = new client.AuthHandler(auth); 60 | 61 | return this; 62 | }; 63 | 64 | client.HttpClient.prototype.get = function (path, params, options, callback) { 65 | options.query = params; 66 | 67 | this.request(path, {}, 'GET', options, callback); 68 | }; 69 | 70 | client.HttpClient.prototype.post = function (path, body, options, callback) { 71 | this.request(path, body, 'POST', options, callback); 72 | }; 73 | 74 | client.HttpClient.prototype.patch = function (path, body, options, callback) { 75 | this.request(path, body, 'PATCH', options, callback); 76 | }; 77 | 78 | client.HttpClient.prototype.delete = function (path, body, options, callback) { 79 | this.request(path, body, 'DELETE', options, callback); 80 | }; 81 | 82 | client.HttpClient.prototype.put = function (path, body, options, callback) { 83 | this.request(path, body, 'PUT', options, callback); 84 | }; 85 | 86 | /** 87 | * Intermediate function which does three main things 88 | * 89 | * - Transforms the body of request into correct format 90 | * - Creates the requests with give parameters 91 | * - Returns response body after parsing it into correct format 92 | */ 93 | client.HttpClient.prototype.request = function (path, body, method, options, callback) { 94 | var headers = {}, self = this; 95 | 96 | for (var key in this.options) { 97 | if (!options[key]) { 98 | options[key] = this.options[key]; 99 | } 100 | } 101 | 102 | if (options.headers) { 103 | headers = options.headers; 104 | delete options.headers; 105 | } 106 | 107 | for (key in headers) { 108 | var lowerKey = key.toLowerCase(); 109 | 110 | if (key !== lowerKey) { 111 | headers[lowerKey] = headers[key]; 112 | delete headers[key]; 113 | } 114 | } 115 | 116 | for (key in this.headers) { 117 | if (!headers[key]) { 118 | headers[key] = this.headers[key]; 119 | } 120 | } 121 | 122 | var reqobj = { 123 | 'url': path, 124 | 'qs': options.query || {}, 125 | 'method': method, 126 | 'headers': headers 127 | }; 128 | 129 | delete options.query; 130 | delete options.body; 131 | 132 | delete options.base; 133 | delete options.user_agent; 134 | {{if .Api.NoVerifySSL}} 135 | reqobj['strictSSL'] = false; 136 | {{end}} 137 | if (method !== 'GET') { 138 | reqobj = this.setBody(reqobj, body, options); 139 | } 140 | 141 | this.auth.set(reqobj, function (err, reqobj) { 142 | if (err) { 143 | return callback(err); 144 | } 145 | 146 | self.createRequest(reqobj, options, function(err, response, body) { 147 | if (err) { 148 | return callback(err); 149 | } 150 | 151 | self.getBody(response, body, function(err, response, body) { 152 | if (err) { 153 | return callback(err); 154 | } 155 | 156 | client.ErrorHandler(response, body, function(err, response, body) { 157 | if (err) { 158 | return callback(err); 159 | } 160 | 161 | callback(null, new client.Response(body, response.statusCode, response.headers)); 162 | }); 163 | }); 164 | }); 165 | }); 166 | }; 167 | 168 | /** 169 | * Creating a request with the given arguments 170 | * 171 | * If api_version is set, appends it immediately after host 172 | */ 173 | client.HttpClient.prototype.createRequest = function (reqobj, options, callback) { 174 | var version = (options.api_version ? '/' + options.api_version : ''); 175 | {{if .Api.Response.Suffix}} 176 | // Adds a suffix (ex: ".html", ".json") to url 177 | var suffix = (options.response_type ? options.response_type : '{{.Api.Response.Formats.Default}}'); 178 | reqobj.url = reqobj.url + '.' + suffix; 179 | {{end}} 180 | reqobj.url = url.resolve(this.base, version + reqobj.url); 181 | 182 | request(reqobj, callback); 183 | }; 184 | 185 | /** 186 | * Get response body in correct format 187 | */ 188 | client.HttpClient.prototype.getBody = function (response, body, callback) { 189 | client.ResponseHandler.getBody(response, body, callback); 190 | }; 191 | 192 | /** 193 | * Set request body in correct format 194 | */ 195 | client.HttpClient.prototype.setBody = function (request, body, options) { 196 | return client.RequestHandler.setBody(request, body, options); 197 | }; 198 | -------------------------------------------------------------------------------- /examples/buffer/doc.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "title": "Information", 4 | "desc": "Returns api instance to get auxilary information about Buffer useful when creating your app.", 5 | "functions": { 6 | "show": { 7 | "title": "Services and configuration", 8 | "desc": "Returns an object with the current configuration that Buffer is using, including supported services, their icons and the varying limits of character and schedules." 9 | } 10 | } 11 | }, 12 | "user": { 13 | "title": "Authenticated user", 14 | "desc": "Returns authenticated user api instance.", 15 | "functions": { 16 | "show": { 17 | "title": "User information", 18 | "desc": "Returns information about the authenticated user." 19 | }, 20 | "profiles": { 21 | "title": "List of user's social profiles", 22 | "desc": "Returns an array of social media profiles connected to the authenticated users account." 23 | }, 24 | "create_update": { 25 | "title": "Create a social update", 26 | "desc": "Create one or more new status updates.", 27 | "params": { 28 | "text": { 29 | "desc": "The status update text.", 30 | "value": "This is an example update" 31 | }, 32 | "profile_ids": { 33 | "desc": "An array of profile id's that the status update should be sent to. Invalid profile_id's will be silently ignored.", 34 | "value": ["4eb854340acb04e870000010", "4eb9276e0acb04bb81000067"] 35 | } 36 | } 37 | } 38 | } 39 | }, 40 | "link": { 41 | "title": "Links", 42 | "desc": "Returns api instance to get information about links shared through Buffer.", 43 | "functions": { 44 | "shares": { 45 | "title": "Amount of link shares", 46 | "desc": "Returns an object with a the numbers of shares a link has had using Buffer.", 47 | "params": { 48 | "url": { 49 | "desc": "URL of the page for which the number of shares is requested.", 50 | "value": "http://bufferapp.com" 51 | } 52 | } 53 | } 54 | } 55 | }, 56 | "profile": { 57 | "title": "Social profiles", 58 | "desc": "Returns a social media profile api instance.", 59 | "args": { 60 | "id": { 61 | "desc": "Identifier of a social media profile", 62 | "value": "519fc3ca4d5e93901900002f" 63 | } 64 | }, 65 | "functions": { 66 | "show": { 67 | "title": "Get this social profile", 68 | "desc": "Returns details of the single specified social media profile." 69 | }, 70 | "pending": { 71 | "title": "List profile's pending updates", 72 | "desc": "Returns an array of updates that are currently in the buffer for an individual social media profile." 73 | }, 74 | "sent": { 75 | "title": "List profile's sent updates", 76 | "desc": "Returns an array of updates that have been sent from the buffer for an individual social media profile." 77 | }, 78 | "reorder": { 79 | "title": "Edit profile's updates order", 80 | "desc": "Edit the order at which statuses for the specified social media profile will be sent out of the buffer.", 81 | "params": { 82 | "order": { 83 | "desc": "An ordered array of status update id's. This can be a partial array in combination with the offset parameter or a full array of every update in the profiles Buffer.", 84 | "value": ["4eb854340acb04e870000010", "4eb9276e0acb04bb81000067", "4eb2567e0ade04ba51000001"] 85 | } 86 | } 87 | }, 88 | "shuffle": { 89 | "title": "Shuffle profile's updates", 90 | "desc": "Randomize the order at which statuses for the specified social media profile will be sent out of the buffer." 91 | } 92 | } 93 | }, 94 | "schedule": { 95 | "title": "Posting schedules", 96 | "desc": "Returns scheduling api instance for social media profile.", 97 | "args": { 98 | "id": { 99 | "desc": "Identifier of a social media profile", 100 | "value": "519fc3ca4d5e93901900002f" 101 | } 102 | }, 103 | "functions": { 104 | "list": { 105 | "title": "Get profile's posting schedules", 106 | "desc": "Returns details of the posting schedules associated with a social media profile." 107 | }, 108 | "update": { 109 | "title": "Update profile's posting schedules", 110 | "desc": "Set the posting schedules for the specified social media profile.", 111 | "params": { 112 | "schedules": { 113 | "desc": "Each item in the array is an individual posting schedule which consists of days and times to match the format return by the above method.", 114 | "value": [{ 115 | "days": ["mon", "tue", "thu"], 116 | "times": ["12:45", "15:30", "17:43"] 117 | }] 118 | } 119 | } 120 | } 121 | } 122 | }, 123 | "update": { 124 | "title": "Social updates", 125 | "desc": "Returns a social media update api instance.", 126 | "args": { 127 | "id": { 128 | "desc": "Identifier of a social media update", 129 | "value": "4eb8565e0acb04bb82000004" 130 | } 131 | }, 132 | "functions": { 133 | "show": { 134 | "title": "Get this social update", 135 | "desc": "Returns a single social media update." 136 | }, 137 | "interactions": { 138 | "title": "List interactions of the update", 139 | "desc": "Returns the detailed information on individual interactions with the social media update such as favorites, retweets and likes." 140 | }, 141 | "update": { 142 | "title": "Edit this update", 143 | "desc": "Edit an existing, individual status update.", 144 | "params": { 145 | "text": { 146 | "desc": "The status update text.", 147 | "value": "This is an edited update" 148 | } 149 | } 150 | }, 151 | "share": { 152 | "title": "Share this update", 153 | "desc": "Immediately shares a single pending update and recalculates times for updates remaining in the queue." 154 | }, 155 | "destroy": { 156 | "title": "Delete this update", 157 | "desc": "Permanently delete an existing status update." 158 | }, 159 | "top": { 160 | "title": "Move this update to top", 161 | "desc": "Move an existing status update to the top of the queue and recalculate times for all updates in the queue. Returns the update with its new posting time." 162 | } 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /templates/php/lib/HttpClient/HttpClient.php: -------------------------------------------------------------------------------- 1 | '{{.Api.Base}}',{{with .Api.Version}} 22 | 'api_version' => '{{.}}',{{end}} 23 | 'user_agent' => 'alpaca/{{.Version}} (https://github.com/pksunkara/alpaca)' 24 | ); 25 | 26 | protected $headers = array(); 27 | 28 | public function __construct({{if .Api.BaseAsArg}}$baseUrl, {{end}}$auth = array(), array $options = array()) 29 | { 30 | {{if .Api.BaseAsArg}} $this->options['base'] = $baseUrl; 31 | {{end}}{{if .Api.Authorization.Oauth}} 32 | if (gettype($auth) == 'string') { 33 | $auth = array('access_token' => $auth); 34 | } 35 | {{else}}{{if .Api.Authorization.Header}} 36 | if (gettype($auth) == 'string') { 37 | $auth = array('http_header' => $auth); 38 | } 39 | {{end}}{{end}} 40 | $this->options = array_merge($this->options, $options); 41 | 42 | $this->headers = array( 43 | 'user-agent' => $this->options['user_agent'], 44 | ); 45 | 46 | if (isset($this->options['headers'])) { 47 | $this->headers = array_merge($this->headers, array_change_key_case($this->options['headers'])); 48 | unset($this->options['headers']); 49 | } 50 | 51 | $client = new GuzzleClient($this->options['base'], $this->options); 52 | $this->client = $client; 53 | 54 | $listener = array(new ErrorHandler(), 'onRequestError'); 55 | $this->client->getEventDispatcher()->addListener('request.error', $listener); 56 | 57 | if (!empty($auth)) { 58 | $listener = array(new AuthHandler($auth), 'onRequestBeforeSend'); 59 | $this->client->getEventDispatcher()->addListener('request.before_send', $listener); 60 | } 61 | } 62 | 63 | public function get($path, array $params = array(), array $options = array()) 64 | { 65 | return $this->request($path, null, 'GET', array_merge($options, array('query' => $params))); 66 | } 67 | 68 | public function post($path, $body, array $options = array()) 69 | { 70 | return $this->request($path, $body, 'POST', $options); 71 | } 72 | 73 | public function patch($path, $body, array $options = array()) 74 | { 75 | return $this->request($path, $body, 'PATCH', $options); 76 | } 77 | 78 | public function delete($path, $body, array $options = array()) 79 | { 80 | return $this->request($path, $body, 'DELETE', $options); 81 | } 82 | 83 | public function put($path, $body, array $options = array()) 84 | { 85 | return $this->request($path, $body, 'PUT', $options); 86 | } 87 | 88 | /** 89 | * Intermediate function which does three main things 90 | * 91 | * - Transforms the body of request into correct format 92 | * - Creates the requests with give parameters 93 | * - Returns response body after parsing it into correct format 94 | */ 95 | public function request($path, $body = null, $httpMethod = 'GET', array $options = array()) 96 | { 97 | $headers = array(); 98 | 99 | $options = array_merge($this->options, $options); 100 | 101 | if (isset($options['headers'])) { 102 | $headers = $options['headers']; 103 | unset($options['headers']); 104 | } 105 | 106 | $headers = array_merge($this->headers, array_change_key_case($headers)); 107 | 108 | unset($options['body']); 109 | 110 | unset($options['base']); 111 | unset($options['user_agent']); 112 | {{if .Api.NoVerifySSL}} 113 | $options['verify'] = false; 114 | {{end}} 115 | $request = $this->createRequest($httpMethod, $path, null, $headers, $options); 116 | 117 | if ($httpMethod != 'GET') { 118 | $request = $this->setBody($request, $body, $options); 119 | } 120 | 121 | try { 122 | $response = $this->client->send($request); 123 | } catch (\LogicException $e) { 124 | throw new \ErrorException($e->getMessage()); 125 | } catch (\RuntimeException $e) { 126 | throw new \RuntimeException($e->getMessage()); 127 | } 128 | 129 | return new Response($this->getBody($response), $response->getStatusCode(), $response->getHeaders()); 130 | } 131 | 132 | /** 133 | * Creating a request with the given arguments 134 | * 135 | * If api_version is set, appends it immediately after host 136 | */ 137 | public function createRequest($httpMethod, $path, $body = null, array $headers = array(), array $options = array()) 138 | { 139 | $version = (isset($options['api_version']) ? "/".$options['api_version'] : ""); 140 | {{if .Api.Response.Suffix}} 141 | // Adds a suffix (ex: ".html", ".json") to url 142 | $suffix = (isset($options['response_type']) ? $options['response_type'] : "{{.Api.Response.Formats.Default}}"); 143 | $path = $path.".".$suffix; 144 | {{end}} 145 | $path = $version.$path; 146 | 147 | return $this->client->createRequest($httpMethod, $path, $headers, $body, $options); 148 | } 149 | 150 | /** 151 | * Get response body in correct format 152 | */ 153 | public function getBody($response) 154 | { 155 | return ResponseHandler::getBody($response); 156 | } 157 | 158 | /** 159 | * Set request body in correct format 160 | */ 161 | public function setBody(RequestInterface $request, $body, $options) 162 | { 163 | return RequestHandler::setBody($request, $body, $options); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /templates/python/readme.md: -------------------------------------------------------------------------------- 1 | # {{.Pkg.Git.Name}}-python 2 | 3 | {{if .Pkg.Official}}Official {{end}}{{.Pkg.Name}} API library client for python 4 | 5 | __This library is generated by [alpaca](https://github.com/pksunkara/alpaca)__ 6 | 7 | ## Installation 8 | 9 | Make sure you have [pip](https://pypi.python.org/pypi/pip) installed 10 | 11 | ```bash 12 | $ pip install {{.Pkg.Package}} 13 | ``` 14 | 15 | #### Versions 16 | 17 | Works with [ 2.6 / 2.7 / 3.2 / 3.3 ] 18 | 19 | ## Usage 20 | 21 | ```python 22 | import {{call .Fnc.underscore .Pkg.Name}} 23 | 24 | # Then we instantiate a client (as shown below) 25 | ``` 26 | 27 | ### Build a client 28 | {{if .Api.Authorization.NeedAuth}} 29 | __Using this api without authentication gives an error__ 30 | {{else}} 31 | ##### Without any authentication 32 | 33 | ```python 34 | client = {{call .Fnc.underscore .Pkg.Name}}.Client({{if .Api.BaseAsArg}}'{{.Api.Base}}'{{end}}) 35 | 36 | # If you need to send options 37 | client = {{call .Fnc.underscore .Pkg.Name}}.Client({{if .Api.BaseAsArg}}'{{.Api.Base}}', {{end}}{}, client_options) 38 | ``` 39 | {{end}}{{if .Api.Authorization.Basic}} 40 | ##### Basic authentication 41 | 42 | ```python 43 | auth = { 'username': 'pksunkara', 'password': 'password' } 44 | 45 | client = {{call .Fnc.underscore .Pkg.Name}}.Client({{if .Api.BaseAsArg}}'{{.Api.Base}}', {{end}}auth, client_options) 46 | ``` 47 | {{end}}{{if .Api.Authorization.Header}} 48 | ##### Authorization header token 49 | 50 | ```python 51 | client = {{call .Fnc.underscore .Pkg.Name}}.Client({{if .Api.BaseAsArg}}'{{.Api.Base}}', {{end}}{{if .Api.Authorization.Oauth}}{'http_header': '1a2b3'}{{else}}'1a2b3'{{end}}, client_options) 52 | ``` 53 | {{end}}{{if .Api.Authorization.Oauth}} 54 | ##### Oauth access token 55 | 56 | ```python 57 | client = {{call .Fnc.underscore .Pkg.Name}}.Client({{if .Api.BaseAsArg}}'{{.Api.Base}}', {{end}}'1a2b3', client_options) 58 | ``` 59 | 60 | ##### Oauth client secret 61 | 62 | ```python 63 | auth = { 'client_id': '09a8b7', 'client_secret': '1a2b3' } 64 | 65 | client = {{call .Fnc.underscore .Pkg.Name}}.Client({{if .Api.BaseAsArg}}'{{.Api.Base}}', {{end}}auth, client_options) 66 | ``` 67 | {{end}} 68 | ### Client Options 69 | 70 | The following options are available while instantiating a client: 71 | 72 | * __base__: Base url for the api 73 | * __api_version__: Default version of the api (to be used in url) 74 | * __user_agent__: Default user-agent for all requests 75 | * __headers__: Default headers for all requests 76 | * __request_type__: Default format of the request body{{if .Api.Response.Suffix}} 77 | * __response_type__: Default format of the response (to be used in url suffix){{end}} 78 | 79 | ### Response information 80 | 81 | __All the callbacks provided to an api call will receive the response as shown below__ 82 | 83 | ```python 84 | response = client.klass('args').method('args', method_options) 85 | 86 | response.code 87 | # >>> 200 88 | 89 | response.headers 90 | # >>> {'x-server': 'apache'} 91 | ``` 92 | {{if .Api.Response.Formats.Html}} 93 | ##### HTML/TEXT response 94 | 95 | When the response sent by server is either __html__ or __text__, it is not changed in any way 96 | 97 | ```python 98 | response.body 99 | # >>> 'The username is pksunkara!' 100 | ``` 101 | {{end}}{{if .Api.Response.Formats.Json}} 102 | ##### JSON response 103 | 104 | When the response sent by server is __json__, it is decoded into a dict 105 | 106 | ```python 107 | response.body 108 | # >>> {'user': 'pksunkara'} 109 | ``` 110 | {{end}} 111 | ### Method Options 112 | 113 | The following options are available while calling a method of an api: 114 | 115 | * __api_version__: Version of the api (to be used in url) 116 | * __headers__: Headers for the request 117 | * __query__: Query parameters for the url 118 | * __body__: Body of the request 119 | * __request_type__: Format of the request body{{if .Api.Response.Suffix}} 120 | * __response_type__: Format of the response (to be used in url suffix){{end}} 121 | 122 | ### Request body information 123 | 124 | Set __request_type__ in options to modify the body accordingly 125 | 126 | ##### RAW request 127 | 128 | When the value is set to __raw__, don't modify the body at all. 129 | 130 | ```python 131 | body = 'username=pksunkara' 132 | # >>> 'username=pksunkara' 133 | ``` 134 | {{if .Api.Request.Formats.Form}} 135 | ##### FORM request 136 | 137 | When the value is set to __form__, urlencode the body. 138 | 139 | ```python 140 | body = {'user': 'pksunkara'} 141 | # >>> 'user=pksunkara' 142 | ``` 143 | {{end}}{{if .Api.Request.Formats.Json}} 144 | ##### JSON request 145 | 146 | When the value is set to __json__, JSON encode the body. 147 | 148 | ```python 149 | body = {'user': 'pksunkara'} 150 | # >>> '{"user": "pksunkara"}' 151 | ``` 152 | {{end}}{{with $data := .}}{{range .Api.Classes}} 153 | ### {{(index $data.Doc .Name).Title}} api 154 | 155 | {{(index $data.Doc .Name).Desc}}{{with .Args}} 156 | 157 | The following arguments are required: 158 | {{end}}{{with $class := .}}{{range .Args}} 159 | * __{{.}}__: {{(index ((index $data.Doc $class.Name).Args) .).Desc}}{{end}} 160 | 161 | ```python 162 | {{call $data.Fnc.underscore $class.Name}} = client.{{call $data.Fnc.underscore $class.Name}}({{call $data.Fnc.prnt.python .Args ((index $data.Doc $class.Name).Args) ", " false}}) 163 | ``` 164 | {{range $class.Functions}} 165 | ##### {{(index ((index $data.Doc $class.Name).Functions) .Name).Title}} ({{call $data.Fnc.upper (or .Method "get")}} {{.Path}}) 166 | 167 | {{(index ((index $data.Doc $class.Name).Functions) .Name).Desc}}{{with .Params}} 168 | 169 | The following arguments are required: 170 | {{end}}{{with $method := .}}{{range .Params}}{{if .Required}} 171 | * __{{.Name}}__: {{(index ((index ((index $data.Doc $class.Name).Functions) $method.Name).Params) .Name).Desc}}{{end}}{{end}}{{end}} 172 | 173 | ```python 174 | response = {{call $data.Fnc.underscore $class.Name}}.{{call $data.Fnc.underscore .Name}}({{call $data.Fnc.prnt.python .Params ((index ((index $data.Doc $class.Name).Functions) .Name).Params) ", " true}}options) 175 | ``` 176 | {{end}}{{end}}{{end}}{{end}} 177 | ## Contributors 178 | Here is a list of [Contributors](https://{{.Pkg.Git.Site}}/{{.Pkg.Git.User}}/{{.Pkg.Git.Name}}-python/contributors) 179 | 180 | ### TODO 181 | 182 | ## License 183 | {{.Pkg.License}} 184 | 185 | ## Bug Reports 186 | Report [here](https://{{.Pkg.Git.Site}}/{{.Pkg.Git.User}}/{{.Pkg.Git.Name}}-python/issues). 187 | 188 | ## Contact 189 | {{.Pkg.Author.Name}} ({{.Pkg.Author.Email}}) 190 | -------------------------------------------------------------------------------- /templates/ruby/readme.md: -------------------------------------------------------------------------------- 1 | # {{.Pkg.Git.Name}}-ruby 2 | 3 | {{if .Pkg.Official}}Official {{end}}{{.Pkg.Name}} API library client for ruby 4 | 5 | __This library is generated by [alpaca](https://github.com/pksunkara/alpaca)__ 6 | 7 | ## Installation 8 | 9 | Make sure you have [rubygems](https://rubygems.org) installed 10 | 11 | ```bash 12 | $ gem install {{.Pkg.Package}} 13 | ``` 14 | 15 | #### Versions 16 | 17 | Works with [ 1.8.6 / 1.8.7 / 1.9.1 / 1.9.2 / 1.9.3 / 2.0.0 / 2.1.0 / 2.1.1 ] 18 | 19 | ## Usage 20 | 21 | ```ruby 22 | require "{{.Pkg.Package}}" 23 | 24 | # Then we instantiate a client (as shown below) 25 | ``` 26 | 27 | ### Build a client 28 | {{if .Api.Authorization.NeedAuth}} 29 | __Using this api without authentication gives an error__ 30 | {{else}} 31 | ##### Without any authentication 32 | 33 | ```ruby 34 | client = {{call .Fnc.camelize .Pkg.Name}}::Client.new{{if .Api.BaseAsArg}}('{{.Api.Base}}'){{end}} 35 | 36 | # If you need to send options 37 | client = {{call .Fnc.camelize .Pkg.Name}}::Client.new({{if .Api.BaseAsArg}}'{{.Api.Base}}', {{end}}{}, client_options) 38 | ``` 39 | {{end}}{{if .Api.Authorization.Basic}} 40 | ##### Basic authentication 41 | 42 | ```ruby 43 | auth = { :username => 'pksunkara', :password => 'password' } 44 | 45 | client = {{call .Fnc.camelize .Pkg.Name}}::Client.new({{if .Api.BaseAsArg}}'{{.Api.Base}}', {{end}}auth, client_options) 46 | ``` 47 | {{end}}{{if .Api.Authorization.Header}} 48 | ##### Authorization header token 49 | 50 | ```ruby 51 | client = {{call .Fnc.camelize .Pkg.Name}}::Client.new({{if .Api.BaseAsArg}}'{{.Api.Base}}', {{end}}{{if .Api.Authorization.Oauth}}{:http_header => '1a2b3'}{{else}}'1a2b3'{{end}}, client_options) 52 | ``` 53 | {{end}}{{if .Api.Authorization.Oauth}} 54 | ##### Oauth access token 55 | 56 | ```ruby 57 | client = {{call .Fnc.camelize .Pkg.Name}}::Client.new({{if .Api.BaseAsArg}}'{{.Api.Base}}', {{end}}'1a2b3', client_options) 58 | ``` 59 | 60 | ##### Oauth client secret 61 | 62 | ```ruby 63 | auth = { :client_id => '09a8b7', :client_secret => '1a2b3' } 64 | 65 | client = {{call .Fnc.camelize .Pkg.Name}}::Client.new({{if .Api.BaseAsArg}}'{{.Api.Base}}', {{end}}auth, client_options) 66 | ``` 67 | {{end}} 68 | ### Client Options 69 | 70 | The following options are available while instantiating a client: 71 | 72 | * __base__: Base url for the api 73 | * __api_version__: Default version of the api (to be used in url) 74 | * __user_agent__: Default user-agent for all requests 75 | * __headers__: Default headers for all requests 76 | * __request_type__: Default format of the request body{{if .Api.Response.Suffix}} 77 | * __response_type__: Default format of the response (to be used in url suffix){{end}} 78 | 79 | ### Response information 80 | 81 | __All the callbacks provided to an api call will receive the response as shown below__ 82 | 83 | ```ruby 84 | response = client.klass('args').method('args', method_options) 85 | 86 | response.code 87 | # >>> 200 88 | 89 | response.headers 90 | # >>> {'x-server' => 'apache'} 91 | ``` 92 | {{if .Api.Response.Formats.Html}} 93 | ##### HTML/TEXT response 94 | 95 | When the response sent by server is either __html__ or __text__, it is not changed in any way 96 | 97 | ```ruby 98 | response.body 99 | # >>> 'The username is pksunkara!' 100 | ``` 101 | {{end}}{{if .Api.Response.Formats.Json}} 102 | ##### JSON response 103 | 104 | When the response sent by server is __json__, it is decoded into a hash 105 | 106 | ```ruby 107 | response.body 108 | # >>> {'user' => 'pksunkara'} 109 | ``` 110 | {{end}} 111 | ### Method Options 112 | 113 | The following options are available while calling a method of an api: 114 | 115 | * __api_version__: Version of the api (to be used in url) 116 | * __headers__: Headers for the request 117 | * __query__: Query parameters for the url 118 | * __body__: Body of the request 119 | * __request_type__: Format of the request body{{if .Api.Response.Suffix}} 120 | * __response_type__: Format of the response (to be used in url suffix){{end}} 121 | 122 | ### Request body information 123 | 124 | Set __request_type__ in options to modify the body accordingly 125 | 126 | ##### RAW request 127 | 128 | When the value is set to __raw__, don't modify the body at all. 129 | 130 | ```ruby 131 | body = 'username=pksunkara' 132 | # >>> 'username=pksunkara' 133 | ``` 134 | {{if .Api.Request.Formats.Form}} 135 | ##### FORM request 136 | 137 | When the value is set to __form__, urlencode the body. 138 | 139 | ```ruby 140 | body = {'user' => 'pksunkara'} 141 | # >>> 'user=pksunkara' 142 | ``` 143 | {{end}}{{if .Api.Request.Formats.Json}} 144 | ##### JSON request 145 | 146 | When the value is set to __json__, JSON encode the body. 147 | 148 | ```ruby 149 | body = {'user' => 'pksunkara'} 150 | # >>> '{"user": "pksunkara"}' 151 | ``` 152 | {{end}}{{with $data := .}}{{range .Api.Classes}} 153 | ### {{(index $data.Doc .Name).Title}} api 154 | 155 | {{(index $data.Doc .Name).Desc}}{{with .Args}} 156 | 157 | The following arguments are required: 158 | {{end}}{{with $class := .}}{{range .Args}} 159 | * __{{.}}__: {{(index ((index $data.Doc $class.Name).Args) .).Desc}}{{end}} 160 | 161 | ```ruby 162 | {{call $data.Fnc.underscore $class.Name}} = client.{{call $data.Fnc.underscore $class.Name}}({{call $data.Fnc.prnt.ruby .Args ((index $data.Doc $class.Name).Args) ", " false}}) 163 | ``` 164 | {{range $class.Functions}} 165 | ##### {{(index ((index $data.Doc $class.Name).Functions) .Name).Title}} ({{call $data.Fnc.upper (or .Method "get")}} {{.Path}}) 166 | 167 | {{(index ((index $data.Doc $class.Name).Functions) .Name).Desc}}{{with .Params}} 168 | 169 | The following arguments are required: 170 | {{end}}{{with $method := .}}{{range .Params}}{{if .Required}} 171 | * __{{.Name}}__: {{(index ((index ((index $data.Doc $class.Name).Functions) $method.Name).Params) .Name).Desc}}{{end}}{{end}}{{end}} 172 | 173 | ```ruby 174 | response = {{call $data.Fnc.underscore $class.Name}}.{{call $data.Fnc.underscore .Name}}({{call $data.Fnc.prnt.ruby .Params ((index ((index $data.Doc $class.Name).Functions) .Name).Params) ", " true}}options) 175 | ``` 176 | {{end}}{{end}}{{end}}{{end}} 177 | ## Contributors 178 | Here is a list of [Contributors](https://{{.Pkg.Git.Site}}/{{.Pkg.Git.User}}/{{.Pkg.Git.Name}}-ruby/contributors) 179 | 180 | ### TODO 181 | 182 | ## License 183 | {{.Pkg.License}} 184 | 185 | ## Bug Reports 186 | Report [here](https://{{.Pkg.Git.Site}}/{{.Pkg.Git.User}}/{{.Pkg.Git.Name}}-ruby/issues). 187 | 188 | ## Contact 189 | {{.Pkg.Author.Name}} ({{.Pkg.Author.Email}}) 190 | -------------------------------------------------------------------------------- /templates/node/readme.md: -------------------------------------------------------------------------------- 1 | # {{.Pkg.Git.Name}}-node 2 | 3 | {{if .Pkg.Official}}Official {{end}}{{.Pkg.Name}} API library client for node.js 4 | 5 | __This library is generated by [alpaca](https://github.com/pksunkara/alpaca)__ 6 | 7 | ## Installation 8 | 9 | Make sure you have [npm](https://npmjs.org) installed. 10 | 11 | ```bash 12 | $ npm install {{.Pkg.Package}} 13 | ``` 14 | 15 | #### Versions 16 | 17 | Works with [ 0.8 / 0.9 / 0.10 / 0.11 ] 18 | 19 | ## Usage 20 | 21 | ```js 22 | var {{call .Fnc.camelizeDownFirst .Pkg.Name}} = require('{{.Pkg.Package}}'); 23 | 24 | // Then we instantiate a client (as shown below) 25 | ``` 26 | 27 | ### Build a client 28 | {{if .Api.Authorization.NeedAuth}} 29 | __Using this api without authentication gives an error__ 30 | {{else}} 31 | ##### Without any authentication 32 | 33 | ```js 34 | var client = {{call .Fnc.camelizeDownFirst .Pkg.Name}}.client({{if .Api.BaseAsArg}}'{{.Api.Base}}'{{end}}); 35 | 36 | // If you need to send options 37 | var client = {{call .Fnc.camelizeDownFirst .Pkg.Name}}.client({{if .Api.BaseAsArg}}'{{.Api.Base}}', {{end}}{}, clientOptions); 38 | ``` 39 | {{end}}{{if .Api.Authorization.Basic}} 40 | ##### Basic authentication 41 | 42 | ```js 43 | var auth = { username: 'pksunkara', password: 'password' }; 44 | 45 | var client = {{call .Fnc.camelizeDownFirst .Pkg.Name}}.client({{if .Api.BaseAsArg}}'{{.Api.Base}}', {{end}}auth, clientOptions); 46 | ``` 47 | {{end}}{{if .Api.Authorization.Header}} 48 | ##### Authorization header token 49 | 50 | ```js 51 | var client = {{call .Fnc.camelizeDownFirst .Pkg.Name}}.client({{if .Api.BaseAsArg}}'{{.Api.Base}}', {{end}}{{if .Api.Authorization.Oauth}}{ http_header: '1a2b3' }{{else}}'1a2b3'{{end}}, clientOptions); 52 | ``` 53 | {{end}}{{if .Api.Authorization.Oauth}} 54 | ##### Oauth access token 55 | 56 | ```js 57 | var client = {{call .Fnc.camelizeDownFirst .Pkg.Name}}.client({{if .Api.BaseAsArg}}'{{.Api.Base}}', {{end}}'1a2b3', clientOptions); 58 | ``` 59 | 60 | ##### Oauth client secret 61 | 62 | ```js 63 | var client = {{call .Fnc.camelizeDownFirst .Pkg.Name}}.client({{if .Api.BaseAsArg}}'{{.Api.Base}}', {{end}}{ 64 | client_id: '09a8b7', 65 | client_secret: '1a2b3' 66 | }, clientOptions); 67 | ``` 68 | {{end}} 69 | ### Client Options 70 | 71 | The following options are available while instantiating a client: 72 | 73 | * __base__: Base url for the api 74 | * __api_version__: Default version of the api (to be used in url) 75 | * __user_agent__: Default user-agent for all requests 76 | * __headers__: Default headers for all requests 77 | * __request_type__: Default format of the request body{{if .Api.Response.Suffix}} 78 | * __response_type__: Default format of the response (to be used in url suffix){{end}} 79 | 80 | ### Response information 81 | 82 | __All the callbacks provided to an api call will receive the response as shown below__ 83 | 84 | ```js 85 | // You can also omit the 'methodOptions' param below 86 | client.klass('args').method('args', methodOptions, function (err, response) { 87 | if (err) console.log(err); 88 | 89 | response.code; 90 | // >>> 200 91 | 92 | response.headers; 93 | // >>> {'x-server': 'apache'} 94 | } 95 | ``` 96 | {{if .Api.Response.Formats.Html}} 97 | ##### HTML/TEXT response 98 | 99 | When the response sent by server is either __html__ or __text__, it is not changed in any way 100 | 101 | ```js 102 | response.body; 103 | // >>> 'The username is pksunkara!' 104 | ``` 105 | {{end}}{{if .Api.Response.Formats.Json}} 106 | ##### JSON response 107 | 108 | When the response sent by server is __json__, it is decoded into a hash 109 | 110 | ```js 111 | response.body; 112 | // >>> {'user': 'pksunkara'} 113 | ``` 114 | {{end}} 115 | ### Method Options 116 | 117 | The following options are available while calling a method of an api: 118 | 119 | * __api_version__: Version of the api (to be used in url) 120 | * __headers__: Headers for the request 121 | * __query__: Query parameters for the url 122 | * __body__: Body of the request 123 | * __request_type__: Format of the request body{{if .Api.Response.Suffix}} 124 | * __response_type__: Format of the response (to be used in url suffix){{end}} 125 | 126 | ### Request body information 127 | 128 | Set __request_type__ in options to modify the body accordingly 129 | 130 | ##### RAW request 131 | 132 | When the value is set to __raw__, don't modify the body at all. 133 | 134 | ```js 135 | body = 'username=pksunkara'; 136 | // >>> 'username=pksunkara' 137 | ``` 138 | {{if .Api.Request.Formats.Form}} 139 | ##### FORM request 140 | 141 | When the value is set to __form__, urlencode the body. 142 | 143 | ```js 144 | body = {'user': 'pksunkara'}; 145 | // >>> 'user=pksunkara' 146 | ``` 147 | {{end}}{{if .Api.Request.Formats.Json}} 148 | ##### JSON request 149 | 150 | When the value is set to __json__, JSON encode the body. 151 | 152 | ```js 153 | body = {'user': 'pksunkara'}; 154 | // >>> '{"user": "pksunkara"}' 155 | ``` 156 | {{end}}{{with $data := .}}{{range .Api.Classes}} 157 | ### {{(index $data.Doc .Name).Title}} api 158 | 159 | {{(index $data.Doc .Name).Desc}}{{with .Args}} 160 | 161 | The following arguments are required: 162 | {{end}}{{with $class := .}}{{range .Args}} 163 | * __{{.}}__: {{(index ((index $data.Doc $class.Name).Args) .).Desc}}{{end}} 164 | 165 | ```js 166 | var {{call $data.Fnc.camelizeDownFirst $class.Name}} = client.{{call $data.Fnc.camelizeDownFirst $class.Name}}({{call $data.Fnc.prnt.node .Args ((index $data.Doc $class.Name).Args) ", " false}}); 167 | ``` 168 | {{range $class.Functions}} 169 | ##### {{(index ((index $data.Doc $class.Name).Functions) .Name).Title}} ({{call $data.Fnc.upper (or .Method "get")}} {{.Path}}) 170 | 171 | {{(index ((index $data.Doc $class.Name).Functions) .Name).Desc}}{{with .Params}} 172 | 173 | The following arguments are required: 174 | {{end}}{{with $method := .}}{{range .Params}}{{if .Required}} 175 | * __{{.Name}}__: {{(index ((index ((index $data.Doc $class.Name).Functions) $method.Name).Params) .Name).Desc}}{{end}}{{end}}{{end}} 176 | 177 | ```js 178 | {{call $data.Fnc.camelizeDownFirst $class.Name}}.{{call $data.Fnc.camelizeDownFirst .Name}}({{call $data.Fnc.prnt.node .Params ((index ((index $data.Doc $class.Name).Functions) .Name).Params) ", " true}}options, callback); 179 | ``` 180 | {{end}}{{end}}{{end}}{{end}} 181 | ## Contributors 182 | Here is a list of [Contributors](https://{{.Pkg.Git.Site}}/{{.Pkg.Git.User}}/{{.Pkg.Git.Name}}-node/contributors) 183 | 184 | ### TODO 185 | 186 | ## License 187 | {{.Pkg.License}} 188 | 189 | ## Bug Reports 190 | Report [here](https://{{.Pkg.Git.Site}}/{{.Pkg.Git.User}}/{{.Pkg.Git.Name}}-node/issues). 191 | 192 | ## Contact 193 | {{.Pkg.Author.Name}} ({{.Pkg.Author.Email}}) 194 | -------------------------------------------------------------------------------- /templates/php/readme.md: -------------------------------------------------------------------------------- 1 | # {{.Pkg.Git.Name}}-php 2 | 3 | {{if .Pkg.Official}}Official {{end}}{{.Pkg.Name}} API library client for PHP 4 | 5 | __This library is generated by [alpaca](https://github.com/pksunkara/alpaca)__ 6 | 7 | ## Installation 8 | 9 | Make sure you have [composer](https://getcomposer.org) installed. 10 | 11 | Add the following to your composer.json 12 | 13 | ```js 14 | { 15 | "require": { 16 | "{{.Pkg.Php.Vendor}}/{{.Pkg.Package}}": "*" 17 | } 18 | } 19 | ``` 20 | 21 | Update your dependencies 22 | 23 | ```bash 24 | $ php composer.phar update 25 | ``` 26 | 27 | > This package follows the `PSR-0` convention names for its classes, which means you can easily integrate these classes loading in your own autoloader. 28 | 29 | #### Versions 30 | 31 | Works with [ 5.4 / 5.5 ] 32 | 33 | ## Usage 34 | 35 | ```php 36 | 'pksunkara', 'password' => 'password'); 61 | 62 | $client = new {{call .Fnc.camelize .Pkg.Name}}\Client({{if .Api.BaseAsArg}}'{{.Api.Base}}', {{end}}$auth, $clientOptions); 63 | ``` 64 | {{end}}{{if .Api.Authorization.Header}} 65 | ##### Authorization header token 66 | 67 | ```php 68 | $client = new {{call .Fnc.camelize .Pkg.Name}}\Client({{if .Api.BaseAsArg}}'{{.Api.Base}}', {{end}}{{if .Api.Authorization.Oauth}}array('http_header' => '1a2b3'){{else}}'1a2b3'{{end}}, $clientOptions); 69 | ``` 70 | {{end}}{{if .Api.Authorization.Oauth}} 71 | ##### Oauth access token 72 | 73 | ```php 74 | $client = new {{call .Fnc.camelize .Pkg.Name}}\Client({{if .Api.BaseAsArg}}'{{.Api.Base}}', {{end}}'1a2b3', $clientOptions); 75 | ``` 76 | 77 | ##### Oauth client secret 78 | 79 | ```php 80 | $auth = array('client_id' => '09a8b7', 'client_secret' => '1a2b3'); 81 | 82 | $client = new {{call .Fnc.camelize .Pkg.Name}}\Client({{if .Api.BaseAsArg}}'{{.Api.Base}}', {{end}}$auth, $clientOptions); 83 | ``` 84 | {{end}} 85 | ### Client Options 86 | 87 | The following options are available while instantiating a client: 88 | 89 | * __base__: Base url for the api 90 | * __api_version__: Default version of the api (to be used in url) 91 | * __user_agent__: Default user-agent for all requests 92 | * __headers__: Default headers for all requests 93 | * __request_type__: Default format of the request body{{if .Api.Response.Suffix}} 94 | * __response_type__: Default format of the response (to be used in url suffix){{end}} 95 | 96 | ### Response information 97 | 98 | __All the callbacks provided to an api call will receive the response as shown below__ 99 | 100 | ```php 101 | $response = $client->klass('args')->method('args', $methodOptions); 102 | 103 | $response->code; 104 | // >>> 200 105 | 106 | $response->headers; 107 | // >>> array('x-server' => 'apache') 108 | ``` 109 | {{if .Api.Response.Formats.Html}} 110 | ##### HTML/TEXT response 111 | 112 | When the response sent by server is either __html__ or __text__, it is not changed in any way 113 | 114 | ```php 115 | $response->body; 116 | // >>> 'The username is pksunkara!' 117 | ``` 118 | {{end}}{{if .Api.Response.Formats.Json}} 119 | ##### JSON response 120 | 121 | When the response sent by server is __json__, it is decoded into an array 122 | 123 | ```php 124 | $response->body; 125 | // >>> array('user' => 'pksunkara') 126 | ``` 127 | {{end}} 128 | ### Method Options 129 | 130 | The following options are available while calling a method of an api: 131 | 132 | * __api_version__: Version of the api (to be used in url) 133 | * __headers__: Headers for the request 134 | * __query__: Query parameters for the url 135 | * __body__: Body of the request 136 | * __request_type__: Format of the request body{{if .Api.Response.Suffix}} 137 | * __response_type__: Format of the response (to be used in url suffix){{end}} 138 | 139 | ### Request body information 140 | 141 | Set __request_type__ in options to modify the body accordingly 142 | 143 | ##### RAW request 144 | 145 | When the value is set to __raw__, don't modify the body at all. 146 | 147 | ```php 148 | $body = 'username=pksunkara'; 149 | // >>> 'username=pksunkara' 150 | ``` 151 | {{if .Api.Request.Formats.Form}} 152 | ##### FORM request 153 | 154 | When the value is set to __form__, urlencode the body. 155 | 156 | ```php 157 | $body = array('user' => 'pksunkara'); 158 | // >>> 'user=pksunkara' 159 | ``` 160 | {{end}}{{if .Api.Request.Formats.Json}} 161 | ##### JSON request 162 | 163 | When the value is set to __json__, JSON encode the body. 164 | 165 | ```php 166 | $body = array('user' => 'pksunkara'); 167 | // >>> '{"user": "pksunkara"}' 168 | ``` 169 | {{end}}{{with $data := .}}{{range .Api.Classes}} 170 | ### {{(index $data.Doc .Name).Title}} api 171 | 172 | {{(index $data.Doc .Name).Desc}}{{with .Args}} 173 | 174 | The following arguments are required: 175 | {{end}}{{with $class := .}}{{range .Args}} 176 | * __{{.}}__: {{(index ((index $data.Doc $class.Name).Args) .).Desc}}{{end}} 177 | 178 | ```php 179 | ${{call $data.Fnc.camelizeDownFirst $class.Name}} = $client->{{call $data.Fnc.camelizeDownFirst $class.Name}}({{call $data.Fnc.prnt.php .Args ((index $data.Doc $class.Name).Args) ", " false}}); 180 | ``` 181 | {{range $class.Functions}} 182 | ##### {{(index ((index $data.Doc $class.Name).Functions) .Name).Title}} ({{call $data.Fnc.upper (or .Method "get")}} {{.Path}}) 183 | 184 | {{(index ((index $data.Doc $class.Name).Functions) .Name).Desc}}{{with .Params}} 185 | 186 | The following arguments are required: 187 | {{end}}{{with $method := .}}{{range .Params}}{{if .Required}} 188 | * __{{.Name}}__: {{(index ((index ((index $data.Doc $class.Name).Functions) $method.Name).Params) .Name).Desc}}{{end}}{{end}}{{end}} 189 | 190 | ```php 191 | $response = ${{call $data.Fnc.camelizeDownFirst $class.Name}}->{{call $data.Fnc.camelizeDownFirst .Name}}({{call $data.Fnc.prnt.php .Params ((index ((index $data.Doc $class.Name).Functions) .Name).Params) ", " true}}$options); 192 | ``` 193 | {{end}}{{end}}{{end}}{{end}} 194 | ## Contributors 195 | Here is a list of [Contributors](https://{{.Pkg.Git.Site}}/{{.Pkg.Git.User}}/{{.Pkg.Git.Name}}-php/contributors) 196 | 197 | ### TODO 198 | 199 | ## License 200 | {{.Pkg.License}} 201 | 202 | ## Bug Reports 203 | Report [here](https://{{.Pkg.Git.Site}}/{{.Pkg.Git.User}}/{{.Pkg.Git.Name}}-php/issues). 204 | 205 | ## Contact 206 | {{.Pkg.Author.Name}} ({{.Pkg.Author.Email}}) 207 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # alpaca [![Build Status](https://travis-ci.org/pksunkara/alpaca.svg?branch=master)](https://travis-ci.org/pksunkara/alpaca) [![Gitter chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/pksunkara/alpaca) 2 | 3 | Api Libraries Powered And Created by Alpaca 4 | 5 | --- 6 | 7 | Tired of maintaining API libraries in different languages for your website API? _This is for you_ 8 | 9 | You have an API for your website but no API libraries for whatever reason? _This is for you_ 10 | 11 | You are planning to build an API for your website and develop API libraries? _This is for you_ 12 | 13 | --- 14 | 15 | You define your API according to the format given below, __alpaca__ builds the API libraries along with their documentation. All you have to do is publishing them to their respective package managers. 16 | 17 | Join us at [gitter](https://gitter.im/pksunkara/alpaca) if you need any help. 18 | 19 | ## Installation 20 | 21 | You can download the binaries (v0.2.1) 22 | 23 | * Architecture i386 [ [linux](https://dl.bintray.com//content/pksunkara/utils/alpaca_0.2.1_linux_386.tar.gz?direct) / [windows](https://dl.bintray.com//content/pksunkara/utils/alpaca_0.2.1_windows_386.zip?direct) / [darwin](https://dl.bintray.com//content/pksunkara/utils/alpaca_0.2.1_darwin_386.zip?direct) / [freebsd](https://dl.bintray.com//content/pksunkara/utils/alpaca_0.2.1_freebsd_386.zip?direct) / [openbsd](https://dl.bintray.com//content/pksunkara/utils/alpaca_0.2.1_openbsd_386.zip?direct) / [netbsd](https://dl.bintray.com//content/pksunkara/utils/alpaca_0.2.1_netbsd_386.zip?direct) / [plan9](https://dl.bintray.com//content/pksunkara/utils/alpaca_0.2.1_plan9_386.zip?direct) ] 24 | * Architecture amd64 [ [linux](https://dl.bintray.com//content/pksunkara/utils/alpaca_0.2.1_linux_amd64.tar.gz?direct) / [windows](https://dl.bintray.com//content/pksunkara/utils/alpaca_0.2.1_windows_amd64.zip?direct) / [darwin](https://dl.bintray.com//content/pksunkara/utils/alpaca_0.2.1_darwin_amd64.zip?direct) / [freebsd](https://dl.bintray.com//content/pksunkara/utils/alpaca_0.2.1_freebsd_amd64.zip?direct) / [openbsd](https://dl.bintray.com//content/pksunkara/utils/alpaca_0.2.1_openbsd_amd64.zip?direct) / [netbsd](https://dl.bintray.com//content/pksunkara/utils/alpaca_0.2.1_netbsd_amd64.zip?direct) ] 25 | * Architecture arm [ [linux](https://dl.bintray.com//content/pksunkara/utils/alpaca_0.2.1_linux_arm.tar.gz?direct) / [freebsd](https://dl.bintray.com//content/pksunkara/utils/alpaca_0.2.1_freebsd_arm.zip?direct) / [netbsd](https://dl.bintray.com//content/pksunkara/utils/alpaca_0.2.1_netbsd_arm.zip?direct) ] 26 | 27 | Or by using deb packages (v0.2.1) 28 | 29 | * [ [i386](https://dl.bintray.com//content/pksunkara/utils/alpaca_0.2.1_i386.deb?direct) / [amd64](https://dl.bintray.com//content/pksunkara/utils/alpaca_0.2.1_amd64.deb?direct) / [armhf](https://dl.bintray.com//content/pksunkara/utils/alpaca_0.2.1_armhf.deb?direct) ] 30 | 31 | Or by using golang (v1.2) 32 | 33 | ```bash 34 | # Clone the project into your golang workspace 35 | $ git clone git://github.com/pksunkara/alpaca 36 | $ cd alpaca 37 | 38 | # Install program 39 | $ make && make install 40 | ``` 41 | 42 | ## Examples 43 | 44 | You can find some api definitions in the [examples](https://github.com/pksunkara/alpaca/tree/master/examples) directory. The api libraries generated are at [https://github.com/alpaca-api](https://github.com/alpaca-api) 45 | 46 | Completed api definitions are [buffer](https://github.com/pksunkara/alpaca/tree/master/examples/buffer). 47 | 48 | ## Usage 49 | 50 | ```bash 51 | $ alpaca 52 | ``` 53 | 54 | The path here should be a directory with `api.json`, `pkg.json`, `doc.json` 55 | 56 | ``` 57 | Usage: 58 | alpaca [options] 59 | 60 | Application Options: 61 | -v, --version Show version information 62 | 63 | Language Options: 64 | --no-php Do not write php library 65 | --no-python Do not write python library 66 | --no-ruby Do not write ruby library 67 | --no-node Do not write node library 68 | 69 | Help Options: 70 | -h, --help Show this help message 71 | ``` 72 | 73 | _Please remove the comments when actually using these json files_ 74 | 75 | #### pkg.json 76 | 77 | All the following fields are required unless mentioned. 78 | 79 | ```js 80 | { 81 | "name": "Example", // Name of the api (also used as class name for the library) 82 | "package": "example-alpaca", // Name of the package 83 | "version": "0.1.0", // Version of the package 84 | "url": "https://exampleapp.com", // URL of the api 85 | "keywords": ["alpaca", "exampleapp", "api"], // Keywords for the package 86 | "official": false, // Are the api libraries official? 87 | "author": { 88 | "name": "Pavan Kumar Sunkara", // Name of the package author 89 | "email": "pavan.sss1991@gmail.com", // Email of the package author 90 | "url": "http://github.com/pksunkara" // URL of the package author 91 | }, 92 | "git": { // Used in the package definition 93 | "site": "github.com", // Name of the git website 94 | "user": "alpaca-api", // Username of the git website 95 | "name": "buffer" // Namespace of the git repositories 96 | }, 97 | "license": "MIT", // License of the package 98 | "php": { // Required only if creating php api lib 99 | "vendor": "pksunkara" // Packagist vendor name for the package 100 | }, 101 | "python": { // Required only if creating python api lib 102 | "license": "MIT License" // Classifier of the license used for the module 103 | } 104 | } 105 | ``` 106 | 107 | #### api.json 108 | 109 | All the following fields are required unless mentioned. 110 | 111 | ```js 112 | { 113 | "base": "https://exampleapp.com", // Base URL of the api 114 | "base_as_arg": true, // Force Base URL to be an argument in generated clients [optional] (default: false) 115 | "version": "v1", // Default version for the api (https://api.example.com{/version}/users) [optional] 116 | "no_verify_ssl": true, // Do not verify SSL cert [optional] (default: false) 117 | "authorization": { // Authorization strategies 118 | "need_auth": true, // Authentication is compulsory [optional] (default: false) 119 | "basic" : true, // Basic authentication [optional] (default: false) 120 | "header": true, // Token in authorization header [optional] (default: false) 121 | "header_prefix": "oompaloompa", // The first word in header if using token auth header [optional] (default: token) 122 | "oauth" : true // OAUTH authorization [optional] (default: false) 123 | }, 124 | "request": { // Settings for requests to the api 125 | "formats": { // Format of the request body 126 | "default": "form", // Default format for the request body [optional] (default: raw) 127 | "form": true, // Support form-url-encoded? [optional] (default: false) 128 | "json": true // Support json? [optional] (default: false) 129 | } 130 | }, 131 | "response": { // Settings for responses from the api 132 | "suffix": true, // Should the urls be suffixed with response format? [optional] (default: false) 133 | "formats": { // Format of the response body 134 | "default": "json", // Default response format. Used when 'suffix' is 'true' 135 | "html": true, // Support html? [optional] (default: false) 136 | "json": true // Support json? [optional] (default: false) 137 | } 138 | }, 139 | "error": { // Required if response format is 'json' 140 | "message": "error" // The field to be used from the response body for error message 141 | }, 142 | "class": { // The classes for the api 143 | "users": { // Name of a class of the api 144 | "args": ["login"], // Arguments required for the api class [optional] 145 | "profile": { // Name of a method of the api 146 | "path": "/users/:login/:type", // URL of the api method 147 | "method": "post", // HTTP method of the api method [optional] (default: get) 148 | "params": [ // Parameters for the api method [optional] 149 | { 150 | "name": "type", // Name of the parameter 151 | "required": true, // The parameter will become an argument of api method [optional] (default: false) 152 | "url_use": true // This parameter is only used to build url [optional] (default: false) 153 | }, 154 | { 155 | "name": "bio", 156 | "required": true 157 | } 158 | ] 159 | } 160 | } 161 | } 162 | } 163 | ``` 164 | 165 | #### doc.json 166 | 167 | The following is filled according to the entries in `api.json` 168 | 169 | ```js 170 | { 171 | "users": { // Name of a class of the api 172 | "title": "Users", // Title of the api class 173 | "desc": "Returns user api instance", // Description of the api class 174 | "args": { // Arguments of the api class 175 | "login": { // Name of the argument 176 | "desc": "Username of the user", // Description of the argument 177 | "value": "pksunkara" // Value of the argument in docs 178 | } 179 | }, 180 | "profile": { // Name of a method of the api 181 | "title": "Edit profile", // Title of the api method 182 | "desc": "Edit the user's profile", // Description of the api method 183 | "params": { // Parameter of the api class 184 | "bio": { // Name of the parameter 185 | "desc": "Short bio in profile", // Description of the parameter 186 | "value": "I am awesome!" // Value of the parameter in docs 187 | }, 188 | "type": { 189 | "desc": "Circle of the profile", 190 | "value": "friends" 191 | } 192 | } 193 | } 194 | } 195 | } 196 | ``` 197 | 198 | ### Request formats 199 | 200 | Supported request formats are `raw`, `form`, `json`. The format `raw` is always true. 201 | 202 | This means, the `body` set in the options when calling an API method will be able to be encoded according to the respective `request_type` 203 | 204 | __If set to `raw`, body is not modified at all__ 205 | 206 | ### Response formats 207 | 208 | Supported response formats are `html`, `json`. 209 | 210 | ### Authorization strategies 211 | 212 | Supported are `basic`, `header`, `oauth` 213 | 214 | ### Language Versions 215 | 216 | Supported programming language versions are: 217 | 218 | | Language | V | E | R | S | I | O | N | 219 | |----------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:| 220 | | node | 0.8 | 0.9 | 0.10 | 0.11 | 0.12 | | | 221 | | php | 5.4 | 5.5 | | | | | | 222 | | python | 2.6 | 2.7 | 3.2 | 3.3 | | | | 223 | | ruby | 1.8.7 | 1.9.1 | 1.9.2 | 1.9.3 | 2.0.0 | 2.1.0 | 2.1.1 | 224 | 225 | ### Package Managers 226 | 227 | | Language | Package Manager | 228 | |----------|----------------------------------------------------| 229 | | node | [https://npmjs.org](https://npmjs.org) | 230 | | php | [https://packagist.org](https://packagist.org) | 231 | | python | [https://pypi.python.org](https://pypi.python.org) | 232 | | ruby | [https://rubygems.org](https://rubygems.org) | 233 | 234 | ## Testing 235 | 236 | Check [here](https://github.com/pksunkara/alpaca/tree/testing) to learn about testing. 237 | 238 | ## Contributors 239 | 240 | Here is a list of [Contributors](https://github.com/pksunkara/alpaca/contributors) 241 | 242 | __I accept pull requests and guarantee a reply back within a day__ 243 | 244 | ### TODO 245 | 246 | You get internet points for pull requesting the following features. 247 | 248 | ##### Responses 249 | 250 | * [Add support for XML](https://github.com/pksunkara/alpaca/issues/36) 251 | * [Add support for CSV](https://github.com/pksunkara/alpaca/issues/36) 252 | 253 | ##### Requests 254 | 255 | * HTTP Method Overloading 256 | * What about file uploads? 257 | 258 | ##### API 259 | 260 | * Check returned status code 261 | * Special case for 204:true and 404:false 262 | 263 | ##### Libraries 264 | 265 | * Pagination support 266 | * Classes inside classes (so on..) 267 | * Validations for params/body in api methods 268 | * Allow customization of errors 269 | * Tests for libraries (lots and lots of tests) 270 | 271 | ##### Readme 272 | 273 | * [Optional params available](https://github.com/pksunkara/alpaca/issues/57) 274 | * Return types of api calls 275 | 276 | ##### Comments 277 | 278 | * The descriptions should be wrapped 279 | * Align @param descriptions 280 | 281 | ##### Formats 282 | 283 | * [Support YAML](https://github.com/pksunkara/alpaca/issues/63) 284 | * [Support API blueprint](https://github.com/pksunkara/alpaca/issues/56) 285 | * [Support Swagger](https://github.com/pksunkara/alpaca/issues/61) 286 | * [Support WADL](https://github.com/pksunkara/alpaca/issues/13) 287 | * [Support JSON Schema](https://github.com/pksunkara/alpaca/issues/17) 288 | * [Support RAML](https://github.com/pksunkara/alpaca/issues/54) 289 | 290 | ##### Languages 291 | 292 | * [Support Java](https://github.com/pksunkara/alpaca/issues/11) 293 | * [Support Go](https://github.com/pksunkara/alpaca/issues/9) 294 | * [Support Clojure](https://github.com/pksunkara/alpaca/issues/49) 295 | * [Support Rust](https://github.com/pksunkara/alpaca/issues/62) 296 | * [Support Swift](https://github.com/pksunkara/alpaca/issues/64) 297 | * Support C, C++, Perl, Scala, C#, Erlang, Lua, Haskell, D, Julia, Groovy 298 | * Build cli tool for APIs (bash? python? go?) 299 | 300 | ### Support Projects 301 | 302 | Alternatively, you can write your own converter from `alpaca.json` to the following 303 | 304 | * Convert into API Blueprint 305 | * Convert into Swagger 306 | 307 | ## License 308 | 309 | MIT/X11 310 | 311 | ## Bug Reports 312 | 313 | Report [here](http://github.com/pksunkara/alpaca/issues). __Guaranteed reply within a day__. 314 | 315 | ## Contact 316 | 317 | Pavan Kumar Sunkara (pavan.sss1991@gmail.com) 318 | 319 | Follow me on [github](https://github.com/users/follow?target=pksunkara), [twitter](http://twitter.com/pksunkara) 320 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License, version 2.0 2 | 3 | 1. Definitions 4 | 5 | 1.1. "Contributor" 6 | 7 | means each individual or legal entity that creates, contributes to the 8 | creation of, or owns Covered Software. 9 | 10 | 1.2. "Contributor Version" 11 | 12 | means the combination of the Contributions of others (if any) used by a 13 | Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | 17 | means Covered Software of a particular Contributor. 18 | 19 | 1.4. "Covered Software" 20 | 21 | means Source Code Form to which the initial Contributor has attached the 22 | notice in Exhibit A, the Executable Form of such Source Code Form, and 23 | Modifications of such Source Code Form, in each case including portions 24 | thereof. 25 | 26 | 1.5. "Incompatible With Secondary Licenses" 27 | means 28 | 29 | a. that the initial Contributor has attached the notice described in 30 | Exhibit B to the Covered Software; or 31 | 32 | b. that the Covered Software was made available under the terms of 33 | version 1.1 or earlier of the License, but not also under the terms of 34 | a Secondary License. 35 | 36 | 1.6. "Executable Form" 37 | 38 | means any form of the work other than Source Code Form. 39 | 40 | 1.7. "Larger Work" 41 | 42 | means a work that combines Covered Software with other material, in a 43 | separate file or files, that is not Covered Software. 44 | 45 | 1.8. "License" 46 | 47 | means this document. 48 | 49 | 1.9. "Licensable" 50 | 51 | means having the right to grant, to the maximum extent possible, whether 52 | at the time of the initial grant or subsequently, any and all of the 53 | rights conveyed by this License. 54 | 55 | 1.10. "Modifications" 56 | 57 | means any of the following: 58 | 59 | a. any file in Source Code Form that results from an addition to, 60 | deletion from, or modification of the contents of Covered Software; or 61 | 62 | b. any new file in Source Code Form that contains any Covered Software. 63 | 64 | 1.11. "Patent Claims" of a Contributor 65 | 66 | means any patent claim(s), including without limitation, method, 67 | process, and apparatus claims, in any patent Licensable by such 68 | Contributor that would be infringed, but for the grant of the License, 69 | by the making, using, selling, offering for sale, having made, import, 70 | or transfer of either its Contributions or its Contributor Version. 71 | 72 | 1.12. "Secondary License" 73 | 74 | means either the GNU General Public License, Version 2.0, the GNU Lesser 75 | General Public License, Version 2.1, the GNU Affero General Public 76 | License, Version 3.0, or any later versions of those licenses. 77 | 78 | 1.13. "Source Code Form" 79 | 80 | means the form of the work preferred for making modifications. 81 | 82 | 1.14. "You" (or "Your") 83 | 84 | means an individual or a legal entity exercising rights under this 85 | License. For legal entities, "You" includes any entity that controls, is 86 | controlled by, or is under common control with You. For purposes of this 87 | definition, "control" means (a) the power, direct or indirect, to cause 88 | the direction or management of such entity, whether by contract or 89 | otherwise, or (b) ownership of more than fifty percent (50%) of the 90 | outstanding shares or beneficial ownership of such entity. 91 | 92 | 93 | 2. License Grants and Conditions 94 | 95 | 2.1. Grants 96 | 97 | Each Contributor hereby grants You a world-wide, royalty-free, 98 | non-exclusive license: 99 | 100 | a. under intellectual property rights (other than patent or trademark) 101 | Licensable by such Contributor to use, reproduce, make available, 102 | modify, display, perform, distribute, and otherwise exploit its 103 | Contributions, either on an unmodified basis, with Modifications, or 104 | as part of a Larger Work; and 105 | 106 | b. under Patent Claims of such Contributor to make, use, sell, offer for 107 | sale, have made, import, and otherwise transfer either its 108 | Contributions or its Contributor Version. 109 | 110 | 2.2. Effective Date 111 | 112 | The licenses granted in Section 2.1 with respect to any Contribution 113 | become effective for each Contribution on the date the Contributor first 114 | distributes such Contribution. 115 | 116 | 2.3. Limitations on Grant Scope 117 | 118 | The licenses granted in this Section 2 are the only rights granted under 119 | this License. No additional rights or licenses will be implied from the 120 | distribution or licensing of Covered Software under this License. 121 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 122 | Contributor: 123 | 124 | a. for any code that a Contributor has removed from Covered Software; or 125 | 126 | b. for infringements caused by: (i) Your and any other third party's 127 | modifications of Covered Software, or (ii) the combination of its 128 | Contributions with other software (except as part of its Contributor 129 | Version); or 130 | 131 | c. under Patent Claims infringed by Covered Software in the absence of 132 | its Contributions. 133 | 134 | This License does not grant any rights in the trademarks, service marks, 135 | or logos of any Contributor (except as may be necessary to comply with 136 | the notice requirements in Section 3.4). 137 | 138 | 2.4. Subsequent Licenses 139 | 140 | No Contributor makes additional grants as a result of Your choice to 141 | distribute the Covered Software under a subsequent version of this 142 | License (see Section 10.2) or under the terms of a Secondary License (if 143 | permitted under the terms of Section 3.3). 144 | 145 | 2.5. Representation 146 | 147 | Each Contributor represents that the Contributor believes its 148 | Contributions are its original creation(s) or it has sufficient rights to 149 | grant the rights to its Contributions conveyed by this License. 150 | 151 | 2.6. Fair Use 152 | 153 | This License is not intended to limit any rights You have under 154 | applicable copyright doctrines of fair use, fair dealing, or other 155 | equivalents. 156 | 157 | 2.7. Conditions 158 | 159 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in 160 | Section 2.1. 161 | 162 | 163 | 3. Responsibilities 164 | 165 | 3.1. Distribution of Source Form 166 | 167 | All distribution of Covered Software in Source Code Form, including any 168 | Modifications that You create or to which You contribute, must be under 169 | the terms of this License. You must inform recipients that the Source 170 | Code Form of the Covered Software is governed by the terms of this 171 | License, and how they can obtain a copy of this License. You may not 172 | attempt to alter or restrict the recipients' rights in the Source Code 173 | Form. 174 | 175 | 3.2. Distribution of Executable Form 176 | 177 | If You distribute Covered Software in Executable Form then: 178 | 179 | a. such Covered Software must also be made available in Source Code Form, 180 | as described in Section 3.1, and You must inform recipients of the 181 | Executable Form how they can obtain a copy of such Source Code Form by 182 | reasonable means in a timely manner, at a charge no more than the cost 183 | of distribution to the recipient; and 184 | 185 | b. You may distribute such Executable Form under the terms of this 186 | License, or sublicense it under different terms, provided that the 187 | license for the Executable Form does not attempt to limit or alter the 188 | recipients' rights in the Source Code Form under this License. 189 | 190 | 3.3. Distribution of a Larger Work 191 | 192 | You may create and distribute a Larger Work under terms of Your choice, 193 | provided that You also comply with the requirements of this License for 194 | the Covered Software. If the Larger Work is a combination of Covered 195 | Software with a work governed by one or more Secondary Licenses, and the 196 | Covered Software is not Incompatible With Secondary Licenses, this 197 | License permits You to additionally distribute such Covered Software 198 | under the terms of such Secondary License(s), so that the recipient of 199 | the Larger Work may, at their option, further distribute the Covered 200 | Software under the terms of either this License or such Secondary 201 | License(s). 202 | 203 | 3.4. Notices 204 | 205 | You may not remove or alter the substance of any license notices 206 | (including copyright notices, patent notices, disclaimers of warranty, or 207 | limitations of liability) contained within the Source Code Form of the 208 | Covered Software, except that You may alter any license notices to the 209 | extent required to remedy known factual inaccuracies. 210 | 211 | 3.5. Application of Additional Terms 212 | 213 | You may choose to offer, and to charge a fee for, warranty, support, 214 | indemnity or liability obligations to one or more recipients of Covered 215 | Software. However, You may do so only on Your own behalf, and not on 216 | behalf of any Contributor. You must make it absolutely clear that any 217 | such warranty, support, indemnity, or liability obligation is offered by 218 | You alone, and You hereby agree to indemnify every Contributor for any 219 | liability incurred by such Contributor as a result of warranty, support, 220 | indemnity or liability terms You offer. You may include additional 221 | disclaimers of warranty and limitations of liability specific to any 222 | jurisdiction. 223 | 224 | 4. Inability to Comply Due to Statute or Regulation 225 | 226 | If it is impossible for You to comply with any of the terms of this License 227 | with respect to some or all of the Covered Software due to statute, 228 | judicial order, or regulation then You must: (a) comply with the terms of 229 | this License to the maximum extent possible; and (b) describe the 230 | limitations and the code they affect. Such description must be placed in a 231 | text file included with all distributions of the Covered Software under 232 | this License. Except to the extent prohibited by statute or regulation, 233 | such description must be sufficiently detailed for a recipient of ordinary 234 | skill to be able to understand it. 235 | 236 | 5. Termination 237 | 238 | 5.1. The rights granted under this License will terminate automatically if You 239 | fail to comply with any of its terms. However, if You become compliant, 240 | then the rights granted under this License from a particular Contributor 241 | are reinstated (a) provisionally, unless and until such Contributor 242 | explicitly and finally terminates Your grants, and (b) on an ongoing 243 | basis, if such Contributor fails to notify You of the non-compliance by 244 | some reasonable means prior to 60 days after You have come back into 245 | compliance. Moreover, Your grants from a particular Contributor are 246 | reinstated on an ongoing basis if such Contributor notifies You of the 247 | non-compliance by some reasonable means, this is the first time You have 248 | received notice of non-compliance with this License from such 249 | Contributor, and You become compliant prior to 30 days after Your receipt 250 | of the notice. 251 | 252 | 5.2. If You initiate litigation against any entity by asserting a patent 253 | infringement claim (excluding declaratory judgment actions, 254 | counter-claims, and cross-claims) alleging that a Contributor Version 255 | directly or indirectly infringes any patent, then the rights granted to 256 | You by any and all Contributors for the Covered Software under Section 257 | 2.1 of this License shall terminate. 258 | 259 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user 260 | license agreements (excluding distributors and resellers) which have been 261 | validly granted by You or Your distributors under this License prior to 262 | termination shall survive termination. 263 | 264 | 6. Disclaimer of Warranty 265 | 266 | Covered Software is provided under this License on an "as is" basis, 267 | without warranty of any kind, either expressed, implied, or statutory, 268 | including, without limitation, warranties that the Covered Software is free 269 | of defects, merchantable, fit for a particular purpose or non-infringing. 270 | The entire risk as to the quality and performance of the Covered Software 271 | is with You. Should any Covered Software prove defective in any respect, 272 | You (not any Contributor) assume the cost of any necessary servicing, 273 | repair, or correction. This disclaimer of warranty constitutes an essential 274 | part of this License. No use of any Covered Software is authorized under 275 | this License except under this disclaimer. 276 | 277 | 7. Limitation of Liability 278 | 279 | Under no circumstances and under no legal theory, whether tort (including 280 | negligence), contract, or otherwise, shall any Contributor, or anyone who 281 | distributes Covered Software as permitted above, be liable to You for any 282 | direct, indirect, special, incidental, or consequential damages of any 283 | character including, without limitation, damages for lost profits, loss of 284 | goodwill, work stoppage, computer failure or malfunction, or any and all 285 | other commercial damages or losses, even if such party shall have been 286 | informed of the possibility of such damages. This limitation of liability 287 | shall not apply to liability for death or personal injury resulting from 288 | such party's negligence to the extent applicable law prohibits such 289 | limitation. Some jurisdictions do not allow the exclusion or limitation of 290 | incidental or consequential damages, so this exclusion and limitation may 291 | not apply to You. 292 | 293 | 8. Litigation 294 | 295 | Any litigation relating to this License may be brought only in the courts 296 | of a jurisdiction where the defendant maintains its principal place of 297 | business and such litigation shall be governed by laws of that 298 | jurisdiction, without reference to its conflict-of-law provisions. Nothing 299 | in this Section shall prevent a party's ability to bring cross-claims or 300 | counter-claims. 301 | 302 | 9. Miscellaneous 303 | 304 | This License represents the complete agreement concerning the subject 305 | matter hereof. If any provision of this License is held to be 306 | unenforceable, such provision shall be reformed only to the extent 307 | necessary to make it enforceable. Any law or regulation which provides that 308 | the language of a contract shall be construed against the drafter shall not 309 | be used to construe this License against a Contributor. 310 | 311 | 312 | 10. Versions of the License 313 | 314 | 10.1. New Versions 315 | 316 | Mozilla Foundation is the license steward. Except as provided in Section 317 | 10.3, no one other than the license steward has the right to modify or 318 | publish new versions of this License. Each version will be given a 319 | distinguishing version number. 320 | 321 | 10.2. Effect of New Versions 322 | 323 | You may distribute the Covered Software under the terms of the version 324 | of the License under which You originally received the Covered Software, 325 | or under the terms of any subsequent version published by the license 326 | steward. 327 | 328 | 10.3. Modified Versions 329 | 330 | If you create software not governed by this License, and you want to 331 | create a new license for such software, you may create and use a 332 | modified version of this License if you rename the license and remove 333 | any references to the name of the license steward (except to note that 334 | such modified license differs from this License). 335 | 336 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 337 | Licenses If You choose to distribute Source Code Form that is 338 | Incompatible With Secondary Licenses under the terms of this version of 339 | the License, the notice described in Exhibit B of this License must be 340 | attached. 341 | 342 | Exhibit A - Source Code Form License Notice 343 | 344 | This Source Code Form is subject to the 345 | terms of the Mozilla Public License, v. 346 | 2.0. If a copy of the MPL was not 347 | distributed with this file, You can 348 | obtain one at 349 | http://mozilla.org/MPL/2.0/. 350 | 351 | If it is not possible or desirable to put the notice in a particular file, 352 | then You may include the notice in a location (such as a LICENSE file in a 353 | relevant directory) where a recipient would be likely to look for such a 354 | notice. 355 | 356 | You may add additional accurate notices of copyright ownership. 357 | 358 | Exhibit B - "Incompatible With Secondary Licenses" Notice 359 | 360 | This Source Code Form is "Incompatible 361 | With Secondary Licenses", as defined by 362 | the Mozilla Public License, v. 2.0. 363 | --------------------------------------------------------------------------------