Anonymous
×
Create a new article
Write your page title here:
We currently have 194 articles on The Dream Shrine. Type your article name above or click on one of the titles below and start writing!



The Dream Shrine
194Articles

Documentation for this module may be created at Module:UtilsCargo/doc

local p = {}
local h = {}

local frame = mw.getCurrentFrame()

function p.query(tables, fields, args)
	return mw.ext.cargo.query(tables, fields, args), p.categoryQuerying()
end

function p.allOf(...)
	local query
	for i, v in ipairs({...}) do
		if type(v) == "table" then
			for k, v in pairs(v) do
				v = p.escape(v)
				query = h.andClause(query, string.format("%s='%s'", k, v))
			end
		else
			query = h.andClause(query, v)
		end
	end
	return query
end

function p.anyOf(...)
	local query
	for i, v in ipairs({...}) do
		if type(v) == "table" then
			for k, v in pairs(v) do
				v = p.escape(v)
				query = h.orClause(query, string.format("%s='%s'", k, v))
			end
		else
			query = h.orClause(query, v)
		end
	end
	return query
end

function p.categoryQuerying()
	return "[[Category:Pages querying Cargo data]]"
end

function p.categoryStoring()
	local categories = "[[Category:Pages storing Cargo data]]"
	if mw.title.getCurrentTitle().nsText == "" then
		categories = categories.."[[Category:Articles storing Cargo data]]"
	end
	return categories
end

function p.escape(str)
	return string.gsub(str, "'", "\\'")
end

function p.holdsAll(field, values)
	local query
	for i, value in ipairs(values) do
		value = p.escape(value)
		if #values > 1 then
			-- Workaround for Cargo bug: https://phabricator.wikimedia.org/T267498
			-- __full lists all the categories as a string separated by pipes.
			-- Specific regex is needed to account for categories which are at the start or end of the string, which will be missing a pipe.
			-- (^|\\|) matches the beginning
			-- ($|\\|) matches the end
			query = h.andClause(query, field..[[__full REGEXP '(^|\\|)]]..value..[[($|\\|)']])
		else
			query = h.andClause(query, string.format("%s HOLDS '%s'", field, value))
		end
	end
	return query
end

function p.holdsAny(field, values)
	local query
	for i, value in ipairs(values) do
		value = p.escape(value)
		query = h.orClause(query, string.format("%s HOLDS '%s'", field, value))
	end
	return query
end

function p.IN(field, values)
	local inValues = {}
	for i, value in ipairs(values) do
		if type(value) == "string" then
			value = p.escape(value)
			value = string.format("'%s'", value)
		end
		inValues[i] = value
	end
	inValues = table.concat(inValues, ", ")
	if inValues == "" then
		inValues = "''"
	end
	return string.format("%s IN (%s)", field, inValues)
end

function h.andClause(query, clause)
	return h.addClause("AND", query, "("..clause..")")
end

function h.orClause(query, clause)
	return h.addClause("OR", query, clause)
end

function h.addClause(operator, query, clause)
	if not query or query == "" then
		return clause
	end
	return table.concat({query, operator, clause}, " ")
end

function p.Schemas()
	return {
		query = {
			tables = {
				type = "string",
				required = true,
			},
			fields = {
				type = "string",
				required = true,
			},
			args = {
				type = "record",
				properties = {
					{
						name = "where",
						type = "string",
					},	
					{
						name = "join",
						type = "string",
					},
					{
						name = "groupBy",
						type = "string",
					},
					{
						name = "having",
						type = "string",
					},
					{
						name = "orderBy",
						type = "string",
					},
					{
						name = "limit",
						type = "number",
						default = 100,
					},
					{
						name = "offset",
						type = "number",
						default = 0,
					},
				}
			},
		},
		allOf = {
			["..."] = {
				type = "array",
				required = true,
				items = {
					oneOf = {
						{
							type = "string",
						},
						{
							type = "map",
							keys = { type = "string" },
							values = { type = "string" },
						},
					},
				}
			},
		},
		anyOf = {
			["..."] = {
				type = "array",
				required = true,
				items = {
					oneOf = {
						{
							type = "string",
						},
						{
							type = "map",
							keys = { type = "string" },
							values = { type = "string" },
						},
					},
				}
			},
		},
		holdsAll = {
			field = {
				type = "string",
				required = true,
			},
			values = {
				type = "array",
				required = true,
				items = { type = "string" },
			},
		},
		holdsAny = {
			field = {
				type = "string",
				required = true,
			},
			values = {
				type = "array",
				required = true,
				items = { type = "string" },
			}
		},
		IN = {
			field = {
				type = "string",
				required = true,
			},
			values = {
				type = "array",
				required = true,
				items = { type = "string" },
			},
		}
	}
