Class: Generator::Notification
- Inherits:
-
Object
- Object
- Generator::Notification
- Includes:
- FileWriter, SchemaGenerator, SchemaHelpers
- Defined in:
- lib/generator/notification.rb
Overview
Generates Ruby type classes for SP-API notification payloads from JSON Schema files
Constant Summary
Constants included from Formatter
Instance Attribute Summary collapse
-
#file_path ⇒ Object
readonly
Returns the value of attribute file_path.
-
#schema ⇒ Object
readonly
Returns the value of attribute schema.
Class Method Summary collapse
Instance Method Summary collapse
-
#class_name ⇒ Object
Class name for the notification (e.g., "AnyOfferChanged").
-
#envelope_properties ⇒ Object
Get envelope properties (notificationVersion, notificationType, eventTime, etc.).
-
#envelope_required_properties ⇒ Object
Get required envelope properties.
-
#generate ⇒ Object
-
#initialize(file_path) ⇒ Notification
constructor
A new instance of Notification.
-
#notification_name ⇒ Object
Extract notification name from filename (e.g., "AnyOfferChangedNotification.json" => "AnyOfferChanged").
-
#notification_object_name ⇒ Object
Get the actual notification object name within payload (handles casing variations).
-
#notification_type ⇒ Object
Detect if this is Type A (nested) or Type B (flat) notification.
-
#payload_key_name ⇒ Object
Get the actual payload key name from the schema (handles PascalCase vs camelCase).
-
#payload_properties ⇒ Object
Get payload properties based on notification type.
-
#payload_required_properties ⇒ Object
Get required payload properties based on notification type.
-
#payload_schema ⇒ Object
Get payload schema (the full payload object).
-
#payload_version ⇒ Object
Extract payload version from the schema examples or filename.
-
#raw_description ⇒ Object
Class description from schema (no leading spaces - template handles indentation) Raw description from schema (will be formatted by Type class).
-
#replace_inline_objects_with_refs(properties, extracted_types) ⇒ Object
Replace inline object definitions with $ref pointers to extracted types.
-
#wrapper_properties ⇒ Object
Properties from the wrapper level (notificationVersion, notificationType, etc.).
-
#wrapper_required_properties ⇒ Object
Required properties from the wrapper level.
Methods included from SchemaGenerator
#generate_main_file!, included, #needs_money?, #output_file_path, #sorted_properties
Methods included from SchemaHelpers
#api_name_for_type_resolver, #attribute_name_for, #format_property_comment, #generate_nested_types!, #generate_rbs!, #generic_placeholder?, #needs_money?, #nested_type_files, #ruby_type_for, #type_resolver
Methods included from Formatter
#convert_doc_links_to_full_url, #convert_html_links_to_yard, #format_method_definition, #split_long_comment_line
Methods included from FileWriter
Constructor Details
#initialize(file_path) ⇒ Notification
Returns a new instance of Notification.
35 36 37 38 39 40 |
# File 'lib/generator/notification.rb', line 35 def initialize(file_path) @file_path = file_path @schema = JSON.parse(File.read(file_path)) resolve_root_ref! apply_json_patch! end |
Instance Attribute Details
#file_path ⇒ Object (readonly)
Returns the value of attribute file_path.
27 28 29 |
# File 'lib/generator/notification.rb', line 27 def file_path @file_path end |
#schema ⇒ Object (readonly)
Returns the value of attribute schema.
27 28 29 |
# File 'lib/generator/notification.rb', line 27 def schema @schema end |
Class Method Details
.schema_type ⇒ Object
30 31 32 |
# File 'lib/generator/notification.rb', line 30 def schema_type "notifications" end |
Instance Method Details
#class_name ⇒ Object
Class name for the notification (e.g., "AnyOfferChanged")
104 105 106 107 |
# File 'lib/generator/notification.rb', line 104 def class_name # Underscore first to ensure ActiveSupport::Inflector applies acronym rules correctly notification_name.underscore.camelize end |
#envelope_properties ⇒ Object
Get envelope properties (notificationVersion, notificationType, eventTime, etc.)
154 155 156 157 158 159 160 161 |
# File 'lib/generator/notification.rb', line 154 def envelope_properties keys = ["notificationVersion", "notificationType", "payloadVersion", "eventTime", "notificationMetadata"] keys.flat_map { |key| [key, key.sub(/\A./, &:upcase)] }.each_with_object({}) do |key, props| prop = schema.dig("properties", key) props[key] = prop if prop end end |
#envelope_required_properties ⇒ Object
Get required envelope properties
164 165 166 167 168 |
# File 'lib/generator/notification.rb', line 164 def envelope_required_properties required = schema["required"] || [] envelope_keys = envelope_properties.keys required.select { |r| envelope_keys.include?(r) } end |
#generate ⇒ Object
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/generator/notification.rb', line 42 def generate written_files = [] all_types = [] # Generate nested types first nested_results = generate_nested_types! written_files.concat(nested_results[:files]) all_types.concat(nested_results[:types]) # Generate Payload and Notification as types payload_results = generate_payload_types! written_files.concat(payload_results[:files]) all_types.concat(payload_results[:types]) notification_result = generate_notification_type! written_files << notification_result[:file] all_types << notification_result[:type] # Generate main convenience file written_files << generate_main_file! # Reload to pick up newly generated files for RBS introspection IntrospectionLoader.reload written_files << generate_rbs!(all_types) # Batch format all written files format_files(written_files) Generator.logger.info("Generated notification #{notification_name.underscore}") end |
#notification_name ⇒ Object
Extract notification name from filename (e.g., "AnyOfferChangedNotification.json" => "AnyOfferChanged")
74 75 76 77 78 79 |
# File 'lib/generator/notification.rb', line 74 def notification_name base_name = File.basename(file_path, ".json") # Remove "Notification" suffix but keep version suffix (e.g., "_2023-12-13") # Convert dashes in version suffix to underscores for valid Ruby identifiers base_name.sub(/Notification$/, "").tr("-", "_") end |
#notification_object_name ⇒ Object
Get the actual notification object name within payload (handles casing variations)
94 95 96 97 98 99 100 101 |
# File 'lib/generator/notification.rb', line 94 def notification_object_name payload = resolved_payload_schema return unless payload notification_object_candidates.find do |candidate| payload.dig("properties", candidate) end end |
#notification_type ⇒ Object
Detect if this is Type A (nested) or Type B (flat) notification
147 148 149 150 151 |
# File 'lib/generator/notification.rb', line 147 def notification_type return :unknown unless resolved_payload_schema notification_object_schema ? :nested : :flat end |
#payload_key_name ⇒ Object
Get the actual payload key name from the schema (handles PascalCase vs camelCase)
82 83 84 85 86 87 88 89 90 91 |
# File 'lib/generator/notification.rb', line 82 def payload_key_name # Check if schema uses "payload" or "Payload" if schema.dig("properties", "payload") "payload" elsif schema.dig("properties", "Payload") "Payload" else "payload" # fallback end end |
#payload_properties ⇒ Object
Get payload properties based on notification type
176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/generator/notification.rb', line 176 def payload_properties payload = resolved_payload_schema return {} unless payload if notification_type == :nested nested_obj = notification_object_schema return sorted_properties(nested_obj["properties"] || {}) if nested_obj {} else # Type B: use payload properties directly sorted_properties(payload["properties"] || {}) end end |
#payload_required_properties ⇒ Object
Get required payload properties based on notification type
192 193 194 195 196 197 198 199 200 201 202 203 |
# File 'lib/generator/notification.rb', line 192 def payload_required_properties payload = resolved_payload_schema return [] unless payload if notification_type == :nested nested_obj = notification_object_schema nested_obj ? (nested_obj["required"] || []) : [] else # Type B: use payload required directly payload["required"] || [] end end |
#payload_schema ⇒ Object
Get payload schema (the full payload object)
171 172 173 |
# File 'lib/generator/notification.rb', line 171 def payload_schema schema.dig("properties", "payload") || schema.dig("properties", "Payload") end |
#payload_version ⇒ Object
Extract payload version from the schema examples or filename
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/generator/notification.rb', line 110 def payload_version # Try to get from examples first examples = schema.dig("examples") if examples&.first return examples.first["payloadVersion"] || examples.first["PayloadVersion"] end # Fall back to filename-based version (e.g., "_2023-12-13") if file_path =~ /_(\d{4}-\d{2}-\d{2})\.json$/ matched = ::Regexp.last_match(1) return matched if matched end # Default to 1.0 if no version found "1.0" end |
#raw_description ⇒ Object
Class description from schema (no leading spaces - template handles indentation) Raw description from schema (will be formatted by Type class)
139 140 141 142 143 144 |
# File 'lib/generator/notification.rb', line 139 def raw_description return unless schema["description"] return if generic_placeholder?(schema["description"]) schema["description"] end |
#replace_inline_objects_with_refs(properties, extracted_types) ⇒ Object
Replace inline object definitions with $ref pointers to extracted types
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
# File 'lib/generator/notification.rb', line 206 def replace_inline_objects_with_refs(properties, extracted_types) result = {} properties.each do |prop_name, prop_def| # Check if this is a money-like object if MoneyDetector.money_like?(prop_def) result[prop_name] = { "$ref" => "#/definitions/Money", "description" => prop_def["description"], }.compact next end # Check if this property matches an extracted type name if prop_def["type"] == "object" && extracted_types.key?(prop_name) # Replace with $ref result[prop_name] = { "$ref" => "#/definitions/#{prop_name}" } elsif prop_def["type"] == "array" && prop_def["items"] # Handle array items - check if they're objects that match extracted types items = prop_def["items"] # Handle case where items is an array (non-standard format) items = items.first if items.is_a?(Array) && !items.empty? # Check if items are money-like objects if items.is_a?(Hash) && MoneyDetector.money_like?(items) result[prop_name] = prop_def.dup result[prop_name]["items"] = { "$ref" => "#/definitions/Money" } next end # Check if items are inline objects that match extracted types if items.is_a?(Hash) && (items["type"] == "object" || items["anyOf"]) item_type_name = prop_name.singularize if extracted_types.key?(item_type_name) result[prop_name] = prop_def.dup result[prop_name]["items"] = { "$ref" => "#/definitions/#{item_type_name}" } else result[prop_name] = prop_def end else result[prop_name] = prop_def end else result[prop_name] = prop_def end end result end |
#wrapper_properties ⇒ Object
Properties from the wrapper level (notificationVersion, notificationType, etc.)
128 129 130 |
# File 'lib/generator/notification.rb', line 128 def wrapper_properties sorted_properties(schema["properties"] || {}) end |
#wrapper_required_properties ⇒ Object
Required properties from the wrapper level
133 134 135 |
# File 'lib/generator/notification.rb', line 133 def wrapper_required_properties schema["required"] || [] end |