@shortlink-org/portolan 0.2.4 → 0.4.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.
- package/README.md +174 -6
- package/catalog/enum_test.go +46 -0
- package/catalog/evidence_test.go +35 -0
- package/catalog/model.go +1213 -0
- package/catalog/roundtrip_test.go +210 -0
- package/catalog/via_test.go +38 -0
- package/cli/init.test.mjs +6 -1
- package/cli/portolan.mjs +14 -1
- package/cli/portolan.test.mjs +49 -0
- package/go.mod +21 -0
- package/go.sum +34 -0
- package/internal/gocall/README.md +19 -0
- package/internal/gocall/analyze.go +189 -0
- package/internal/gocall/analyze_test.go +107 -0
- package/internal/gohttp/analyze.go +2562 -0
- package/internal/gohttp/destination.go +373 -0
- package/internal/gohttp/endpoints.go +1067 -0
- package/internal/gohttp/roots.go +320 -0
- package/internal/gohttp/typed.go +96 -0
- package/internal/goscan/constants.go +85 -0
- package/internal/goscan/goscan_test.go +227 -0
- package/internal/goscan/index.go +629 -0
- package/internal/goscan/index_test.go +66 -0
- package/internal/goscan/names.go +52 -0
- package/internal/goscan/parse_test.go +11 -0
- package/internal/goscan/source.go +37 -0
- package/internal/goscan/tree.go +284 -0
- package/internal/goscan/types.go +99 -0
- package/internal/wsdl/ids.go +127 -0
- package/internal/wsdl/ids_test.go +21 -0
- package/internal/wsdl/model.go +70 -0
- package/internal/wsdl/parse.go +949 -0
- package/internal/wsdl/parse_test.go +170 -0
- package/package.json +28 -15
- package/plugin/describe.go +156 -0
- package/plugin/describe_test.go +114 -0
- package/plugin/protocol.go +141 -0
- package/plugin/schematest/schematest.go +126 -0
- package/plugins/README.md +404 -50
- package/plugins/cmd/portolan-http-clients/main.go +19 -0
- package/plugins/extract-argocd/options.schema.json +44 -0
- package/plugins/extract-celery/extract.py +0 -2
- package/plugins/extract-celery/extract_test.py +1 -1
- package/plugins/extract-celery/main.py +1 -0
- package/plugins/extract-csharp-ddd/README.md +213 -0
- package/plugins/extract-csharp-ddd/options.schema.json +55 -0
- package/plugins/extract-django/README.md +52 -19
- package/plugins/extract-django/choices.py +47 -0
- package/plugins/extract-django/domain.py +30 -18
- package/plugins/extract-django/extract.py +21 -7
- package/plugins/extract-django/extract_test.py +68 -2
- package/plugins/extract-django/lifecycle.py +4 -28
- package/plugins/extract-django/main.py +1 -0
- package/plugins/extract-django/operations.py +1 -1
- package/plugins/extract-django/routing_test.py +109 -1
- package/plugins/extract-django/rules.py +154 -0
- package/plugins/extract-django/rules_test.py +158 -0
- package/plugins/extract-django/store.py +1 -1
- package/plugins/extract-django/transport.py +101 -55
- package/plugins/extract-django/verbs.py +241 -0
- package/plugins/extract-go/README.md +47 -0
- package/plugins/extract-go-sqs/options.schema.json +21 -0
- package/plugins/extract-http-clients/describe.go +20 -0
- package/plugins/extract-http-clients/describe_test.go +11 -0
- package/plugins/extract-http-clients/extract.go +740 -0
- package/plugins/extract-http-clients/extract_test.go +1561 -0
- package/plugins/extract-http-clients/main.go +41 -0
- package/plugins/extract-java/build/org/portolan/extract/Extract.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Main.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol$Builder.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol$Input.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol$Options.class +0 -0
- package/plugins/extract-k8s/options.schema.json +32 -0
- package/plugins/extract-laravel/Cargo.lock +962 -0
- package/plugins/extract-laravel/Cargo.toml +20 -0
- package/plugins/extract-laravel/README.md +200 -0
- package/plugins/extract-laravel/options.schema.json +68 -0
- package/plugins/extract-laravel/rustfmt.toml +1 -0
- package/plugins/extract-php-ddd/Cargo.lock +972 -0
- package/plugins/extract-php-ddd/Cargo.toml +22 -0
- package/plugins/extract-php-ddd/README.md +141 -0
- package/plugins/extract-php-ddd/options.schema.json +50 -0
- package/plugins/extract-php-ddd/rustfmt.toml +1 -0
- package/plugins/extract-python-kafka/extract.py +0 -2
- package/plugins/extract-python-kafka/extract_test.py +1 -1
- package/plugins/extract-python-kafka/main.py +1 -0
- package/plugins/extract-terraform/options.schema.json +26 -0
- package/plugins/extract-ts/extract.test.ts +2 -2
- package/plugins/extract-ts/extract.ts +4 -5
- package/plugins/extract-ts/graphql.test.ts +1 -1
- package/plugins/extract-ts/main.ts +1 -0
- package/plugins/openapi/ids.go +261 -0
- package/plugins/openapi/ids_test.go +98 -0
- package/plugins/phpscan/Cargo.lock +953 -0
- package/plugins/phpscan/Cargo.toml +19 -0
- package/plugins/phpscan/rustfmt.toml +1 -0
- package/plugins/portolan-go.wasm +0 -0
- package/plugins/pyplugin/catalog.py +24 -1
- package/plugins/pyplugin/protocol.py +1 -5
- package/plugins/verify-otel/options.schema.json +12 -0
- package/portolan.json +132 -4
- package/rules/builtin.json +218 -0
- package/schema/portolan.schema.json +905 -4
- package/scripts/README.md +21 -13
- package/scripts/catalog-sources.mjs +6 -0
- package/scripts/delivery-presets.mjs +57 -14
- package/scripts/diff.mjs +5 -1
- package/scripts/django-aggregates.test.mjs +58 -0
- package/scripts/gen-likec4.mjs +150 -17
- package/scripts/gen-likec4.test.mjs +96 -0
- package/scripts/gen.mjs +148 -118
- package/scripts/gitops-example.test.mjs +108 -0
- package/scripts/go-discovery.test.mjs +30 -0
- package/scripts/history.mjs +186 -3
- package/scripts/history.test.mjs +1 -1
- package/scripts/host-plugins/fetch-argocd.mjs +338 -0
- package/scripts/host-plugins/fetch-argocd.options.json +49 -0
- package/scripts/host-plugins/fetch-argocd.test.mjs +274 -0
- package/scripts/host-plugins/fetch-bsr.mjs +1 -0
- package/scripts/host-plugins/fetch-csr.mjs +1 -0
- package/scripts/host-plugins/fetch-git.mjs +78 -21
- package/scripts/host-plugins/fetch-git.test.mjs +62 -8
- package/scripts/host-plugins/fetch-k8s.mjs +263 -0
- package/scripts/host-plugins/fetch-k8s.options.json +50 -0
- package/scripts/host-plugins/fetch-k8s.test.mjs +259 -0
- package/scripts/host-plugins/k8s-topology.mjs +183 -0
- package/scripts/json-format.mjs +192 -0
- package/scripts/json-format.test.mjs +97 -0
- package/scripts/local-api.mjs +287 -12
- package/scripts/local-api.test.mjs +150 -5
- package/scripts/local-discovery.mjs +94 -9
- package/scripts/manifest.mjs +14 -3
- package/scripts/manifest.test.mjs +24 -0
- package/scripts/output-diff.mjs +94 -0
- package/scripts/output-diff.test.mjs +36 -0
- package/scripts/package-smoke.mjs +62 -4
- package/scripts/plugin-host.mjs +45 -3
- package/scripts/plugin-host.test.mjs +9 -0
- package/scripts/plugin-wasm-worker.mjs +4 -1
- package/scripts/plugins-fresh.mjs +97 -0
- package/scripts/plugins-fresh.test.mjs +64 -0
- package/scripts/provenance.mjs +72 -0
- package/scripts/provenance.test.mjs +149 -0
- package/scripts/run-builtin.mjs +62 -7
- package/scripts/schema.mjs +167 -19
- package/scripts/trace-trials.mjs +176 -0
- package/scripts/trace-trials.test.mjs +142 -0
- package/scripts/warning-policy.mjs +167 -0
- package/scripts/warning-policy.test.mjs +93 -0
- package/src/app/Breadcrumbs.test.ts +3 -0
- package/src/app/Breadcrumbs.tsx +3 -0
- package/src/app/CatalogApp.tsx +2 -0
- package/src/app/Sidebar.tsx +3 -3
- package/src/app/SidebarFooter.tsx +20 -4
- package/src/catalog-docs.test.ts +64 -0
- package/src/catalog-docs.ts +35 -0
- package/src/catalog-error.test.ts +15 -0
- package/src/catalog-index.ts +25 -0
- package/src/catalog-model.ts +293 -5
- package/src/catalog-validation.ts +113 -2
- package/src/catalog.test.ts +40 -1
- package/src/chat/Starter.tsx +5 -11
- package/src/chat/tools.test.ts +27 -0
- package/src/chat/tools.ts +5 -9
- package/src/components/CatalogStamp.tsx +10 -8
- package/src/components/ChannelRows.test.tsx +76 -1
- package/src/components/ChannelRows.tsx +223 -60
- package/src/components/DeploymentRows.tsx +131 -0
- package/src/components/DocsLinks.test.tsx +27 -0
- package/src/components/DocsLinks.tsx +56 -0
- package/src/components/FieldTree.tsx +5 -2
- package/src/components/HTTPDestinationEvidence.test.tsx +23 -0
- package/src/components/HTTPDestinationEvidence.tsx +31 -0
- package/src/components/Integrations.tsx +1 -1
- package/src/components/MachineDocs.tsx +6 -5
- package/src/components/MethodRows.tsx +9 -2
- package/src/components/PluginIcon.tsx +77 -0
- package/src/components/ProblemRow.tsx +173 -153
- package/src/components/RelationEvidence.test.tsx +14 -0
- package/src/components/RelationEvidence.tsx +53 -0
- package/src/components/RuleMarks.tsx +22 -0
- package/src/components/ShapeRows.tsx +24 -20
- package/src/data.ts +46 -7
- package/src/enrich.test.ts +459 -4
- package/src/enrich.ts +308 -7
- package/src/er/ErCanvas.tsx +218 -12
- package/src/er/GroupNode.tsx +57 -0
- package/src/er/StoreHeader.tsx +1 -0
- package/src/er/layout.test.ts +85 -2
- package/src/er/layout.ts +140 -5
- package/src/er/spec.test.ts +17 -0
- package/src/er/spec.ts +23 -10
- package/src/flow/Recordings.test.tsx +52 -0
- package/src/flow/Recordings.tsx +236 -0
- package/src/flow/StepDetail.tsx +59 -0
- package/src/flow/TraceTrial.tsx +419 -0
- package/src/flow/evidence.test.ts +16 -0
- package/src/flow/evidence.ts +34 -0
- package/src/flow/examples.test.ts +33 -0
- package/src/flow/examples.ts +37 -0
- package/src/flow/outline.test.ts +28 -0
- package/src/flow/outline.ts +7 -2
- package/src/flow/trace-trial-resume.test.ts +42 -0
- package/src/flow/trace-trial-resume.ts +74 -0
- package/src/graph/elk.ts +78 -0
- package/src/index.css +68 -0
- package/src/landing/DraggableReveal.tsx +3 -2
- package/src/landing/EvidencePipeline.tsx +105 -0
- package/src/landing/LandingPage.tsx +17 -70
- package/src/landing/ProductTour.tsx +6 -6
- package/src/lib/all-problems.ts +27 -17
- package/src/lib/catalog-diff.ts +1 -1
- package/src/lib/centrality.test.ts +251 -0
- package/src/lib/centrality.ts +232 -0
- package/src/lib/confluence.test.ts +37 -0
- package/src/lib/confluence.ts +41 -0
- package/src/lib/context-color.ts +1 -1
- package/src/lib/deployment-drift.ts +22 -0
- package/src/lib/derive.ts +43 -69
- package/src/lib/django-aggregates.d.mts +9 -0
- package/src/lib/django-aggregates.mjs +36 -0
- package/src/lib/django-aggregates.test.ts +29 -0
- package/src/lib/django-aggregates.ts +5 -0
- package/src/lib/environments.test.ts +83 -0
- package/src/lib/environments.ts +57 -0
- package/src/lib/integration-url.test.ts +30 -0
- package/src/lib/integration-url.ts +63 -0
- package/src/lib/kafka-ui.ts +3 -45
- package/src/lib/local-api.ts +116 -4
- package/src/lib/notion.ts +13 -0
- package/src/lib/plugin-index.json +2765 -0
- package/src/lib/plugins.test.ts +68 -0
- package/src/lib/plugins.ts +264 -0
- package/src/lib/problem-flows.test.ts +61 -0
- package/src/lib/problem-flows.ts +78 -0
- package/src/lib/problem-rules-cel.d.mts +43 -0
- package/src/lib/problem-rules-cel.mjs +407 -0
- package/src/lib/problem-rules.test.ts +287 -0
- package/src/lib/problem-rules.ts +271 -0
- package/src/lib/problem-subjects.ts +737 -0
- package/src/lib/rule-entries.ts +39 -0
- package/src/lib/{data-problems.test.ts → rules-data.test.ts} +18 -16
- package/src/lib/rules-deploy.test.ts +133 -0
- package/src/lib/{problems.test.ts → rules-edges.test.ts} +8 -3
- package/src/lib/{proto-problems.test.ts → rules-proto.test.ts} +9 -25
- package/src/lib/{wire-problems.test.ts → rules-wire.test.ts} +13 -11
- package/src/lib/rules.test.ts +51 -0
- package/src/lib/rules.ts +86 -0
- package/src/lib/setup-info.test.ts +17 -0
- package/src/lib/setup-info.ts +58 -0
- package/src/lib/shape.test.ts +32 -0
- package/src/lib/shape.ts +30 -6
- package/src/lib/tech.ts +16 -0
- package/src/lib/trace-project.test.ts +34 -0
- package/src/lib/trace-project.ts +50 -0
- package/src/lib/use-problems.ts +23 -0
- package/src/lib/warnings.test.ts +63 -0
- package/src/lib/warnings.ts +260 -0
- package/src/likec4/ids.test.ts +6 -2
- package/src/likec4/ids.ts +43 -0
- package/src/main.tsx +23 -0
- package/src/map/ContextMapGraph.tsx +76 -32
- package/src/merge-deployments.test.ts +127 -0
- package/src/merge.test.ts +82 -0
- package/src/merge.ts +189 -18
- package/src/pages/AggregatePage.tsx +65 -14
- package/src/pages/ContextMap.tsx +45 -3
- package/src/pages/ContextPage.tsx +8 -5
- package/src/pages/EventPage.tsx +15 -5
- package/src/pages/FlowDetail.tsx +23 -2
- package/src/pages/GraphPage.tsx +40 -3
- package/src/pages/Overview.tsx +152 -12
- package/src/pages/PluginIndex.tsx +190 -0
- package/src/pages/Problems.tsx +396 -128
- package/src/pages/ServicePage.tsx +62 -5
- package/src/pages/Settings.tsx +217 -43
- package/src/pages/settings/AboutSettings.tsx +8 -1
- package/src/pages/settings/DjangoAggregateChoices.tsx +79 -0
- package/src/pages/settings/IntegrationsSettings.tsx +63 -17
- package/src/pages/settings/RecordingSettings.tsx +138 -0
- package/src/pages/settings/RulesSettings.tsx +825 -0
- package/src/routes.test.ts +9 -0
- package/src/routes.ts +22 -1
- package/src/selection/DetailPanel.tsx +15 -0
- package/src/virtual-provenance.d.ts +11 -0
- package/vite.config.ts +5 -0
- package/scripts/vendor-lock.mjs +0 -58
- package/scripts/vendor-lock.test.mjs +0 -69
- package/src/lib/data-problems.ts +0 -314
- package/src/lib/proto-problems.ts +0 -237
- package/src/lib/wire-problems.ts +0 -342
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
package goscan
|
|
2
|
+
|
|
3
|
+
import "strings"
|
|
4
|
+
|
|
5
|
+
// LastSegment is what follows the last dot: the name off a type key, the
|
|
6
|
+
// event off a wire name.
|
|
7
|
+
func LastSegment(value string) string {
|
|
8
|
+
if at := strings.LastIndex(value, "."); at >= 0 {
|
|
9
|
+
return value[at+1:]
|
|
10
|
+
}
|
|
11
|
+
return value
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Slug is a value as an identifier in a URL or a catalog id: lower case,
|
|
15
|
+
// runs of anything else collapsed to one dash, none at the ends.
|
|
16
|
+
func Slug(value string) string {
|
|
17
|
+
value = strings.ToLower(strings.TrimSpace(value))
|
|
18
|
+
var out strings.Builder
|
|
19
|
+
dash := false
|
|
20
|
+
for _, r := range value {
|
|
21
|
+
if r >= 'a' && r <= 'z' || r >= '0' && r <= '9' {
|
|
22
|
+
out.WriteRune(r)
|
|
23
|
+
dash = false
|
|
24
|
+
} else if out.Len() > 0 && !dash {
|
|
25
|
+
out.WriteByte('-')
|
|
26
|
+
dash = true
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return strings.TrimSuffix(out.String(), "-")
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Title is a slug or an identifier as words: split on dash, underscore and
|
|
33
|
+
// dot, each capitalised.
|
|
34
|
+
func Title(value string) string {
|
|
35
|
+
parts := strings.FieldsFunc(value, func(r rune) bool { return r == '-' || r == '_' || r == '.' })
|
|
36
|
+
for i := range parts {
|
|
37
|
+
if parts[i] != "" {
|
|
38
|
+
parts[i] = strings.ToUpper(parts[i][:1]) + parts[i][1:]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return strings.Join(parts, " ")
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// FirstNonEmpty is the first value that is more than whitespace.
|
|
45
|
+
func FirstNonEmpty(values ...string) string {
|
|
46
|
+
for _, value := range values {
|
|
47
|
+
if strings.TrimSpace(value) != "" {
|
|
48
|
+
return value
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return ""
|
|
52
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
package goscan
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"go/ast"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// Source is a place in the tree, the way the catalog cites one: the file
|
|
9
|
+
// relative to the root, and the line when there is one.
|
|
10
|
+
type Source struct {
|
|
11
|
+
File string
|
|
12
|
+
Line int
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
func (s Source) String() string {
|
|
16
|
+
if s.Line == 0 {
|
|
17
|
+
return s.File
|
|
18
|
+
}
|
|
19
|
+
return fmt.Sprintf("%s:%d", s.File, s.Line)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// File is one parsed file: its path under the root, the import path of its
|
|
23
|
+
// package, its imports by local name, and the syntax.
|
|
24
|
+
type File struct {
|
|
25
|
+
Generated bool
|
|
26
|
+
Name string
|
|
27
|
+
Pkg string
|
|
28
|
+
Imports map[string]string
|
|
29
|
+
Node *ast.File
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ConstExpr is a constant as it was declared: the expression, and the file it
|
|
33
|
+
// is in, since the expression's names mean what that file's imports say.
|
|
34
|
+
type ConstExpr struct {
|
|
35
|
+
Expr ast.Expr
|
|
36
|
+
File *File
|
|
37
|
+
}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
// Package goscan reads a Go source tree the way the extractors need it: every
|
|
2
|
+
// non-test, non-generated file parsed once, each with the import path of its
|
|
3
|
+
// package and its imports by local name, and the string constants of the tree
|
|
4
|
+
// indexed so that a topic, a queue or a subject named by a constant resolves to
|
|
5
|
+
// the literal behind it.
|
|
6
|
+
//
|
|
7
|
+
// It is the part of a Go extractor that has nothing to do with what the
|
|
8
|
+
// extractor is looking for. River, Watermill and go-nats read the tree this
|
|
9
|
+
// way, and the next Go extractor should not have to copy it a fourth time.
|
|
10
|
+
//
|
|
11
|
+
// Index, over a Tree, is the second shared part: the structs with their field
|
|
12
|
+
// types and tag defaults, the interfaces by method name, and every function
|
|
13
|
+
// with its parameters and results, so that a value - a subject, a topic, a
|
|
14
|
+
// queue, a job type - is followed through locals, fields, constructors and
|
|
15
|
+
// parameters up to the callers that pass it, the same way in each extractor.
|
|
16
|
+
package goscan
|
|
17
|
+
|
|
18
|
+
import (
|
|
19
|
+
"fmt"
|
|
20
|
+
"go/ast"
|
|
21
|
+
"go/build"
|
|
22
|
+
"go/parser"
|
|
23
|
+
"go/token"
|
|
24
|
+
"io/fs"
|
|
25
|
+
"os"
|
|
26
|
+
"path"
|
|
27
|
+
"path/filepath"
|
|
28
|
+
"runtime"
|
|
29
|
+
"sort"
|
|
30
|
+
"strconv"
|
|
31
|
+
"strings"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
// ReadOptions controls a source snapshot. Generated declarations can be used
|
|
35
|
+
// for type/contract resolution without treating them as handwritten behavior.
|
|
36
|
+
type ReadOptions struct {
|
|
37
|
+
IncludeGenerated bool
|
|
38
|
+
AllowPartial bool
|
|
39
|
+
GOOS string
|
|
40
|
+
GOARCH string
|
|
41
|
+
Tags []string
|
|
42
|
+
// Directories restricts parsing to these module-relative directories.
|
|
43
|
+
Directories []string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Tree is one Go module's source, read once.
|
|
47
|
+
type Tree struct {
|
|
48
|
+
Diagnostics []string
|
|
49
|
+
ByDir map[string][]*File
|
|
50
|
+
Options ReadOptions
|
|
51
|
+
|
|
52
|
+
Root string
|
|
53
|
+
Module string
|
|
54
|
+
Fset *token.FileSet
|
|
55
|
+
// Files in path order, so that whatever is read off them comes out in the
|
|
56
|
+
// same order every run.
|
|
57
|
+
Files []*File
|
|
58
|
+
// Constants is every constant of the tree by "<import path>.<Name>", as
|
|
59
|
+
// the expression that declared it, so a constant defined through another
|
|
60
|
+
// is followed rather than copied.
|
|
61
|
+
Constants map[string]ConstExpr
|
|
62
|
+
// Foreign is what an identifier from a package outside the tree is worth,
|
|
63
|
+
// when the extractor knows: river.QueueDefault is "default", and nothing
|
|
64
|
+
// in the tree says so. Nil when there is nothing to say.
|
|
65
|
+
Foreign func(importPath, name string) (string, bool)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Read parses the tree under root. Test files, generated files, vendored
|
|
69
|
+
// code and dot-directories are left out: they are not what the service
|
|
70
|
+
// says about itself.
|
|
71
|
+
func Read(root string) (*Tree, error) { return ReadWithOptions(root, ReadOptions{}) }
|
|
72
|
+
|
|
73
|
+
func ReadWithOptions(root string, options ReadOptions) (*Tree, error) {
|
|
74
|
+
root = filepath.Clean(root)
|
|
75
|
+
t := &Tree{Root: root, Module: ModulePath(root), Fset: token.NewFileSet(), Constants: map[string]ConstExpr{}, ByDir: map[string][]*File{}, Options: options}
|
|
76
|
+
buildContext := build.Default
|
|
77
|
+
if options.GOOS != "" {
|
|
78
|
+
buildContext.GOOS = options.GOOS
|
|
79
|
+
} else if target := os.Getenv("GOOS"); target != "" {
|
|
80
|
+
buildContext.GOOS = target
|
|
81
|
+
}
|
|
82
|
+
if options.GOARCH != "" {
|
|
83
|
+
buildContext.GOARCH = options.GOARCH
|
|
84
|
+
} else if target := os.Getenv("GOARCH"); target != "" {
|
|
85
|
+
buildContext.GOARCH = target
|
|
86
|
+
}
|
|
87
|
+
buildContext.BuildTags = append([]string(nil), options.Tags...)
|
|
88
|
+
// The WASI host supplies the target platform. Never silently analyze the
|
|
89
|
+
// wasip1 runtime's file set when running the syntax extractor as a plugin.
|
|
90
|
+
matchBuild := runtime.GOOS != "wasip1" || options.GOOS != "" || os.Getenv("GOOS") != ""
|
|
91
|
+
allowed := map[string]bool{}
|
|
92
|
+
for _, dir := range options.Directories {
|
|
93
|
+
allowed[filepath.ToSlash(filepath.Clean(dir))] = true
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
var names []string
|
|
97
|
+
err := filepath.WalkDir(root, func(name string, entry fs.DirEntry, err error) error {
|
|
98
|
+
if err != nil {
|
|
99
|
+
return err
|
|
100
|
+
}
|
|
101
|
+
if entry.IsDir() {
|
|
102
|
+
if name != root {
|
|
103
|
+
if _, err := os.Stat(filepath.Join(name, "go.mod")); err == nil {
|
|
104
|
+
return filepath.SkipDir
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if name != root && strings.HasPrefix(entry.Name(), ".") {
|
|
108
|
+
return filepath.SkipDir
|
|
109
|
+
}
|
|
110
|
+
switch entry.Name() {
|
|
111
|
+
case ".git", ".portolan", "node_modules", "vendor", "testdata":
|
|
112
|
+
if name != root {
|
|
113
|
+
return filepath.SkipDir
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return nil
|
|
117
|
+
}
|
|
118
|
+
base := entry.Name()
|
|
119
|
+
if !strings.HasSuffix(base, ".go") || strings.HasSuffix(base, "_test.go") {
|
|
120
|
+
return nil
|
|
121
|
+
}
|
|
122
|
+
rel, _ := filepath.Rel(root, filepath.Dir(name))
|
|
123
|
+
if len(allowed) > 0 && !allowed[filepath.ToSlash(rel)] {
|
|
124
|
+
return nil
|
|
125
|
+
}
|
|
126
|
+
if matchBuild {
|
|
127
|
+
match, err := buildContext.MatchFile(filepath.Dir(name), base)
|
|
128
|
+
if err != nil {
|
|
129
|
+
return err
|
|
130
|
+
}
|
|
131
|
+
if !match {
|
|
132
|
+
return nil
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if !options.IncludeGenerated && generatedName(base) {
|
|
136
|
+
return nil
|
|
137
|
+
}
|
|
138
|
+
names = append(names, name)
|
|
139
|
+
return nil
|
|
140
|
+
})
|
|
141
|
+
if err != nil {
|
|
142
|
+
return nil, err
|
|
143
|
+
}
|
|
144
|
+
sort.Strings(names)
|
|
145
|
+
|
|
146
|
+
for _, name := range names {
|
|
147
|
+
node, err := parser.ParseFile(t.Fset, name, nil, parser.ParseComments)
|
|
148
|
+
if err != nil {
|
|
149
|
+
if !options.AllowPartial {
|
|
150
|
+
return nil, fmt.Errorf("parse %s: %w", name, err)
|
|
151
|
+
}
|
|
152
|
+
t.Diagnostics = append(t.Diagnostics, fmt.Sprintf("parse %s: %v", name, err))
|
|
153
|
+
continue
|
|
154
|
+
}
|
|
155
|
+
generated := IsGenerated(name, node)
|
|
156
|
+
if generated && !options.IncludeGenerated {
|
|
157
|
+
continue
|
|
158
|
+
}
|
|
159
|
+
rel, _ := filepath.Rel(root, name)
|
|
160
|
+
t.Files = append(t.Files, &File{
|
|
161
|
+
Name: filepath.ToSlash(rel),
|
|
162
|
+
Generated: generated,
|
|
163
|
+
Pkg: t.PackagePath(filepath.Dir(name)),
|
|
164
|
+
Imports: ImportsOf(node),
|
|
165
|
+
Node: node,
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
// Index actual local package names after all declarations are known.
|
|
169
|
+
namesByImport := map[string]string{}
|
|
170
|
+
for _, file := range t.Files {
|
|
171
|
+
namesByImport[file.Pkg] = file.Node.Name.Name
|
|
172
|
+
dir := filepath.ToSlash(filepath.Dir(file.Name))
|
|
173
|
+
t.ByDir[dir] = append(t.ByDir[dir], file)
|
|
174
|
+
}
|
|
175
|
+
for _, file := range t.Files {
|
|
176
|
+
for _, spec := range file.Node.Imports {
|
|
177
|
+
if spec.Name != nil {
|
|
178
|
+
continue
|
|
179
|
+
}
|
|
180
|
+
imported, _ := strconv.Unquote(spec.Path.Value)
|
|
181
|
+
if name := namesByImport[imported]; name != "" {
|
|
182
|
+
delete(file.Imports, path.Base(imported))
|
|
183
|
+
file.Imports[name] = imported
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
t.indexConstants()
|
|
188
|
+
|
|
189
|
+
return t, nil
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// PackagePath is the import path of the package in dir: the module path
|
|
193
|
+
// with the directory under it.
|
|
194
|
+
func (t *Tree) PackagePath(dir string) string {
|
|
195
|
+
rel, err := filepath.Rel(t.Root, dir)
|
|
196
|
+
if err != nil || rel == "." {
|
|
197
|
+
return t.Module
|
|
198
|
+
}
|
|
199
|
+
return strings.TrimSuffix(t.Module, "/") + "/" + filepath.ToSlash(rel)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// At is where a node is, relative to the root.
|
|
203
|
+
func (t *Tree) At(pos token.Pos) Source {
|
|
204
|
+
position := t.Fset.Position(pos)
|
|
205
|
+
rel, err := filepath.Rel(t.Root, position.Filename)
|
|
206
|
+
if err != nil {
|
|
207
|
+
rel = position.Filename
|
|
208
|
+
}
|
|
209
|
+
return Source{File: filepath.ToSlash(rel), Line: position.Line}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ModulePath is what go.mod under root calls the module; the cleaned root
|
|
213
|
+
// when there is no go.mod to ask.
|
|
214
|
+
func ModulePath(root string) string {
|
|
215
|
+
data, err := os.ReadFile(filepath.Join(root, "go.mod"))
|
|
216
|
+
if err == nil {
|
|
217
|
+
for _, line := range strings.Split(string(data), "\n") {
|
|
218
|
+
if value, ok := strings.CutPrefix(strings.TrimSpace(line), "module "); ok {
|
|
219
|
+
return strings.TrimSpace(value)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return filepath.ToSlash(filepath.Clean(root))
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ImportsOf is a file's imports by the name the file uses for them: the alias
|
|
227
|
+
// when there is one, the last path segment when there is not. Blank and dot
|
|
228
|
+
// imports keep the segment, since nothing in the file refers to them by name.
|
|
229
|
+
//
|
|
230
|
+
// A segment with a dot in it - `nats.go`, `yaml.v3` - is not a Go identifier,
|
|
231
|
+
// and the package behind it is called by what comes before the dot; that name
|
|
232
|
+
// is recorded too, since it is the one the file uses.
|
|
233
|
+
func ImportsOf(node *ast.File) map[string]string {
|
|
234
|
+
out := map[string]string{}
|
|
235
|
+
for _, spec := range node.Imports {
|
|
236
|
+
value, err := strconv.Unquote(spec.Path.Value)
|
|
237
|
+
if err != nil {
|
|
238
|
+
continue
|
|
239
|
+
}
|
|
240
|
+
name := path.Base(value)
|
|
241
|
+
if spec.Name != nil && spec.Name.Name != "_" && spec.Name.Name != "." {
|
|
242
|
+
name = spec.Name.Name
|
|
243
|
+
}
|
|
244
|
+
out[name] = value
|
|
245
|
+
if short, _, dotted := strings.Cut(name, "."); dotted && short != "" {
|
|
246
|
+
out[short] = value
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return out
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
func generatedName(name string) bool {
|
|
253
|
+
return strings.HasSuffix(name, ".gen.go") || strings.HasSuffix(name, "_generated.go") || strings.HasSuffix(name, ".generated.go")
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
func IsGenerated(name string, file *ast.File) bool {
|
|
257
|
+
return generatedName(name) || ast.IsGenerated(file)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// PackageFiles and PackageDirs use the same immutable source selection in
|
|
261
|
+
// every consumer; repeated lookups reuse the AST and token positions.
|
|
262
|
+
func (t *Tree) PackageFiles(dir string) []*File {
|
|
263
|
+
return t.ByDir[filepath.ToSlash(filepath.Clean(dir))]
|
|
264
|
+
}
|
|
265
|
+
func (t *Tree) PackageDirs(prefix string) []string {
|
|
266
|
+
prefix = filepath.ToSlash(filepath.Clean(prefix))
|
|
267
|
+
var dirs []string
|
|
268
|
+
for dir := range t.ByDir {
|
|
269
|
+
if prefix == "." || dir == prefix || strings.HasPrefix(dir, prefix+"/") {
|
|
270
|
+
dirs = append(dirs, dir)
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
sort.Strings(dirs)
|
|
274
|
+
return dirs
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// PackageIndex also serves small standalone readers and tests. Extractors
|
|
278
|
+
// should pass their request-scoped index to reuse parsing across passes.
|
|
279
|
+
func PackageIndex(root, dir string, indexes ...*Tree) (*Tree, error) {
|
|
280
|
+
if len(indexes) > 0 && indexes[0] != nil {
|
|
281
|
+
return indexes[0], nil
|
|
282
|
+
}
|
|
283
|
+
return ReadWithOptions(root, ReadOptions{IncludeGenerated: true, AllowPartial: true})
|
|
284
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
package goscan
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"go/ast"
|
|
6
|
+
"go/printer"
|
|
7
|
+
"reflect"
|
|
8
|
+
"strconv"
|
|
9
|
+
"strings"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
// Unwrap takes the parentheses, the address-of and the pointer off an
|
|
13
|
+
// expression, down to the thing itself.
|
|
14
|
+
func Unwrap(expr ast.Expr) ast.Expr {
|
|
15
|
+
for {
|
|
16
|
+
switch value := expr.(type) {
|
|
17
|
+
case *ast.ParenExpr:
|
|
18
|
+
expr = value.X
|
|
19
|
+
case *ast.UnaryExpr:
|
|
20
|
+
expr = value.X
|
|
21
|
+
case *ast.StarExpr:
|
|
22
|
+
expr = value.X
|
|
23
|
+
default:
|
|
24
|
+
return expr
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// TypeKey names a type the way the tree indexes types: "<import path>.<Name>"
|
|
30
|
+
// for a named type, whether written bare or through an import; the builtin's
|
|
31
|
+
// own name; "[]T" and "map[K]V" over their parts; "interface" for any
|
|
32
|
+
// interface literal. Empty for a type this reader does not name.
|
|
33
|
+
func (t *Tree) TypeKey(expr ast.Expr, file *File) string {
|
|
34
|
+
if expr == nil {
|
|
35
|
+
return ""
|
|
36
|
+
}
|
|
37
|
+
switch value := Unwrap(expr).(type) {
|
|
38
|
+
case *ast.Ident:
|
|
39
|
+
if IsBuiltin(value.Name) {
|
|
40
|
+
return value.Name
|
|
41
|
+
}
|
|
42
|
+
return file.Pkg + "." + value.Name
|
|
43
|
+
case *ast.SelectorExpr:
|
|
44
|
+
if base, ok := value.X.(*ast.Ident); ok {
|
|
45
|
+
return file.Imports[base.Name] + "." + value.Sel.Name
|
|
46
|
+
}
|
|
47
|
+
case *ast.ArrayType:
|
|
48
|
+
return "[]" + t.TypeKey(value.Elt, file)
|
|
49
|
+
case *ast.MapType:
|
|
50
|
+
return "map[" + t.TypeKey(value.Key, file) + "]" + t.TypeKey(value.Value, file)
|
|
51
|
+
case *ast.InterfaceType:
|
|
52
|
+
return "interface"
|
|
53
|
+
}
|
|
54
|
+
return ""
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// IsBuiltin is whether a bare identifier is one of the language's own types.
|
|
58
|
+
func IsBuiltin(name string) bool {
|
|
59
|
+
switch name {
|
|
60
|
+
case "string", "bool", "byte", "rune", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64", "error", "any":
|
|
61
|
+
return true
|
|
62
|
+
}
|
|
63
|
+
return false
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// FieldsOf lists a struct's named fields as "name type", the name being what
|
|
67
|
+
// the json tag says when it says anything, and a field tagged "-" left out:
|
|
68
|
+
// the fields as they travel, not as the code calls them.
|
|
69
|
+
func (t *Tree) FieldsOf(body *ast.StructType) []string {
|
|
70
|
+
var out []string
|
|
71
|
+
for _, field := range body.Fields.List {
|
|
72
|
+
if len(field.Names) == 0 {
|
|
73
|
+
continue
|
|
74
|
+
}
|
|
75
|
+
typeName := t.PrintNode(field.Type)
|
|
76
|
+
for _, ident := range field.Names {
|
|
77
|
+
name := ident.Name
|
|
78
|
+
if field.Tag != nil {
|
|
79
|
+
tagText, _ := strconv.Unquote(field.Tag.Value)
|
|
80
|
+
jsonName := strings.Split(reflect.StructTag(tagText).Get("json"), ",")[0]
|
|
81
|
+
if jsonName == "-" {
|
|
82
|
+
continue
|
|
83
|
+
}
|
|
84
|
+
if jsonName != "" {
|
|
85
|
+
name = jsonName
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
out = append(out, name+" "+typeName)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return out
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// PrintNode is a node as source text.
|
|
95
|
+
func (t *Tree) PrintNode(node ast.Node) string {
|
|
96
|
+
var out bytes.Buffer
|
|
97
|
+
_ = printer.Fprint(&out, t.Fset, node)
|
|
98
|
+
return out.String()
|
|
99
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
package wsdl
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"path/filepath"
|
|
5
|
+
"regexp"
|
|
6
|
+
"strconv"
|
|
7
|
+
"strings"
|
|
8
|
+
"unicode"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
var versionInSource = regexp.MustCompile(`(?i)(?:^|[_-])(\d+(?:\.\d+)+)(?:[_-]|\.wsdl$)`)
|
|
12
|
+
|
|
13
|
+
// APIID is the stable prefix shared by the contract extractor and call-site
|
|
14
|
+
// analyzer. WSDL has no standard version field, so the protocol is the suffix.
|
|
15
|
+
func APIID(contract Contract) string {
|
|
16
|
+
name := Slug(contract.Name)
|
|
17
|
+
if name == "" {
|
|
18
|
+
name = "soap"
|
|
19
|
+
}
|
|
20
|
+
return name + ".soap"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// APIIDs resolves the case WSDL itself cannot: two checked-in versions may
|
|
24
|
+
// repeat the same service, port and targetNamespace. Only colliding interface
|
|
25
|
+
// ids receive a source-backed version suffix, so an unrelated file rename does
|
|
26
|
+
// not churn stable ids.
|
|
27
|
+
func APIIDs(contracts []Contract) map[string]string {
|
|
28
|
+
bases := map[string]int{}
|
|
29
|
+
for _, contract := range contracts {
|
|
30
|
+
base := APIID(contract)
|
|
31
|
+
for _, iface := range contract.Interfaces {
|
|
32
|
+
bases[InterfaceID(base, iface)]++
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
out := map[string]string{}
|
|
36
|
+
for index, contract := range contracts {
|
|
37
|
+
base := APIID(contract)
|
|
38
|
+
collides := false
|
|
39
|
+
for _, iface := range contract.Interfaces {
|
|
40
|
+
if bases[InterfaceID(base, iface)] > 1 {
|
|
41
|
+
collides = true
|
|
42
|
+
break
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if collides {
|
|
46
|
+
version := sourceVersion(contract.Source)
|
|
47
|
+
if version == "" {
|
|
48
|
+
version = Slug(strings.TrimSuffix(filepath.Base(contract.Source), filepath.Ext(contract.Source)))
|
|
49
|
+
}
|
|
50
|
+
if version == "" {
|
|
51
|
+
version = "copy-" + strconv.Itoa(index+1)
|
|
52
|
+
}
|
|
53
|
+
base += ".v" + strings.ReplaceAll(strings.TrimPrefix(version, "v"), ".", "-")
|
|
54
|
+
}
|
|
55
|
+
out[ContractKey(contract)] = base
|
|
56
|
+
}
|
|
57
|
+
return out
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
func ContractKey(contract Contract) string {
|
|
61
|
+
return contract.Source + "\x00" + contract.Name
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
func sourceVersion(source string) string {
|
|
65
|
+
match := versionInSource.FindStringSubmatch(filepath.Base(source))
|
|
66
|
+
if len(match) > 1 {
|
|
67
|
+
return match[1]
|
|
68
|
+
}
|
|
69
|
+
return ""
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// InterfaceID keeps distinct WSDL ports distinct. A document commonly exposes
|
|
73
|
+
// SOAP 1.1 and 1.2 ports with the same operations but different bindings.
|
|
74
|
+
func InterfaceID(api string, iface Interface) string {
|
|
75
|
+
name := title(iface.Name)
|
|
76
|
+
if name == "" {
|
|
77
|
+
name = title(iface.PortType)
|
|
78
|
+
}
|
|
79
|
+
if name == "" {
|
|
80
|
+
return api
|
|
81
|
+
}
|
|
82
|
+
return api + "." + name
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
func CallID(api string, iface Interface, operation Operation) string {
|
|
86
|
+
return InterfaceID(api, iface) + "/" + operation.Name
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
func ExternalID(contract Contract) string {
|
|
90
|
+
id := Slug(contract.Name)
|
|
91
|
+
id = strings.TrimSuffix(id, "-service")
|
|
92
|
+
id = strings.TrimSuffix(id, "service")
|
|
93
|
+
id = strings.TrimSuffix(id, "-soap")
|
|
94
|
+
if id == "" {
|
|
95
|
+
return "soap-peer"
|
|
96
|
+
}
|
|
97
|
+
return id
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
func Slug(value string) string {
|
|
101
|
+
var out strings.Builder
|
|
102
|
+
dash := false
|
|
103
|
+
for _, r := range strings.ToLower(strings.TrimSpace(value)) {
|
|
104
|
+
if unicode.IsLetter(r) || unicode.IsDigit(r) {
|
|
105
|
+
out.WriteRune(r)
|
|
106
|
+
dash = false
|
|
107
|
+
} else if out.Len() > 0 && !dash {
|
|
108
|
+
out.WriteByte('-')
|
|
109
|
+
dash = true
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return strings.Trim(out.String(), "-")
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
func title(value string) string {
|
|
116
|
+
var out strings.Builder
|
|
117
|
+
for _, word := range strings.FieldsFunc(value, func(r rune) bool {
|
|
118
|
+
return r == '-' || r == '_' || unicode.IsSpace(r)
|
|
119
|
+
}) {
|
|
120
|
+
runes := []rune(word)
|
|
121
|
+
if len(runes) > 0 {
|
|
122
|
+
runes[0] = unicode.ToUpper(runes[0])
|
|
123
|
+
out.WriteString(string(runes))
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return out.String()
|
|
127
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
package wsdl
|
|
2
|
+
|
|
3
|
+
import "testing"
|
|
4
|
+
|
|
5
|
+
func TestAPIIDsOnlyVersionCollidingContracts(t *testing.T) {
|
|
6
|
+
contracts := []Contract{
|
|
7
|
+
{Name: "BookingService", Source: "booking_1.0_4.0.wsdl", Interfaces: []Interface{{Name: "BookingPort"}}},
|
|
8
|
+
{Name: "BookingService", Source: "booking_1.2_4.0.wsdl", Interfaces: []Interface{{Name: "BookingPort"}}},
|
|
9
|
+
{Name: "StatusService", Source: "status.wsdl", Interfaces: []Interface{{Name: "StatusPort"}}},
|
|
10
|
+
}
|
|
11
|
+
ids := APIIDs(contracts)
|
|
12
|
+
if got := ids[ContractKey(contracts[0])]; got != "bookingservice.soap.v1-0" {
|
|
13
|
+
t.Fatalf("v1 id = %q", got)
|
|
14
|
+
}
|
|
15
|
+
if got := ids[ContractKey(contracts[1])]; got != "bookingservice.soap.v1-2" {
|
|
16
|
+
t.Fatalf("v1.2 id = %q", got)
|
|
17
|
+
}
|
|
18
|
+
if got := ids[ContractKey(contracts[2])]; got != "statusservice.soap" {
|
|
19
|
+
t.Fatalf("unrelated id = %q", got)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Package wsdl reads local WSDL 1.1 contracts and the XML Schemas they reach.
|
|
2
|
+
//
|
|
3
|
+
// It deliberately performs no network access. A remote import is evidence that
|
|
4
|
+
// part of a contract is unavailable in the checkout, and is returned as a
|
|
5
|
+
// warning rather than fetched behind the caller's back.
|
|
6
|
+
package wsdl
|
|
7
|
+
|
|
8
|
+
// Contract is one WSDL service, or one standalone port type when the document
|
|
9
|
+
// does not declare a service.
|
|
10
|
+
type Contract struct {
|
|
11
|
+
Name string
|
|
12
|
+
Namespace string
|
|
13
|
+
Summary string
|
|
14
|
+
Source string
|
|
15
|
+
Interfaces []Interface
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Interface is a WSDL port bound to one SOAP binding. Keeping the port rather
|
|
19
|
+
// than flattening the whole document preserves distinct endpoints and SOAP
|
|
20
|
+
// versions when a service publishes more than one.
|
|
21
|
+
type Interface struct {
|
|
22
|
+
Name string
|
|
23
|
+
Binding string
|
|
24
|
+
PortType string
|
|
25
|
+
Endpoint string
|
|
26
|
+
Version string
|
|
27
|
+
Style string
|
|
28
|
+
Operations []Operation
|
|
29
|
+
Messages []Message
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Operation joins the abstract portType operation to its concrete SOAP
|
|
33
|
+
// binding. Request, response and faults name entries in Interface.Messages.
|
|
34
|
+
type Operation struct {
|
|
35
|
+
Name string
|
|
36
|
+
Action string
|
|
37
|
+
Doc string
|
|
38
|
+
Request string
|
|
39
|
+
Response string
|
|
40
|
+
Faults []string
|
|
41
|
+
Headers []string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Message is a WSDL message or an XSD complex type reachable from one.
|
|
45
|
+
type Message struct {
|
|
46
|
+
Name string
|
|
47
|
+
Fields []Field
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Field is the compact XML shape the catalog can display.
|
|
51
|
+
type Field struct {
|
|
52
|
+
Name string
|
|
53
|
+
Type string
|
|
54
|
+
Doc string
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Result is deterministic: contracts, interfaces, operations and messages are
|
|
58
|
+
// sorted by the parser before they leave the package.
|
|
59
|
+
type Result struct {
|
|
60
|
+
Contracts []Contract
|
|
61
|
+
// Warnings are what stopped a document from being read in full: a remote
|
|
62
|
+
// import, a port whose binding is not here. They matter to anyone reading
|
|
63
|
+
// the contracts, including a client extractor resolving calls against them.
|
|
64
|
+
Warnings []string
|
|
65
|
+
// SchemaWarnings are about the quality of the schemas themselves, such as
|
|
66
|
+
// one name declared twice in a namespace. Nothing failed to resolve; the
|
|
67
|
+
// loader kept the first and says so. They belong to whoever publishes the
|
|
68
|
+
// contracts, not to every extractor that happens to read them.
|
|
69
|
+
SchemaWarnings []string
|
|
70
|
+
}
|