midb 1.0.5 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,235 +1,289 @@
1
1
  require 'midb/server_controller'
2
2
  require 'midb/dbengine_model'
3
3
  require 'midb/server_view'
4
+ require 'midb/hooks'
4
5
 
5
6
  require 'sqlite3'
6
7
  require 'json'
7
8
  require 'cgi'
8
9
  module MIDB
9
- class ServerModel
10
- attr_accessor :jsf
10
+ module API
11
+ class Model
11
12
 
12
- # Method: get_structure
13
- # Safely get the structure
14
- def self.get_structure()
15
- JSON.parse(IO.read("./json/#{@jsf}.json"))["id"]
16
- end
17
-
18
- # Method: query_to_hash
19
- # Convert a HTTP query string to a JSONable hash
20
- def self.query_to_hash(query)
21
- Hash[CGI.parse(query).map {|key,values| [key, values[0]||true]}]
22
- end
13
+ attr_accessor :jsf, :db, :engine
23
14
 
24
- # Method: post
25
- # Act on POST requests - create a new resource
26
- def self.post(db, jsf, data)
27
- @jsf = jsf
28
- jss = self.get_structure() # For referencing purposes
15
+ # Constructor
16
+ #
17
+ # @param jsf [String] JSON file with the schema
18
+ # @param db [String] Database to operate on.
19
+ # @param engine [Object] Reference to the API engine.
20
+ def initialize(jsf, db, engine)
21
+ @jsf = jsf
22
+ @db = db
23
+ @engine = engine
24
+ end
29
25
 
30
- input = self.query_to_hash(data)
31
- bad_request = false
32
- resp = nil
33
- jss.each do |key, value|
34
- # Check if we have it on the query too
35
- unless input.has_key? key
36
- resp = MIDB::ServerView.json_error(400, "Bad Request - Not enough data for a new resource")
37
- MIDB::ServerController.http_status = 400
38
- bad_request = true
39
- end
26
+ # Safely get the structure
27
+ def get_structure()
28
+ JSON.parse(IO.read("./json/#{@jsf}.json"))["id"]
40
29
  end
41
- input.each do |key, value|
42
- # Check if we have it on the structure too
43
- unless jss.has_key? key
44
- resp = MIDB::ServerView.json_error(400, "Bad Request - Wrong argument #{key}")
45
- MIDB::ServerController.http_status = 400
46
- bad_request = true
47
- end
30
+
31
+ # Convert a HTTP query string to a JSONable hash
32
+ #
33
+ # @param query [String] HTTP query string
34
+ def query_to_hash(query)
35
+ Hash[CGI.parse(query).map {|key,values| [key, values[0]||true]}]
48
36
  end
49
-
50
37
 
51
- # Insert the values if we have a good request
52
- unless bad_request
53
- fields = Hash.new
54
- inserts = Hash.new
55
- main_table = self.get_structure.values[0].split('/')[0]
56
- input.each do |key, value|
57
- struct = jss[key]
58
- table = struct.split("/")[0]
59
- inserts[table] ||= []
60
- fields[table] ||= []
61
- inserts[table].push "\"" + value + "\""
62
- fields[table].push struct.split("/")[1]
63
- if struct.split("/").length > 2
64
- match = struct.split("/")[2]
65
- matching_field = match.split("->")[0]
66
- row_field = match.split("->")[1]
67
- fields[table].push matching_field
68
- if MIDB::ServerController.config["dbengine"] == :mysql
69
- inserts[table].push "(SELECT #{row_field} FROM #{main_table} WHERE id=(SELECT LAST_INSERT_ID()))"
70
- else
71
- inserts[table].push "(SELECT #{row_field} FROM #{main_table} WHERE id=(last_insert_rowid()))"
72
- end
38
+ # Act on POST requests - create a new resource
39
+ #
40
+ # @param data [String] The HTTP query string containing what to POST.
41
+ def post(data)
42
+ jss = self.get_structure() # For referencing purposes
43
+
44
+ input = self.query_to_hash(data)
45
+ bad_request = false
46
+ resp = nil
47
+ jss.each do |key, value|
48
+ # Check if we have it on the query too
49
+ unless input.has_key? key
50
+ resp = MIDB::Interface::Server.json_error(400, "Bad Request - Not enough data for a new resource")
51
+ @engine.http_status = 400
52
+ bad_request = true
73
53
  end
74
54
  end
