agile-proxy 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/.bowerrc +3 -0
  3. data/.gitignore +8 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +36 -0
  6. data/.travis.yml +8 -0
  7. data/Gemfile +4 -0
  8. data/Gemfile.lock +267 -0
  9. data/Guardfile +20 -0
  10. data/LICENSE +22 -0
  11. data/README.md +93 -0
  12. data/Rakefile +13 -0
  13. data/agile-proxy.gemspec +50 -0
  14. data/assets/index.html +39 -0
  15. data/assets/ui/app/HttpFlexibleProxyApi.js +31 -0
  16. data/assets/ui/app/app.js +1 -0
  17. data/assets/ui/app/controller/Stubs.js +64 -0
  18. data/assets/ui/app/controller/main.js +12 -0
  19. data/assets/ui/app/directive/AppEnhancedFormElement.js +21 -0
  20. data/assets/ui/app/directive/AppFor.js +16 -0
  21. data/assets/ui/app/directive/AppResponseEditor.js +54 -0
  22. data/assets/ui/app/model/RequestSpec.js +6 -0
  23. data/assets/ui/app/routes.js +10 -0
  24. data/assets/ui/app/service/Dialog.js +49 -0
  25. data/assets/ui/app/service/DomId.js +10 -0
  26. data/assets/ui/app/service/Error.js +7 -0
  27. data/assets/ui/app/service/Stub.js +36 -0
  28. data/assets/ui/app/view/404.html +2 -0
  29. data/assets/ui/app/view/dialog/error.html +10 -0
  30. data/assets/ui/app/view/dialog/yesNo.html +8 -0
  31. data/assets/ui/app/view/responses/editForm.html +78 -0
  32. data/assets/ui/app/view/status.html +1 -0
  33. data/assets/ui/app/view/stubs.html +19 -0
  34. data/assets/ui/app/view/stubs/edit.html +58 -0
  35. data/assets/ui/css/main.css +3 -0
  36. data/bin/agile_proxy +113 -0
  37. data/bower.json +27 -0
  38. data/config.yml +6 -0
  39. data/db.yml +10 -0
  40. data/db/migrations/20140818110800_create_users.rb +9 -0
  41. data/db/migrations/20140818134700_create_applications.rb +10 -0
  42. data/db/migrations/20140818135200_create_request_specs.rb +13 -0
  43. data/db/migrations/20140821115300_create_responses.rb +14 -0
  44. data/db/migrations/20140823082900_add_method_to_request_specs.rb +7 -0
  45. data/db/migrations/20140823083900_rename_request_spec_columns.rb +8 -0
  46. data/db/migrations/20141031072100_add_url_type_to_request_specs.rb +8 -0
  47. data/db/migrations/20141105125600_add_conditions_to_request_specs.rb +7 -0
  48. data/db/migrations/20141106083100_add_username_and_password_to_applications.rb +8 -0
  49. data/db/migrations/20141119143800_add_record_to_applications.rb +7 -0
  50. data/db/migrations/20141119174300_create_recordings.rb +18 -0
  51. data/db/schema.rb +78 -0
  52. data/examples/README.md +1 -0
  53. data/examples/facebook_api.html +59 -0
  54. data/examples/tumblr_api.html +22 -0
  55. data/lib/agile_proxy.rb +8 -0
  56. data/lib/agile_proxy/api/applications.rb +77 -0
  57. data/lib/agile_proxy/api/recordings.rb +52 -0
  58. data/lib/agile_proxy/api/request_specs.rb +85 -0
  59. data/lib/agile_proxy/api/root.rb +41 -0
  60. data/lib/agile_proxy/config.rb +63 -0
  61. data/lib/agile_proxy/handlers/handler.rb +43 -0
  62. data/lib/agile_proxy/handlers/proxy_handler.rb +110 -0
  63. data/lib/agile_proxy/handlers/request_handler.rb +57 -0
  64. data/lib/agile_proxy/handlers/stub_handler.rb +113 -0
  65. data/lib/agile_proxy/mitm.crt +22 -0
  66. data/lib/agile_proxy/mitm.key +27 -0
  67. data/lib/agile_proxy/model/application.rb +20 -0
  68. data/lib/agile_proxy/model/recording.rb +16 -0
  69. data/lib/agile_proxy/model/request_spec.rb +47 -0
  70. data/lib/agile_proxy/model/response.rb +56 -0
  71. data/lib/agile_proxy/model/user.rb +17 -0
  72. data/lib/agile_proxy/proxy_connection.rb +113 -0
  73. data/lib/agile_proxy/route.rb +106 -0
  74. data/lib/agile_proxy/router.rb +99 -0
  75. data/lib/agile_proxy/server.rb +85 -0
  76. data/lib/agile_proxy/servers/api.rb +41 -0
  77. data/lib/agile_proxy/servers/request_spec.rb +30 -0
  78. data/lib/agile_proxy/version.rb +6 -0
  79. data/load_proxy.js +39 -0
  80. data/log/.gitkeep +0 -0
  81. data/spec/common_helper.rb +32 -0
  82. data/spec/fixtures/test-server.crt +15 -0
  83. data/spec/fixtures/test-server.key +15 -0
  84. data/spec/integration/helpers/request_spec_helper.rb +60 -0
  85. data/spec/integration/specs/lib/server_spec.rb +407 -0
  86. data/spec/integration_spec_helper.rb +18 -0
  87. data/spec/spec_helper.rb +39 -0
  88. data/spec/support/test_server.rb +75 -0
  89. data/spec/unit/agile_proxy/api/applications_spec.rb +102 -0
  90. data/spec/unit/agile_proxy/api/common_helper.rb +31 -0
  91. data/spec/unit/agile_proxy/api/recordings_spec.rb +115 -0
  92. data/spec/unit/agile_proxy/api/request_specs_spec.rb +159 -0
  93. data/spec/unit/agile_proxy/handlers/handler_spec.rb +8 -0
  94. data/spec/unit/agile_proxy/handlers/proxy_handler_spec.rb +138 -0
  95. data/spec/unit/agile_proxy/handlers/request_handler_spec.rb +55 -0
  96. data/spec/unit/agile_proxy/handlers/stub_handler_spec.rb +154 -0
  97. data/spec/unit/agile_proxy/model/recording_spec.rb +0 -0
  98. data/spec/unit/agile_proxy/model/request_spec_spec.rb +45 -0
  99. data/spec/unit/agile_proxy/model/response_spec.rb +38 -0
  100. data/spec/unit/agile_proxy/server_spec.rb +88 -0
  101. data/spec/unit/agile_proxy/servers/api_spec.rb +31 -0
  102. data/spec/unit/agile_proxy/servers/request_spec_spec.rb +32 -0
  103. metadata +618 -0
