diff --git a/samples/server-generator/sinatra/SinatraServerGenerator.scala b/samples/server-generator/sinatra/SinatraServerGenerator.scala new file mode 100644 index 00000000000..10f7beab74c --- /dev/null +++ b/samples/server-generator/sinatra/SinatraServerGenerator.scala @@ -0,0 +1,76 @@ +/** + * Copyright 2012 Wordnik, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.wordnik.swagger.codegen.BasicScalaGenerator + +import com.wordnik.swagger.core._ + +import java.io.File + +import scala.collection.mutable.{ HashMap, ListBuffer } + +object SinatraServerGenerator extends BasicScalaGenerator { + def main(args: Array[String]) = generateClient(args) + + override def templateDir = "samples/server-generator/sinatra/templates" + + val outputFolder = "samples/server-generator/sinatra/output" + + // where to write generated code + override def destinationDir = outputFolder + "" + + override def modelPackage = Some("com.wordnik.client.model") + + // template used for apis + apiTemplateFiles ++= Map("api.mustache" -> ".rb") + + modelTemplateFiles.clear + + override def apiPackage = Some("lib") + + // supporting classes + override def supportingFiles = List( + ("README.md", outputFolder, "README.md"), + ("config.ru", outputFolder, "config.ru"), + ("MyApp.rb", outputFolder, "MyApp.rb"), + ("lib/Swaggering.rb", outputFolder + File.separator + "lib", "Swaggering.rb")) + + override def processApiMap(m: Map[String, AnyRef]): Map[String, AnyRef] = { + val mutable = scala.collection.mutable.Map() ++ m + + mutable.map(k => { + k._1 match { + // the scalatra templates like lower-case httpMethods + case e: String if (e == "httpMethod") => mutable += "httpMethod" -> k._2.toString.toLowerCase + + // convert path into ruby-ish syntax without basePart (i.e. /pet.{format}/{petId} => /:petId + case e: String if (e == "path") => { + val path = { + val arr = k._2.toString.split("/") + if (arr.length >= 2) { + mutable += "basePart" -> (arr.slice(2, arr.length).mkString("", "/", "")) + "/" + arr.slice(2, arr.length).mkString("", "/", "") + } else + k._2.toString + } + mutable += "path" -> path.replaceAll("\\{", ":").replaceAll("\\}", "") + } + case _ => + } + }) + mutable.toMap + } +} diff --git a/samples/server-generator/sinatra/templates/MyApp.rb b/samples/server-generator/sinatra/templates/MyApp.rb new file mode 100644 index 00000000000..d01cf69327d --- /dev/null +++ b/samples/server-generator/sinatra/templates/MyApp.rb @@ -0,0 +1,12 @@ +require './lib/swaggering' + +# only need to extend if you want special configuration! +class MyApp < Swaggering + self.configure do |config| + config.api_version = '0.2' + end +end + +require './lib/PetApi.rb' +require './lib/StoreApi.rb' +require './lib/UserApi.rb' diff --git a/samples/server-generator/sinatra/templates/README.md b/samples/server-generator/sinatra/templates/README.md new file mode 100644 index 00000000000..2774d951da2 --- /dev/null +++ b/samples/server-generator/sinatra/templates/README.md @@ -0,0 +1 @@ +## empty diff --git a/samples/server-generator/sinatra/templates/api.mustache b/samples/server-generator/sinatra/templates/api.mustache new file mode 100644 index 00000000000..4dede10ab79 --- /dev/null +++ b/samples/server-generator/sinatra/templates/api.mustache @@ -0,0 +1,57 @@ +require 'json' + +{{#operations}} +{{#operation}} + +MyApp.add_route('{{httpMethod}}', '{{path}}', { + "resourcePath" => "/{{name}}", + "summary" => "{{{summary}}}", + "nickname" => "{{nickname}}", + "responseClass" => "{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}void{{/returnType}}", + "endpoint" => "{{path}}", + "notes" => "{{{notes}}}", + "parameters" => [ + {{#queryParams}} + { + "name" => "{{paramName}}", + "description" => "{{description}}", + "dataType" => "{{swaggerDataType}}", + "paramType" => "query", + "allowMultiple" => {{allowMultiple}}, + "allowableValues" => "{{allowableValues}}", + {{#defaultValue}}"defaultValue" => {{{defaultValue}}}{{/defaultValue}} + }, + {{/queryParams}} + {{#pathParams}} + { + "name" => "{{paramName}}", + "description" => "{{description}}", + "dataType" => "{{swaggerDataType}}", + "paramType" => "path", + }, + {{/pathParams}} + {{#headerParams}} + { + "name" => "{{paramName}}", + "description" => "{{description}}", + "dataType" => "{{swaggerDataType}}", + "paramType" => "header", + }, + {{/headerParams}} + {{#bodyParams}} + { + "name" => "body", + "description" => "{{description}}", + "dataType" => "{{swaggerDataType}}", + "paramType" => "body", + } + {{/bodyParams}} + ]}) do + cross_origin + # the guts live here + + {"message" => "yes, it worked"}.to_json +end + +{{/operation}} +{{/operations}} diff --git a/samples/server-generator/sinatra/templates/config.ru b/samples/server-generator/sinatra/templates/config.ru new file mode 100644 index 00000000000..dd65689c94e --- /dev/null +++ b/samples/server-generator/sinatra/templates/config.ru @@ -0,0 +1,2 @@ +require './myapp' +run MyApp diff --git a/samples/server-generator/sinatra/templates/lib/Swaggering.rb b/samples/server-generator/sinatra/templates/lib/Swaggering.rb new file mode 100644 index 00000000000..79aca1da2c9 --- /dev/null +++ b/samples/server-generator/sinatra/templates/lib/Swaggering.rb @@ -0,0 +1,154 @@ +require 'json' +require 'sinatra/base' +require 'sinatra/cross_origin' + +class Configuration + attr_accessor :base_path, :api_version, :swagger_version, :format_specifier + + def initialize + @api_version = '1.0' + @base_path = 'http://localhost:4567' + @swagger_version = '1.1' + @format_specifier = ".json" + end +end + +class Swaggering < Sinatra::Base + register Sinatra::CrossOrigin + + @@routes = {} + @@configuration = Configuration.new + + attr_accessor :configuration + + def self.configure + get("/resources" + @@configuration.format_specifier) { + cross_origin + Swaggering.to_resource_listing + } + @@configuration ||= Configuration.new + yield(@@configuration) if block_given? + end + + def self.add_route(method, path, swag={}, opts={}, &block) + fullPath = swag["resourcePath"].to_s + @@configuration.format_specifier + path + + accepted = case method + when 'get' + get(fullPath, opts, &block) + true + when 'post' + post(fullPath, opts, &block) + true + when 'delete' + delete(fullPath, opts, &block) + true + when 'put' + put(fullPath, opts, &block) + true + else + false + end + + if accepted then + resourcePath = swag["resourcePath"].to_s + ops = @@routes[resourcePath] + if ops.nil? + ops = Array.new + @@routes.merge!(resourcePath => ops) + + get(resourcePath + @@configuration.format_specifier) do + cross_origin + Swaggering.to_api(resourcePath) + end + end + + swag.merge!("httpMethod" => method.to_s.upcase) + ops.push(swag) + end + end + + def self.to_resource_listing + apis = Array.new + (@@routes.keys).each do |key| + api = { + "path" => (key + ".{format}"), + "description" => "no description" + } + apis.push api + end + + resource = { + "apiVersion" => @@configuration.api_version, + "swaggerVersion" => @@configuration.swagger_version, + "apis" => apis + } + + resource.to_json + end + + def self.to_api(resourcePath) + apis = {} + models = [] + + @@routes[resourcePath].each do |route| + endpoint = route["endpoint"].gsub(/:(\w+)(\/?)/,'{\1}\2') + path = (resourcePath + ".{format}" + endpoint) + api = apis[path] + if api.nil? + api = {"path" => path, "description" => "description", "operations" => []} + apis.merge!(path => api) + end + + parameters = route["parameters"] + + unless parameters.nil? then + parameters.each do |param| + av_string = param["allowableValues"] + unless av_string.nil? + if av_string.count('[') > 0 + pattern = /^([A-Z]*)\[(.*)\]/ + match = pattern.match av_string + case match.to_a[1] + when "LIST" + allowables = match.to_a[2].split(',') + param["allowableValues"] = { + "valueType" => "LIST", + "values" => allowables + } + when "RANGE" + allowables = match.to_a[2].split(',') + param["allowableValues"] = { + "valueType" => "RANGE", + "min" => allowables[0], + "max" => allowables[1] + } + end + end + end + end + end + + op = { + "httpMethod" => route["httpMethod"], + "description" => route["summary"], + "responseClass" => route["responseClass"], + "notes" => route["notes"], + "nickname" => route["nickname"], + "summary" => route["summary"], + "parameters" => route["parameters"] + } + api["operations"].push(op) + end + + api_listing = { + "apiVersion" => @@configuration.api_version, + "swaggerVersion" => @@configuration.swagger_version, + "basePath" => @@configuration.base_path, + "resourcePath" => resourcePath, + "apis" => apis.values, + "models" => models + } + api_listing.to_json + end +end diff --git a/samples/server-generator/sinatra/templates/main.mustache b/samples/server-generator/sinatra/templates/main.mustache new file mode 100644 index 00000000000..038dc22cf9e --- /dev/null +++ b/samples/server-generator/sinatra/templates/main.mustache @@ -0,0 +1,42 @@ +require 'json' +require './lib/swaggering' +require 'sinatra/cross_origin' + +configure do + +end + + +# only need to extend if you want special configuration! +class MyApp < Swaggering + self.configure do |config| + config.api_version = '0.2' + end +end + +# add all your routes +{{#apis}} +{{#operations}} +MyApp.add_route('{{httpMethod}}', '{{path}}', { + "resourcePath" => "{{resourcePath}}", + "summary" => "{{{summary}}}", + "nickname" => "{{nickname}}", + "responseClass" => "{{responseClass}}", + "endpoint" => "{{name}}", + "notes" => "{{{notes}}}", + "parameters" => [{ + "name" => "status", + "description" => "Status values that need to be considered for filter", + "dataType" => "string", + "paramType" => "path", + "allowMultiple" => true, + "allowableValues" => "LIST[available,pending,sold]", + "defaultValue" => "available"}]}) do + cross_origin + # the guts live here + + {"message" => "yes, it worked"}.to_json +end + +{{/operations}} +{{/apis}}