Module: RSMP::Schema

Defined in:
lib/rsmp/schema.rb,
lib/rsmp/schema_error.rb

Overview

Provides JSON Schema validation for RSMP messages across core and SXL versions.

Defined Under Namespace

Classes: Error, UnknownSchemaError, UnknownSchemaTypeError, UnknownSchemaVersionError

Class Method Summary collapse

Class Method Details

.core_versionsObject

get array of core schema versions



61
62
63
# File 'lib/rsmp/schema.rb', line 61

def self.core_versions
  versions :core
end

.earliest_core_versionObject

get earliest core schema version



66
67
68
# File 'lib/rsmp/schema.rb', line 66

def self.earliest_core_version
  earliest_version :core
end

.earliest_version(type) ⇒ Object

get earliest schema version for a particular schema type



82
83
84
85
# File 'lib/rsmp/schema.rb', line 82

def self.earliest_version(type)
  schemas = find_schemas!(type).keys
  sort_versions(schemas).first
end

.find_schema(type, version, options = {}) ⇒ Object

find schema for a particular schema and version return nil if not found

Raises:

  • (ArgumentError)


131
132
133
134
135
136
137
138
139
140
# File 'lib/rsmp/schema.rb', line 131

def self.find_schema(type, version, options = {})
  raise ArgumentError, 'version missing' unless version

  version = sanitize_version version if options[:lenient]
  if version
    schemas = find_schemas type
    return schemas[version] if schemas
  end
  nil
end

.find_schema!(type, version, options = {}) ⇒ Object

find schema for a particular schema and version raise error if not found

Raises:

  • (ArgumentError)


159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/rsmp/schema.rb', line 159

def self.find_schema!(type, version, options = {})
  schema = find_schema type, version, options
  raise ArgumentError, 'version missing' unless version

  version = sanitize_version version if options[:lenient]
  if version
    schemas = find_schemas! type
    schema = schemas[version]
    return schema if schema
  end
  raise UnknownSchemaVersionError, "Unknown schema version #{type} #{version}"
end

.find_schemas(type) ⇒ Object

find schemas versions for particular schema type return nil if type not found

Raises:

  • (ArgumentError)


114
115
116
117
118
# File 'lib/rsmp/schema.rb', line 114

def self.find_schemas(type)
  raise ArgumentError, 'type missing' unless type

  @schemas[type.to_sym]
end

.find_schemas!(type) ⇒ Object

find schemas versions for particular schema type raise error if not found



122
123
124
125
126
127
# File 'lib/rsmp/schema.rb', line 122

def self.find_schemas!(type)
  schemas = find_schemas type
  raise UnknownSchemaTypeError, "Unknown schema type #{type}" unless schemas

  schemas
end

.latest_core_versionObject

get latesty core schema version



71
72
73
# File 'lib/rsmp/schema.rb', line 71

def self.latest_core_version
  latest_version :core
end

.latest_version(type) ⇒ Object

get latest schema version for a particular schema type



88
89
90
91
# File 'lib/rsmp/schema.rb', line 88

def self.latest_version(type)
  schemas = find_schemas!(type).keys
  sort_versions(schemas).last
end

.load_schema_type(type, type_path, force: false) ⇒ Object

load an schema from a folder. schemas are organized by version, and contain json schema files, with the entry point being rsmp.jspon, eg: tlc

 1.0.7
   rsmp.json
   other jon schema files...
 1.0.8
 ...

an error is raised if the schema type already exists, and force is not set to true


28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/rsmp/schema.rb', line 28

def self.load_schema_type(type, type_path, force: false)
  raise "Schema type #{type} already loaded" if @schemas[type] && force != true

  @schemas[type] = {}
  Dir.glob("#{type_path}/*").select { |f| File.directory? f }.each do |schema_path|
    version = File.basename(schema_path)
    file_path = File.join(schema_path, 'rsmp.json')
    next unless File.exist? file_path

    @schemas[type][version] = JSONSchemer.schema(
      Pathname.new(File.join(schema_path, 'rsmp.json'))
    )
  end
end

.remove_schema_type(type) ⇒ Object

remove a schema type



44
45
46
# File 'lib/rsmp/schema.rb', line 44

def self.remove_schema_type(type)
  schemas.delete type
end

.sanitize_version(version) ⇒ Object