@@ -0,0 +1,2 @@
1
+ <h1>Error</h1>
2
+ <p>Page not found</p>
@@ -0,0 +1,10 @@
1
+ <div class="modal-header text-center text-danger">
2
+ <span class="glyphicon glyphicon-warning-sign"></span>
3
+ <span class="text-uppercase">{{title}}</span>
4
+ </div>
5
+ <div class="modal-body text-center">
6
+ {{message}}
7
+ </div>
8
+ <div class="modal-footer">
9
+ <button ng-click="close()" class="btn btn-default">OK</button>
10
+ </div>
@@ -0,0 +1,8 @@
1
+ <div class="modal-header">Confirmation</div>
2
+ <div class="modal-body">
3
+ {{message}}
4
+ </div>
5
+ <div class="modal-footer">
6
+ <button ng-click="no()" class="btn btn-warning">No</button>
7
+ <button ng-click="yes()" class="btn btn-default">Yes</button>
8
+ </div>
@@ -0,0 +1,78 @@
1
+ <form name="editResponseForm" class="form-horizontal" novalidate>
2
+ <div class="form-group">
3
+ <label app-for="name" class="col-xs-2">Name</label>
4
+ <div class="col-xs-10">
5
+ <input name="name" class="form-control" type="text" ng-model="response.name"/>
6
+ </div>
7
+ </div>
8
+ <div class="form-group">
9
+ <label app-for="contentType" class="col-xs-2">Content Type</label>
10
+ <div class="col-xs-10">
11
+ <select ng-change="onContentTypeChange(response.contentType)" name="contentType" class="form-control" ng-model="response.contentType">
12
+ <option value="text/html">Text / HTML</option>
13
+ <option value="application/json">JSON</option>
14
+ <option value="application/javascript">Javascript</option>
15
+ <option value="text/plain">Plain Text</option>
16
+ </select>
17
+ </div>
18
+ </div>
19
+ <div class="form-group">
20
+ <label app-for="statusCode" class="col-xs-2">Status Code</label>
21
+ <div class="col-xs-10">
22
+ <select name="statusCode" class="form-control" ng-model="response.statusCode">
23
+ <option value="200">200 OK</option>
24
+ <option value="404">404 Not Found</option>
25
+ <option value="100">100 Continue</option>
26
+ <option value="101">101 Switching Protocols</option>
27
+ <option value="102">102 Processing</option>
28
+ <option value="201">201 Created</option>
29
+ <option value="202">202 Accepted</option>
30
+ <option value="203">203 Non-Authoritative Information</option>
31
+ <option value="204">204 No Content</option>
32
+ <option value="205">205 Reset Content</option>
33
+ <option value="206">206 Partial Content</option>
34
+ <option value="207">207 Multi-Status</option>
35
+ <option value="208">208 Already Reported</option>
36
+ <option value="226">226 IM Used</option>
37
+ <option value="300">300 Multiple Choices</option>
38
+ <option value="301">301 Moved Permanently</option>
39
+ <option value="302">302 Found</option>
40
+ <option value="303">303 See Other</option>
41
+ <option value="304">304 Not Modified</option>
42
+ <option value="305">305 Use Proxy</option>
43
+ <option value="306">306 Switch Proxy</option>
44
+ <option value="307">307 Temporary Redirect</option>
45
+ <option value="308">308 Permanent Redirect</option>
46
+ <option value="400">400 Bad Request</option>
47
+ <option value="401">401 Unauthorized</option>
48
+ <option value="402">402 Payment Required</option>
49
+ <option value="403">403 Forbidden</option>
50
+ <option value="405">405 Method Not Allowed</option>
51
+ <option value="406">406 Not Acceptible</option>
52
+ <option value="407">407 Proxy Authentication Required</option>
53
+ <option value="408">408 Request Timeout</option>
54
+ <option value="409">409 Conflict</option>
55
+ <option value="410">410 Gone</option>
56
+ <option value="411">411 Length Required</option>
57
+ <option value="422">422 Unprocessable Entity</option>
58
+ <option value="500">500 Internal Server Error</option>
59
+ <option value="501">501 Not Implemented</option>
60
+ </select>
61
+ </div>
62
+ </div>
63
+ <div class="form-group">
64
+ <div class="text-center">
65
+ <label app-for="content">Content</label>
66
+ </div>
67
+ <div>
68
+ <div class="btn-toolbar">
69
+ <label app-for="isTemplate">Is Template ?</label>
70
+ <input type="checkbox" ng-model="response.isTemplate" />
71
+
72
+ <button ng-show="hasBeautifier" ng-click="reformat()" class="btn btn-xs pull-right">Re Format</button>
73
+ </div>
74
+ <div style="height: 400px;" ui-ace="{mode: aceMode, useWrapMode: true, onLoad: onAceLoaded}" name="content" class="form-control" ng-model="response.content" required></div>
75
+ </div>
76
+ </div>
77
+
78
+ </form>
@@ -0,0 +1 @@
1
+ <h1>Status</h1>
@@ -0,0 +1,19 @@
1
+ <div class="panel panel-default">
2
+ <div class="panel-heading">
3
+ <span>HTTP Stubs</span>
4
+ <button ng-click="deleteSelection()" class="btn btn-xs pull-right" ng-disabled="selectionCount < 1">Delete Selected</button>
5
+ <button ng-click="addStub()" class="btn btn-xs pull-right">Add Stub</button>
6
+ </div>
7
+ <div class="panel-body">
8
+ <table class="table table-striped">
9
+ <tr ng-repeat="stub in requestSpecs">
10
+ <td>{{stub.http_method}}</td>
11
+ <td>{{stub.url}}</td>
12
+ <td><button ng-click="editStub(stub)" class="btn btn-xs btn-default">Edit</button></td>
13
+ <td><button ng-click="deleteStub(stub)" class="btn btn-xs btn-danger">Delete</button></td>
14
+ <td><input type="checkbox" ng-model="stub.$isSelected" ng-change="updateSelection(stub)" /></td>
15
+ </tr>
16
+
17
+ </table>
18
+ </div>
19
+ </div>
@@ -0,0 +1,58 @@
1
+ <div class="modal-header text-center text-uppercase">Edit / Create Stub</div>
2
+ <div class="modal-body">
3
+ <div class="container-fluid">
4
+ <form name="editStubForm" class="form-horizontal" role="form" novalidate>
5
+ <div class="form-group">
6
+ <label app-for="httpMethod" class="col-xs-2">Method</label>
7
+ <div class="col-xs-3">
8
+ <select name="httpMethod" class="form-control" ng-model="stub.httpMethod" required>
9
+ <option value="GET">Get</option>
10
+ <option value="POST">Post</option>
11
+ <option value="UPDATE">Update</option>
12
+ <option value="DELETE">Delete</option>
13
+ </select>
14
+ </div>
15
+
16
+ </div>
17
+ <div class="form-group">
18
+ <label app-for="url" class="col-xs-2">URL</label>
19
+ <div class="col-xs-6">
20
+ <input name="url" class="form-control" type="url" ng-model="stub.url" required/>
21
+ </div>
22
+ <label app-for="urlType" class="col-xs-1">Match</label>
23
+ <div class="col-xs-3">
24
+ <select name="urlType" class="form-control" ng-model="stub.urlType">
25
+ <option value="url">Exact</option>
26
+ <option value="regex">Regex</option>
27
+ </select>
28
+ </div>
29
+
30
+ </div>
31
+ <div class="form-group">
32
+ <label app-for="conditions" class="col-xs-2">Conditions</label>
33
+ <div class="col-xs-10">
34
+ <textarea rows="3" class="form-control" ng-model="stub.conditions"></textarea>
35
+ </div>
36
+
37
+
38
+ </div>
39
+ <div class="form-group">
40
+ <label app-for="note" class="col-xs-2">Note</label>
41
+ <div class="col-xs-10">
42
+ <textarea name="note" class="form-control" ng-model="stub.note"></textarea>
43
+ </div>
44
+ </div>
45
+ <fieldset>
46
+ <legend>Response</legend>
47
+ <app-response-editor ng-model="stub.response"></app-response-editor>
48
+ </fieldset>
49
+ </form>
50
+ </div>
51
+
52
+ </div>
53
+ <div class="modal-footer">
54
+ <button ng-click="onCancel(stub)" class="btn btn-warning">Cancel</button>
55
+ <button ng-disabled="editStubForm.$invalid || editStubForm.$pristine" ng-click="onOk(stub)" class="btn btn-default">
56
+ OK
57
+ </button>
58
+ </div>
@@ -0,0 +1,3 @@
1
+ tr.selected {
2
+ background-color: cornflowerblue;
3
+ }
data/bin/agile_proxy ADDED
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env ruby
2
+ require 'agile_proxy'
3
+ require 'thor'
4
+ require 'rake'
5
+ require 'active_record'
6
+ module AgileProxy
7
+ include ActiveRecord::Tasks
8
+ class Cli < Thor
9
+ class << self
10
+ def data_dir_base
11
+ if RUBY_PLATFORM =~ /win32/
12
+ ENV['APPDATA']
13
+ elsif RUBY_PLATFORM =~ /linux/
14
+ ENV['HOME']
15
+ elsif RUBY_PLATFORM =~ /darwin/
16
+ ENV['HOME']
17
+ elsif RUBY_PLATFORM =~ /freebsd/
18
+ ENV['HOME']
19
+ else
20
+ ENV['HOME']
21
+ end
22
+ end
23
+
24
+ def data_dir
25
+ if Dir.pwd == File.expand_path('..', File.dirname(__FILE__))
26
+ Dir.pwd
27
+ else
28
+ File.join data_dir_base, '.agile_proxy'
29
+ end
30
+ end
31
+
32
+ def environment
33
+ ENV['AGILE_PROXY_ENV'] || Dir.pwd == File.expand_path('..', File.dirname(__FILE__)) ? 'development' : 'production'
34
+ end
35
+ end
36
+ package_name 'Http Flexible Proxy'
37
+ desc 'start PROXY_PORT WEBSERVER_PORT', 'Runs the agile proxy'
38
+ method_options data_dir: data_dir, database_config_file: 'db.yml', env: environment
39
+ def start(proxy_port = nil, webserver_port = nil)
40
+ ensure_database_config_file_exists database_config_file(options)
41
+ puts "Data dir is #{options.data_dir}, environment is #{options.env}"
42
+ setup_for_migrations(options)
43
+ ::AgileProxy.configure do |config|
44
+ config.proxy_port = proxy_port unless proxy_port.nil?
45
+ config.webserver_port = webserver_port unless webserver_port.nil?
46
+ config.environment = options.env
47
+ config.database_config_file = database_config_file(options)
48
+ end
49
+ server = AgileProxy::Server.new
50
+ update_db
51
+ server.start
52
+ end
53
+
54
+ private
55
+
56
+ def setup_for_migrations(options)
57
+ ActiveRecord::Tasks::DatabaseTasks.db_dir = options.data_dir
58
+ ActiveRecord::Tasks::DatabaseTasks.migrations_paths = [File.expand_path('../db/migrate', File.dirname(__FILE__))]
59
+ ActiveRecord::Tasks::DatabaseTasks.env = options.env
60
+ ActiveRecord::Tasks::DatabaseTasks.root = File.expand_path('..', __FILE__)
61
+ ActiveRecord::Migrator.migrations_paths = ActiveRecord::Tasks::DatabaseTasks.migrations_paths
62
+ end
63
+ def run_migrations
64
+ ActiveRecord::Migration.verbose = true
65
+ ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, nil) do |migration|
66
+ ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope)
67
+ end
68
+
69
+ end
70
+
71
+ def server
72
+ AgileProxy::Server.new
73
+ end
74
+
75
+ def update_db
76
+ ActiveRecord::Tasks::DatabaseTasks.create_current
77
+ run_migrations
78
+ # Rake::Task['db:create'].invoke
79
+ # Rake::Task['db:migrate'].invoke
80
+
81
+
82
+ end
83
+
84
+ def ensure_database_config_file_exists(fn)
85
+ return if File.exist? fn
86
+ FileUtils.mkdir_p File.dirname fn
87
+ db = {
88
+ :development => {
89
+ adapter: 'sqlite3',
90
+ database: File.join(File.dirname(fn), 'db', 'development.db')
91
+ },
92
+ :test => {
93
+ adapter: 'sqlite3',
94
+ database: File.join(File.dirname(fn), 'db', 'test.db')
95
+ },
96
+ :production => {
97
+ adapter: 'sqlite3',
98
+ database: File.join(File.dirname(fn), 'db', 'production.db')
99
+ }
100
+ }
101
+ File.open(fn, 'w') {|f| f.write(db.to_yaml) }
102
+ end
103
+ def database_config_file(options)
104
+ if File.exist? options.database_config_file
105
+ options.database_config_file
106
+ else
107
+ File.join(options.data_dir, options.database_config_file)
108
+ end
109
+ end
110
+ end
111
+ end
112
+ AgileProxy::Cli.start(ARGV)
113
+ # AgileProxy::Server.new.start(false)
data/bower.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "agile-proxy",
3
+ "version": "0.0.1",
4
+ "authors": [
5
+ "Gary Taylor <gary.taylor@hismessages.com>"
6
+ ],
7
+ "description": "Http Flexible Proxy",
8
+ "license": "MIT",
9
+ "private": true,
10
+ "ignore": [
11
+ "**/.*",
12
+ "node_modules",
13
+ "bower_components",
14
+ "test",
15
+ "tests"
16
+ ],
17
+ "dependencies": {
18
+ "angular": "~1.3.0",
19
+ "angular-bootstrap": "~0.11.2",
20
+ "bootstrap": "~3.2.0",
21
+ "angular-route": "~1.3.0",
22
+ "angular-resource": "~1.3.0",
23
+ "jquery": "~2.1.1",
24
+ "angular-restmod": "~1.1.3",
25
+ "angular-ui-ace": "~0.1.1"
26
+ }
27
+ }
data/config.yml ADDED
@@ -0,0 +1,6 @@
1
+ development:
2
+ adapter: sqlite3
3
+ database: 'db/development.db'
4
+ test:
5
+ adapter: sqlite3
6
+ database: 'db/test.db'
data/db.yml ADDED
@@ -0,0 +1,10 @@
1
+ ---
2
+ :development:
3
+ :adapter: sqlite3
4
+ :database: "/home/gary/projects/ruby/http-flexible-proxy/db/development.db"
5
+ :test:
6
+ :adapter: sqlite3
7
+ :database: "/home/gary/projects/ruby/http-flexible-proxy/db/test.db"
8
+ :production:
9
+ :adapter: sqlite3
10
+ :database: "/home/gary/projects/ruby/http-flexible-proxy/db/production.db"
@@ -0,0 +1,9 @@
1
+ class CreateUsers < ActiveRecord::Migration
2
+ def change
3
+ create_table :users do |t|
4
+ t.string :name
5
+ t.string :email
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ class CreateApplications < ActiveRecord::Migration
2
+ def change
3
+ create_table :applications do |t|
4
+ t.integer :user_id #The owner
5
+ t.string :name #The application name
6
+ t.timestamps
7
+ end
8
+ add_index :applications, :user_id, :unique => false
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ class CreateRequestSpecs < ActiveRecord::Migration
2
+ def change
3
+ create_table :request_specs do |t|
4
+ t.integer :user_id #The owner of this spec
5
+ t.integer :application_id #The application that this spec belongs to
6
+ t.string :spec #The url matching spec
7
+ t.text :note #A manual note for this spec
8
+ t.integer :response_id #The response template to respond with
9
+ end
10
+ add_index :request_specs, :application_id, :unique => false
11
+ add_index :request_specs, :user_id, :unique => false
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ class CreateResponses < ActiveRecord::Migration
2
+ def change
3
+ create_table :responses do |t|
4
+ t.string :name #A user friendly name for the response
5
+ t.text :content
6
+ t.string :content_type
7
+ t.integer :status_code, :default => 200
8
+ t.text :headers, :default => "{}"
9
+ t.boolean :is_template
10
+ t.float :delay, :default => 0
11
+ t.timestamps
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ class AddMethodToRequestSpecs < ActiveRecord::Migration
2
+ def change
3
+ change_table(:request_specs) do |t|
4
+ t.string :http_method, :default => 'GET' #The http method
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ class RenameRequestSpecColumns < ActiveRecord::Migration
2
+ def change
3
+ rename_column :request_specs, :spec, :url
4
+ change_table :request_specs do |t|
5
+ t.boolean :regex, :default => false #Indicates that the url is a regex
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ class AddUrlTypeToRequestSpecs < ActiveRecord::Migration
2
+ def change
3
+ change_table(:request_specs) do |t|
4
+ t.string :url_type, :default => 'url' #The http method
5
+ end
6
+ remove_column :request_specs, :regex, :boolean
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ class AddConditionsToRequestSpecs < ActiveRecord::Migration
2
+ def change
3
+ change_table(:request_specs) do |t|
4
+ t.text :conditions, :default => '{}' #The conditions object used for matching
5
+ end
6
+ end
7
+ end