@shortlink-org/portolan 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (381) hide show
  1. package/README.md +69 -205
  2. package/cli/init.mjs +285 -0
  3. package/cli/init.test.mjs +158 -0
  4. package/cli/portolan.mjs +37 -73
  5. package/index.html +19 -0
  6. package/package.json +5 -10
  7. package/plugins/README.md +66 -27
  8. package/plugins/portolan-go.wasm +0 -0
  9. package/portolan.json +52 -73
  10. package/public/landing/cats/bug-hunter.webp +0 -0
  11. package/public/landing/cats/compass-nap.webp +0 -0
  12. package/public/landing/cats/diagrammer.webp +0 -0
  13. package/public/landing/cats/docs-reader.webp +0 -0
  14. package/public/landing/cats/laptop.webp +0 -0
  15. package/public/landing/cats/map-inspector.webp +0 -0
  16. package/public/landing/cats/server-break.webp +0 -0
  17. package/public/landing/cats/star-mapper.webp +0 -0
  18. package/public/landing/cats/system-builder.webp +0 -0
  19. package/public/landing/cats/thread-tangle.webp +0 -0
  20. package/public/og.png +0 -0
  21. package/public/readme-header.webp +0 -0
  22. package/schema/portolan.schema.json +180 -0
  23. package/scripts/builtin-plugins.mjs +14 -0
  24. package/scripts/delivery-presets.mjs +353 -0
  25. package/scripts/gen.mjs +37 -4
  26. package/scripts/history.mjs +126 -0
  27. package/scripts/history.test.mjs +110 -0
  28. package/scripts/host-plugins/fetch-bsr.mjs +351 -0
  29. package/{plugins/fetch-bsr/options.schema.json → scripts/host-plugins/fetch-bsr.options.json} +4 -0
  30. package/scripts/host-plugins/fetch-bsr.test.mjs +212 -0
  31. package/scripts/host-plugins/fetch-csr.mjs +386 -0
  32. package/scripts/host-plugins/fetch-csr.test.mjs +178 -0
  33. package/scripts/host-plugins/fetch-git.mjs +379 -0
  34. package/scripts/host-plugins/fetch-git.test.mjs +210 -0
  35. package/scripts/local-api.mjs +22 -4
  36. package/scripts/local-api.test.mjs +84 -2
  37. package/scripts/manifest.mjs +3 -2
  38. package/scripts/package-smoke.mjs +6 -2
  39. package/scripts/plugin-host.mjs +77 -18
  40. package/scripts/plugin-host.test.mjs +50 -1
  41. package/scripts/plugin-wasm-worker.mjs +6 -3
  42. package/scripts/run-builtin.mjs +6 -26
  43. package/scripts/schema.mjs +6 -1
  44. package/src/app/App.tsx +24 -429
  45. package/src/app/Breadcrumbs.test.ts +7 -0
  46. package/src/app/Breadcrumbs.tsx +15 -2
  47. package/src/app/CatalogApp.tsx +436 -0
  48. package/src/app/Sidebar.tsx +0 -1
  49. package/src/chat/ChatPanel.tsx +98 -41
  50. package/src/chat/Composer.tsx +4 -2
  51. package/src/chat/Conversation.tsx +79 -15
  52. package/src/chat/Header.tsx +43 -9
  53. package/src/chat/Starter.tsx +64 -3
  54. package/src/chat/Steps.tsx +1 -1
  55. package/src/chat/flags.test.ts +51 -14
  56. package/src/chat/flags.ts +8 -2
  57. package/src/chat/page-context.test.ts +51 -0
  58. package/src/chat/page-context.ts +128 -0
  59. package/src/chat/prompt.test.ts +49 -1
  60. package/src/chat/prompt.ts +42 -1
  61. package/src/chat/tools.ts +7 -2
  62. package/src/chat/transport.ts +16 -4
  63. package/src/components/ProblemRow.tsx +182 -0
  64. package/src/data.ts +20 -5
  65. package/src/er/ErCanvas.tsx +1 -1
  66. package/src/graph/DependencyGraph.tsx +10 -1
  67. package/src/graph/FocusedEventGraph.tsx +1 -1
  68. package/src/graph/GraphToolbar.tsx +2 -4
  69. package/src/index.css +220 -2
  70. package/src/landing/DraggableReveal.tsx +168 -0
  71. package/src/landing/EstateGraph.tsx +28 -0
  72. package/src/landing/FlowPlayback.tsx +282 -0
  73. package/src/landing/HeroMap.tsx +128 -0
  74. package/src/landing/LandingPage.tsx +757 -0
  75. package/src/landing/ProductFrame.tsx +68 -0
  76. package/src/landing/ProductTour.tsx +371 -0
  77. package/src/landing/catalog.ts +25 -0
  78. package/src/landing/motion.tsx +52 -0
  79. package/src/lib/all-problems.ts +28 -0
  80. package/src/lib/local-api.ts +35 -1
  81. package/src/lib/setup-info.test.ts +8 -0
  82. package/src/lib/setup-info.ts +3 -2
  83. package/src/lib/source-code.ts +2 -1
  84. package/src/likec4/C4View.tsx +5 -0
  85. package/src/likec4/InteractiveView.tsx +5 -1
  86. package/src/map/ContextMapGraph.tsx +47 -20
  87. package/src/pages/ContextMap.tsx +5 -1
  88. package/src/pages/FlowDetail.tsx +24 -7
  89. package/src/pages/Overview.tsx +68 -12
  90. package/src/pages/Problems.tsx +6 -193
  91. package/src/pages/Settings.tsx +133 -108
  92. package/src/pages/settings/DeliverySettings.tsx +229 -0
  93. package/src/pages/settings/PreferencesSettings.tsx +101 -0
  94. package/src/routes.test.ts +20 -0
  95. package/src/routes.ts +13 -1
  96. package/vite.config.ts +12 -8
  97. package/catalog/enum_test.go +0 -46
  98. package/catalog/model.go +0 -932
  99. package/catalog/roundtrip_test.go +0 -185
  100. package/catalog/via_test.go +0 -38
  101. package/go.mod +0 -14
  102. package/go.sum +0 -14
  103. package/internal/commands/cargo.go +0 -154
  104. package/internal/commands/commands.go +0 -143
  105. package/internal/commands/commands_test.go +0 -323
  106. package/internal/commands/gradle.go +0 -77
  107. package/internal/commands/justfile.go +0 -82
  108. package/internal/commands/makefile.go +0 -106
  109. package/internal/commands/maven.go +0 -93
  110. package/internal/commands/packagejson.go +0 -159
  111. package/internal/commands/pyproject.go +0 -169
  112. package/internal/commands/taskfile.go +0 -101
  113. package/internal/commands/testdata/estate/.cargo/config.toml +0 -7
  114. package/internal/commands/testdata/estate/Makefile +0 -33
  115. package/internal/commands/testdata/estate/Taskfile.yml +0 -21
  116. package/internal/commands/testdata/estate/build.gradle.kts +0 -21
  117. package/internal/commands/testdata/estate/justfile +0 -20
  118. package/internal/commands/testdata/estate/package.json +0 -13
  119. package/internal/commands/testdata/estate/pom.xml +0 -29
  120. package/internal/commands/testdata/estate/pyproject.toml +0 -27
  121. package/internal/commands/testdata/estate/xtask/src/main.rs +0 -27
  122. package/internal/commands/testdata/golden/commands.json +0 -305
  123. package/internal/gohttp/analyze.go +0 -2500
  124. package/internal/gohttp/endpoints.go +0 -1067
  125. package/internal/gohttp/roots.go +0 -320
  126. package/internal/gohttp/typed.go +0 -143
  127. package/internal/goscan/constants.go +0 -85
  128. package/internal/goscan/goscan_test.go +0 -227
  129. package/internal/goscan/names.go +0 -52
  130. package/internal/goscan/parse_test.go +0 -11
  131. package/internal/goscan/source.go +0 -36
  132. package/internal/goscan/tree.go +0 -155
  133. package/internal/goscan/types.go +0 -99
  134. package/internal/wsdl/ids.go +0 -127
  135. package/internal/wsdl/ids_test.go +0 -21
  136. package/internal/wsdl/model.go +0 -62
  137. package/internal/wsdl/parse.go +0 -920
  138. package/internal/wsdl/parse_test.go +0 -133
  139. package/plugin/describe.go +0 -107
  140. package/plugin/describe_test.go +0 -114
  141. package/plugin/protocol.go +0 -117
  142. package/plugin/schematest/schematest.go +0 -126
  143. package/plugins/extract-adr/describe.go +0 -19
  144. package/plugins/extract-adr/describe_test.go +0 -11
  145. package/plugins/extract-adr/extract.go +0 -153
  146. package/plugins/extract-adr/extract_test.go +0 -350
  147. package/plugins/extract-adr/history.go +0 -99
  148. package/plugins/extract-adr/main.go +0 -65
  149. package/plugins/extract-adr/parse.go +0 -642
  150. package/plugins/extract-adr/parse_test.go +0 -405
  151. package/plugins/extract-asyncapi/describe.go +0 -19
  152. package/plugins/extract-asyncapi/describe_test.go +0 -11
  153. package/plugins/extract-asyncapi/extract.go +0 -315
  154. package/plugins/extract-asyncapi/extract_test.go +0 -197
  155. package/plugins/extract-asyncapi/main.go +0 -48
  156. package/plugins/extract-asyncapi/spec.go +0 -151
  157. package/plugins/extract-commands/describe.go +0 -19
  158. package/plugins/extract-commands/describe_test.go +0 -11
  159. package/plugins/extract-commands/extract.go +0 -79
  160. package/plugins/extract-commands/extract_test.go +0 -92
  161. package/plugins/extract-commands/main.go +0 -55
  162. package/plugins/extract-csr/avro.go +0 -258
  163. package/plugins/extract-csr/describe.go +0 -19
  164. package/plugins/extract-csr/describe_test.go +0 -11
  165. package/plugins/extract-csr/extract.go +0 -338
  166. package/plugins/extract-csr/extract_test.go +0 -374
  167. package/plugins/extract-csr/jsonschema.go +0 -343
  168. package/plugins/extract-csr/lock.go +0 -25
  169. package/plugins/extract-csr/main.go +0 -84
  170. package/plugins/extract-csr/subject.go +0 -102
  171. package/plugins/extract-flows/describe.go +0 -19
  172. package/plugins/extract-flows/describe_test.go +0 -11
  173. package/plugins/extract-flows/extract.go +0 -91
  174. package/plugins/extract-flows/main.go +0 -45
  175. package/plugins/extract-flows/parse.go +0 -593
  176. package/plugins/extract-flows/parse_test.go +0 -204
  177. package/plugins/extract-glossary/describe.go +0 -19
  178. package/plugins/extract-glossary/describe_test.go +0 -11
  179. package/plugins/extract-glossary/extract.go +0 -115
  180. package/plugins/extract-glossary/extract_test.go +0 -220
  181. package/plugins/extract-glossary/main.go +0 -59
  182. package/plugins/extract-glossary/parse.go +0 -214
  183. package/plugins/extract-glossary/parse_test.go +0 -203
  184. package/plugins/extract-go/aggregate.go +0 -214
  185. package/plugins/extract-go/client.go +0 -409
  186. package/plugins/extract-go/client_test.go +0 -266
  187. package/plugins/extract-go/describe.go +0 -19
  188. package/plugins/extract-go/describe_test.go +0 -11
  189. package/plugins/extract-go/enum.go +0 -195
  190. package/plugins/extract-go/enum_test.go +0 -82
  191. package/plugins/extract-go/event.go +0 -99
  192. package/plugins/extract-go/extract.go +0 -191
  193. package/plugins/extract-go/extract_test.go +0 -261
  194. package/plugins/extract-go/flow.go +0 -1441
  195. package/plugins/extract-go/flow_test.go +0 -609
  196. package/plugins/extract-go/httpclient.go +0 -174
  197. package/plugins/extract-go/httpclient_test.go +0 -296
  198. package/plugins/extract-go/ids.go +0 -92
  199. package/plugins/extract-go/layout.go +0 -225
  200. package/plugins/extract-go/layout_test.go +0 -125
  201. package/plugins/extract-go/lifecycle.go +0 -324
  202. package/plugins/extract-go/lifecycle_test.go +0 -108
  203. package/plugins/extract-go/main.go +0 -79
  204. package/plugins/extract-go/operation.go +0 -157
  205. package/plugins/extract-go/source.go +0 -316
  206. package/plugins/extract-go/transport.go +0 -321
  207. package/plugins/extract-go/transport_test.go +0 -145
  208. package/plugins/extract-go/wiring.go +0 -434
  209. package/plugins/extract-go-nats/describe.go +0 -19
  210. package/plugins/extract-go-nats/describe_test.go +0 -11
  211. package/plugins/extract-go-nats/extract.go +0 -177
  212. package/plugins/extract-go-nats/extract_test.go +0 -389
  213. package/plugins/extract-go-nats/index.go +0 -379
  214. package/plugins/extract-go-nats/main.go +0 -42
  215. package/plugins/extract-go-nats/resolve.go +0 -161
  216. package/plugins/extract-go-nats/sites.go +0 -224
  217. package/plugins/extract-graphql/describe.go +0 -19
  218. package/plugins/extract-graphql/describe_test.go +0 -11
  219. package/plugins/extract-graphql/extract.go +0 -433
  220. package/plugins/extract-graphql/extract_test.go +0 -256
  221. package/plugins/extract-graphql/ids.go +0 -49
  222. package/plugins/extract-graphql/lex.go +0 -237
  223. package/plugins/extract-graphql/main.go +0 -51
  224. package/plugins/extract-graphql/parse.go +0 -621
  225. package/plugins/extract-graphql/parse_test.go +0 -122
  226. package/plugins/extract-http-clients/describe.go +0 -19
  227. package/plugins/extract-http-clients/describe_test.go +0 -11
  228. package/plugins/extract-http-clients/extract.go +0 -705
  229. package/plugins/extract-http-clients/extract_test.go +0 -1263
  230. package/plugins/extract-http-clients/main.go +0 -42
  231. package/plugins/extract-openapi/describe.go +0 -19
  232. package/plugins/extract-openapi/describe_test.go +0 -11
  233. package/plugins/extract-openapi/discover.go +0 -243
  234. package/plugins/extract-openapi/extract.go +0 -526
  235. package/plugins/extract-openapi/extract_test.go +0 -545
  236. package/plugins/extract-openapi/main.go +0 -75
  237. package/plugins/extract-openapi/spec.go +0 -350
  238. package/plugins/extract-project/describe.go +0 -19
  239. package/plugins/extract-project/describe_test.go +0 -11
  240. package/plugins/extract-project/extract.go +0 -221
  241. package/plugins/extract-project/extract_test.go +0 -109
  242. package/plugins/extract-project/main.go +0 -41
  243. package/plugins/extract-proto/ast.go +0 -125
  244. package/plugins/extract-proto/consumes.go +0 -77
  245. package/plugins/extract-proto/describe.go +0 -19
  246. package/plugins/extract-proto/extract.go +0 -293
  247. package/plugins/extract-proto/extract_test.go +0 -459
  248. package/plugins/extract-proto/ids.go +0 -89
  249. package/plugins/extract-proto/ids_test.go +0 -57
  250. package/plugins/extract-proto/lex.go +0 -285
  251. package/plugins/extract-proto/main.go +0 -100
  252. package/plugins/extract-proto/module.go +0 -120
  253. package/plugins/extract-proto/parse.go +0 -720
  254. package/plugins/extract-proto/parse_test.go +0 -307
  255. package/plugins/extract-proto/provides.go +0 -236
  256. package/plugins/extract-proto/resolve.go +0 -222
  257. package/plugins/extract-proto/resolve_test.go +0 -119
  258. package/plugins/extract-redis/describe.go +0 -19
  259. package/plugins/extract-redis/describe_test.go +0 -11
  260. package/plugins/extract-redis/extract.go +0 -183
  261. package/plugins/extract-redis/extract_test.go +0 -168
  262. package/plugins/extract-redis/keyspaces.go +0 -469
  263. package/plugins/extract-redis/main.go +0 -44
  264. package/plugins/extract-river/describe.go +0 -19
  265. package/plugins/extract-river/describe_test.go +0 -11
  266. package/plugins/extract-river/extract.go +0 -507
  267. package/plugins/extract-river/extract_test.go +0 -132
  268. package/plugins/extract-river/main.go +0 -42
  269. package/plugins/extract-sql/ddl.go +0 -893
  270. package/plugins/extract-sql/ddl_test.go +0 -401
  271. package/plugins/extract-sql/describe.go +0 -19
  272. package/plugins/extract-sql/describe_test.go +0 -11
  273. package/plugins/extract-sql/layout.go +0 -221
  274. package/plugins/extract-sql/layout_test.go +0 -55
  275. package/plugins/extract-sql/lineage.go +0 -107
  276. package/plugins/extract-sql/main.go +0 -141
  277. package/plugins/extract-sql/maps.go +0 -564
  278. package/plugins/extract-sql/maps_java.go +0 -117
  279. package/plugins/extract-sql/maps_rust.go +0 -333
  280. package/plugins/extract-sql/maps_rust_test.go +0 -70
  281. package/plugins/extract-sql/maps_test.go +0 -204
  282. package/plugins/extract-sql/maps_ts.go +0 -398
  283. package/plugins/extract-sql/maps_ts_test.go +0 -136
  284. package/plugins/extract-sql/projection.go +0 -70
  285. package/plugins/extract-sql/projection_test.go +0 -50
  286. package/plugins/extract-sql/store.go +0 -420
  287. package/plugins/extract-sql/store_test.go +0 -233
  288. package/plugins/extract-sql/view.go +0 -295
  289. package/plugins/extract-watermill/describe.go +0 -19
  290. package/plugins/extract-watermill/describe_test.go +0 -11
  291. package/plugins/extract-watermill/extract.go +0 -1228
  292. package/plugins/extract-watermill/extract_test.go +0 -234
  293. package/plugins/extract-watermill/main.go +0 -41
  294. package/plugins/extract-wsdl/describe.go +0 -19
  295. package/plugins/extract-wsdl/describe_test.go +0 -11
  296. package/plugins/extract-wsdl/extract.go +0 -148
  297. package/plugins/extract-wsdl/extract_test.go +0 -45
  298. package/plugins/extract-wsdl/main.go +0 -53
  299. package/plugins/fetch-bsr/auth.go +0 -114
  300. package/plugins/fetch-bsr/bsr.go +0 -240
  301. package/plugins/fetch-bsr/cache.go +0 -66
  302. package/plugins/fetch-bsr/describe.go +0 -19
  303. package/plugins/fetch-bsr/fetch.go +0 -192
  304. package/plugins/fetch-bsr/fetch_test.go +0 -465
  305. package/plugins/fetch-bsr/lock.go +0 -71
  306. package/plugins/fetch-bsr/main.go +0 -117
  307. package/plugins/fetch-csr/auth.go +0 -95
  308. package/plugins/fetch-csr/cache.go +0 -69
  309. package/plugins/fetch-csr/describe.go +0 -19
  310. package/plugins/fetch-csr/describe_test.go +0 -11
  311. package/plugins/fetch-csr/fetch.go +0 -254
  312. package/plugins/fetch-csr/fetch_test.go +0 -494
  313. package/plugins/fetch-csr/lock.go +0 -95
  314. package/plugins/fetch-csr/main.go +0 -122
  315. package/plugins/fetch-csr/registry.go +0 -209
  316. package/plugins/fetch-git/cache.go +0 -65
  317. package/plugins/fetch-git/describe.go +0 -19
  318. package/plugins/fetch-git/describe_test.go +0 -11
  319. package/plugins/fetch-git/fetch.go +0 -181
  320. package/plugins/fetch-git/fetch_test.go +0 -332
  321. package/plugins/fetch-git/git.go +0 -169
  322. package/plugins/fetch-git/lock.go +0 -72
  323. package/plugins/fetch-git/main.go +0 -127
  324. package/plugins/fetch-git/offline.go +0 -39
  325. package/plugins/fetch-git/pin.go +0 -90
  326. package/plugins/fetch-git/pin_test.go +0 -74
  327. package/plugins/gen-backstage/describe.go +0 -17
  328. package/plugins/gen-backstage/main.go +0 -22
  329. package/plugins/gen-backstage/plugin.go +0 -473
  330. package/plugins/gen-backstage/plugin_test.go +0 -145
  331. package/plugins/gen-backstage.wasm +0 -0
  332. package/plugins/gen-markdown/adr.go +0 -162
  333. package/plugins/gen-markdown/aggregate.go +0 -362
  334. package/plugins/gen-markdown/canonical.go +0 -93
  335. package/plugins/gen-markdown/context.go +0 -90
  336. package/plugins/gen-markdown/coverage_test.go +0 -89
  337. package/plugins/gen-markdown/describe.go +0 -19
  338. package/plugins/gen-markdown/describe_test.go +0 -11
  339. package/plugins/gen-markdown/external.go +0 -71
  340. package/plugins/gen-markdown/flow.go +0 -277
  341. package/plugins/gen-markdown/glossary.go +0 -76
  342. package/plugins/gen-markdown/glossary_test.go +0 -148
  343. package/plugins/gen-markdown/llms.go +0 -302
  344. package/plugins/gen-markdown/main.go +0 -22
  345. package/plugins/gen-markdown/markdown.go +0 -254
  346. package/plugins/gen-markdown/markdown_test.go +0 -100
  347. package/plugins/gen-markdown/module.go +0 -107
  348. package/plugins/gen-markdown/plugin.go +0 -39
  349. package/plugins/gen-markdown/quality_test.go +0 -176
  350. package/plugins/gen-markdown/redis_test.go +0 -38
  351. package/plugins/gen-markdown/render.go +0 -344
  352. package/plugins/gen-markdown/render_test.go +0 -186
  353. package/plugins/gen-markdown/service.go +0 -456
  354. package/plugins/gen-markdown/source.go +0 -177
  355. package/plugins/gen-markdown/store.go +0 -201
  356. package/plugins/gen-markdown.wasm +0 -0
  357. package/plugins/gen-mermaid/describe.go +0 -17
  358. package/plugins/gen-mermaid/main.go +0 -22
  359. package/plugins/gen-mermaid/plugin.go +0 -104
  360. package/plugins/gen-mermaid/plugin_test.go +0 -33
  361. package/plugins/gen-mermaid.wasm +0 -0
  362. package/plugins/openapi/ids.go +0 -261
  363. package/plugins/openapi/ids_test.go +0 -98
  364. package/plugins/verify-codeowners/describe.go +0 -19
  365. package/plugins/verify-codeowners/describe_test.go +0 -11
  366. package/plugins/verify-codeowners/main.go +0 -75
  367. package/plugins/verify-codeowners/match.go +0 -85
  368. package/plugins/verify-codeowners/match_test.go +0 -47
  369. package/plugins/verify-codeowners/owners.go +0 -164
  370. package/plugins/verify-codeowners/owners_test.go +0 -225
  371. package/plugins/verify-codeowners/parse.go +0 -90
  372. package/plugins/verify-codeowners/parse_test.go +0 -62
  373. package/plugins/verify-otel/describe.go +0 -19
  374. package/plugins/verify-otel/describe_test.go +0 -11
  375. package/plugins/verify-otel/main.go +0 -53
  376. package/plugins/verify-otel/match.go +0 -336
  377. package/plugins/verify-otel/otlp.go +0 -200
  378. package/plugins/verify-otel/verify.go +0 -734
  379. package/plugins/verify-otel/verify_test.go +0 -460
  380. /package/{plugins/fetch-csr/options.schema.json → scripts/host-plugins/fetch-csr.options.json} +0 -0
  381. /package/{plugins/fetch-git/options.schema.json → scripts/host-plugins/fetch-git.options.json} +0 -0
