apitest 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +37 -0
- data/app/assets/config/apitest_manifest.js +2 -0
- data/app/assets/javascripts/apitest/application.coffee +100 -0
- data/app/assets/javascripts/apitest/jquery.json-viewer.js +148 -0
- data/app/assets/stylesheets/apitest/application.scss +37 -0
- data/app/assets/stylesheets/apitest/jquery.json-viewer.css +45 -0
- data/app/controllers/apitest/apitest_controller.rb +62 -0
- data/app/controllers/apitest/application_controller.rb +5 -0
- data/app/helpers/apitest/apitest_helper.rb +4 -0
- data/app/helpers/apitest/application_helper.rb +4 -0
- data/app/jobs/apitest/application_job.rb +4 -0
- data/app/mailers/apitest/application_mailer.rb +6 -0
- data/app/models/apitest/application_record.rb +5 -0
- data/app/views/apitest/apitest/index.slim +0 -0
- data/app/views/apitest/apitest/show.slim +92 -0
- data/app/views/layouts/apitest/application.slim +36 -0
- data/config/initializers/apitest.rb +33 -0
- data/config/routes.rb +5 -0
- data/lib/apitest/engine.rb +5 -0
- data/lib/apitest/version.rb +3 -0
- data/lib/apitest.rb +14 -0
- data/lib/tasks/apitest_tasks.rake +4 -0
- metadata +208 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2106d3cc53ccbac1df7e6001bea56f01120b1db8
|
4
|
+
data.tar.gz: 91547d4b2d5afea5e35d1e80462f7fa9fe3d131f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 056ea12e6a741d65f266f1a413da52e7f383dc7da5b1601dbf5c1d83b3fe59d7c15b3b2bbd6b7fa0595971f2f7d66439567c4997d010133b43b43a4cc594c262
|
7
|
+
data.tar.gz: 3f5dbefb1dcf9d0398ac703172229d0aca1b398a94434b60446df93ddbd7e2c01e11831e7428dcd6552a9da44a6a923a54623ca3e5a91e69c5051bb986557e7d
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2017 Kyuubi
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Apitest
|
2
|
+
Short description and motivation.
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
How to use my plugin.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'apitest'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
```bash
|
16
|
+
$ bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
```bash
|
21
|
+
$ gem install apitest
|
22
|
+
```
|
23
|
+
|
24
|
+
## Contributing
|
25
|
+
Contribution directions go here.
|
26
|
+
|
27
|
+
## License
|
28
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Apitest'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
|
21
|
+
load 'rails/tasks/statistics.rake'
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
require 'bundler/gem_tasks'
|
26
|
+
|
27
|
+
require 'rake/testtask'
|
28
|
+
|
29
|
+
Rake::TestTask.new(:test) do |t|
|
30
|
+
t.libs << 'lib'
|
31
|
+
t.libs << 'test'
|
32
|
+
t.pattern = 'test/**/*_test.rb'
|
33
|
+
t.verbose = false
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
task default: :test
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# Place all the behaviors and hooks related to the matching controller here.
|
2
|
+
# All this logic will automatically be available in application.js.
|
3
|
+
# You can use CoffeeScript in this file: http://coffeescript.org/
|
4
|
+
|
5
|
+
#= require jquery3
|
6
|
+
#= require jquery_ujs
|
7
|
+
#= require turbolinks
|
8
|
+
#= require bootstrap
|
9
|
+
#= require adminlte/adminlte
|
10
|
+
#= require vue
|
11
|
+
#= require_directory .
|
12
|
+
#= require_self
|
13
|
+
|
14
|
+
$(document).on "turbolinks:load" , ->
|
15
|
+
if $('#apitest_show').length == 1
|
16
|
+
$('body').addClass('sidebar-collapse')
|
17
|
+
vm = new Vue
|
18
|
+
el : '#apitest_show'
|
19
|
+
data :
|
20
|
+
root_url : null
|
21
|
+
apis : null
|
22
|
+
ws : null
|
23
|
+
output_area : null
|
24
|
+
current_group : ''
|
25
|
+
current_api : ''
|
26
|
+
token : ''
|
27
|
+
apitest_tab_show : 'api_test'
|
28
|
+
mounted : ->
|
29
|
+
@apis = $('#data').data('apis')
|
30
|
+
@token = $('#data').data('token')
|
31
|
+
@output_area = $('#server-logs-output')
|
32
|
+
@root_url = $('#data').data('root-url')
|
33
|
+
@get_log()
|
34
|
+
|
35
|
+
methods :
|
36
|
+
|
37
|
+
group_select : (name) ->
|
38
|
+
@current_group = name
|
39
|
+
@current_api = ''
|
40
|
+
|
41
|
+
api_select : (name ) ->
|
42
|
+
@current_api = name
|
43
|
+
|
44
|
+
api_submit : (e) ->
|
45
|
+
elm = $(e.target).parents('.api')
|
46
|
+
result = elm.find('.result')
|
47
|
+
result_pre = elm.find('.result_pre')
|
48
|
+
path = elm.find('.path').val()
|
49
|
+
method = elm.find('.method').text()
|
50
|
+
postData = {}
|
51
|
+
|
52
|
+
elm.find('.params').each ->
|
53
|
+
postData[$(this).attr('name')] = $(this).val()
|
54
|
+
|
55
|
+
$.ajax
|
56
|
+
url : path
|
57
|
+
type : method
|
58
|
+
data : postData
|
59
|
+
success : (data) =>
|
60
|
+
# result.val JSON.stringify(data , null , 4)
|
61
|
+
result_pre.jsonViewer(data)
|
62
|
+
error : (data) =>
|
63
|
+
result.val data.responseText
|
64
|
+
|
65
|
+
clear_result : (e) ->
|
66
|
+
# $(e.target).parents('.api').find('.result').val('')
|
67
|
+
$(e.target).parents('.api').find('.result_pre').html('')
|
68
|
+
|
69
|
+
log_scroll : ->
|
70
|
+
$('#server-logs-output').scrollTop document.getElementById("server-logs-output").scrollHeight
|
71
|
+
|
72
|
+
get_log : (e) ->
|
73
|
+
unless @ws
|
74
|
+
@ws = new WebSocket($('#data').data('ws-url'))
|
75
|
+
@ws.onmessage = (evt) =>
|
76
|
+
$("<div>" + evt.data + "<div>").appendTo(@output_area)
|
77
|
+
@log_scroll()
|
78
|
+
|
79
|
+
$('#server-logs-output').height $('.api-wrap').height() - 104
|
80
|
+
|
81
|
+
clear_log : ->
|
82
|
+
$('#server-logs-output').html('')
|
83
|
+
|
84
|
+
|
85
|
+
show_api_test : ->
|
86
|
+
@apitest_tab_show = 'api_test'
|
87
|
+
show_server_log : ->
|
88
|
+
@apitest_tab_show = 'server_log'
|
89
|
+
@get_log()
|
90
|
+
$.AdminLTE.layout.fix() if $.AdminLTE.layout
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
|
@@ -0,0 +1,148 @@
|
|
1
|
+
/**
|
2
|
+
* jQuery json-viewer
|
3
|
+
* @author: Alexandre Bodelot <alexandre.bodelot@gmail.com>
|
4
|
+
*/
|
5
|
+
(function($){
|
6
|
+
|
7
|
+
/**
|
8
|
+
* Check if arg is either an array with at least 1 element, or a dict with at least 1 key
|
9
|
+
* @return boolean
|
10
|
+
*/
|
11
|
+
function isCollapsable(arg) {
|
12
|
+
return arg instanceof Object && Object.keys(arg).length > 0;
|
13
|
+
}
|
14
|
+
|
15
|
+
/**
|
16
|
+
* Check if a string represents a valid url
|
17
|
+
* @return boolean
|
18
|
+
*/
|
19
|
+
function isUrl(string) {
|
20
|
+
var regexp = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/
|
21
|
+
return regexp.test(string);
|
22
|
+
}
|
23
|
+
|
24
|
+
/**
|
25
|
+
* Transform a json object into html representation
|
26
|
+
* @return string
|
27
|
+
*/
|
28
|
+
function json2html(json, options) {
|
29
|
+
var html = '';
|
30
|
+
if (typeof json === 'string') {
|
31
|
+
// Escape tags
|
32
|
+
json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
33
|
+
if (isUrl(json))
|
34
|
+
html += '<a href="' + json + '" class="json-string">' + json + '</a>';
|
35
|
+
else
|
36
|
+
html += '<span class="json-string">"' + json + '"</span>';
|
37
|
+
}
|
38
|
+
else if (typeof json === 'number') {
|
39
|
+
html += '<span class="json-literal">' + json + '</span>';
|
40
|
+
}
|
41
|
+
else if (typeof json === 'boolean') {
|
42
|
+
html += '<span class="json-literal">' + json + '</span>';
|
43
|
+
}
|
44
|
+
else if (json === null) {
|
45
|
+
html += '<span class="json-literal">null</span>';
|
46
|
+
}
|
47
|
+
else if (json instanceof Array) {
|
48
|
+
if (json.length > 0) {
|
49
|
+
html += '[<ol class="json-array">';
|
50
|
+
for (var i = 0; i < json.length; ++i) {
|
51
|
+
html += '<li>'
|
52
|
+
// Add toggle button if item is collapsable
|
53
|
+
if (isCollapsable(json[i])) {
|
54
|
+
html += '<a href class="json-toggle"></a>';
|
55
|
+
}
|
56
|
+
html += json2html(json[i], options);
|
57
|
+
// Add comma if item is not last
|
58
|
+
if (i < json.length - 1) {
|
59
|
+
html += ',';
|
60
|
+
}
|
61
|
+
html += '</li>';
|
62
|
+
}
|
63
|
+
html += '</ol>]';
|
64
|
+
}
|
65
|
+
else {
|
66
|
+
html += '[]';
|
67
|
+
}
|
68
|
+
}
|
69
|
+
else if (typeof json === 'object') {
|
70
|
+
var key_count = Object.keys(json).length;
|
71
|
+
if (key_count > 0) {
|
72
|
+
html += '{<ul class="json-dict">';
|
73
|
+
for (var key in json) {
|
74
|
+
if (json.hasOwnProperty(key)) {
|
75
|
+
html += '<li>';
|
76
|
+
var keyRepr = options.withQuotes ?
|
77
|
+
'<span class="json-string">"' + key + '"</span>' : key;
|
78
|
+
// Add toggle button if item is collapsable
|
79
|
+
if (isCollapsable(json[key])) {
|
80
|
+
html += '<a href class="json-toggle">' + keyRepr + '</a>';
|
81
|
+
}
|
82
|
+
else {
|
83
|
+
html += keyRepr;
|
84
|
+
}
|
85
|
+
html += ': ' + json2html(json[key], options);
|
86
|
+
// Add comma if item is not last
|
87
|
+
if (--key_count > 0)
|
88
|
+
html += ',';
|
89
|
+
html += '</li>';
|
90
|
+
}
|
91
|
+
}
|
92
|
+
html += '</ul>}';
|
93
|
+
}
|
94
|
+
else {
|
95
|
+
html += '{}';
|
96
|
+
}
|
97
|
+
}
|
98
|
+
return html;
|
99
|
+
}
|
100
|
+
|
101
|
+
/**
|
102
|
+
* jQuery plugin method
|
103
|
+
* @param json: a javascript object
|
104
|
+
* @param options: an optional options hash
|
105
|
+
*/
|
106
|
+
$.fn.jsonViewer = function(json, options) {
|
107
|
+
options = options || {};
|
108
|
+
|
109
|
+
// jQuery chaining
|
110
|
+
return this.each(function() {
|
111
|
+
|
112
|
+
// Transform to HTML
|
113
|
+
var html = json2html(json, options)
|
114
|
+
if (isCollapsable(json))
|
115
|
+
html = '<a href class="json-toggle"></a>' + html;
|
116
|
+
|
117
|
+
// Insert HTML in target DOM element
|
118
|
+
$(this).html(html);
|
119
|
+
|
120
|
+
// Bind click on toggle buttons
|
121
|
+
$(this).off('click');
|
122
|
+
$(this).on('click', 'a.json-toggle', function() {
|
123
|
+
var target = $(this).toggleClass('collapsed').siblings('ul.json-dict, ol.json-array');
|
124
|
+
target.toggle();
|
125
|
+
if (target.is(':visible')) {
|
126
|
+
target.siblings('.json-placeholder').remove();
|
127
|
+
}
|
128
|
+
else {
|
129
|
+
var count = target.children('li').length;
|
130
|
+
var placeholder = count + (count > 1 ? ' items' : ' item');
|
131
|
+
target.after('<a href class="json-placeholder">' + placeholder + '</a>');
|
132
|
+
}
|
133
|
+
return false;
|
134
|
+
});
|
135
|
+
|
136
|
+
// Simulate click on toggle button when placeholder is clicked
|
137
|
+
$(this).on('click', 'a.json-placeholder', function() {
|
138
|
+
$(this).siblings('a.json-toggle').click();
|
139
|
+
return false;
|
140
|
+
});
|
141
|
+
|
142
|
+
if (options.collapsed == true) {
|
143
|
+
// Trigger click to collapse all nodes
|
144
|
+
$(this).find('a.json-toggle').click();
|
145
|
+
}
|
146
|
+
});
|
147
|
+
};
|
148
|
+
})(jQuery);
|
@@ -0,0 +1,37 @@
|
|
1
|
+
// Place all the styles related to the apitest/v1 controller here.
|
2
|
+
// They will automatically be included in application.css.
|
3
|
+
// You can use Sass (SCSS) here: http://sass-lang.com/
|
4
|
+
//= require_directory .
|
5
|
+
//= require bootstrap
|
6
|
+
//= require adminlte/adminlte
|
7
|
+
//= require adminlte/skins/_all-skins
|
8
|
+
//= require font-awesome
|
9
|
+
//= require_self
|
10
|
+
|
11
|
+
#apitest_show{
|
12
|
+
#server-logs-output {
|
13
|
+
background-color: #444;
|
14
|
+
color: #ddd;
|
15
|
+
height: 794px;
|
16
|
+
overflow: scroll;
|
17
|
+
padding: 10px;
|
18
|
+
vertical-align: top;
|
19
|
+
}
|
20
|
+
.result{
|
21
|
+
resize: none;
|
22
|
+
}
|
23
|
+
.result_pre{
|
24
|
+
background-color: #fff;
|
25
|
+
height: 514px;
|
26
|
+
border-radius: 0px;
|
27
|
+
padding: 15px 30px 15px 30px;
|
28
|
+
}
|
29
|
+
.nav-pills.nav-stacked{
|
30
|
+
li.active {
|
31
|
+
a{
|
32
|
+
background-color: #337ab7;
|
33
|
+
color: #fff;
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
/* Syntax highlighting for JSON objects */
|
2
|
+
ul.json-dict, ol.json-array {
|
3
|
+
list-style-type: none;
|
4
|
+
margin: 0 0 0 1px;
|
5
|
+
border-left: 1px dotted #ccc;
|
6
|
+
padding-left: 2em;
|
7
|
+
}
|
8
|
+
.json-string {
|
9
|
+
color: #0B7500;
|
10
|
+
}
|
11
|
+
.json-literal {
|
12
|
+
color: #1A01CC;
|
13
|
+
font-weight: bold;
|
14
|
+
}
|
15
|
+
|
16
|
+
/* Toggle button */
|
17
|
+
a.json-toggle {
|
18
|
+
position: relative;
|
19
|
+
color: inherit;
|
20
|
+
text-decoration: none;
|
21
|
+
}
|
22
|
+
a.json-toggle:focus {
|
23
|
+
outline: none;
|
24
|
+
}
|
25
|
+
a.json-toggle:before {
|
26
|
+
color: #aaa;
|
27
|
+
content: "\25BC"; /* down arrow */
|
28
|
+
position: absolute;
|
29
|
+
display: inline-block;
|
30
|
+
width: 1em;
|
31
|
+
left: -1em;
|
32
|
+
}
|
33
|
+
a.json-toggle.collapsed:before {
|
34
|
+
content: "\25B6"; /* left arrow */
|
35
|
+
}
|
36
|
+
|
37
|
+
/* Collapsable placeholder links */
|
38
|
+
a.json-placeholder {
|
39
|
+
color: #aaa;
|
40
|
+
padding: 0 1em;
|
41
|
+
text-decoration: none;
|
42
|
+
}
|
43
|
+
a.json-placeholder:hover {
|
44
|
+
text-decoration: underline;
|
45
|
+
}
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require_dependency "apitest/application_controller"
|
2
|
+
|
3
|
+
module Apitest
|
4
|
+
class ApitestController < ApplicationController
|
5
|
+
before_action :get_doc
|
6
|
+
def index
|
7
|
+
respond_to do |format|
|
8
|
+
format.json { render json: @apidocs}
|
9
|
+
format.html
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def show
|
14
|
+
respond_to do |format|
|
15
|
+
format.html
|
16
|
+
format.json { render json: @apidocs[params[:id]]}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def get_doc
|
22
|
+
path_root = "app/controllers/api/"
|
23
|
+
@apidocs = {}
|
24
|
+
Dir.glob("#{path_root}*").map do |path|
|
25
|
+
if File.directory?(path)
|
26
|
+
@apidocs[path.gsub("#{path_root}" , '')] = get_version_doc "#{path}/"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
@root_url = main_app.root_url[0,main_app.root_url.length - 1]
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_version_doc(path_root)
|
33
|
+
|
34
|
+
docs = {
|
35
|
+
'业务API' => [] ,
|
36
|
+
'工具API' => [] ,
|
37
|
+
'辅助API' => []
|
38
|
+
}
|
39
|
+
dirs = []
|
40
|
+
Dir.glob("#{path_root}*_controller.rb").each do |path|
|
41
|
+
controller_name_base = path.gsub(path_root , '').gsub('.rb' , '').singularize.camelize
|
42
|
+
controller_class = "Api::V1::#{controller_name_base}".constantize
|
43
|
+
if defined? controller_class::APIDOC
|
44
|
+
token_need = {
|
45
|
+
token: {
|
46
|
+
text: 'token' ,
|
47
|
+
required: true ,
|
48
|
+
}
|
49
|
+
}
|
50
|
+
doc = controller_class::APIDOC
|
51
|
+
doc[:sort] = 99 if doc[:sort].blank?
|
52
|
+
doc[:apis].each do |k,v|
|
53
|
+
doc[:apis][k][:params] = token_need.merge doc[:apis][k][:params] unless v[:token] == false
|
54
|
+
end
|
55
|
+
docs[doc[:type]] = [] if docs[doc[:type]].blank?
|
56
|
+
docs[doc[:type]].push doc
|
57
|
+
end
|
58
|
+
end
|
59
|
+
docs
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
File without changes
|
@@ -0,0 +1,92 @@
|
|
1
|
+
.content-header
|
2
|
+
h1 =params[:id].upcase
|
3
|
+
section.content
|
4
|
+
.col-sm-5.row
|
5
|
+
.groups v-bind:class="{hide: apitest_tab_show == 'server_log'}"
|
6
|
+
.col-sm-6
|
7
|
+
.box.box-primary v-for="(group_doc , group_name) in apis"
|
8
|
+
.box-header.with-border
|
9
|
+
h3.box-title ="{{group_name}}"
|
10
|
+
.box-tools
|
11
|
+
button.btn.btn-box-tool data-widget="collapse" type="button"
|
12
|
+
.fa.fa-minus
|
13
|
+
.box-body.no-padding
|
14
|
+
.nav.nav-pills.nav-stacked
|
15
|
+
li v-bind:class="{active: current_group == group.group_name , disabled: group.active == false}" v-for="group in group_doc"
|
16
|
+
a @click="group_select(group.group_name)" href="javascript:void(0)" ="{{group.group_name}}"
|
17
|
+
|
18
|
+
.col-sm-6 v-for="(group_doc , group_name) in apis"
|
19
|
+
.box.box-primary v-for="group in group_doc" v-bind:class="{hide: current_group != group.group_name}"
|
20
|
+
.box-header.with-border
|
21
|
+
h3.box-title ="{{group.group_name}}"
|
22
|
+
.box-body.no-padding
|
23
|
+
.nav.nav-pills.nav-stacked
|
24
|
+
li v-for="(api , name) in group.apis" v-bind:class="{active: current_group == group.group_name && current_api == name ,disabled: api.active == false}"
|
25
|
+
a @click="api_select(name)" href="javascript:void(0)"
|
26
|
+
.list-group-item-heading = "{{api.api_name}}"
|
27
|
+
.list-group-item-text = "{{api.path}}"
|
28
|
+
|
29
|
+
.col-sm-12
|
30
|
+
.box.box-danger.server-logs-wrap v-bind:class="{hide: apitest_tab_show == 'api_test'}"
|
31
|
+
.box-header.with-border
|
32
|
+
h4.box-title Server Logs
|
33
|
+
.box-body
|
34
|
+
#server-logs-output contenteditable='true'
|
35
|
+
.col-sm-7
|
36
|
+
.api-wrap
|
37
|
+
.row v-for="(group_doc , group_name) in apis"
|
38
|
+
.col-sm-12 v-for="group in group_doc" v-bind:class="{hide: current_group != group.group_name}"
|
39
|
+
.api v-for="(api , name) in group.apis" v-bind:class="{hide: current_api != name || current_group != group.group_name}"
|
40
|
+
.nav-tabs-custom
|
41
|
+
ul.nav.nav-tabs
|
42
|
+
li.api-wrap-tab @click="show_api_test" v-bind:class="{active: apitest_tab_show == 'api_test'}"
|
43
|
+
a href="javascript:void(0)" API Test
|
44
|
+
li.server-logs-warp-tab @click="show_server_log" v-bind:class="{active: apitest_tab_show == 'server_log'}"
|
45
|
+
a href="javascript:void(0)" Server Logs
|
46
|
+
.tab-content.no-padding
|
47
|
+
table.table
|
48
|
+
tbody
|
49
|
+
tr
|
50
|
+
td.col-md-2 style="border-top:none"
|
51
|
+
h4 API名称
|
52
|
+
td style="border-top:none"
|
53
|
+
h4 ="{{api.api_name}}"
|
54
|
+
tr
|
55
|
+
td url
|
56
|
+
td
|
57
|
+
input.form-control.url :value="root_url + api.path"
|
58
|
+
tr
|
59
|
+
td path
|
60
|
+
td
|
61
|
+
input.form-control.path :value="api.path"
|
62
|
+
tr
|
63
|
+
td 提交方式
|
64
|
+
td.method ="{{api.method}}"
|
65
|
+
tr
|
66
|
+
td 参数
|
67
|
+
td
|
68
|
+
table.table.table-hover.table-bordered
|
69
|
+
tr v-for="(value , name , index) in api.params"
|
70
|
+
td.col-md-2 v-bind:class="{'border-top-none': (index == 0)}"
|
71
|
+
span style="color:red;width:10px;display:inline-block"
|
72
|
+
span v-if="value.required" *
|
73
|
+
span ="{{name}}"
|
74
|
+
td v-if="name == 'token'" v-bind:class="{'border-top-none': index == 0}"
|
75
|
+
input.form-control.params :name="name" :value="token"
|
76
|
+
span style="line-height:25px" = "{{value.text}}"
|
77
|
+
td v-if="name != 'token'" v-bind:class="{'border-top-none': index == 0}"
|
78
|
+
input.form-control.params :name="name"
|
79
|
+
span style="line-height:25px" = "{{value.text}}"
|
80
|
+
tr
|
81
|
+
td
|
82
|
+
td
|
83
|
+
button.btn.bg-olive.btn-flat @click="api_submit" 提交
|
84
|
+
button.btn.bg-orange.btn-flat style="margin-left:10px;margin-right:10px" @click="clear_result" 清空结果
|
85
|
+
button.btn.bg-purple.btn-flat style="margin-left:10px;margin-right:10px" @click="show_server_log" 查看server log
|
86
|
+
button.btn.btn-warning.btn-flat style="margin-right:10px" @click="clear_log" 清空log
|
87
|
+
tr
|
88
|
+
td
|
89
|
+
td
|
90
|
+
pre.result_pre contenteditable='true'
|
91
|
+
/ textarea.form-control.result rows="25"
|
92
|
+
.clearfix
|
@@ -0,0 +1,36 @@
|
|
1
|
+
doctype 5
|
2
|
+
html
|
3
|
+
head
|
4
|
+
title API TEST
|
5
|
+
= csrf_meta_tags
|
6
|
+
= stylesheet_link_tag 'apitest/application', 'data-turbolinks-track': 'reload' , media: 'all'
|
7
|
+
= javascript_include_tag 'apitest/application', 'data-turbolinks-track': 'reload'
|
8
|
+
body.hold-transition.skin-purple-light.sidebar-mini
|
9
|
+
.wrapper id="apitest_#{params[:action]}"
|
10
|
+
header.main-header
|
11
|
+
a.logo href="#{root_path}"
|
12
|
+
span.logo-mini T
|
13
|
+
span.logo-lg API TEST
|
14
|
+
.navbar.navbar-static-top role="navigation"
|
15
|
+
a.sidebar-toggle data-toggle="offcanvas" row="button" href="#"
|
16
|
+
span.sr-only Toggle navigation
|
17
|
+
aside.main-sidebar
|
18
|
+
section.sidebar
|
19
|
+
ul.sidebar-menu
|
20
|
+
li.treeview.active
|
21
|
+
a href="#"
|
22
|
+
i.fa.fa-code.text-purple
|
23
|
+
span API版本
|
24
|
+
ul.treeview-menu
|
25
|
+
-@apidocs.each do |version , apis|
|
26
|
+
li.treeview
|
27
|
+
a href="#{root_path}#{version}"
|
28
|
+
i.fa.fa-code-fork.text-purple
|
29
|
+
span =version.upcase
|
30
|
+
main.content-wrapper
|
31
|
+
=yield
|
32
|
+
footer.main-footer
|
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="#{session[:token]}"
|
35
|
+
|
36
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
Process.detach(
|
2
|
+
fork do
|
3
|
+
p 'Kill Apitest WebSocket Process ...'
|
4
|
+
`lsof -i :9527 |awk '$1 == "ruby" {print $2}' |xargs kill -9`
|
5
|
+
begin
|
6
|
+
class Reader < EventMachine::FileTail
|
7
|
+
def initialize(path, startpos=-1, &block)
|
8
|
+
super(path, startpos)
|
9
|
+
@buffer = BufferedTokenizer.new
|
10
|
+
@block = block
|
11
|
+
end
|
12
|
+
|
13
|
+
def receive_data(data)
|
14
|
+
@buffer.extract(data).each { |line| @block.call(line) }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
filename = "#{Rails.root}/log/#{Rails.env}.log"
|
19
|
+
|
20
|
+
EventMachine.run do
|
21
|
+
WebSocket::EventMachine::Server.start(:host => "0.0.0.0", :port => 9527) do |ws|
|
22
|
+
proc = Proc.new { |line|
|
23
|
+
ws.send Ansi::To::Html.new(line.strip).to_html.strip
|
24
|
+
}
|
25
|
+
EventMachine::file_tail(filename, Reader, &proc)
|
26
|
+
end
|
27
|
+
p 'Start Apitest WebSocket Process !'
|
28
|
+
end
|
29
|
+
rescue Exception => e
|
30
|
+
retry
|
31
|
+
end
|
32
|
+
end
|
33
|
+
)
|
data/config/routes.rb
ADDED
data/lib/apitest.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "apitest/engine"
|
2
|
+
require "slim-rails"
|
3
|
+
require "rails-adminlte"
|
4
|
+
require "vuejs-rails"
|
5
|
+
require "ansi-to-html"
|
6
|
+
require "font-awesome-rails"
|
7
|
+
require "ionicons-rails"
|
8
|
+
require 'eventmachine'
|
9
|
+
require 'eventmachine-tail'
|
10
|
+
require 'websocket-eventmachine-server'
|
11
|
+
|
12
|
+
module Apitest
|
13
|
+
# Your code goes here...
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: apitest
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kyuubi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-03-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: slim-rails
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rails-adminlte
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: vuejs-rails
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: ansi-to-html
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: font-awesome-rails
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: ionicons-rails
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: eventmachine
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: eventmachine-tail
|
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'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: websocket-eventmachine-server
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
description: ''
|
154
|
+
email:
|
155
|
+
- kyuubi@chinacluster.com
|
156
|
+
executables: []
|
157
|
+
extensions: []
|
158
|
+
extra_rdoc_files: []
|
159
|
+
files:
|
160
|
+
- MIT-LICENSE
|
161
|
+
- README.md
|
162
|
+
- Rakefile
|
163
|
+
- app/assets/config/apitest_manifest.js
|
164
|
+
- app/assets/javascripts/apitest/application.coffee
|
165
|
+
- app/assets/javascripts/apitest/jquery.json-viewer.js
|
166
|
+
- app/assets/stylesheets/apitest/application.scss
|
167
|
+
- app/assets/stylesheets/apitest/jquery.json-viewer.css
|
168
|
+
- app/controllers/apitest/apitest_controller.rb
|
169
|
+
- app/controllers/apitest/application_controller.rb
|
170
|
+
- app/helpers/apitest/apitest_helper.rb
|
171
|
+
- app/helpers/apitest/application_helper.rb
|
172
|
+
- app/jobs/apitest/application_job.rb
|
173
|
+
- app/mailers/apitest/application_mailer.rb
|
174
|
+
- app/models/apitest/application_record.rb
|
175
|
+
- app/views/apitest/apitest/index.slim
|
176
|
+
- app/views/apitest/apitest/show.slim
|
177
|
+
- app/views/layouts/apitest/application.slim
|
178
|
+
- config/initializers/apitest.rb
|
179
|
+
- config/routes.rb
|
180
|
+
- lib/apitest.rb
|
181
|
+
- lib/apitest/engine.rb
|
182
|
+
- lib/apitest/version.rb
|
183
|
+
- lib/tasks/apitest_tasks.rake
|
184
|
+
homepage: https://github.com/kyuubi9/apitest
|
185
|
+
licenses:
|
186
|
+
- MIT
|
187
|
+
metadata: {}
|
188
|
+
post_install_message:
|
189
|
+
rdoc_options: []
|
190
|
+
require_paths:
|
191
|
+
- lib
|
192
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
193
|
+
requirements:
|
194
|
+
- - ">="
|
195
|
+
- !ruby/object:Gem::Version
|
196
|
+
version: '0'
|
197
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - ">="
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '0'
|
202
|
+
requirements: []
|
203
|
+
rubyforge_project:
|
204
|
+
rubygems_version: 2.6.8
|
205
|
+
signing_key:
|
206
|
+
specification_version: 4
|
207
|
+
summary: ''
|
208
|
+
test_files: []
|