Skip to content

Instantly share code, notes, and snippets.

@Totktonada
Created March 8, 2024 21:04
Show Gist options
  • Save Totktonada/5a310dfee015e8ac68fb01c066f8c222 to your computer and use it in GitHub Desktop.
Save Totktonada/5a310dfee015e8ac68fb01c066f8c222 to your computer and use it in GitHub Desktop.
Generate JSON schema for Tarantool's cluster configuration
#!/usr/bin/env tarantool
local json = require('json')
local cluster_config = require('internal.config.cluster_config')
local scalars = {}
scalars.string = {
jsonschema = {
type = 'string',
},
}
scalars.number = {
jsonschema = {
type = 'number',
},
}
scalars['string, number'] = {
jsonschema = {
type = {'string', 'number'},
},
}
scalars['number, string'] = {
jsonschema = {
type = {'string', 'number'},
},
}
scalars.integer = {
jsonschema = {
type = 'integer',
},
}
scalars.boolean = {
jsonschema = {
type = 'boolean',
},
}
scalars.any = {
jsonschema = {},
}
local function is_scalar(schema)
return scalars[schema.type] ~= nil
end
local function extract_validate_no_repeat()
local schema = require('internal.config.utils.schema')
local nups = debug.getinfo(schema.set).nups
for i = 1, nups do
local k, v = debug.getupvalue(schema.set, i)
if k == 'validate_no_repeat' then
return v
end
end
end
local validate_no_repeat = extract_validate_no_repeat()
assert(type(validate_no_repeat) == 'function')
local function set_common_fields(res, schema)
-- Ignores apply_default_if.
res.default = schema.default
res.enum = schema.allowed_values
return setmetatable(res, {
__serialize = 'map',
})
end
local function traverse_impl(schema)
if is_scalar(schema) then
local scalar_def = scalars[schema.type]
assert(scalar_def ~= nil)
return set_common_fields(table.copy(scalar_def.jsonschema), schema)
elseif schema.type == 'record' then
local properties = {}
for field_name, field_def in pairs(schema.fields) do
properties[field_name] = traverse_impl(field_def)
end
return set_common_fields({
type = 'object',
properties = properties,
additionalProperties = false,
}, schema)
elseif schema.type == 'map' then
assert(schema.key.type == 'string')
return set_common_fields({
type = 'object',
additionalProperties = traverse_impl(schema.value),
}, schema)
elseif schema.type == 'array' then
local res = {
type = 'array',
items = traverse_impl(schema.items),
}
if schema.validate == validate_no_repeat then
res.uniqueItems = true
end
return set_common_fields(res, schema)
else
assert(false)
end
end
local function traverse(schema_obj)
local res = traverse_impl(rawget(schema_obj, 'schema'))
res['$schema'] = 'https://json-schema.org/draft/2020-12/schema'
return res
end
local jsonschema = traverse(cluster_config)
print(json.encode(jsonschema))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment