actn-api 0.0.1

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 (48) hide show
  1. checksums.yaml +7 -0
  2. data/.env +2 -0
  3. data/.gitignore +17 -0
  4. data/.travis.yml +3 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/Procfile +11 -0
  8. data/README.md +130 -0
  9. data/Rakefile +16 -0
  10. data/actn-api.gemspec +33 -0
  11. data/apis/core/backend.rb +103 -0
  12. data/apis/core/frontend.rb +46 -0
  13. data/apis/public/connect.rb +40 -0
  14. data/apis/public/delete.rb +15 -0
  15. data/apis/public/query.rb +26 -0
  16. data/apis/public/upsert.rb +16 -0
  17. data/bin/actn-api +36 -0
  18. data/config/common.rb +16 -0
  19. data/config/core.rb +1 -0
  20. data/config/haproxy.cfg +73 -0
  21. data/config/public.rb +5 -0
  22. data/db/1_api.sql +9 -0
  23. data/db/schemas/client.json +41 -0
  24. data/db/schemas/user.json +23 -0
  25. data/lib/actn/api/client.rb +92 -0
  26. data/lib/actn/api/core.rb +35 -0
  27. data/lib/actn/api/goliath/params.rb +70 -0
  28. data/lib/actn/api/goliath/validator.rb +54 -0
  29. data/lib/actn/api/mw/auth.rb +122 -0
  30. data/lib/actn/api/mw/cors.rb +58 -0
  31. data/lib/actn/api/mw/no_xss.rb +28 -0
  32. data/lib/actn/api/public.rb +89 -0
  33. data/lib/actn/api/user.rb +59 -0
  34. data/lib/actn/api/version.rb +5 -0
  35. data/lib/actn/api.rb +16 -0
  36. data/test/actn/test_backend.rb +81 -0
  37. data/test/actn/test_client.rb +31 -0
  38. data/test/actn/test_connect.rb +42 -0
  39. data/test/actn/test_delete.rb +53 -0
  40. data/test/actn/test_frontend.rb +32 -0
  41. data/test/actn/test_query.rb +60 -0
  42. data/test/actn/test_upsert.rb +77 -0
  43. data/test/actn/test_user.rb +37 -0
  44. data/test/minitest_helper.rb +27 -0
  45. data/test/support/test.html +7 -0
  46. data/views/core/app.erb +1 -0
  47. data/views/public/connect.erb +123 -0
  48. metadata +258 -0
