surikat 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.idea/workspace.xml +186 -81
  4. data/README.md +1 -1
  5. data/exe/surikat +2 -1
  6. data/frontend-demo/LICENSE +21 -0
  7. data/frontend-demo/README.md +89 -0
  8. data/frontend-demo/aaa.html +153 -0
  9. data/frontend-demo/aaa.js +115 -0
  10. data/frontend-demo/authors.js +159 -0
  11. data/frontend-demo/books.html +138 -0
  12. data/frontend-demo/books.js +201 -0
  13. data/frontend-demo/gulpfile.js +42 -0
  14. data/frontend-demo/index.html +137 -0
  15. data/frontend-demo/package-lock.json +4757 -0
  16. data/frontend-demo/package.json +36 -0
  17. data/frontend-demo/vendor/bootstrap/css/bootstrap.css +8981 -0
  18. data/frontend-demo/vendor/bootstrap/css/bootstrap.css.map +1 -0
  19. data/frontend-demo/vendor/bootstrap/css/bootstrap.min.css +7 -0
  20. data/frontend-demo/vendor/bootstrap/css/bootstrap.min.css.map +1 -0
  21. data/frontend-demo/vendor/bootstrap/js/bootstrap.bundle.js +6444 -0
  22. data/frontend-demo/vendor/bootstrap/js/bootstrap.bundle.js.map +1 -0
  23. data/frontend-demo/vendor/bootstrap/js/bootstrap.bundle.min.js +7 -0
  24. data/frontend-demo/vendor/bootstrap/js/bootstrap.bundle.min.js.map +1 -0
  25. data/frontend-demo/vendor/bootstrap/js/bootstrap.js +3927 -0
  26. data/frontend-demo/vendor/bootstrap/js/bootstrap.js.map +1 -0
  27. data/frontend-demo/vendor/bootstrap/js/bootstrap.min.js +7 -0
  28. data/frontend-demo/vendor/bootstrap/js/bootstrap.min.js.map +1 -0
  29. data/frontend-demo/vendor/graphql.min.js +1 -0
  30. data/frontend-demo/vendor/jquery/jquery.js +10364 -0
  31. data/frontend-demo/vendor/jquery/jquery.min.js +2 -0
  32. data/frontend-demo/vendor/jquery/jquery.min.map +1 -0
  33. data/frontend-demo/vendor/jquery/jquery.slim.js +8269 -0
  34. data/frontend-demo/vendor/jquery/jquery.slim.min.js +2 -0
  35. data/frontend-demo/vendor/jquery/jquery.slim.min.map +1 -0
  36. data/frontend-demo/vendor/notify.min.js +1 -0
  37. data/lib/surikat.rb +16 -11
  38. data/lib/surikat/templates/aaa_queries.rb.tmpl +5 -3
  39. data/lib/surikat/templates/config.ru.tmpl +5 -0
  40. data/lib/surikat/version.rb +1 -1
  41. metadata +33 -2
data/README.md CHANGED
@@ -407,7 +407,7 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/alxx/s
407
407
 
408
408
  ## Version
409
409
 
410
- This code reflects version 0.2.5.
410
+ This code reflects version 0.3.0.
411
411
 
412
412
  ## License
413
413
 
@@ -143,7 +143,8 @@ def exemplify_mutations(query_name, route)
143
143
  r_args.each.each do |var_name, var_type|
144
144
  k_var_name = ActiveSupport::Inflector.singularize(var_name.underscore)
145
145
  variables[k_var_name] = {}
146
- all_types[var_type]['arguments'].each do |a, t|
146
+ arguments = all_types[var_type] && all_types[var_type]['arguments'] || []
147
+ arguments.each do |a, t|
147
148
  next if a == 'id' && route['method'] == 'create' # yes, someone may make create queries with other names
148
149
  if Types::BASIC.include?(t)
