Skip to content

Commit

Permalink
[wip] feat(dao) first db-agnostic insert
Browse files Browse the repository at this point in the history
  • Loading branch information
thibaultcha committed Mar 9, 2016
1 parent 44d9765 commit eba2d90
Show file tree
Hide file tree
Showing 17 changed files with 331 additions and 76 deletions.
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,14 @@ test-plugins:
test-all:
@busted -v spec/

test-model:
@busted -v -o gtest spec/integration/model

test-model-only:
@busted -v -o gtest --tags=only spec/integration/model

coverage:
@rm -f luacov.*
@busted --coverage spec/
@luacov -c spec/.luacov
@tail -n 1 luacov.report.out | awk '{ print $$3 }'
@tail -n 1 luacov.report.out | awk '{ print $$3 }'
2 changes: 2 additions & 0 deletions kong-0.7.0-0.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ dependencies = {
"lapis ~> 1.3.1-1",
"stringy ~> 0.4-1",
"lua-cassandra ~> 0.5.0",
"pgmoon ~> 1.2.0",
"multipart ~> 0.2-1",
"lua-path ~> 0.2.3-1",
"lua-cjson ~> 2.1.0-1",
Expand Down Expand Up @@ -94,6 +95,7 @@ build = {
["kong.base.dao"] = "kong/base/dao.lua",
["kong.base.dao_factory"] = "kong/base/dao_factory.lua",

["kong.dao.errors"] = "kong/dao/errors.lua",
["kong.dao.error"] = "kong/dao/error.lua",
["kong.dao.schemas_validation"] = "kong/dao/schemas_validation.lua",
["kong.dao.schemas.apis"] = "kong/dao/schemas/apis.lua",
Expand Down
6 changes: 3 additions & 3 deletions kong/constants.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ return {
NGINX_CONFIG = "nginx.conf"
},
DATABASE_NULL_ID = "00000000-0000-0000-0000-000000000000",
DATABASE_ERROR_TYPES = setmetatable ({
DB_ERROR_TYPES = setmetatable ({
SCHEMA = "schema",
INVALID_TYPE = "invalid_type",
DATABASE = "database",
INVALID_TYPE = "type",
db = "db",
UNIQUE = "unique",
FOREIGN = "foreign"
}, { __index = function(t, key)
Expand Down
7 changes: 5 additions & 2 deletions kong/dao/base_db.lua
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
local inspect = require "inspect"

local timestamp = require "kong.tools.timestamp"
local Object = require "classic"
local utils = require "kong.tools.utils"
local uuid = require "lua_uuid"

local function debug_log(self, ...)
local prefix = "[DB:"..self.db_type.."]"
if ngx ~= nil then
ngx.log(ngx.DEBUG, prefix, inspect(...))
--ngx.log(ngx.DEBUG, prefix, inspect(...))
--print(prefix, inspect(...))
else
print(prefix, inspect(...))
end
Expand Down Expand Up @@ -40,7 +43,7 @@ function BaseDB:query(query)
-- to be implemented in child
end

function BaseDB:insert(table_name, tbl)
function BaseDB:insert(model)
-- to be implemented in child
end

Expand Down
20 changes: 0 additions & 20 deletions kong/dao/base_model.lua

This file was deleted.

58 changes: 45 additions & 13 deletions kong/dao/cassandra_db.lua
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
local inspect = require "inspect"

local timestamp = require "kong.tools.timestamp"
local BaseDB = require "kong.dao.base_db"
local utils = require "kong.tools.utils"
local cassandra = require "cassandra"
local uuid = require "lua_uuid"

cassandra.set_log_level("QUIET")
local ngx_stub = _G.ngx
_G.ngx = nil
local cassandra = require "cassandra"
_G.ngx = ngx_stub

local CassandraDB = BaseDB:extend()

CassandraDB.dao_insert_values = {
id = function()
return uuid()
end,
timestamp = function()
return timestamp.get_utc()
end
}

function CassandraDB:new(options)
local conn_opts = {
shm = "cassandra",
Expand All @@ -33,6 +46,31 @@ function CassandraDB:init_db()

end

-- Formatting

function CassandraDB:_get_args(model)
local fields = model.__schema.fields
local cols, bind_args, args = {}, {}, {}

for col, field in pairs(fields) do
-- cassandra serializers
local value = model[col]
if value == nil then
value = cassandra.unset
elseif field.type == "id" then
value = cassandra.uuid(value)
elseif field.type == "timestamp" then
value = cassandra.timestamp(value)
end

cols[#cols + 1] = col
args[#args + 1] = value
bind_args[#bind_args + 1] = "?"
end

return table.concat(cols, ", "), table.concat(bind_args, ", "), args
end

function CassandraDB:query(query, args)
CassandraDB.super.query(self, query, args)

Expand All @@ -51,22 +89,16 @@ function CassandraDB:query(query, args)
return res
end

function CassandraDB:insert(table_name, tbl)
local values_buf = {}
for col in pairs(tbl) do
values_buf[#values_buf + 1] = "?"
end

function CassandraDB:insert(model)
local cols, binds, args = self:_get_args(model)
local query = string.format("INSERT INTO %s(%s) VALUES(%s)",
table_name,
self:_get_columns(tbl),
table.concat(values_buf, ", "))
local err = select(2, self:query(query, tbl))
model.__table, cols, binds)
local err = select(2, self:query(query, args))
if err then
return nil, err
end

return tbl
return model
end

-- Migrations
Expand Down
42 changes: 42 additions & 0 deletions kong/dao/dao.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
local inspect = require "inspect"

local uuid = require "lua_uuid"
local utils = require "kong.tools.utils"
local Object = require "classic"
local Errors = require "kong.dao.errors"

--- DAO
-- this just avoids having to deal with instanciating models

local DAO = Object:extend()

function DAO:new(db, model_mt)
self.db = db
self.model_mt = model_mt
end

function DAO:insert(tbl)
local model = self.model_mt(tbl)
local ok, err = model:validate()
if not ok then
return nil, err
end

for col, field in pairs(model.__schema.fields) do
if field.dao_insert_value and model[col] == nil then
local f = self.db.dao_insert_values[field.type]
if f then
model[col] = f()
end
end
end

local res, err = self.db:insert(model)
if err ~= nil then
return nil, Errors.db(err)
end

return res
end

return DAO
53 changes: 53 additions & 0 deletions kong/dao/errors.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
local constants = require "kong.constants"
local printable_mt = require "kong.tools.printable"
local setmetatable = setmetatable
local getmetatable = getmetatable
local tostring = tostring
local type = type

local error_mt = {}
error_mt.__index = error_mt

function error_mt:__tostring()
return tostring(self.message)
end

function error_mt.__concat(a, b)
if getmetatable(a) == error_mt then
return tostring(a)..b
else
return a..tostring(b)
end
end

local function build_error(err_type)
return function(err)
local err_tbl -- in case arg1 is a table

if err == nil then
return nil
elseif getmetatable(err) == error_mt then
return err
elseif type(err) == "table" then
err_tbl = err
setmetatable(err, printable_mt)
err = tostring(err) -- convert to string
end

local err_obj = {
message = err,
err_tbl = err_tbl,
[err_type] = true
}

return setmetatable(err_obj, error_mt)
end
end

local Errors = {}

for _, v in pairs(constants.DB_ERROR_TYPES) do
Errors[v] = build_error(v)
end

return Errors
22 changes: 12 additions & 10 deletions kong/dao/factory.lua
Original file line number Diff line number Diff line change
@@ -1,40 +1,42 @@
local BaseModel = require "kong.dao.base_model"
local DAO = require "kong.dao.dao"
local Object = require "classic"
local ModelFactory = require "kong.dao.model_factory"

local CORE_MODELS = {"apis", "consumers", "plugins"}
local _db

local Factory = Object:extend()

function Factory:__index(key)
local models = rawget(self, "models")
if models and models[key] then
return models[key]
local daos = rawget(self, "daos")
if daos and daos[key] then
return daos[key]
else
return Factory[key]
end
end

function Factory:new(db_type, options)
self.db_type = db_type
self.models = {}
self.daos = {}

local DB = require("kong.dao."..db_type.."_db")
_db = DB(options)

-- Create models and give them the db instance
-- Create daos and give them the db instance
for _, m_name in ipairs(CORE_MODELS) do
local m_schema = require("kong.dao.schemas."..m_name)
local model = BaseModel(_db, m_schema)
self.models[m_name] = model
local model_mt = ModelFactory(m_schema)
local dao = DAO(_db, model_mt)
self.daos[m_name] = dao
end
end

-- Migrations

function Factory:drop_schema()
for _, model in ipairs(self.models) do
_db:drop_table(model.table)
for _, dao in ipairs(self.daos) do
_db:drop_table(dao.table)
end

_db:drop_table("schema_migrations")
Expand Down
46 changes: 46 additions & 0 deletions kong/dao/model_factory.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
local Errors = require "kong.dao.errors"
local schemas_validation = require "kong.dao.schemas_validation"
local validate = schemas_validation.validate_entity

return setmetatable({}, {
__call = function(_, schema)
local Model_mt = {}
Model_mt.__meta = {
__schema = schema,
__name = schema.name,
__table = schema.table
}

function Model_mt.__index(self, key)
if string.find(key, "__") == 1 then
local meta_field = Model_mt.__meta[key]
if meta_field then
return meta_field
end
end

return Model_mt[key]
end

function Model_mt:validate()
local ok, errors, self_check_err = validate(self, self.__schema)
if errors ~= nil then
return nil, Errors.schema(errors)
elseif self_check_err ~= nil then
-- TODO: merge errors and self_check_err now that our errors handle this
return nil, Errors.schema(self_check_err)
end
return ok
end

return setmetatable({}, {
__call = function(_, tbl)
local m = {}
for k,v in pairs(tbl) do
m[k] = v
end
return setmetatable(m, Model_mt)
end
})
end
})
Loading

0 comments on commit eba2d90

Please sign in to comment.