Module: Soaspec::WsdlGenerator

Includes:
ExeHelpers
Defined in:
lib/soaspec/wsdl_generator.rb

Overview

Produce test content from a WSDL

Instance Method Summary collapse

Methods included from ExeHelpers

#class_content, #create_file, #create_files, #create_files_for, #create_folder, #generated_soap_spec_for, #retrieve_contents, #spec_task

Instance Method Details

#ask_wsdlObject

Prompt user for wsdl



161
162
163
164
165
166
167
168
# File 'lib/soaspec/wsdl_generator.rb', line 161

def ask_wsdl
  prompt = "  Enter WSDL:\n  WSDL_LOC\n  print prompt.chop\n  @wsdl = $stdin.gets.strip\n  puts\nend\n"

#camel_case(underscore_separated) ⇒ String

Returns CamelCased value.

Parameters:

  • underscore_separated (String, Symbol)

    Snakecase value to be converted to camel case

Returns:

  • (String)

    CamelCased value



103
104
105
# File 'lib/soaspec/wsdl_generator.rb', line 103

def camel_case(underscore_separated)
  underscore_separated.to_s.split('_').collect(&:capitalize).join
end

#complex_type?(element) ⇒ Boolean

Returns True if Nokogiri element is a complex type, that is, has a complexType element underneath itself.

Parameters:

  • element (Nokogiri::XML::Element)

    Element to check

Returns:

  • (Boolean)

    True if Nokogiri element is a complex type, that is, has a complexType element underneath itself



97
98
99
# File 'lib/soaspec/wsdl_generator.rb', line 97

def complex_type?(element)
  element.children.any? { |child| child.name == 'complexType' }
end

#document_type_for(element, depth = 1) ⇒ Object

Adds documentation content for all children of XML element

Parameters:

  • element (Nokogiri::XML::Element)

    Type to document for

  • depth (Integer) (defaults to: 1)

    How many times to iterate depth for



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/soaspec/wsdl_generator.rb', line 131

def document_type_for(element, depth = 1)
  # raise "Too far deep for #{element}" unless depth < 6
  return unless depth < 6

  if complex_type? element
    complex_type = element.children.find { |c| c.name == 'complexType' }
    sequence = complex_type.children.find { |c| c.name == 'sequence' }
    sequence.children.select { |node| node.class == Nokogiri::XML::Element }.each do |sub_element|
      document_type_for sub_element, depth += 1
    end
  else
    return "No type seen for #{element}, #{element.class}" unless element['type']

    name = element['name']
    type = value_after_namespace(element['type'])
    @use_camel_case = true unless /[[:upper:]]/.match(name[0]).nil?
    spaces = '  ' * depth
    @content += "#{spaces}#{name.snakecase}: #{fill_in_field_from_type(type)} # #{type} \n"
  end
end

#enter_auth_detailsArray

Prompt user to enter basic auth details

Returns:

  • (Array)

    Array with Basic auth username and password entered



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/soaspec/wsdl_generator.rb', line 182

def enter_auth_details
  prompt = "  User Name:\n  AUTH_PROMPT\n  print prompt.chop\n  auth_name = $stdin.gets.strip\n  puts\n\n  prompt = <<-PASSWORD\n  User Password:\n  PASSWORD\n  print prompt.chop\n  auth_password = $stdin.gets.strip\n  puts\n  [auth_name, auth_password]\nend\n"

#enumeration?(type) ⇒ Boolean

Returns Whether WSDL type is an enumeration.

Parameters:

  • type (Nokogiri::XML::NodeSet)

    WSDL element type

Returns:

  • (Boolean)

    Whether WSDL type is an enumeration



68
69
70
71
72
# File 'lib/soaspec/wsdl_generator.rb', line 68

def enumeration?(type)
  return false unless type.first

  !type.xpath("*/#{type.first.namespace.prefix}:enumeration").empty?
end

#fill_in_field_from_type(type) ⇒ Object

Based on WSDL type return a valid value

Parameters:

  • type (String)

    Type without the WSDL

Returns:

  • (Object)

    Value representing type to fill in



84
85
86
87
88
89
90
91
92
93
# File 'lib/soaspec/wsdl_generator.rb', line 84

def fill_in_field_from_type(type)
  case type
  when 'string' then options[:string_default] # 'test string'
  when 'int' then 2
  when 'boolean' then true
  when 'double' then '1.5'
  else
    try_enum_for type
  end
end

#generate_from_wsdl(options) ⇒ Object

Generate from WSDL



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/soaspec/wsdl_generator.rb', line 7