@@ -1,920 +0,0 @@
1
- package wsdl
2
-
3
- import (
4
- "encoding/xml"
5
- "fmt"
6
- "io"
7
- "io/fs"
8
- "net/url"
9
- "os"
10
- "path/filepath"
11
- "sort"
12
- "strings"
13
- )
14
-
15
- const (
16
- soap11WSDL = "http://schemas.xmlsoap.org/wsdl/soap/"
17
- soap12WSDL = "http://schemas.xmlsoap.org/wsdl/soap12/"
18
- xsdNS = "http://www.w3.org/2001/XMLSchema"
19
- )
20
-
21
- type qname struct {
22
- ns string
23
- local string
24
- }
25
-
26
- type node struct {
27
- name xml.Name
28
- attrs []xml.Attr
29
- ns map[string]string
30
- text string
31
- children []*node
32
- }
33
-
34
- func (n *node) attr(name string) string {
35
- for _, attr := range n.attrs {
36
- if attr.Name.Local == name && attr.Name.Space != "xmlns" {
37
- return strings.TrimSpace(attr.Value)
38
- }
39
- }
40
- return ""
41
- }
42
-
43
- func (n *node) child(name string) *node {
44
- for _, child := range n.children {
45
- if child.name.Local == name {
46
- return child
47
- }
48
- }
49
- return nil
50
- }
51
-
52
- func (n *node) childrenNamed(name string) []*node {
53
- var out []*node
54
- for _, child := range n.children {
55
- if child.name.Local == name {
56
- out = append(out, child)
57
- }
58
- }
59
- return out
60
- }
61
-
62
- func (n *node) documentation() string {
63
- doc := n.child("documentation")
64
- if doc == nil {
65
- annotation := n.child("annotation")
66
- if annotation != nil {
67
- doc = annotation.child("documentation")
68
- }
69
- }
70
- if doc == nil {
71
- return ""
72
- }
73
- return strings.Join(strings.Fields(doc.deepText()), " ")
74
- }
75
-
76
- func (n *node) deepText() string {
77
- parts := []string{n.text}
78
- for _, child := range n.children {
79
- parts = append(parts, child.deepText())
80
- }
81
- return strings.Join(parts, " ")
82
- }
83
-
84
- func parseXML(reader io.Reader) (*node, error) {
85
- decoder := xml.NewDecoder(reader)
86
- rootNS := map[string]string{"xml": "http://www.w3.org/XML/1998/namespace"}
87
- var root *node
88
- var stack []*node
89
- for {
90
- token, err := decoder.Token()
91
- if err == io.EOF {
92
- break
93
- }
94
- if err != nil {
95
- return nil, err
96
- }
97
- switch value := token.(type) {
98
- case xml.StartElement:
99
- inScope := rootNS
100
- if len(stack) > 0 {
101
- inScope = stack[len(stack)-1].ns
102
- }
103
- namespaces := make(map[string]string, len(inScope)+2)
104
- for prefix, namespace := range inScope {
105
- namespaces[prefix] = namespace
106
- }
107
- for _, attr := range value.Attr {
108
- switch {
109
- case attr.Name.Space == "xmlns":
110
- namespaces[attr.Name.Local] = attr.Value
111
- case attr.Name.Space == "" && attr.Name.Local == "xmlns":
112
- namespaces[""] = attr.Value
113
- }
114
- }
115
- current := &node{name: value.Name, attrs: value.Attr, ns: namespaces}
116
- if len(stack) == 0 {
117
- root = current
118
- } else {
119
- parent := stack[len(stack)-1]
120
- parent.children = append(parent.children, current)
121
- }
122
- stack = append(stack, current)
123
- case xml.CharData:
124
- if len(stack) > 0 {
125
- stack[len(stack)-1].text += string(value)
126
- }
127
- case xml.EndElement:
128
- if len(stack) > 0 {
129
- stack = stack[:len(stack)-1]
130
- }
131
- }
132
- }
133
- if root == nil {
134
- return nil, fmt.Errorf("empty XML document")
135
- }
136
- return root, nil
137
- }
138
-
139
- type document struct {
140
- path string
141
- root *node
142
- }
143
-
144
- type loader struct {
145
- root string
146
- docs map[string]*document
147
- loaded map[string]bool
148
- adopted map[string]map[string]bool
149
- warnings []string
150
- }
151
-
152
- // Read loads one WSDL document and every local WSDL/XSD import or include it
153
- // reaches. spec is relative to root.
154
- func Read(root, spec string) (Result, error) {
155
- absRoot, err := filepath.Abs(root)
156
- if err != nil {
157
- return Result{}, err
158
- }
159
- l := newLoader(absRoot)
160
- path, err := l.localPath(absRoot, spec)
161
- if err != nil {
162
- return Result{}, err
163
- }
164
- if err := l.load(path, ""); err != nil {
165
- return Result{}, err
166
- }
167
- result := l.result([]string{path}, true)
168
- return result, nil
169
- }
170
-
171
- // Discover reads every WSDL in root. Imported interface-only documents are
172
- // used to complete their parent and are not emitted a second time.
173
- func Discover(root string) (Result, error) {
174
- absRoot, err := filepath.Abs(root)
175
- if err != nil {
176
- return Result{}, err
177
- }
178
- var candidates []string
179
- err = filepath.WalkDir(absRoot, func(path string, entry fs.DirEntry, walkErr error) error {
180
- if walkErr != nil {
181
- return walkErr
182
- }
183
- if entry.IsDir() {
184
- if path != absRoot && (strings.HasPrefix(entry.Name(), ".") || entry.Name() == "node_modules") {
185
- return filepath.SkipDir
186
- }
187
- return nil
188
- }
189
- if strings.EqualFold(filepath.Ext(entry.Name()), ".wsdl") {
190
- candidates = append(candidates, path)
191
- }
192
- return nil
193
- })
194
- if err != nil {
195
- return Result{}, err
196
- }
197
- sort.Strings(candidates)
198
- var roots []string
199
- for _, path := range candidates {
200
- file, openErr := os.Open(path)
201
- if openErr != nil {
202
- return Result{}, openErr
203
- }
204
- node, parseErr := parseXML(file)
205
- _ = file.Close()
206
- if parseErr != nil {
207
- return Result{}, fmt.Errorf("%s: %w", path, parseErr)
208
- }
209
- if node.name.Local == "definitions" && len(node.childrenNamed("service")) > 0 {
210
- roots = append(roots, path)
211
- }
212
- }
213
- standalone := false
214
- if len(roots) == 0 {
215
- // Some repositories keep only the reusable portType document. It is
216
- // still a useful contract when no service document exists to own it.
217
- roots = candidates
218
- standalone = true
219
- }
220
- combined := Result{}
221
- for _, path := range roots {
222
- l := newLoader(absRoot)
223
- if err := l.load(path, ""); err != nil {
224
- return Result{}, err
225
- }
226
- part := l.result([]string{path}, standalone)
227
- combined.Contracts = append(combined.Contracts, part.Contracts...)
228
- combined.Warnings = append(combined.Warnings, part.Warnings...)
229
- }
230
- combined.Contracts = uniqueContracts(combined.Contracts)
231
- combined.Warnings = unique(combined.Warnings)
232
- sort.Slice(combined.Contracts, func(i, j int) bool {
233
- if combined.Contracts[i].Name != combined.Contracts[j].Name {
234
- return combined.Contracts[i].Name < combined.Contracts[j].Name
235
- }
236
- return combined.Contracts[i].Source < combined.Contracts[j].Source
237
- })
238
- sort.Strings(combined.Warnings)
239
- return combined, nil
240
- }
241
-
242
- func newLoader(root string) *loader {
243
- return &loader{
244
- root: root, docs: map[string]*document{}, loaded: map[string]bool{},
245
- adopted: map[string]map[string]bool{},
246
- }
247
- }
248
-
249
- func (l *loader) localPath(from, location string) (string, error) {
250
- if strings.TrimSpace(location) == "" {
251
- return "", fmt.Errorf("empty WSDL path")
252
- }
253
- if parsed, err := url.Parse(location); err == nil && parsed.Scheme != "" {
254
- return "", fmt.Errorf("remote import %q is not read", location)
255
- }
256
- path := location
257
- if !filepath.IsAbs(path) {
258
- path = filepath.Join(from, filepath.FromSlash(path))
259
- }
260
- path, err := filepath.Abs(filepath.Clean(path))
261
- if err != nil {
262
- return "", err
263
- }
264
- rel, err := filepath.Rel(l.root, path)
265
- if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
266
- return "", fmt.Errorf("import %q leaves the input root", location)
267
- }
268
- return path, nil
269
- }
270
-
271
- func (l *loader) load(path, adoptedNS string) error {
272
- key := path + "\x00" + adoptedNS
273
- if l.loaded[key] {
274
- return nil
275
- }
276
- l.loaded[key] = true
277
- doc := l.docs[path]
278
- if doc == nil {
279
- file, err := os.Open(path)
280
- if err != nil {
281
- return err
282
- }
283
- root, parseErr := parseXML(file)
284
- closeErr := file.Close()
285
- if parseErr != nil {
286
- return fmt.Errorf("%s: %w", path, parseErr)
287
- }
288
- if closeErr != nil {
289
- return closeErr
290
- }
291
- doc = &document{path: path, root: root}
292
- l.docs[path] = doc
293
- }
294
- if doc.root.name.Local == "schema" && doc.root.attr("targetNamespace") == "" && adoptedNS != "" {
295
- if l.adopted[path] == nil {
296
- l.adopted[path] = map[string]bool{}
297
- }
298
- l.adopted[path][adoptedNS] = true
299
- }
300
-
301
- for _, ref := range importReferences(doc.root, adoptedNS) {
302
- imported, err := l.localPath(filepath.Dir(path), ref.location)
303
- if err != nil {
304
- rel, _ := filepath.Rel(l.root, path)
305
- l.warnings = append(l.warnings, filepath.ToSlash(rel)+": "+err.Error())
306
- continue
307
- }
308
- if err := l.load(imported, ref.adoptedNS); err != nil {
309
- rel, _ := filepath.Rel(l.root, path)
310
- l.warnings = append(l.warnings, filepath.ToSlash(rel)+": import "+ref.location+": "+err.Error())
311
- }
312
- }
313
- return nil
314
- }
315
-
316
- type importReference struct {
317
- location string
318
- adoptedNS string
319
- }
320
-
321
- func importReferences(root *node, inheritedNS string) []importReference {
322
- var out []importReference
323
- var walk func(*node, string)
324
- walk = func(n *node, schemaNS string) {
325
- if n.name.Local == "schema" {
326
- schemaNS = first(n.attr("targetNamespace"), schemaNS)
327
- }
328
- if n.name.Local == "import" || n.name.Local == "include" || n.name.Local == "redefine" {
329
- if location := first(n.attr("location"), n.attr("schemaLocation")); location != "" {
330
- adopted := ""
331
- if n.name.Local == "include" || n.name.Local == "redefine" {
332
- adopted = schemaNS
333
- }
334
- out = append(out, importReference{location: location, adoptedNS: adopted})
335
- }
336
- }
337
- for _, child := range n.children {
338
- walk(child, schemaNS)
339
- }
340
- }
341
- walk(root, inheritedNS)
342
- seen := map[string]bool{}
343
- var uniqueRefs []importReference
344
- for _, ref := range out {
345
- key := ref.location + "\x00" + ref.adoptedNS
346
- if !seen[key] {
347
- uniqueRefs = append(uniqueRefs, ref)
348
- seen[key] = true
349
- }
350
- }
351
- return uniqueRefs
352
- }
353
-
354
- type index struct {
355
- messages map[qname]*node
356
- portTypes map[qname]*node
357
- bindings map[qname]*node
358
- elements map[qname]*node
359
- complexTypes map[qname]*node
360
- namespace map[*node]string
361
- }
362
-
363
- func (l *loader) buildIndex() index {
364
- ix := index{
365
- messages: map[qname]*node{}, portTypes: map[qname]*node{}, bindings: map[qname]*node{},
366
- elements: map[qname]*node{}, complexTypes: map[qname]*node{}, namespace: map[*node]string{},
367
- }
368
- paths := make([]string, 0, len(l.docs))
369
- for path := range l.docs {
370
- paths = append(paths, path)
371
- }
372
- sort.Strings(paths)
373
- for _, path := range paths {
374
- root := l.docs[path].root
375
- source := l.relative(path)
376
- switch root.name.Local {
377
- case "definitions":
378
- ns := root.attr("targetNamespace")
379
- ix.namespace[root] = ns
380
- for _, child := range root.children {
381
- switch child.name.Local {
382
- case "message":
383
- put(ix.messages, qname{ns, child.attr("name")}, child, &l.warnings, source)
384
- case "portType", "interface":
385
- put(ix.portTypes, qname{ns, child.attr("name")}, child, &l.warnings, source)
386
- case "binding":
387
- put(ix.bindings, qname{ns, child.attr("name")}, child, &l.warnings, source)
388
- case "types":
389
- for _, schema := range child.childrenNamed("schema") {
390
- ix.addSchema(schema, source, "", &l.warnings)
391
- }
392
- }
393
- }
394
- case "schema":
395
- namespaces := []string{root.attr("targetNamespace")}
396
- if namespaces[0] == "" && len(l.adopted[path]) > 0 {
397
- namespaces = namespaces[:0]
398
- for namespace := range l.adopted[path] {
399
- namespaces = append(namespaces, namespace)
400
- }
401
- sort.Strings(namespaces)
402
- }
403
- for _, namespace := range namespaces {
404
- ix.addSchema(root, source, namespace, &l.warnings)
405
- }
406
- }
407
- }
408
- return ix
409
- }
410
-
411
- func (ix *index) addSchema(schema *node, path, adoptedNS string, warnings *[]string) {
412
- ns := first(schema.attr("targetNamespace"), adoptedNS)
413
- ix.namespace[schema] = ns
414
- for _, child := range schema.children {
415
- switch child.name.Local {
416
- case "element":
417
- put(ix.elements, qname{ns, child.attr("name")}, child, warnings, path)
418
- case "complexType":
419
- put(ix.complexTypes, qname{ns, child.attr("name")}, child, warnings, path)
420
- }
421
- }
422
- }
423
-
424
- func put(target map[qname]*node, key qname, value *node, warnings *[]string, path string) {
425
- if key.local == "" {
426
- return
427
- }
428
- if previous, exists := target[key]; exists && previous != value {
429
- *warnings = append(*warnings, path+": duplicate declaration "+key.local+" in namespace "+key.ns)
430
- return
431
- }
432
- target[key] = value
433
- }
434
-
435
- func (l *loader) result(rootPaths []string, includeStandalone bool) Result {
436
- ix := l.buildIndex()
437
- var contracts []Contract
438
- for _, path := range rootPaths {
439
- doc := l.docs[path]
440
- if doc == nil || doc.root.name.Local != "definitions" {
441
- continue
442
- }
443
- definitions := doc.root
444
- namespace := definitions.attr("targetNamespace")
445
- services := definitions.childrenNamed("service")
446
- if len(services) == 0 && includeStandalone {
447
- contract := Contract{Name: first(definitions.attr("name"), strings.TrimSuffix(filepath.Base(path), filepath.Ext(path))), Namespace: namespace, Summary: definitions.documentation(), Source: l.relative(path)}
448
- for _, portType := range definitions.childrenNamed("portType") {
449
- contract.Interfaces = append(contract.Interfaces, ix.abstractInterface(portType))
450
- }
451
- if len(contract.Interfaces) > 0 {
452
- contracts = append(contracts, contract)
453
- }
454
- continue
455
- }
456
- for _, service := range services {
457
- contract := Contract{Name: first(service.attr("name"), definitions.attr("name")), Namespace: namespace, Summary: first(service.documentation(), definitions.documentation()), Source: l.relative(path)}
458
- for _, port := range service.childrenNamed("port") {
459
- if iface, ok := ix.boundInterface(port); ok {
460
- contract.Interfaces = append(contract.Interfaces, iface)
461
- } else {
462
- l.warnings = append(l.warnings, l.relative(path)+": port "+first(port.attr("name"), "<unnamed>")+" refers to a binding or portType that is not available locally")
463
- }
464
- }
465
- if len(contract.Interfaces) > 0 {
466
- contracts = append(contracts, contract)
467
- }
468
- }
469
- }
470
- contracts = uniqueContracts(contracts)
471
- for i := range contracts {
472
- sort.Slice(contracts[i].Interfaces, func(a, b int) bool { return contracts[i].Interfaces[a].Name < contracts[i].Interfaces[b].Name })
473
- }
474
- sort.Slice(contracts, func(i, j int) bool {
475
- if contracts[i].Name != contracts[j].Name {
476
- return contracts[i].Name < contracts[j].Name
477
- }
478
- return contracts[i].Source < contracts[j].Source
479
- })
480
- l.warnings = summarizeDuplicateWarnings(l.warnings)
481
- sort.Strings(l.warnings)
482
- return Result{Contracts: contracts, Warnings: unique(l.warnings)}
483
- }
484
-
485
- func summarizeDuplicateWarnings(warnings []string) []string {
486
- type group struct {
487
- first string
488
- count int
489
- }
490
- groups := map[string]*group{}
491
- var out []string
492
- for _, warning := range warnings {
493
- const marker = ": duplicate declaration "
494
- at := strings.Index(warning, marker)
495
- if at < 0 {
496
- out = append(out, warning)
497
- continue
498
- }
499
- namespaceAt := strings.LastIndex(warning, " in namespace ")
500
- if namespaceAt < 0 {
501
- out = append(out, warning)
502
- continue
503
- }
504
- namespace := warning[namespaceAt+len(" in namespace "):]
505
- key := namespace
506
- found := groups[key]
507
- if found == nil {
508
- found = &group{first: warning[:namespaceAt]}
509
- groups[key] = found
510
- }
511
- found.count++
512
- }
513
- keys := make([]string, 0, len(groups))
514
- for key := range groups {
515
- keys = append(keys, key)
516
- }
517
- sort.Strings(keys)
518
- for _, namespace := range keys {
519
- found := groups[namespace]
520
- out = append(out, fmt.Sprintf("%s; %d declarations share names in namespace %s; the first declaration is used", found.first, found.count, namespace))
521
- }
522
- return out
523
- }
524
-
525
- func (l *loader) relative(path string) string {
526
- rel, err := filepath.Rel(l.root, path)
527
- if err != nil {
528
- return filepath.ToSlash(path)
529
- }
530
- return filepath.ToSlash(rel)
531
- }
532
-
533
- func (ix index) abstractInterface(portType *node) Interface {
534
- iface := Interface{Name: portType.attr("name"), PortType: portType.attr("name")}
535
- iface.Operations, iface.Messages = ix.operations(portType, nil)
536
- return iface
537
- }
538
-
539
- func (ix index) boundInterface(port *node) (Interface, bool) {
540
- bindingRef := resolveQName(port, port.attr("binding"), "")
541
- binding := ix.lookup(ix.bindings, bindingRef)
542
- if binding == nil {
543
- return Interface{}, false
544
- }
545
- portTypeRef := resolveQName(binding, binding.attr("type"), bindingRef.ns)
546
- portType := ix.lookup(ix.portTypes, portTypeRef)
547
- if portType == nil {
548
- return Interface{}, false
549
- }
550
- version, style := bindingSOAP(binding)
551
- endpoint := ""
552
- for _, child := range port.children {
553
- if child.name.Local == "address" && (child.name.Space == soap11WSDL || child.name.Space == soap12WSDL || strings.Contains(child.name.Space, "soap")) {
554
- endpoint = child.attr("location")
555
- if child.name.Space == soap12WSDL {
556
- version = "1.2"
557
- } else if version == "" {
558
- version = "1.1"
559
- }
560
- }
561
- }
562
- iface := Interface{Name: first(port.attr("name"), binding.attr("name")), Binding: binding.attr("name"), PortType: portType.attr("name"), Endpoint: endpoint, Version: version, Style: style}
563
- iface.Operations, iface.Messages = ix.operations(portType, binding)
564
- return iface, true
565
- }
566
-
567
- func bindingSOAP(binding *node) (version, style string) {
568
- for _, child := range binding.children {
569
- if child.name.Local != "binding" {
570
- continue
571
- }
572
- if child.name.Space == soap12WSDL {
573
- version = "1.2"
574
- } else if child.name.Space == soap11WSDL || strings.Contains(child.name.Space, "soap") {
575
- version = "1.1"
576
- }
577
- style = child.attr("style")
578
- }
579
- return version, style
580
- }
581
-
582
- func (ix index) operations(portType, binding *node) ([]Operation, []Message) {
583
- bindingOps := map[string]*node{}
584
- if binding != nil {
585
- for _, operation := range binding.childrenNamed("operation") {
586
- bindingOps[operation.attr("name")] = operation
587
- }
588
- }
589
- var operations []Operation
590
- messageRefs := map[qname]bool{}
591
- for _, abstract := range portType.childrenNamed("operation") {
592
- name := abstract.attr("name")
593
- bound := bindingOps[name]
594
- operation := Operation{Name: name, Doc: abstract.documentation()}
595
- if input := abstract.child("input"); input != nil {
596
- ref := resolveQName(input, input.attr("message"), "")
597
- operation.Request = ref.local
598
- messageRefs[ref] = ref.local != ""
599
- }
600
- if output := abstract.child("output"); output != nil {
601
- ref := resolveQName(output, output.attr("message"), "")
602
- operation.Response = ref.local
603
- messageRefs[ref] = ref.local != ""
604
- }
605
- for _, fault := range abstract.childrenNamed("fault") {
606
- ref := resolveQName(fault, fault.attr("message"), "")
607
- faultName := first(fault.attr("name"), ref.local)
608
- if faultName != "" {
609
- operation.Faults = append(operation.Faults, faultName)
610
- }
611
- messageRefs[ref] = ref.local != ""
612
- }
613
- if bound != nil {
614
- for _, child := range bound.children {
615
- if child.name.Local == "operation" && (child.name.Space == soap11WSDL || child.name.Space == soap12WSDL || strings.Contains(child.name.Space, "soap")) {
616
- operation.Action = first(child.attr("soapAction"), child.attr("soapActionURI"))
617
- }
618
- }
619
- operation.Headers = bindingHeaders(bound)
620
- if operation.Doc == "" {
621
- operation.Doc = bound.documentation()
622
- }
623
- }
624
- operation.Faults = unique(operation.Faults)
625
- operations = append(operations, operation)
626
- }
627
- var messages []Message
628
- seen := map[string]bool{}
629
- var typeQueue []qname
630
- refs := make([]qname, 0, len(messageRefs))
631
- for ref, present := range messageRefs {
632
- if present {
633
- refs = append(refs, ref)
634
- }
635
- }
636
- sort.Slice(refs, func(i, j int) bool { return qnameLess(refs[i], refs[j]) })
637
- for _, ref := range refs {
638
- message, reached := ix.message(ref)
639
- if message.Name != "" && !seen[message.Name] {
640
- messages = append(messages, message)
641
- seen[message.Name] = true
642
- }
643
- typeQueue = append(typeQueue, reached...)
644
- }
645
- for len(typeQueue) > 0 {
646
- ref := typeQueue[0]
647
- typeQueue = typeQueue[1:]
648
- if ref.local == "" || seen[ref.local] || builtin(ref) {
649
- continue
650
- }
651
- complex := ix.lookup(ix.complexTypes, ref)
652
- if complex == nil {
653
- continue
654
- }
655
- fields, reached := ix.shape(complex, map[qname]bool{})
656
- messages = append(messages, Message{Name: ref.local, Fields: fields})
657
- seen[ref.local] = true
658
- typeQueue = append(typeQueue, reached...)
659
- }
660
- sort.Slice(operations, func(i, j int) bool { return operations[i].Name < operations[j].Name })
661
- sort.Slice(messages, func(i, j int) bool { return messages[i].Name < messages[j].Name })
662
- return operations, messages
663
- }
664
-
665
- func bindingHeaders(operation *node) []string {
666
- var headers []string
667
- for _, direction := range []string{"input", "output"} {
668
- container := operation.child(direction)
669
- if container == nil {
670
- continue
671
- }
672
- for _, child := range container.children {
673
- if child.name.Local != "header" || !(child.name.Space == soap11WSDL || child.name.Space == soap12WSDL || strings.Contains(child.name.Space, "soap")) {
674
- continue
675
- }
676
- message := resolveQName(child, child.attr("message"), "").local
677
- header := first(child.attr("part"), message)
678
- if message != "" && child.attr("part") != "" {
679
- header = message + "." + child.attr("part")
680
- }
681
- if header != "" {
682
- headers = append(headers, header)
683
- }
684
- }
685
- }
686
- return unique(headers)
687
- }
688
-
689
- func (ix index) message(ref qname) (Message, []qname) {
690
- messageNode := ix.lookup(ix.messages, ref)
691
- if messageNode == nil {
692
- return Message{Name: ref.local}, nil
693
- }
694
- message := Message{Name: messageNode.attr("name")}
695
- var reached []qname
696
- parts := messageNode.childrenNamed("part")
697
- for _, part := range parts {
698
- if elementValue := part.attr("element"); elementValue != "" {
699
- elementRef := resolveQName(part, elementValue, ref.ns)
700
- element := ix.lookup(ix.elements, elementRef)
701
- if element != nil && len(parts) == 1 {
702
- fields, refs := ix.elementShape(element, elementRef.ns)
703
- if len(fields) > 0 {
704
- message.Fields = append(message.Fields, fields...)
705
- reached = append(reached, refs...)
706
- continue
707
- }
708
- }
709
- typeName := elementRef.local
710
- if element != nil && element.attr("type") != "" {
711
- typeRef := resolveQName(element, element.attr("type"), elementRef.ns)
712
- typeName = typeLabel(typeRef)
713
- reached = append(reached, typeRef)
714
- }
715
- message.Fields = append(message.Fields, Field{Name: first(part.attr("name"), elementRef.local), Type: typeName, Doc: documentationOf(element)})
716
- continue
717
- }
718
- typeRef := resolveQName(part, part.attr("type"), ref.ns)
719
- message.Fields = append(message.Fields, Field{Name: first(part.attr("name"), "value"), Type: typeLabel(typeRef), Doc: part.documentation()})
720
- reached = append(reached, typeRef)
721
- }
722
- return message, reached
723
- }
724
-
725
- func (ix index) elementShape(element *node, fallbackNS string) ([]Field, []qname) {
726
- if inline := element.child("complexType"); inline != nil {
727
- return ix.shape(inline, map[qname]bool{})
728
- }
729
- typeRef := resolveQName(element, element.attr("type"), fallbackNS)
730
- if complex := ix.lookup(ix.complexTypes, typeRef); complex != nil {
731
- fields, refs := ix.shape(complex, map[qname]bool{typeRef: true})
732
- return fields, append(refs, typeRef)
733
- }
734
- return nil, []qname{typeRef}
735
- }
736
-
737
- func (ix index) shape(complex *node, resolving map[qname]bool) ([]Field, []qname) {
738
- var fields []Field
739
- var refs []qname
740
- var walk func(*node)
741
- walk = func(current *node) {
742
- for _, child := range current.children {
743
- switch child.name.Local {
744
- case "extension", "restriction":
745
- base := resolveQName(child, child.attr("base"), "")
746
- if !builtin(base) && !resolving[base] {
747
- if parent := ix.lookup(ix.complexTypes, base); parent != nil {
748
- resolving[base] = true
749
- parentFields, parentRefs := ix.shape(parent, resolving)
750
- delete(resolving, base)
751
- fields = append(fields, parentFields...)
752
- refs = append(refs, parentRefs...)
753
- }
754
- }
755
- walk(child)
756
- case "sequence", "all", "choice", "complexContent", "simpleContent", "group":
757
- walk(child)
758
- case "element":
759
- name := child.attr("name")
760
- ref := resolveQName(child, child.attr("ref"), "")
761
- if name == "" {
762
- name = ref.local
763
- }
764
- typeRef := resolveQName(child, child.attr("type"), "")
765
- if typeRef.local == "" && ref.local != "" {
766
- if declared := ix.lookup(ix.elements, ref); declared != nil {
767
- typeRef = resolveQName(declared, declared.attr("type"), ref.ns)
768
- if name == "" {
769
- name = declared.attr("name")
770
- }
771
- }
772
- }
773
- typeName := typeLabel(typeRef)
774
- if inline := child.child("complexType"); inline != nil {
775
- typeName = first(name, "object")
776
- }
777
- if typeName == "" {
778
- typeName = "any"
779
- }
780
- if max := child.attr("maxOccurs"); max == "unbounded" || (max != "" && max != "1") {
781
- typeName += "[]"
782
- }
783
- fields = append(fields, Field{Name: name, Type: typeName, Doc: child.documentation()})
784
- refs = append(refs, typeRef)
785
- case "attribute":
786
- name := first(child.attr("name"), resolveQName(child, child.attr("ref"), "").local)
787
- typeRef := resolveQName(child, child.attr("type"), "")
788
- fields = append(fields, Field{Name: "@" + name, Type: first(typeLabel(typeRef), "string"), Doc: child.documentation()})
789
- refs = append(refs, typeRef)
790
- }
791
- }
792
- }
793
- walk(complex)
794
- return uniqueFields(fields), refs
795
- }
796
-
797
- func (ix index) lookup(values map[qname]*node, ref qname) *node {
798
- if found := values[ref]; found != nil {
799
- return found
800
- }
801
- if ref.local == "" {
802
- return nil
803
- }
804
- var match *node
805
- for key, value := range values {
806
- if key.local != ref.local {
807
- continue
808
- }
809
- if match != nil {
810
- return nil
811
- }
812
- match = value
813
- }
814
- return match
815
- }
816
-
817
- func resolveQName(n *node, value, fallbackNS string) qname {
818
- value = strings.TrimSpace(value)
819
- if value == "" {
820
- return qname{}
821
- }
822
- if prefix, local, ok := strings.Cut(value, ":"); ok {
823
- return qname{ns: n.ns[prefix], local: local}
824
- }
825
- namespace := n.ns[""]
826
- if namespace == "" || namespace == "http://schemas.xmlsoap.org/wsdl/" {
827
- namespace = fallbackNS
828
- }
829
- return qname{ns: namespace, local: value}
830
- }
831
-
832
- func typeLabel(ref qname) string {
833
- if ref.local == "" {
834
- return ""
835
- }
836
- if ref.ns == xsdNS || strings.Contains(strings.ToLower(ref.ns), "xmlschema") {
837
- switch strings.ToLower(ref.local) {
838
- case "integer", "int", "long", "short", "byte", "nonnegativeinteger", "positiveinteger":
839
- return "int"
840
- case "decimal", "double", "float":
841
- return "number"
842
- case "boolean":
843
- return "boolean"
844
- case "datetime", "date", "time", "duration":
845
- return strings.ToLower(ref.local)
846
- case "base64binary", "hexbinary":
847
- return "bytes"
848
- case "anytype", "anysimpletype":
849
- return "any"
850
- default:
851
- return "string"
852
- }
853
- }
854
- return ref.local
855
- }
856
-
857
- func builtin(ref qname) bool {
858
- return ref.local == "" || ref.ns == xsdNS || strings.Contains(strings.ToLower(ref.ns), "xmlschema")
859
- }
860
-
861
- func documentationOf(n *node) string {
862
- if n == nil {
863
- return ""
864
- }
865
- return n.documentation()
866
- }
867
-
868
- func uniqueContracts(in []Contract) []Contract {
869
- seen := map[string]bool{}
870
- var out []Contract
871
- for _, contract := range in {
872
- key := contract.Source + "\x00" + contract.Name
873
- if !seen[key] {
874
- out = append(out, contract)
875
- seen[key] = true
876
- }
877
- }
878
- return out
879
- }
880
-
881
- func uniqueFields(in []Field) []Field {
882
- seen := map[string]bool{}
883
- var out []Field
884
- for _, field := range in {
885
- if field.Name == "" || seen[field.Name] {
886
- continue
887
- }
888
- seen[field.Name] = true
889
- out = append(out, field)
890
- }
891
- return out
892
- }
893
-
894
- func qnameLess(left, right qname) bool {
895
- if left.ns != right.ns {
896
- return left.ns < right.ns
897
- }
898
- return left.local < right.local
899
- }
900
-
901
- func unique(in []string) []string {
902
- seen := map[string]bool{}
903
- var out []string
904
- for _, value := range in {
905
- if value != "" && !seen[value] {
906
- seen[value] = true
907
- out = append(out, value)
908
- }
909
- }
910
- return out
911
- }
912
-
913
- func first(values ...string) string {
914
- for _, value := range values {
915
- if strings.TrimSpace(value) != "" {
916
- return strings.TrimSpace(value)
917
- }
918
- }
919
- return ""
920
- }