midb 1.0.5 → 1.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.
@@ -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