agile-proxy 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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