75
- queries = []
76
- inserts.each do |table, values|
77
- queries.push "INSERT INTO #{table}(#{fields[table].join(',')}) VALUES (#{inserts[table].join(',')});"
78
- end
79
- # Connect to the database
80
- dbe = MIDB::DbengineModel.new
81
- dblink = dbe.connect()
82
- results = []
83
- rid = nil
84
- # Find the ID to return in the response (only for the first query)
85
- queries.each do |q|
86
- results.push dbe.query(dblink, q)
87
- if MIDB::ServerController.config["dbengine"] == :mysql
88
- rid ||= dbe.extract(dbe.query(dblink, "SELECT id FROM #{main_table} WHERE id=(SELECT LAST_INSERT_ID());"), "id")
89
- else
90
- rid ||= dbe.extract(dbe.query(dblink, "SELECT id FROM #{main_table} WHERE id=(last_insert_rowid());"), "id")
55
+ input.each do |key, value|
56
+ # Check if we have it on the structure too
57
+ unless jss.has_key? key
58
+ resp = MIDB::Interface::Server.json_error(400, "Bad Request - Wrong argument #{key}")
59
+ @engine.http_status = 400
60
+ bad_request = true
91
61
  end
92
62
  end
93
- MIDB::ServerController.http_status = "201 Created"
94
- resp = {"status": "201 created", "id": rid}
95
- end
96
- return resp
97
- end
63
+
98
64
 
99
- # Method: put
100
- # Update an already existing resource
101
- def self.put(db, jsf, id, data)
102
- @jsf = jsf
103
- jss = self.get_structure() # For referencing purposes
104
-
105
- input = self.query_to_hash(data)
106
- bad_request = false
107
- resp = nil
108
- input.each do |key, value|
109
- # Check if we have it on the structure too
110
- unless jss.has_key? key
111
- resp = MIDB::ServerView.json_error(400, "Bad Request - Wrong argument #{key}")
112
- MIDB::ServerController.http_status = 400
113
- bad_request = true
65
+ # Insert the values if we have a good request
66
+ unless bad_request
67
+ fields = Hash.new
68
+ inserts = Hash.new
69
+ main_table = self.get_structure.values[0].split('/')[0]
70
+ input.each do |key, value|
71
+ struct = jss[key]
72
+ table = struct.split("/")[0]
73
+ inserts[table] ||= []
74
+ fields[table] ||= []
75
+ inserts[table].push "\"" + value + "\""
76
+ fields[table].push struct.split("/")[1]
77
+ if struct.split("/").length > 2
78
+ match = struct.split("/")[2]
79
+ matching_field = match.split("->")[0]
80
+ row_field = match.split("->")[1]
81
+ fields[table].push matching_field
82
+ if @engine.config["dbengine"] == :mysql
83
+ inserts[table].push "(SELECT #{row_field} FROM #{main_table} WHERE id=(SELECT LAST_INSERT_ID()))"
84
+ else
85
+ inserts[table].push "(SELECT #{row_field} FROM #{main_table} WHERE id=(last_insert_rowid()))"
86
+ end
87
+ end
88
+ end
89
+ queries = []
90
+ inserts.each do |table, values|
91
+ queries.push "INSERT INTO #{table}(#{fields[table].join(',')}) VALUES (#{inserts[table].join(',')});"
92
+ end
93
+ # Connect to the database
94
+ dbe = MIDB::API::Dbengine.new(@engine.config, @db)
95
+ dblink = dbe.connect()
96
+ results = []
97
+ rid = nil
98
+ # Find the ID to return in the response (only for the first query)
99
+ queries.each do |q|
100
+ results.push dbe.query(dblink, q)
101
+ if @engine.config["dbengine"] == :mysql
102
+ rid ||= dbe.extract(dbe.query(dblink, "SELECT id FROM #{main_table} WHERE id=(SELECT LAST_INSERT_ID());"), "id")
103
+ else
104
+ rid ||= dbe.extract(dbe.query(dblink, "SELECT id FROM #{main_table} WHERE id=(last_insert_rowid());"), "id")
105
+ end
106
+ end
107
+ @engine.http_status = "201 Created"
108
+ resp = {status: "201 created", id: rid}
114
109
  end
110
+ return resp
115
111
  end
116
112
 
117
- # Check if the ID exists
118
- db = MIDB::DbengineModel.new
119
- dbc = db.connect()
120
- dbq = db.query(dbc, "SELECT * FROM #{self.get_structure.values[0].split('/')[0]} WHERE id=#{id};")
121
- unless db.length(dbq) > 0
122
- resp = MIDB::ServerView.json_error(404, "ID not found")
123
- MIDB::ServerController.http_status = 404
124
- bad_request = true
125
- end
126
-
127
- # Update the values if we have a good request
128
- unless bad_request
129
- fields = Hash.new
130
- inserts = Hash.new
131
- where_clause = Hash.new
132
- main_table = self.get_structure.values[0].split('/')[0]
133
- where_clause[main_table] = "id=#{id}"
113
+ # Update an already existing resource
114
+ #
115
+ # @param id [Fixnum] ID to alter
116
+ # @param data [String] HTTP query string
117
+ def put(id, data)
118
+ jss = self.get_structure() # For referencing purposes
119
+
120
+ input = self.query_to_hash(data)
121
+ bad_request = false
122
+ resp = nil
134
123
  input.each do |key, value|
135
- struct = jss[key]
136
- table = struct.split("/")[0]
137
- inserts[table] ||= []
138
- fields[table] ||= []
139
- inserts[table].push "\"" + value + "\""
140
- fields[table].push struct.split("/")[1]
141
- if struct.split("/").length > 2
142
- match = struct.split("/")[2]
143
- matching_field = match.split("->")[0]
144
- row_field = match.split("->")[1]
145
- where_clause[table] = "#{matching_field}=(SELECT #{row_field} FROM #{main_table} WHERE #{where_clause[main_table]});"
124
+ # Check if we have it on the structure too
125
+ unless jss.has_key? key
126
+ resp = MIDB::Interface::Server.json_error(400, "Bad Request - Wrong argument #{key}")
127
+ @engine.http_status = 400
128
+ bad_request = true
146
129
  end
147
130
  end
148
- queries = []
149
- updates = Hash.new
150
- # Turn it into a hash
151
- inserts.each do |table, values|
152
- updates[table] ||= Hash.new
153
- updates[table] = Hash[fields[table].zip(inserts[table])]
154
- query = "UPDATE #{table} SET "
155
- updates[table].each do |f, v|
156
- query = query + "#{f}=#{v} "
157
- end
158
- queries.push query + "WHERE #{where_clause[table]};"
131
+
132
+ # Check if the ID exists
133
+ db = MIDB::API::Dbengine.new(@engine.config, @db)
134
+ dbc = db.connect()
135
+ dbq = db.query(dbc, "SELECT * FROM #{self.get_structure.values[0].split('/')[0]} WHERE id=#{id};")
136
+ unless db.length(dbq) > 0
137
+ resp = MIDB::Interface::Server.json_error(404, "ID not found")
138
+ @engine.http_status = 404
139
+ bad_request = true
159
140
  end
160
- # Run the queries
161
- results = []
162
- queries.each do |q|
163
- results.push db.query(dbc, q)
141
+
142
+ # Update the values if we have a good request
143
+ unless bad_request
144
+ fields = Hash.new
145
+ inserts = Hash.new
146
+ where_clause = Hash.new
147
+ main_table = self.get_structure.values[0].split('/')[0]
148
+ where_clause[main_table] = "id=#{id}"
149
+ input.each do |key, value|
150
+ struct = jss[key]
151
+ table = struct.split("/")[0]
152
+ inserts[table] ||= []
153
+ fields[table] ||= []
154
+ inserts[table].push "\"" + value + "\""
155
+ fields[table].push struct.split("/")[1]
156
+ if struct.split("/").length > 2
157
+ match = struct.split("/")[2]
158
+ matching_field = match.split("->")[0]
159
+ row_field = match.split("->")[1]
160
+ where_clause[table] = "#{matching_field}=(SELECT #{row_field} FROM #{main_table} WHERE #{where_clause[main_table]});"
161
+ end
162
+ end
163
+ queries = []
164
+ updates = Hash.new
165
+ # Turn it into a hash
166
+ inserts.each do |table, values|
167
+ updates[table] ||= Hash.new
168
+ updates[table] = Hash[fields[table].zip(inserts[table])]
169
+ query = "UPDATE #{table} SET "
170
+ updates[table].each do |f, v|
171
+ query = query + "#{f}=#{v} "
172
+ end
173
+ queries.push query + "WHERE #{where_clause[table]};"
174
+ end
175
+ # Run the queries
176
+ results = []
177
+ queries.each do |q|
178
+ results.push db.query(dbc, q)
179
+ end
180
+ @engine.http_status = "200 OK"
181
+ resp = {status: "200 OK"}
164
182
  end
165
- MIDB::ServerController.http_status = "200 OK"
166
- resp = {"status": "200 OK"}
183
+ return resp
167
184
  end
168
- return resp
169
- end
170
185
 
