このコミットが含まれているのは:
@@ -0,0 +1,88 @@
|
||||
# Public query grammar shared with frontend/src/lib/queryState.ts.
|
||||
class QueryState
|
||||
SCHEMAS = {
|
||||
'conditions' => ['field', 'operator', 'value', 'end', 'blank'],
|
||||
'drill' => ['field', 'grain', 'value', 'value_type'],
|
||||
'tag_refs' => ['name', 'tag_id', 'external_tag_id', 'negative', 'missing'],
|
||||
'column_order' => ['value', 'value_type'],
|
||||
'measures' => ['key', 'axis', 'value', 'value_type'] }.freeze
|
||||
|
||||
def self.list(value)
|
||||
return [] if value.nil? || value == ''
|
||||
raise ArgumentError unless value.is_a?(String) && value.bytesize <= 32_768
|
||||
|
||||
values = value.split(/ /, -1)
|
||||
raise ArgumentError if values.length > 200 || values.any?(&:empty?) || values.uniq != values
|
||||
|
||||
values
|
||||
end
|
||||
|
||||
def self.valid_key?(key)
|
||||
root, index, field, extra = key.split('.', -1)
|
||||
!extra && SCHEMAS.key?(root) && index&.match?(/\A(?:0|[1-9][0-9]?)\z/) &&
|
||||
SCHEMAS[root].include?(field)
|
||||
end
|
||||
|
||||
def self.validate!(params, allowed)
|
||||
raise ArgumentError if params.length > 1000
|
||||
params.each do |key, value|
|
||||
raise ArgumentError unless allowed.include?(key) || valid_key?(key)
|
||||
raise ArgumentError unless value.is_a?(String) && value.bytesize <= 131_072
|
||||
end
|
||||
SCHEMAS.each_key { records(params, _1) }
|
||||
end
|
||||
|
||||
def self.records(params, key)
|
||||
records = {}
|
||||
params.each do |name, value|
|
||||
next unless name.start_with?("#{ key }.")
|
||||
raise ArgumentError unless params.key?(key) && valid_key?(name)
|
||||
|
||||
_root, index, field = name.split('.')
|
||||
record = records[index.to_i] ||= {}
|
||||
if ['blank', 'negative', 'missing'].include?(field)
|
||||
raise ArgumentError unless ['0', '1'].include?(value)
|
||||
|
||||
value = value == '1'
|
||||
elsif ['tag_id', 'external_tag_id', 'axis'].include?(field)
|
||||
raise ArgumentError unless value.match?(/\A[0-9]{1,18}\z/)
|
||||
|
||||
value = value.to_i
|
||||
end
|
||||
record[field] = value
|
||||
end
|
||||
raise ArgumentError if params.key?(key) && params[key] != ''
|
||||
raise ArgumentError unless records.keys.sort == (0...records.length).to_a
|
||||
|
||||
records.sort.map do |_index, record|
|
||||
type = record.delete('value_type')
|
||||
if ['drill', 'measures', 'column_order'].include?(key)
|
||||
raise ArgumentError unless ['null', 'string', 'number', 'boolean'].include?(type)
|
||||
|
||||
raise ArgumentError unless type == 'null' || record.key?('value')
|
||||
|
||||
value = record['value']
|
||||
record['value'] = case type
|
||||
when 'null' then nil
|
||||
when 'number'
|
||||
raise ArgumentError unless value&.match?(/\A-?[0-9]{1,18}\z/)
|
||||
Integer(value, 10)
|
||||
when 'boolean'
|
||||
raise ArgumentError unless ['true', 'false'].include?(value)
|
||||
value == 'true'
|
||||
else value
|
||||
end
|
||||
end
|
||||
record
|
||||
end
|
||||
end
|
||||
|
||||
def self.axes(value)
|
||||
list(value).map do |token|
|
||||
field, grain, extra = token.split(':', -1)
|
||||
raise ArgumentError if extra || !field.match?(/\A[a-z_]+\z/) || grain == ''
|
||||
|
||||
grain ? { 'field' => field, 'grain' => grain } : { 'field' => field }
|
||||
end
|
||||
end
|
||||
end
|
||||
新しいイシューから参照
ユーザーをブロックする