Class: Genio::Parser::Format::JsonSchema

Inherits:
Base
  • Object
show all
Includes:
Logging
Defined in:
lib/genio/parser/format/json_schema.rb

Instance Attribute Summary collapse

Attributes inherited from Base

#data_types, #endpoint, #enum_types, #files, #options, #services

Instance Method Summary collapse

Methods included from Logging

#logger

Methods inherited from Base

#expand_path, #initialize, #load_files, #open, #read_file, #to_iodocs

Constructor Details

This class inherits a constructor from Genio::Parser::Format::Base

Instance Attribute Details

#current_schemaObject

Returns the value of attribute current_schema.



10
11
12
# File 'lib/genio/parser/format/json_schema.rb', line 10

def current_schema
  @current_schema
end

Instance Method Details

#class_name(name) ⇒ Object

Format class name

Example

class_name("credit-card") # return "CreditCard"
class_name("/path/to/payment.json") # return "Payment"


249
250
251
252
253
254
# File 'lib/genio/parser/format/json_schema.rb', line 249

def class_name(name)
  name, anchor = name.to_s.split("#", 2)
  name = File.basename(name, ".json")
  name = name + "_" + anchor if anchor.present?
  name.gsub(/[^\w]/, "_").camelcase
end

#file_name(name) ⇒ Object

Fix file name format



257
258
259
# File 'lib/genio/parser/format/json_schema.rb', line 257

def file_name(name)
  name.to_s.gsub(/-/, "_").underscore
end

#fix_unknown_serviceObject

Map operations based on the Request or Response types.



262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/genio/parser/format/json_schema.rb', line 262

def fix_unknown_service
  new_services = Types::Base.new
  services.each do |service_name, service|
    unless data_types[service_name]
      service.operations.each do |operation_name, operation|
        if data_types[operation.request]
          new_services[operation.request] ||= Types::Base.new( :operations => {} )
          new_services[operation.request].operations[operation_name] = operation
        elsif data_types[operation.response]
          new_services[operation.response] ||= Types::Base.new( :operations => {} )
          new_services[operation.response].operations[operation_name] = operation
        end
      end
    end
  end
  services.merge!(new_services)
end

#get_inline_schema_klass(names, schema, filename) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/genio/parser/format/json_schema.rb', line 53

def get_inline_schema_klass(names, schema, filename)
  names.each do |name|
    schema = schema[name] if schema
  end

  if schema
    klass = class_name(class_name(names.join("_")))
    data_types[klass] = {}
    data_types[klass] = parse_object(schema)
    klass
  else
    raise "Unable to find schema #{filename}"
  end
end

#inline_schema(filename) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/genio/parser/format/json_schema.rb', line 36

def inline_schema(filename)
  file, names  = filename.split("#", 2)
  names = names.split("/").delete_if(&:empty?)

  schema = self.current_schema
  if file.present?
    read_file(file) do |data|
      schema = JSON.parse(data, :object_class => Types::Base, :max_nesting => 100)
      store_schema(schema) do
        get_inline_schema_klass(names, schema, filename)
      end
    end
  else
    get_inline_schema_klass(names, schema, filename)
  end
end

#load(filename, force = false) ⇒ Object

Load schema

Example

schema.load("path/to/json_schema.json")
schema.load("http://example.com/json_schema.json")


16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/genio/parser/format/json_schema.rb', line 16

def load(filename, force = false)
  if data_types[filename] || (!force and data_types[class_name(filename)])
    class_name(filename)
  elsif filename =~ /#./ and self.current_schema
    inline_schema(filename)
  elsif files[filename]
    files[filename]
  else
    files[filename] = class_name(filename)
    parse_file(filename)
  end
rescue Errno::ENOENT => error
  if force and data_types[class_name(filename)]
    logger.error error.message
    class_name(filename)
  else
    raise error
  end
end

#parse_file(filename) ⇒ Object

Parse Given schema file and return class name



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/genio/parser/format/json_schema.rb', line 77

def parse_file(filename)
  klass = class_name(filename)
  read_file(filename) do |data|
    data = JSON.parse(data, :object_class => Types::Base, :max_nesting => 100)
    store_schema(data) do
      if data.resources # discovery format
        parse_resource(data)
      else
        data_types[klass] = {}
        data_types[klass] = parse_object(data)
      end
    end
  end
  klass
end

#parse_object(data) ⇒ Object

Parse object schema



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/genio/parser/format/json_schema.rb', line 94