end

function p.Documentation()
	return {
		sections = {
			{
				heading = "<code>mw.ext.cargo.query</code> wrapper",
				section = {
					query = {
						params = {"tables", "fields", "args"},
						returns = {
							"An array of the query results. Throws an error when query syntax is invalid.",
							"Categories to add to the page.",
						},
						cases = {
							{
								args = {
									"Games",
									"code, shortName",
									{
										where = "type='main'",
										orderBy = "releaseDate",
										limit = 3,
									},
								},
								expect = {
									{
										{ code = "TLoZ", shortName = "The Legend of Zelda"},
										{ code = "TAoL", shortName = "The Adventure of Link"},
										{ code = "ALttP", shortName = "A Link to the Past"},
									},
									"[[Category:Pages querying Cargo data]]",
								},
							},
						}
					},
				}
			},
			{
				heading = "Query builders",
				section = {
					allOf = {
						params = {"..."},
						returns = "A WHERE clause with ANDed conditions and escaped quotation marks.",
						cases = {
							outputOnly = true,
							{
								args = {
									{
										game = "Link's Awakening",
										remakeNum = 2,
									},
									"foo HOLDS 'bar'",
									"baz LIKE '%quux%'",
								},
								expect = [[(remakeNum='2') AND (game='Link\'s Awakening') AND (foo HOLDS 'bar') AND (baz LIKE '%quux%')]]
							}
						},
					},
					anyOf = {
						params = {"..."},
						returns = "A WHERE clause with ORed conditions and escaped quotation marks.",
						cases = {
							outputOnly = true,
							{
								args = {
									{
										game = "Link's Awakening",
										remakeNum = 2,
									},
									"foo HOLDS 'bar'",
									"baz LIKE '%quux%'",
								},
								expect = [[remakeNum='2' OR game='Link\'s Awakening' OR foo HOLDS 'bar' OR baz LIKE '%quux%']]
							}
						},
					},
					escape = {
						desc = "Escapes special characters (namely apostrophes) that are not supported in Cargo string values.",
						params = {"str"},
						returns = "A string with escaped apostrophes.",
						cases = {
							outputOnly = true,
							{
								args = {"Dinraal's Claw"},
								expect = "Dinraal\\'s Claw",
							},
						},
					},
					holdsAll = {
						params = {"field", "values"},
						returns = "A query string of and'ed HOLDS clauses.",
						cases = {
							outputOnly = true,
							{
								args = {"game", {"Link's Awakening"}},
								expect = "(game HOLDS 'Link\\'s Awakening')",
							},
							{
								desc = "As a workaround to a [https://phabricator.wikimedia.org/T267498 Cargo issue], multiple HOLDS statements are converted to an equivalent regex-based syntax.",
								args = {"game", {"OoT", "TP"}},
								expect = "(game__full REGEXP '(^|\\\\|)OoT($|\\\\|)') AND (game__full REGEXP '(^|\\\\|)TP($|\\\\|)')",
							}
						},
					},
					holdsAny = {
						params = {"field", "values"},
						returns = "A query string of or'ed HOLDS clauses.",
						cases = {
							outputOnly = true,
							{
								args = {"game", {"Link's Awakening"}},
								expect = "game HOLDS 'Link\\'s Awakening'",
							},
							{
								args = {"game", {"OoT", "TP"}},
								expect = "game HOLDS 'OoT' OR game HOLDS 'TP'",
							}
						}
					},
					IN = {
						params = {"field", "values"},
						returns = "A where clause using the SQL IN keyword.",
						cases = {
							outputOnly = true,
							{
								args = {"_pageName", {"Link's Shadow", "Zelda", "Ganon"}},
								expect = [[_pageName IN ('Link\'s Shadow', 'Zelda', 'Ganon')]],
							},
							{
								args = {"_pageName", {}},
								expect = [[_pageName IN ('')]],
							}
						}
					},
				}
			},
			{
				heading = "Category strings",
				section = {
					categoryQuerying = {
						params = {},
						returns =  "The categories that should be added to pages querying Cargo data, as per [[Guidelines:Cargo#Best Practices]].",
						cases = {
							{
								args = {},
							}
						}
					},
					categoryStoring = {
						params = {},
						returns = "The categories that should be added to pages storing Cargo data, as per [[Guidelines:Cargo#Best Practices]].",
						cases = {
							{
								args = {},
							}
						}
					},
				},
			},
		},
	}
end

return p