Skip to content
1 change: 1 addition & 0 deletions lua/spec/match2_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ describe('match2', function()
local tournamentData = require('test_assets.tournaments').dummy
insulate('matchlist', function()
local Json = require('Module:Json')

before_each(function ()
Comment thread
hjpalpha marked this conversation as resolved.
local dataSaved, dataSavedOpponent, dataSavedPlayer, dataSavedGame = {}, {}, {}, {}
-- Mock the lpdb functions
Expand Down
13 changes: 11 additions & 2 deletions lua/spec/orm_spec.lua
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
--- Triple Comment to Enable our LLS Plugin
describe('LPDB Object-Relational Mapping', function()
local FeatureFlag = require('Module:FeatureFlag')
local Lpdb = require('Module:Lpdb')

before_each(function ()
FeatureFlag.set('force_type_check', true)
end)

after_each(function ()
FeatureFlag.set('force_type_check', false)
end)

describe('setting data', function()
it('assign value on init', function()
local match2 = Lpdb.Match2:new({bestof = 10})
Expand Down Expand Up @@ -33,7 +42,7 @@ describe('LPDB Object-Relational Mapping', function()
describe('saving data', function()
it('saving', function()
local stub = stub(mw.ext.LiquipediaDB, 'lpdb_match2')
Lpdb.Match2:new({match2id = 'Foo', match2bracketid = 'Bar', bestof = 3, game = 'r6s'}):save()
Lpdb.Match2:new({match2id = 'Foo', match2bracketid = 'Bar', bestof = 3, game = 'r6s', parent = 'DummyPage'}):save()
assert.stub(stub).called_with('Foo', {
bestof = 3,
date = 0,
Expand All @@ -52,7 +61,7 @@ describe('LPDB Object-Relational Mapping', function()
match2id = 'Foo',
match2opponents = {},
mode = '',
parent = '',
parent = 'DummyPage',
patch = '',
publishertier = '',
section = '',
Expand Down
48 changes: 29 additions & 19 deletions lua/wikis/commons/Lpdb.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ local Lua = require('Module:Lua')

local Array = Lua.import('Module:Array')
local Class = Lua.import('Module:Class')
local FeatureFlag = Lua.import('Module:FeatureFlag')
local FnUtil = Lua.import('Module:FnUtil')
local Logic = Lua.import('Module:Logic')
local Opponent = Lua.import('Module:Opponent/Custom')
local Table = Lua.import('Module:Table')
local TextSanitizer = Lua.import('Module:TextSanitizer')
local TypeUtil = Lua.import('Module:TypeUtil')
local Variables = Lua.import('Module:Variables')

local Lpdb = {}
Expand Down Expand Up @@ -103,17 +106,17 @@ end

--- LPDB Object-Relational Mapping

---@alias ModelColumnData {name: string, fieldType: string|any, default: any}
---@alias ModelColumnData {name: string, fieldType: TypeUtilType, default: any}

---@class Model
---@class Model: BaseClass
---@field tableName string
---@field tableColumns ModelColumnData[]
local Model = Class.new(function(self, name, columns)
self.tableName = name
self.tableColumns = columns
end)

---@class ModelRow
---@class ModelRow: BaseClass
---@field private tableName string
---@field private tableColumns ModelColumnData[]
---@field private fields table<string, any>
Expand All @@ -140,7 +143,14 @@ function ModelRow:_validateField(columnData)
if not self.fields[columnData.name] then
error(self.tableName .. ' expects ' .. columnData.name .. ' to be set')
end
-- TODO: Verify types (at least when running tests)

local typeCheckFeature = FeatureFlag.get('force_type_check')
if not typeCheckFeature then
return
end
TypeUtil.assertValue(
self.fields[columnData.name], columnData.fieldType, {name = self.tableName .. '.' .. columnData.name}
)
end

---@private
Expand Down Expand Up @@ -230,10 +240,10 @@ Lpdb.Match2 = Model('match2', {
{name = 'section', fieldType = 'string', default = ''},
{name = 'game', fieldType = 'string', default = ''},
{name = 'patch', fieldType = 'string', default = ''},
{name = 'date', fieldType = 'string', default = 0},
{name = 'date', fieldType = TypeUtil.union('string', 'integer'), default = 0},
{name = 'dateexact', fieldType = 'number', default = 0},
{name = 'stream', fieldType = 'struct', default = {}},
{name = 'links', fieldType = 'struct', default = {}},
{name = 'stream', fieldType = 'table', default = {}},
{name = 'links', fieldType = 'table', default = {}},
{name = 'bestof', fieldType = 'number', default = 0},
{name = 'vod', fieldType = 'string', default = ''},
{name = 'tournament', fieldType = 'string', default = ''},
Expand All @@ -243,11 +253,11 @@ Lpdb.Match2 = Model('match2', {
{name = 'series', fieldType = 'string', default = ''},
{name = 'icon', fieldType = 'string', default = ''},
{name = 'icondark', fieldType = 'string', default = ''},
{name = 'liquipediatier', fieldType = 'string|number', default = ''},
{name = 'liquipediatier', fieldType = TypeUtil.union('string', 'number'), default = ''},
{name = 'liquipediatiertype', fieldType = 'string', default = ''},
{name = 'publishertier', fieldType = 'string', default = ''},
{name = 'extradata', fieldType = 'struct', default = {}},
{name = 'match2bracketdata', fieldType = 'struct', default = {}},
{name = 'extradata', fieldType = 'table', default = {}},
{name = 'match2bracketdata', fieldType = 'table', default = {}},
{name = 'match2opponents', fieldType = 'array', default = {}},
{name = 'match2games', fieldType = 'array', default = {}},
})
Expand All @@ -260,29 +270,29 @@ Lpdb.Placement = Model('placement', {
{name = 'parent', fieldType = 'pagename', default = ''},
{name = 'shortname', fieldType = 'string', default = ''},
{name = 'startdate', fieldType = 'string', default = 0},
{name = 'date', fieldType = 'string', default = 0},
{name = 'date', fieldType = TypeUtil.union('string', 'integer'), default = 0},
{name = 'placement', fieldType = 'string', default = ''},
{name = 'prizemoney', fieldType = 'number', default = 0},
{name = 'individualprizemoney', fieldType = 'number', default = 0},
{name = 'prizepoolindex', fieldType = 'number'},
{name = 'weight', fieldType = 'number', default = 0},
{name = 'mode', fieldType = 'string', default = ''},
{name = 'type', fieldType = 'string', default = ''},
{name = 'liquipediatier', fieldType = 'string|number', default = ''},
{name = 'liquipediatier', fieldType = TypeUtil.union('string', 'number'), default = ''},
{name = 'liquipediatiertype', fieldType = 'string', default = ''},
{name = 'publishertier', fieldType = 'string', default = ''},
{name = 'icon', fieldType = 'string', default = ''},
{name = 'icondark', fieldType = 'string', default = ''},
{name = 'game', fieldType = 'string', default = ''},
{name = 'lastvsdata', fieldType = 'struct', default = {}},
{name = 'lastvsdata', fieldType = 'table', default = {}},
{name = 'opponentname', fieldType = 'string', default = ''},
{name = 'opponenttemplate', fieldType = 'string', default = ''},
{name = 'opponenttype', fieldType = 'string', default = ''},
{name = 'opponentplayers', fieldType = 'struct', default = {}},
{name = 'opponenttype', fieldType = Opponent.types.OpponentType, default = ''},
{name = 'opponentplayers', fieldType = 'table', default = {}},
{name = 'qualifier', fieldType = 'string', default = ''},
{name = 'qualifierpage', fieldType = 'string', default = ''},
{name = 'qualifierpage', fieldType = 'pagename?', default = ''},
{name = 'qualifierurl', fieldType = 'string', default = ''},
{name = 'extradata', fieldType = 'struct', default = {}},
{name = 'extradata', fieldType = 'table', default = {}},
})

---@class SquadPlayerModel:Model
Expand All @@ -308,7 +318,7 @@ Lpdb.SquadPlayer = Model('squadplayer', {
{name = 'joindate', fieldType = 'string', default = ''},
{name = 'leavedate', fieldType = 'string', default = ''},
{name = 'inactivedate', fieldType = 'string', default = ''},
{name = 'extradata', fieldType = 'struct', default = {}},
{name = 'extradata', fieldType = 'table', default = {}},
})

---@class DataPoint:Model
Expand All @@ -320,7 +330,7 @@ Lpdb.DataPoint = Model('datapoint', {
{name = 'image', fieldType = 'string', default = ''},
{name = 'imagedark', fieldType = 'string', default = ''},
{name = 'date', fieldType = 'string', default = 0},
{name = 'extradata', fieldType = 'struct', default = {}},
{name = 'extradata', fieldType = 'table', default = {}},
})

return Lpdb
11 changes: 7 additions & 4 deletions lua/wikis/commons/Opponent.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ local Logic = Lua.import('Module:Logic')
local Page = Lua.import('Module:Page')
local PlayerExt = Lua.import('Module:Player/Ext/Custom')
local String = Lua.import('Module:StringUtils')
local Table = Lua.import('Module:Table')
local TeamTemplate = Lua.import('Module:TeamTemplate')
local TypeUtil = Lua.import('Module:TypeUtil')

Expand Down Expand Up @@ -100,6 +99,10 @@ Opponent.types.Opponent = TypeUtil.union(
Opponent.types.LiteralOpponent
)

Opponent.types.OpponentType = TypeUtil.literalUnion(
Opponent.solo, Opponent.duo, Opponent.trio, Opponent.quad, Opponent.team, Opponent.literal
)

---Checks if the provided opponent type is a party type
---@param type OpponentType?
---@return boolean
Expand Down Expand Up @@ -206,21 +209,21 @@ end
---@param type string
---@return boolean
function Opponent.isType(type)
return Table.includes(Opponent.types, type)
return #TypeUtil.checkValue(type, Opponent.types.OpponentType) == 0
end

---Reads an opponent type.
---If an invalid entry is given returns nil.
---@param type string
---@return OpponentType?
function Opponent.readType(type)
return Table.includes(Opponent.types, type) and type or nil
return Opponent.isType(type) and type or nil
end

---Asserts that an arbitrary value is a valid representation of an opponent
---@param opponent any
function Opponent.assertOpponent(opponent)
assert(Opponent.isOpponent(opponent), 'Invalid opponent')
TypeUtil.assertValue(opponent, Opponent.types.Opponent, {name = 'Opponent'})
end

---Validates that an arbitrary value is a valid representation of an opponent
Expand Down
9 changes: 6 additions & 3 deletions lua/wikis/commons/TypeUtil.lua
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,7 @@ function TypeUtil.table (keyType, valueType)
end

--[[
Type for tables that are arrays. Not strict - arrays may have additional fields
besides numeric indexes, and may have gaps in indexes.
Type for tables that are arrays.
]]
---@param elemType TypeUtilType
---@return TypeUtilArrayType
Expand Down Expand Up @@ -172,6 +171,8 @@ function TypeUtil.valueIsTypeNoTable (value, typeSpec)
elseif typeSpec == 'pagename' then
-- A pagename is a string, with first letter capitalized and may not contains spaces
return type(value) == 'string' and value:find('^%u') and not value:find(' ')
elseif typeSpec == 'array' then
return Array.isArray(value)
elseif String.endsWith(typeSpec, '?') then
return value == nil or TypeUtil.valueIsTypeNoTable(value, typeSpec:sub(1, -2))
elseif typeSpec == 'any' then
Expand All @@ -192,8 +193,10 @@ function TypeUtil.valueIsTypeNoTable (value, typeSpec)
typeSpec.types,
function(t) return TypeUtil.valueIsTypeNoTable(value, t) end
)
elseif typeSpec.op == 'table' or typeSpec.op == 'struct' or typeSpec.op == 'array' then
elseif typeSpec.op == 'table' or typeSpec.op == 'struct' then
return type(value) == 'table'
elseif typeSpec.op == 'array' then
return Array.isArray(value)
end
end
return true
Expand Down
Loading