def parse_object(data)
  if data["$ref"]
    return self.data_types[self.load(data["$ref"], true)]
  end

  properties = Types::Base.new
  required_properties = data.required.is_a?(Array) ? data.required : []

  # Parse each properties
  if data.properties
    data.properties.each do |name, options|
      properties[name] = parse_property(name, options)
      properties[name].required = true if required_properties.include? name
    end
  elsif data.type.is_a?(Array)
    data.type.each do |object|
      properties.merge!(parse_object(object).properties) if object.is_a? Hash
    end
  end

  # Load extends class.
  data.extends =
    if data.extends.is_a? String
      self.load(data.extends)
    elsif data.extends.is_a? Hash
      if data.extends["$ref"]
        self.load(data.extends["$ref"])
      else
        properties.merge!(parse_object(data.extends).properties)
        nil
      end
    else
      data.extends = nil
    end

  ["oneOf", "anyOf", "allOf"].each do |name|
    data[name] = data[name].map do |schema|
      parse_property(name, schema)
    end if data[name].is_a? Array
  end

  # Parse array type
  if data.items
    array_type = parse_object(data.items)
    properties.merge!(array_type.properties)
    data.extends ||= array_type.extends
    data.array = true
  end

  data.properties = properties
  Types::DataType.new(data)
end

#parse_property(name, data) ⇒ Object

Parse property.



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/genio/parser/format/json_schema.rb', line 148

def parse_property(name, data)
  data.array = true if data.type == "array"
  data.type  =
    if data["$ref"]               # Check the type is refer to another schema or not
      self.load(data["$ref"])
    elsif data.additionalProperties and data.additionalProperties["$ref"]
      self.load(data.additionalProperties["$ref"])
    elsif data.properties # Check the type has object definition or not
      klass_name = class_name(name)
      data_types[klass_name] = parse_object(data)
      klass_name
    elsif data.type.is_a? Array
      data.oneOf = data.type.map do |type|
        type = Types::Base.new( "type" => type ) unless type.is_a? Hash
        parse_object(type) || Types::Base.new( "type" => "self" )
      end
      "object"
    elsif data.items              # Parse array value type
      array_property = parse_property(name, data.items)
      array_property.type
    else
      data.merge!(parse_object(data))
      data.type || "object"
    end
  Types::Property.new(data)
rescue => error
  logger.error error.message
  Types::Property.new
end

#parse_resource(data) ⇒ Object

Parse resource schema



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/genio/parser/format/json_schema.rb', line 179

def parse_resource(data)

  self.endpoint ||= data.rootUrl

  if data.schemas
    data.schemas.each do |name, options|
      data_types[class_name(name)] = true
    end
    data.schemas.each do |name, options|
      data_types[class_name(name)] = parse_object(options)
    end
  end

  parse_services(data.resources, data)
end

#parse_service(data, service) ⇒ Object

Parse each operation in service



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
# File 'lib/genio/parser/format/json_schema.rb', line 208

def parse_service(data, service)
  data["methods"] ||= {}

  data["methods"].each do |name, options|
    options.relative_path = options.path
    options.path = File.join(service.servicePath, options.path)
    options.type = options.httpMethod
    if options.request
      options.request_property = parse_property("#{name}_request", options.request)
      options.request = options.request_property.type
    end
    if options.response
      options.response_property = parse_property("#{name}_response", options.response)
      options.response = options.response_property.type
    end
    # Load service parameters
    if options.parameters.nil? and options.type == "GET"
      options.parameters = service.parameters
    end
  end

  data.operations = data["methods"]

  parse_services(data.resources, service) if data.resources

  Types::Service.new(data)
end

#parse_services(resources, data) ⇒ Object



195
196
197
198
199
200
201
202
203
204
205
# File 'lib/genio/parser/format/json_schema.rb', line 195

def parse_services(resources, data)
  # Parse Resources
  resources.each do |name, options|
    service = parse_service(options, data)
    service.path = File.join(data.servicePath, name)
    if services[class_name(name)]
      service.operations.merge!(services[class_name(name)].operations)
    end
    services[class_name(name)] = service
  end
end

#store_schema(schema) ⇒ Object



68
69
70
71
72
73
74
# File 'lib/genio/parser/format/json_schema.rb', line 68

def store_schema(schema)
  backup = self.current_schema
  self.current_schema = schema
  yield
ensure
  self.current_schema = backup
end

#update_ref_paths(data, paths = {}) ⇒ Object

Update configured ref links



237
238
239
240
241
242
243
# File 'lib/genio/parser/format/json_schema.rb', line 237

def update_ref_paths(data, paths = {})
  paths.each do |path, replace_path|
    replace_path = replace_path.sub(/\/?$/, "/")
    data = data.gsub(/("\$ref"\s*:\s*")#{Regexp.escape(path)}\/?/, "\\1#{replace_path}")
  end
  data
end