apitest 0.1.9 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +61 -4
- data/app/assets/javascripts/apitest/application.coffee +33 -7
- data/app/assets/stylesheets/apitest/application.scss +2 -0
- data/app/controllers/apitest/apitest_controller.rb +13 -10
- data/app/views/apitest/apitest/show.slim +28 -8
- data/app/views/layouts/apitest/application.slim +1 -1
- data/lib/apitest.rb +12 -12
- data/lib/apitest/error.rb +35 -0
- data/lib/apitest/module_error.rb +11 -0
- data/lib/apitest/reader.rb +14 -0
- data/lib/apitest/service_error.rb +41 -0
- data/lib/apitest/version.rb +1 -1
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 573eef6c4af633b41ade5cf70edc2abb18676367
|
4
|
+
data.tar.gz: d128c020fe260b55e550d90b2e4c59089db4e63d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1b50589b5877b6f1c96d8bfc00d86962a96e3954d7da75e5667a34eb514d7e872a49372f25607b146a5d2cf430837575148f02585fd0e0c1bd7efbc8a0dc265
|
7
|
+
data.tar.gz: fcb4faabaad7d12e54d5ffb56320f721b3214a5b92ae35da169b25dbb6a9ad35f986dca6bfb6ab171460ee130cbd77b4790174e9f4e04b65dc12d89602b0053a
|
data/README.md
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# Apitest
|
2
|
-
- 通过在API controller
|
2
|
+
- 通过在API controller里配置常量,生成API文档
|
3
3
|
- 并附带一个测试界面,辅助API开发。
|
4
4
|
- 适用与API开发人员,客户端/前端开发人员交流验证文档。
|
5
|
-
-
|
5
|
+
- 目前无出参,需要提交后,得到。无法完全替代API文档
|
6
6
|
|
7
7
|
## Version
|
8
|
-
0.
|
8
|
+
0.2.0
|
9
9
|
|
10
10
|
## Installation
|
11
11
|
添加如下代码到 Gemfile:
|
@@ -137,14 +137,71 @@ Rails.application.routes.draw do
|
|
137
137
|
end
|
138
138
|
```
|
139
139
|
|
140
|
+
如果所有API有公共必填项,例如token,version,可如下配置
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
Rails.application.routes.draw do
|
144
|
+
|
145
|
+
apitest_for '/apitest' do
|
146
|
+
Apitest::public_required [ 'token' , 'version' ]
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
```
|
151
|
+
|
152
|
+
可以在指定APITEST中关闭公共必填项,比如登录API,可在具体方法的APIDOC中使用false设定
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
token:false
|
156
|
+
```
|
157
|
+
|
158
|
+
完整配置方法如下
|
159
|
+
```ruby
|
160
|
+
class Api::V1::LoginController < ApplicationController
|
161
|
+
|
162
|
+
APIDOC = {
|
163
|
+
type: '业务API' ,
|
164
|
+
group_name: '用户登录' ,
|
165
|
+
sort: 1 ,
|
166
|
+
apis: {
|
167
|
+
create: {
|
168
|
+
api_name: '登录' ,
|
169
|
+
path: '/api/v1/login' ,
|
170
|
+
method: 'post' ,
|
171
|
+
token: false , # 这里可关闭该API的token选项
|
172
|
+
params: {
|
173
|
+
mobile: {
|
174
|
+
required: true ,
|
175
|
+
} ,
|
176
|
+
password: {
|
177
|
+
required: true
|
178
|
+
}
|
179
|
+
}
|
180
|
+
}
|
181
|
+
}
|
182
|
+
}
|
183
|
+
def create
|
184
|
+
...
|
185
|
+
end
|
186
|
+
|
187
|
+
```
|
188
|
+
|
189
|
+
|
190
|
+
|
191
|
+
|
192
|
+
|
193
|
+
|
140
194
|
|
141
195
|
|
142
196
|
## TODO
|
143
197
|
- [x] 可自定义api目录
|
144
198
|
- [x] 可自定义theme
|
145
199
|
- [x] 可配置分类
|
146
|
-
- [
|
200
|
+
- [x] 可设定公共必填项,比如token、客户端类型、客户端版本号
|
201
|
+
- [x] 公共必填项可在指定API中关闭
|
202
|
+
- [x] 支持header token和params token
|
147
203
|
- [ ] ERROR类工具
|
204
|
+
- [ ] 出参example
|
148
205
|
- [ ] Apitest Example 网站
|
149
206
|
- [ ] 使用前端route
|
150
207
|
- [ ] 生成前端mock数据
|
@@ -5,6 +5,7 @@
|
|
5
5
|
#= require jquery3
|
6
6
|
#= require jquery_ujs
|
7
7
|
#= require turbolinks
|
8
|
+
#= require lodash
|
8
9
|
#= require bootstrap
|
9
10
|
#= require adminlte/adminlte
|
10
11
|
#= require vue
|
@@ -17,6 +18,7 @@ $(document).on "turbolinks:load" , ->
|
|
17
18
|
vm = new Vue
|
18
19
|
el : '#apitest_show'
|
19
20
|
data :
|
21
|
+
apitest_tab_show : 'api_test'
|
20
22
|
root_url : null
|
21
23
|
apis : null
|
22
24
|
ws : null
|
@@ -24,21 +26,39 @@ $(document).on "turbolinks:load" , ->
|
|
24
26
|
current_group : ''
|
25
27
|
current_api : ''
|
26
28
|
token : ''
|
27
|
-
|
29
|
+
token_type : ''
|
30
|
+
token_set : {}
|
31
|
+
token_get : []
|
32
|
+
headers : {}
|
28
33
|
mounted : ->
|
29
34
|
@apis = $('#data').data('apis')
|
30
|
-
@
|
35
|
+
@token_set = $('#data').data('token-set')
|
36
|
+
@token_get = $('#data').data('token-get')
|
37
|
+
@token_type = @token_set[0]
|
31
38
|
@output_area = $('#server-logs-output')
|
32
39
|
@root_url = $('#data').data('root-url')
|
33
40
|
@get_log()
|
41
|
+
@set_token(localStorage.apitest_token)
|
42
|
+
# @token_set()
|
34
43
|
|
35
44
|
methods :
|
36
|
-
|
45
|
+
set_token : (token) ->
|
46
|
+
@token = token
|
47
|
+
localStorage.apitest_token = token
|
48
|
+
@set_header_token() if @token_set[0] == 'header'
|
49
|
+
|
50
|
+
set_header_token : ->
|
51
|
+
@headers[@token_set[1]] = @token
|
52
|
+
|
53
|
+
fetch_token : (params) ->
|
54
|
+
token = eval 'params["' + @token_get.join('"]["') + '"]'
|
55
|
+
@set_token(token) if token
|
56
|
+
|
37
57
|
group_select : (name) ->
|
38
58
|
@current_group = name
|
39
59
|
@current_api = ''
|
40
60
|
|
41
|
-
api_select : (name
|
61
|
+
api_select : (name) ->
|
42
62
|
@current_api = name
|
43
63
|
|
44
64
|
api_submit : (e) ->
|
@@ -50,17 +70,23 @@ $(document).on "turbolinks:load" , ->
|
|
50
70
|
postData = {}
|
51
71
|
|
52
72
|
elm.find('.params').each ->
|
53
|
-
|
54
73
|
postData[$(this).attr('name')] = $(this).val() unless $(this).attr('name').indexOf(':id') >= 0
|
55
|
-
|
74
|
+
|
56
75
|
$.ajax
|
57
76
|
url : path
|
58
77
|
type : method
|
59
78
|
data : postData
|
79
|
+
headers : @headers
|
60
80
|
success : (data) =>
|
61
81
|
result_pre.jsonViewer(data)
|
82
|
+
@fetch_token(data)
|
62
83
|
error : (data) =>
|
63
|
-
|
84
|
+
try
|
85
|
+
result_pre.jsonViewer(JSON.parse(data.responseText))
|
86
|
+
catch e
|
87
|
+
result_pre.text(data.responseText)
|
88
|
+
|
89
|
+
|
64
90
|
|
65
91
|
clear_result : (e) ->
|
66
92
|
$(e.target).parents('.api').find('.result_pre').html('')
|
@@ -5,7 +5,10 @@ module Apitest
|
|
5
5
|
before_action :get_doc ,:root_url
|
6
6
|
def initialize
|
7
7
|
super
|
8
|
-
@apidocs
|
8
|
+
@apidocs = {}
|
9
|
+
@headers = Apitest::set_headers
|
10
|
+
@token_set = Apitest::token_setting[:set]
|
11
|
+
@token_get = Apitest::token_setting[:get]
|
9
12
|
end
|
10
13
|
def index
|
11
14
|
respond_to do |format|
|
@@ -29,24 +32,24 @@ module Apitest
|
|
29
32
|
def get_doc(path_root = nil)
|
30
33
|
path_root ||= "app/controllers/#{Apitest.api_dir}/"
|
31
34
|
Dir.glob("#{path_root}*").each do |path|
|
32
|
-
@apidocs[path.gsub("#{path_root}" , '')] = get_version_doc "#{path}
|
35
|
+
@apidocs[path.gsub("#{path_root}" , '')] = get_version_doc "#{path}" if File.directory?(path) && !path.gsub("#{path_root}" , '').blank?
|
33
36
|
end
|
34
37
|
end
|
35
38
|
|
36
|
-
def get_version_doc(path_root)
|
37
|
-
docs = Apitest::default_types.clone
|
39
|
+
def get_version_doc(path_root , docs = {})
|
40
|
+
docs = Apitest::default_types.clone if docs.blank?
|
38
41
|
Dir.glob("#{path_root}/*").each do |path|
|
39
42
|
if File.directory?(path)
|
40
|
-
docs = docs.merge get_version_doc(path)
|
43
|
+
docs = docs.merge get_version_doc(path , docs)
|
41
44
|
else
|
42
45
|
class_match = File.open(path).read.match(/class (.*) </)
|
43
46
|
controller_class = class_match[1].constantize if class_match && class_match[1]
|
44
47
|
if defined? controller_class::APIDOC
|
45
|
-
doc
|
46
|
-
doc[:sort]
|
48
|
+
doc = controller_class::APIDOC
|
49
|
+
doc[:sort] = 99 if doc[:sort].blank?
|
47
50
|
doc[:apis].each do |k,v|
|
48
51
|
d = doc[:apis][k]
|
49
|
-
d
|
52
|
+
d = public_required d unless Apitest::public_required.blank?
|
50
53
|
d[:params] = {':id' => { required: true }}.merge d[:params] if d[:path].include? ':id'
|
51
54
|
end
|
52
55
|
|
@@ -55,13 +58,13 @@ module Apitest
|
|
55
58
|
end
|
56
59
|
end
|
57
60
|
end
|
58
|
-
docs
|
61
|
+
return docs
|
59
62
|
end
|
60
63
|
def public_required(api)
|
61
64
|
Apitest::public_required.reverse.each do |need|
|
62
65
|
api[:params] = need.merge api[:params] if api[need.keys.first.to_sym] != false
|
63
66
|
end
|
64
|
-
api
|
67
|
+
return api
|
65
68
|
end
|
66
69
|
end
|
67
70
|
end
|
@@ -66,24 +66,44 @@ section.content
|
|
66
66
|
td 参数
|
67
67
|
td
|
68
68
|
table.table.table-hover.table-bordered
|
69
|
+
tr v-if="token_type == 'params'"
|
70
|
+
td
|
71
|
+
span style="color:red;width:10px;display:inline-block"
|
72
|
+
span *
|
73
|
+
span token
|
74
|
+
td
|
75
|
+
input.form-control.params name="token" :value="token"
|
69
76
|
tr v-for="(value , name , index) in api.params"
|
70
|
-
td.col-md-2
|
77
|
+
td.col-md-2
|
71
78
|
span style="color:red;width:10px;display:inline-block"
|
72
79
|
span v-if="value.required" *
|
73
80
|
span ="{{name}}"
|
74
|
-
td v-if="
|
75
|
-
|
76
|
-
|
77
|
-
|
81
|
+
td v-if="value.nested == true"
|
82
|
+
table.table.table-hover
|
83
|
+
tr v-for="(v , n , i) in value" v-if="n != 'nested'"
|
84
|
+
td.col-md-3
|
85
|
+
span style="color:red;width:10px;display:inline-block"
|
86
|
+
span v-if="v.required" *
|
87
|
+
span ="{{name}}[{{n}}]"
|
88
|
+
td
|
89
|
+
input.form-control.params :name="name + '[' + n + ']'"
|
90
|
+
span style="line-height:25px" = "{{v.text}}"
|
91
|
+
td v-else=""
|
78
92
|
input.form-control.params :name="name"
|
79
93
|
span style="line-height:25px" = "{{value.text}}"
|
94
|
+
/ td v-if="name == 'token'" v-bind:class="{'border-top-none': index == 0}"
|
95
|
+
/ input.form-control.params :name="name" :value="token"
|
96
|
+
/ span style="line-height:25px" = "{{value.text}}"
|
97
|
+
/ td v-if="name != 'token'" v-bind:class="{'border-top-none': index == 0}"
|
98
|
+
/ input.form-control.params :name="name"
|
99
|
+
/ span style="line-height:25px" = "{{value.text}}"
|
80
100
|
tr
|
81
101
|
td
|
82
102
|
td
|
83
103
|
button.btn.bg-olive.btn-flat @click="api_submit" 提交
|
84
|
-
button.btn.bg-orange.btn-flat style="margin-left:10px;margin-right:10px"
|
85
|
-
button.btn.bg-purple.btn-flat style="margin-left:10px;margin-right:10px"
|
86
|
-
button.btn.btn-warning.btn-flat style="margin-right:10px"
|
104
|
+
button.btn.bg-orange.btn-flat style="margin-left:10px;margin-right:10px" @click="clear_result" 清空结果
|
105
|
+
button.btn.bg-purple.btn-flat style="margin-left:10px;margin-right:10px" @click="show_server_log" 查看server log
|
106
|
+
button.btn.btn-warning.btn-flat style="margin-right:10px" @click="clear_log" 清空log
|
87
107
|
tr
|
88
108
|
td
|
89
109
|
td
|
@@ -31,6 +31,6 @@ html
|
|
31
31
|
=yield
|
32
32
|
footer.main-footer
|
33
33
|
-if params[:action] == 'show'
|
34
|
-
#data data-root-url="#{@root_url}" data-ws-url="ws://#{request.host}:9527" data-apis="#{@apidocs[params[:id]].to_json}" data-token="#{
|
34
|
+
#data data-root-url="#{@root_url}" data-ws-url="ws://#{request.host}:9527" data-apis="#{@apidocs[params[:id]].to_json}" data-headers="#{@headers.to_json}" data-token-set="#{@token_set.to_json}" data-token-get="#{@token_get.to_json}"
|
35
35
|
|
36
36
|
|
data/lib/apitest.rb
CHANGED
@@ -7,6 +7,7 @@ require "font-awesome-rails"
|
|
7
7
|
require "ionicons-rails"
|
8
8
|
require 'eventmachine'
|
9
9
|
require 'eventmachine-tail'
|
10
|
+
require 'lodash-rails'
|
10
11
|
require 'websocket-eventmachine-server'
|
11
12
|
|
12
13
|
module Apitest
|
@@ -14,6 +15,8 @@ module Apitest
|
|
14
15
|
@theme
|
15
16
|
@default_types
|
16
17
|
@public_required
|
18
|
+
@token_setting
|
19
|
+
|
17
20
|
class << self
|
18
21
|
def api_dir(dir = nil)
|
19
22
|
@api_dir = dir if dir
|
@@ -44,6 +47,15 @@ module Apitest
|
|
44
47
|
@public_required
|
45
48
|
end
|
46
49
|
|
50
|
+
def token_setting(setting = nil)
|
51
|
+
@token_setting = setting if setting
|
52
|
+
@token_setting
|
53
|
+
end
|
54
|
+
|
55
|
+
def set_headers
|
56
|
+
@token_setting[:set] && @token_setting[:set][1] ? {@token_setting[:set][1] => ''} : {}
|
57
|
+
end
|
58
|
+
|
47
59
|
def start_server_log_listen
|
48
60
|
Process.detach(
|
49
61
|
fork do
|
@@ -68,18 +80,6 @@ module Apitest
|
|
68
80
|
)
|
69
81
|
end
|
70
82
|
end
|
71
|
-
|
72
|
-
class Reader < EventMachine::FileTail
|
73
|
-
def initialize(path, startpos=-1, &block)
|
74
|
-
super(path, startpos)
|
75
|
-
@buffer = BufferedTokenizer.new
|
76
|
-
@block = block
|
77
|
-
end
|
78
|
-
|
79
|
-
def receive_data(data)
|
80
|
-
@buffer.extract(data).each { |line| @block.call(line) }
|
81
|
-
end
|
82
|
-
end
|
83
83
|
end
|
84
84
|
|
85
85
|
module ActionDispatch::Routing
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Apitest
|
2
|
+
module Error
|
3
|
+
def error(error_code = nil , data = nil,message = nil)
|
4
|
+
hash = {
|
5
|
+
status: 'error' ,
|
6
|
+
error_code: error_code ,
|
7
|
+
message: message.nil? ? self::ERROR[error_code] : message ,
|
8
|
+
data: data ,
|
9
|
+
}
|
10
|
+
raise "#{self.to_s}::Error".constantize.new hash
|
11
|
+
end
|
12
|
+
|
13
|
+
def success(message = nil , data = nil)
|
14
|
+
hash = {}
|
15
|
+
hash[:status] = 'ok'
|
16
|
+
if message.is_a? Hash
|
17
|
+
hash[:message] = message[:message] ? message[:message] : ''
|
18
|
+
hash[:data] = message[:data] ? message[:data] : message
|
19
|
+
else
|
20
|
+
hash[:message] = message ? message : ''
|
21
|
+
hash[:data] = data ? data : {}
|
22
|
+
end
|
23
|
+
return hash
|
24
|
+
end
|
25
|
+
|
26
|
+
class Error < StandardError
|
27
|
+
def error
|
28
|
+
eval(message)
|
29
|
+
end
|
30
|
+
def error_code
|
31
|
+
error[:error_code]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Apitest
|
2
|
+
class Reader < EventMachine::FileTail
|
3
|
+
def initialize(path, startpos=-1, &block)
|
4
|
+
super(path, startpos)
|
5
|
+
@buffer = BufferedTokenizer.new
|
6
|
+
@block = block
|
7
|
+
end
|
8
|
+
|
9
|
+
def receive_data(data)
|
10
|
+
@buffer.extract(data).each { |line| @block.call(line) }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Apitest
|
2
|
+
module ServiceError
|
3
|
+
# include Apitest::Error
|
4
|
+
def self.include(klass)
|
5
|
+
klass.class_eval do
|
6
|
+
include ServiceError
|
7
|
+
extend ServiceError
|
8
|
+
end
|
9
|
+
end
|
10
|
+
def self.error(error_code = nil , data = nil,message = nil)
|
11
|
+
hash = {
|
12
|
+
status: 'error' ,
|
13
|
+
error_code: error_code ,
|
14
|
+
message: message.nil? ? self::ERROR[error_code] : message ,
|
15
|
+
data: data ,
|
16
|
+
}
|
17
|
+
raise "#{self.to_s}::Error".constantize.new hash
|
18
|
+
end
|
19
|
+
|
20
|
+
def success(message = nil , data = nil)
|
21
|
+
hash = {}
|
22
|
+
hash[:status] = 'ok'
|
23
|
+
if message.is_a? Hash
|
24
|
+
hash[:message] = message[:message] ? message[:message] : ''
|
25
|
+
hash[:data] = message[:data] ? message[:data] : message
|
26
|
+
else
|
27
|
+
hash[:message] = message ? message : ''
|
28
|
+
hash[:data] = data ? data : {}
|
29
|
+
end
|
30
|
+
return hash
|
31
|
+
end
|
32
|
+
class Error < StandardError
|
33
|
+
def error
|
34
|
+
eval(message)
|
35
|
+
end
|
36
|
+
def error_code
|
37
|
+
error[:error_code]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/apitest/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: apitest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kyuubi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-05-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -122,6 +122,20 @@ dependencies:
|
|
122
122
|
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: lodash-rails
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
125
139
|
- !ruby/object:Gem::Dependency
|
126
140
|
name: eventmachine-tail
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -179,6 +193,10 @@ files:
|
|
179
193
|
- config/routes.rb
|
180
194
|
- lib/apitest.rb
|
181
195
|
- lib/apitest/engine.rb
|
196
|
+
- lib/apitest/error.rb
|
197
|
+
- lib/apitest/module_error.rb
|
198
|
+
- lib/apitest/reader.rb
|
199
|
+
- lib/apitest/service_error.rb
|
182
200
|
- lib/apitest/version.rb
|
183
201
|
- lib/tasks/apitest_tasks.rake
|
184
202
|
homepage: https://github.com/kyuubi9/apitest
|