186
+ # Delete a resource
187
+ #
188
+ # @param id [Fixnum] ID to delete
189
+ def delete(id)
190
+ # Check if the ID exists
191
+ db = MIDB::API::Dbengine.new(@engine.config, @db)
192
+ dbc = db.connect()
193
+ dbq = db.query(dbc, "SELECT * FROM #{self.get_structure.values[0].split('/')[0]} WHERE id=#{id};")
194
+ if not db.length(dbq) > 0
195
+ resp = MIDB::Interface::Server.json_error(404, "ID not found").to_json
196
+ @engine.http_status = 404
197
+ bad_request = true
198
+ else
199
+ # ID Found, so let's delete it. (including linked resources!)
200
+ jss = self.get_structure() # Referencing
171
201
 
172
- def self.delete(db, jsf, id)
173
- # Check if the ID exists
174
- db = MIDB::DbengineModel.new
175
- dbc = db.connect()
176
- dbq = db.query(dbc, "SELECT * FROM #{self.get_structure.values[0].split('/')[0]} WHERE id=#{id};")
177
- if not db.length(dbq) > 0
178
- resp = MIDB::ServerView.json_error(404, "ID not found").to_json
179
- MIDB::ServerController.http_status = 404
180
- bad_request = true
181
- else
182
- # ID Found, so let's delete it. (including linked resources!)
183
- @jsf = jsf
184
- jss = self.get_structure() # Referencing
202
+ where_clause = {}
203
+ tables = []
204
+ main_table = jss.values[0].split('/')[0]
205
+ where_clause[main_table] = "id=#{id}"
185
206
 
186
- where_clause = {}
187
- tables = []
188
- main_table = jss.values[0].split('/')[0]
189
- where_clause[main_table] = "id=#{id}"
207
+ jss.each do |k, v|
208
+ table = v.split("/")[0]
209
+ tables.push table unless tables.include? table
210
+ # Check if it's a linked resource, generate WHERE clause accordingly
211
+ if v.split("/").length > 2
212
+ match = v.split("/")[2]
213
+ matching_field = match.split("->")[0]
214
+ row_field = match.split("->")[1]
215
+ # We have to run the subquery now because it'll be deleted later!
216
+ subq = "SELECT #{row_field} FROM #{main_table} WHERE #{where_clause[main_table]};"
217
+ res = db.query(dbc, subq)
218
+ subqres = db.extract(res, row_field)
219
+ where_clause[table] ||= "#{matching_field}=#{subqres}"
220
+ else
221
+ # Normal WHERE clause
222
+ where_clause[table] ||= "id=#{id}"
223
+ end
224
+ end
190
225
 
191
- jss.each do |k, v|
192
- table = v.split("/")[0]
193
- tables.push table unless tables.include? table
194
- # Check if it's a linked resource, generate WHERE clause accordingly
195
- if v.split("/").length > 2
196
- match = v.split("/")[2]
197
- matching_field = match.split("->")[0]
198
- row_field = match.split("->")[1]
199
- # We have to run the subquery now because it'll be deleted later!
200
- subq = "SELECT #{row_field} FROM #{main_table} WHERE #{where_clause[main_table]};"
201
- res = db.query(dbc, subq)
202
- subqres = db.extract(res, row_field)
203
- where_clause[table] ||= "#{matching_field}=#{subqres}"
204
- else
205
- # Normal WHERE clause
206
- where_clause[table] ||= "id=#{id}"
226
+ # Generate and run queries
227
+ results = []
228
+ tables.each do |tb|
229
+ query = "DELETE FROM #{tb} WHERE #{where_clause[tb]};"
230
+ results.push db.query(dbc, query)
207
231
  end
232
+ @engine.http_status = "200 OK"
233
+ resp = {status: "200 OK"}
208
234
  end
235
+ return resp
236
+ end
209
237
 
