github.com/vmware/govmomi@v0.37.1/gen/vim_wsdl.rb (about) 1 # Copyright (c) 2014-2021 VMware, Inc. All Rights Reserved. 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 15 require "nokogiri" 16 require "test/unit" 17 18 # SINCE_API_FORMAT is used to capture the minimum API version for which some API 19 # symbol is valid. 20 SINCE_API_FORMAT = /^\*\*\*Since:\*\*\* \w+? API (?:Release )?(.+)$/ 21 22 # ENCLOSED_BY_ASTERIK_FORMAT is used to capture words enclosed by a single 23 # asterik on either side. 24 ENCLOSED_BY_ASTERIK_FORMAT = /\*([^\s]+)\*/ 25 26 # POSSIBLE_VALUE_FORMAT is used to capture a possible enum value. 27 POSSIBLE_VALUE_FORMAT = /^- `([^`]+?)`(?:: (.*))?$/ 28 29 $namespaces = %w(vim25) 30 $force_base_interface_for_types = ENV['FORCE_BASE_INTERFACE_FOR_TYPES'] 31 32 def sanitize_line(line) 33 line.gsub!("***Required privileges:***", "Required privileges:") 34 line.gsub!(ENCLOSED_BY_ASTERIK_FORMAT, '`\1`') 35 if line.start_with?("- ") || line.start_with?(" ") 36 line = " " + line 37 end 38 return line 39 end 40 41 def valid_ns?(t) 42 $namespaces.include?(t) 43 end 44 45 def ucfirst(v) 46 x = "ArrayOf" 47 if v.start_with?(x) 48 # example: ArrayOfvslmInfrastructureObjectPolicy -> ArrayOfVslm... 49 return x + ucfirst(v[x.length..-1]) 50 end 51 52 # example: vslmInfrastructureObjectPolicy -. VslmInfrastructureObjectPolicy 53 v[0].capitalize + v[1..-1] 54 end 55 56 def init_type(io, name, kind, minApiVersion=nil, minApiVersionsForValues=nil) 57 t = "reflect.TypeOf((*#{ucfirst kind})(nil)).Elem()" 58 59 io.print "func init() {\n" 60 61 if $target == "vim25" 62 io.print "t[\"#{name}\"] = #{t}\n" 63 if minApiVersion != nil 64 io.print "minAPIVersionForType[\"#{name}\"] = \"#{minApiVersion}\"\n" 65 end 66 if minApiVersionsForValues != nil 67 io.print "minAPIVersionForEnumValue[\"#{name}\"] = map[string]string{\n" 68 minApiVersionsForValues.each do |k, v| 69 io.print "\t\t\"#{k}\": \"#{v}\",\n" 70 end 71 io.print "}\n" 72 end 73 else 74 unless name.start_with? "Base" 75 name = "#{$target}:#{name}" 76 end 77 io.print "types.Add(\"#{name}\", #{t})\n" 78 if minApiVersion != nil 79 io.print "types.AddMinAPIVersionForType(\"#{name}\", \"#{minApiVersion}\")\n" 80 end 81 if minApiVersionsForValues != nil 82 minApiVersionsForValues.each do |k, v| 83 io.print "types.AddMinAPIVersionForEnumValue(\"#{name}\", \"#{k}\", \"#{v}\")\n" 84 end 85 end 86 end 87 88 io.print "}\n\n" 89 end 90 91 class Peek 92 class Type 93 attr_accessor :parent, :children, :klass 94 95 def initialize(name) 96 @name = name 97 @children = [] 98 end 99 100 def base? 101 # VrpResourceAllocationInfo is removed in 6.7, so base will no longer generated 102 return false if ["ResourceAllocationInfo", "FaultDomainId"].include?(@name) 103 104 return !children.empty? || $force_base_interface_for_types.split(",").include?(@name) 105 end 106 end 107 108 @@types = {} 109 @@refs = {} 110 @@enums = {} 111 112 def self.types 113 return @@types 114 end 115 116 def self.refs 117 return @@refs 118 end 119 120 def self.enums 121 return @@enums 122 end 123 124 def self.ref(type) 125 refs[type] = true 126 end 127 128 def self.enum(type) 129 enums[type] = true 130 end 131 132 def self.enum?(type) 133 enums[type] 134 end 135 136 def self.register(name) 137 raise unless name 138 types[name] ||= Type.new(name) 139 end 140 141 def self.base?(name) 142 return unless c = types[name] 143 c.base? 144 end 145 146 def self.dump_interfaces(io) 147 types.keys.sort.each do |name| 148 next unless base?(name) 149 klass = types[name].klass 150 klass.dump_interface(io, name) if klass 151 end 152 end 153 end 154 155 class EnumValue 156 attr_reader :comments 157 158 def initialize(type, value, comments) 159 @type = type 160 @value = value 161 @comments = comments 162 end 163 164 def type_name 165 ucfirst(@type.name) 166 end 167 168 def var_name 169 n = ucfirst(@type.name) 170 v = var_value 171 if v == "" 172 n += "Null" 173 else 174 n += ucfirst(v) 175 end 176 177 return n 178 end 179 180 def var_value 181 @value 182 end 183 184 def dump(io) 185 if @comments 186 io.print @comments 187 end 188 io.print "%s = %s(\"%s\")\n" % [var_name, type_name, var_value] 189 end 190 end 191 192 class Simple 193 include Test::Unit::Assertions 194 195 attr_accessor :name, :type 196 attr_reader :vijson, :vijson_props 197 198 def initialize(node, vijson) 199 @node = node 200 @vijson = vijson 201 202 if vijson != nil && name != nil 203 ucfirstName = ucfirst(name) 204 if vijson.has_key?(ucfirstName) 205 if vijson[ucfirstName].has_key?("properties") 206 @vijson_props = vijson[ucfirstName]["properties"] 207 end 208 end 209 end 210 end 211 212 def name 213 @name || @node["name"] 214 end 215 216 def type 217 @type || @node["type"] 218 end 219 220 def is_enum? 221 false 222 end 223 224 def dump_init(io) 225 # noop 226 end 227 228 def var_name 229 n = self.name 230 n = n[1..-1] if n[0] == "_" # Strip leading _ 231 n = ucfirst(n) 232 return n 233 end 234 235 def ns(t = self.type) 236 t.split(":", 2)[0] 237 end 238 239 def vim_type? 240 valid_ns? ns 241 end 242 243 def vim_type(t = self.type) 244 ns, kind = t.split(":", 2) 245 if ! valid_ns? ns 246 raise 247 end 248 ucfirst(kind) 249 end 250 251 def base_type? 252 vim_type? && (Peek.base?(vim_type) || $force_base_interface_for_types.split(",").include?(vim_type)) 253 end 254 255 def enum_type? 256 vim_type? && Peek.enum?(vim_type) 257 end 258 259 def any_type? 260 self.type == "xsd:anyType" 261 end 262 263 def pointer_type? 264 ["UnitNumber"].include?(var_name) or 265 optional? && ["IpPoolId", "OwnerId", "GroupId", "MaxWaitSeconds", "Reservation", "Limit", "OverheadLimit", "ResourceReductionToToleratePercent"].include?(var_name) 266 end 267 268 def var_type 269 t = self.type 270 prefix = "" 271 272 if slice? 273 prefix += "[]" 274 if ["AffinitySet"].include?(var_name) 275 self.need_omitempty = false 276 end 277 end 278 279 if t =~ /^xsd:(.*)$/ 280 t = $1 281 case t 282 when "string" 283 if ["IpPoolName"].include?(var_name) 284 self.need_omitempty = false 285 end 286 when "int" 287 if pointer_type? 288 prefix += "*" 289 self.need_omitempty = false 290 self.json_omitempty = true 291 end 292 t = "int32" 293 when "boolean" 294 t = "bool" 295 if !slice? && optional? 296 prefix += "*" 297 self.need_omitempty = false 298 self.json_omitempty = true 299 end 300 when "long" 301 if pointer_type? 302 prefix += "*" 303 self.need_omitempty = false 304 self.json_omitempty = true 305 end 306 t = "int64" 307 when "dateTime" 308 t = "time.Time" 309 if !slice? && optional? 310 prefix += "*" 311 self.need_omitempty = false 312 self.json_omitempty = true 313 end 314 when "anyType" 315 pkg = "" 316 if $target != "vim25" 317 pkg = "types." 318 end 319 t = "#{pkg}AnyType" 320 if ["Value", "Val"].include?(var_name) 321 self.need_omitempty = false 322 end 323 when "byte" 324 when "double" 325 t = "float64" 326 when "float" 327 t = "float32" 328 when "short" 329 t = "int16" 330 when "base64Binary" 331 t = "[]byte" 332 when "anyURI" 333 t = "string" 334 else 335 raise "unknown type: %s" % t 336 end 337 else 338 pkg = "" 339 if $target != self.ns 340 pkg = "types." 341 end 342 343 t = vim_type 344 345 if base_type? 346 prefix += "#{pkg}Base" 347 else 348 t = pkg + t 349 prefix += "*" if !slice? && !enum_type? && optional? 350 end 351 end 352 353 prefix + t 354 end 355 356 def slice? 357 test_attr("maxOccurs", "unbounded") 358 end 359 360 def optional? 361 test_attr("minOccurs", "0") 362 end 363 364 def need_omitempty=(v) 365 @need_omitempty = v 366 end 367 368 def json_omitempty=(v) 369 @json_omitempty = v 370 end 371 372 def need_omitempty? 373 var_type # HACK: trigger setting need_omitempty if necessary 374 if @need_omitempty.nil? 375 @need_omitempty = optional? 376 else 377 @need_omitempty 378 end 379 end 380 381 def json_omitempty? 382 var_type # HACK: trigger setting json_omitempty if necessary 383 if @json_omitempty.nil? 384 @json_omitempty = need_omitempty? 385 else 386 @json_omitempty 387 end 388 end 389 390 def need_typeattr? 391 base_type? || any_type? 392 end 393 394 protected 395 396 def test_attr(attr, expected) 397 actual = @node.attr(attr) 398 if actual != nil 399 case actual 400 when expected 401 true 402 else 403 raise "%s=%s" % [value, type.attr(value)] 404 end 405 else 406 false 407 end 408 end 409 end 410 411 class Element < Simple 412 def initialize(node, vijson) 413 super(node, vijson) 414 end 415 416 def has_type? 417 !@node["type"].nil? 418 end 419 420 def child 421 cs = @node.element_children 422 assert_equal 1, cs.length 423 assert_equal "complexType", cs.first.name 424 425 t = ComplexType.new(cs.first, @vijson) 426 t.name = self.name 427 t 428 end 429 430 def dump(io) 431 if has_type? 432 ucfirstName = ucfirst(name) 433 if @vijson != nil 434 if @vijson.has_key?(ucfirstName) 435 if @vijson[ucfirstName].has_key?("description") 436 @vijson[ucfirstName]["description"].each_line do |line| 437 io.print "// #{sanitize_line(line)}" 438 end 439 end 440 end 441 end 442 io.print "type %s %s\n\n" % [ucfirstName, var_type] 443 else 444 child.dump(io) 445 end 446 end 447 448 def dump_init(io) 449 if has_type? 450 init_type io, name, name 451 end 452 end 453 454 def dump_field(io, json_tag="", vijson_props=nil) 455 xmlTag = name 456 xmlTag += ",omitempty" if need_omitempty? 457 xmlTag += ",typeattr" if need_typeattr? 458 tag = "%s %s `xml:\"%s\"" % [var_name, var_type, xmlTag] 459 460 jsonTag = "" 461 if json_tag != "" 462 jsonTag = json_tag # Caller-provided JSON tag 463 elsif var_name == "This" && var_type == "ManagedObjectReference" 464 jsonTag = "-" # For marshal/unmarshal operations using a type 465 # discriminator 466 else 467 jsonTag = name 468 jsonTag += ",omitempty" if json_omitempty? 469 end 470 tag += " json:\"%s\"" % [jsonTag] 471 472 # Print the field's comments as well as determining whether or not the field 473 # has a comment with a line that matches the following regex with a 474 # capturing group to parse the API version: 475 # 476 # ***Since:*** vSphere API (.+)$ 477 # 478 # If the comments do contain this line, it will not be printed, instead the 479 # captured version is added to the field's Go tags to persist the minimum 480 # API version for the field. 481 if vijson_props != nil 482 if vijson_props.has_key?(name) 483 if vijson_props[name].has_key?("description") 484 comments = [] 485 vijson_props[name]["description"].each_line do |line| 486 m = line.match(SINCE_API_FORMAT) 487 if m == nil 488 comments.append("// #{sanitize_line(line)}") 489 else 490 tag += " vim:\"%s\"" % [m[1]] 491 comments.pop(1) 492 end 493 end 494 io.print comments.join() 495 end 496 end 497 end 498 499 io.print "%s`\n" % [tag] 500 end 501 502 def peek(type=nil) 503 if has_type? 504 return if self.type =~ /^xsd:/ 505 506 Peek.ref(vim_type) 507 else 508 child.peek() 509 end 510 end 511 end 512 513 class Attribute < Simple 514 def dump_field(io) 515 xmlTag = name 516 xmlTag += ",omitempty" if need_omitempty? 517 xmlTag += ",attr" 518 xmlTag += ",typeattr" if need_typeattr? 519 tag = "%s %s `xml:\"%s\"" % [var_name, var_type, xmlTag] 520 521 jsonTag = name 522 jsonTag += ",omitempty" if json_omitempty? 523 tag += " json:\"%s\"" % [jsonTag] 524 525 io.print "%s`\n" % [tag] 526 end 527 end 528 529 class SimpleType < Simple 530 def is_enum? 531 true 532 end 533 534 def dump(io) 535 ucfirstName = ucfirst(name) 536 posValCmnts = {} 537 if @vijson != nil 538 ucfirstNameEnum = ucfirstName + "_enum" 539 if @vijson.has_key?(ucfirstNameEnum) 540 if @vijson[ucfirstNameEnum].has_key?("description") 541 comments = [] 542 posValCur = nil 543 posValSectionActive = false 544 @vijson[ucfirstNameEnum]["description"].each_line do |line| 545 if line.match?(SINCE_API_FORMAT) 546 comments.pop(1) 547 if posValCur != nil 548 posValCmnts[posValCur].pop(1) 549 end 550 elsif line.start_with?("Possible values:") 551 comments.pop(1) 552 posValSectionActive = true 553 elsif posValSectionActive 554 if line == "" 555 comments.pop(1) 556 posValSectionActive = false 557 else 558 m = line.match(POSSIBLE_VALUE_FORMAT) 559 if m != nil 560 posValCur = m[1] 561 if m[2] == nil 562 posValCmnts[posValCur] = [] 563 elsif !line.match?(SINCE_API_FORMAT) 564 posValCmnts[posValCur] = ["// #{sanitize_line(m[2])}\n"] 565 end 566 else 567 line.sub!(/^\s{2}/, '') 568 if line.match?(SINCE_API_FORMAT) 569 posValCmnts[posValCur].pop(1) 570 else 571 posValCmnts[posValCur].append("// #{sanitize_line(line)}") 572 end 573 end 574 end 575 else 576 comments.append("// #{sanitize_line(line)}") 577 end 578 end 579 io.print comments.join() 580 end 581 end 582 end 583 io.print "type %s string\n\n" % ucfirstName 584 585 enums = @node.xpath(".//xsd:enumeration").map do |n| 586 comments = nil 587 if posValCmnts.has_key?(n["value"]) 588 comments = posValCmnts[n["value"]].join() 589 end 590 EnumValue.new(self, n["value"], comments) 591 end 592 593 io.print "const (\n" 594 enums.each { |e| e.dump(io) } 595 io.print ")\n\n" 596 end 597 598 def dump_init(io) 599 ucfirstName = ucfirst(name) 600 minApiVersion = nil 601 minApiVersionsForValues = {} 602 if @vijson != nil 603 ucfirstNameEnum = ucfirstName + "_enum" 604 if @vijson.has_key?(ucfirstNameEnum) 605 if @vijson[ucfirstNameEnum].has_key?("description") 606 posValCur = nil 607 posValSectionActive = false 608 @vijson[ucfirstNameEnum]["description"].each_line do |line| 609 m = line.match(SINCE_API_FORMAT) 610 if m != nil 611 minApiVersion = m[1] 612 elsif line.start_with?("Possible values:") 613 posValSectionActive = true 614 elsif posValSectionActive 615 if line == "" 616 posValSectionActive = false 617 else 618 m = line.match(POSSIBLE_VALUE_FORMAT) 619 if m != nil 620 posValCur = m[1] 621 if m[2] != nil 622 m = m[2].match(SINCE_API_FORMAT) 623 if m != nil 624 minApiVersionsForValues[posValCur] = m[1] 625 end 626 end 627 else 628 line.sub!(/^\s{2}/, '') 629 m = line.match(SINCE_API_FORMAT) 630 if m != nil 631 minApiVersionsForValues[posValCur] = m[1] 632 end 633 end 634 end 635 end 636 end 637 end 638 end 639 end 640 641 if minApiVersionsForValues.size() == 0 642 minApiVersionsForValues = nil 643 end 644 645 init_type io, name, name, minApiVersion, minApiVersionsForValues 646 end 647 648 def peek 649 Peek.enum(name) 650 end 651 end 652 653 class ComplexType < Simple 654 class SimpleContent < Simple 655 def dump(io) 656 attr = Attribute.new(@node.at_xpath(".//xsd:attribute"), @vijson) 657 attr.dump_field(io) 658 659 # HACK DELUXE(PN) 660 extension = @node.at_xpath(".//xsd:extension") 661 type = extension["base"].split(":", 2)[1] 662 io.print "Value %s `xml:\",chardata\" json:\"value\"`\n" % type 663 io.print "ServerGUID %s `xml:\"serverGuid,attr,omitempty\" json:\"serverGuid,omitempty\"`\n" % type 664 end 665 666 def peek 667 end 668 end 669 670 class ComplexContent < Simple 671 def base 672 extension = @node.at_xpath(".//xsd:extension") 673 assert_not_nil extension 674 675 base = extension["base"] 676 assert_not_nil base 677 678 base 679 end 680 681 def dump(io) 682 Sequence.new(@node, @vijson).dump(io, base) 683 end 684 685 def dump_interface(io, name) 686 Sequence.new(@node, @vijson).dump_interface(io, name) 687 end 688 689 def peek 690 Sequence.new(@node, @vijson).peek(vim_type(base)) 691 end 692 end 693 694 class Sequence < Simple 695 attr_accessor :array_of 696 697 def initialize(node, vijson, array_of=false) 698 super(node, vijson) 699 self.array_of = array_of 700 end 701 702 def sequence 703 sequence = @node.at_xpath(".//xsd:sequence") 704 if sequence != nil 705 sequence.element_children.map do |n| 706 Element.new(n, @vijson) 707 end 708 else 709 nil 710 end 711 end 712 713 def dump(io, base = nil) 714 return unless elements = sequence 715 if base != nil 716 kind = vim_type(base) 717 718 pkg = "" 719 if $target != ns(base) 720 pkg = "types." 721 end 722 io.print "#{pkg}#{kind}\n\n" 723 end 724 725 elements.each do |e| 726 e.dump_field(io, json_tag=self.array_of ? "_value" : "", vijson_props=@vijson_props) 727 end 728 end 729 730 def dump_interface(io, name) 731 method = "Get%s() *%s" % [name, name] 732 io.print "func (b *%s) %s { return b }\n" % [name, method] 733 io.print "type Base%s interface {\n" % name 734 io.print "%s\n" % method 735 io.print "}\n\n" 736 init_type io, "Base#{name}", name 737 end 738 739 def peek(base = nil) 740 return unless elements = sequence 741 name = @node.attr("name") 742 return unless name 743 744 elements.each do |e| 745 e.peek(name) 746 end 747 748 c = Peek.register(name) 749 if base 750 c.parent = base 751 Peek.register(c.parent).children << name 752 end 753 end 754 end 755 756 def klass 757 @klass ||= begin 758 cs = @node.element_children 759 if !cs.empty? 760 assert_equal 1, cs.length 761 762 case cs.first.name 763 when "simpleContent" 764 SimpleContent.new(@node, @vijson) 765 when "complexContent" 766 ComplexContent.new(@node, @vijson) 767 when "sequence" 768 Sequence.new(@node, @vijson, self.name.start_with?("ArrayOf")) 769 else 770 raise "don't know what to do for element: %s..." % cs.first.name 771 end 772 end 773 end 774 end 775 776 def dump_init(io) 777 minApiVersion = nil 778 ucfirstName = ucfirst(name) 779 if @vijson != nil 780 if @vijson.has_key?(ucfirstName) 781 if @vijson[ucfirstName].has_key?("description") 782 @vijson[ucfirstName]["description"].each_line do |line| 783 m = line.match(SINCE_API_FORMAT) 784 if m != nil 785 minApiVersion = m[1] 786 break 787 end 788 end 789 end 790 end 791 end 792 init_type io, name, name, minApiVersion 793 end 794 795 def dump(io) 796 ucfirstName = ucfirst(name) 797 if @vijson != nil 798 if @vijson.has_key?(ucfirstName) 799 if @vijson[ucfirstName].has_key?("description") 800 comments = [] 801 @vijson[ucfirstName]["description"].each_line do |line| 802 if line.match?(SINCE_API_FORMAT) 803 comments.pop(1) 804 else 805 comments.append("// #{sanitize_line(line)}") 806 end 807 end 808 io.print comments.join() 809 end 810 end 811 end 812 io.print "type %s struct {\n" % ucfirstName 813 klass.dump(io) if klass 814 io.print "}\n\n" 815 end 816 817 def peek 818 Peek.register(name).klass = klass 819 klass.peek if klass 820 end 821 end 822 823 class Schema 824 include Test::Unit::Assertions 825 826 attr_accessor :namespace 827 attr_reader :vijson 828 829 def initialize(xml, vijson) 830 @xml = Nokogiri::XML.parse(xml) 831 @vijson = vijson 832 @namespace = @xml.root.attr("targetNamespace").split(":", 2)[1] 833 @xml 834 end 835 836 # We have some assumptions about structure, make sure they hold. 837 def validate_assumptions! 838 # Every enumeration is part of a restriction 839 @xml.xpath(".//xsd:enumeration").each do |n| 840 assert_equal "restriction", n.parent.name 841 end 842 843 # See type == enum 844 @xml.xpath(".//xsd:restriction").each do |n| 845 # Every restriction has type xsd:string (it's an enum) 846 assert_equal "xsd:string", n["base"] 847 848 # Every restriction is part of a simpleType 849 assert_equal "simpleType", n.parent.name 850 851 # Every restriction is alone 852 assert_equal 1, n.parent.element_children.size 853 end 854 855 # See type == complex_content 856 @xml.xpath(".//xsd:complexContent").each do |n| 857 # complexContent is child of complexType 858 assert_equal "complexType", n.parent.name 859 860 end 861 862 # See type == complex_type 863 @xml.xpath(".//xsd:complexType").each do |n| 864 cc = n.element_children 865 866 # OK to have an empty complexType 867 next if cc.size == 0 868 869 # Require 1 element otherwise 870 assert_equal 1, cc.size 871 872 case cc.first.name 873 when "complexContent" 874 # complexContent has 1 "extension" element 875 cc = cc.first.element_children 876 assert_equal 1, cc.size 877 assert_equal "extension", cc.first.name 878 879 # extension has 1 "sequence" element 880 ec = cc.first.element_children 881 assert_equal 1, ec.size 882 assert_equal "sequence", ec.first.name 883 884 # sequence has N "element" elements 885 sc = ec.first.element_children 886 assert sc.all? { |e| e.name == "element" } 887 when "simpleContent" 888 # simpleContent has 1 "extension" element 889 cc = cc.first.element_children 890 assert_equal 1, cc.size 891 assert_equal "extension", cc.first.name 892 893 # extension has 1 or more "attribute" elements 894 ec = cc.first.element_children 895 assert_not_equal 0, ec.size 896 assert_equal "attribute", ec.first.name 897 when "sequence" 898 # sequence has N "element" elements 899 sc = cc.first.element_children 900 assert sc.all? { |e| e.name == "element" } 901 else 902 raise "unknown element: %s" % cc.first.name 903 end 904 end 905 906 imports.each do |i| 907 i.validate_assumptions! 908 end 909 910 includes.each do |i| 911 i.validate_assumptions! 912 end 913 end 914 915 def types 916 return to_enum(:types) unless block_given? 917 918 if $target != self.namespace 919 return 920 end 921 922 imports.each do |i| 923 i.types do |t| 924 yield t 925 end 926 end 927 928 includes.each do |i| 929 i.types do |t| 930 yield t 931 end 932 end 933 934 @xml.root.children.each do |n| 935 case n.class.to_s 936 when "Nokogiri::XML::Text" 937 next 938 when "Nokogiri::XML::Element" 939 case n.name 940 when "include", "import" 941 next 942 when "element" 943 e = Element.new(n, @vijson) 944 if e.has_type? && e.vim_type? 945 if e.ns == $target 946 yield e 947 end 948 else 949 yield e 950 end 951 when "simpleType" 952 yield SimpleType.new(n, @vijson) 953 when "complexType" 954 yield ComplexType.new(n, @vijson) 955 else 956 raise "unknown child: %s" % n.name 957 end 958 else 959 raise "unknown type: %s" % n.class 960 end 961 end 962 end 963 964 def imports 965 @imports ||= @xml.root.xpath(".//xmlns:import").map do |n| 966 Schema.new(WSDL.read(n["schemaLocation"]), @vijson) 967 end 968 end 969 970 def includes 971 @includes ||= @xml.root.xpath(".//xmlns:include").map do |n| 972 Schema.new(WSDL.read(n["schemaLocation"]), @vijson) 973 end 974 end 975 end 976 977 978 class Operation 979 include Test::Unit::Assertions 980 981 def initialize(wsdl, operation_node) 982 @wsdl = wsdl 983 @operation_node = operation_node 984 end 985 986 def name 987 @operation_node["name"] 988 end 989 990 def namespace 991 type = @operation_node.at_xpath("./xmlns:input").attr("message") 992 keep_ns(type) 993 end 994 995 def remove_ns(x) 996 ns, x = x.split(":", 2) 997 if ! valid_ns? ns 998 raise 999 end 1000 x 1001 end 1002 1003 def keep_ns(x) 1004 ns, x = x.split(":", 2) 1005 if ! valid_ns? ns 1006 raise 1007 end 1008 ns 1009 end 1010 1011 def find_type_for(type) 1012 type = remove_ns(type) 1013 1014 message = @wsdl.message(type) 1015 assert_not_nil message 1016 1017 part = message.at_xpath("./xmlns:part") 1018 assert_not_nil message 1019 1020 remove_ns(part["element"]) 1021 end 1022 1023 def input 1024 type = @operation_node.at_xpath("./xmlns:input").attr("message") 1025 find_type_for(type) 1026 end 1027 1028 def go_input 1029 "types." + ucfirst(input) 1030 end 1031 1032 def output 1033 type = @operation_node.at_xpath("./xmlns:output").attr("message") 1034 find_type_for(type) 1035 end 1036 1037 def go_output 1038 "types." + ucfirst(output) 1039 end 1040 1041 def dump(io) 1042 func = ucfirst(name) 1043 if namespace != "vim25" 1044 tag = "urn:#{namespace} " 1045 end 1046 io.print <<EOS 1047 type #{func}Body struct{ 1048 Req *#{go_input} `xml:"urn:#{namespace} #{input},omitempty"` 1049 Res *#{go_output} `xml:"#{tag}#{output},omitempty"` 1050 Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"` 1051 } 1052 1053 func (b *#{func}Body) Fault() *soap.Fault { return b.Fault_ } 1054 1055 EOS 1056 1057 io.print "func %s(ctx context.Context, r soap.RoundTripper, req *%s) (*%s, error) {\n" % [func, go_input, go_output] 1058 io.print <<EOS 1059 var reqBody, resBody #{func}Body 1060 1061 reqBody.Req = req 1062 1063 if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil { 1064 return nil, err 1065 } 1066 1067 return resBody.Res, nil 1068 EOS 1069 1070 io.print "}\n\n" 1071 end 1072 end 1073 1074 class WSDL 1075 attr_reader :xml 1076 attr_reader :vijson 1077 1078 PATH = File.expand_path("../sdk", __FILE__) 1079 1080 def self.read(file) 1081 File.open(File.join(PATH, file)) 1082 end 1083 1084 def initialize(xml, vijson) 1085 @xml = Nokogiri::XML.parse(xml) 1086 @vijson = vijson 1087 $target = @xml.root["targetNamespace"].split(":", 2)[1] 1088 1089 unless $namespaces.include? $target 1090 $namespaces.push $target 1091 end 1092 end 1093 1094 def validate_assumptions! 1095 schemas.each do |s| 1096 s.validate_assumptions! 1097 end 1098 end 1099 1100 def types(&blk) 1101 return to_enum(:types) unless block_given? 1102 1103 schemas.each do |s| 1104 s.types(&blk) 1105 end 1106 end 1107 1108 def schemas 1109 @schemas ||= @xml.xpath('.//xmlns:types/xsd:schema').map do |n| 1110 Schema.new(n.to_xml, @vijson) 1111 end 1112 end 1113 1114 def operations 1115 @operations ||= @xml.xpath('.//xmlns:portType/xmlns:operation').map do |o| 1116 Operation.new(self, o) 1117 end 1118 end 1119 1120 def message(type) 1121 @messages ||= begin 1122 h = {} 1123 @xml.xpath('.//xmlns:message').each do |n| 1124 h[n.attr("name")] = n 1125 end 1126 h 1127 end 1128 1129 @messages[type] 1130 end 1131 1132 def peek 1133 types. 1134 sort_by { |x| x.name }. 1135 uniq { |x| x.name }. 1136 each { |e| e.peek() } 1137 end 1138 1139 def self.header(name) 1140 return <<EOF 1141 /* 1142 Copyright (c) #{ENV['COPYRIGHT_DATE_RANGE'] || '2014-2018'} VMware, Inc. All Rights Reserved. 1143 1144 Licensed under the Apache License, Version 2.0 (the "License"); 1145 you may not use this file except in compliance with the License. 1146 You may obtain a copy of the License at 1147 1148 http://www.apache.org/licenses/LICENSE-2.0 1149 1150 Unless required by applicable law or agreed to in writing, software 1151 distributed under the License is distributed on an "AS IS" BASIS, 1152 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1153 See the License for the specific language governing permissions and 1154 limitations under the License. 1155 */ 1156 1157 package #{name} 1158 1159 EOF 1160 end 1161 end