Permalink
Browse files

source2swagger, complete with tests

  • Loading branch information...
0 parents commit cb3c61486963db6cc1377be00ea58513a3f0c4bd @solso committed Feb 26, 2012
22 LICENCE
@@ -0,0 +1,22 @@
+The MIT License
+
+Copyright (c) 2012 3scale networks S.L.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
@@ -0,0 +1,9 @@
+require 'rake/testtask'
+
+desc 'Run all unit tests'
+
+Rake::TestTask.new(:test) do |task|
+ task.test_files = FileList['test/unit/**/*_test.rb']
+ task.verbose = true
+end
+
@@ -0,0 +1,75 @@
+#! /usr/bin/env ruby
+
+$:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
+
+require "ruby-debug"
+require 'optparse'
+require "json"
+require "swagger_hash"
+require "swagger_reader"
+
+opt_input = nil
+opt_output = nil
+opt_comment = nil
+opt_extension = nil
+
+parser = OptionParser.new do |parser|
+ parser.on('-i','--input PATH', 'Directory of the input source code') do |value|
+ opt_input = value
+ end
+
+ parser.on('-e','--ext ("rb"|"c"|"js"|"py")', 'File extension of the source code') do |value|
+ opt_extension = value
+ end
+
+ parser.on('-c','--comment ("##~"|"//~")','Comment tag used to write docs') do |value|
+ opt_comment = value
+ end
+
+ parser.on('-o','--output PATH','Directory where the json output will be saved (optional)') do |value|
+ opt_output = value
+ end
+
+ parser.parse!
+end
+
+unless opt_extension and opt_input and opt_comment
+ puts parser
+ abort
+end
+
+def save(results, output_path)
+ results.each do |k,v|
+ puts " Saving API #{k} to #{output_path}/#{k}.json"
+ File.new("#{output_path}/#{k}.json","w").puts v
+ end
+end
+
+def print(results)
+ cont = 1
+ results.each do |k,v|
+ puts "API: #{cont}, #{k}\n"
+ puts v
+ cont=cont+1
+ end
+end
+
+def run(input, extension, comment, output)
+ reader = SwaggerReader.new
+ $_swaggerhash = Hash.new
+
+ code = reader.analyze_all_files(input,extension,comment)
+ results = reader.process_code(code) unless code.nil? || code.empty?
+
+ puts "Swagger API in #{ARGV[0]}/**/*.#{ARGV[1]}: #{results.size} API\n"
+ if output.nil?
+ print(results)
+ else
+ save(results,output)
+ end
+ puts "Done!"
+
+end
+
+
+run(opt_input, opt_extension, opt_comment, opt_output)
@@ -0,0 +1,67 @@
+
+$_swaggerhash = Hash.new
+
+class SwaggerHash < Hash
+
+ KEEP_METHODS = %w{default []= each merge! debugger puts __id__ __send__ instance_eval == equal? initialize delegate caller object_id raise class [] to_json inspect to_s nil?}
+ ((private_instance_methods + instance_methods).map(&:to_sym) - KEEP_METHODS.map(&:to_sym)).each{|m| undef_method(m) }
+
+ def self.namespace(name)
+ @current_name = name
+ if $_swaggerhash[name].nil?
+ $_swaggerhash[name] = SwaggerHash.new
+ end
+ return $_swaggerhash[name]
+ end
+
+ def method_missing(method, *args, &block)
+
+ if method.to_s.match(/=$/)
+ self[method.to_s.gsub("=","").to_sym] = args.first
+ else
+ if self[method].nil?
+ if not method == :add
+ self[method] = SwaggerHash.new
+ else
+ if (args.nil? || args.empty?)
+ self[:_array] ||= Array.new
+ item = SwaggerHash.new
+ self[:_array] << item
+ return item
+ else
+ self[:_array] ||= Array.new
+ args.each do |item|
+ self[:_array] << item
+ end
+ end
+ end
+ end
+ return self[method]
+ end
+ end
+
+ def set(*args)
+ merge!(*args)
+ end
+
+ def to_hash
+ h2 = Hash.new
+ self.each do |k,v|
+ if v.class != SwaggerHash && v.class != Hash
+ h2[k] = v
+ else
+ if not (v[:_array].nil?)
+ v2 = []
+ v[:_array].each do |item|
+ v2 << item.to_hash
+ end
+ h2[k] = v2
+ else
+ h2[k] = v.to_hash
+ end
+ end
+ end
+ return h2
+ end
+
+end
@@ -0,0 +1,79 @@
+
+class SwaggerReader
+
+ #def analyze(line)
+ # return nil unless line.strip!.match(/^##~/)
+ # return line.gsub!("##~","").strip!
+ #end
+
+ def analyze_file(file, comment_str)
+
+ code = {:code => [], :line_number => [], :file =>[]}
+
+ File.open(file,"r") do |f|
+ line_number = 1
+ while (line = f.gets)
+ v = line.strip!.split(" ")
+ if !v.nil? && v.size > 0 && (v[0]==comment_str)
+ code[:code] << v[1..v.size].join(" ")
+ code[:file] << file
+ code[:line_number] << line_number
+ end
+ line_number = line_number + 1
+ end
+ end
+
+ return code
+
+ end
+
+ def analyze_all_files(base_path, file_extension, comment_str)
+
+ code = {:code => [], :line_number => [], :file =>[]}
+
+ files = Dir["#{base_path}/**/*.#{file_extension}"].sort
+
+ files.each do |file|
+ fcode = analyze_file(file,comment_str)
+ [:code, :line_number, :file].each do |lab|
+ code[lab] = code[lab] + fcode[lab]
+ end
+ end
+
+ return code
+
+ end
+
+ def process_code(code)
+
+ code[:code].size.times do |cont|
+ $_swaggerhash = Hash.new
+ begin
+ v = code[:code][0..cont]
+ v << "out = {:apis => []}"
+ v << "$_swaggerhash.each {|k,v| out[k] = v.to_hash}"
+ eval(v.join(";"))
+ rescue Exception => e
+ raise SwaggerReaderException, "Error parsing source files at #{code[:file][cont]}:#{code[:line_number][cont]}\n#{e.inspect}"
+ end
+ end
+ $_swaggerhash = Hash.new
+
+ code[:code] << "out = {:apis => []}"
+ code[:code] << "$_swaggerhash.each {|k,v| out[k] = v.to_hash}"
+
+ res = eval(code[:code].join(";"))
+
+ res.each do |k, v|
+ res[k] = v.to_hash
+ end
+
+ res
+ end
+
+end
+
+class SwaggerReaderException < Exception
+
+end
+
@@ -0,0 +1,27 @@
+{
+ "apis": [
+ {
+ "path": "/ping",
+ "format":"text",
+ "description": "Check the status to see if it's up and running",
+ "operations": [
+ {
+ "httpMethod":"GET",
+ "tags":["test"],
+ "nickname":"ping",
+ "deprecated":true,
+ "summary":"This operation is DEPRECATED. It returns the string \"that's getting old... pong \" if the API is up and running"
+ }
+ ],
+ "errorResponses":[
+ {
+ "reason":"API down",
+ "code":500
+ }
+ ]
+ }
+ ],
+ "basePath":"http://helloworld.3scale.net",
+ "swagrVersion":"0.1a",
+ "apiVersion":"1.0"
+}
@@ -0,0 +1,18 @@
+##~ sapi = SwaggerHash::namespace("sentiment")
+##~ sapi.basePath = "http://helloworld.3scale.net"
+##~ sapi.swagrVersion = "0.1a"
+##~ sapi.apiVersion = "1.0"
+##
+##~ a = sapi.apis.add
+##
+##~ a.set :path => "/ping", :format => "text"
+##~ a.description = "Check the status to see if it's up and running"
+##
+## declaring errors
+##
+##~ a.errorResponses.add :reason => "API down", :code => 500
+##
+##~ op = a.operations.add
+##~ op.set :httpMethod => "GET", :tags => ["test"], :nickname => "ping", :deprecated => true
+##~ op.summary = "This operation is DEPRECATED. It returns the string \"that's getting old... pong \" if the API is up and running"
+##
@@ -0,0 +1,86 @@
+{
+ "apis": [
+ {
+ "path": "/ping",
+ "format":"text",
+ "description": "Check the status to see if it's up and running",
+ "operations": [
+ {
+ "httpMethod":"GET",
+ "tags":["test"],
+ "nickname":"ping",
+ "deprecated":true,
+ "summary":"This operation is DEPRECATED. It returns the string \"that's getting old... pong \" if the API is up and running"
+ }
+ ],
+ "errorResponses":[
+ {
+ "reason":"API down",
+ "code":500
+ }
+ ]
+ },
+ {
+ "path":"/word/{word}",
+ "format":"json",
+ "description":"Access to the sentiment of a given word",
+ "operations": [
+ {
+ "parameters":[
+ {
+ "name":"word",
+ "description":"The word whose sentiment is returned",
+ "dataType":"string",
+ "allowMultiple":false,
+ "required":true,
+ "paramType":"path"
+ },
+ {
+ "name":"app_id",
+ "description":"Your access application id",
+ "dataType":"string",
+ "allowMultiple":false,
+ "required":true,
+ "paramType":"query"
+ },
+ {
+ "name":"app_key",
+ "description":"Your access application key",
+ "dataType":"string",
+ "allowMultiple":false,
+ "required":false,
+ "paramType":"query"
+ }
+ ],
+ "httpMethod":"GET",
+ "tags":["production"],
+ "nickname":"get_word",
+ "deprecated":false,
+ "summary":"Returns the sentiment values of a given word"
+ }
+ ],
+
+ "errorResponses":[
+ {
+ "reason":"failure to sanitize: \"input\"",
+ "code":422
+ },
+ {
+ "reason":"failure to sanitize: \"input\", returns empty set",
+ "code":422
+ },
+ {
+ "reason":"access denied, either your access credentials are incorrect or you are about the limits of your quota",
+ "code":403
+ },
+ {
+ "reason":"API down",
+ "code":500
+ }
+ ]
+ }
+ ],
+ "basePath":"http://helloworld.3scale.net",
+ "swagrVersion":"0.1a",
+ "apiVersion":"1.0"
+}
Oops, something went wrong.

0 comments on commit cb3c614

Please sign in to comment.