210
- # Generate and run queries
211
- results = []
212
- tables.each do |tb|
213
- query = "DELETE FROM #{tb} WHERE #{where_clause[tb]};"
214
- results.push db.query(dbc, query)
238
+ # Get an entry with a given id.
239
+ #
240
+ # @param id [Fixnum] ID of the entry
241
+ def get_entries(id)
242
+ jso = Hash.new()
243
+
244
+ dbe = MIDB::API::Dbengine.new(@engine.config, @db)
245
+ dblink = dbe.connect()
246
+ rows = dbe.query(dblink, "SELECT * FROM #{self.get_structure.values[0].split('/')[0]} WHERE id=#{id};")
247
+ if dbe.length(rows) > 0
248
+ rows.each do |row|
249
+ jso[row["id"]] = self.get_structure
250
+
251
+ self.get_structure.each do |name, dbi|
252
+ table = dbi.split("/")[0]
253
+ field = dbi.split("/")[1]
254
+ # Must-match relations ("table2/field/table2-field->row-field")
255
+ if dbi.split("/").length > 2
256
+ match = dbi.split("/")[2]
257
+ matching_field = match.split("->")[0]
258
+ row_field = match.split("->")[1]
259
+ query = dbe.query(dblink, "SELECT #{field} FROM #{table} WHERE #{matching_field}=#{row[row_field]};")
260
+ else
261
+ query = dbe.query(dblink, "SELECT #{field} from #{table} WHERE id=#{row['id']};")
262
+ end
263
+ jso[row["id"]][name] = dbe.length(query) > 0 ? dbe.extract(query,field) : "unknown"
264
+ end
265
+ end
266
+ @engine.http_status = "200 OK"
267
+ else
268
+ @engine.http_status = "404 Not Found"
269
+ jso = MIDB::Interface::Server.json_error(404, "Not Found")
215
270
  end
216
- MIDB::ServerController.http_status = "200 OK"
217
- resp = {"status": "200 OK"}
271
+ return jso
272
+
218
273
  end
219
- return resp
220
- end
221
274
 
222
- # Method: get_entries
223
- # Get the entries from a given ID.
224
- def self.get_entries(db, jsf, id)
225
- @jsf = jsf
226
- jso = Hash.new()
275
+ # Get all the entries from the database
276
+ def get_all_entries()
277
+ jso = Hash.new()
278
+
279
+ # Connect to database
280
+ dbe = MIDB::API::Dbengine.new(@engine.config, @db)
281
+ dblink = dbe.connect()
282
+ rows = dbe.query(dblink, "SELECT * FROM #{self.get_structure.values[0].split('/')[0]};")
227
283
 
228
- dbe = MIDB::DbengineModel.new()
229
- dblink = dbe.connect()
230
- rows = dbe.query(dblink, "SELECT * FROM #{self.get_structure.values[0].split('/')[0]} WHERE id=#{id};")
231
- if dbe.length(rows) > 0
284
+ # Iterate over all rows of this table
232
285
  rows.each do |row|
286
+ # Replace the "id" in the given JSON with the actual ID and expand it with the fields
233
287
  jso[row["id"]] = self.get_structure
234
288
 
235
289
  self.get_structure.each do |name, dbi|
@@ -247,47 +301,9 @@ module MIDB
247
301
  jso[row["id"]][name] = dbe.length(query) > 0 ? dbe.extract(query,field) : "unknown"
248
302
  end
249
303
  end
250
- MIDB::ServerController.http_status = "200 OK"
251
- else
252
- MIDB::ServerController.http_status = "404 Not Found"
253
- jso = MIDB::ServerView.json_error(404, "Not Found")
254
- end
255
- return jso
256
-
257
- end
258
-
259
- # Method: get_all_entries
260
- # Get all the entries from the fields specified in a JSON-parsed hash
261
- def self.get_all_entries(db, jsf)
262
- @jsf = jsf
263
- jso = Hash.new()
264
-
265
- # Connect to database
266
- dbe = MIDB::DbengineModel.new()
267
- dblink = dbe.connect()
268
- rows = dbe.query(dblink, "SELECT * FROM #{self.get_structure.values[0].split('/')[0]};")
269
-
270
- # Iterate over all rows of this table
271
- rows.each do |row|
272
- # Replace the "id" in the given JSON with the actual ID and expand it with the fields
273
- jso[row["id"]] = self.get_structure
274
-
275
- self.get_structure.each do |name, dbi|
276
- table = dbi.split("/")[0]
277
- field = dbi.split("/")[1]
278
- # Must-match relations ("table2/field/table2-field->row-field")
279
- if dbi.split("/").length > 2
280
- match = dbi.split("/")[2]
281
- matching_field = match.split("->")[0]
282
- row_field = match.split("->")[1]
283
- query = dbe.query(dblink, "SELECT #{field} FROM #{table} WHERE #{matching_field}=#{row[row_field]};")
284
- else
285
- query = dbe.query(dblink, "SELECT #{field} from #{table} WHERE id=#{row['id']};")
286
- end
287
- jso[row["id"]][name] = dbe.length(query) > 0 ? dbe.extract(query,field) : "unknown"
288
- end
304
+ MIDB::API::Hooks.after_get_all_entries()
305
+ return jso
289
306
  end
290
- return jso
291
307
  end
292
308
  end
293
309
  end