149
150
  variables[k_var_name][a] = random_values(t)
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013-2018 Blackrock Digital LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,89 @@
1
+ # Surikat Demo
2
+
3
+ This is a collection of three HTML pages which test a few basic [Surikat](https://github.com/alxx/surikat) features.
4
+ The layout is based on [Bare](http://startbootstrap.com/template-overviews/bare/) by [Start Bootstrap](http://startbootstrap.com/)
5
+ and, in addition to Bootstrap and jQuery, it uses [jquery-graphql](https://www.jquerycards.com/media/tables-graphs/jquery-graphql/).
6
+
7
+ ## Page 1. Authors
8
+
9
+ The purpose of this page is to implement CRUD (create, retrieve, update and delete) operations using GraphQL.
10
+ These operations can automatically created in a Surikat app by means of a scaffold command:
11
+
12
+ ```bash
13
+ $ surikat generate scaffold Author name:string yob:integer
14
+ $ rake db:migrate
15
+ ```
16
+
17
+ ## Page 2. Books
18
+
19
+ To further comment on Surikat's simplicity, there's a secondary page where an author's list of books
20
+ can be managed. In order for this to work, first the underlying scaffold must be generated:
21
+
22
+ ```bash
23
+ $ surikat generate scaffold Book title:string
24
+ $ rake db:migrate
25
+ ```
26
+
27
+ Then, the `Author` and `Book` models need to be connected
28
+
29
+ ```ruby
30
+ class Author < Surikat::BaseModel
31
+ has_many :books, dependent: :destroy
32
+ end
33
+ ```
34
+
35
+ ```ruby
36
+ class Book < Surikat::BaseModel
37
+ belongs_to :author
38
+ end
39
+ ```
40
+
41
+ And finally, the `Author` GraphQL type needs to include a field called `books`. So in `config/types.yml` add the last line, `books: "[Book]"`:
42
+
43
+ ```yaml
44
+ Author:
45
+ type: Output
46
+ fields:
47
+ name: String
48
+ yob: Int
49
+ age: Int
50
+ created_at: String
51
+ updated_at: String
52
+ id: ID
53
+ books: "[Book]"
54
+ ```
55
+
56
+ ## Page 3. AAA
57
+
58
+ The last page in this demo works with authentication, authorisation and access control (AAA).
59
+
60
+ It requires that AAA is added to the Surikat app:
61
+
62
+ ```bash
63
+ $ surikat generate aaa
64
+ $ rake db:migrate
65
+ ```
66
+
67
+ and that there's a user to login with:
68
+
69
+ ```bash
70
+ $ bin/console
71
+ > User.create email:'a@b.c', password:'1'
72
+ ```
73
+
74
+ ### Live Reload
75
+
76
+ If you want to edit this demo and would like the benefits of live reloading, you can use `gulp`. First, install the necessary modules:
77
+
78
+ ```bash
79
+ $ npm install
80
+ ```
81
+
82
+ Then, the `gulp` command line interface:
83
+
84
+ ```bash
85
+ $ npm install gulp-cli
86
+ ```
87
+
88
+ Now, if you run `gulp dev` the index page will open in a browser, and it will refresh
89
+ when you make changes in the files.
@@ -0,0 +1,153 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+
6
+ <meta charset="utf-8">
7
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
8
+ <meta name="description" content="">
9
+ <meta name="author" content="">
10
+
11
+ <title>Surikat Demo</title>
12
+
13
+ <!-- Bootstrap core CSS -->
14
+ <link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
15
+
16
+ <!-- Custom styles for this template -->
17
+ <style>
18
+ body {
19
+ padding-top: 54px;
20
+ }
21
+
22
+ @media (min-width: 992px) {
23
+ body {
24
+ padding-top: 56px;
25
+ }
26
+ }
27
+
28
+ </style>
29
+
30
+ </head>
31
+
32
+ <body>
33
+
34
+ <!-- Navigation -->
35
+ <nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
36
+ <div class="container">
37
+ <a class="navbar-brand" href="https://github.com/alxx/surikat">
38
+ <img src="https://camo.githubusercontent.com/b17e471f697a115ac68f565e637602328c402478/68747470733a2f2f692e696d6775722e636f6d2f4f6c43557733382e706e67"
39
+ width="26" height="40" alt="">
40
+ Surikat Demo
41
+ </a>
42
+ <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive"
43
+ aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
44
+ <span class="navbar-toggler-icon"></span>
45
+ </button>
46
+ <div class="collapse navbar-collapse" id="navbarResponsive">
47
+ <ul class="navbar-nav ml-auto">
48
+ <li class="nav-item">
49
+ <a class="nav-link" href="index.html">Authors</a>
50
+ </li>
51
+ <li class="nav-item">
52
+ <a class="nav-link" href="books.html">Books</a>
53
+ </li>
54
+ <li class="nav-item active">
55
+ <a class="nav-link" href="aaa.html">AAA</a>
56
+ <span class="sr-only">(current)</span>
57
+ </li>
58
+ </ul>
59
+ </div>
60
+ </div>
61
+ </nav>
62
+
63
+ <!-- Page Content -->
64
+ <div class="container">
65
+ <div class="row">
66
+ <div class="col-lg-12 text-center">
67
+ <h1 class="mt-5">Authentication, Authorisation, Access</h1>
68
+
69
+
70
+ <div id="not-logged-in">
71
+ <form>
72
+
73
+ <div class="input-group mb-3">
74
+ <div class="input-group-prepend">
75
+ <label class="input-group-text" for="email">Email</label>
76
+ </div>
77
+ <input type="text" class="form-control" placeholder="Enter your email address" id="email">
78
+ </div>
79
+
80
+ <div class="input-group mb-3">
81
+ <div class="input-group-prepend">
82
+ <label class="input-group-text">Password</label>
83
+ </div>
84
+ <input type="password" class="form-control" placeholder="Enter your password" id="password">
85
+ </div>
86
+
87
+ <button type="submit" class="btn btn-primary" id="btnLogin" disabled="disabled">Login</button>
88
+ </form>
89
+ </div>
90
+
91
+ <div id="logged-in" style="display: none;">
92
+ <div id="current_user" class="alert alert-primary mb-3">You are logged in.</div>
93
+
94
+ <table class="table mb-lg-5">
95
+ <tr width="100%">
96
+ <td width="33%">
97
+ <div class="card mb-lg-5">
98
+ Click this button to make a call to CurrentUser.<br/>
99
+ <button class="btn btn-outline-info" id="btnCurUser">Retrieve current user</button>
100
+ </div>
101
+ </td>
102
+ <td width="33%">
103
+ <div class="card mb-lg-5">
104
+ Try to call DemoTwo, which only users who have the role 'hotdog' or 'hamburger' may call
105
+ <button class="btn btn-outline-info" id="btnProtected">Try a role-protected action</button>
106
+ </div>
107
+ </td>
108
+ <td width="33%">
109
+ <div class="card mb-lg-5">
110
+ Logout as this user and return to the login form
111
+ <button class="btn btn-outline-info" id="btnLogout">Logout</button>
112
+ </div>
113
+ </td>
114
+ </tr>
115
+ </table>
116
+
117
+ </div>
118
+
119
+ </div>
120
+ </div>
121
+ </div>
122
+
123
+ <!-- Bootstrap core JavaScript -->
124
+ <script src="vendor/jquery/jquery.min.js"></script>
125
+ <script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
126
+
127
+ <!-- GraphQL Plugin -->
128
+ <script src="vendor/graphql.min.js"></script>
129
+
130
+ <!-- Notifications -->
131
+ <script src="vendor/notify.min.js"></script>
132
+
133
+ <!-- App -->
134
+ <script src="aaa.js"></script>
135
+
136
+ <script language="JavaScript">
137
+ $(document).ready(function () {
138
+
139
+ $('input[type="text"]').keyup(function () {
140
+ if ($(this).val() != '') {
141
+ $(':input[type="submit"]').prop('disabled', false);
142
+ } else {
143
+ $(':input[type="submit"]').prop('disabled', true);
144
+ }
145
+ });
146
+
147
+ run();
148
+ })
149
+ </script>
150
+
151
+ </body>
152
+
153
+ </html>
@@ -0,0 +1,115 @@
1
+ var graph = graphql("http://localhost:3000/", {
2
+ headers: {
3
+ 'Surikat': 'abc'//Math.random().toString(36).replace(/[^a-z]+/g, '')
4
+ }
5
+ });
6
+
7
+ var current_user;
8
+
9
+ $("#btnLogin").click(function (el) {
10
+ el.preventDefault();
11
+
12
+ $(this).attr('disabled', true);
13
+
14
+ var email = $('#email').val();
15
+ var password = $('#password').val();
16
+
17
+ authenticate(email, password);
18
+ });
19
+
20
+ $('#btnCurUser').click(function (el) {
21
+ el.preventDefault();
22
+
23
+ $(this).attr('disabled', true);
24
+ getCurrentUser();
25
+ });
26
+
27
+ $('#btnLogout').click(function (e) {
28
+ e.preventDefault();
29
+
30
+ logout();
31
+ });
32
+
33
+ $('#btnProtected').click(function (e) {
34
+ e.preventDefault();
35
+
36
+ demoTwo();
37
+ });
38
+
39
+ function demoTwo() {
40
+ var q = '{DemoTwo}';
41
+ console.log('calling a role-protected method...');
42
+
43
+ graph.query.run(q).then(function (r) {
44
+ $.notify(r.DemoTwo, {position: 'top left', autoHideDelay: 3000})
45
+ }).catch(function(error) {
46
+ if (error.find(e => e.accessDenied)) {
47
+ $.notify('Access denied', {position: "top center", autoHideDelay: 3000});
48
+ } else {
49
+ alert(JSON.stringify(error))
50
+ }
51
+ })
52
+ }
53
+
54
+ function logout() {
55
+ var q = '{Logout}';
56
+ console.log('logging out...');
57
+
58
+ graph.query.run(q).then(function (r) {
59
+ $.notify('You have been logged out.', {position: "top center", autoHideDelay: 3000});
60
+ $('#not-logged-in').show();
61
+ $('#logged-in').hide();
62
+ $('#btnLogin').removeAttr('disabled');
63
+ }).catch(function(error) {
64
+ alert(JSON.stringify(error))
65
+ })
66
+ }
67
+
68
+ function getCurrentUser() {
69
+ var q = '{CurrentUser {id,email,roleids}}';
70
+ console.log('getting current user...');
71
+
72
+ graph.query.run(q).then(function (r) {
73
+ current_user = r.CurrentUser;
74
+ console.log('current user: ', current_user);
75
+
76
+ $('#not-logged-in').hide();
77
+ $('#logged-in').show();
78
+
79
+ $('#current_user').html(`You are logged in as ${current_user.email}. Your roles are: ${current_user.roleids}`);
80
+ $.notify(`You are logged in as ${current_user.email}. Your roles are: ${current_user.roleids}`, {position: "top center", autoHideDelay: 3000});
81
+ $('#btnCurUser').removeAttr('disabled');
82
+ }).catch(function(error) {
83
+ if (error.find(e => e.accessDenied)) {
84
+ $.notify('Bad credentials', {position: "top center", autoHideDelay: 3000});
85
+ } else {
86
+ alert(JSON.stringify(error))
87
+ }
88
+ alert(JSON.stringify(error))
89
+ })
90
+ }
91
+
92
+ function authenticate(email, password) {
93
+ var q = `{Authenticate(email: "${email}", password: "${password}")}`;
94
+ console.log(`logging in...`);
95
+
96
+ graph.query.run(q).then(function (r) {
97
+ $.notify('Successfully authenticated!', {position: "top center", autoHideDelay: 3000});
98
+ $('#not-logged-in').hide();
99
+ $('#logged-in').show();
100
+ }).catch(function (error) {
101
+ if (error.find(e => e.noResult)) {
102
+ $.notify('Bad credentials', {position: "top center", autoHideDelay: 3000});
103
+ } else {
104
+ alert(JSON.stringify(error))
105
+ }
106
+ $('#btnLogin').removeAttr('disabled');
107
+ })
108
+ }
109
+
110
+
111
+ function run() {
112
+ console.log('starting...');
113
+
114
+ getCurrentUser();
115
+ }
@@ -0,0 +1,159 @@
1
+ var graph = graphql("http://localhost:3000/", {});
2
+
3
+ $(document).on("click", '#all tbody tr', function (tr) {
4
+ var id = $(tr.target.parentNode).data('id');
5
+
6
+ // simple way to ignore graphql if the click was on a link
7
+ if (tr.target.outerHTML.indexOf('<a href') == -1) {
8
+ setAuthor(id);
9
+ }
10
+ });
11
+
12
+ $("#btnDelete").click(function (el) {
13
+ el.preventDefault();
14
+
15
+ var id = $('#author_id').val();
16
+
17
+ $(this).attr('disabled', true);
18
+ $("#btnSubmit").attr('disabled', true);
19
+ deleteAuthor(id);
20
+ });
21
+
22
+ $("#btnSubmit").click(function (el) {
23
+ el.preventDefault();
24
+
25
+ $(this).attr('disabled', true);
26
+ $("#btnDelete").attr('disabled', true);
27
+
28
+ var id = $('#author_id').val();
29
+ var name = $('#name').val();
30
+ var yob = $('#yob').val();
31
+
32
+ if (id == null || id === "") {
33
+ console.log('save author');
34
+ saveAuthor(name, yob);
35
+ } else {
36
+ console.log('update author');
37
+ updateAuthor(id, name, yob);
38
+ }
39
+ });
40
+
41
+ function setAuthor(id) {
42
+ var q = `{Author(id: ${id}) {name,yob}}`;
43
+ console.log(`loading author ${id}...`);
44
+
45
+ graph.query.run(q).then(function (r) {
46
+ author = r.Author;
47
+ console.log(author);
48
+ $("#author_id").val(id);
49
+ $("#name").val(author.name);
50
+ $("#yob").val(author.yob);
51
+
52
+ $("#btnSubmit").removeAttr('disabled');
53
+ $("#btnDelete").removeAttr('disabled');
54
+ }).catch(function (error) {
55
+ alert(JSON.stringify(error))
56
+ })
57
+ }
58
+
59
+ function saveAuthor(name, yob) {
60
+ var q = `mutation Author($author: AuthorInput) {Author(author: $author) {id}}`;
61
+ var vars = {author: {name: name, yob: parseInt(yob)}};
62
+ console.log('creating author...');
63
+
64
+ var create = graph(q);
65
+
66
+ create(vars).then(function (r) {
67
+ author = r.Author;
68
+ $.notify(`Author created with id ${author.id}`, {position: "top center", autoHideDelay: 3000});
69
+
70
+ $("#author_id").val(author.id);
71
+
72
+ $("#btnSubmit").removeAttr('disabled');
73
+ $("#btnDelete").removeAttr('disabled');
74
+
75
+ authorList();
76
+ }).catch(function (error) {
77
+ alert(JSON.stringify(error))
78
+ })
79
+ }
80
+
81
+ function updateAuthor(id, name, yob) {
82
+ var q = `mutation UpdateAuthor($author: AuthorInput) {UpdateAuthor(author: $author) {id}}`;
83
+ var vars = {author: {id: id, name: name, yob: parseInt(yob)}};
84
+ console.log(`updating author ${id}...`);
85
+
86
+ var update = graph(q);
87
+
88
+ update(vars).then(function (r) {
89
+ author = r.UpdateAuthor;
90
+ $.notify(`Author updated`, {position: "top center", autoHideDelay: 3000});
91
+
92
+ $("#btnSubmit").removeAttr('disabled');
93
+ $("#btnDelete").removeAttr('disabled');
94
+
95
+ authorList();
96
+ }).catch(function (error) {
97
+ alert(JSON.stringify(error))
98
+ })
99
+ }
100
+
101
+ function deleteAuthor(id) {
102
+ var q = `mutation DeleteAuthor($id: ID) {DeleteAuthor(id: $id)}`;
103
+ var vars = {id: id};
104
+ console.log(`deleting author ${id}...`);
105
+
106
+ var del = graph(q);
107
+
108
+ del(vars).then(function (r) {
109
+ $.notify(`Author ${id} deleted.`, {position: "top center", autoHideDelay: 3000});
110
+
111
+ $('#author_id').val(null);
112
+ $('#name').val(null);
113
+ $('#yob').val(null);
114
+
115
+ authorList();
116
+ }).catch(function (error) {
117
+ alert(JSON.stringify(error))
118
+ })
119
+ }
120
+
121
+ function authorList() {
122
+ $("#all > tbody").html("");
123
+
124
+ var q = `{
125
+ Authors {
126
+ id
127
+ name
128
+ yob
129
+ created_at
130
+ updated_at
131
+ books {
132
+ id
133
+ }
134
+ }
135
+ }`;
136
+
137
+ console.log('loading authors...');
138
+
139
+ graph.query.run(q).then(function (all) {
140
+ all.Authors.forEach(function (r) {
141
+ $('#all tbody').append(`<tr data-id="${r.id}"><th scope="row">${r.id}</th>
142
+ <td>${r.name}</td>
143
+ <td>${r.yob}</td>
144
+ <td>${r.created_at}</td>
145
+ <td>${r.updated_at}</td>
146
+ <td><a href="books.html?author_id=${r.id}">${r.books.length}</a></td></tr>`);
147
+ });
148
+
149
+ }).catch(function (error) {
150
+ alert(JSON.stringify(error));
151
+ })
152
+ }
153
+
154
+
155
+ function run() {
156
+ console.log('starting...');
157
+
158
+ authorList();
159
+ }