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 on lines 5 to 7

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

side effect of db73f52...1226085
(typechecker didn't like the partial data input being used for our testing purposes)

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