get major.minor.patch part of a version string, where patch is optional ignore trailing characters, e.g.

3.1.3.32A => 3.1.3
3.1A3r3 >= 3.1

return nil if string doesn’t match



147
148
149
150
151
152
153
154
155
# File 'lib/rsmp/schema.rb', line 147

def self.sanitize_version(version)
  # match normal semver z.y.z format
  if (matched = /^\d+\.\d+\.\d+/.match(version))
    matched.to_s
  # match x.y format, and add patch version zero to get z.y.0
  elsif (matched = /^\d+\.\d+/.match(version))
    "#{matched}.0"
  end
end

.schema?(type, version, options = {}) ⇒ Boolean

true if a particular schema type and version found

Returns:

  • (Boolean)


173
174
175
# File 'lib/rsmp/schema.rb', line 173

def self.schema?(type, version, options = {})
  find_schema(type, version, options) != nil
end

.schema_typesObject

get schemas types



49
50
51
# File 'lib/rsmp/schema.rb', line 49

def self.schema_types
  schemas.keys
end

.schemasObject

get all schemas, oganized by type and version



54
55
56
57
58
# File 'lib/rsmp/schema.rb', line 54

def self.schemas
  raise 'No schemas available, perhaps Schema.setup was never called?' unless @schemas

  @schemas
end

.setupObject



9
10
11
12
13
14
15
16
# File 'lib/rsmp/schema.rb', line 9

def self.setup
  @schemas = {}
  schemas_path = File.expand_path(File.join(__dir__, '..', '..', 'schemas'))
  Dir.glob("#{schemas_path}/*").select { |f| File.directory? f }.each do |type_path|
    type = File.basename(type_path).to_sym
    load_schema_type type, type_path
  end
end

.sort_versions(versions) ⇒ Object

sort version strings



108
109
110
# File 'lib/rsmp/schema.rb', line 108

def self.sort_versions(versions)
  versions.sort_by { |k| Gem::Version.new(k) }
end

.status_catalogue(type, version) ⇒ Object

return a catalogue of statuses for a particular schema type and version returns a hash of { status_code_id_sym => [arg_name_sym, …] } raises an error if the schema type/version is not found, or has no sxl.yaml



180
181
182
183
184
185
186
187
188
189
190
# File 'lib/rsmp/schema.rb', line 180

def self.status_catalogue(type, version)
  find_schema! type, version
  schemas_path = File.expand_path(File.join(__dir__, '..', '..', 'schemas'))
  yaml_path = File.join(schemas_path, type.to_s, version, 'sxl.yaml')
  raise "No sxl.yaml for #{type} #{version}" unless File.exist?(yaml_path)

  sxl = RSMP::Convert::Import::YAML.read(yaml_path)
  sxl[:statuses].transform_keys(&:to_sym).transform_values do |status|
    (status['arguments'] || {}).keys.map(&:to_sym)
  end
end

.validate(message, schemas, options = {}) ⇒ Object

validate using a particular schema and version raises error if schema is not found return nil if validation succeds, otherwise returns an array of errors

Raises:

  • (ArgumentError)


195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/rsmp/schema.rb', line 195

def self.validate(message, schemas, options = {})
  raise ArgumentError, 'message missing' unless message
  raise ArgumentError, 'schemas missing' unless schemas
  raise ArgumentError, 'schemas must be a Hash' unless schemas.is_a?(Hash)
  raise ArgumentError, 'schemas cannot be empty' unless schemas.any?

  errors = schemas.flat_map do |type, version|
    schema = find_schema! type, version, options
    validate_using_schema(message, schema)
  end
  return nil if errors.empty?

  errors
end

.validate_using_schema(message, schema) ⇒ Object

validate an rsmp messages using a schema object

Raises:

  • (ArgumentError)


94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/rsmp/schema.rb', line 94

def self.validate_using_schema(message, schema)
  raise ArgumentError, 'message missing' unless message
  raise ArgumentError, 'schema missing' unless schema

  if schema.valid? message
    []
  else
    schema.validate(message).map do |item|
      [item['data_pointer'], item['type'], item['details']]
    end
  end
end

.versions(type) ⇒ Object

get array of schema versions for a particular schema type



76
77
78
79
# File 'lib/rsmp/schema.rb', line 76

def self.versions(type)
  schemas = find_schemas!(type).keys
  sort_versions(schemas)
end