Class: LcpSdk::SchemaValidator
- Inherits:
-
Object
- Object
- LcpSdk::SchemaValidator
- Defined in:
- lib/schema_validator.rb
Class Method Summary collapse
Instance Method Summary collapse
-
#initialize(schema_documents) ⇒ SchemaValidator
constructor
A new instance of SchemaValidator.
- #validate(schema_name, instance) ⇒ Object
- #validate_envelope(envelope) ⇒ Object
- #validate_offer(offer) ⇒ Object
- #validate_ping_safe(vertical, value, schema, path) ⇒ Object
- #validate_vertical(vertical, attributes) ⇒ Object
- #validate_vertical_policy(type, payload) ⇒ Object
Constructor Details
#initialize(schema_documents) ⇒ SchemaValidator
Returns a new instance of SchemaValidator.
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/schema_validator.rb', line 9 def initialize(schema_documents) require "json_schemer" # Keep a private copy so self-references in core.json can be made # fragment-local without mutating the caller's canonical documents. @documents = schema_documents.transform_values do |document| JSON.parse(JSON.generate(document)) end @documents.each_value do |document| rewrite_core_refs(document) if document["$id"] == "https://lcp.dev/schemas/core.json" end @ids = {} @documents.each do |name, document| id = document.fetch("$id", name) @ids[normalize(name)] = id @ids[normalize(id)] = id end end |
Class Method Details
.from_directory(root) ⇒ Object
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/schema_validator.rb', line 27 def self.from_directory(root) documents = {} Dir.glob(File.join(root, "**", "*.json")).each do |path| name = path.delete_prefix("#{root}/") documents[name] = JSON.parse(File.read(path)) end if File.basename(root.to_s) == "schemas" vertical_root = File.join(File.dirname(root), "verticals") Dir.glob(File.join(vertical_root, "**", "*.json")).each do |path| name = path.delete_prefix("#{vertical_root}/") documents["verticals/#{name}"] = JSON.parse(File.read(path)) end end new(documents) end |
Instance Method Details
#validate(schema_name, instance) ⇒ Object
43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/schema_validator.rb', line 43 def validate(schema_name, instance) id = @ids.fetch(normalize(schema_name)) { raise KeyError, "unknown LCP schema: #{schema_name}" } schema = @documents.values.find { |candidate| candidate["$id"] == id } || @documents.fetch(schema_name) errors = JSONSchemer.schema(schema, ref_resolver: lambda do |uri| reference = uri.to_s @documents.values.find { |candidate| candidate["$id"].to_s == reference } || @documents[reference] || @documents[reference.sub(%r{.*/}, "")] end).validate(instance).to_a raise SchemaValidationError, "LCP schema validation failed for #{schema_name}: #{errors.first}" unless errors.empty? true end |
#validate_envelope(envelope) ⇒ Object
56 57 58 59 60 61 62 63 |
# File 'lib/schema_validator.rb', line 56 def validate_envelope(envelope) validate("schemas/envelope.json", envelope) type = envelope.dig("lcp", "message", "type") raise SchemaValidationError, "missing lcp.message.type" unless type.is_a?(String) payload = envelope.dig("lcp", "payload") validate("schemas/#{type}.json", payload) validate_vertical_policy(type, payload) end |
#validate_offer(offer) ⇒ Object
97 98 99 |
# File 'lib/schema_validator.rb', line 97 def validate_offer(offer) validate("schemas/offer.json", offer) end |
#validate_ping_safe(vertical, value, schema, path) ⇒ Object
84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/schema_validator.rb', line 84 def validate_ping_safe(vertical, value, schema, path) return unless value.is_a?(Hash) properties = schema.fetch("properties", {}) value.each do |name, child| next if path == "attributes" && %w[vertical schema_version].include?(name) definition = properties[name] unless definition.is_a?(Hash) && definition["ping_safe"] == true raise SchemaValidationError, "#{path}.#{name} is not tagged ping_safe: true in vertical '#{vertical}'" end validate_ping_safe(vertical, child, definition, "#{path}.#{name}") if child.is_a?(Hash) && definition["properties"].is_a?(Hash) end end |
#validate_vertical(vertical, attributes) ⇒ Object
101 102 103 |
# File 'lib/schema_validator.rb', line 101 def validate_vertical(vertical, attributes) validate("verticals/#{vertical}.json", attributes) end |
#validate_vertical_policy(type, payload) ⇒ Object
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/schema_validator.rb', line 65 def validate_vertical_policy(type, payload) return unless %w[lead call post ping].include?(type) && payload.is_a?(Hash) attributes = payload["attributes"] return unless attributes.is_a?(Hash) vertical = type == "ping" ? payload["vertical"] : attributes["vertical"] return unless vertical.is_a?(String) && !vertical.empty? schema = @documents[normalize("verticals/#{vertical}.json")] || @documents["verticals/#{vertical}.json"] || @documents["#{vertical}.json"] raise SchemaValidationError, "Vertical schema '#{vertical}' not found" unless schema.is_a?(Hash) vertical_attributes = JSON.parse(JSON.generate(attributes)) if type == "ping" vertical_attributes["vertical"] ||= vertical vertical_attributes["schema_version"] ||= schema.dig("properties", "schema_version", "const") || "1.0.0" end validate_vertical(vertical, vertical_attributes) validate_ping_safe(vertical, attributes, schema, "attributes") if type == "ping" end |