def generate_from_wsdl(options)
  auth_name, auth_password = enter_auth_details if options[:auth] == 'basic'
  @virtual = false
  savon_options = { wsdl: options[:wsdl] }
  savon_options[:basic_auth] = [auth_name, auth_password] if options[:auth] == 'basic'

  wsdl_doc = Savon.client(**savon_options).wsdl
  @wsdl_schemas = wsdl_doc.parser.schemas
  # Create basic project files
  create_files %w[Rakefile Gemfile README.md spec/spec_helper.rb], ignore_if_present: true
  create_file(filename: '.rspec')
  create_file(filename: '.travis.yml') if options[:ci] == 'travis'
  create_folder 'logs'
  create_file filename: "lib/#{options[:name].snakecase}.rb", content: class_content

  # Files according to WSDL
  wsdl_doc.operations.each do |operation, op_details|
    puts "Creating files for operation: #{operation}"
    @content = "default:\n"
    @use_camel_case = false
    puts 'Message params: ' + op_details.to_s
    # From namespace identifier, find namespace, and for that find schemaLocation xsd and use that to build request
    if op_details[:parameters]
      op_details[:parameters].each do |element, details|
        @use_camel_case = true unless /[[:upper:]]/.match(element.to_s[0]).nil?
        @content += "  #{element.to_s.snakecase}: #{fill_in_field_from_type(details[:type])} # #{details[:type]} \n"
        # TODO: If details is a Hash need to loop again
      end
    end
    wsdl_to_yaml_for root_elements_for(op_details)
    params = []
    params << 'convert_request_keys_to: :camelcase' if @use_camel_case
    params_string = params == [] ? '' : ', ' + params.join(', ')
    @class_params = "'#{camel_case(operation)}'#{params_string}"

    create_file(filename: "config/data/#{operation}.yml", content: @content)
    create_file(filename: "spec/#{operation}_spec.rb", content: generated_soap_spec_for(operation))
  end
end

#name_of_wsdlObject

Prompt user for Service name for wsdl



171
172
173
174
175
176
177
178
# File 'lib/soaspec/wsdl_generator.rb', line 171

def name_of_wsdl
  prompt = "  Enter what you would like to name WSDL (CamelCase):\n  WSDL_NAME\n  print prompt.chop\n  @name = $stdin.gets.strip\n  puts\nend\n"

#root_elements_for(op_details) ⇒ Nokogiri::XML::NodeSet

Returns List of the root elements in the SOAP body.

Parameters:

  • op_details (Hash)

    Hash with details from WSDL for an operation

Returns:

  • (Nokogiri::XML::NodeSet)

    List of the root elements in the SOAP body



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/soaspec/wsdl_generator.rb', line 109

def root_elements_for(op_details)
  raise "'@wsdl_schemas' must be defined" if @wsdl_schemas.nil?

  root_element = @wsdl_schemas.at_xpath("//*[@name='#{op_details[:input]}']")
  raise 'Operation has no input defined' if root_element.nil?

  schema_namespace = root_element.namespace.prefix
  root_type = root_element['type']
  if root_type
    @wsdl_schemas.xpath("//*[@name='#{root_type.split(':').last}']//#{schema_namespace}:element")
  else
    return [] unless complex_type? root_element # Empty Array if there are no root elements

    complex_type = root_element.children.find { |c| c.name == 'complexType' }
    sequence = complex_type.children.find { |c| c.name == 'sequence' }
    sequence.xpath("#{schema_namespace}:element")
  end
end

#try_enum_for(type) ⇒ Object

Attempt to calculate values of enumeration by looking up type in Schema

Parameters:

  • type (String)

    Try out filling enumeration for type. Return Custom Type if can’t be done



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/soaspec/wsdl_generator.rb', line 49

def try_enum_for(type)
  raise "'@wsdl_schemas' must be defined" if @wsdl_schemas.nil?

  custom_type = @wsdl_schemas.xpath("//*[@name='#{type}']")
  if enumeration? custom_type
    prefix = custom_type.first.namespace.prefix
    enumerations = custom_type.xpath("//#{prefix}:enumeration")
    return 'Custom Type' if enumerations.empty?

    @enums_values = []
    enumerations.each { |enum_value| @enums_values << "'#{enum_value['value']}'" }
    "~randomize [#{@enums_values.join(', ')}]"
  else
    'Custom Type'
  end
end

#value_after_namespace(string) ⇒ String

Return value of string after a namespace

Parameters:

  • string (String)

    String to parse for part after namespace

Returns:

  • (String)

    Part after the namespace, demonstrated by ‘:’



77
78
79
# File 'lib/soaspec/wsdl_generator.rb', line 77

def value_after_namespace(string)
  string.split(':').last
end

#wsdl_to_yaml_for(list) ⇒ Object

Makes a yaml string in a ‘@content’ instance variable

Parameters:

  • list (Nokogiri::XML::NodeSet)

    List to convert to YAML



154
155
156
157
158
# File 'lib/soaspec/wsdl_generator.rb', line 154

def wsdl_to_yaml_for(list)
  raise "'@content' string must be set" if @content.nil?

  list.each { |element| document_type_for element }
end