@@ -0,0 +1,81 @@
1
+ require 'minitest_helper'
2
+ require 'goliath/test_helper'
3
+ require 'apis/core/backend'
4
+
5
+ require 'actn/api/client'
6
+ require 'actn/api/user'
7
+ require 'actn/jobs/job'
8
+ require 'actn/db/model'
9
+
10
+
11
+ module Actn
12
+ module Api
13
+ class TestBackend < MiniTest::Test
14
+
15
+ include Goliath::TestHelper
16
+
17
+ def setup
18
+ @model = Actn::DB::Model.create(name: "papa")
19
+
20
+ @api_options = { :verbose => true, :log_stdout => true, config: "#{Actn::Api.root}/config/core.rb" }
21
+ @err = Proc.new { assert false, "API request failed" }
22
+
23
+ @client = Client.create({domain: "localhost:9900"})
24
+ @headrw = { 'X_APIKEY' => @client.credentials['apikey'], 'X_SECRET' => @client.credentials['secret'] }
25
+ end
26
+
27
+ def teardown
28
+ @model.destroy
29
+ Actn::DB::Model.delete_all
30
+ @client.destroy
31
+ end
32
+
33
+ def test_index
34
+ with_api(Backend,@api_options) do
35
+ get_request({path: '/models', head: @headrw }, @err) do |c|
36
+ assert_equal 200, c.response_header.status
37
+ assert_match /Papa/, c.response
38
+ end
39
+ end
40
+ end
41
+
42
+ def test_show
43
+ with_api(Backend,@api_options) do
44
+ get_request({path: "/models/#{@model.uuid}", head: @headrw }, @err) do |c|
45
+ assert_equal 200, c.response_header.status
46
+ assert_match /Papa/, c.response
47
+ end
48
+ end
49
+ end
50
+
51
+ def test_create
52
+ with_api(Backend,@api_options) do
53
+ post_request({path: "/models", head: @headrw, body: {name: "bobo"} }, @err) do |c|
54
+ assert_equal 200, c.response_header.status
55
+ assert_match /Bobo/, c.response
56
+ end
57
+ end
58
+ end
59
+
60
+ def test_update
61
+ with_api(Backend,@api_options) do
62
+ put_request({path: "/models/#{@model.uuid}", head: @headrw, body: {city: "London"} }, @err) do |c|
63
+ assert_equal 200, c.response_header.status
64
+ assert_match /London/, c.response
65
+ end
66
+ end
67
+ end
68
+
69
+ def test_destroy
70
+ with_api(Backend,@api_options) do
71
+ model = Actn::DB::Model.create(name: "destroyme")
72
+ delete_request({path: "/models/#{model.uuid}", head: @headrw }, @err) do |c|
73
+ assert_equal 200, c.response_header.status
74
+ assert_match /Destroyme/ , c.response
75
+ end
76
+ end
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,31 @@
1
+ require 'minitest_helper'
2
+ require 'actn/api/client'
3
+
4
+ module Actn
5
+ module Api
6
+ class TestClient < Minitest::Test
7
+
8
+ def teardown
9
+ Client.delete_all
10
+ end
11
+
12
+ def test_create
13
+ client = Client.create(domain: "localhost:9000")
14
+ assert client.persisted?
15
+ end
16
+
17
+ def test_auth
18
+ client = Client.create(domain: "localhost:9000")
19
+ creds = client.credentials
20
+
21
+ found = Client.find_for_auth('localhost:9000', creds['apikey'] )
22
+ assert found
23
+ assert found.auth_by_secret(creds['secret'])
24
+
25
+ found.set_session("sessionid1234")
26
+ assert found.auth_by_session("sessionid1234")
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,42 @@
1
+ require 'minitest_helper'
2
+ require 'goliath/test_helper'
3
+ require 'apis/public/connect'
4
+ require 'actn/api/client'
5
+ require 'actn/db'
6
+
7
+ module Actn
8
+ module Api
9
+ class TestConnect < MiniTest::Test
10
+
11
+ include Goliath::TestHelper
12
+
13
+ def setup
14
+
15
+ @api_options = { :verbose => true, :log_stdout => true, config: "#{Actn::Api.root}/config/core.rb" }
16
+ @err = Proc.new { assert false, "API request failed" }
17
+ @client = Client.create({domain: "localhost:9900"})
18
+
19
+ end
20
+
21
+ def teardown
22
+
23
+
24
+ @client.destroy
25
+
26
+ end
27
+
28
+ def test_not_connect_because_referer_check
29
+
30
+ with_api(Connect,@api_options) do
31
+ get_request({path: '/connect', query: { 'apikey' => @client.credentials['apikey'] } }, @err) do |c|
32
+ assert_equal 401, c.response_header.status
33
+ end
34
+ end
35
+
36
+ end
37
+
38
+
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,53 @@
1
+ require 'minitest_helper'
2
+ require 'goliath/test_helper'
3
+ require 'apis/public/delete'
4
+ require 'actn/api/client'
5
+ require 'actn/db'
6
+
7
+ module Actn
8
+ module Api
9
+ class TestDelete < MiniTest::Test
10
+
11
+ include Goliath::TestHelper
12
+
13
+ def setup
14
+
15
+ mdata = Oj.dump({ name: "Supporter", schema: {
16
+ type: 'object',
17
+ properties: {
18
+ first_name: { type: 'string' }
19
+ },
20
+ required: ['first_name']
21
+ }
22
+ })
23
+ DB.exec_func(:upsert, 'core', 'models', mdata)
24
+
25
+ 10.times{ |i| DB::Set['supporters'].upsert({path: "/supporters", first_name: "supporter_#{random_str}"}) }
26
+
27
+ @uuid = Oj.load(DB::Set['supporters'].query({select: 'uuid', limit: 1}))[0]['uuid']
28
+
29
+ @api_options = { :verbose => true, :log_stdout => true, config: "#{Actn::Api.root}/config/core.rb" }
30
+ @err = Proc.new { assert false, "API request failed" }
31
+
32
+ @client = Client.create({domain: "localhost:9900"})
33
+ @headr = { 'X_APIKEY' => @client.credentials['apikey'] }
34
+ @headrw = { 'X_APIKEY' => @client.credentials['apikey'], 'X_SECRET' => @client.credentials['secret'] }
35
+ end
36
+
37
+ def teardown
38
+ DB.exec_func(:delete, 'core', 'models', Oj.dump(name:"Supporter"))
39
+ @client.destroy
40
+ end
41
+
42
+ def test_remove
43
+ with_api(Delete,@api_options) do
44
+ delete_request({path: '/supporters', head: @headrw, query: { 'uuid' => @uuid} }, @err) do |c|
45
+ assert_equal 200, c.response_header.status
46
+ assert_match /9/, DB::Set['supporters'].count
47
+ end
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,32 @@
1
+ require 'minitest_helper'
2
+ require 'goliath/test_helper'
3
+ require 'apis/core/frontend'
4
+ require 'actn/api/client'
5
+ require 'actn/api/user'
6
+ require 'actn/jobs/job'
7
+ require 'actn/db/model'
8
+ require 'actn/db'
9
+
10
+ module Actn
11
+ module Api
12
+ class TestFrontend < MiniTest::Test
13
+
14
+ include Goliath::TestHelper
15
+
16
+ def setup
17
+
18
+ @api_options = { :verbose => true, :log_stdout => true, config: "#{Actn::Api.root}/config/core.rb" }
19
+ @err = Proc.new { assert false, "API request failed" }
20
+ end
21
+
22
+ def test_front_api
23
+ with_api(Frontend,@api_options) do
24
+ get_request({path: '/' }, @err) do |c|
25
+ assert_equal 200, c.response_header.status
26
+ end
27
+ end
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,60 @@
1
+ require 'minitest_helper'
2
+ require 'goliath/test_helper'
3
+ require 'apis/public/query'
4
+ require 'actn/api/client'
5
+ require 'actn/db'
6
+
7
+ module Actn
8
+ module Api
9
+ class TestQuery < MiniTest::Test
10
+
11
+ include Goliath::TestHelper
12
+
13
+ def setup
14
+
15
+ DB.exec_func(:upsert, 'core', 'models', Oj.dump({name: "Supporter"}))
16
+
17
+ 10.times{ |i| DB::Set['supporters'].upsert({path: "/supporters", name: "supporter_#{random_str}"}) }
18
+ DB::Set['supporters'].upsert({path: "/supporters/customs", name: "user_#{random_str}"})
19
+
20
+ @api_options = { :verbose => true, :log_stdout => true, config: "#{Actn::Api.root}/config/core.rb" }
21
+ @err = Proc.new { assert false, "API request failed" }
22
+ @client = Client.create({domain: "localhost:9900"})
23
+ @headr = { 'X_APIKEY' => @client.credentials['apikey'] }
24
+
25
+ @query = { 'query' => Oj.dump({ 'select' => '*' }) }
26
+
27
+ end
28
+
29
+ def teardown
30
+
31
+ DB.exec_func(:delete, 'core', 'models', Oj.dump(name:"Supporter"))
32
+ @client.destroy
33
+
34
+ end
35
+
36
+ def test_query
37
+
38
+ with_api(Query,@api_options) do
39
+ get_request({path: '/supporters', head: @headr, query: @query }, @err) do |c|
40
+ assert_equal 200, c.response_header.status
41
+ assert_equal 10, Oj.load(c.response).size
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ def test_path
48
+
49
+ with_api(Query,@api_options) do
50
+
51
+ get_request({path: '/supporters/customs', head: @headr, query: @query }, @err) do |c|
52
+ assert_equal 200, c.response_header.status
53
+ assert_equal 1, Oj.load(c.response).size
54
+ end
55
+ end
56
+ end
57
+
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,77 @@
1
+ require 'minitest_helper'
2
+ require 'goliath/test_helper'
3
+ require 'apis/public/upsert'
4
+ require 'actn/api/client'
5
+ require 'actn/db'
6
+
7
+ module Actn
8
+ module Api
9
+ class TestUpsert < MiniTest::Test
10
+
11
+ include Goliath::TestHelper
12
+
13
+ def setup
14
+
15
+ mdata = Oj.dump({ name: "Supporter", schema: {
16
+ type: 'object',
17
+ properties: {
18
+ first_name: { type: 'string' }
19
+ },
20
+ required: ['first_name']
21
+ }
22
+ })
23
+ DB.exec_func(:upsert, 'core', 'models', mdata)
24
+
25
+ 10.times{ |i| DB::Set['supporters'].upsert({path: "/supporters", first_name: "supporter_#{random_str}"}) }
26
+
27
+ @uuid = Oj.load(DB::Set['supporters'].query({select: 'uuid', limit: 1}))[0]['uuid']
28
+
29
+ @api_options = { :verbose => true, :log_stdout => true, config: "#{Actn::Api.root}/config/core.rb" }
30
+ @err = Proc.new { assert false, "API request failed" }
31
+
32
+ @client = Client.create({domain: "localhost:9900"})
33
+ @headr = { 'X_APIKEY' => @client.credentials['apikey'] }
34
+ @headrw = { 'X_APIKEY' => @client.credentials['apikey'], 'X_SECRET' => @client.credentials['secret'] }
35
+ end
36
+
37
+ def teardown
38
+ DB.exec_func(:delete, 'core', 'models', Oj.dump(name:"Supporter"))
39
+ @client.destroy
40
+ end
41
+
42
+ def test_validation
43
+ with_api(Upsert,@api_options) do
44
+ body = { data: Oj.dump({}) }
45
+ post_request({path: '/supporters', head: @headrw, body: body }, @err) do |c|
46
+ assert_equal 406, c.response_header.status
47
+ assert_equal '{"errors":{"validation":{"first_name":{"required":true}}}}', c.response
48
+ end
49
+ end
50
+ end
51
+
52
+ def test_insert
53
+ with_api(Upsert,@api_options) do
54
+
55
+ body = { first_name: "Lemmy" }
56
+
57
+ post_request({path: '/supporters', head: @headrw, body: body }, @err) do |c|
58
+ assert_equal 200, c.response_header.status
59
+ assert_match /11/, DB::Set['supporters'].query({select: "COUNT(id)"})
60
+ end
61
+ end
62
+ end
63
+
64
+ def test_update
65
+ # body = { 'user' => Oj.dump({ 'select' => '*' }) }
66
+ with_api(Upsert,@api_options) do
67
+ body = { uuid: @uuid, first_name: "Bonzo", last_name: "Boke" }
68
+ put_request({path: '/supporters', head: @headrw, body: body }, @err) do |c|
69
+ assert_equal 200, c.response_header.status
70
+ assert_match /uuid/, c.response
71
+ end
72
+ end
73
+ end
74
+
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,37 @@
1
+ require 'minitest_helper'
2
+ require 'actn/api/user'
3
+
4
+ module Actn
5
+ module Api
6
+ class TestUser < Minitest::Test
7
+
8
+ def teardown
9
+ User.delete_all
10
+ end
11
+
12
+ def test_create_and_find_for_auth
13
+ user = User.create({'first_name' => "Name", 'last_name' => "Last",
14
+ 'email' => "email2@email.com", 'password' => "password", 'password_confirmation' => "password"})
15
+ assert user.persisted?
16
+ assert User.find_for_auth('email' => "email2@email.com", 'password' => 'password')
17
+ end
18
+
19
+ def test_validation
20
+ user = User.create({})
21
+ assert_match /can't be blank/, user.errors.inspect
22
+ end
23
+
24
+ def test_validation_uniq
25
+ User.create('first_name' => "Name", 'last_name' => "Last", 'email' => "email4@email.com",
26
+ 'password' => "password", 'password_confirmation' => "password")
27
+
28
+ user = User.create('first_name' => "Name", 'last_name' => "Last", 'email' => "email4@email.com",
29
+ 'password' => "password", 'password_confirmation' => "password")
30
+
31
+ assert_match /has already been taken/, user.errors.inspect
32
+ end
33
+
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,27 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../', __FILE__)
2
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
3
+ $stdout.sync = true
4
+ $stderr.sync = true
5
+
6
+ ENV['RACK_ENV']='test'
7
+ ENV['DATABASE_URL']="postgres://localhost:5432/actn_test"
8
+ ENV['SECRET']="secret"
9
+
10
+ require 'actn/api'
11
+ require 'actn/db'
12
+ require 'actn/paths'
13
+
14
+ require 'minitest/autorun'
15
+ require 'minitest/pride'
16
+ require 'i18n'
17
+
18
+
19
+ I18n.enforce_available_locales = false
20
+
21
+ class MiniTest::Test
22
+ private
23
+
24
+ def random_str
25
+ (0...8).map { (65 + rand(26)).chr }.join
26
+ end
27
+ end
@@ -0,0 +1,7 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head></head>
4
+ <body>
5
+ <script id="actn" src="http://api.lvh.me:5000/connect?apikey=9073876249001ca995f2bf05d17dfac2"></script>
6
+ </body>
7
+ </html>
@@ -0,0 +1 @@
1
+ CORE!
@@ -0,0 +1,123 @@
1
+ (function(window){
2
+
3
+ function init(){
4
+
5
+ var
6
+ verbs = ['get','post','put','delete'],
7
+ verb = null,
8
+ i = null,
9
+ actn = {
10
+ _csrf: '<%= csrf %>',
11
+ _apikey: '<%= apikey %>',
12
+ _host: 'http://<%= host %>'
13
+ }
14
+
15
+ jQuery.ajaxSetup({
16
+ beforeSend: function(xhr) {
17
+ xhr.setRequestHeader('X_CSRF_TOKEN', actn._csrf);
18
+ xhr.setRequestHeader('X_APIKEY', actn._apikey);
19
+ },
20
+ complete: function(jxhr) {
21
+ actn._csrf = jxhr.getResponseHeader('X_CSRF_TOKEN');
22
+ },
23
+ crossDomain: true
24
+ });
25
+
26
+ $.support.cors = true;
27
+
28
+ actn.api = function(params) {
29
+
30
+ switch(params['type']){
31
+ case "GET":
32
+ params['data'] = params['data'] || {}
33
+ break;
34
+ case "POST":
35
+ case "PUT":
36
+ case "PATCH":
37
+ params['data'] = params['data']
38
+ break;
39
+ }
40
+
41
+ $.ajax({
42
+ url: actn._host + params['path'],
43
+ type: params['type'],
44
+ xhrFields: { withCredentials: true },
45
+ dataType: 'json',
46
+ data: params['data'],
47
+ success: function(data) {
48
+ actn.lastResult = data;
49
+ if(typeof params['success'] === 'function'){
50
+ params['success'](data);
51
+ }
52
+ },
53
+ error: function(jqXHR,textStatus,errorThrown) {
54
+ if(jqXHR.responseText !== ""){
55
+ actn.lastError = JSON.parse(jqXHR.responseText);
56
+ }
57
+ if(typeof params['error'] === 'function'){
58
+ params['error'](jqXHR);
59
+ }
60
+ }
61
+ })
62
+ }
63
+
64
+ actn.get = function(params){
65
+ params['type'] = "GET";
66
+ actn.api(params);
67
+ }
68
+
69
+ actn.post = function(params){
70
+ params['type'] = "POST";
71
+ actn.api(params);
72
+ }
73
+
74
+ actn.put = function(params){
75
+ params['type'] = "PUT";
76
+ actn.api(params);
77
+ }
78
+
79
+ actn.delete = function(params){
80
+ params['type'] = "DELETE";
81
+ actn.api(params);
82
+ }
83
+
84
+ window.actn = actn;
85
+
86
+ setTimeout(function(){
87
+ var src = $("#actn").attr("src");
88
+ $("#actn").remove();
89
+ loadScript("actn",src);
90
+ },<%= ttl %>);
91
+
92
+ }
93
+
94
+ function loadScript(id, url, callback) {
95
+
96
+ var script = document.createElement("script")
97
+ script.id = id;
98
+ script.type = "text/javascript";
99
+
100
+ if (script.readyState) { //IE
101
+ script.onreadystatechange = function () {
102
+ if (script.readyState == "loaded" || script.readyState == "complete") {
103
+ script.onreadystatechange = null;
104
+ if (typeof(callback) === "function"){ callback(); }
105
+ }
106
+ };
107
+ } else { //Others
108
+ script.onload = function () {
109
+ if (typeof(callback) === "function"){ callback(); }
110
+ };
111
+ }
112
+
113
+ script.src = url;
114
+ document.getElementsByTagName("head")[0].appendChild(script);
115
+ }
116
+
117
+ if(typeof jQuery === 'undefined'){
118
+ loadScript("jquery", "https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js", init);
119
+ }else{
120
+ init();
121
+ }
122
+
123
+ })(window);