diff --git a/clients/cmd/docker-driver/config.go b/clients/cmd/docker-driver/config.go index 8a25b9b78..80c84a247 100644 --- a/clients/cmd/docker-driver/config.go +++ b/clients/cmd/docker-driver/config.go @@ -372,8 +372,11 @@ func relabelConfig(config string, lbs model.LabelSet) (model.LabelSet, error) { return nil, err } } - relabed, _ := relabel.Process(labels.FromMap(util.ModelLabelSetToMap(lbs)), relabelConfig...) - return model.LabelSet(util.LabelsToMetric(relabed)), nil + lb := labels.NewBuilder(labels.FromMap(util.ModelLabelSetToMap(lbs))) + if keep := relabel.ProcessBuilder(lb, relabelConfig...); !keep { + return nil, nil + } + return model.LabelSet(util.LabelsToMetric(lb.Labels())), nil } func parseBoolean(key string, logCtx logger.Info, defaultValue bool) (bool, error) { diff --git a/clients/pkg/promtail/promtail_test.go b/clients/pkg/promtail/promtail_test.go index 91cfded4d..28c63fcc9 100644 --- a/clients/pkg/promtail/promtail_test.go +++ b/clients/pkg/promtail/promtail_test.go @@ -475,7 +475,7 @@ func (h *testServerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } h.recMtx.Lock() for _, s := range req.Streams { - parsedLabels, err := parser.ParseMetric(s.Labels) + parsedLabels, err := parser.NewParser(parser.Options{}).ParseMetric(s.Labels) if err != nil { h.t.Error("Failed to parse incoming labels", err) return diff --git a/clients/pkg/promtail/targets/azureeventhubs/parser.go b/clients/pkg/promtail/targets/azureeventhubs/parser.go index 530a3d7f2..0e8b3a4d1 100644 --- a/clients/pkg/promtail/targets/azureeventhubs/parser.go +++ b/clients/pkg/promtail/targets/azureeventhubs/parser.go @@ -200,12 +200,13 @@ func (e *messageParser) getLabels(logRecord *azureMonitorResourceLog, relabelCon Value: logRecord.Category, }) - var processed labels.Labels + lb := labels.NewBuilder(lbs) if len(relabelConfig) > 0 { - processed, _ = relabel.Process(lbs, relabelConfig...) - } else { - processed = lbs + if keep := relabel.ProcessBuilder(lb, relabelConfig...); !keep { + return nil + } } + processed := lb.Labels() // final labelset that will be sent to loki resultLabels := make(model.LabelSet) diff --git a/clients/pkg/promtail/targets/docker/target.go b/clients/pkg/promtail/targets/docker/target.go index 10293b915..f6baf72c6 100644 --- a/clients/pkg/promtail/targets/docker/target.go +++ b/clients/pkg/promtail/targets/docker/target.go @@ -244,7 +244,10 @@ func (t *Target) handleOutput(logStream string, ts time.Time, payload string) { lb.Set(string(k), string(v)) } lb.Set(dockerLabelLogStream, logStream) - processed, _ := relabel.Process(lb.Labels(), t.relabelConfig...) + if keep := relabel.ProcessBuilder(lb, t.relabelConfig...); !keep { + return + } + processed := lb.Labels() filtered := make(model.LabelSet) processed.Range(func(lbl labels.Label) { diff --git a/clients/pkg/promtail/targets/file/filetargetmanager.go b/clients/pkg/promtail/targets/file/filetargetmanager.go index 36ca6d73e..3d93feb43 100644 --- a/clients/pkg/promtail/targets/file/filetargetmanager.go +++ b/clients/pkg/promtail/targets/file/filetargetmanager.go @@ -318,7 +318,9 @@ func (s *targetSyncer) sync(groups []*targetgroup.Group, targetEventHandler chan labelMap[string(k)] = string(v) } - processedLabels, keep := relabel.Process(labels.FromMap(labelMap), s.relabelConfig...) + lb := labels.NewBuilder(labels.FromMap(labelMap)) + keep := relabel.ProcessBuilder(lb, s.relabelConfig...) + processedLabels := lb.Labels() var labels = make(model.LabelSet) for k, v := range processedLabels.Map() { diff --git a/clients/pkg/promtail/targets/gcplog/formatter.go b/clients/pkg/promtail/targets/gcplog/formatter.go index 52ef51485..0f5679882 100644 --- a/clients/pkg/promtail/targets/gcplog/formatter.go +++ b/clients/pkg/promtail/targets/gcplog/formatter.go @@ -1,6 +1,7 @@ package gcplog import ( + "errors" "fmt" "strings" "time" @@ -15,6 +16,9 @@ import ( "github.com/grafana/loki/v3/pkg/logproto" ) +// errEntryDropped is returned when a log entry is dropped by relabeling rules. +var errEntryDropped = errors.New("entry dropped by relabeling") + // GCPLogEntry that will be written to the pubsub topic. // According to the following spec. // https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry @@ -70,12 +74,12 @@ func parseGCPLogsEntry(data []byte, other model.LabelSet, otherInternal labels.L lbs.Set("__gcp_labels_"+convertToLokiCompatibleLabel(k), v) } - var processed labels.Labels if len(relabelConfig) > 0 { - processed, _ = relabel.Process(lbs.Labels(), relabelConfig...) - } else { - processed = lbs.Labels() + if keep := relabel.ProcessBuilder(lbs, relabelConfig...); !keep { + return api.Entry{}, errEntryDropped + } } + processed := lbs.Labels() // final labelset that will be sent to loki final := make(model.LabelSet) diff --git a/clients/pkg/promtail/targets/gcplog/pull_target.go b/clients/pkg/promtail/targets/gcplog/pull_target.go index 180219a6f..ee88fc139 100644 --- a/clients/pkg/promtail/targets/gcplog/pull_target.go +++ b/clients/pkg/promtail/targets/gcplog/pull_target.go @@ -2,6 +2,7 @@ package gcplog import ( "context" + "errors" "fmt" "io" "sync" @@ -112,6 +113,10 @@ func (t *pullTarget) run() error { case m := <-t.msgs: entry, err := parseGCPLogsEntry(m.Data, t.config.Labels, labels.EmptyLabels(), t.config.UseIncomingTimestamp, t.config.UseFullLine, t.relabelConfig) if err != nil { + if errors.Is(err, errEntryDropped) { + m.Ack() + break + } level.Error(t.logger).Log("event", "error formating log entry", "cause", err) m.Ack() break diff --git a/clients/pkg/promtail/targets/gcplog/push_target.go b/clients/pkg/promtail/targets/gcplog/push_target.go index 28c3c46f1..4295d439a 100644 --- a/clients/pkg/promtail/targets/gcplog/push_target.go +++ b/clients/pkg/promtail/targets/gcplog/push_target.go @@ -3,6 +3,7 @@ package gcplog import ( "context" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -134,6 +135,10 @@ func (h *pushTarget) push(w http.ResponseWriter, r *http.Request) { entry, err := translate(pushMessage, h.config.Labels, h.config.UseIncomingTimestamp, h.config.UseFullLine, h.relabelConfigs, r.Header.Get("X-Scope-OrgID")) if err != nil { + if errors.Is(err, errEntryDropped) { + w.WriteHeader(http.StatusNoContent) + return + } h.metrics.gcpPushErrors.WithLabelValues("translation").Inc() level.Warn(h.logger).Log("msg", "failed to translate gcp push request", "err", err.Error()) http.Error(w, err.Error(), http.StatusBadRequest) diff --git a/clients/pkg/promtail/targets/gelf/gelftarget.go b/clients/pkg/promtail/targets/gelf/gelftarget.go index e395e2880..7d2e31bca 100644 --- a/clients/pkg/promtail/targets/gelf/gelftarget.go +++ b/clients/pkg/promtail/targets/gelf/gelftarget.go @@ -122,12 +122,12 @@ func (t *Target) handleMessage(msg *gelf.Message) { lb.Set("__gelf_message_version", msg.Version) lb.Set("__gelf_message_facility", msg.Facility) - var processed labels.Labels if len(t.relabelConfig) > 0 { - processed, _ = relabel.Process(lb.Labels(), t.relabelConfig...) - } else { - processed = lb.Labels() + if keep := relabel.ProcessBuilder(lb, t.relabelConfig...); !keep { + return + } } + processed := lb.Labels() filtered := make(model.LabelSet) processed.Range(func(lbl labels.Label) { diff --git a/clients/pkg/promtail/targets/heroku/target.go b/clients/pkg/promtail/targets/heroku/target.go index 75912a295..cb81821da 100644 --- a/clients/pkg/promtail/targets/heroku/target.go +++ b/clients/pkg/promtail/targets/heroku/target.go @@ -128,12 +128,12 @@ func (h *Target) drain(w http.ResponseWriter, r *http.Request) { lb.Set(lokiClient.ReservedLabelTenantID, tenantIDHeaderValue) } - var processed labels.Labels if len(h.relabelConfigs) > 0 { - processed, _ = relabel.Process(lb.Labels(), h.relabelConfigs...) - } else { - processed = lb.Labels() + if keep := relabel.ProcessBuilder(lb, h.relabelConfigs...); !keep { + continue + } } + processed := lb.Labels() // Start with the set of labels fixed in the configuration filtered := h.Labels().Clone() diff --git a/clients/pkg/promtail/targets/journal/journaltarget.go b/clients/pkg/promtail/targets/journal/journaltarget.go index fa04ac50c..3ed94a261 100644 --- a/clients/pkg/promtail/targets/journal/journaltarget.go +++ b/clients/pkg/promtail/targets/journal/journaltarget.go @@ -312,7 +312,11 @@ func (t *JournalTarget) formatter(entry *sdjournal.JournalEntry) (string, error) entryLabels[string(k)] = string(v) } - processedLabels, _ := relabel.Process(labels.FromMap(entryLabels), t.relabelConfig...) + lb := labels.NewBuilder(labels.FromMap(entryLabels)) + if keep := relabel.ProcessBuilder(lb, t.relabelConfig...); !keep { + return journalEmptyStr, nil + } + processedLabels := lb.Labels() processedLabelsMap := processedLabels.Map() labels := make(model.LabelSet, len(processedLabelsMap)) diff --git a/clients/pkg/promtail/targets/kafka/formatter.go b/clients/pkg/promtail/targets/kafka/formatter.go index 5cbf7817a..8cd1efbed 100644 --- a/clients/pkg/promtail/targets/kafka/formatter.go +++ b/clients/pkg/promtail/targets/kafka/formatter.go @@ -14,12 +14,13 @@ func format(lbs labels.Labels, cfg []*relabel.Config) model.LabelSet { if lbs.IsEmpty() { return nil } - var processed labels.Labels + lb := labels.NewBuilder(lbs) if len(cfg) > 0 { - processed, _ = relabel.Process(lbs, cfg...) - } else { - processed = lbs + if keep := relabel.ProcessBuilder(lb, cfg...); !keep { + return nil + } } + processed := lb.Labels() labelOut := model.LabelSet(util.LabelsToMetric(processed)) for k := range labelOut { if strings.HasPrefix(string(k), "__") { diff --git a/clients/pkg/promtail/targets/lokipush/pushtarget.go b/clients/pkg/promtail/targets/lokipush/pushtarget.go index 8ace3a807..45b12ca02 100644 --- a/clients/pkg/promtail/targets/lokipush/pushtarget.go +++ b/clients/pkg/promtail/targets/lokipush/pushtarget.go @@ -123,7 +123,7 @@ func (t *PushTarget) handleLoki(w http.ResponseWriter, r *http.Request) { } var lastErr error for _, stream := range req.Streams { - ls, err := promql_parser.ParseMetric(stream.Labels) + ls, err := promql_parser.NewParser(promql_parser.Options{}).ParseMetric(stream.Labels) if err != nil { lastErr = err continue @@ -137,7 +137,8 @@ func (t *PushTarget) handleLoki(w http.ResponseWriter, r *http.Request) { } // Apply relabeling - processed, keep := relabel.Process(lb.Labels(), t.relabelConfig...) + keep := relabel.ProcessBuilder(lb, t.relabelConfig...) + processed := lb.Labels() if !keep || processed.IsEmpty() { w.WriteHeader(http.StatusNoContent) return diff --git a/clients/pkg/promtail/targets/syslog/syslogtarget.go b/clients/pkg/promtail/targets/syslog/syslogtarget.go index f5523b675..3e254d32c 100644 --- a/clients/pkg/promtail/targets/syslog/syslogtarget.go +++ b/clients/pkg/promtail/targets/syslog/syslogtarget.go @@ -149,12 +149,12 @@ func (t *SyslogTarget) handleMessageRFC5424(connLabels labels.Labels, msg syslog } } - var processed labels.Labels if len(t.relabelConfig) > 0 { - processed, _ = relabel.Process(lb.Labels(), t.relabelConfig...) - } else { - processed = lb.Labels() + if keep := relabel.ProcessBuilder(lb, t.relabelConfig...); !keep { + return + } } + processed := lb.Labels() filtered := make(model.LabelSet) processed.Range(func(lbl labels.Label) { @@ -211,12 +211,12 @@ func (t *SyslogTarget) handleMessageRFC3164(connLabels labels.Labels, msg syslog lb.Set("__syslog_message_msg_id", *v) } - var processed labels.Labels if len(t.relabelConfig) > 0 { - processed, _ = relabel.Process(lb.Labels(), t.relabelConfig...) - } else { - processed = lb.Labels() + if keep := relabel.ProcessBuilder(lb, t.relabelConfig...); !keep { + return + } } + processed := lb.Labels() filtered := make(model.LabelSet) processed.Range(func(lbl labels.Label) { diff --git a/clients/pkg/promtail/targets/windows/target.go b/clients/pkg/promtail/targets/windows/target.go index 44bffa293..15fa89716 100644 --- a/clients/pkg/promtail/targets/windows/target.go +++ b/clients/pkg/promtail/targets/windows/target.go @@ -141,8 +141,8 @@ func (t *Target) loop() { // renderEntries renders Loki entries from windows event logs func (t *Target) renderEntries(events []win_eventlog.Event) []api.Entry { res := make([]api.Entry, 0, len(events)) - lbs := labels.NewBuilder(labels.EmptyLabels()) for _, event := range events { + lbs := labels.NewBuilder(labels.EmptyLabels()) entry := api.Entry{ Labels: make(model.LabelSet), } @@ -168,7 +168,10 @@ func (t *Target) renderEntries(events []win_eventlog.Event) []api.Entry { lbs.Set("computer", event.Computer) } // apply relabelings. - processed, _ := relabel.Process(lbs.Labels(), t.relabelConfig...) + if keep := relabel.ProcessBuilder(lbs, t.relabelConfig...); !keep { + continue + } + processed := lbs.Labels() processed.Range(func(lbl labels.Label) { if strings.HasPrefix(lbl.Name, "__") { diff --git a/go.mod b/go.mod index bc9780c92..511be084f 100644 --- a/go.mod +++ b/go.mod @@ -86,7 +86,7 @@ require ( github.com/prometheus/client_golang v1.23.2 github.com/prometheus/client_model v0.6.2 github.com/prometheus/common v0.67.5 - github.com/prometheus/prometheus v0.309.1 + github.com/prometheus/prometheus v0.309.2-0.20260219114400-b2e63376da74 github.com/redis/go-redis/v9 v9.17.3 github.com/segmentio/fasthash v1.0.3 github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c @@ -104,10 +104,10 @@ require ( golang.org/x/sync v0.19.0 golang.org/x/sys v0.41.0 golang.org/x/time v0.14.0 - google.golang.org/api v0.257.0 + google.golang.org/api v0.265.0 google.golang.org/grpc v1.79.1 gopkg.in/yaml.v2 v2.4.0 - gopkg.in/yaml.v3 v3.0.1 + gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.130.1 ) @@ -160,6 +160,7 @@ require ( go.opentelemetry.io/collector/pdata v1.51.0 go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.65.0 go.opentelemetry.io/otel/sdk v1.40.0 + go.yaml.in/yaml/v3 v3.0.4 go4.org/netipx v0.0.0-20230125063823-8449b0a6169f golang.org/x/oauth2 v0.35.0 golang.org/x/text v0.34.0 @@ -172,7 +173,7 @@ require ( require ( cel.dev/expr v0.25.1 // indirect - cloud.google.com/go/auth v0.18.0 // indirect + cloud.google.com/go/auth v0.18.1 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/monitoring v1.24.3 // indirect filippo.io/edwards25519 v1.1.0 // indirect @@ -185,16 +186,19 @@ require ( github.com/aws/aws-sdk-go v1.55.7 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17 // indirect - github.com/aws/aws-sdk-go-v2/service/ec2 v1.279.0 // indirect - github.com/aws/aws-sdk-go-v2/service/ecs v1.69.5 // indirect + github.com/aws/aws-sdk-go-v2/service/ec2 v1.285.0 // indirect + github.com/aws/aws-sdk-go-v2/service/ecs v1.71.0 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8 // indirect github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.11.17 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17 // indirect - github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.10 // indirect + github.com/aws/aws-sdk-go-v2/service/kafka v1.46.7 // indirect + github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.11 // indirect github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/bahlo/generic-list-go v0.2.0 // indirect + github.com/basgys/goxml2json v1.1.1-0.20231018121955-e66ee54ceaad // indirect github.com/bits-and-blooms/bitset v1.24.4 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/charmbracelet/colorprofile v0.4.1 // indirect @@ -227,14 +231,14 @@ require ( github.com/go-openapi/swag/stringutils v0.25.4 // indirect github.com/go-openapi/swag/typeutils v0.25.4 // indirect github.com/go-openapi/swag/yamlutils v0.25.4 // indirect - github.com/go-viper/mapstructure/v2 v2.4.0 // indirect + github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/google/flatbuffers v25.12.19+incompatible // indirect github.com/gophercloud/gophercloud/v2 v2.10.0 // indirect github.com/gorilla/handlers v1.5.2 // indirect github.com/grafana/otel-profiling-go v0.5.1 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect github.com/hashicorp/go-metrics v0.5.4 // indirect github.com/hashicorp/go-msgpack/v2 v2.1.5 // indirect github.com/hashicorp/go-version v1.8.0 // indirect @@ -245,7 +249,7 @@ require ( github.com/klauspost/crc32 v1.3.0 // indirect github.com/knadh/koanf/maps v0.1.2 // indirect github.com/knadh/koanf/providers/confmap v1.0.0 // indirect - github.com/knadh/koanf/v2 v2.3.0 // indirect + github.com/knadh/koanf/v2 v2.3.2 // indirect github.com/lucasb-eyer/go-colorful v1.3.0 // indirect github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect github.com/mattn/go-localereader v0.0.1 // indirect @@ -265,22 +269,27 @@ require ( github.com/muesli/termenv v0.16.0 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/ncw/swift v1.0.53 // indirect - github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.142.0 // indirect - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.142.0 // indirect - github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.142.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.145.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.145.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.145.0 // indirect github.com/oschwald/maxminddb-golang/v2 v2.1.1 // indirect github.com/parquet-go/bitpack v1.0.0 // indirect github.com/parquet-go/jsonlite v1.0.0 // indirect + github.com/pb33f/jsonpath v0.7.1 // indirect + github.com/pb33f/libopenapi v0.33.4 // indirect + github.com/pb33f/libopenapi-validator v0.11.1 // indirect + github.com/pb33f/ordered-map/v2 v2.3.0 // indirect github.com/philhofer/fwd v1.2.0 // indirect github.com/pires/go-proxyproto v0.8.1 // indirect github.com/pkg/xattr v0.4.12 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/prometheus/client_golang/exp v0.0.0-20251212205219-7ba246a648ca // indirect + github.com/prometheus/client_golang/exp v0.0.0-20260108101519-fb0838f53562 // indirect github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/sahilm/fuzzy v0.1.1 // indirect + github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect github.com/sercand/kuberesolver/v6 v6.0.1 // indirect github.com/sethvargo/go-retry v0.3.0 // indirect github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect @@ -294,13 +303,14 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/collector/component v1.48.0 // indirect - go.opentelemetry.io/collector/confmap v1.48.0 // indirect - go.opentelemetry.io/collector/confmap/xconfmap v0.142.0 // indirect - go.opentelemetry.io/collector/consumer v1.48.0 // indirect + go.opentelemetry.io/collector/component v1.51.0 // indirect + go.opentelemetry.io/collector/confmap v1.51.0 // indirect + go.opentelemetry.io/collector/confmap/xconfmap v0.145.0 // indirect + go.opentelemetry.io/collector/consumer v1.51.0 // indirect go.opentelemetry.io/collector/featuregate v1.51.0 // indirect - go.opentelemetry.io/collector/pipeline v1.48.0 // indirect - go.opentelemetry.io/collector/processor v1.48.0 // indirect + go.opentelemetry.io/collector/internal/componentalias v0.145.0 // indirect + go.opentelemetry.io/collector/pipeline v1.51.0 // indirect + go.opentelemetry.io/collector/processor v1.51.0 // indirect go.opentelemetry.io/contrib/bridges/prometheus v0.64.0 // indirect go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect go.opentelemetry.io/contrib/exporters/autoexport v0.64.0 // indirect @@ -311,9 +321,9 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.15.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.39.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 // indirect go.opentelemetry.io/otel/exporters/prometheus v0.61.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.15.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.39.0 // indirect @@ -323,8 +333,8 @@ require ( go.opentelemetry.io/otel/sdk/metric v1.40.0 // indirect go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/exp v0.0.0-20251209150349-8475f28825e9 // indirect + go.yaml.in/yaml/v4 v4.0.0-rc.4 // indirect + golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect @@ -341,13 +351,13 @@ require ( cloud.google.com/go/compute/metadata v0.9.0 // indirect cloud.google.com/go/iam v1.5.3 // indirect cloud.google.com/go/longrunning v0.7.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.7.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4 v4.3.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1 // indirect - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect @@ -378,7 +388,7 @@ require ( github.com/dennwc/varint v1.0.0 // indirect github.com/dgryski/go-metro v0.0.0-20250106013310-edb8663e5e33 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/digitalocean/godo v1.171.0 // indirect + github.com/digitalocean/godo v1.173.0 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/go-connections v0.6.0 // indirect @@ -409,14 +419,14 @@ require ( github.com/go-zookeeper/zk v1.0.4 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect - github.com/golang-jwt/jwt/v5 v5.3.0 // indirect + github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/gnostic-models v0.7.0 // indirect - github.com/google/go-querystring v1.1.0 // indirect - github.com/google/pprof v0.0.0-20251213031049-b05bdaca462f // indirect + github.com/google/go-querystring v1.2.0 // indirect + github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef // indirect github.com/google/s2a-go v0.1.9 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.7 // indirect - github.com/googleapis/gax-go/v2 v2.15.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.11 // indirect + github.com/googleapis/gax-go/v2 v2.16.0 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect @@ -443,14 +453,14 @@ require ( github.com/leodido/ragel-machinery v0.0.0-20190525184631-5f46317e436b // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/miekg/dns v1.1.69 // indirect + github.com/miekg/dns v1.1.72 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/moby/term v0.5.0 // indirect + github.com/moby/term v0.5.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/morikuni/aec v1.0.0 // indirect + github.com/morikuni/aec v1.1.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect @@ -476,7 +486,6 @@ require ( go.etcd.io/etcd/client/v3 v3.6.7 // indirect go.mongodb.org/mongo-driver v1.17.6 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/collector/semconv v0.128.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 go.opentelemetry.io/otel v1.40.0 @@ -487,8 +496,8 @@ require ( golang.org/x/mod v0.33.0 // indirect golang.org/x/term v0.40.0 // indirect golang.org/x/tools v0.42.0 - google.golang.org/genproto v0.0.0-20250922171735-9219d122eba9 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20251213004720-97cd9d5aeac2 // indirect + google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/go.sum b/go.sum index 8f6caab7c..9425f1b0a 100644 --- a/go.sum +++ b/go.sum @@ -17,8 +17,8 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= -cloud.google.com/go/auth v0.18.0 h1:wnqy5hrv7p3k7cShwAU/Br3nzod7fxoqG+k0VZ+/Pk0= -cloud.google.com/go/auth v0.18.0/go.mod h1:wwkPM1AgE1f2u6dG443MiWoD8C3BtOywNsUMcUTVDRo= +cloud.google.com/go/auth v0.18.1 h1:IwTEx92GFUo2pJ6Qea0EU3zYvKnTAeRCODxfA/G5UWs= +cloud.google.com/go/auth v0.18.1/go.mod h1:GfTYoS9G3CWpRA3Va9doKN9mjPGRS+v41jmZAhBzbrA= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= @@ -35,8 +35,8 @@ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7 cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc= cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU= -cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc= -cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA= +cloud.google.com/go/logging v1.13.1 h1:O7LvmO0kGLaHY/gq8cV7T0dyp6zJhYAOtZPX4TF3QtY= +cloud.google.com/go/logging v1.13.1/go.mod h1:XAQkfkMBxQRjQek96WLPNze7vsOmay9H5PqfsNYDqvw= cloud.google.com/go/longrunning v0.7.0 h1:FV0+SYF1RIj59gyoWDRi45GiYUMM3K1qO51qoboQT1E= cloud.google.com/go/longrunning v0.7.0/go.mod h1:ySn2yXmjbK9Ba0zsQqunhDkYi0+9rlXIwnoAf+h+TPY= cloud.google.com/go/monitoring v1.24.3 h1:dde+gMNc0UhPZD1Azu6at2e79bfdztVDS5lvhOdsgaE= @@ -54,8 +54,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.59.2 h1:gmOAuG1opU8YvycMNpP+DvHfT9BfzzK5Cy+arP+Nocw= cloud.google.com/go/storage v1.59.2/go.mod h1:cMWbtM+anpC74gn6qjLh+exqYcfmB9Hqe5z6adx+CLI= -cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4= -cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI= +cloud.google.com/go/trace v1.11.7 h1:kDNDX8JkaAG3R2nq1lIdkb7FCSi1rCmsEtKVsty7p+U= +cloud.google.com/go/trace v1.11.7/go.mod h1:TNn9d5V3fQVf6s4SCveVMIBS2LJUqo73GACmq/Tky0s= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -66,8 +66,8 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U= github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 h1:fou+2+WFTib47nS+nz/ozhEBnvU96bKHy6LjRsY4E28= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0/go.mod h1:t76Ruy8AHvUAC8GfMWJMa0ElSbuIcO03NLpynfbgsPA= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= @@ -86,8 +86,8 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.0/go.mod h1:DWAciXemNf++PQJLeXUB4HHH5OpsAh12HZnu2wXE1jA= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1 h1:lhZdRq7TIx0GJQvSyX2Si406vrYsov2FXGp/RnSEtcs= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1/go.mod h1:8cl44BDmi+effbARHMQjgOKA2AYvcohNm7KEt42mSV8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= @@ -210,10 +210,10 @@ github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17 h1:JqcdRG//czea7Ppjb+g/n4o8i/R github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17/go.mod h1:CO+WeGmIdj/MlPel2KwID9Gt7CNq4M65HUfBW97liM0= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.55.0 h1:CyYoeHWjVSGimzMhlL0Z4l5gLCa++ccnRJKrsaNssxE= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.55.0/go.mod h1:ctEsEHY2vFQc6i4KU07q4n68v7BAmTbujv2Y+z8+hQY= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.279.0 h1:o7eJKe6VYAnqERPlLAvDW5VKXV6eTKv1oxTpMoDP378= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.279.0/go.mod h1:Wg68QRgy2gEGGdmTPU/UbVpdv8sM14bUZmF64KFwAsY= -github.com/aws/aws-sdk-go-v2/service/ecs v1.69.5 h1:5nkhwt0d/gjuT3AQ2LUK0aFRNB3MGlzB2elqy/ZsKP4= -github.com/aws/aws-sdk-go-v2/service/ecs v1.69.5/go.mod h1:LQMlcWBoiFVD3vUVEz42ST0yTiaDujv2dRE6sXt1yPE= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.285.0 h1:cRZQsqCy59DSJmvmUYzi9K+dutysXzfx6F+fkcIHtOk= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.285.0/go.mod h1:Uy+C+Sc58jozdoL1McQr8bDsEvNFx+/nBY+vpO1HVUY= +github.com/aws/aws-sdk-go-v2/service/ecs v1.71.0 h1:MzP/ElwTpINq+hS80ZQz4epKVnUTlz8Sz+P/AFORCKM= +github.com/aws/aws-sdk-go-v2/service/ecs v1.71.0/go.mod h1:pMlGFDpHoLTJOIZHGdJOAWmi+xeIlQXuFTuQxs1epYE= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8 h1:Z5EiPIzXKewUQK0QTMkutjiaPVeVYXX7KIqhXu/0fXs= @@ -224,8 +224,10 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 h1:RuNSMooz github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17/go.mod h1:F2xxQ9TZz5gDWsclCtPQscGpP0VUOc8RqgFM3vDENmU= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17 h1:bGeHBsGZx0Dvu/eJC0Lh9adJa3M1xREcndxLNZlve2U= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17/go.mod h1:dcW24lbU0CzHusTE8LLHhRLI42ejmINN8Lcr22bwh/g= -github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.10 h1:MQuZZ6Tq1qQabPlkVxrCMdyVl70Ogl4AERZKo+y9Wzo= -github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.10/go.mod h1:U5C3JME1ibKESmpzBAqlRpTYZfVbTqrb5ICJm+sVVd8= +github.com/aws/aws-sdk-go-v2/service/kafka v1.46.7 h1:0jDb9b505gbCmtjH1RT7kx8hDbVDzOhnTeZm7dzskpQ= +github.com/aws/aws-sdk-go-v2/service/kafka v1.46.7/go.mod h1:tWnHS64fg5ydLHivFlCAtEh/1iMNzr56QsH3F+UTwD4= +github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.11 h1:VM5e5M39zRSs+aT0O9SoxHjUXqXxhbw3Yi0FdMQWPIc= +github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.11/go.mod h1:0jvzYPIQGCpnY/dmdaotTk2JH4QuBlnW0oeyrcGLWJ4= github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0 h1:oeu8VPlOre74lBA/PMhxa5vewaMIMmILM+RraSyB8KA= github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0/go.mod h1:5jggDlZ2CLQhwJBiZJb4vfk4f0GxWdEDruWKEJ1xOdo= github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 h1:VrhDvQib/i0lxvr3zqlUwLwJP4fpmpyD9wYG1vfSu+Y= @@ -244,14 +246,20 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY= github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E= +github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/baidubce/bce-sdk-go v0.9.260 h1:1v1+2GTP+NGK3L24rJ+bnoiTaDaIy2YoaUM+ot2GTcw= github.com/baidubce/bce-sdk-go v0.9.260/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg= +github.com/basgys/goxml2json v1.1.1-0.20231018121955-e66ee54ceaad h1:3swAvbzgfaI6nKuDDU7BiKfZRdF+h2ZwKgMHd8Ha4t8= +github.com/basgys/goxml2json v1.1.1-0.20231018121955-e66ee54ceaad/go.mod h1:9+nBLYNWkvPcq9ep0owWUsPTLgL9ZXTsZWcCSVGGLJ0= github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 h1:6df1vn4bBlDDo4tARvBm7l6KA9iVMnE3NWizDeWSrps= github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3/go.mod h1:CIWtjkly68+yqLPbvwwR/fjNJA/idrtULjZWh2v1ys0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow= +github.com/bitly/go-simplejson v0.5.1/go.mod h1:YOPVLzCfwK14b4Sff3oP1AmGhI9T9Vsg84etUnlyp+Q= github.com/bits-and-blooms/bitset v1.24.2/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bits-and-blooms/bitset v1.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE= github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= @@ -350,8 +358,8 @@ github.com/dgryski/go-metro v0.0.0-20250106013310-edb8663e5e33 h1:ucRHb6/lvW/+mT github.com/dgryski/go-metro v0.0.0-20250106013310-edb8663e5e33/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/digitalocean/godo v1.171.0 h1:QwpkwWKr3v7yxc8D4NQG973NoR9APCEWjYnLOQeXVpQ= -github.com/digitalocean/godo v1.171.0/go.mod h1:xQsWpVCCbkDrWisHA72hPzPlnC+4W5w/McZY5ij9uvU= +github.com/digitalocean/godo v1.173.0 h1:tgzevGhlz9VFjk2y3NmeItUT4vIVVCRFETlG/1GlEQI= +github.com/digitalocean/godo v1.173.0/go.mod h1:xQsWpVCCbkDrWisHA72hPzPlnC+4W5w/McZY5ij9uvU= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= @@ -520,8 +528,8 @@ github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI6 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= -github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= +github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-zookeeper/zk v1.0.4 h1:DPzxraQx7OrPyXq2phlGlNSIyWEsAox0RJmjTseMV6I= github.com/go-zookeeper/zk v1.0.4/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= @@ -547,8 +555,8 @@ github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= -github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -596,15 +604,14 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0= +github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= @@ -622,8 +629,8 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= -github.com/google/pprof v0.0.0-20251213031049-b05bdaca462f h1:HU1RgM6NALf/KW9HEY6zry3ADbDKcmpQ+hJedoNGQYQ= -github.com/google/pprof v0.0.0-20251213031049-b05bdaca462f/go.mod h1:67FPmZWbr+KDT/VlpWtw6sO9XSjpJmLuHpoLmWiTGgY= +github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef h1:xpF9fUHpoIrrjX24DURVKiwHcFpw19ndIs+FwTSMbno= +github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio/v2 v2.0.2 h1:qKZs+tfn+arruZZhQ7TKC/ergJunuJicWS6gLDt/dGw= github.com/google/renameio/v2 v2.0.2/go.mod h1:OX+G6WHHpHq3NVj7cAOleLOwJfcQ1s3uUJQCrr78SWo= @@ -633,12 +640,12 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.7 h1:zrn2Ee/nWmHulBx5sAVrGgAa0f2/R35S4DJwfFaUPFQ= -github.com/googleapis/enterprise-certificate-proxy v0.3.7/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= +github.com/googleapis/enterprise-certificate-proxy v0.3.11 h1:vAe81Msw+8tKUxi2Dqh/NZMz7475yUvmRIkXr4oN2ao= +github.com/googleapis/enterprise-certificate-proxy v0.3.11/go.mod h1:RFV7MUdlb7AgEq2v7FmMCfeSMCllAzWxFgRdusoGks8= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= -github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= +github.com/googleapis/gax-go/v2 v2.16.0 h1:iHbQmKLLZrexmb0OSsNGTeSTS0HO4YvFOG8g5E4Zd0Y= +github.com/googleapis/gax-go/v2 v2.16.0/go.mod h1:o1vfQjjNZn4+dPnRdl/4ZD7S9414Y4xA+a/6Icj6l14= github.com/gophercloud/gophercloud/v2 v2.10.0 h1:NRadC0aHNvy4iMoFXj5AFiPmut/Sj3hAPAo9B59VMGc= github.com/gophercloud/gophercloud/v2 v2.10.0/go.mod h1:Ki/ILhYZr/5EPebrPL9Ej+tUg4lqx71/YH2JWVeU+Qk= github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= @@ -673,8 +680,8 @@ github.com/grafana/tail v0.0.0-20230510142333-77b18831edf0 h1:bjh0PVYSVVFxzINqPF github.com/grafana/tail v0.0.0-20230510142333-77b18831edf0/go.mod h1:7t5XR+2IA8P2qggOAHTj/GCZfoLBle3OvNSYh1VkRBU= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajRPKYMrB7E0MbtzWVi1K4ns= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII= github.com/hashicorp/consul/api v1.33.2 h1:Q6mE0WZsUTJerlnl9TuXzqrtZ0cKdOCsxcZhj5mKbMs= github.com/hashicorp/consul/api v1.33.2/go.mod h1:K3yoL/vnIBcQV/25NeMZVokRvPPERiqp2Udtr4xAfhs= github.com/hashicorp/consul/sdk v0.17.1 h1:LumAh8larSXmXw2wvw/lK5ZALkJ2wK8VRwWMLVV5M5c= @@ -717,14 +724,14 @@ github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iP github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/hashicorp/nomad/api v0.0.0-20251216171439-1dee0671280e h1:wGl06iy/H90NSbWjfXWeRwk9SJOks0u4voIryeJFlSA= -github.com/hashicorp/nomad/api v0.0.0-20251216171439-1dee0671280e/go.mod h1:sldFTIgs+FsUeKU3LwVjviAIuksxD8TzDOn02MYwslE= +github.com/hashicorp/nomad/api v0.0.0-20260205205048-8315996478d1 h1:2T7Ay5FMAnZUBxSbrkjufY5YKiLPWij0dDPnbM/KYak= +github.com/hashicorp/nomad/api v0.0.0-20260205205048-8315996478d1/go.mod h1:JAmS1nGJ1KcTM+MHAkgyrL0GDbsnKiJsp75KyqO2wWc= github.com/hashicorp/serf v0.10.2 h1:m5IORhuNSjaxeljg5DeQVDlQyVkhRIjJDimbkCa8aAc= github.com/hashicorp/serf v0.10.2/go.mod h1:T1CmSGfSeGfnfNy/w0odXQUR1rfECGd2Qdsp84DjOiY= github.com/heroku/x v0.5.3 h1:lf4/Yg3j/e/QIgAUFmoVd0ELgQjUX9DUx+CULX2AWag= github.com/heroku/x v0.5.3/go.mod h1:MEW61sTisuKAjLRoO1reSmX4xKqKxAKWWl2HER8Vn2M= -github.com/hetznercloud/hcloud-go/v2 v2.32.0 h1:BRe+k7ESdYv3xQLBGdKUfk+XBFRJNGKzq70nJI24ciM= -github.com/hetznercloud/hcloud-go/v2 v2.32.0/go.mod h1:hAanyyfn9M0cMmZ68CXzPCF54KRb9EXd8eiE2FHKGIE= +github.com/hetznercloud/hcloud-go/v2 v2.36.0 h1:HlLL/aaVXUulqe+rsjoJmrxKhPi1MflL5O9iq5QEtvo= +github.com/hetznercloud/hcloud-go/v2 v2.36.0/go.mod h1:MnN/QJEa/RYNQiiVoJjNHPntM7Z1wlYPgJ2HA40/cDE= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= @@ -737,8 +744,8 @@ github.com/influxdata/tdigest v0.0.2-0.20210216194612-fc98d27c9e8b h1:i44CesU68Z github.com/influxdata/tdigest v0.0.2-0.20210216194612-fc98d27c9e8b/go.mod h1:Z0kXnxzbTC2qrx4NaIzYkE1k66+6oEDQTvL95hQFh5Y= github.com/influxdata/telegraf v1.37.1 h1:V2vidW2otrQ2d0wek5ZJn1ZO+R4oGNUHKPuPXOFOIZk= github.com/influxdata/telegraf v1.37.1/go.mod h1:VFWKwWoignqWCd98ffiRMpjTA8I7XxbdRpcNXDMu9vY= -github.com/ionos-cloud/sdk-go/v6 v6.3.5 h1:6fHArdV1lf50iRhCkCP7wkvGwWzVwi+l9w1t5mwkOa8= -github.com/ionos-cloud/sdk-go/v6 v6.3.5/go.mod h1:nUGHP4kZHAZngCVr4v6C8nuargFrtvt7GrzH/hqn7c4= +github.com/ionos-cloud/sdk-go/v6 v6.3.6 h1:l/TtKgdQ1wUH3DDe2SfFD78AW+TJWdEbDpQhHkWd6CM= +github.com/ionos-cloud/sdk-go/v6 v6.3.6/go.mod h1:nUGHP4kZHAZngCVr4v6C8nuargFrtvt7GrzH/hqn7c4= github.com/jaegertracing/jaeger-idl v0.6.0 h1:LOVQfVby9ywdMPI9n3hMwKbyLVV3BL1XH2QqsP5KTMk= github.com/jaegertracing/jaeger-idl v0.6.0/go.mod h1:mpW0lZfG907/+o5w5OlnNnig7nHJGT3SfKmRqC42HGQ= github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= @@ -795,8 +802,8 @@ github.com/knadh/koanf/maps v0.1.2 h1:RBfmAW5CnZT+PJ1CVc1QSJKf4Xu9kxfQgYVQSu8hpb github.com/knadh/koanf/maps v0.1.2/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= github.com/knadh/koanf/providers/confmap v1.0.0 h1:mHKLJTE7iXEys6deO5p6olAiZdG5zwp8Aebir+/EaRE= github.com/knadh/koanf/providers/confmap v1.0.0/go.mod h1:txHYHiI2hAtF0/0sCmcuol4IDcuQbKTybiB1nOcUo1A= -github.com/knadh/koanf/v2 v2.3.0 h1:Qg076dDRFHvqnKG97ZEsi9TAg2/nFTa9hCdcSa1lvlM= -github.com/knadh/koanf/v2 v2.3.0/go.mod h1:gRb40VRAbd4iJMYYD5IxZ6hfuopFcXBpc9bbQpZwo28= +github.com/knadh/koanf/v2 v2.3.2 h1:Ee6tuzQYFwcZXQpc2MiVeC6qHMandf5SMUJJNoFp/c4= +github.com/knadh/koanf/v2 v2.3.2/go.mod h1:gRb40VRAbd4iJMYYD5IxZ6hfuopFcXBpc9bbQpZwo28= github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00= github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -820,8 +827,8 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/leodido/ragel-machinery v0.0.0-20190525184631-5f46317e436b h1:11UHH39z1RhZ5dc4y4r/4koJo6IYFgTRMe/LlwRTEw0= github.com/leodido/ragel-machinery v0.0.0-20190525184631-5f46317e436b/go.mod h1:WZxr2/6a/Ar9bMDc2rN/LJrE/hF6bXE4LPyDSIxwAfg= -github.com/linode/linodego v1.63.0 h1:MdjizfXNJDVJU6ggoJmMO5O9h4KGPGivNX0fzrAnstk= -github.com/linode/linodego v1.63.0/go.mod h1:GoiwLVuLdBQcAebxAVKVL3mMYUgJZR/puOUSla04xBE= +github.com/linode/linodego v1.65.0 h1:SdsuGD8VSsPWeShXpE7ihl5vec+fD3MgwhnfYC/rj7k= +github.com/linode/linodego v1.65.0/go.mod h1:tOFiTErdjkbVnV+4S0+NmIE9dqqZUEM2HsJaGu8wMh8= github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= @@ -849,8 +856,8 @@ github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ= github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE= github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY= github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= -github.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc= -github.com/miekg/dns v1.1.69/go.mod h1:7OyjD9nEba5OkqQ/hB4fy3PIoxafSZJtducccIelz3g= +github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= +github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI= @@ -881,8 +888,8 @@ github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7z github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= -github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= -github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -891,8 +898,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ= +github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw= github.com/mozillazg/go-httpheader v0.2.1 h1:geV7TrjbL8KXSyvghnFm+NyTux/hxwueTSrwhe88TQQ= github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= @@ -926,12 +933,12 @@ github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= -github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.142.0 h1:agYk41V3eIfV6aIMxIeRQ7SFhfaW5k2O96HEebpmPwM= -github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.142.0/go.mod h1:ZmMdcBia20ih8NYia5b4dNhfNLT68xHgaqF+fNW+TLM= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.142.0 h1:bLp+Ii1UQ9cNr+Dm1jKzbcklhd0eBnPuIFQY6NPzkZ0= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.142.0/go.mod h1:6N36UrFd9Yiz2aYpXM5xiK7Eqp2RyAr3O8lUE+wK2Y8= -github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.142.0 h1:fL8LBVeje+nbts2VIInvRa4T5LlsC0BZCI60wNGoS+Y= -github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.142.0/go.mod h1:fSnKuTN91I68Ou1Lgfwe3Mt6BGl9kcA8PYCpnGkPnsY= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.145.0 h1:0dYiJ7krIwaHFX6YLNDo/yawTZIu8X16tT/nwW1UTG8= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.145.0/go.mod h1:mhoa9lipcEH0heeKf6+xHzGUrCuAgImQv4/Qpmu0+Fk= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.145.0 h1:sB4yuYx45zig1ceQ+kmrEYy0xMZ+mGagwYIFtJkkU1w= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.145.0/go.mod h1:uLhceuH7ZtiVxk+B0MHI0vhJG2Y4aOzT/hrV6c5KjVU= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.145.0 h1:en86L47oOTsAkbDc5VEMF5cziXPBK2D4hqGRqLaJtCw= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.145.0/go.mod h1:osDRUOIfd7IiKkDvcE/VrPp9FFOPJmFp73RuvgOn5gE= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= @@ -960,6 +967,14 @@ github.com/parquet-go/parquet-go v0.27.0 h1:vHWK2xaHbj+v1DYps03yDRpEsdtOeKbhiXUa github.com/parquet-go/parquet-go v0.27.0/go.mod h1:navtkAYr2LGoJVp141oXPlO/sxLvaOe3la2JEoD8+rg= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pb33f/jsonpath v0.7.1 h1:dEp6oIZuJbpDSyuHAl9m7GonoDW4M20BcD5vT0tPYRE= +github.com/pb33f/jsonpath v0.7.1/go.mod h1:zBV5LJW4OQOPatmQE2QdKpGQJvhDTlE5IEj6ASaRNTo= +github.com/pb33f/libopenapi v0.33.4 h1:Rgczgrg4VQKXW/NtSj/nApmtYKS+TVpLgTsG692JxmE= +github.com/pb33f/libopenapi v0.33.4/go.mod h1:e/dmd2Pf1nkjqkI0r7guFSyt9T5V0IIQKgs0L6B/3b0= +github.com/pb33f/libopenapi-validator v0.11.1 h1:lTW738oB3lwpS9poDzmI3jpTPZSb5W46vklZqtyf7+Q= +github.com/pb33f/libopenapi-validator v0.11.1/go.mod h1:7CfboslU/utKhiuQRuenriGYZ+HQLDOvARxjqRwd57w= +github.com/pb33f/ordered-map/v2 v2.3.0 h1:k2OhVEQkhTCQMhAicQ3Z6iInzoZNQ7L9MVomwKBZ5WQ= +github.com/pb33f/ordered-map/v2 v2.3.0/go.mod h1:oe5ue+6ZNhy7QN9cPZvPA23Hx0vMHnNVeMg4fGdCANw= github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= @@ -996,8 +1011,8 @@ github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqr github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= -github.com/prometheus/client_golang/exp v0.0.0-20251212205219-7ba246a648ca h1:BOxmsLoL2ymn8lXJtorca7N/m+2vDQUDoEtPjf0iAxA= -github.com/prometheus/client_golang/exp v0.0.0-20251212205219-7ba246a648ca/go.mod h1:gndBHh3ZdjBozGcGrjUYjN3UJLRS3l2drALtu4lUt+k= +github.com/prometheus/client_golang/exp v0.0.0-20260108101519-fb0838f53562 h1:vwqZvuobg82U0gcG2eVrFH27806bUbNr32SvfRbvdsg= +github.com/prometheus/client_golang/exp v0.0.0-20260108101519-fb0838f53562/go.mod h1:PmAYDB13uBFBG9qE1qxZZgZWhg7Rg6SfKM5DMK7hjyI= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -1026,8 +1041,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= -github.com/prometheus/prometheus v0.309.1 h1:jutK6eCYDpWdPTUbVbkcQsNCMO9CCkSwjQRMLds4jSo= -github.com/prometheus/prometheus v0.309.1/go.mod h1:d+dOGiVhuNDa4MaFXHVdnUBy/CzqlcNTooR8oM1wdTU= +github.com/prometheus/prometheus v0.309.2-0.20260219114400-b2e63376da74 h1:dWs4FsohdSfoDgfKkjba0olUKjsAYZ9aqUqa+PK4Oyo= +github.com/prometheus/prometheus v0.309.2-0.20260219114400-b2e63376da74/go.mod h1:MQ96Y1phgxlhaVPG5BgBTrdGucO3NlPojeCs6dRwVkE= github.com/prometheus/sigv4 v0.4.1 h1:EIc3j+8NBea9u1iV6O5ZAN8uvPq2xOIUPcqCTivHuXs= github.com/prometheus/sigv4 v0.4.1/go.mod h1:eu+ZbRvsc5TPiHwqh77OWuCnWK73IdkETYY46P4dXOU= github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg= @@ -1051,8 +1066,10 @@ github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.35 h1:8xfn1RzeI9yoCUuEwDy08F+No6PcKZGEDOQ6hrRyLts= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.35/go.mod h1:47B1d/YXmSAxlJxUJxClzHR6b3T4M1WyCvwENPQNBWc= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.36 h1:ObX9hZmK+VmijreZO/8x9pQ8/P/ToHD/bdSb4Eg4tUo= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.36/go.mod h1:LEsDu4BubxK7/cWhtlQWfuxwL4rf/2UEpxXz1o1EMtM= github.com/schollz/progressbar/v3 v3.19.0 h1:Ea18xuIRQXLAUidVDox3AbwfUhD0/1IvohyTutOIFoc= github.com/schollz/progressbar/v3 v3.19.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= @@ -1088,8 +1105,8 @@ github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= -github.com/stackitcloud/stackit-sdk-go/core v0.20.1 h1:odiuhhRXmxvEvnVTeZSN9u98edvw2Cd3DcnkepncP3M= -github.com/stackitcloud/stackit-sdk-go/core v0.20.1/go.mod h1:fqto7M82ynGhEnpZU6VkQKYWYoFG5goC076JWXTUPRQ= +github.com/stackitcloud/stackit-sdk-go/core v0.21.1 h1:Y/PcAgM7DPYMNqum0MLv4n1mF9ieuevzcCIZYQfm3Ts= +github.com/stackitcloud/stackit-sdk-go/core v0.21.1/go.mod h1:osMglDby4csGZ5sIfhNyYq1bS1TxIdPY88+skE/kkmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -1196,42 +1213,42 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/collector/component v1.48.0 h1:0hZKOvT6fIlXoE+6t40UXbXOH7r/h9jyE3eIt0W19Qg= -go.opentelemetry.io/collector/component v1.48.0/go.mod h1:Kmc9Z2CT53M2oRRf+WXHUHHgjCC+ADbiqfPO5mgZe3g= -go.opentelemetry.io/collector/component/componentstatus v0.142.0 h1:a1KkLCtShI5SfhO2ga75VqWjjBRGgrerelt/2JXWLBI= -go.opentelemetry.io/collector/component/componentstatus v0.142.0/go.mod h1:IRWKvFcUrFrkz1gJEV+cKAdE2ZBT128gk1sHt0OzKI4= -go.opentelemetry.io/collector/component/componenttest v0.142.0 h1:a8XclEutO5dv4AnzThHK8dfqR4lDWjJKLtRNM2aVUFM= -go.opentelemetry.io/collector/component/componenttest v0.142.0/go.mod h1:JhX/zKaEbjhFcsiV2ha2spzo24A6RL/jqNBS0svURD0= -go.opentelemetry.io/collector/confmap v1.48.0 h1:vGhg25NEUX5DiYziJEw2siwdzsvtXBRZVuYyLVinFR8= -go.opentelemetry.io/collector/confmap v1.48.0/go.mod h1:8tJHJowmvUkJ8AHzZ6SaH61dcWbdfRE9Sd/hwsKLgRE= -go.opentelemetry.io/collector/confmap/xconfmap v0.142.0 h1:SNfuFP8TA0PmUkx6ryY63uNjLN2HMh5VeGO++IYdPgA= -go.opentelemetry.io/collector/confmap/xconfmap v0.142.0/go.mod h1:FXuX6B8b7Ub7qkLqloWKanmPhADL18EEkaFptcd4eDQ= -go.opentelemetry.io/collector/consumer v1.48.0 h1:g1uroz2AA0cqnEsjqFTSZG+y8uH1gQBqqyzk8kd3QiM= -go.opentelemetry.io/collector/consumer v1.48.0/go.mod h1:lC6PnVXBwI456SV5WtvJqE7vjCNN6DAUc8xjFQ9wUV4= -go.opentelemetry.io/collector/consumer/consumertest v0.142.0 h1:TRt8zR57Vk1PTjtqjHOwOAMbIl+IeloHxWAuF8sWdRw= -go.opentelemetry.io/collector/consumer/consumertest v0.142.0/go.mod h1:yq2dhMxFUlCFkRN7LES3fzsTmUDw9VaunyRAka2TEaY= -go.opentelemetry.io/collector/consumer/xconsumer v0.142.0 h1:qOoQnLZXQ9sRLexTkkmBx3qfaOmEgco9VBPmryg5UhA= -go.opentelemetry.io/collector/consumer/xconsumer v0.142.0/go.mod h1:oPN0yJzEpovwlWvmSaiYgtDqGuOmMMLmmg352sqZdsE= +go.opentelemetry.io/collector/component v1.51.0 h1:btNW76MCRmpsk0ARRT5wspDXF9tvdaLd3uBtYXIiQn0= +go.opentelemetry.io/collector/component v1.51.0/go.mod h1:Zlgwh4yTLDhJglOXqiyXZ7paepTvvoijfFjLqOr/Qww= +go.opentelemetry.io/collector/component/componentstatus v0.145.0 h1:EwUZfSaagdpRXnlrb0TqReJXXW2p9HWBU5YiIeXPCAE= +go.opentelemetry.io/collector/component/componentstatus v0.145.0/go.mod h1:OiYb8rT4FtSJPFSGCKYvOaajdueDUTJZncixGrmy5aM= +go.opentelemetry.io/collector/component/componenttest v0.145.0 h1:ryhRrXqQybGMhz7A7t32NC8BXAFcX2o1RetgPM7vw88= +go.opentelemetry.io/collector/component/componenttest v0.145.0/go.mod h1:5uStrhUdZ0Fw3se00CPmVaRtW8o9N8kKiY76OSCWFjQ= +go.opentelemetry.io/collector/confmap v1.51.0 h1:C9YlMNkIgzuauLpUz2F7DLlWwqAmkQKNcKj1XATVWuE= +go.opentelemetry.io/collector/confmap v1.51.0/go.mod h1:uWi4b9lHfvEC2poJ2I2vXwGUREVEQTcdUguOpfqdcHM= +go.opentelemetry.io/collector/confmap/xconfmap v0.145.0 h1:ngbyfh4+SKlA+osgsak3AxUNPxVxaJTmA0Sl7VfJzwY= +go.opentelemetry.io/collector/confmap/xconfmap v0.145.0/go.mod h1:zTSK+c76NAy/tI1R3xfZjdoI04D9EYDnzAHQQwl6AmA= +go.opentelemetry.io/collector/consumer v1.51.0 h1:Ex1x/k9VEEA2DOgt/eSc2Z9KTp0I6xBSruLmrYFfIFY= +go.opentelemetry.io/collector/consumer v1.51.0/go.mod h1:Erk6qdfVj+24QTrGCpurcrF+qdUlHkb4dgMy5wJxLvY= +go.opentelemetry.io/collector/consumer/consumertest v0.145.0 h1:3+uMwuMHoXMAU+Z6mwCRA3AxWeL7SujcAQwqqHJ1gCc= +go.opentelemetry.io/collector/consumer/consumertest v0.145.0/go.mod h1:IFc/FeaIHQClb8KK0aVn0tFDNMc+/MmfQ+aBT1cJNeo= +go.opentelemetry.io/collector/consumer/xconsumer v0.145.0 h1:9w7KKv9lVJoHvMLC6SUJHenU/KySdEgFJXbB4JQOEsk= +go.opentelemetry.io/collector/consumer/xconsumer v0.145.0/go.mod h1:SryDCLP2ZaFeZJtA2CSksJ0XvjH8k3LmlfXvy/kC7Wc= go.opentelemetry.io/collector/featuregate v1.51.0 h1:dxJuv/3T84dhNKp7fz5+8srHz1dhquGzDpLW4OZTFBw= go.opentelemetry.io/collector/featuregate v1.51.0/go.mod h1:/1bclXgP91pISaEeNulRxzzmzMTm4I5Xih2SnI4HRSo= +go.opentelemetry.io/collector/internal/componentalias v0.145.0 h1:A9V5IiETzz8FCtjxjRM5gf7RE3sOtA1h8phmpQjXTZ4= +go.opentelemetry.io/collector/internal/componentalias v0.145.0/go.mod h1:sEKEAwAn45ZiXRk3T/vbkvetw14tIRd0CJIxcEx9SsQ= go.opentelemetry.io/collector/internal/testutil v0.145.0 h1:H/KL0GH3kGqSMKxZvnQ0B0CulfO9xdTg4DZf28uV7fY= go.opentelemetry.io/collector/internal/testutil v0.145.0/go.mod h1:YAD9EAkwh/l5asZNbEBEUCqEjoL1OKMjAMoPjPqH76c= go.opentelemetry.io/collector/pdata v1.51.0 h1:DnDhSEuDXNdzGRB7f6oOfXpbDApwBX3tY+3K69oUrDA= go.opentelemetry.io/collector/pdata v1.51.0/go.mod h1:GoX1bjKDR++mgFKdT7Hynv9+mdgQ1DDXbjs7/Ww209Q= -go.opentelemetry.io/collector/pdata/pprofile v0.142.0 h1:Ivyw7WY8SIIWqzXsnNmjEgz3ysVs/OkIf0KIpJUnuuo= -go.opentelemetry.io/collector/pdata/pprofile v0.142.0/go.mod h1:94GAph54K4WDpYz9xirhroHB3ptNLuPiY02k8fyoNUI= -go.opentelemetry.io/collector/pdata/testdata v0.142.0 h1:+jf9RyLWl8WyhIVjpg7yuH+bRdQH4mW20cPtCMlY1cI= -go.opentelemetry.io/collector/pdata/testdata v0.142.0/go.mod h1:kgAu5ZLEcVuPH3RFiHDg23RGitgm1M0cUAVwiGX4SB8= -go.opentelemetry.io/collector/pipeline v1.48.0 h1:E4zyQ7+4FTGvdGS4pruUnItuyRTGhN0Qqk1CN71lfW0= -go.opentelemetry.io/collector/pipeline v1.48.0/go.mod h1:xUrAqiebzYbrgxyoXSkk6/Y3oi5Sy3im2iCA51LwUAI= -go.opentelemetry.io/collector/processor v1.48.0 h1:3Kttw79mnrf463QKJGoGZzFfiNzQuMWK0p2nHuvOhaQ= -go.opentelemetry.io/collector/processor v1.48.0/go.mod h1:A3OsW6ga+a48J1mrnVNH5L5kB0v+n9nVFlmOQB5/Jwk= -go.opentelemetry.io/collector/processor/processortest v0.142.0 h1:wQnJeXDejBL6r8ov66AYAGf8Q0/JspjuqAjPVBdCUoI= -go.opentelemetry.io/collector/processor/processortest v0.142.0/go.mod h1:QU5SWj0L+92MSvQxZDjwWCsKssNDm+nD6SHn7IvviUE= -go.opentelemetry.io/collector/processor/xprocessor v0.142.0 h1:7a1Crxrd5iBMVnebTxkcqxVkRHAlOBUUmNTUVUTnlCU= -go.opentelemetry.io/collector/processor/xprocessor v0.142.0/go.mod h1:LY/GS2DiJILJKS3ynU3eOLLWSP8CmN1FtdpAMsVV8AU= -go.opentelemetry.io/collector/semconv v0.128.0 h1:MzYOz7Vgb3Kf5D7b49pqqgeUhEmOCuT10bIXb/Cc+k4= -go.opentelemetry.io/collector/semconv v0.128.0/go.mod h1:OPXer4l43X23cnjLXIZnRj/qQOjSuq4TgBLI76P9hns= +go.opentelemetry.io/collector/pdata/pprofile v0.145.0 h1:ASMKpoqokf8HhzjoeMKZf0K6UXLhufVwNXH0sSuUn5w= +go.opentelemetry.io/collector/pdata/pprofile v0.145.0/go.mod h1:a60GC7wQPhLAixWzKbbP51QLwwc+J0Cmp4SurOlhGUk= +go.opentelemetry.io/collector/pdata/testdata v0.145.0 h1:iFsxsCMtE3lnAc/5kZbhZHpRv1OMmM+O5ry46xdQHbg= +go.opentelemetry.io/collector/pdata/testdata v0.145.0/go.mod h1:0y2ERArdzqmYdJHdKLKue+AUubSEGlwK49F+23+Mbic= +go.opentelemetry.io/collector/pipeline v1.51.0 h1:GZBNW+aaOE+zufGzAkXy0OI7n1cqepEa5J+beaOpS2k= +go.opentelemetry.io/collector/pipeline v1.51.0/go.mod h1:xUrAqiebzYbrgxyoXSkk6/Y3oi5Sy3im2iCA51LwUAI= +go.opentelemetry.io/collector/processor v1.51.0 h1:PKpCzkLQmqaW08TOVh/zM0qx07Ihq+DR5J/OBkPiL9o= +go.opentelemetry.io/collector/processor v1.51.0/go.mod h1:rtIPFS+EFRAkG+CSwtjxs2IsIkuZStObvALeueD02XI= +go.opentelemetry.io/collector/processor/processortest v0.145.0 h1:RDGBmyZnHk7XVK/EdLt/8iPWj+QLStbbVi1nFTNR01s= +go.opentelemetry.io/collector/processor/processortest v0.145.0/go.mod h1:WAvxAzSojkdoZB915Z1lsVHCPDJBb2fepjJBjenrzjg= +go.opentelemetry.io/collector/processor/xprocessor v0.145.0 h1:DaIE7MxRlg0OL1o2P0GQZtmZeExAmVso3qWv8S0RLps= +go.opentelemetry.io/collector/processor/xprocessor v0.145.0/go.mod h1:kUwRyKBU/kjCmXodd+0z7CpvcP0A9G9/QL+MaJt4U2o= go.opentelemetry.io/contrib/bridges/prometheus v0.64.0 h1:7TYhBCu6Xz6vDJGNtEslWZLuuX2IJ/aH50hBY4MVeUg= go.opentelemetry.io/contrib/bridges/prometheus v0.64.0/go.mod h1:tHQctZfAe7e4PBPGyt3kae6mQFXNpj+iiDJa3ithM50= go.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE= @@ -1261,12 +1278,12 @@ go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0 h1:cEf go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0/go.mod h1:k1lzV5n5U3HkGvTCJHraTAGJ7MqsgL1wrGwTj1Isfiw= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.39.0 h1:nKP4Z2ejtHn3yShBb+2KawiXgpn8In5cT7aO2wXuOTE= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.39.0/go.mod h1:NwjeBbNigsO4Aj9WgM0C+cKIrxsZUaRmZUO7A8I7u8o= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 h1:f0cb2XPmrqn4XMy9PNliTgRKJgS5WcL/u0/WRYGz4t0= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0/go.mod h1:vnakAaFckOMiMtOIhFI2MNH4FYrZzXCYxmb1LlhoGz8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 h1:in9O8ESIOlwJAEGTkkf34DesGRAc/Pn8qJ7k3r/42LM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0/go.mod h1:Rp0EXBm5tfnv0WL+ARyO/PHBEaEAT8UUHQ6AGJcSq6c= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 h1:Ckwye2FpXkYgiHX7fyVrN1uA/UYd9ounqqTuSNAv0k4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0/go.mod h1:teIFJh5pW2y+AN7riv6IBPX2DuesS3HgP39mwOspKwU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDOPmSWQHWywQS6lKL+pb8s3gBLOZUtw4N+mavW1I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 h1:wVZXIWjQSeSmMoxF74LzAnpVQOAFDo3pPji9Y4SOFKc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0/go.mod h1:khvBS2IggMFNwZK/6lEeHg/W57h/IX6J4URh57fuI40= go.opentelemetry.io/otel/exporters/prometheus v0.61.0 h1:cCyZS4dr67d30uDyh8etKM2QyDsQ4zC9ds3bdbrVoD0= go.opentelemetry.io/otel/exporters/prometheus v0.61.0/go.mod h1:iivMuj3xpR2DkUrUya3TPS/Z9h3dz7h01GxU+fQBRNg= go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.15.0 h1:0BSddrtQqLEylcErkeFrJBmwFzcqfQq9+/uxfTZq+HE= @@ -1312,6 +1329,8 @@ go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U= +go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= go4.org/netipx v0.0.0-20230125063823-8449b0a6169f h1:ketMxHg+vWm3yccyYiq+uK8D3fRmna2Fcj+awpQp84s= go4.org/netipx v0.0.0-20230125063823-8449b0a6169f/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1324,6 +1343,7 @@ golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= @@ -1338,8 +1358,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20251209150349-8475f28825e9 h1:MDfG8Cvcqlt9XXrmEiD4epKn7VJHZO84hejP9Jmp0MM= -golang.org/x/exp v0.0.0-20251209150349-8475f28825e9/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1402,6 +1422,7 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1483,6 +1504,7 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= @@ -1493,6 +1515,7 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= @@ -1506,6 +1529,7 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= @@ -1590,8 +1614,8 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.257.0 h1:8Y0lzvHlZps53PEaw+G29SsQIkuKrumGWs9puiexNAA= -google.golang.org/api v0.257.0/go.mod h1:4eJrr+vbVaZSqs7vovFd1Jb/A6ml6iw2e6FBYf3GAO4= +google.golang.org/api v0.265.0 h1:FZvfUdI8nfmuNrE34aOWFPmLC+qRBEiNm3JdivTvAAU= +google.golang.org/api v0.265.0/go.mod h1:uAvfEl3SLUj/7n6k+lJutcswVojHPp2Sp08jWCu8hLY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1628,10 +1652,10 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20250922171735-9219d122eba9 h1:LvZVVaPE0JSqL+ZWb6ErZfnEOKIqqFWUJE2D0fObSmc= -google.golang.org/genproto v0.0.0-20250922171735-9219d122eba9/go.mod h1:QFOrLhdAe2PsTp3vQY4quuLKTi9j3XG3r6JPPaw7MSc= -google.golang.org/genproto/googleapis/api v0.0.0-20251213004720-97cd9d5aeac2 h1:7LRqPCEdE4TP4/9psdaB7F2nhZFfBiGJomA5sojLWdU= -google.golang.org/genproto/googleapis/api v0.0.0-20251213004720-97cd9d5aeac2/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto= +google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217 h1:GvESR9BIyHUahIb0NcTum6itIWtdoglGX+rnGxm2934= +google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:yJ2HH4EHEDTd3JiLmhds6NkJ17ITVYOdV3m3VKOnws0= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -1677,8 +1701,8 @@ gopkg.in/fsnotify/fsnotify.v1 v1.4.7 h1:XNNYLJHt73EyYiCZi6+xjupS9CpvmiDgjPTAjrBl gopkg.in/fsnotify/fsnotify.v1 v1.4.7/go.mod h1:Fyux9zXlo4rWoMSIzpn9fDAYjalPqJ/K1qJ27s+7ltE= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k= +gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= gopkg.in/tomb.v1 v1.0.0-20140529071818-c131134a1947/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= @@ -1695,8 +1719,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/kafkav2/consumer.go b/pkg/kafkav2/consumer.go index e4f2c32e6..26cedfce1 100644 --- a/pkg/kafkav2/consumer.go +++ b/pkg/kafkav2/consumer.go @@ -208,7 +208,7 @@ func (c *SinglePartitionConsumer) GetInitialOffset() int64 { // SetInitialOffset sets the initial offset for the consumer. It cannot // be called after the service has started. func (c *SinglePartitionConsumer) SetInitialOffset(offset int64) error { - if c.BasicService.State() != services.New { + if c.State() != services.New { return errors.New("cannot set initial offset after service has started") } c.initialOffset = offset diff --git a/pkg/kafkav2/consumer_test.go b/pkg/kafkav2/consumer_test.go index d82d88491..ee9ae54d9 100644 --- a/pkg/kafkav2/consumer_test.go +++ b/pkg/kafkav2/consumer_test.go @@ -125,7 +125,7 @@ func TestSinglePartitionConsumer_SetInitialOffset(t *testing.T) { // The consumer should have the initial offset of OffsetStart, and allow us // to change the initial offset until the service is started. - require.Equal(t, services.New, consumer.BasicService.State()) + require.Equal(t, services.New, consumer.State()) require.Equal(t, OffsetStart, consumer.GetInitialOffset()) require.NoError(t, consumer.SetInitialOffset(OffsetEnd)) require.Equal(t, OffsetEnd, consumer.GetInitialOffset()) diff --git a/pkg/logql/engine_test.go b/pkg/logql/engine_test.go index a11e3fcd0..560c3e91c 100644 --- a/pkg/logql/engine_test.go +++ b/pkg/logql/engine_test.go @@ -3499,7 +3499,7 @@ func newQuerierRecorder(t *testing.T, data interface{}, params interface{}) *que for vi := range variants { for si, s := range curSeries { - lbls, err := promql_parser.ParseMetric(s.Labels) + lbls, err := promql_parser.NewParser(promql_parser.Options{}).ParseMetric(s.Labels) if err != nil { return nil } diff --git a/pkg/logql/evaluator.go b/pkg/logql/evaluator.go index 1b8d20e0a..02dc31758 100644 --- a/pkg/logql/evaluator.go +++ b/pkg/logql/evaluator.go @@ -1519,7 +1519,7 @@ func (it *bufferedVariantsIterator) Next(index int) bool { // getVariantIndex determines the variant index for a given sample based on the "__variant__" label func (it *bufferedVariantsIterator) getVariantIndex(lbls string) int { - metric, err := parser.ParseMetric(lbls) + metric, err := parser.NewParser(parser.Options{}).ParseMetric(lbls) if err != nil { it.err = err return -1 diff --git a/pkg/logql/range_vector.go b/pkg/logql/range_vector.go index e7865e570..8a49f5faf 100644 --- a/pkg/logql/range_vector.go +++ b/pkg/logql/range_vector.go @@ -166,7 +166,7 @@ func (r *batchRangeVectorIterator) load(start, end int64) { var metric labels.Labels if metric, ok = r.metrics[lbs]; !ok { var err error - metric, err = promql_parser.ParseMetric(lbs) + metric, err = promql_parser.NewParser(promql_parser.Options{}).ParseMetric(lbs) if err != nil { _ = r.iter.Next() continue @@ -559,7 +559,7 @@ func (r *streamRangeVectorIterator) load(start, end int64) { var metric labels.Labels if _, ok = r.metrics[lbs]; !ok { var err error - metric, err = promql_parser.ParseMetric(lbs) + metric, err = promql_parser.NewParser(promql_parser.Options{}).ParseMetric(lbs) if err != nil { _ = r.iter.Next() continue diff --git a/pkg/logql/syntax/ast.go b/pkg/logql/syntax/ast.go index f678b75f1..75c9faad3 100644 --- a/pkg/logql/syntax/ast.go +++ b/pkg/logql/syntax/ast.go @@ -880,7 +880,7 @@ func (e *DropLabelsExpr) Names() []string { func (e *DropLabelsExpr) String() string { var sb strings.Builder - sb.WriteString(fmt.Sprintf("%s %s ", OpPipe, OpDrop)) + fmt.Fprintf(&sb, "%s %s ", OpPipe, OpDrop) for i, dropLabel := range e.dropLabels { if dropLabel.Matcher != nil { @@ -920,7 +920,7 @@ func (e *KeepLabelsExpr) Stage() (log.Stage, error) { func (e *KeepLabelsExpr) String() string { var sb strings.Builder - sb.WriteString(fmt.Sprintf("%s %s ", OpPipe, OpKeep)) + fmt.Fprintf(&sb, "%s %s ", OpPipe, OpKeep) for i, keepLabel := range e.keepLabels { if keepLabel.Matcher != nil { @@ -985,7 +985,7 @@ func (e *LabelFmtExpr) Stage() (log.Stage, error) { func (e *LabelFmtExpr) String() string { var sb strings.Builder - sb.WriteString(fmt.Sprintf("%s %s ", OpPipe, OpFmtLabel)) + fmt.Fprintf(&sb, "%s %s ", OpPipe, OpFmtLabel) for i, f := range e.Formats { sb.WriteString(f.Name) @@ -1024,7 +1024,7 @@ func (j *JSONExpressionParserExpr) Stage() (log.Stage, error) { func (j *JSONExpressionParserExpr) String() string { var sb strings.Builder - sb.WriteString(fmt.Sprintf("%s %s ", OpPipe, OpParserTypeJSON)) + fmt.Fprintf(&sb, "%s %s ", OpPipe, OpParserTypeJSON) for i, exp := range j.Expressions { sb.WriteString(exp.Identifier) sb.WriteString("=") @@ -1076,7 +1076,7 @@ func (l *LogfmtExpressionParserExpr) Stage() (log.Stage, error) { func (l *LogfmtExpressionParserExpr) String() string { var sb strings.Builder - sb.WriteString(fmt.Sprintf("%s %s ", OpPipe, OpParserTypeLogfmt)) + fmt.Fprintf(&sb, "%s %s ", OpPipe, OpParserTypeLogfmt) if l.Strict { sb.WriteString(OpStrict) sb.WriteString(" ") @@ -1141,12 +1141,12 @@ type UnwrapExpr struct { func (u UnwrapExpr) String() string { var sb strings.Builder if u.Operation != "" { - sb.WriteString(fmt.Sprintf(" %s %s %s(%s)", OpPipe, OpUnwrap, u.Operation, u.Identifier)) + fmt.Fprintf(&sb, " %s %s %s(%s)", OpPipe, OpUnwrap, u.Operation, u.Identifier) } else { - sb.WriteString(fmt.Sprintf(" %s %s %s", OpPipe, OpUnwrap, u.Identifier)) + fmt.Fprintf(&sb, " %s %s %s", OpPipe, OpUnwrap, u.Identifier) } for _, f := range u.PostFilters { - sb.WriteString(fmt.Sprintf(" %s %s", OpPipe, f)) + fmt.Fprintf(&sb, " %s %s", OpPipe, f) } return sb.String() } @@ -1174,7 +1174,7 @@ func (r LogRangeExpr) String() string { if r.Unwrap != nil { sb.WriteString(r.Unwrap.String()) } - sb.WriteString(fmt.Sprintf("[%v]", model.Duration(r.Interval))) + fmt.Fprintf(&sb, "[%v]", model.Duration(r.Interval)) if r.Offset != 0 { offsetExpr := OffsetExpr{Offset: r.Offset} sb.WriteString(offsetExpr.String()) @@ -1229,7 +1229,7 @@ type OffsetExpr struct { func (o *OffsetExpr) String() string { var sb strings.Builder - sb.WriteString(fmt.Sprintf(" %s %s", OpOffset, o.Offset.String())) + fmt.Fprintf(&sb, " %s %s", OpOffset, o.Offset.String()) return sb.String() } @@ -2387,7 +2387,7 @@ func (e *VectorExpr) String() string { var sb strings.Builder sb.WriteString(OpTypeVector) sb.WriteString("(") - sb.WriteString(fmt.Sprintf("%f", e.Val)) + fmt.Fprintf(&sb, "%f", e.Val) sb.WriteString(")") return sb.String() } diff --git a/pkg/logql/syntax/parser.go b/pkg/logql/syntax/parser.go index 6b9171fa3..bb923b904 100644 --- a/pkg/logql/syntax/parser.go +++ b/pkg/logql/syntax/parser.go @@ -274,7 +274,7 @@ func ParseLogSelector(input string, validate bool) (LogSelectorExpr, error) { // ParseLabels parses labels from a string using logql parser. func ParseLabels(lbs string) (labels.Labels, error) { - ls, err := promql_parser.ParseMetric(lbs) + ls, err := promql_parser.NewParser(promql_parser.Options{}).ParseMetric(lbs) if err != nil { return labels.EmptyLabels(), err } diff --git a/pkg/logql/test_utils.go b/pkg/logql/test_utils.go index 3ed62e20c..8ab0737f0 100644 --- a/pkg/logql/test_utils.go +++ b/pkg/logql/test_utils.go @@ -307,7 +307,7 @@ func randomStreams(nStreams, nEntries, nShards int, labelNames []string, valueFi } func mustParseLabels(s string) labels.Labels { - labels, err := promql_parser.ParseMetric(s) + labels, err := promql_parser.NewParser(promql_parser.Options{}).ParseMetric(s) if err != nil { logger.Fatalf("Failed to parse %s", s) } diff --git a/pkg/logqlanalyzer/analyzer.go b/pkg/logqlanalyzer/analyzer.go index ad6f5ca8c..04e90ec75 100644 --- a/pkg/logqlanalyzer/analyzer.go +++ b/pkg/logqlanalyzer/analyzer.go @@ -28,7 +28,7 @@ func (a logQLAnalyzer) analyze(query string, logs []string) (*Result, error) { if err != nil { return nil, errors.Wrap(err, "can not create pipeline") } - streamLabels, err := parser.ParseMetric(streamSelector) + streamLabels, err := parser.NewParser(parser.Options{}).ParseMetric(streamSelector) if err != nil { return nil, errors.Wrap(err, "can not parse labels from stream selector") } diff --git a/pkg/loki/loki.go b/pkg/loki/loki.go index 8ec50ef15..a95eb6a0b 100644 --- a/pkg/loki/loki.go +++ b/pkg/loki/loki.go @@ -689,7 +689,7 @@ func (t *Loki) readyHandler(sm *services.Manager, shutdownRequested *atomic.Bool byState := sm.ServicesByState() for st, ls := range byState { - msg.WriteString(fmt.Sprintf("%v: %d\n", st, len(ls))) + fmt.Fprintf(&msg, "%v: %d\n", st, len(ls)) } http.Error(w, msg.String(), http.StatusServiceUnavailable) diff --git a/pkg/querier/astmapper/astmapper.go b/pkg/querier/astmapper/astmapper.go index 2bee52786..5dd606026 100644 --- a/pkg/querier/astmapper/astmapper.go +++ b/pkg/querier/astmapper/astmapper.go @@ -58,7 +58,7 @@ func NewMultiMapper(xs ...ASTMapper) *MultiMapper { // CloneNode is a helper function to clone a node. func CloneNode(node parser.Node) (parser.Node, error) { - return parser.ParseExpr(node.String()) + return parser.NewParser(parser.Options{}).ParseExpr(node.String()) } // NodeMapper either maps a single AST node or returns the unaltered node. diff --git a/pkg/querier/queryrange/detected_fields_test.go b/pkg/querier/queryrange/detected_fields_test.go index e3b92568d..f517f9ef4 100644 --- a/pkg/querier/queryrange/detected_fields_test.go +++ b/pkg/querier/queryrange/detected_fields_test.go @@ -54,7 +54,7 @@ func Test_parseDetectedFields(t *testing.T) { } rulerLbls := `{cluster="us-east-1", namespace="mimir-dev", pod="mimir-ruler-nfb37", service_name="mimir-ruler"}` - rulerMetric, err := parser.ParseMetric(rulerLbls) + rulerMetric, err := parser.NewParser(parser.Options{}).ParseMetric(rulerLbls) require.NoError(t, err) rulerStream := push.Stream{ @@ -89,7 +89,7 @@ func Test_parseDetectedFields(t *testing.T) { } nginxLbls := `{ cluster="eu-west-1", level="debug", namespace="gateway", pod="nginx-json-oghco", service_name="nginx-json" }` - nginxMetric, err := parser.ParseMetric(nginxLbls) + nginxMetric, err := parser.NewParser(parser.Options{}).ParseMetric(nginxLbls) require.NoError(t, err) nginxStream := push.Stream{ @@ -185,7 +185,7 @@ func Test_parseDetectedFields(t *testing.T) { t.Run("correctly applies _extracted for a single stream", func(t *testing.T) { rulerLbls := `{cluster="us-east-1", namespace="mimir-dev", pod="mimir-ruler-nfb37", service_name="mimir-ruler", tenant="42", caller="inside-the-house"}` - rulerMetric, err := parser.ParseMetric(rulerLbls) + rulerMetric, err := parser.NewParser(parser.Options{}).ParseMetric(rulerLbls) require.NoError(t, err) rulerStream := push.Stream{ @@ -214,7 +214,7 @@ func Test_parseDetectedFields(t *testing.T) { t.Run("correctly applies _extracted for multiple streams", func(t *testing.T) { rulerLbls := `{cluster="us-east-1", namespace="mimir-dev", pod="mimir-ruler-nfb37", service_name="mimir-ruler", tenant="42", caller="inside-the-house"}` - rulerMetric, err := parser.ParseMetric(rulerLbls) + rulerMetric, err := parser.NewParser(parser.Options{}).ParseMetric(rulerLbls) require.NoError(t, err) rulerStream := push.Stream{ @@ -224,7 +224,7 @@ func Test_parseDetectedFields(t *testing.T) { } nginxLbls := `{ cluster="eu-west-1", level="debug", namespace="gateway", pod="nginx-json-oghco", service_name="nginx-json", host="localhost"}` - nginxMetric, err := parser.ParseMetric(nginxLbls) + nginxMetric, err := parser.NewParser(parser.Options{}).ParseMetric(nginxLbls) require.NoError(t, err) nginxStream := push.Stream{ @@ -616,7 +616,7 @@ func Test_parseDetectedFields(t *testing.T) { t.Run("correctly applies _extracted for a single stream", func(t *testing.T) { rulerLbls := `{cluster="us-east-1", namespace="mimir-dev", pod="mimir-ruler-nfb37", service_name="mimir-ruler", tenant="42", caller="inside-the-house"}` - rulerMetric, err := parser.ParseMetric(rulerLbls) + rulerMetric, err := parser.NewParser(parser.Options{}).ParseMetric(rulerLbls) require.NoError(t, err) rulerStream := push.Stream{ @@ -681,7 +681,7 @@ func Test_parseDetectedFields(t *testing.T) { t.Run("correctly applies _extracted for multiple streams", func(t *testing.T) { rulerLbls := `{cluster="us-east-1", namespace="mimir-dev", pod="mimir-ruler-nfb37", service_name="mimir-ruler", tenant="42", caller="inside-the-house"}` - rulerMetric, err := parser.ParseMetric(rulerLbls) + rulerMetric, err := parser.NewParser(parser.Options{}).ParseMetric(rulerLbls) require.NoError(t, err) rulerStream := push.Stream{ @@ -727,7 +727,7 @@ func Test_parseDetectedFields(t *testing.T) { } nginxLbls := `{ cluster="eu-west-1", level="debug", namespace="gateway", pod="nginx-json-oghco", service_name="nginx-json", host="localhost"}` - nginxMetric, err := parser.ParseMetric(nginxLbls) + nginxMetric, err := parser.NewParser(parser.Options{}).ParseMetric(nginxLbls) require.NoError(t, err) nginxStream := push.Stream{ @@ -822,7 +822,7 @@ func Test_parseDetectedFields(t *testing.T) { t.Run("handles level in all the places", func(t *testing.T) { rulerLbls := `{cluster="us-east-1", namespace="mimir-dev", pod="mimir-ruler-nfb37", service_name="mimir-ruler", tenant="42", caller="inside-the-house", level="debug"}` - rulerMetric, err := parser.ParseMetric(rulerLbls) + rulerMetric, err := parser.NewParser(parser.Options{}).ParseMetric(rulerLbls) require.NoError(t, err) rulerStream := push.Stream{ @@ -1321,7 +1321,7 @@ func TestQuerier_DetectedFields(t *testing.T) { t.Run("correctly formats bytes values for detected fields", func(t *testing.T) { lbls := `{cluster="us-east-1", namespace="mimir-dev", pod="mimir-ruler-nfb37", service_name="mimir-ruler"}` - metric, err := parser.ParseMetric(lbls) + metric, err := parser.NewParser(parser.Options{}).ParseMetric(lbls) require.NoError(t, err) now := time.Now() @@ -1498,7 +1498,7 @@ func TestNestedJSONFieldDetection(t *testing.T) { } nestedJSONLbls := `{cluster="test-cluster", job="json-test"}` - nestedJSONMetric, err := parser.ParseMetric(nestedJSONLbls) + nestedJSONMetric, err := parser.NewParser(parser.Options{}).ParseMetric(nestedJSONLbls) require.NoError(t, err) nestedJSONStream := push.Stream{ @@ -1596,7 +1596,7 @@ func TestNestedJSONFieldDetection(t *testing.T) { } nestedJSONLbls := `{cluster="test-cluster", job="json-test"}` - nestedJSONMetric, err := parser.ParseMetric(nestedJSONLbls) + nestedJSONMetric, err := parser.NewParser(parser.Options{}).ParseMetric(nestedJSONLbls) require.NoError(t, err) nestedJSONStream := push.Stream{ diff --git a/pkg/querier/queryrange/parquet.go b/pkg/querier/queryrange/parquet.go index a1db5d2dc..edb1e12b4 100644 --- a/pkg/querier/queryrange/parquet.go +++ b/pkg/querier/queryrange/parquet.go @@ -87,7 +87,7 @@ func encodeLogsParquetTo(response *LokiResponse, w io.Writer) error { writer := parquet.NewGenericWriter[LogStreamRowType](w, schema) for _, stream := range response.Data.Result { - lbls, err := parser.ParseMetric(stream.Labels) + lbls, err := parser.NewParser(parser.Options{}).ParseMetric(stream.Labels) if err != nil { return err } diff --git a/pkg/querier/queryrange/queryrangebase/results_cache.go b/pkg/querier/queryrange/queryrangebase/results_cache.go index f4fb57c04..226d37033 100644 --- a/pkg/querier/queryrange/queryrangebase/results_cache.go +++ b/pkg/querier/queryrange/queryrangebase/results_cache.go @@ -260,7 +260,7 @@ func (s resultsCache) isAtModifierCachable(r Request, maxCacheTime int64) bool { if !strings.Contains(query, "@") { return true } - expr, err := parser.ParseExpr(query) + expr, err := parser.NewParser(parser.Options{}).ParseExpr(query) if err != nil { // We are being pessimistic in such cases. level.Warn(s.logger).Log("msg", "failed to parse query, considering @ modifier as not cachable", "query", query, "err", err) diff --git a/pkg/querier/store_combiner.go b/pkg/querier/store_combiner.go index 32ee3e40f..80189ad2d 100644 --- a/pkg/querier/store_combiner.go +++ b/pkg/querier/store_combiner.go @@ -515,7 +515,7 @@ func stringifyMatchers(matchers []*labels.Matcher) string { if i > 0 { result.WriteString(", ") } - result.WriteString(fmt.Sprintf("%s %s %s", m.Type.String(), m.Name, m.Value)) + fmt.Fprintf(&result, "%s %s %s", m.Type.String(), m.Name, m.Value) } return result.String() } diff --git a/pkg/ruler/base/api.go b/pkg/ruler/base/api.go index a1d7e590f..b4cacc88f 100644 --- a/pkg/ruler/base/api.go +++ b/pkg/ruler/base/api.go @@ -20,7 +20,7 @@ import ( v1 "github.com/prometheus/client_golang/api/prometheus/v1" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/model/rulefmt" - "gopkg.in/yaml.v3" + "go.yaml.in/yaml/v3" "github.com/grafana/dskit/tenant" diff --git a/pkg/ruler/base/api_test.go b/pkg/ruler/base/api_test.go index f653f13c0..bd59bef81 100644 --- a/pkg/ruler/base/api_test.go +++ b/pkg/ruler/base/api_test.go @@ -22,7 +22,7 @@ import ( "github.com/prometheus/prometheus/model/rulefmt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gopkg.in/yaml.v3" + "go.yaml.in/yaml/v3" "github.com/grafana/loki/v3/pkg/ruler/rulespb" ) diff --git a/pkg/ruler/base/compat_test.go b/pkg/ruler/base/compat_test.go index 1dd65282d..8869d01f8 100644 --- a/pkg/ruler/base/compat_test.go +++ b/pkg/ruler/base/compat_test.go @@ -70,7 +70,7 @@ func TestPusherAppendable(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ctx := context.Background() - lbls, err := parser.ParseMetric(tc.series) + lbls, err := parser.NewParser(parser.Options{}).ParseMetric(tc.series) require.NoError(t, err) pusher.response = &logproto.WriteResponse{} @@ -125,7 +125,7 @@ func TestPusherErrors(t *testing.T) { pa := NewPusherAppendable(pusher, "user-1", writes, failures) - lbls, err := parser.ParseMetric("foo_bar") + lbls, err := parser.NewParser(parser.Options{}).ParseMetric("foo_bar") require.NoError(t, err) a := pa.Appender(ctx) diff --git a/pkg/ruler/base/manager.go b/pkg/ruler/base/manager.go index 1d38b54fb..143d6c446 100644 --- a/pkg/ruler/base/manager.go +++ b/pkg/ruler/base/manager.go @@ -20,8 +20,10 @@ import ( "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" + "go.yaml.in/yaml/v3" "golang.org/x/net/context/ctxhttp" - "gopkg.in/yaml.v3" + + "github.com/prometheus/prometheus/promql/parser" "github.com/grafana/loki/v3/pkg/ruler/rulespb" ) @@ -307,7 +309,7 @@ func (*DefaultMultiTenantManager) ValidateRuleGroup(g rulefmt.RuleGroup) []error Alert: yaml.Node{Value: r.Alert}, Expr: yaml.Node{Value: r.Expr}, } - for _, err := range r.Validate(ruleNode, model.UTF8Validation) { + for _, err := range r.Validate(ruleNode, model.UTF8Validation, parser.NewParser(parser.Options{})) { var ruleName string if r.Alert != "" { ruleName = r.Alert diff --git a/pkg/ruler/base/mapper.go b/pkg/ruler/base/mapper.go index 38a3e229a..1e25a868b 100644 --- a/pkg/ruler/base/mapper.go +++ b/pkg/ruler/base/mapper.go @@ -10,8 +10,8 @@ import ( "github.com/go-kit/log/level" "github.com/prometheus/prometheus/model/rulefmt" "github.com/spf13/afero" + "go.yaml.in/yaml/v3" "golang.org/x/crypto/sha3" - "gopkg.in/yaml.v3" ) // mapper is designed to enusre the provided rule sets are identical diff --git a/pkg/ruler/base/ruler_test.go b/pkg/ruler/base/ruler_test.go index ef42a5b7d..8ff64df25 100644 --- a/pkg/ruler/base/ruler_test.go +++ b/pkg/ruler/base/ruler_test.go @@ -12,7 +12,6 @@ import ( "os" "reflect" "sort" - "strings" "sync" "testing" "time" @@ -211,7 +210,7 @@ func buildRuler(t *testing.T, rulerConfig Config, q storage.Querier, clientMetri require.NoError(t, rulerConfig.Validate(log.NewNopLogger())) engine, queryable, pusher, logger, overrides, reg := testSetup(t, q) - storage, err := NewLegacyRuleStore(rulerConfig.StoreConfig, hedging.Config{}, clientMetrics, promRules.FileLoader{}, log.NewNopLogger()) + storage, err := NewLegacyRuleStore(rulerConfig.StoreConfig, hedging.Config{}, clientMetrics, newDefaultFileLoader(), log.NewNopLogger()) require.NoError(t, err) managerFactory := DefaultTenantManagerFactory(rulerConfig, pusher, queryable, engine, reg, constants.Loki) @@ -281,11 +280,9 @@ func TestNotifierSendsUserIDHeader(t *testing.T) { wg.Wait() // Ensure we have metrics in the notifier. - assert.NoError(t, prom_testutil.GatherAndCompare(manager.registry.(*prometheus.Registry), strings.NewReader(` - # HELP loki_prometheus_notifications_dropped_total Total number of alerts dropped due to errors when sending to Alertmanager. - # TYPE loki_prometheus_notifications_dropped_total counter - loki_prometheus_notifications_dropped_total{user="1"} 0 - `), "loki_prometheus_notifications_dropped_total")) + count, err := prom_testutil.GatherAndCount(manager.registry.(*prometheus.Registry), "loki_prometheus_notifications_dropped_total") + assert.NoError(t, err) + assert.Equal(t, 1, count) } func TestMultiTenantsNotifierSendsUserIDHeader(t *testing.T) { diff --git a/pkg/ruler/base/storage.go b/pkg/ruler/base/storage.go index 068718f54..b4359ec51 100644 --- a/pkg/ruler/base/storage.go +++ b/pkg/ruler/base/storage.go @@ -7,6 +7,9 @@ import ( "github.com/go-kit/log" "github.com/pkg/errors" + "github.com/prometheus/common/model" + "github.com/prometheus/prometheus/model/rulefmt" + "github.com/prometheus/prometheus/promql/parser" promRules "github.com/prometheus/prometheus/rules" "github.com/grafana/loki/v3/pkg/ruler/rulestore" @@ -84,7 +87,7 @@ func NewLegacyRuleStore(cfg RuleStoreConfig, hedgeCfg hedging.Config, clientMetr } if loader == nil { - loader = promRules.FileLoader{} + loader = newDefaultFileLoader() } var err error @@ -122,7 +125,7 @@ func NewLegacyRuleStore(cfg RuleStoreConfig, hedgeCfg hedging.Config, clientMetr func NewRuleStore(ctx context.Context, cfg rulestore.Config, cfgProvider bucket.SSEConfigProvider, loader promRules.GroupLoader, logger log.Logger) (rulestore.RuleStore, error) { if cfg.Backend == local.Name { if loader == nil { - loader = promRules.FileLoader{} + loader = newDefaultFileLoader() } return local.NewLocalRulesClient(cfg.Local, loader) } @@ -134,3 +137,22 @@ func NewRuleStore(ctx context.Context, cfg rulestore.Config, cfgProvider bucket. return bucketclient.NewBucketRuleStore(bucketClient, cfgProvider, logger), nil } + +// defaultFileLoader is a GroupLoader that delegates to rulefmt.ParseFile with a +// default PromQL parser. This replaces direct use of newDefaultFileLoader(), +// whose parser field is unexported and nil by default, causing panics. +type defaultFileLoader struct { + p parser.Parser +} + +func newDefaultFileLoader() defaultFileLoader { + return defaultFileLoader{p: parser.NewParser(parser.Options{})} +} + +func (fl defaultFileLoader) Load(identifier string, ignoreUnknownFields bool, nameValidationScheme model.ValidationScheme) (*rulefmt.RuleGroups, []error) { + return rulefmt.ParseFile(identifier, ignoreUnknownFields, nameValidationScheme, fl.p) +} + +func (fl defaultFileLoader) Parse(query string) (parser.Expr, error) { + return fl.p.ParseExpr(query) +} diff --git a/pkg/ruler/grouploader.go b/pkg/ruler/grouploader.go index 8da963d4b..1c50d6181 100644 --- a/pkg/ruler/grouploader.go +++ b/pkg/ruler/grouploader.go @@ -10,7 +10,7 @@ import ( "github.com/prometheus/prometheus/model/rulefmt" "github.com/prometheus/prometheus/promql/parser" "github.com/prometheus/prometheus/rules" - "gopkg.in/yaml.v3" + "go.yaml.in/yaml/v3" "github.com/grafana/loki/v3/pkg/logql/syntax" ) diff --git a/pkg/ruler/rulestore/local/local_test.go b/pkg/ruler/rulestore/local/local_test.go index 6a2e35e28..fc4af41b1 100644 --- a/pkg/ruler/rulestore/local/local_test.go +++ b/pkg/ruler/rulestore/local/local_test.go @@ -9,9 +9,9 @@ import ( "github.com/prometheus/common/model" "github.com/prometheus/prometheus/model/rulefmt" - promRules "github.com/prometheus/prometheus/rules" + "github.com/prometheus/prometheus/promql/parser" "github.com/stretchr/testify/require" - "gopkg.in/yaml.v3" + "go.yaml.in/yaml/v3" "github.com/grafana/loki/v3/pkg/ruler/rulespb" ) @@ -66,7 +66,7 @@ func TestClient_LoadAllRuleGroups(t *testing.T) { client, err := NewLocalRulesClient(Config{ Directory: dir, - }, promRules.FileLoader{}) + }, testFileLoader{}) require.NoError(t, err) ctx := context.Background() @@ -83,3 +83,13 @@ func TestClient_LoadAllRuleGroups(t *testing.T) { require.Equal(t, rulespb.ToProto(u, namespace2, ruleGroups.Groups[0]), actual[1]) } } + +type testFileLoader struct{} + +func (testFileLoader) Load(identifier string, ignoreUnknownFields bool, nameValidationScheme model.ValidationScheme) (*rulefmt.RuleGroups, []error) { + return rulefmt.ParseFile(identifier, ignoreUnknownFields, nameValidationScheme, parser.NewParser(parser.Options{})) +} + +func (testFileLoader) Parse(query string) (parser.Expr, error) { + return parser.NewParser(parser.Options{}).ParseExpr(query) +} diff --git a/pkg/ruler/storage/instance/instance.go b/pkg/ruler/storage/instance/instance.go index b353c0e77..bce77d070 100644 --- a/pkg/ruler/storage/instance/instance.go +++ b/pkg/ruler/storage/instance/instance.go @@ -522,6 +522,7 @@ type walStorage interface { WriteStalenessMarkers(remoteTsFunc func() int64) error SetWriteNotified(wlog.WriteNotified) Appender(context.Context) storage.Appender + AppenderV2(context.Context) storage.AppenderV2 Truncate(mint int64) error Close() error diff --git a/pkg/ruler/storage/instance/instance_test.go b/pkg/ruler/storage/instance/instance_test.go index ee9985017..9daff16a9 100644 --- a/pkg/ruler/storage/instance/instance_test.go +++ b/pkg/ruler/storage/instance/instance_test.go @@ -252,6 +252,28 @@ func (s *mockWalStorage) Appender(context.Context) storage.Appender { return &mockAppender{s: s} } +func (s *mockWalStorage) AppenderV2(ctx context.Context) storage.AppenderV2 { + return &mockAppenderV2{inner: s.Appender(ctx)} +} + +type mockAppenderV2 struct { + inner storage.Appender +} + +func (a *mockAppenderV2) Append(ref storage.SeriesRef, ls labels.Labels, _, t int64, v float64, h *histogram.Histogram, fh *histogram.FloatHistogram, _ storage.AppendV2Options) (storage.SeriesRef, error) { + switch { + case fh != nil: + return a.inner.AppendHistogram(ref, ls, t, nil, fh) + case h != nil: + return a.inner.AppendHistogram(ref, ls, t, h, nil) + default: + return a.inner.Append(ref, ls, t, v) + } +} + +func (a *mockAppenderV2) Commit() error { return a.inner.Commit() } +func (a *mockAppenderV2) Rollback() error { return a.inner.Rollback() } + type mockAppender struct { s *mockWalStorage } diff --git a/pkg/ruler/storage/wal/wal.go b/pkg/ruler/storage/wal/wal.go index e81ace09c..0ec1a6b1d 100644 --- a/pkg/ruler/storage/wal/wal.go +++ b/pkg/ruler/storage/wal/wal.go @@ -328,6 +328,45 @@ func (w *Storage) Appender(_ context.Context) storage.Appender { return w.appenderPool.Get().(storage.Appender) } +// AppenderV2 returns a new AppenderV2 against the storage. +func (w *Storage) AppenderV2(ctx context.Context) storage.AppenderV2 { + return &appenderV2Adapter{inner: w.Appender(ctx)} +} + +// appenderV2Adapter wraps a v1 Appender to satisfy the AppenderV2 interface. +type appenderV2Adapter struct { + inner storage.Appender +} + +func (a *appenderV2Adapter) Append(ref storage.SeriesRef, ls labels.Labels, _, t int64, v float64, h *histogram.Histogram, fh *histogram.FloatHistogram, opts storage.AppendV2Options) (storage.SeriesRef, error) { + var sRef storage.SeriesRef + var err error + + switch { + case fh != nil: + sRef, err = a.inner.AppendHistogram(ref, ls, t, nil, fh) + case h != nil: + sRef, err = a.inner.AppendHistogram(ref, ls, t, h, nil) + default: + sRef, err = a.inner.Append(ref, ls, t, v) + } + if err != nil { + return 0, err + } + + // Forward exemplars to the inner appender per the AppenderV2 contract. + var pErr storage.AppendPartialError + for _, e := range opts.Exemplars { + if _, exemplarErr := a.inner.AppendExemplar(sRef, ls, e); exemplarErr != nil { + pErr.ExemplarErrors = append(pErr.ExemplarErrors, exemplarErr) + } + } + return sRef, pErr.ToError() +} + +func (a *appenderV2Adapter) Commit() error { return a.inner.Commit() } +func (a *appenderV2Adapter) Rollback() error { return a.inner.Rollback() } + // StartTime always returns 0, nil. It is implemented for compatibility with // Prometheus, but is unused in the agent. func (*Storage) StartTime() (int64, error) { diff --git a/pkg/storage/bucket/s3/config_test.go b/pkg/storage/bucket/s3/config_test.go index 8f27d01b1..727604e85 100644 --- a/pkg/storage/bucket/s3/config_test.go +++ b/pkg/storage/bucket/s3/config_test.go @@ -15,7 +15,7 @@ import ( "github.com/grafana/dskit/flagext" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gopkg.in/yaml.v3" + "go.yaml.in/yaml/v3" ) func TestSSEConfig_Validate(t *testing.T) { diff --git a/pkg/storage/chunk/client/gcp/gcs_object_client.go b/pkg/storage/chunk/client/gcp/gcs_object_client.go index 7b23c21cc..34eddaedc 100644 --- a/pkg/storage/chunk/client/gcp/gcs_object_client.go +++ b/pkg/storage/chunk/client/gcp/gcs_object_client.go @@ -370,7 +370,7 @@ func gcsTransport(ctx context.Context, scope string, insecure bool, http2 bool, transportOptions = append(transportOptions, option.WithoutAuthentication()) } if serviceAccount.String() != "" { - transportOptions = append(transportOptions, option.WithCredentialsJSON([]byte(serviceAccount.String()))) + transportOptions = append(transportOptions, option.WithAuthCredentialsJSON(option.ServiceAccount, []byte(serviceAccount.String()))) } return google_http.NewTransport(ctx, customTransport, transportOptions...) } diff --git a/pkg/storage/stores/series/index/schema_test.go b/pkg/storage/stores/series/index/schema_test.go index 5a7493608..14c647386 100644 --- a/pkg/storage/stores/series/index/schema_test.go +++ b/pkg/storage/stores/series/index/schema_test.go @@ -195,7 +195,7 @@ func BenchmarkEncodeLabelsString(b *testing.B) { var err error for n := 0; n < b.N; n++ { data = []byte(lbs.String()) - decoded, err = parser.ParseMetric(string(data)) + decoded, err = parser.NewParser(parser.Options{}).ParseMetric(string(data)) if err != nil { panic(err) } diff --git a/pkg/storage/stores/series/series_store_test.go b/pkg/storage/stores/series/series_store_test.go index 9c406b060..284618c8b 100644 --- a/pkg/storage/stores/series/series_store_test.go +++ b/pkg/storage/stores/series/series_store_test.go @@ -399,7 +399,7 @@ func TestChunkStore_getMetricNameChunks(t *testing.T) { for _, tc := range testCases { t.Run(fmt.Sprintf("%s / %s / %s", tc.query, schema, storeCase.name), func(t *testing.T) { - matchers, err := parser.ParseMetricSelector(tc.query) + matchers, err := parser.NewParser(parser.Options{}).ParseMetricSelector(tc.query) if err != nil { t.Fatal(err) } @@ -528,7 +528,7 @@ func Test_GetSeries(t *testing.T) { for _, tc := range testCases { t.Run(fmt.Sprintf("%s / %s / %s", tc.query, schema, storeCase.name), func(t *testing.T) { - matchers, err := parser.ParseMetricSelector(tc.query) + matchers, err := parser.NewParser(parser.Options{}).ParseMetricSelector(tc.query) if err != nil { t.Fatal(err) } @@ -584,7 +584,7 @@ func Test_GetSeriesShard(t *testing.T) { for _, tc := range testCases { t.Run(fmt.Sprintf("%s / %s / %s", tc.query, "v12", storeCase.name), func(t *testing.T) { t.Log("========= Running query", tc.query, "with schema", "v12") - matchers, err := parser.ParseMetricSelector(tc.query) + matchers, err := parser.NewParser(parser.Options{}).ParseMetricSelector(tc.query) if err != nil { t.Fatal(err) } @@ -659,7 +659,7 @@ func TestChunkStoreError(t *testing.T) { store, _ := newTestChunkStore(t, schema) defer store.Stop() - matchers, err := parser.ParseMetricSelector(tc.query) + matchers, err := parser.NewParser(parser.Options{}).ParseMetricSelector(tc.query) require.NoError(t, err) // Query with ordinary time-range diff --git a/pkg/tool/client/alerts.go b/pkg/tool/client/alerts.go index 7b59aabbc..8bc7237fd 100644 --- a/pkg/tool/client/alerts.go +++ b/pkg/tool/client/alerts.go @@ -6,7 +6,7 @@ import ( "github.com/pkg/errors" log "github.com/sirupsen/logrus" - "gopkg.in/yaml.v3" + "go.yaml.in/yaml/v3" ) const alertmanagerAPIPath = "/api/v1/alerts" diff --git a/pkg/tool/client/rules.go b/pkg/tool/client/rules.go index d662794d8..298369ab0 100644 --- a/pkg/tool/client/rules.go +++ b/pkg/tool/client/rules.go @@ -8,7 +8,7 @@ import ( "github.com/pkg/errors" log "github.com/sirupsen/logrus" - "gopkg.in/yaml.v3" + "go.yaml.in/yaml/v3" "github.com/grafana/loki/v3/pkg/tool/rules/rwrulefmt" ) diff --git a/pkg/tool/commands/rules.go b/pkg/tool/commands/rules.go index e24f46317..99fc6d52a 100644 --- a/pkg/tool/commands/rules.go +++ b/pkg/tool/commands/rules.go @@ -14,7 +14,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/prometheus/model/rulefmt" log "github.com/sirupsen/logrus" - yamlv3 "gopkg.in/yaml.v3" + yamlv3 "go.yaml.in/yaml/v3" "github.com/grafana/loki/v3/pkg/tool/client" "github.com/grafana/loki/v3/pkg/tool/printer" diff --git a/pkg/tool/printer/printer.go b/pkg/tool/printer/printer.go index e0efa0694..857b0730c 100644 --- a/pkg/tool/printer/printer.go +++ b/pkg/tool/printer/printer.go @@ -11,7 +11,7 @@ import ( "github.com/alecthomas/chroma/v2/quick" "github.com/mitchellh/colorstring" - "gopkg.in/yaml.v3" + "go.yaml.in/yaml/v3" "github.com/grafana/loki/v3/pkg/tool/rules" "github.com/grafana/loki/v3/pkg/tool/rules/rwrulefmt" diff --git a/pkg/tool/rules/compare.go b/pkg/tool/rules/compare.go index de9493bf2..bf9df26ca 100644 --- a/pkg/tool/rules/compare.go +++ b/pkg/tool/rules/compare.go @@ -8,7 +8,7 @@ import ( "github.com/mitchellh/colorstring" "github.com/prometheus/prometheus/model/rulefmt" - yaml "gopkg.in/yaml.v3" + yaml "go.yaml.in/yaml/v3" "github.com/grafana/loki/v3/pkg/tool/rules/rwrulefmt" ) diff --git a/pkg/tool/rules/parser.go b/pkg/tool/rules/parser.go index aa8f83363..14fe5ede7 100644 --- a/pkg/tool/rules/parser.go +++ b/pkg/tool/rules/parser.go @@ -10,7 +10,7 @@ import ( "github.com/prometheus/prometheus/model/rulefmt" log "github.com/sirupsen/logrus" - yaml "gopkg.in/yaml.v3" + yaml "go.yaml.in/yaml/v3" "github.com/grafana/loki/v3/pkg/ruler" ) diff --git a/pkg/tool/rules/rules.go b/pkg/tool/rules/rules.go index adc5f63f5..b48c3d663 100644 --- a/pkg/tool/rules/rules.go +++ b/pkg/tool/rules/rules.go @@ -8,7 +8,7 @@ import ( "github.com/prometheus/prometheus/model/rulefmt" "github.com/prometheus/prometheus/promql/parser" log "github.com/sirupsen/logrus" - "gopkg.in/yaml.v3" + "go.yaml.in/yaml/v3" logql "github.com/grafana/loki/v3/pkg/logql/syntax" @@ -120,7 +120,7 @@ func (r RuleNamespace) AggregateBy(label string, applyTo func(group rwrulefmt.Ru } log.WithFields(log.Fields{"rule": getRuleName(rule)}).Debugf("evaluating...") - exp, err := parser.ParseExpr(rule.Expr) + exp, err := parser.NewParser(parser.Options{}).ParseExpr(rule.Expr) if err != nil { return count, mod, err } @@ -246,7 +246,7 @@ func ValidateRuleGroup(g rwrulefmt.RuleGroup) []error { Alert: yaml.Node{Value: r.Alert}, Expr: yaml.Node{Value: r.Expr}, } - for _, err := range r.Validate(ruleNode, model.UTF8Validation) { + for _, err := range r.Validate(ruleNode, model.UTF8Validation, parser.NewParser(parser.Options{})) { var ruleName string if r.Alert != "" { ruleName = r.Alert diff --git a/pkg/ui/cluster.go b/pkg/ui/cluster.go index 1ddb0b080..0224e2bd6 100644 --- a/pkg/ui/cluster.go +++ b/pkg/ui/cluster.go @@ -10,8 +10,8 @@ import ( "sync" "github.com/go-kit/log/level" + "go.yaml.in/yaml/v3" "golang.org/x/sync/errgroup" - "gopkg.in/yaml.v3" "github.com/grafana/dskit/ring" diff --git a/pkg/util/marshal/labels.go b/pkg/util/marshal/labels.go index c5e19e979..4179b16e2 100644 --- a/pkg/util/marshal/labels.go +++ b/pkg/util/marshal/labels.go @@ -9,7 +9,7 @@ import ( // NewLabelSet constructs a Labelset from a promql metric list as a string func NewLabelSet(s string) (loghttp.LabelSet, error) { - lbls, err := parser.ParseMetric(s) + lbls, err := parser.NewParser(parser.Options{}).ParseMetric(s) if err != nil { return nil, err } diff --git a/pkg/util/marshal/query.go b/pkg/util/marshal/query.go index 8e2a2caa0..6ac48545b 100644 --- a/pkg/util/marshal/query.go +++ b/pkg/util/marshal/query.go @@ -413,7 +413,7 @@ func encodeStream(stream logproto.Stream, s *jsoniter.Stream, encodeFlags httpre s.WriteObjectField("stream") s.WriteObjectStart() - lbls, err := parser.ParseMetric(stream.Labels) + lbls, err := parser.NewParser(parser.Options{}).ParseMetric(stream.Labels) if err != nil { return err } diff --git a/tools/deprecated-config-checker/checker/checker.go b/tools/deprecated-config-checker/checker/checker.go index af8af9ae9..063a9220f 100644 --- a/tools/deprecated-config-checker/checker/checker.go +++ b/tools/deprecated-config-checker/checker/checker.go @@ -6,7 +6,7 @@ import ( "os" "strings" - "gopkg.in/yaml.v3" + "go.yaml.in/yaml/v3" ) const ( diff --git a/tools/doc-generator/writer.go b/tools/doc-generator/writer.go index 7a71c6706..12b73ab16 100644 --- a/tools/doc-generator/writer.go +++ b/tools/doc-generator/writer.go @@ -13,7 +13,7 @@ import ( "github.com/grafana/regexp" "github.com/mitchellh/go-wordwrap" - "gopkg.in/yaml.v3" + "go.yaml.in/yaml/v3" "github.com/grafana/loki/v3/tools/doc-generator/parse" ) diff --git a/vendor/cloud.google.com/go/auth/CHANGES.md b/vendor/cloud.google.com/go/auth/CHANGES.md index 36c7db49c..b48828c15 100644 --- a/vendor/cloud.google.com/go/auth/CHANGES.md +++ b/vendor/cloud.google.com/go/auth/CHANGES.md @@ -1,5 +1,12 @@ # Changes +## [0.18.1](https://github.com/googleapis/google-cloud-go/releases/tag/auth%2Fv0.18.1) (2026-01-21) + +### Bug Fixes + +* add InternalOptions.TelemetryAttributes for internal client use (#13641) ([3876978](https://github.com/googleapis/google-cloud-go/commit/38769789755ed47d85e85dcd56596109de65f780)) +* remove singleton and restore normal usage of otelgrpc.clientHandler (#13522) ([673d4b0](https://github.com/googleapis/google-cloud-go/commit/673d4b05617f833aa433f7f6a350b5cb888ea20d)) + ## [0.18.0](https://github.com/googleapis/google-cloud-go/releases/tag/auth%2Fv0.18.0) (2025-12-15) ### Features diff --git a/vendor/cloud.google.com/go/auth/grpctransport/grpctransport.go b/vendor/cloud.google.com/go/auth/grpctransport/grpctransport.go index 6bcd3ef54..9b4b1f06e 100644 --- a/vendor/cloud.google.com/go/auth/grpctransport/grpctransport.go +++ b/vendor/cloud.google.com/go/auth/grpctransport/grpctransport.go @@ -24,7 +24,6 @@ import ( "log/slog" "net/http" "os" - "sync" "cloud.google.com/go/auth" "cloud.google.com/go/auth/credentials" @@ -36,7 +35,6 @@ import ( "google.golang.org/grpc" grpccreds "google.golang.org/grpc/credentials" grpcinsecure "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/stats" ) const ( @@ -54,27 +52,6 @@ var ( timeoutDialerOption grpc.DialOption ) -// otelStatsHandler is a singleton otelgrpc.clientHandler to be used across -// all dial connections to avoid the memory leak documented in -// https://github.com/open-telemetry/opentelemetry-go-contrib/issues/4226 -// -// TODO: When this module depends on a version of otelgrpc containing the fix, -// replace this singleton with inline usage for simplicity. -// The fix should be in https://github.com/open-telemetry/opentelemetry-go/pull/5797. -var ( - initOtelStatsHandlerOnce sync.Once - otelStatsHandler stats.Handler -) - -// otelGRPCStatsHandler returns singleton otelStatsHandler for reuse across all -// dial connections. -func otelGRPCStatsHandler() stats.Handler { - initOtelStatsHandlerOnce.Do(func() { - otelStatsHandler = otelgrpc.NewClientHandler() - }) - return otelStatsHandler -} - // ClientCertProvider is a function that returns a TLS client certificate to be // used when opening TLS connections. It follows the same semantics as // [crypto/tls.Config.GetClientCertificate]. @@ -223,6 +200,15 @@ type InternalOptions struct { // SkipValidation bypasses validation on Options. It should only be used // internally for clients that needs more control over their transport. SkipValidation bool + // TelemetryAttributes specifies a map of telemetry attributes to be added + // to all OpenTelemetry signals, such as tracing and metrics, for purposes + // including representing the static identity of the client (e.g., service + // name, version). These attributes are expected to be consistent across all + // signals to enable cross-signal correlation. + // + // It should only be used internally by generated clients. Callers should not + // modify the map after it is passed in. + TelemetryAttributes map[string]string } // Dial returns a GRPCClientConnPool that can be used to communicate with a @@ -444,5 +430,5 @@ func addOpenTelemetryStatsHandler(dialOpts []grpc.DialOption, opts *Options) []g if opts.DisableTelemetry { return dialOpts } - return append(dialOpts, grpc.WithStatsHandler(otelGRPCStatsHandler())) + return append(dialOpts, grpc.WithStatsHandler(otelgrpc.NewClientHandler())) } diff --git a/vendor/cloud.google.com/go/auth/httptransport/httptransport.go b/vendor/cloud.google.com/go/auth/httptransport/httptransport.go index c9126535d..bd693907f 100644 --- a/vendor/cloud.google.com/go/auth/httptransport/httptransport.go +++ b/vendor/cloud.google.com/go/auth/httptransport/httptransport.go @@ -168,6 +168,15 @@ type InternalOptions struct { // for the credentials. It should only be used internally for clients that // need more control over their transport. The default is false. SkipUniverseDomainValidation bool + // TelemetryAttributes specifies a map of telemetry attributes to be added + // to all OpenTelemetry signals, such as tracing and metrics, for purposes + // including representing the static identity of the client (e.g., service + // name, version). These attributes are expected to be consistent across all + // signals to enable cross-signal correlation. + // + // It should only be used internally by generated clients. Callers should not + // modify the map after it is passed in. + TelemetryAttributes map[string]string } // AddAuthorizationMiddleware adds a middleware to the provided client's diff --git a/vendor/cloud.google.com/go/auth/internal/version.go b/vendor/cloud.google.com/go/auth/internal/version.go index 702a6840d..ec7f21a8a 100644 --- a/vendor/cloud.google.com/go/auth/internal/version.go +++ b/vendor/cloud.google.com/go/auth/internal/version.go @@ -1,4 +1,4 @@ -// Copyright 2025 Google LLC +// Copyright 2026 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,4 +17,4 @@ package internal // Version is the current tagged release of the library. -const Version = "0.18.0" +const Version = "0.18.1" diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/CHANGELOG.md b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/CHANGELOG.md index 47d2b85fa..fa477145f 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/CHANGELOG.md +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/CHANGELOG.md @@ -1,5 +1,16 @@ # Release History +## 1.21.0 (2026-01-12) + +### Features Added + +* Added `runtime/datetime` package which provides specialized time type wrappers for serializing and deserializing +time values in various formats used by Azure services. + +### Other Changes + +* Aligned `cloud.AzureGovernment` and `cloud.AzureChina` audience values with Azure CLI + ## 1.20.0 (2025-11-06) ### Features Added diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/client.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/client.go index c373cc43f..d103de88a 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/client.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/client.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/doc.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/doc.go index 1bdd16a3d..370754179 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/doc.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/doc.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright 2017 Microsoft Corporation. All rights reserved. // Use of this source code is governed by an MIT // license that can be found in the LICENSE file. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource/resource_identifier.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource/resource_identifier.go index 8a40ebe4d..c602e77d1 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource/resource_identifier.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource/resource_identifier.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource/resource_type.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource/resource_type.go index ca03ac971..34d09b6d7 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource/resource_type.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource/resource_type.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/policy/policy.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/policy/policy.go index f18caf848..787c8e503 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/policy/policy.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/policy/policy.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/resource_identifier.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/resource_identifier.go index d1c3191f2..7f7b0f141 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/resource_identifier.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/resource_identifier.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/resource_type.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/resource_type.go index fc7fbffd2..270d495e4 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/resource_type.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/resource_type.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/pipeline.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/pipeline.go index 6a7c916b4..fa0f29206 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/pipeline.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/pipeline.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/policy_register_rp.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/policy_register_rp.go index 810ac9d9f..3bfb7d02d 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/policy_register_rp.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/policy_register_rp.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/policy_trace_namespace.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/policy_trace_namespace.go index 6cea18424..cc84d542d 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/policy_trace_namespace.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/policy_trace_namespace.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/runtime.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/runtime.go index 1400d4379..0b58f542e 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/runtime.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/runtime.go @@ -1,6 +1,3 @@ -//go:build go1.16 -// +build go1.16 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. @@ -10,11 +7,11 @@ import "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud" func init() { cloud.AzureChina.Services[cloud.ResourceManager] = cloud.ServiceConfiguration{ - Audience: "https://management.core.chinacloudapi.cn", + Audience: "https://management.core.chinacloudapi.cn/", Endpoint: "https://management.chinacloudapi.cn", } cloud.AzureGovernment.Services[cloud.ResourceManager] = cloud.ServiceConfiguration{ - Audience: "https://management.core.usgovcloudapi.net", + Audience: "https://management.core.usgovcloudapi.net/", Endpoint: "https://management.usgovcloudapi.net", } cloud.AzurePublic.Services[cloud.ResourceManager] = cloud.ServiceConfiguration{ diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud/cloud.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud/cloud.go index 9d077a3e1..4ef739a6c 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud/cloud.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud/cloud.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud/doc.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud/doc.go index 985b1bde2..39d0d489e 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud/doc.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud/doc.go @@ -1,6 +1,3 @@ -//go:build go1.16 -// +build go1.16 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/core.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/core.go index 9d1c2f0c0..c3572985b 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/core.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/core.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/doc.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/doc.go index 654a5f404..4862a9f68 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/doc.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/doc.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright 2017 Microsoft Corporation. All rights reserved. // Use of this source code is governed by an MIT // license that can be found in the LICENSE file. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/errors.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/errors.go index 03cb227d0..66111a403 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/errors.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/errors.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/etag.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/etag.go index 2b19d01f7..4a5d7462d 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/etag.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/etag.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/exported.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/exported.go index 612af11ac..96be304eb 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/exported.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/exported.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/pipeline.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/pipeline.go index e45f831ed..5d9892a05 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/pipeline.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/pipeline.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/request.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/request.go index 9b3f5badb..4bf722630 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/request.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/request.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. @@ -115,7 +112,7 @@ func NewRequest(ctx context.Context, httpMethod string, endpoint string) (*Reque if req.URL.Host == "" { return nil, errors.New("no Host in request URL") } - if !(req.URL.Scheme == "http" || req.URL.Scheme == "https") { + if req.URL.Scheme != "http" && req.URL.Scheme != "https" { return nil, fmt.Errorf("unsupported protocol scheme %s", req.URL.Scheme) } // populate values so that the same instance is propagated across policies diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/response_error.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/response_error.go index 8aec256bd..ef0635bb2 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/response_error.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/response_error.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/log/log.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/log/log.go index 6fc6d1400..d3e7191f7 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/log/log.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/log/log.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/async/async.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/async/async.go index a53462760..fcaf6e168 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/async/async.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/async/async.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. @@ -128,10 +125,11 @@ func (p *Poller[T]) Result(ctx context.Context, out *T) error { } var req *exported.Request var err error - if p.Method == http.MethodPatch || p.Method == http.MethodPut { + switch p.Method { + case http.MethodPatch, http.MethodPut: // for PATCH and PUT, the final GET is on the original resource URL req, err = exported.NewRequest(ctx, http.MethodGet, p.OrigURL) - } else if p.Method == http.MethodPost { + case http.MethodPost: if p.FinalState == pollers.FinalStateViaAzureAsyncOp { // no final GET required } else if p.FinalState == pollers.FinalStateViaOriginalURI { diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/body/body.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/body/body.go index 8751b0514..8eebebf78 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/body/body.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/body/body.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/fake/fake.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/fake/fake.go index 7f8d11b8b..3284ea709 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/fake/fake.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/fake/fake.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/loc/loc.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/loc/loc.go index 048285275..cbd8e5880 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/loc/loc.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/loc/loc.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/op/op.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/op/op.go index f49633189..a89aed378 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/op/op.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/op/op.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/poller.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/poller.go index 37ed647f4..2f15bc1de 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/poller.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/poller.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/util.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/util.go index 6a7a32e03..de5454319 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/util.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/util.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. @@ -165,7 +162,10 @@ func ResultHelper[T any](resp *http.Response, failed bool, jsonPath string, out return nil } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() + if !poller.StatusCodeValid(resp) || failed { // the LRO failed. unmarshall the error and update state return azexported.NewResponseError(resp) diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/constants.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/constants.go index f15200091..213202e33 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/constants.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/constants.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. @@ -40,5 +37,5 @@ const ( Module = "azcore" // Version is the semantic version (see http://semver.org) of this module. - Version = "v1.20.0" + Version = "v1.21.0" ) diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/shared.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/shared.go index d3da2c5fd..e82d4f00c 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/shared.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/shared.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/log/doc.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/log/doc.go index 2f3901bff..174bbf99b 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/log/doc.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/log/doc.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright 2017 Microsoft Corporation. All rights reserved. // Use of this source code is governed by an MIT // license that can be found in the LICENSE file. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/log/log.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/log/log.go index f260dac36..6880cd9cd 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/log/log.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/log/log.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/policy/doc.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/policy/doc.go index fad2579ed..01d788ad2 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/policy/doc.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/policy/doc.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright 2017 Microsoft Corporation. All rights reserved. // Use of this source code is governed by an MIT // license that can be found in the LICENSE file. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/policy/policy.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/policy/policy.go index 368a2199e..074d1a600 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/policy/policy.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/policy/policy.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/doc.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/doc.go index c9cfa438c..2c169da79 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/doc.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/doc.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright 2017 Microsoft Corporation. All rights reserved. // Use of this source code is governed by an MIT // license that can be found in the LICENSE file. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/errors.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/errors.go index c0d56158e..931d6f862 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/errors.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/errors.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/pager.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/pager.go index edb4a3cd4..743513be4 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/pager.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/pager.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/pipeline.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/pipeline.go index 6b1f5c083..3d95fe30d 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/pipeline.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/pipeline.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_api_version.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_api_version.go index c3646feb5..21c1430be 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_api_version.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_api_version.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_body_download.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_body_download.go index 99dc029f0..08ce2e4b0 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_body_download.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_body_download.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_http_header.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_http_header.go index c230af0af..fa6d643f1 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_http_header.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_http_header.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. @@ -34,6 +31,7 @@ func httpHeaderPolicy(req *policy.Request) (*http.Response, error) { // WithHTTPHeader adds the specified http.Header to the parent context. // Use this to specify custom HTTP headers at the API-call level. // Any overlapping headers will have their values replaced with the values specified here. +// // Deprecated: use [policy.WithHTTPHeader] instead. func WithHTTPHeader(parent context.Context, header http.Header) context.Context { return policy.WithHTTPHeader(parent, header) diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_http_trace.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_http_trace.go index f375195c4..ddf9ede01 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_http_trace.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_http_trace.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_include_response.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_include_response.go index bb00f6c2f..eaa6c7375 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_include_response.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_include_response.go @@ -1,6 +1,3 @@ -//go:build go1.16 -// +build go1.16 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. @@ -29,6 +26,7 @@ func includeResponsePolicy(req *policy.Request) (*http.Response, error) { // WithCaptureResponse applies the HTTP response retrieval annotation to the parent context. // The resp parameter will contain the HTTP response after the request has completed. +// // Deprecated: use [policy.WithCaptureResponse] instead. func WithCaptureResponse(parent context.Context, resp **http.Response) context.Context { return policy.WithCaptureResponse(parent, resp) diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_logging.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_logging.go index f048d7fb5..dd59fbc99 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_logging.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_logging.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_request_id.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_request_id.go index 360a7f211..a8f1cbac3 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_request_id.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_request_id.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_retry.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_retry.go index 4c3a31fea..696e1d9f4 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_retry.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_retry.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. @@ -117,7 +114,10 @@ func (p *retryPolicy) Do(req *policy.Request) (resp *http.Response, err error) { // wrap the body so we control when it's actually closed. // do this outside the for loop so defers don't accumulate. rwbody = &retryableRequestBody{body: req.Body()} - defer rwbody.realClose() + defer func() { + // TODO: https://github.com/Azure/azure-sdk-for-go/issues/25649 + _ = rwbody.realClose() + }() } try := int32(1) for { @@ -222,6 +222,7 @@ func (p *retryPolicy) Do(req *policy.Request) (resp *http.Response, err error) { // WithRetryOptions adds the specified RetryOptions to the parent context. // Use this to specify custom RetryOptions at the API-call level. +// // Deprecated: use [policy.WithRetryOptions] instead. func WithRetryOptions(parent context.Context, options policy.RetryOptions) context.Context { return policy.WithRetryOptions(parent, options) diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_telemetry.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_telemetry.go index 80a903546..2c60e9d23 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_telemetry.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_telemetry.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/poller.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/poller.go index a89ae9b7b..2e172cdd1 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/poller.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/poller.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. @@ -87,7 +84,10 @@ func NewPoller[T any](resp *http.Response, pl exported.Pipeline, options *NewPol }, nil } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() + // this is a back-stop in case the swagger is incorrect (i.e. missing one or more status codes for success). // ideally the codegen should return an error if the initial response failed and not even create a poller. if !poller.StatusCodeValid(resp) { diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/request.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/request.go index 7d34b7803..df7826b76 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/request.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/request.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/response.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/response.go index 048566e02..e95f8c8ec 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/response.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/response.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. @@ -84,8 +81,9 @@ func UnmarshalAsXML(resp *http.Response, v any) error { // Drain reads the response body to completion then closes it. The bytes read are discarded. func Drain(resp *http.Response) { if resp != nil && resp.Body != nil { + // TODO: this might not be necessary when the bodyDownloadPolicy is in play _, _ = io.Copy(io.Discard, resp.Body) - resp.Body.Close() + _ = resp.Body.Close() } } diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/transport_default_http_client.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/transport_default_http_client.go index 2124c1d48..928e9bf92 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/transport_default_http_client.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/transport_default_http_client.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming/doc.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming/doc.go index cadaef3d5..10d041486 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming/doc.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming/doc.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright 2017 Microsoft Corporation. All rights reserved. // Use of this source code is governed by an MIT // license that can be found in the LICENSE file. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming/progress.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming/progress.go index 2468540bd..c93824a66 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming/progress.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming/progress.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/to/doc.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/to/doc.go index faa98c9dc..13263fbc9 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/to/doc.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/to/doc.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright 2017 Microsoft Corporation. All rights reserved. // Use of this source code is governed by an MIT // license that can be found in the LICENSE file. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/to/to.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/to/to.go index e0e4817b9..e434f7795 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/to/to.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/to/to.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/tracing/constants.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/tracing/constants.go index 80282d4ab..c00c21a3b 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/tracing/constants.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/tracing/constants.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/tracing/tracing.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/tracing/tracing.go index 1ade7c560..8f3248560 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/tracing/tracing.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/tracing/tracing.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/vendor/github.com/Azure/go-ansiterm/SECURITY.md b/vendor/github.com/Azure/go-ansiterm/SECURITY.md new file mode 100644 index 000000000..e138ec5d6 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/vendor/github.com/Azure/go-ansiterm/osc_string_state.go b/vendor/github.com/Azure/go-ansiterm/osc_string_state.go index 593b10ab6..194d5e9c9 100644 --- a/vendor/github.com/Azure/go-ansiterm/osc_string_state.go +++ b/vendor/github.com/Azure/go-ansiterm/osc_string_state.go @@ -11,21 +11,13 @@ func (oscState oscStringState) Handle(b byte) (s state, e error) { return nextState, err } - switch { - case isOscStringTerminator(b): + // There are several control characters and sequences which can + // terminate an OSC string. Most of them are handled by the baseState + // handler. The ANSI_BEL character is a special case which behaves as a + // terminator only for an OSC string. + if b == ANSI_BEL { return oscState.parser.ground, nil } return oscState, nil } - -// See below for OSC string terminators for linux -// http://man7.org/linux/man-pages/man4/console_codes.4.html -func isOscStringTerminator(b byte) bool { - - if b == ANSI_BEL || b == 0x5C { - return true - } - - return false -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/CHANGELOG.md index bacdc6d20..6f3ae3876 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/CHANGELOG.md @@ -1,3 +1,35 @@ +# v1.285.0 (2026-01-29) + +* **Feature**: G7e instances feature up to 8 NVIDIA RTX PRO 6000 Blackwell Server Edition GPUs with 768 GB of memory and 5th generation Intel Xeon Scalable processors. Supporting up to 192 vCPUs, 1600 Gbps networking bandwidth with EFA, up to 2 TiB of system memory, and up to 15.2 TB of local NVMe SSD storage. + +# v1.284.0 (2026-01-28) + +* **Feature**: SearchTransitGatewayRoutes API response now includes a NextToken field, enabling pagination when retrieving large sets of transit gateway routes. Pass the returned NextToken value in subsequent requests to retrieve the next page of results. + +# v1.283.0 (2026-01-27) + +* **Feature**: Releasing new EC2 instances. C8gb and M8gb with highest EBS performance, M8gn with 600 Gbps network bandwidth, X8aedz and M8azn with 5GHz AMD processors, X8i with Intel Xeon 6 processors and up to 6TB memory, and Mac-m4max with Apple M4 Max chip for 25 percent faster builds. + +# v1.282.0 (2026-01-26) + +* **Feature**: DescribeInstanceTypes API response now includes an additionalFlexibleNetworkInterfaces field, the number of interfaces attachable to an instance when using flexible Elastic Network Adapter (ENA) queues in addition to the base number specified by maximumNetworkInterfaces. + +# v1.281.0 (2026-01-22) + +* **Feature**: Add better support for fractional GPU instances in DescribeInstanceTypes API. The new fields, logicalGpuCount, gpuPartitionSize, and workload array enable better GPU resource selection and filtering for both full and fractional GPU instance types. + +# v1.280.0 (2026-01-21) + +* **Feature**: Added support of multiple EBS cards. New EbsCardIndex parameter enables attaching volumes to specific EBS cards on supported instance types for improved storage performance. + +# v1.279.2 (2026-01-15) + +* **Documentation**: This release includes documentation updates to support up to four Elastic Volume modifications per Amazon EBS volume within a rolling 24-hour period. + +# v1.279.1 (2026-01-09) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.279.0 (2025-12-22) * **Feature**: Adds support for linkedGroupId on the CreatePlacementGroup and DescribePlacementGroups APIs. The linkedGroupId parameter is reserved for future use. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/api_op_AttachVolume.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/api_op_AttachVolume.go index 70a0242bf..a88337a8c 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/api_op_AttachVolume.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/api_op_AttachVolume.go @@ -80,6 +80,10 @@ type AttachVolumeInput struct { // UnauthorizedOperation . DryRun *bool + // The index of the EBS card. Some instance types support multiple EBS cards. The + // default EBS card index is 0. + EbsCardIndex *int32 + noSmithyDocumentSerde } @@ -102,6 +106,10 @@ type AttachVolumeOutput struct { // parameter returns null . Device *string + // The index of the EBS card. Some instance types support multiple EBS cards. The + // default EBS card index is 0. + EbsCardIndex *int32 + // The ID of the instance. // // If the volume is attached to an Amazon Web Services-managed resource, this diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/api_op_DetachVolume.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/api_op_DetachVolume.go index 2c6980eb1..8f3d6c86b 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/api_op_DetachVolume.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/api_op_DetachVolume.go @@ -96,6 +96,10 @@ type DetachVolumeOutput struct { // parameter returns null . Device *string + // The index of the EBS card. Some instance types support multiple EBS cards. The + // default EBS card index is 0. + EbsCardIndex *int32 + // The ID of the instance. // // If the volume is attached to an Amazon Web Services-managed resource, this diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/api_op_GetIpamPolicyOrganizationTargets.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/api_op_GetIpamPolicyOrganizationTargets.go index d9a536686..72fb803bd 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/api_op_GetIpamPolicyOrganizationTargets.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/api_op_GetIpamPolicyOrganizationTargets.go @@ -70,7 +70,10 @@ type GetIpamPolicyOrganizationTargetsOutput struct { // The token to use to retrieve the next page of results. NextToken *string - // The Amazon Web Services Organizations targets for an IPAM policy. + // The IDs of the Amazon Web Services Organizations targets. + // + // A target can be an individual Amazon Web Services account or an entity within + // an Amazon Web Services Organization to which an IPAM policy can be applied. OrganizationTargets []types.IpamPolicyOrganizationTarget // Metadata pertaining to the operation's result. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/api_op_ModifyVolume.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/api_op_ModifyVolume.go index bc3f4a281..4eecfb917 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/api_op_ModifyVolume.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/api_op_ModifyVolume.go @@ -26,9 +26,13 @@ import ( // With previous-generation instance types, resizing an EBS volume might require // detaching and reattaching the volume or stopping and restarting the instance. // -// After modifying a volume, you must wait at least six hours and ensure that the -// volume is in the in-use or available state before you can modify the same -// volume. This is sometimes referred to as a cooldown period. +// After you initiate a volume modification, you must wait for that modification +// to reach the completed state before you can initiate another modification for +// the same volume. You can modify a volume up to four times within a rolling +// 24-hour period, as long as the volume is in the in-use or available state, and +// all previous modifications for that volume are completed . If you exceed this +// limit, you get an error message that indicates when you can perform your next +// modification. // // [Monitor the progress of volume modifications]: https://docs.aws.amazon.com/ebs/latest/userguide/monitoring-volume-modifications.html // [Amazon EBS Elastic Volumes]: https://docs.aws.amazon.com/ebs/latest/userguide/ebs-modify-volume.html diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/api_op_SearchTransitGatewayRoutes.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/api_op_SearchTransitGatewayRoutes.go index 6f6546180..71603313d 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/api_op_SearchTransitGatewayRoutes.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/api_op_SearchTransitGatewayRoutes.go @@ -76,6 +76,9 @@ type SearchTransitGatewayRoutesInput struct { // is 1000. MaxResults *int32 + // The token for the next page of results. + NextToken *string + noSmithyDocumentSerde } @@ -84,6 +87,10 @@ type SearchTransitGatewayRoutesOutput struct { // Indicates whether there are additional routes available. AdditionalRoutesAvailable *bool + // The token to use to retrieve the next page of results. This value is null when + // there are no more results to return. + NextToken *string + // Information about the routes. Routes []types.TransitGatewayRoute @@ -193,6 +200,103 @@ func (c *Client) addOperationSearchTransitGatewayRoutesMiddlewares(stack *middle return nil } +// SearchTransitGatewayRoutesPaginatorOptions is the paginator options for +// SearchTransitGatewayRoutes +type SearchTransitGatewayRoutesPaginatorOptions struct { + // The maximum number of routes to return. If a value is not provided, the default + // is 1000. + Limit int32 + + // Set to true if pagination should stop if the service returns a pagination token + // that matches the most recent token provided to the service. + StopOnDuplicateToken bool +} + +// SearchTransitGatewayRoutesPaginator is a paginator for +// SearchTransitGatewayRoutes +type SearchTransitGatewayRoutesPaginator struct { + options SearchTransitGatewayRoutesPaginatorOptions + client SearchTransitGatewayRoutesAPIClient + params *SearchTransitGatewayRoutesInput + nextToken *string + firstPage bool +} + +// NewSearchTransitGatewayRoutesPaginator returns a new +// SearchTransitGatewayRoutesPaginator +func NewSearchTransitGatewayRoutesPaginator(client SearchTransitGatewayRoutesAPIClient, params *SearchTransitGatewayRoutesInput, optFns ...func(*SearchTransitGatewayRoutesPaginatorOptions)) *SearchTransitGatewayRoutesPaginator { + if params == nil { + params = &SearchTransitGatewayRoutesInput{} + } + + options := SearchTransitGatewayRoutesPaginatorOptions{} + if params.MaxResults != nil { + options.Limit = *params.MaxResults + } + + for _, fn := range optFns { + fn(&options) + } + + return &SearchTransitGatewayRoutesPaginator{ + options: options, + client: client, + params: params, + firstPage: true, + nextToken: params.NextToken, + } +} + +// HasMorePages returns a boolean indicating whether more pages are available +func (p *SearchTransitGatewayRoutesPaginator) HasMorePages() bool { + return p.firstPage || (p.nextToken != nil && len(*p.nextToken) != 0) +} + +// NextPage retrieves the next SearchTransitGatewayRoutes page. +func (p *SearchTransitGatewayRoutesPaginator) NextPage(ctx context.Context, optFns ...func(*Options)) (*SearchTransitGatewayRoutesOutput, error) { + if !p.HasMorePages() { + return nil, fmt.Errorf("no more pages available") + } + + params := *p.params + params.NextToken = p.nextToken + + var limit *int32 + if p.options.Limit > 0 { + limit = &p.options.Limit + } + params.MaxResults = limit + + optFns = append([]func(*Options){ + addIsPaginatorUserAgent, + }, optFns...) + result, err := p.client.SearchTransitGatewayRoutes(ctx, ¶ms, optFns...) + if err != nil { + return nil, err + } + p.firstPage = false + + prevToken := p.nextToken + p.nextToken = result.NextToken + + if p.options.StopOnDuplicateToken && + prevToken != nil && + p.nextToken != nil && + *prevToken == *p.nextToken { + p.nextToken = nil + } + + return result, nil +} + +// SearchTransitGatewayRoutesAPIClient is a client that implements the +// SearchTransitGatewayRoutes operation. +type SearchTransitGatewayRoutesAPIClient interface { + SearchTransitGatewayRoutes(context.Context, *SearchTransitGatewayRoutesInput, ...func(*Options)) (*SearchTransitGatewayRoutesOutput, error) +} + +var _ SearchTransitGatewayRoutesAPIClient = (*Client)(nil) + func newServiceMetadataMiddleware_opSearchTransitGatewayRoutes(region string) *awsmiddleware.RegisterServiceMetadata { return &awsmiddleware.RegisterServiceMetadata{ Region: region, diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/deserializers.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/deserializers.go index 8a7d7009c..906db8039 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/deserializers.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/deserializers.go @@ -89728,6 +89728,23 @@ func awsEc2query_deserializeDocumentEbsBlockDevice(v **types.EbsBlockDevice, dec sv.DeleteOnTermination = ptr.Bool(xtv) } + case strings.EqualFold("EbsCardIndex", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.EbsCardIndex = ptr.Int32(int32(i64)) + } + case strings.EqualFold("encrypted", t.Name.Local): val, err := decoder.Value() if err != nil { @@ -90036,6 +90053,229 @@ func awsEc2query_deserializeDocumentEbsBlockDeviceResponse(v **types.EbsBlockDev return nil } +func awsEc2query_deserializeDocumentEbsCardInfo(v **types.EbsCardInfo, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.EbsCardInfo + if *v == nil { + sv = &types.EbsCardInfo{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("baselineBandwidthInMbps", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.BaselineBandwidthInMbps = ptr.Int32(int32(i64)) + } + + case strings.EqualFold("baselineIops", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.BaselineIops = ptr.Int32(int32(i64)) + } + + case strings.EqualFold("baselineThroughputInMBps", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + f64, err := strconv.ParseFloat(xtv, 64) + if err != nil { + return err + } + sv.BaselineThroughputInMBps = ptr.Float64(f64) + } + + case strings.EqualFold("ebsCardIndex", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.EbsCardIndex = ptr.Int32(int32(i64)) + } + + case strings.EqualFold("maximumBandwidthInMbps", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.MaximumBandwidthInMbps = ptr.Int32(int32(i64)) + } + + case strings.EqualFold("maximumIops", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.MaximumIops = ptr.Int32(int32(i64)) + } + + case strings.EqualFold("maximumThroughputInMBps", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + f64, err := strconv.ParseFloat(xtv, 64) + if err != nil { + return err + } + sv.MaximumThroughputInMBps = ptr.Float64(f64) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsEc2query_deserializeDocumentEbsCardInfoList(v *[]types.EbsCardInfo, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv []types.EbsCardInfo + if *v == nil { + sv = make([]types.EbsCardInfo, 0) + } else { + sv = *v + } + + originalDecoder := decoder + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + switch { + case strings.EqualFold("item", t.Name.Local): + var col types.EbsCardInfo + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &col + if err := awsEc2query_deserializeDocumentEbsCardInfo(&destAddr, nodeDecoder); err != nil { + return err + } + col = *destAddr + sv = append(sv, col) + + default: + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsEc2query_deserializeDocumentEbsCardInfoListUnwrapped(v *[]types.EbsCardInfo, decoder smithyxml.NodeDecoder) error { + var sv []types.EbsCardInfo + if *v == nil { + sv = make([]types.EbsCardInfo, 0) + } else { + sv = *v + } + + switch { + default: + var mv types.EbsCardInfo + t := decoder.StartEl + _ = t + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &mv + if err := awsEc2query_deserializeDocumentEbsCardInfo(&destAddr, nodeDecoder); err != nil { + return err + } + mv = *destAddr + sv = append(sv, mv) + } + *v = sv + return nil +} func awsEc2query_deserializeDocumentEbsInfo(v **types.EbsInfo, decoder smithyxml.NodeDecoder) error { if v == nil { return fmt.Errorf("unexpected nil of type %T", v) @@ -90071,6 +90311,12 @@ func awsEc2query_deserializeDocumentEbsInfo(v **types.EbsInfo, decoder smithyxml sv.AttachmentLimitType = types.AttachmentLimitType(xtv) } + case strings.EqualFold("ebsCardSet", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsEc2query_deserializeDocumentEbsCardInfoList(&sv.EbsCards, nodeDecoder); err != nil { + return err + } + case strings.EqualFold("ebsOptimizedInfo", t.Name.Local): nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) if err := awsEc2query_deserializeDocumentEbsOptimizedInfo(&sv.EbsOptimizedInfo, nodeDecoder); err != nil { @@ -90120,6 +90366,23 @@ func awsEc2query_deserializeDocumentEbsInfo(v **types.EbsInfo, decoder smithyxml sv.MaximumEbsAttachments = ptr.Int32(int32(i64)) } + case strings.EqualFold("maximumEbsCards", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.MaximumEbsCards = ptr.Int32(int32(i64)) + } + case strings.EqualFold("nvmeSupport", t.Name.Local): val, err := decoder.Value() if err != nil { @@ -90215,6 +90478,23 @@ func awsEc2query_deserializeDocumentEbsInstanceBlockDevice(v **types.EbsInstance sv.DeleteOnTermination = ptr.Bool(xtv) } + case strings.EqualFold("ebsCardIndex", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.EbsCardIndex = ptr.Int32(int32(i64)) + } + case strings.EqualFold("operator", t.Name.Local): nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) if err := awsEc2query_deserializeDocumentOperatorResponse(&sv.Operator, nodeDecoder); err != nil { @@ -96800,6 +97080,40 @@ func awsEc2query_deserializeDocumentGpuDeviceInfo(v **types.GpuDeviceInfo, decod sv.Count = ptr.Int32(int32(i64)) } + case strings.EqualFold("gpuPartitionSize", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + f64, err := strconv.ParseFloat(xtv, 64) + if err != nil { + return err + } + sv.GpuPartitionSize = ptr.Float64(f64) + } + + case strings.EqualFold("logicalGpuCount", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.LogicalGpuCount = ptr.Int32(int32(i64)) + } + case strings.EqualFold("manufacturer", t.Name.Local): val, err := decoder.Value() if err != nil { @@ -96832,6 +97146,12 @@ func awsEc2query_deserializeDocumentGpuDeviceInfo(v **types.GpuDeviceInfo, decod sv.Name = ptr.String(xtv) } + case strings.EqualFold("workloadSet", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsEc2query_deserializeDocumentWorkloadsList(&sv.Workloads, nodeDecoder); err != nil { + return err + } + default: // Do nothing and ignore the unexpected tag element err = decoder.Decoder.Skip() @@ -119518,6 +119838,23 @@ func awsEc2query_deserializeDocumentLaunchTemplateEbsBlockDevice(v **types.Launc sv.DeleteOnTermination = ptr.Bool(xtv) } + case strings.EqualFold("ebsCardIndex", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.EbsCardIndex = ptr.Int32(int32(i64)) + } + case strings.EqualFold("encrypted", t.Name.Local): val, err := decoder.Value() if err != nil { @@ -127677,6 +128014,23 @@ func awsEc2query_deserializeDocumentNetworkCardInfo(v **types.NetworkCardInfo, d originalDecoder := decoder decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) switch { + case strings.EqualFold("additionalFlexibleNetworkInterfaces", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.AdditionalFlexibleNetworkInterfaces = ptr.Int32(int32(i64)) + } + case strings.EqualFold("baselineBandwidthInGbps", t.Name.Local): val, err := decoder.Value() if err != nil { @@ -166765,6 +167119,23 @@ func awsEc2query_deserializeDocumentVolumeAttachment(v **types.VolumeAttachment, sv.Device = ptr.String(xtv) } + case strings.EqualFold("ebsCardIndex", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.EbsCardIndex = ptr.Int32(int32(i64)) + } + case strings.EqualFold("instanceId", t.Name.Local): val, err := decoder.Value() if err != nil { @@ -172653,6 +173024,86 @@ func awsEc2query_deserializeDocumentVpnTunnelLogOptions(v **types.VpnTunnelLogOp return nil } +func awsEc2query_deserializeDocumentWorkloadsList(v *[]string, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv []string + if *v == nil { + sv = make([]string, 0) + } else { + sv = *v + } + + originalDecoder := decoder + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + memberDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + decoder = memberDecoder + switch { + case strings.EqualFold("item", t.Name.Local): + var col string + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + col = xtv + } + sv = append(sv, col) + + default: + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsEc2query_deserializeDocumentWorkloadsListUnwrapped(v *[]string, decoder smithyxml.NodeDecoder) error { + var sv []string + if *v == nil { + sv = make([]string, 0) + } else { + sv = *v + } + + switch { + default: + var mv string + t := decoder.StartEl + _ = t + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + mv = xtv + } + sv = append(sv, mv) + } + *v = sv + return nil +} func awsEc2query_deserializeOpDocumentAcceptAddressTransferOutput(v **AcceptAddressTransferOutput, decoder smithyxml.NodeDecoder) error { if v == nil { return fmt.Errorf("unexpected nil of type %T", v) @@ -174635,6 +175086,23 @@ func awsEc2query_deserializeOpDocumentAttachVolumeOutput(v **AttachVolumeOutput, sv.Device = ptr.String(xtv) } + case strings.EqualFold("ebsCardIndex", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.EbsCardIndex = ptr.Int32(int32(i64)) + } + case strings.EqualFold("instanceId", t.Name.Local): val, err := decoder.Value() if err != nil { @@ -194879,6 +195347,23 @@ func awsEc2query_deserializeOpDocumentDetachVolumeOutput(v **DetachVolumeOutput, sv.Device = ptr.String(xtv) } + case strings.EqualFold("ebsCardIndex", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.EbsCardIndex = ptr.Int32(int32(i64)) + } + case strings.EqualFold("instanceId", t.Name.Local): val, err := decoder.Value() if err != nil { @@ -209477,6 +209962,19 @@ func awsEc2query_deserializeOpDocumentSearchTransitGatewayRoutesOutput(v **Searc sv.AdditionalRoutesAvailable = ptr.Bool(xtv) } + case strings.EqualFold("nextToken", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.NextToken = ptr.String(xtv) + } + case strings.EqualFold("routeSet", t.Name.Local): nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) if err := awsEc2query_deserializeDocumentTransitGatewayRouteList(&sv.Routes, nodeDecoder); err != nil { diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/go_module_metadata.go index 216410f90..64abb22a8 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/go_module_metadata.go @@ -3,4 +3,4 @@ package ec2 // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.279.0" +const goModuleVersion = "1.285.0" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/serializers.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/serializers.go index 2c340daab..e9a3bcf3a 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/serializers.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/serializers.go @@ -54478,6 +54478,11 @@ func awsEc2query_serializeDocumentEbsBlockDevice(v *types.EbsBlockDevice, value objectKey.Boolean(*v.DeleteOnTermination) } + if v.EbsCardIndex != nil { + objectKey := object.Key("EbsCardIndex") + objectKey.Integer(*v.EbsCardIndex) + } + if v.Encrypted != nil { objectKey := object.Key("Encrypted") objectKey.Boolean(*v.Encrypted) @@ -57555,6 +57560,11 @@ func awsEc2query_serializeDocumentLaunchTemplateEbsBlockDeviceRequest(v *types.L objectKey.Boolean(*v.DeleteOnTermination) } + if v.EbsCardIndex != nil { + objectKey := object.Key("EbsCardIndex") + objectKey.Integer(*v.EbsCardIndex) + } + if v.Encrypted != nil { objectKey := object.Key("Encrypted") objectKey.Boolean(*v.Encrypted) @@ -65212,6 +65222,11 @@ func awsEc2query_serializeOpDocumentAttachVolumeInput(v *AttachVolumeInput, valu objectKey.Boolean(*v.DryRun) } + if v.EbsCardIndex != nil { + objectKey := object.Key("EbsCardIndex") + objectKey.Integer(*v.EbsCardIndex) + } + if v.InstanceId != nil { objectKey := object.Key("InstanceId") objectKey.String(*v.InstanceId) @@ -87730,6 +87745,11 @@ func awsEc2query_serializeOpDocumentSearchTransitGatewayRoutesInput(v *SearchTra objectKey.Integer(*v.MaxResults) } + if v.NextToken != nil { + objectKey := object.Key("NextToken") + objectKey.String(*v.NextToken) + } + if v.TransitGatewayRouteTableId != nil { objectKey := object.Key("TransitGatewayRouteTableId") objectKey.String(*v.TransitGatewayRouteTableId) diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/types/enums.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/types/enums.go index 64c4cbea3..2cf81f96a 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/types/enums.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/types/enums.go @@ -4995,6 +4995,80 @@ const ( InstanceTypeC8a48xlarge InstanceType = "c8a.48xlarge" InstanceTypeC8aMetal24xl InstanceType = "c8a.metal-24xl" InstanceTypeC8aMetal48xl InstanceType = "c8a.metal-48xl" + InstanceTypeC8gb12xlarge InstanceType = "c8gb.12xlarge" + InstanceTypeC8gb16xlarge InstanceType = "c8gb.16xlarge" + InstanceTypeC8gb24xlarge InstanceType = "c8gb.24xlarge" + InstanceTypeC8gb2xlarge InstanceType = "c8gb.2xlarge" + InstanceTypeC8gb4xlarge InstanceType = "c8gb.4xlarge" + InstanceTypeC8gb8xlarge InstanceType = "c8gb.8xlarge" + InstanceTypeC8gbLarge InstanceType = "c8gb.large" + InstanceTypeC8gbMedium InstanceType = "c8gb.medium" + InstanceTypeC8gbMetal24xl InstanceType = "c8gb.metal-24xl" + InstanceTypeC8gbXlarge InstanceType = "c8gb.xlarge" + InstanceTypeC8gb48xlarge InstanceType = "c8gb.48xlarge" + InstanceTypeC8gbMetal48xl InstanceType = "c8gb.metal-48xl" + InstanceTypeM8gb12xlarge InstanceType = "m8gb.12xlarge" + InstanceTypeM8gb16xlarge InstanceType = "m8gb.16xlarge" + InstanceTypeM8gb24xlarge InstanceType = "m8gb.24xlarge" + InstanceTypeM8gb2xlarge InstanceType = "m8gb.2xlarge" + InstanceTypeM8gb4xlarge InstanceType = "m8gb.4xlarge" + InstanceTypeM8gb8xlarge InstanceType = "m8gb.8xlarge" + InstanceTypeM8gbLarge InstanceType = "m8gb.large" + InstanceTypeM8gbMedium InstanceType = "m8gb.medium" + InstanceTypeM8gbXlarge InstanceType = "m8gb.xlarge" + InstanceTypeM8gb48xlarge InstanceType = "m8gb.48xlarge" + InstanceTypeM8gbMetal24xl InstanceType = "m8gb.metal-24xl" + InstanceTypeM8gbMetal48xl InstanceType = "m8gb.metal-48xl" + InstanceTypeM8gn12xlarge InstanceType = "m8gn.12xlarge" + InstanceTypeM8gn16xlarge InstanceType = "m8gn.16xlarge" + InstanceTypeM8gn24xlarge InstanceType = "m8gn.24xlarge" + InstanceTypeM8gn2xlarge InstanceType = "m8gn.2xlarge" + InstanceTypeM8gn48xlarge InstanceType = "m8gn.48xlarge" + InstanceTypeM8gn4xlarge InstanceType = "m8gn.4xlarge" + InstanceTypeM8gn8xlarge InstanceType = "m8gn.8xlarge" + InstanceTypeM8gnLarge InstanceType = "m8gn.large" + InstanceTypeM8gnMedium InstanceType = "m8gn.medium" + InstanceTypeM8gnXlarge InstanceType = "m8gn.xlarge" + InstanceTypeM8gnMetal24xl InstanceType = "m8gn.metal-24xl" + InstanceTypeM8gnMetal48xl InstanceType = "m8gn.metal-48xl" + InstanceTypeX8aedz12xlarge InstanceType = "x8aedz.12xlarge" + InstanceTypeX8aedz24xlarge InstanceType = "x8aedz.24xlarge" + InstanceTypeX8aedz3xlarge InstanceType = "x8aedz.3xlarge" + InstanceTypeX8aedz6xlarge InstanceType = "x8aedz.6xlarge" + InstanceTypeX8aedzLarge InstanceType = "x8aedz.large" + InstanceTypeX8aedzMetal12xl InstanceType = "x8aedz.metal-12xl" + InstanceTypeX8aedzMetal24xl InstanceType = "x8aedz.metal-24xl" + InstanceTypeX8aedzXlarge InstanceType = "x8aedz.xlarge" + InstanceTypeM8aznMedium InstanceType = "m8azn.medium" + InstanceTypeM8aznLarge InstanceType = "m8azn.large" + InstanceTypeM8aznXlarge InstanceType = "m8azn.xlarge" + InstanceTypeM8azn3xlarge InstanceType = "m8azn.3xlarge" + InstanceTypeM8azn6xlarge InstanceType = "m8azn.6xlarge" + InstanceTypeM8azn12xlarge InstanceType = "m8azn.12xlarge" + InstanceTypeM8azn24xlarge InstanceType = "m8azn.24xlarge" + InstanceTypeM8aznMetal12xl InstanceType = "m8azn.metal-12xl" + InstanceTypeM8aznMetal24xl InstanceType = "m8azn.metal-24xl" + InstanceTypeX8iLarge InstanceType = "x8i.large" + InstanceTypeX8iXlarge InstanceType = "x8i.xlarge" + InstanceTypeX8i2xlarge InstanceType = "x8i.2xlarge" + InstanceTypeX8i4xlarge InstanceType = "x8i.4xlarge" + InstanceTypeX8i8xlarge InstanceType = "x8i.8xlarge" + InstanceTypeX8i12xlarge InstanceType = "x8i.12xlarge" + InstanceTypeX8i16xlarge InstanceType = "x8i.16xlarge" + InstanceTypeX8i24xlarge InstanceType = "x8i.24xlarge" + InstanceTypeX8i32xlarge InstanceType = "x8i.32xlarge" + InstanceTypeX8i48xlarge InstanceType = "x8i.48xlarge" + InstanceTypeX8i64xlarge InstanceType = "x8i.64xlarge" + InstanceTypeX8i96xlarge InstanceType = "x8i.96xlarge" + InstanceTypeX8iMetal48xl InstanceType = "x8i.metal-48xl" + InstanceTypeX8iMetal96xl InstanceType = "x8i.metal-96xl" + InstanceTypeMacM4maxMetal InstanceType = "mac-m4max.metal" + InstanceTypeG7e2xlarge InstanceType = "g7e.2xlarge" + InstanceTypeG7e4xlarge InstanceType = "g7e.4xlarge" + InstanceTypeG7e8xlarge InstanceType = "g7e.8xlarge" + InstanceTypeG7e12xlarge InstanceType = "g7e.12xlarge" + InstanceTypeG7e24xlarge InstanceType = "g7e.24xlarge" + InstanceTypeG7e48xlarge InstanceType = "g7e.48xlarge" ) // Values returns all known values for InstanceType. Note that this can be @@ -6101,6 +6175,80 @@ func (InstanceType) Values() []InstanceType { "c8a.48xlarge", "c8a.metal-24xl", "c8a.metal-48xl", + "c8gb.12xlarge", + "c8gb.16xlarge", + "c8gb.24xlarge", + "c8gb.2xlarge", + "c8gb.4xlarge", + "c8gb.8xlarge", + "c8gb.large", + "c8gb.medium", + "c8gb.metal-24xl", + "c8gb.xlarge", + "c8gb.48xlarge", + "c8gb.metal-48xl", + "m8gb.12xlarge", + "m8gb.16xlarge", + "m8gb.24xlarge", + "m8gb.2xlarge", + "m8gb.4xlarge", + "m8gb.8xlarge", + "m8gb.large", + "m8gb.medium", + "m8gb.xlarge", + "m8gb.48xlarge", + "m8gb.metal-24xl", + "m8gb.metal-48xl", + "m8gn.12xlarge", + "m8gn.16xlarge", + "m8gn.24xlarge", + "m8gn.2xlarge", + "m8gn.48xlarge", + "m8gn.4xlarge", + "m8gn.8xlarge", + "m8gn.large", + "m8gn.medium", + "m8gn.xlarge", + "m8gn.metal-24xl", + "m8gn.metal-48xl", + "x8aedz.12xlarge", + "x8aedz.24xlarge", + "x8aedz.3xlarge", + "x8aedz.6xlarge", + "x8aedz.large", + "x8aedz.metal-12xl", + "x8aedz.metal-24xl", + "x8aedz.xlarge", + "m8azn.medium", + "m8azn.large", + "m8azn.xlarge", + "m8azn.3xlarge", + "m8azn.6xlarge", + "m8azn.12xlarge", + "m8azn.24xlarge", + "m8azn.metal-12xl", + "m8azn.metal-24xl", + "x8i.large", + "x8i.xlarge", + "x8i.2xlarge", + "x8i.4xlarge", + "x8i.8xlarge", + "x8i.12xlarge", + "x8i.16xlarge", + "x8i.24xlarge", + "x8i.32xlarge", + "x8i.48xlarge", + "x8i.64xlarge", + "x8i.96xlarge", + "x8i.metal-48xl", + "x8i.metal-96xl", + "mac-m4max.metal", + "g7e.2xlarge", + "g7e.4xlarge", + "g7e.8xlarge", + "g7e.12xlarge", + "g7e.24xlarge", + "g7e.48xlarge", } } diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/types/types.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/types/types.go index 18e136f15..c16e338ed 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/types/types.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ec2/types/types.go @@ -4527,6 +4527,10 @@ type EbsBlockDevice struct { // [Preserving Amazon EBS volumes on instance termination]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/terminating-instances.html#preserving-volumes-on-termination DeleteOnTermination *bool + // The index of the EBS card. Some instance types support multiple EBS cards. The + // default EBS card index is 0. + EbsCardIndex *int32 + // Indicates whether the encryption state of an EBS volume is changed while being // restored from a backing snapshot. The effect of setting the encryption state to // true depends on the volume origin (new or from a snapshot), starting encryption @@ -4709,6 +4713,33 @@ type EbsBlockDeviceResponse struct { noSmithyDocumentSerde } +// Describes the performance characteristics of an EBS card on the instance type. +type EbsCardInfo struct { + + // The baseline bandwidth performance for the EBS card, in Mbps. + BaselineBandwidthInMbps *int32 + + // The baseline IOPS performance for the EBS card. + BaselineIops *int32 + + // The baseline throughput performance for the EBS card, in MBps. + BaselineThroughputInMBps *float64 + + // The index of the EBS card. + EbsCardIndex *int32 + + // The maximum bandwidth performance for the EBS card, in Mbps. + MaximumBandwidthInMbps *int32 + + // The maximum IOPS performance for the EBS card. + MaximumIops *int32 + + // The maximum throughput performance for the EBS card, in MBps. + MaximumThroughputInMBps *float64 + + noSmithyDocumentSerde +} + // Describes the Amazon EBS features supported by the instance type. type EbsInfo struct { @@ -4718,6 +4749,9 @@ type EbsInfo struct { // [Amazon EBS volume limits for Amazon EC2 instances]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/volume_limits.html AttachmentLimitType AttachmentLimitType + // Describes the EBS cards available for the instance type. + EbsCards []EbsCardInfo + // Describes the optimized EBS performance for the instance type. EbsOptimizedInfo *EbsOptimizedInfo @@ -4736,6 +4770,9 @@ type EbsInfo struct { // [Amazon EBS volume limits for Amazon EC2 instances]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/volume_limits.html MaximumEbsAttachments *int32 + // Indicates the number of EBS cards supported by the instance type. + MaximumEbsCards *int32 + // Indicates whether non-volatile memory express (NVMe) is supported. NvmeSupport EbsNvmeSupport @@ -4755,6 +4792,10 @@ type EbsInstanceBlockDevice struct { // Indicates whether the volume is deleted on instance termination. DeleteOnTermination *bool + // The index of the EBS card. Some instance types support multiple EBS cards. The + // default EBS card index is 0. + EbsCardIndex *int32 + // The service provider that manages the EBS volume. Operator *OperatorResponse @@ -6866,6 +6907,13 @@ type GpuDeviceInfo struct { // The number of GPUs for the instance type. Count *int32 + // The size of each GPU as a fraction of a full GPU, between 0 (excluded) and 1 + // (included). + GpuPartitionSize *float64 + + // Total number of GPU devices of this type. + LogicalGpuCount *int32 + // The manufacturer of the GPU accelerator. Manufacturer *string @@ -6875,6 +6923,9 @@ type GpuDeviceInfo struct { // The name of the GPU accelerator. Name *string + // A list of workload types this GPU supports. + Workloads []string + noSmithyDocumentSerde } @@ -11131,7 +11182,10 @@ type IpamPolicyDocument struct { // The Amazon Web Services Organizations target for an IPAM policy. type IpamPolicyOrganizationTarget struct { - // The ID of a Amazon Web Services Organizations target for an IPAM policy. + // The ID of the Amazon Web Services Organizations target. + // + // A target can be an individual Amazon Web Services account or an entity within + // an Amazon Web Services Organization to which an IPAM policy can be applied. OrganizationTargetId *string noSmithyDocumentSerde @@ -12754,6 +12808,10 @@ type LaunchTemplateEbsBlockDevice struct { // Indicates whether the EBS volume is deleted on instance termination. DeleteOnTermination *bool + // The index of the EBS card. Some instance types support multiple EBS cards. The + // default EBS card index is 0. + EbsCardIndex *int32 + // Indicates whether the EBS volume is encrypted. Encrypted *bool @@ -12790,6 +12848,10 @@ type LaunchTemplateEbsBlockDeviceRequest struct { // Indicates whether the EBS volume is deleted on instance termination. DeleteOnTermination *bool + // The index of the EBS card. Some instance types support multiple EBS cards. The + // default EBS card index is 0. + EbsCardIndex *int32 + // Indicates whether the EBS volume is encrypted. Encrypted volumes can only be // attached to instances that support Amazon EBS encryption. If you are creating a // volume from a snapshot, you can't specify an encryption value. @@ -15305,6 +15367,11 @@ type NetworkBandwidthGbpsRequest struct { // Describes the network card support of the instance type. type NetworkCardInfo struct { + // The number of additional network interfaces that can be attached to an instance + // when using flexible Elastic Network Adapter (ENA) queues. This number is in + // addition to the base number specified by maximumNetworkInterfaces . + AdditionalFlexibleNetworkInterfaces *int32 + // The baseline network performance of the network card, in Gbps. BaselineBandwidthInGbps *float64 @@ -24199,6 +24266,10 @@ type VolumeAttachment struct { // parameter returns null . Device *string + // The index of the EBS card. Some instance types support multiple EBS cards. The + // default EBS card index is 0. + EbsCardIndex *int32 + // The ID of the instance. // // If the volume is attached to an Amazon Web Services-managed resource, this diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/CHANGELOG.md index 2f6c8710b..82fa2c529 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/CHANGELOG.md @@ -1,3 +1,15 @@ +# v1.71.0 (2026-01-15) + +* **Feature**: Adds support for configuring FIPS in AWS GovCloud (US) Regions via a new ECS Capacity Provider field fipsEnabled. When enabled, instances launched by the capacity provider will use a FIPS-140 enabled AMI. Instances will use FIPS-140 compliant cryptographic modules and AWS FIPS endpoints. + +# v1.70.1 (2026-01-09) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.70.0 (2025-12-18) + +* **Feature**: Adding support for Event Windows via a new ECS account setting "fargateEventWindows". When enabled, ECS Fargate will use the configured event window for patching tasks. Introducing "CapacityOptionType" for CreateCapacityProvider API, allowing support for Spot capacity for ECS Managed Instances. + # v1.69.5 (2025-12-09) * No change notes available for this release. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/api_op_PutAccountSetting.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/api_op_PutAccountSetting.go index 29baeb752..e1c552a7a 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/api_op_PutAccountSetting.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/api_op_PutAccountSetting.go @@ -98,6 +98,12 @@ type PutAccountSettingInput struct { // Fargate task. For information about the Fargate tasks maintenance, see [Amazon Web Services Fargate task maintenance]in the // Amazon ECS Developer Guide. // + // - fargateEventWindows - When Amazon Web Services determines that a security or + // infrastructure update is needed for an Amazon ECS task hosted on Fargate, the + // tasks need to be stopped and new tasks launched to replace them. Use + // fargateEventWindows to use EC2 Event Windows associated with Fargate tasks to + // configure time windows for task retirement. + // // - tagResourceAuthorization - Amazon ECS is introducing tagging authorization // for resource creation. Users must have permissions for actions that create the // resource, such as ecsCreateCluster . If tags are specified when you create a diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/api_op_PutAccountSettingDefault.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/api_op_PutAccountSettingDefault.go index be40bbe2b..a539ac332 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/api_op_PutAccountSettingDefault.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/api_op_PutAccountSettingDefault.go @@ -97,6 +97,12 @@ type PutAccountSettingDefaultInput struct { // Fargate task. For information about the Fargate tasks maintenance, see [Amazon Web Services Fargate task maintenance]in the // Amazon ECS Developer Guide. // + // - fargateEventWindows - When Amazon Web Services determines that a security or + // infrastructure update is needed for an Amazon ECS task hosted on Fargate, the + // tasks need to be stopped and new tasks launched to replace them. Use + // fargateEventWindows to use EC2 Event Windows associated with Fargate tasks to + // configure time windows for task retirement. + // // - tagResourceAuthorization - Amazon ECS is introducing tagging authorization // for resource creation. Users must have permissions for actions that create the // resource, such as ecsCreateCluster . If tags are specified when you create a diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/deserializers.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/deserializers.go index 27c38384b..3bf829ca3 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/deserializers.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/deserializers.go @@ -15116,6 +15116,15 @@ func awsAwsjson11_deserializeDocumentInstanceLaunchTemplate(v **types.InstanceLa for key, value := range shape { switch key { + case "capacityOptionType": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected CapacityOptionType to be of type string, got %T instead", value) + } + sv.CapacityOptionType = types.CapacityOptionType(jtv) + } + case "ec2InstanceProfileArn": if value != nil { jtv, ok := value.(string) @@ -15125,6 +15134,15 @@ func awsAwsjson11_deserializeDocumentInstanceLaunchTemplate(v **types.InstanceLa sv.Ec2InstanceProfileArn = ptr.String(jtv) } + case "fipsEnabled": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected BoxedBoolean to be of type *bool, got %T instead", value) + } + sv.FipsEnabled = ptr.Bool(jtv) + } + case "instanceRequirements": if err := awsAwsjson11_deserializeDocumentInstanceRequirementsRequest(&sv.InstanceRequirements, value); err != nil { return err diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/go_module_metadata.go index a2659ff0f..13a5d058a 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/go_module_metadata.go @@ -3,4 +3,4 @@ package ecs // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.69.5" +const goModuleVersion = "1.71.0" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/serializers.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/serializers.go index 718e7b12d..0a39efe90 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/serializers.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/serializers.go @@ -5748,11 +5748,21 @@ func awsAwsjson11_serializeDocumentInstanceLaunchTemplate(v *types.InstanceLaunc object := value.Object() defer object.Close() + if len(v.CapacityOptionType) > 0 { + ok := object.Key("capacityOptionType") + ok.String(string(v.CapacityOptionType)) + } + if v.Ec2InstanceProfileArn != nil { ok := object.Key("ec2InstanceProfileArn") ok.String(*v.Ec2InstanceProfileArn) } + if v.FipsEnabled != nil { + ok := object.Key("fipsEnabled") + ok.Boolean(*v.FipsEnabled) + } + if v.InstanceRequirements != nil { ok := object.Key("instanceRequirements") if err := awsAwsjson11_serializeDocumentInstanceRequirementsRequest(v.InstanceRequirements, ok); err != nil { diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/types/enums.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/types/enums.go index a720e3bb8..a6aed51ae 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/types/enums.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/types/enums.go @@ -234,6 +234,25 @@ func (BurstablePerformance) Values() []BurstablePerformance { } } +type CapacityOptionType string + +// Enum values for CapacityOptionType +const ( + CapacityOptionTypeOnDemand CapacityOptionType = "ON_DEMAND" + CapacityOptionTypeSpot CapacityOptionType = "SPOT" +) + +// Values returns all known values for CapacityOptionType. Note that this can be +// expanded in the future, and so it is only as up to date as the client. +// +// The ordering of this slice is not guaranteed to be stable across updates. +func (CapacityOptionType) Values() []CapacityOptionType { + return []CapacityOptionType{ + "ON_DEMAND", + "SPOT", + } +} + type CapacityProviderField string // Enum values for CapacityProviderField @@ -1596,6 +1615,7 @@ const ( SettingNameFargateTaskRetirementWaitPeriod SettingName = "fargateTaskRetirementWaitPeriod" SettingNameGuardDutyActivate SettingName = "guardDutyActivate" SettingNameDefaultLogDriverMode SettingName = "defaultLogDriverMode" + SettingNameFargateEventWindows SettingName = "fargateEventWindows" ) // Values returns all known values for SettingName. Note that this can be expanded @@ -1614,6 +1634,7 @@ func (SettingName) Values() []SettingName { "fargateTaskRetirementWaitPeriod", "guardDutyActivate", "defaultLogDriverMode", + "fargateEventWindows", } } diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/types/types.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/types/types.go index 43f93b9b3..759147ecf 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/types/types.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ecs/types/types.go @@ -3370,6 +3370,35 @@ type InstanceLaunchTemplate struct { // This member is required. NetworkConfiguration *ManagedInstancesNetworkConfiguration + // The capacity option type. This determines whether Amazon ECS launches On-Demand + // or Spot Instances for your managed instance capacity provider. + // + // Valid values are: + // + // - ON_DEMAND - Launches standard On-Demand Instances. On-Demand Instances + // provide predictable pricing and availability. + // + // - SPOT - Launches Spot Instances that use spare Amazon EC2 capacity at reduced + // cost. Spot Instances can be interrupted by Amazon EC2 with a two-minute + // notification when the capacity is needed back. + // + // The default is On-Demand + // + // For more information about Amazon EC2 capacity options, see [Instance purchasing options] in the Amazon EC2 + // User Guide. + // + // [Instance purchasing options]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-purchasing-options.html + CapacityOptionType CapacityOptionType + + // Determines whether to enable FIPS 140-2 validated cryptographic modules on EC2 + // instances launched by the capacity provider. If true , instances use + // FIPS-compliant cryptographic algorithms and modules for enhanced security + // compliance. If false , instances use standard cryptographic implementations. + // + // If not specified, instances are launched with FIPS enabled in AWS GovCloud (US) + // regions and FIPS disabled in other regions. + FipsEnabled *bool + // The instance requirements. You can specify: // // - The instance types diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/CHANGELOG.md new file mode 100644 index 000000000..24091d2ce --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/CHANGELOG.md @@ -0,0 +1,764 @@ +# v1.46.7 (2026-01-09) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.46.6 (2025-12-23) + +* No change notes available for this release. + +# v1.46.5 (2025-12-09) + +* No change notes available for this release. + +# v1.46.4 (2025-12-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.46.3 (2025-12-02) + +* **Dependency Update**: Updated to the latest SDK module versions +* **Dependency Update**: Upgrade to smithy-go v1.24.0. Notably this version of the library reduces the allocation footprint of the middleware system. We observe a ~10% reduction in allocations per SDK call with this change. + +# v1.46.2 (2025-11-25) + +* **Bug Fix**: Add error check for endpoint param binding during auth scheme resolution to fix panic reported in #3234 + +# v1.46.1 (2025-11-19.2) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.46.0 (2025-11-18) + +* **Feature**: Amazon MSK adds three new APIs, ListTopics, DescribeTopic, and DescribeTopicPartitions for viewing Kafka topics in your MSK clusters. + +# v1.45.2 (2025-11-12) + +* **Bug Fix**: Further reduce allocation overhead when the metrics system isn't in-use. +* **Bug Fix**: Reduce allocation overhead when the client doesn't have any HTTP interceptors configured. +* **Bug Fix**: Remove blank trace spans towards the beginning of the request that added no additional information. This conveys a slight reduction in overall allocations. + +# v1.45.1 (2025-11-11) + +* **Bug Fix**: Return validation error if input region is not a valid host label. + +# v1.45.0 (2025-11-10) + +* **Feature**: Amazon MSK now supports intelligent rebalancing for MSK Express brokers. + +# v1.44.3 (2025-11-04) + +* **Dependency Update**: Updated to the latest SDK module versions +* **Dependency Update**: Upgrade to smithy-go v1.23.2 which should convey some passive reduction of overall allocations, especially when not using the metrics system. + +# v1.44.2 (2025-10-30) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.44.1 (2025-10-23) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.44.0 (2025-10-16) + +* **Feature**: Update endpoint ruleset parameters casing +* **Dependency Update**: Bump minimum Go version to 1.23. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.43.6 (2025-09-26) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.43.5 (2025-09-23) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.43.4 (2025-09-10) + +* No change notes available for this release. + +# v1.43.3 (2025-09-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.43.2 (2025-08-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.43.1 (2025-08-27) + +* **Dependency Update**: Update to smithy-go v1.23.0. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.43.0 (2025-08-26) + +* **Feature**: Remove incorrect endpoint tests + +# v1.42.2 (2025-08-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.42.1 (2025-08-20) + +* **Bug Fix**: Remove unused deserialization code. + +# v1.42.0 (2025-08-11) + +* **Feature**: Add support for configuring per-service Options via callback on global config. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.41.0 (2025-08-04) + +* **Feature**: Support configurable auth scheme preferences in service clients via AWS_AUTH_SCHEME_PREFERENCE in the environment, auth_scheme_preference in the config file, and through in-code settings on LoadDefaultConfig and client constructor methods. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.40.1 (2025-07-30) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.40.0 (2025-07-28) + +* **Feature**: Add support for HTTP interceptors. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.39.7 (2025-07-19) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.39.6 (2025-07-16) + +* No change notes available for this release. + +# v1.39.5 (2025-06-17) + +* **Dependency Update**: Update to smithy-go v1.22.4. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.39.4 (2025-06-10) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.39.3 (2025-05-16) + +* No change notes available for this release. + +# v1.39.2 (2025-04-03) + +* No change notes available for this release. + +# v1.39.1 (2025-03-04.2) + +* **Bug Fix**: Add assurance test for operation order. + +# v1.39.0 (2025-02-27) + +* **Feature**: Track credential providers via User-Agent Feature ids +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.38.17 (2025-02-18) + +* **Bug Fix**: Bump go version to 1.22 +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.38.16 (2025-02-05) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.38.15 (2025-01-31) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.38.14 (2025-01-30) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.38.13 (2025-01-24) + +* **Dependency Update**: Updated to the latest SDK module versions +* **Dependency Update**: Upgrade to smithy-go v1.22.2. + +# v1.38.12 (2025-01-17) + +* **Bug Fix**: Fix bug where credentials weren't refreshed during retry loop. + +# v1.38.11 (2025-01-15) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.38.10 (2025-01-09) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.38.9 (2024-12-19) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.38.8 (2024-12-18) + +* No change notes available for this release. + +# v1.38.7 (2024-12-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.38.6 (2024-11-18) + +* **Dependency Update**: Update to smithy-go v1.22.1. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.38.5 (2024-11-07) + +* **Bug Fix**: Adds case-insensitive handling of error message fields in service responses + +# v1.38.4 (2024-11-06) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.38.3 (2024-10-28) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.38.2 (2024-10-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.38.1 (2024-10-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.38.0 (2024-10-04) + +* **Feature**: Add support for HTTP client metrics. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.37.4 (2024-10-03) + +* No change notes available for this release. + +# v1.37.3 (2024-09-27) + +* No change notes available for this release. + +# v1.37.2 (2024-09-25) + +* No change notes available for this release. + +# v1.37.1 (2024-09-23) + +* No change notes available for this release. + +# v1.37.0 (2024-09-20) + +* **Feature**: Add tracing and metrics support to service clients. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.36.1 (2024-09-17) + +* **Bug Fix**: **BREAKFIX**: Only generate AccountIDEndpointMode config for services that use it. This is a compiler break, but removes no actual functionality, as no services currently use the account ID in endpoint resolution. + +# v1.36.0 (2024-09-09) + +* **Feature**: Amazon MSK Replicator can now replicate data to identically named topics between MSK clusters within the same AWS Region or across different AWS Regions. + +# v1.35.6 (2024-09-04) + +* No change notes available for this release. + +# v1.35.5 (2024-09-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.35.4 (2024-08-15) + +* **Dependency Update**: Bump minimum Go version to 1.21. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.35.3 (2024-07-10.2) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.35.2 (2024-07-10) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.35.1 (2024-06-28) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.35.0 (2024-06-26) + +* **Feature**: Support list-of-string endpoint parameter. + +# v1.34.1 (2024-06-19) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.34.0 (2024-06-18) + +* **Feature**: Track usage of various AWS SDK features in user-agent string. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.3 (2024-06-17) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.2 (2024-06-07) + +* **Bug Fix**: Add clock skew correction on all service clients +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.1 (2024-06-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.0 (2024-05-28) + +* **Feature**: Adds ControllerNodeInfo in ListNodes response to support Raft mode for MSK + +# v1.32.1 (2024-05-23) + +* No change notes available for this release. + +# v1.32.0 (2024-05-16) + +* **Feature**: AWS MSK support for Broker Removal. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.31.5 (2024-05-15) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.31.4 (2024-05-08) + +* **Bug Fix**: GoDoc improvement + +# v1.31.3 (2024-04-17) + +* No change notes available for this release. + +# v1.31.2 (2024-03-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.31.1 (2024-03-18) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.31.0 (2024-03-12) + +* **Feature**: Added support for specifying the starting position of topic replication in MSK-Replicator. + +# v1.30.2 (2024-03-07) + +* **Bug Fix**: Remove dependency on go-cmp. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.30.1 (2024-02-23) + +* **Bug Fix**: Move all common, SDK-side middleware stack ops into the service client module to prevent cross-module compatibility issues in the future. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.30.0 (2024-02-22) + +* **Feature**: Add middleware stack snapshot tests. + +# v1.29.2 (2024-02-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.29.1 (2024-02-20) + +* **Bug Fix**: When sourcing values for a service's `EndpointParameters`, the lack of a configured region (i.e. `options.Region == ""`) will now translate to a `nil` value for `EndpointParameters.Region` instead of a pointer to the empty string `""`. This will result in a much more explicit error when calling an operation instead of an obscure hostname lookup failure. + +# v1.29.0 (2024-02-13) + +* **Feature**: Bump minimum Go version to 1.20 per our language support policy. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.28.6 (2024-01-04) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.28.5 (2023-12-08) + +* **Bug Fix**: Reinstate presence of default Retryer in functional options, but still respect max attempts set therein. + +# v1.28.4 (2023-12-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.28.3 (2023-12-06) + +* **Bug Fix**: Restore pre-refactor auth behavior where all operations could technically be performed anonymously. + +# v1.28.2 (2023-12-01) + +* **Bug Fix**: Correct wrapping of errors in authentication workflow. +* **Bug Fix**: Correctly recognize cache-wrapped instances of AnonymousCredentials at client construction. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.28.1 (2023-11-30) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.28.0 (2023-11-29) + +* **Feature**: Expose Options() accessor on service clients. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.27.3 (2023-11-28.2) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.27.2 (2023-11-28) + +* **Bug Fix**: Respect setting RetryMaxAttempts in functional options at client construction. + +# v1.27.1 (2023-11-20) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.27.0 (2023-11-16) + +* **Feature**: Added a new API response field which determines if there is an action required from the customer regarding their cluster. + +# v1.26.2 (2023-11-15) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.26.1 (2023-11-09) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.26.0 (2023-11-01) + +* **Feature**: Adds support for configured endpoints via environment variables and the AWS shared configuration file. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.25.0 (2023-10-31) + +* **Feature**: **BREAKING CHANGE**: Bump minimum go version to 1.19 per the revised [go version support policy](https://aws.amazon.com/blogs/developer/aws-sdk-for-go-aligns-with-go-release-policy-on-supported-runtimes/). +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.24.0 (2023-10-24) + +* **Feature**: **BREAKFIX**: Correct nullability representation of APIGateway-based services. + +# v1.23.0 (2023-10-17) + +* **Feature**: AWS Managed Streaming for Kafka is launching MSK Replicator, a new feature that enables customers to reliably replicate data across Amazon MSK clusters in same or different AWS regions. You can now use SDK to create, list, describe, delete, update, and manage tags of MSK Replicators. + +# v1.22.8 (2023-10-12) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.22.7 (2023-10-06) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.22.6 (2023-09-06) + +* No change notes available for this release. + +# v1.22.5 (2023-08-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.22.4 (2023-08-18) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.22.3 (2023-08-17) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.22.2 (2023-08-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.22.1 (2023-08-01) + +* No change notes available for this release. + +# v1.22.0 (2023-07-31) + +* **Feature**: Adds support for smithy-modeled endpoint resolution. A new rules-based endpoint resolution will be added to the SDK which will supercede and deprecate existing endpoint resolution. Specifically, EndpointResolver will be deprecated while BaseEndpoint and EndpointResolverV2 will take its place. For more information, please see the Endpoints section in our Developer Guide. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.21.0 (2023-07-28.2) + +* **Feature**: Amazon MSK has introduced new versions of ListClusterOperations and DescribeClusterOperation APIs. These v2 APIs provide information and insights into the ongoing operations of both MSK Provisioned and MSK Serverless clusters. + +# v1.20.7 (2023-07-28) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.20.6 (2023-07-13) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.20.5 (2023-06-15) + +* No change notes available for this release. + +# v1.20.4 (2023-06-13) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.20.3 (2023-05-26) + +* No change notes available for this release. + +# v1.20.2 (2023-05-04) + +* No change notes available for this release. + +# v1.20.1 (2023-05-01) + +* No change notes available for this release. + +# v1.20.0 (2023-04-27) + +* **Feature**: Amazon MSK has added new APIs that allows multi-VPC private connectivity and cluster policy support for Amazon MSK clusters that simplify connectivity and access between your Apache Kafka clients hosted in different VPCs and AWS accounts and your Amazon MSK clusters. + +# v1.19.11 (2023-04-24) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.10 (2023-04-14) + +* No change notes available for this release. + +# v1.19.9 (2023-04-10) + +* No change notes available for this release. + +# v1.19.8 (2023-04-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.7 (2023-03-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.6 (2023-03-10) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.5 (2023-03-07) + +* No change notes available for this release. + +# v1.19.4 (2023-02-22) + +* **Bug Fix**: Prevent nil pointer dereference when retrieving error codes. + +# v1.19.3 (2023-02-20) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.2 (2023-02-15) + +* **Announcement**: When receiving an error response in restJson-based services, an incorrect error type may have been returned based on the content of the response. This has been fixed via PR #2012 tracked in issue #1910. +* **Bug Fix**: Correct error type parsing for restJson services. + +# v1.19.1 (2023-02-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.0 (2023-01-05) + +* **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401). + +# v1.18.2 (2022-12-15) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.18.1 (2022-12-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.18.0 (2022-10-26) + +* **Feature**: This release adds support for Tiered Storage. UpdateStorage allows you to control the Storage Mode for supported storage tiers. + +# v1.17.21 (2022-10-24) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.20 (2022-10-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.19 (2022-09-30) + +* No change notes available for this release. + +# v1.17.18 (2022-09-20) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.17 (2022-09-14) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.16 (2022-09-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.15 (2022-08-31) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.14 (2022-08-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.13 (2022-08-11) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.12 (2022-08-09) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.11 (2022-08-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.10 (2022-08-01) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.9 (2022-07-05) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.8 (2022-06-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.7 (2022-06-20) + +* **Documentation**: Documentation updates to use Az Id during cluster creation. + +# v1.17.6 (2022-06-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.5 (2022-05-17) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.4 (2022-04-25) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.3 (2022-03-30) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.2 (2022-03-24) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.1 (2022-03-23) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.0 (2022-03-08) + +* **Feature**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.16.0 (2022-02-24) + +* **Feature**: API client updated +* **Feature**: Adds RetryMaxAttempts and RetryMod to API client Options. This allows the API clients' default Retryer to be configured from the shared configuration files or environment variables. Adding a new Retry mode of `Adaptive`. `Adaptive` retry mode is an experimental mode, adding client rate limiting when throttles reponses are received from an API. See [retry.AdaptiveMode](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/aws/retry#AdaptiveMode) for more details, and configuration options. +* **Feature**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.15.0 (2022-01-28) + +* **Feature**: Updated to latest API model. + +# v1.14.0 (2022-01-14) + +* **Feature**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.13.0 (2022-01-07) + +* **Feature**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.12.0 (2021-12-21) + +* **Feature**: API Paginators now support specifying the initial starting token, and support stopping on empty string tokens. + +# v1.11.0 (2021-12-02) + +* **Feature**: API client updated +* **Bug Fix**: Fixes a bug that prevented aws.EndpointResolverWithOptions from being used by the service client. ([#1514](https://github.com/aws/aws-sdk-go-v2/pull/1514)) +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.10.0 (2021-11-19) + +* **Feature**: API client updated +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.9.0 (2021-11-06) + +* **Feature**: The SDK now supports configuration of FIPS and DualStack endpoints using environment variables, shared configuration, or programmatically. +* **Feature**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.8.0 (2021-10-21) + +* **Feature**: Updated to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.7.2 (2021-10-11) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.7.1 (2021-09-17) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.7.0 (2021-09-10) + +* **Feature**: API client updated + +# v1.6.0 (2021-08-27) + +* **Feature**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.5.3 (2021-08-19) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.5.2 (2021-08-04) + +* **Dependency Update**: Updated `github.com/aws/smithy-go` to latest version. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.5.1 (2021-07-15) + +* **Dependency Update**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.5.0 (2021-06-25) + +* **Feature**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.1 (2021-05-20) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.0 (2021-05-14) + +* **Feature**: Constant has been added to modules to enable runtime version inspection for reporting. +* **Dependency Update**: Updated to the latest SDK module versions + diff --git a/vendor/go.opentelemetry.io/collector/semconv/LICENSE b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/LICENSE.txt similarity index 100% rename from vendor/go.opentelemetry.io/collector/semconv/LICENSE rename to vendor/github.com/aws/aws-sdk-go-v2/service/kafka/LICENSE.txt diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_client.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_client.go new file mode 100644 index 000000000..061c9e322 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_client.go @@ -0,0 +1,950 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "errors" + "fmt" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/defaults" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/aws/retry" + "github.com/aws/aws-sdk-go-v2/aws/signer/v4" + awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http" + internalauth "github.com/aws/aws-sdk-go-v2/internal/auth" + internalauthsmithy "github.com/aws/aws-sdk-go-v2/internal/auth/smithy" + internalConfig "github.com/aws/aws-sdk-go-v2/internal/configsources" + internalmiddleware "github.com/aws/aws-sdk-go-v2/internal/middleware" + smithy "github.com/aws/smithy-go" + smithyauth "github.com/aws/smithy-go/auth" + smithydocument "github.com/aws/smithy-go/document" + "github.com/aws/smithy-go/logging" + "github.com/aws/smithy-go/metrics" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/tracing" + smithyhttp "github.com/aws/smithy-go/transport/http" + "net" + "net/http" + "sync/atomic" + "time" +) + +const ServiceID = "Kafka" +const ServiceAPIVersion = "2018-11-14" + +type operationMetrics struct { + Duration metrics.Float64Histogram + SerializeDuration metrics.Float64Histogram + ResolveIdentityDuration metrics.Float64Histogram + ResolveEndpointDuration metrics.Float64Histogram + SignRequestDuration metrics.Float64Histogram + DeserializeDuration metrics.Float64Histogram +} + +func (m *operationMetrics) histogramFor(name string) metrics.Float64Histogram { + switch name { + case "client.call.duration": + return m.Duration + case "client.call.serialization_duration": + return m.SerializeDuration + case "client.call.resolve_identity_duration": + return m.ResolveIdentityDuration + case "client.call.resolve_endpoint_duration": + return m.ResolveEndpointDuration + case "client.call.signing_duration": + return m.SignRequestDuration + case "client.call.deserialization_duration": + return m.DeserializeDuration + default: + panic("unrecognized operation metric") + } +} + +func timeOperationMetric[T any]( + ctx context.Context, metric string, fn func() (T, error), + opts ...metrics.RecordMetricOption, +) (T, error) { + mm := getOperationMetrics(ctx) + if mm == nil { // not using the metrics system + return fn() + } + + instr := mm.histogramFor(metric) + opts = append([]metrics.RecordMetricOption{withOperationMetadata(ctx)}, opts...) + + start := time.Now() + v, err := fn() + end := time.Now() + + elapsed := end.Sub(start) + instr.Record(ctx, float64(elapsed)/1e9, opts...) + return v, err +} + +func startMetricTimer(ctx context.Context, metric string, opts ...metrics.RecordMetricOption) func() { + mm := getOperationMetrics(ctx) + if mm == nil { // not using the metrics system + return func() {} + } + + instr := mm.histogramFor(metric) + opts = append([]metrics.RecordMetricOption{withOperationMetadata(ctx)}, opts...) + + var ended bool + start := time.Now() + return func() { + if ended { + return + } + ended = true + + end := time.Now() + + elapsed := end.Sub(start) + instr.Record(ctx, float64(elapsed)/1e9, opts...) + } +} + +func withOperationMetadata(ctx context.Context) metrics.RecordMetricOption { + return func(o *metrics.RecordMetricOptions) { + o.Properties.Set("rpc.service", middleware.GetServiceID(ctx)) + o.Properties.Set("rpc.method", middleware.GetOperationName(ctx)) + } +} + +type operationMetricsKey struct{} + +func withOperationMetrics(parent context.Context, mp metrics.MeterProvider) (context.Context, error) { + if _, ok := mp.(metrics.NopMeterProvider); ok { + // not using the metrics system - setting up the metrics context is a memory-intensive operation + // so we should skip it in this case + return parent, nil + } + + meter := mp.Meter("github.com/aws/aws-sdk-go-v2/service/kafka") + om := &operationMetrics{} + + var err error + + om.Duration, err = operationMetricTimer(meter, "client.call.duration", + "Overall call duration (including retries and time to send or receive request and response body)") + if err != nil { + return nil, err + } + om.SerializeDuration, err = operationMetricTimer(meter, "client.call.serialization_duration", + "The time it takes to serialize a message body") + if err != nil { + return nil, err + } + om.ResolveIdentityDuration, err = operationMetricTimer(meter, "client.call.auth.resolve_identity_duration", + "The time taken to acquire an identity (AWS credentials, bearer token, etc) from an Identity Provider") + if err != nil { + return nil, err + } + om.ResolveEndpointDuration, err = operationMetricTimer(meter, "client.call.resolve_endpoint_duration", + "The time it takes to resolve an endpoint (endpoint resolver, not DNS) for the request") + if err != nil { + return nil, err + } + om.SignRequestDuration, err = operationMetricTimer(meter, "client.call.auth.signing_duration", + "The time it takes to sign a request") + if err != nil { + return nil, err + } + om.DeserializeDuration, err = operationMetricTimer(meter, "client.call.deserialization_duration", + "The time it takes to deserialize a message body") + if err != nil { + return nil, err + } + + return context.WithValue(parent, operationMetricsKey{}, om), nil +} + +func operationMetricTimer(m metrics.Meter, name, desc string) (metrics.Float64Histogram, error) { + return m.Float64Histogram(name, func(o *metrics.InstrumentOptions) { + o.UnitLabel = "s" + o.Description = desc + }) +} + +func getOperationMetrics(ctx context.Context) *operationMetrics { + if v := ctx.Value(operationMetricsKey{}); v != nil { + return v.(*operationMetrics) + } + return nil +} + +func operationTracer(p tracing.TracerProvider) tracing.Tracer { + return p.Tracer("github.com/aws/aws-sdk-go-v2/service/kafka") +} + +// Client provides the API client to make operations call for Managed Streaming +// for Kafka. +type Client struct { + options Options + + // Difference between the time reported by the server and the client + timeOffset *atomic.Int64 +} + +// New returns an initialized Client based on the functional options. Provide +// additional functional options to further configure the behavior of the client, +// such as changing the client's endpoint or adding custom middleware behavior. +func New(options Options, optFns ...func(*Options)) *Client { + options = options.Copy() + + resolveDefaultLogger(&options) + + setResolvedDefaultsMode(&options) + + resolveRetryer(&options) + + resolveHTTPClient(&options) + + resolveHTTPSignerV4(&options) + + resolveEndpointResolverV2(&options) + + resolveTracerProvider(&options) + + resolveMeterProvider(&options) + + resolveAuthSchemeResolver(&options) + + for _, fn := range optFns { + fn(&options) + } + + finalizeRetryMaxAttempts(&options) + + ignoreAnonymousAuth(&options) + + wrapWithAnonymousAuth(&options) + + resolveAuthSchemes(&options) + + client := &Client{ + options: options, + } + + initializeTimeOffsetResolver(client) + + return client +} + +// Options returns a copy of the client configuration. +// +// Callers SHOULD NOT perform mutations on any inner structures within client +// config. Config overrides should instead be made on a per-operation basis through +// functional options. +func (c *Client) Options() Options { + return c.options.Copy() +} + +func (c *Client) invokeOperation( + ctx context.Context, opID string, params interface{}, optFns []func(*Options), stackFns ...func(*middleware.Stack, Options) error, +) ( + result interface{}, metadata middleware.Metadata, err error, +) { + ctx = middleware.ClearStackValues(ctx) + ctx = middleware.WithServiceID(ctx, ServiceID) + ctx = middleware.WithOperationName(ctx, opID) + + stack := middleware.NewStack(opID, smithyhttp.NewStackRequest) + options := c.options.Copy() + + for _, fn := range optFns { + fn(&options) + } + + finalizeOperationRetryMaxAttempts(&options, *c) + + finalizeClientEndpointResolverOptions(&options) + + for _, fn := range stackFns { + if err := fn(stack, options); err != nil { + return nil, metadata, err + } + } + + for _, fn := range options.APIOptions { + if err := fn(stack); err != nil { + return nil, metadata, err + } + } + + ctx, err = withOperationMetrics(ctx, options.MeterProvider) + if err != nil { + return nil, metadata, err + } + + tracer := operationTracer(options.TracerProvider) + spanName := fmt.Sprintf("%s.%s", ServiceID, opID) + + ctx = tracing.WithOperationTracer(ctx, tracer) + + ctx, span := tracer.StartSpan(ctx, spanName, func(o *tracing.SpanOptions) { + o.Kind = tracing.SpanKindClient + o.Properties.Set("rpc.system", "aws-api") + o.Properties.Set("rpc.method", opID) + o.Properties.Set("rpc.service", ServiceID) + }) + endTimer := startMetricTimer(ctx, "client.call.duration") + defer endTimer() + defer span.End() + + handler := smithyhttp.NewClientHandlerWithOptions(options.HTTPClient, func(o *smithyhttp.ClientHandler) { + o.Meter = options.MeterProvider.Meter("github.com/aws/aws-sdk-go-v2/service/kafka") + }) + decorated := middleware.DecorateHandler(handler, stack) + result, metadata, err = decorated.Handle(ctx, params) + if err != nil { + span.SetProperty("exception.type", fmt.Sprintf("%T", err)) + span.SetProperty("exception.message", err.Error()) + + var aerr smithy.APIError + if errors.As(err, &aerr) { + span.SetProperty("api.error_code", aerr.ErrorCode()) + span.SetProperty("api.error_message", aerr.ErrorMessage()) + span.SetProperty("api.error_fault", aerr.ErrorFault().String()) + } + + err = &smithy.OperationError{ + ServiceID: ServiceID, + OperationName: opID, + Err: err, + } + } + + span.SetProperty("error", err != nil) + if err == nil { + span.SetStatus(tracing.SpanStatusOK) + } else { + span.SetStatus(tracing.SpanStatusError) + } + + return result, metadata, err +} + +type operationInputKey struct{} + +func setOperationInput(ctx context.Context, input interface{}) context.Context { + return middleware.WithStackValue(ctx, operationInputKey{}, input) +} + +func getOperationInput(ctx context.Context) interface{} { + return middleware.GetStackValue(ctx, operationInputKey{}) +} + +type setOperationInputMiddleware struct { +} + +func (*setOperationInputMiddleware) ID() string { + return "setOperationInput" +} + +func (m *setOperationInputMiddleware) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + ctx = setOperationInput(ctx, in.Parameters) + return next.HandleSerialize(ctx, in) +} + +func addProtocolFinalizerMiddlewares(stack *middleware.Stack, options Options, operation string) error { + if err := stack.Finalize.Add(&resolveAuthSchemeMiddleware{operation: operation, options: options}, middleware.Before); err != nil { + return fmt.Errorf("add ResolveAuthScheme: %w", err) + } + if err := stack.Finalize.Insert(&getIdentityMiddleware{options: options}, "ResolveAuthScheme", middleware.After); err != nil { + return fmt.Errorf("add GetIdentity: %v", err) + } + if err := stack.Finalize.Insert(&resolveEndpointV2Middleware{options: options}, "GetIdentity", middleware.After); err != nil { + return fmt.Errorf("add ResolveEndpointV2: %v", err) + } + if err := stack.Finalize.Insert(&signRequestMiddleware{options: options}, "ResolveEndpointV2", middleware.After); err != nil { + return fmt.Errorf("add Signing: %w", err) + } + return nil +} +func resolveAuthSchemeResolver(options *Options) { + if options.AuthSchemeResolver == nil { + options.AuthSchemeResolver = &defaultAuthSchemeResolver{} + } +} + +func resolveAuthSchemes(options *Options) { + if options.AuthSchemes == nil { + options.AuthSchemes = []smithyhttp.AuthScheme{ + internalauth.NewHTTPAuthScheme("aws.auth#sigv4", &internalauthsmithy.V4SignerAdapter{ + Signer: options.HTTPSignerV4, + Logger: options.Logger, + LogSigning: options.ClientLogMode.IsSigning(), + }), + } + } +} + +type noSmithyDocumentSerde = smithydocument.NoSerde + +type legacyEndpointContextSetter struct { + LegacyResolver EndpointResolver +} + +func (*legacyEndpointContextSetter) ID() string { + return "legacyEndpointContextSetter" +} + +func (m *legacyEndpointContextSetter) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + if m.LegacyResolver != nil { + ctx = awsmiddleware.SetRequiresLegacyEndpoints(ctx, true) + } + + return next.HandleInitialize(ctx, in) + +} +func addlegacyEndpointContextSetter(stack *middleware.Stack, o Options) error { + return stack.Initialize.Add(&legacyEndpointContextSetter{ + LegacyResolver: o.EndpointResolver, + }, middleware.Before) +} + +func resolveDefaultLogger(o *Options) { + if o.Logger != nil { + return + } + o.Logger = logging.Nop{} +} + +func addSetLoggerMiddleware(stack *middleware.Stack, o Options) error { + return middleware.AddSetLoggerMiddleware(stack, o.Logger) +} + +func setResolvedDefaultsMode(o *Options) { + if len(o.resolvedDefaultsMode) > 0 { + return + } + + var mode aws.DefaultsMode + mode.SetFromString(string(o.DefaultsMode)) + + if mode == aws.DefaultsModeAuto { + mode = defaults.ResolveDefaultsModeAuto(o.Region, o.RuntimeEnvironment) + } + + o.resolvedDefaultsMode = mode +} + +// NewFromConfig returns a new client from the provided config. +func NewFromConfig(cfg aws.Config, optFns ...func(*Options)) *Client { + opts := Options{ + Region: cfg.Region, + DefaultsMode: cfg.DefaultsMode, + RuntimeEnvironment: cfg.RuntimeEnvironment, + HTTPClient: cfg.HTTPClient, + Credentials: cfg.Credentials, + APIOptions: cfg.APIOptions, + Logger: cfg.Logger, + ClientLogMode: cfg.ClientLogMode, + AppID: cfg.AppID, + AuthSchemePreference: cfg.AuthSchemePreference, + } + resolveAWSRetryerProvider(cfg, &opts) + resolveAWSRetryMaxAttempts(cfg, &opts) + resolveAWSRetryMode(cfg, &opts) + resolveAWSEndpointResolver(cfg, &opts) + resolveInterceptors(cfg, &opts) + resolveUseDualStackEndpoint(cfg, &opts) + resolveUseFIPSEndpoint(cfg, &opts) + resolveBaseEndpoint(cfg, &opts) + return New(opts, func(o *Options) { + for _, opt := range cfg.ServiceOptions { + opt(ServiceID, o) + } + for _, opt := range optFns { + opt(o) + } + }) +} + +func resolveHTTPClient(o *Options) { + var buildable *awshttp.BuildableClient + + if o.HTTPClient != nil { + var ok bool + buildable, ok = o.HTTPClient.(*awshttp.BuildableClient) + if !ok { + return + } + } else { + buildable = awshttp.NewBuildableClient() + } + + modeConfig, err := defaults.GetModeConfiguration(o.resolvedDefaultsMode) + if err == nil { + buildable = buildable.WithDialerOptions(func(dialer *net.Dialer) { + if dialerTimeout, ok := modeConfig.GetConnectTimeout(); ok { + dialer.Timeout = dialerTimeout + } + }) + + buildable = buildable.WithTransportOptions(func(transport *http.Transport) { + if tlsHandshakeTimeout, ok := modeConfig.GetTLSNegotiationTimeout(); ok { + transport.TLSHandshakeTimeout = tlsHandshakeTimeout + } + }) + } + + o.HTTPClient = buildable +} + +func resolveRetryer(o *Options) { + if o.Retryer != nil { + return + } + + if len(o.RetryMode) == 0 { + modeConfig, err := defaults.GetModeConfiguration(o.resolvedDefaultsMode) + if err == nil { + o.RetryMode = modeConfig.RetryMode + } + } + if len(o.RetryMode) == 0 { + o.RetryMode = aws.RetryModeStandard + } + + var standardOptions []func(*retry.StandardOptions) + if v := o.RetryMaxAttempts; v != 0 { + standardOptions = append(standardOptions, func(so *retry.StandardOptions) { + so.MaxAttempts = v + }) + } + + switch o.RetryMode { + case aws.RetryModeAdaptive: + var adaptiveOptions []func(*retry.AdaptiveModeOptions) + if len(standardOptions) != 0 { + adaptiveOptions = append(adaptiveOptions, func(ao *retry.AdaptiveModeOptions) { + ao.StandardOptions = append(ao.StandardOptions, standardOptions...) + }) + } + o.Retryer = retry.NewAdaptiveMode(adaptiveOptions...) + + default: + o.Retryer = retry.NewStandard(standardOptions...) + } +} + +func resolveAWSRetryerProvider(cfg aws.Config, o *Options) { + if cfg.Retryer == nil { + return + } + o.Retryer = cfg.Retryer() +} + +func resolveAWSRetryMode(cfg aws.Config, o *Options) { + if len(cfg.RetryMode) == 0 { + return + } + o.RetryMode = cfg.RetryMode +} +func resolveAWSRetryMaxAttempts(cfg aws.Config, o *Options) { + if cfg.RetryMaxAttempts == 0 { + return + } + o.RetryMaxAttempts = cfg.RetryMaxAttempts +} + +func finalizeRetryMaxAttempts(o *Options) { + if o.RetryMaxAttempts == 0 { + return + } + + o.Retryer = retry.AddWithMaxAttempts(o.Retryer, o.RetryMaxAttempts) +} + +func finalizeOperationRetryMaxAttempts(o *Options, client Client) { + if v := o.RetryMaxAttempts; v == 0 || v == client.options.RetryMaxAttempts { + return + } + + o.Retryer = retry.AddWithMaxAttempts(o.Retryer, o.RetryMaxAttempts) +} + +func resolveAWSEndpointResolver(cfg aws.Config, o *Options) { + if cfg.EndpointResolver == nil && cfg.EndpointResolverWithOptions == nil { + return + } + o.EndpointResolver = withEndpointResolver(cfg.EndpointResolver, cfg.EndpointResolverWithOptions) +} + +func resolveInterceptors(cfg aws.Config, o *Options) { + o.Interceptors = cfg.Interceptors.Copy() +} + +func addClientUserAgent(stack *middleware.Stack, options Options) error { + ua, err := getOrAddRequestUserAgent(stack) + if err != nil { + return err + } + + ua.AddSDKAgentKeyValue(awsmiddleware.APIMetadata, "kafka", goModuleVersion) + if len(options.AppID) > 0 { + ua.AddSDKAgentKey(awsmiddleware.ApplicationIdentifier, options.AppID) + } + + return nil +} + +func getOrAddRequestUserAgent(stack *middleware.Stack) (*awsmiddleware.RequestUserAgent, error) { + id := (*awsmiddleware.RequestUserAgent)(nil).ID() + mw, ok := stack.Build.Get(id) + if !ok { + mw = awsmiddleware.NewRequestUserAgent() + if err := stack.Build.Add(mw, middleware.After); err != nil { + return nil, err + } + } + + ua, ok := mw.(*awsmiddleware.RequestUserAgent) + if !ok { + return nil, fmt.Errorf("%T for %s middleware did not match expected type", mw, id) + } + + return ua, nil +} + +type HTTPSignerV4 interface { + SignHTTP(ctx context.Context, credentials aws.Credentials, r *http.Request, payloadHash string, service string, region string, signingTime time.Time, optFns ...func(*v4.SignerOptions)) error +} + +func resolveHTTPSignerV4(o *Options) { + if o.HTTPSignerV4 != nil { + return + } + o.HTTPSignerV4 = newDefaultV4Signer(*o) +} + +func newDefaultV4Signer(o Options) *v4.Signer { + return v4.NewSigner(func(so *v4.SignerOptions) { + so.Logger = o.Logger + so.LogSigning = o.ClientLogMode.IsSigning() + }) +} + +func addClientRequestID(stack *middleware.Stack) error { + return stack.Build.Add(&awsmiddleware.ClientRequestID{}, middleware.After) +} + +func addComputeContentLength(stack *middleware.Stack) error { + return stack.Build.Add(&smithyhttp.ComputeContentLength{}, middleware.After) +} + +func addRawResponseToMetadata(stack *middleware.Stack) error { + return stack.Deserialize.Add(&awsmiddleware.AddRawResponse{}, middleware.Before) +} + +func addRecordResponseTiming(stack *middleware.Stack) error { + return stack.Deserialize.Add(&awsmiddleware.RecordResponseTiming{}, middleware.After) +} + +func addSpanRetryLoop(stack *middleware.Stack, options Options) error { + return stack.Finalize.Insert(&spanRetryLoop{options: options}, "Retry", middleware.Before) +} + +type spanRetryLoop struct { + options Options +} + +func (*spanRetryLoop) ID() string { + return "spanRetryLoop" +} + +func (m *spanRetryLoop) HandleFinalize( + ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler, +) ( + middleware.FinalizeOutput, middleware.Metadata, error, +) { + tracer := operationTracer(m.options.TracerProvider) + ctx, span := tracer.StartSpan(ctx, "RetryLoop") + defer span.End() + + return next.HandleFinalize(ctx, in) +} +func addStreamingEventsPayload(stack *middleware.Stack) error { + return stack.Finalize.Add(&v4.StreamingEventsPayload{}, middleware.Before) +} + +func addUnsignedPayload(stack *middleware.Stack) error { + return stack.Finalize.Insert(&v4.UnsignedPayload{}, "ResolveEndpointV2", middleware.After) +} + +func addComputePayloadSHA256(stack *middleware.Stack) error { + return stack.Finalize.Insert(&v4.ComputePayloadSHA256{}, "ResolveEndpointV2", middleware.After) +} + +func addContentSHA256Header(stack *middleware.Stack) error { + return stack.Finalize.Insert(&v4.ContentSHA256Header{}, (*v4.ComputePayloadSHA256)(nil).ID(), middleware.After) +} + +func addIsWaiterUserAgent(o *Options) { + o.APIOptions = append(o.APIOptions, func(stack *middleware.Stack) error { + ua, err := getOrAddRequestUserAgent(stack) + if err != nil { + return err + } + + ua.AddUserAgentFeature(awsmiddleware.UserAgentFeatureWaiter) + return nil + }) +} + +func addIsPaginatorUserAgent(o *Options) { + o.APIOptions = append(o.APIOptions, func(stack *middleware.Stack) error { + ua, err := getOrAddRequestUserAgent(stack) + if err != nil { + return err + } + + ua.AddUserAgentFeature(awsmiddleware.UserAgentFeaturePaginator) + return nil + }) +} + +func addRetry(stack *middleware.Stack, o Options) error { + attempt := retry.NewAttemptMiddleware(o.Retryer, smithyhttp.RequestCloner, func(m *retry.Attempt) { + m.LogAttempts = o.ClientLogMode.IsRetries() + m.OperationMeter = o.MeterProvider.Meter("github.com/aws/aws-sdk-go-v2/service/kafka") + }) + if err := stack.Finalize.Insert(attempt, "ResolveAuthScheme", middleware.Before); err != nil { + return err + } + if err := stack.Finalize.Insert(&retry.MetricsHeader{}, attempt.ID(), middleware.After); err != nil { + return err + } + return nil +} + +// resolves dual-stack endpoint configuration +func resolveUseDualStackEndpoint(cfg aws.Config, o *Options) error { + if len(cfg.ConfigSources) == 0 { + return nil + } + value, found, err := internalConfig.ResolveUseDualStackEndpoint(context.Background(), cfg.ConfigSources) + if err != nil { + return err + } + if found { + o.EndpointOptions.UseDualStackEndpoint = value + } + return nil +} + +// resolves FIPS endpoint configuration +func resolveUseFIPSEndpoint(cfg aws.Config, o *Options) error { + if len(cfg.ConfigSources) == 0 { + return nil + } + value, found, err := internalConfig.ResolveUseFIPSEndpoint(context.Background(), cfg.ConfigSources) + if err != nil { + return err + } + if found { + o.EndpointOptions.UseFIPSEndpoint = value + } + return nil +} + +func resolveAccountID(identity smithyauth.Identity, mode aws.AccountIDEndpointMode) *string { + if mode == aws.AccountIDEndpointModeDisabled { + return nil + } + + if ca, ok := identity.(*internalauthsmithy.CredentialsAdapter); ok && ca.Credentials.AccountID != "" { + return aws.String(ca.Credentials.AccountID) + } + + return nil +} + +func addTimeOffsetBuild(stack *middleware.Stack, c *Client) error { + mw := internalmiddleware.AddTimeOffsetMiddleware{Offset: c.timeOffset} + if err := stack.Build.Add(&mw, middleware.After); err != nil { + return err + } + return stack.Deserialize.Insert(&mw, "RecordResponseTiming", middleware.Before) +} +func initializeTimeOffsetResolver(c *Client) { + c.timeOffset = new(atomic.Int64) +} + +func addUserAgentRetryMode(stack *middleware.Stack, options Options) error { + ua, err := getOrAddRequestUserAgent(stack) + if err != nil { + return err + } + + switch options.Retryer.(type) { + case *retry.Standard: + ua.AddUserAgentFeature(awsmiddleware.UserAgentFeatureRetryModeStandard) + case *retry.AdaptiveMode: + ua.AddUserAgentFeature(awsmiddleware.UserAgentFeatureRetryModeAdaptive) + } + return nil +} + +type setCredentialSourceMiddleware struct { + ua *awsmiddleware.RequestUserAgent + options Options +} + +func (m setCredentialSourceMiddleware) ID() string { return "SetCredentialSourceMiddleware" } + +func (m setCredentialSourceMiddleware) HandleBuild(ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler) ( + out middleware.BuildOutput, metadata middleware.Metadata, err error, +) { + asProviderSource, ok := m.options.Credentials.(aws.CredentialProviderSource) + if !ok { + return next.HandleBuild(ctx, in) + } + providerSources := asProviderSource.ProviderSources() + for _, source := range providerSources { + m.ua.AddCredentialsSource(source) + } + return next.HandleBuild(ctx, in) +} + +func addCredentialSource(stack *middleware.Stack, options Options) error { + ua, err := getOrAddRequestUserAgent(stack) + if err != nil { + return err + } + + mw := setCredentialSourceMiddleware{ua: ua, options: options} + return stack.Build.Insert(&mw, "UserAgent", middleware.Before) +} + +func resolveTracerProvider(options *Options) { + if options.TracerProvider == nil { + options.TracerProvider = &tracing.NopTracerProvider{} + } +} + +func resolveMeterProvider(options *Options) { + if options.MeterProvider == nil { + options.MeterProvider = metrics.NopMeterProvider{} + } +} + +func addRecursionDetection(stack *middleware.Stack) error { + return stack.Build.Add(&awsmiddleware.RecursionDetection{}, middleware.After) +} + +func addRequestIDRetrieverMiddleware(stack *middleware.Stack) error { + return stack.Deserialize.Insert(&awsmiddleware.RequestIDRetriever{}, "OperationDeserializer", middleware.Before) + +} + +func addResponseErrorMiddleware(stack *middleware.Stack) error { + return stack.Deserialize.Insert(&awshttp.ResponseErrorWrapper{}, "RequestIDRetriever", middleware.Before) + +} + +func addRequestResponseLogging(stack *middleware.Stack, o Options) error { + return stack.Deserialize.Add(&smithyhttp.RequestResponseLogger{ + LogRequest: o.ClientLogMode.IsRequest(), + LogRequestWithBody: o.ClientLogMode.IsRequestWithBody(), + LogResponse: o.ClientLogMode.IsResponse(), + LogResponseWithBody: o.ClientLogMode.IsResponseWithBody(), + }, middleware.After) +} + +type disableHTTPSMiddleware struct { + DisableHTTPS bool +} + +func (*disableHTTPSMiddleware) ID() string { + return "disableHTTPS" +} + +func (m *disableHTTPSMiddleware) HandleFinalize(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) ( + out middleware.FinalizeOutput, metadata middleware.Metadata, err error, +) { + req, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, fmt.Errorf("unknown transport type %T", in.Request) + } + + if m.DisableHTTPS && !smithyhttp.GetHostnameImmutable(ctx) { + req.URL.Scheme = "http" + } + + return next.HandleFinalize(ctx, in) +} + +func addDisableHTTPSMiddleware(stack *middleware.Stack, o Options) error { + return stack.Finalize.Insert(&disableHTTPSMiddleware{ + DisableHTTPS: o.EndpointOptions.DisableHTTPS, + }, "ResolveEndpointV2", middleware.After) +} + +func addInterceptBeforeRetryLoop(stack *middleware.Stack, opts Options) error { + return stack.Finalize.Insert(&smithyhttp.InterceptBeforeRetryLoop{ + Interceptors: opts.Interceptors.BeforeRetryLoop, + }, "Retry", middleware.Before) +} + +func addInterceptAttempt(stack *middleware.Stack, opts Options) error { + return stack.Finalize.Insert(&smithyhttp.InterceptAttempt{ + BeforeAttempt: opts.Interceptors.BeforeAttempt, + AfterAttempt: opts.Interceptors.AfterAttempt, + }, "Retry", middleware.After) +} + +func addInterceptors(stack *middleware.Stack, opts Options) error { + // middlewares are expensive, don't add all of these interceptor ones unless the caller + // actually has at least one interceptor configured + // + // at the moment it's all-or-nothing because some of the middlewares here are responsible for + // setting fields in the interceptor context for future ones + if len(opts.Interceptors.BeforeExecution) == 0 && + len(opts.Interceptors.BeforeSerialization) == 0 && len(opts.Interceptors.AfterSerialization) == 0 && + len(opts.Interceptors.BeforeRetryLoop) == 0 && + len(opts.Interceptors.BeforeAttempt) == 0 && + len(opts.Interceptors.BeforeSigning) == 0 && len(opts.Interceptors.AfterSigning) == 0 && + len(opts.Interceptors.BeforeTransmit) == 0 && len(opts.Interceptors.AfterTransmit) == 0 && + len(opts.Interceptors.BeforeDeserialization) == 0 && len(opts.Interceptors.AfterDeserialization) == 0 && + len(opts.Interceptors.AfterAttempt) == 0 && len(opts.Interceptors.AfterExecution) == 0 { + return nil + } + + return errors.Join( + stack.Initialize.Add(&smithyhttp.InterceptExecution{ + BeforeExecution: opts.Interceptors.BeforeExecution, + AfterExecution: opts.Interceptors.AfterExecution, + }, middleware.Before), + stack.Serialize.Insert(&smithyhttp.InterceptBeforeSerialization{ + Interceptors: opts.Interceptors.BeforeSerialization, + }, "OperationSerializer", middleware.Before), + stack.Serialize.Insert(&smithyhttp.InterceptAfterSerialization{ + Interceptors: opts.Interceptors.AfterSerialization, + }, "OperationSerializer", middleware.After), + stack.Finalize.Insert(&smithyhttp.InterceptBeforeSigning{ + Interceptors: opts.Interceptors.BeforeSigning, + }, "Signing", middleware.Before), + stack.Finalize.Insert(&smithyhttp.InterceptAfterSigning{ + Interceptors: opts.Interceptors.AfterSigning, + }, "Signing", middleware.After), + stack.Deserialize.Add(&smithyhttp.InterceptTransmit{ + BeforeTransmit: opts.Interceptors.BeforeTransmit, + AfterTransmit: opts.Interceptors.AfterTransmit, + }, middleware.After), + stack.Deserialize.Insert(&smithyhttp.InterceptBeforeDeserialization{ + Interceptors: opts.Interceptors.BeforeDeserialization, + }, "OperationDeserializer", middleware.After), // (deserialize stack is called in reverse) + stack.Deserialize.Insert(&smithyhttp.InterceptAfterDeserialization{ + Interceptors: opts.Interceptors.AfterDeserialization, + }, "OperationDeserializer", middleware.Before), + ) +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_BatchAssociateScramSecret.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_BatchAssociateScramSecret.go new file mode 100644 index 000000000..ce972c3d5 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_BatchAssociateScramSecret.go @@ -0,0 +1,166 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Associates one or more Scram Secrets with an Amazon MSK cluster. +func (c *Client) BatchAssociateScramSecret(ctx context.Context, params *BatchAssociateScramSecretInput, optFns ...func(*Options)) (*BatchAssociateScramSecretOutput, error) { + if params == nil { + params = &BatchAssociateScramSecretInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "BatchAssociateScramSecret", params, optFns, c.addOperationBatchAssociateScramSecretMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*BatchAssociateScramSecretOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Associates sasl scram secrets to cluster. +type BatchAssociateScramSecretInput struct { + + // The Amazon Resource Name (ARN) of the cluster to be updated. + // + // This member is required. + ClusterArn *string + + // List of AWS Secrets Manager secret ARNs. + // + // This member is required. + SecretArnList []string + + noSmithyDocumentSerde +} + +type BatchAssociateScramSecretOutput struct { + + // The Amazon Resource Name (ARN) of the cluster. + ClusterArn *string + + // List of errors when associating secrets to cluster. + UnprocessedScramSecrets []types.UnprocessedScramSecret + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationBatchAssociateScramSecretMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpBatchAssociateScramSecret{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpBatchAssociateScramSecret{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "BatchAssociateScramSecret"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpBatchAssociateScramSecretValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opBatchAssociateScramSecret(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opBatchAssociateScramSecret(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "BatchAssociateScramSecret", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_BatchDisassociateScramSecret.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_BatchDisassociateScramSecret.go new file mode 100644 index 000000000..4bc186642 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_BatchDisassociateScramSecret.go @@ -0,0 +1,166 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Disassociates one or more Scram Secrets from an Amazon MSK cluster. +func (c *Client) BatchDisassociateScramSecret(ctx context.Context, params *BatchDisassociateScramSecretInput, optFns ...func(*Options)) (*BatchDisassociateScramSecretOutput, error) { + if params == nil { + params = &BatchDisassociateScramSecretInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "BatchDisassociateScramSecret", params, optFns, c.addOperationBatchDisassociateScramSecretMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*BatchDisassociateScramSecretOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Disassociates sasl scram secrets to cluster. +type BatchDisassociateScramSecretInput struct { + + // The Amazon Resource Name (ARN) of the cluster to be updated. + // + // This member is required. + ClusterArn *string + + // List of AWS Secrets Manager secret ARNs. + // + // This member is required. + SecretArnList []string + + noSmithyDocumentSerde +} + +type BatchDisassociateScramSecretOutput struct { + + // The Amazon Resource Name (ARN) of the cluster. + ClusterArn *string + + // List of errors when disassociating secrets to cluster. + UnprocessedScramSecrets []types.UnprocessedScramSecret + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationBatchDisassociateScramSecretMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpBatchDisassociateScramSecret{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpBatchDisassociateScramSecret{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "BatchDisassociateScramSecret"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpBatchDisassociateScramSecretValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opBatchDisassociateScramSecret(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opBatchDisassociateScramSecret(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "BatchDisassociateScramSecret", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_CreateCluster.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_CreateCluster.go new file mode 100644 index 000000000..c2da2b431 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_CreateCluster.go @@ -0,0 +1,209 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Creates a new MSK cluster. +func (c *Client) CreateCluster(ctx context.Context, params *CreateClusterInput, optFns ...func(*Options)) (*CreateClusterOutput, error) { + if params == nil { + params = &CreateClusterInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "CreateCluster", params, optFns, c.addOperationCreateClusterMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*CreateClusterOutput) + out.ResultMetadata = metadata + return out, nil +} + +type CreateClusterInput struct { + + // Information about the broker nodes in the cluster. + // + // This member is required. + BrokerNodeGroupInfo *types.BrokerNodeGroupInfo + + // The name of the cluster. + // + // This member is required. + ClusterName *string + + // The version of Apache Kafka. + // + // This member is required. + KafkaVersion *string + + // The number of broker nodes in the cluster. + // + // This member is required. + NumberOfBrokerNodes *int32 + + // Includes all client authentication related information. + ClientAuthentication *types.ClientAuthentication + + // Represents the configuration that you want MSK to use for the brokers in a + // cluster. + ConfigurationInfo *types.ConfigurationInfo + + // Includes all encryption-related information. + EncryptionInfo *types.EncryptionInfo + + // Specifies the level of monitoring for the MSK cluster. The possible values are + // DEFAULT, PER_BROKER, PER_TOPIC_PER_BROKER, and PER_TOPIC_PER_PARTITION. + EnhancedMonitoring types.EnhancedMonitoring + + LoggingInfo *types.LoggingInfo + + // The settings for open monitoring. + OpenMonitoring *types.OpenMonitoringInfo + + // Specifies if intelligent rebalancing should be turned on for the new MSK + // Provisioned cluster with Express brokers. By default, intelligent rebalancing + // status is ACTIVE for all new clusters. + Rebalancing *types.Rebalancing + + // This controls storage mode for supported storage tiers. + StorageMode types.StorageMode + + // Create tags when creating the cluster. + Tags map[string]string + + noSmithyDocumentSerde +} + +type CreateClusterOutput struct { + + // The Amazon Resource Name (ARN) of the cluster. + ClusterArn *string + + // The name of the MSK cluster. + ClusterName *string + + // The state of the cluster. The possible states are ACTIVE, CREATING, DELETING, + // FAILED, HEALING, MAINTENANCE, REBOOTING_BROKER, and UPDATING. + State types.ClusterState + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationCreateClusterMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpCreateCluster{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpCreateCluster{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "CreateCluster"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpCreateClusterValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opCreateCluster(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opCreateCluster(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "CreateCluster", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_CreateClusterV2.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_CreateClusterV2.go new file mode 100644 index 000000000..3f6bf8c28 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_CreateClusterV2.go @@ -0,0 +1,176 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Creates a new MSK cluster. +func (c *Client) CreateClusterV2(ctx context.Context, params *CreateClusterV2Input, optFns ...func(*Options)) (*CreateClusterV2Output, error) { + if params == nil { + params = &CreateClusterV2Input{} + } + + result, metadata, err := c.invokeOperation(ctx, "CreateClusterV2", params, optFns, c.addOperationCreateClusterV2Middlewares) + if err != nil { + return nil, err + } + + out := result.(*CreateClusterV2Output) + out.ResultMetadata = metadata + return out, nil +} + +type CreateClusterV2Input struct { + + // The name of the cluster. + // + // This member is required. + ClusterName *string + + // Information about the provisioned cluster. + Provisioned *types.ProvisionedRequest + + // Information about the serverless cluster. + Serverless *types.ServerlessRequest + + // A map of tags that you want the cluster to have. + Tags map[string]string + + noSmithyDocumentSerde +} + +type CreateClusterV2Output struct { + + // The Amazon Resource Name (ARN) of the cluster. + ClusterArn *string + + // The name of the MSK cluster. + ClusterName *string + + // The type of the cluster. The possible states are PROVISIONED or SERVERLESS. + ClusterType types.ClusterType + + // The state of the cluster. The possible states are ACTIVE, CREATING, DELETING, + // FAILED, HEALING, MAINTENANCE, REBOOTING_BROKER, and UPDATING. + State types.ClusterState + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationCreateClusterV2Middlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpCreateClusterV2{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpCreateClusterV2{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "CreateClusterV2"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpCreateClusterV2ValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opCreateClusterV2(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opCreateClusterV2(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "CreateClusterV2", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_CreateConfiguration.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_CreateConfiguration.go new file mode 100644 index 000000000..0d73c57ca --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_CreateConfiguration.go @@ -0,0 +1,185 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" + "time" +) + +// Creates a new MSK configuration. +func (c *Client) CreateConfiguration(ctx context.Context, params *CreateConfigurationInput, optFns ...func(*Options)) (*CreateConfigurationOutput, error) { + if params == nil { + params = &CreateConfigurationInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "CreateConfiguration", params, optFns, c.addOperationCreateConfigurationMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*CreateConfigurationOutput) + out.ResultMetadata = metadata + return out, nil +} + +type CreateConfigurationInput struct { + + // The name of the configuration. + // + // This member is required. + Name *string + + // Contents of the server.properties file. When using the API, you must ensure + // that the contents of the file are base64 encoded. When using the AWS Management + // Console, the SDK, or the AWS CLI, the contents of server.properties can be in + // plaintext. + // + // This member is required. + ServerProperties []byte + + // The description of the configuration. + Description *string + + // The versions of Apache Kafka with which you can use this MSK configuration. + KafkaVersions []string + + noSmithyDocumentSerde +} + +type CreateConfigurationOutput struct { + + // The Amazon Resource Name (ARN) of the configuration. + Arn *string + + // The time when the configuration was created. + CreationTime *time.Time + + // Latest revision of the configuration. + LatestRevision *types.ConfigurationRevision + + // The name of the configuration. + Name *string + + // The state of the configuration. The possible states are ACTIVE, DELETING, and + // DELETE_FAILED. + State types.ConfigurationState + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationCreateConfigurationMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpCreateConfiguration{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpCreateConfiguration{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "CreateConfiguration"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpCreateConfigurationValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opCreateConfiguration(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opCreateConfiguration(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "CreateConfiguration", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_CreateReplicator.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_CreateReplicator.go new file mode 100644 index 000000000..3f2d3ca6a --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_CreateReplicator.go @@ -0,0 +1,187 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Creates the replicator. +func (c *Client) CreateReplicator(ctx context.Context, params *CreateReplicatorInput, optFns ...func(*Options)) (*CreateReplicatorOutput, error) { + if params == nil { + params = &CreateReplicatorInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "CreateReplicator", params, optFns, c.addOperationCreateReplicatorMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*CreateReplicatorOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Creates a replicator using the specified configuration. +type CreateReplicatorInput struct { + + // Kafka Clusters to use in setting up sources / targets for replication. + // + // This member is required. + KafkaClusters []types.KafkaCluster + + // A list of replication configurations, where each configuration targets a given + // source cluster to target cluster replication flow. + // + // This member is required. + ReplicationInfoList []types.ReplicationInfo + + // The name of the replicator. Alpha-numeric characters with '-' are allowed. + // + // This member is required. + ReplicatorName *string + + // The ARN of the IAM role used by the replicator to access resources in the + // customer's account (e.g source and target clusters) + // + // This member is required. + ServiceExecutionRoleArn *string + + // A summary description of the replicator. + Description *string + + // List of tags to attach to created Replicator. + Tags map[string]string + + noSmithyDocumentSerde +} + +type CreateReplicatorOutput struct { + + // The Amazon Resource Name (ARN) of the replicator. + ReplicatorArn *string + + // Name of the replicator provided by the customer. + ReplicatorName *string + + // State of the replicator. + ReplicatorState types.ReplicatorState + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationCreateReplicatorMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpCreateReplicator{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpCreateReplicator{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "CreateReplicator"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpCreateReplicatorValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opCreateReplicator(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opCreateReplicator(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "CreateReplicator", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_CreateVpcConnection.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_CreateVpcConnection.go new file mode 100644 index 000000000..3598352e6 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_CreateVpcConnection.go @@ -0,0 +1,202 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" + "time" +) + +// Creates a new MSK VPC connection. +func (c *Client) CreateVpcConnection(ctx context.Context, params *CreateVpcConnectionInput, optFns ...func(*Options)) (*CreateVpcConnectionOutput, error) { + if params == nil { + params = &CreateVpcConnectionInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "CreateVpcConnection", params, optFns, c.addOperationCreateVpcConnectionMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*CreateVpcConnectionOutput) + out.ResultMetadata = metadata + return out, nil +} + +type CreateVpcConnectionInput struct { + + // The authentication type of VPC connection. + // + // This member is required. + Authentication *string + + // The list of client subnets. + // + // This member is required. + ClientSubnets []string + + // The list of security groups. + // + // This member is required. + SecurityGroups []string + + // The cluster Amazon Resource Name (ARN) for the VPC connection. + // + // This member is required. + TargetClusterArn *string + + // The VPC ID of VPC connection. + // + // This member is required. + VpcId *string + + // A map of tags for the VPC connection. + Tags map[string]string + + noSmithyDocumentSerde +} + +type CreateVpcConnectionOutput struct { + + // The authentication type of VPC connection. + Authentication *string + + // The list of client subnets. + ClientSubnets []string + + // The creation time of VPC connection. + CreationTime *time.Time + + // The list of security groups. + SecurityGroups []string + + // The State of Vpc Connection. + State types.VpcConnectionState + + // A map of tags for the VPC connection. + Tags map[string]string + + // The VPC connection ARN. + VpcConnectionArn *string + + // The VPC ID of the VPC connection. + VpcId *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationCreateVpcConnectionMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpCreateVpcConnection{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpCreateVpcConnection{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "CreateVpcConnection"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpCreateVpcConnectionValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opCreateVpcConnection(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opCreateVpcConnection(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "CreateVpcConnection", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DeleteCluster.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DeleteCluster.go new file mode 100644 index 000000000..5387dc9a2 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DeleteCluster.go @@ -0,0 +1,165 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Deletes the MSK cluster specified by the Amazon Resource Name (ARN) in the +// request. +func (c *Client) DeleteCluster(ctx context.Context, params *DeleteClusterInput, optFns ...func(*Options)) (*DeleteClusterOutput, error) { + if params == nil { + params = &DeleteClusterInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DeleteCluster", params, optFns, c.addOperationDeleteClusterMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DeleteClusterOutput) + out.ResultMetadata = metadata + return out, nil +} + +type DeleteClusterInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies the cluster. + // + // This member is required. + ClusterArn *string + + // The current version of the MSK cluster. + CurrentVersion *string + + noSmithyDocumentSerde +} + +type DeleteClusterOutput struct { + + // The Amazon Resource Name (ARN) of the cluster. + ClusterArn *string + + // The state of the cluster. The possible states are ACTIVE, CREATING, DELETING, + // FAILED, HEALING, MAINTENANCE, REBOOTING_BROKER, and UPDATING. + State types.ClusterState + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDeleteClusterMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpDeleteCluster{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpDeleteCluster{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "DeleteCluster"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDeleteClusterValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opDeleteCluster(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opDeleteCluster(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "DeleteCluster", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DeleteClusterPolicy.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DeleteClusterPolicy.go new file mode 100644 index 000000000..4f16dcd18 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DeleteClusterPolicy.go @@ -0,0 +1,153 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Deletes the MSK cluster policy specified by the Amazon Resource Name (ARN) in +// the request. +func (c *Client) DeleteClusterPolicy(ctx context.Context, params *DeleteClusterPolicyInput, optFns ...func(*Options)) (*DeleteClusterPolicyOutput, error) { + if params == nil { + params = &DeleteClusterPolicyInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DeleteClusterPolicy", params, optFns, c.addOperationDeleteClusterPolicyMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DeleteClusterPolicyOutput) + out.ResultMetadata = metadata + return out, nil +} + +type DeleteClusterPolicyInput struct { + + // The Amazon Resource Name (ARN) of the cluster. + // + // This member is required. + ClusterArn *string + + noSmithyDocumentSerde +} + +type DeleteClusterPolicyOutput struct { + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDeleteClusterPolicyMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpDeleteClusterPolicy{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpDeleteClusterPolicy{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "DeleteClusterPolicy"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDeleteClusterPolicyValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opDeleteClusterPolicy(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opDeleteClusterPolicy(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "DeleteClusterPolicy", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DeleteConfiguration.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DeleteConfiguration.go new file mode 100644 index 000000000..4c1c9c27e --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DeleteConfiguration.go @@ -0,0 +1,161 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Deletes an MSK Configuration. +func (c *Client) DeleteConfiguration(ctx context.Context, params *DeleteConfigurationInput, optFns ...func(*Options)) (*DeleteConfigurationOutput, error) { + if params == nil { + params = &DeleteConfigurationInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DeleteConfiguration", params, optFns, c.addOperationDeleteConfigurationMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DeleteConfigurationOutput) + out.ResultMetadata = metadata + return out, nil +} + +type DeleteConfigurationInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies an MSK configuration. + // + // This member is required. + Arn *string + + noSmithyDocumentSerde +} + +type DeleteConfigurationOutput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies an MSK configuration. + Arn *string + + // The state of the configuration. The possible states are ACTIVE, DELETING, and + // DELETE_FAILED. + State types.ConfigurationState + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDeleteConfigurationMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpDeleteConfiguration{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpDeleteConfiguration{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "DeleteConfiguration"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDeleteConfigurationValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opDeleteConfiguration(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opDeleteConfiguration(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "DeleteConfiguration", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DeleteReplicator.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DeleteReplicator.go new file mode 100644 index 000000000..b8fb97a31 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DeleteReplicator.go @@ -0,0 +1,163 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Deletes a replicator. +func (c *Client) DeleteReplicator(ctx context.Context, params *DeleteReplicatorInput, optFns ...func(*Options)) (*DeleteReplicatorOutput, error) { + if params == nil { + params = &DeleteReplicatorInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DeleteReplicator", params, optFns, c.addOperationDeleteReplicatorMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DeleteReplicatorOutput) + out.ResultMetadata = metadata + return out, nil +} + +type DeleteReplicatorInput struct { + + // The Amazon Resource Name (ARN) of the replicator to be deleted. + // + // This member is required. + ReplicatorArn *string + + // The current version of the replicator. + CurrentVersion *string + + noSmithyDocumentSerde +} + +type DeleteReplicatorOutput struct { + + // The Amazon Resource Name (ARN) of the replicator. + ReplicatorArn *string + + // The state of the replicator. + ReplicatorState types.ReplicatorState + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDeleteReplicatorMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpDeleteReplicator{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpDeleteReplicator{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "DeleteReplicator"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDeleteReplicatorValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opDeleteReplicator(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opDeleteReplicator(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "DeleteReplicator", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DeleteVpcConnection.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DeleteVpcConnection.go new file mode 100644 index 000000000..79fe0f5e5 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DeleteVpcConnection.go @@ -0,0 +1,160 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Deletes a MSK VPC connection. +func (c *Client) DeleteVpcConnection(ctx context.Context, params *DeleteVpcConnectionInput, optFns ...func(*Options)) (*DeleteVpcConnectionOutput, error) { + if params == nil { + params = &DeleteVpcConnectionInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DeleteVpcConnection", params, optFns, c.addOperationDeleteVpcConnectionMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DeleteVpcConnectionOutput) + out.ResultMetadata = metadata + return out, nil +} + +type DeleteVpcConnectionInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies an MSK VPC connection. + // + // This member is required. + Arn *string + + noSmithyDocumentSerde +} + +type DeleteVpcConnectionOutput struct { + + // The state of the VPC connection. + State types.VpcConnectionState + + // The Amazon Resource Name (ARN) that uniquely identifies an MSK VPC connection. + VpcConnectionArn *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDeleteVpcConnectionMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpDeleteVpcConnection{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpDeleteVpcConnection{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "DeleteVpcConnection"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDeleteVpcConnectionValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opDeleteVpcConnection(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opDeleteVpcConnection(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "DeleteVpcConnection", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeCluster.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeCluster.go new file mode 100644 index 000000000..f31bb1d52 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeCluster.go @@ -0,0 +1,158 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Returns a description of the MSK cluster whose Amazon Resource Name (ARN) is +// specified in the request. +func (c *Client) DescribeCluster(ctx context.Context, params *DescribeClusterInput, optFns ...func(*Options)) (*DescribeClusterOutput, error) { + if params == nil { + params = &DescribeClusterInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DescribeCluster", params, optFns, c.addOperationDescribeClusterMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DescribeClusterOutput) + out.ResultMetadata = metadata + return out, nil +} + +type DescribeClusterInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies the cluster. + // + // This member is required. + ClusterArn *string + + noSmithyDocumentSerde +} + +type DescribeClusterOutput struct { + + // The cluster information. + ClusterInfo *types.ClusterInfo + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDescribeClusterMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpDescribeCluster{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpDescribeCluster{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "DescribeCluster"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDescribeClusterValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opDescribeCluster(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opDescribeCluster(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "DescribeCluster", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeClusterOperation.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeClusterOperation.go new file mode 100644 index 000000000..1a1554808 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeClusterOperation.go @@ -0,0 +1,158 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Returns a description of the cluster operation specified by the ARN. +func (c *Client) DescribeClusterOperation(ctx context.Context, params *DescribeClusterOperationInput, optFns ...func(*Options)) (*DescribeClusterOperationOutput, error) { + if params == nil { + params = &DescribeClusterOperationInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DescribeClusterOperation", params, optFns, c.addOperationDescribeClusterOperationMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DescribeClusterOperationOutput) + out.ResultMetadata = metadata + return out, nil +} + +type DescribeClusterOperationInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies the MSK cluster + // operation. + // + // This member is required. + ClusterOperationArn *string + + noSmithyDocumentSerde +} + +type DescribeClusterOperationOutput struct { + + // Cluster operation information + ClusterOperationInfo *types.ClusterOperationInfo + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDescribeClusterOperationMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpDescribeClusterOperation{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpDescribeClusterOperation{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "DescribeClusterOperation"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDescribeClusterOperationValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opDescribeClusterOperation(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opDescribeClusterOperation(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "DescribeClusterOperation", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeClusterOperationV2.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeClusterOperationV2.go new file mode 100644 index 000000000..d18869c89 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeClusterOperationV2.go @@ -0,0 +1,157 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Returns a description of the cluster operation specified by the ARN. +func (c *Client) DescribeClusterOperationV2(ctx context.Context, params *DescribeClusterOperationV2Input, optFns ...func(*Options)) (*DescribeClusterOperationV2Output, error) { + if params == nil { + params = &DescribeClusterOperationV2Input{} + } + + result, metadata, err := c.invokeOperation(ctx, "DescribeClusterOperationV2", params, optFns, c.addOperationDescribeClusterOperationV2Middlewares) + if err != nil { + return nil, err + } + + out := result.(*DescribeClusterOperationV2Output) + out.ResultMetadata = metadata + return out, nil +} + +type DescribeClusterOperationV2Input struct { + + // ARN of the cluster operation to describe. + // + // This member is required. + ClusterOperationArn *string + + noSmithyDocumentSerde +} + +type DescribeClusterOperationV2Output struct { + + // Cluster operation information + ClusterOperationInfo *types.ClusterOperationV2 + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDescribeClusterOperationV2Middlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpDescribeClusterOperationV2{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpDescribeClusterOperationV2{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "DescribeClusterOperationV2"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDescribeClusterOperationV2ValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opDescribeClusterOperationV2(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opDescribeClusterOperationV2(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "DescribeClusterOperationV2", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeClusterV2.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeClusterV2.go new file mode 100644 index 000000000..3cd3b2b08 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeClusterV2.go @@ -0,0 +1,158 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Returns a description of the MSK cluster whose Amazon Resource Name (ARN) is +// specified in the request. +func (c *Client) DescribeClusterV2(ctx context.Context, params *DescribeClusterV2Input, optFns ...func(*Options)) (*DescribeClusterV2Output, error) { + if params == nil { + params = &DescribeClusterV2Input{} + } + + result, metadata, err := c.invokeOperation(ctx, "DescribeClusterV2", params, optFns, c.addOperationDescribeClusterV2Middlewares) + if err != nil { + return nil, err + } + + out := result.(*DescribeClusterV2Output) + out.ResultMetadata = metadata + return out, nil +} + +type DescribeClusterV2Input struct { + + // The Amazon Resource Name (ARN) that uniquely identifies the cluster. + // + // This member is required. + ClusterArn *string + + noSmithyDocumentSerde +} + +type DescribeClusterV2Output struct { + + // The cluster information. + ClusterInfo *types.Cluster + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDescribeClusterV2Middlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpDescribeClusterV2{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpDescribeClusterV2{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "DescribeClusterV2"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDescribeClusterV2ValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opDescribeClusterV2(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opDescribeClusterV2(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "DescribeClusterV2", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeConfiguration.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeConfiguration.go new file mode 100644 index 000000000..2adc4e603 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeConfiguration.go @@ -0,0 +1,178 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" + "time" +) + +// Returns a description of this MSK configuration. +func (c *Client) DescribeConfiguration(ctx context.Context, params *DescribeConfigurationInput, optFns ...func(*Options)) (*DescribeConfigurationOutput, error) { + if params == nil { + params = &DescribeConfigurationInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DescribeConfiguration", params, optFns, c.addOperationDescribeConfigurationMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DescribeConfigurationOutput) + out.ResultMetadata = metadata + return out, nil +} + +type DescribeConfigurationInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies an MSK configuration + // and all of its revisions. + // + // This member is required. + Arn *string + + noSmithyDocumentSerde +} + +type DescribeConfigurationOutput struct { + + // The Amazon Resource Name (ARN) of the configuration. + Arn *string + + // The time when the configuration was created. + CreationTime *time.Time + + // The description of the configuration. + Description *string + + // The versions of Apache Kafka with which you can use this MSK configuration. + KafkaVersions []string + + // Latest revision of the configuration. + LatestRevision *types.ConfigurationRevision + + // The name of the configuration. + Name *string + + // The state of the configuration. The possible states are ACTIVE, DELETING, and + // DELETE_FAILED. + State types.ConfigurationState + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDescribeConfigurationMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpDescribeConfiguration{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpDescribeConfiguration{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "DescribeConfiguration"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDescribeConfigurationValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opDescribeConfiguration(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opDescribeConfiguration(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "DescribeConfiguration", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeConfigurationRevision.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeConfigurationRevision.go new file mode 100644 index 000000000..d4bd207a4 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeConfigurationRevision.go @@ -0,0 +1,178 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" + "time" +) + +// Returns a description of this revision of the configuration. +func (c *Client) DescribeConfigurationRevision(ctx context.Context, params *DescribeConfigurationRevisionInput, optFns ...func(*Options)) (*DescribeConfigurationRevisionOutput, error) { + if params == nil { + params = &DescribeConfigurationRevisionInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DescribeConfigurationRevision", params, optFns, c.addOperationDescribeConfigurationRevisionMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DescribeConfigurationRevisionOutput) + out.ResultMetadata = metadata + return out, nil +} + +type DescribeConfigurationRevisionInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies an MSK configuration + // and all of its revisions. + // + // This member is required. + Arn *string + + // A string that uniquely identifies a revision of an MSK configuration. + // + // This member is required. + Revision *int64 + + noSmithyDocumentSerde +} + +type DescribeConfigurationRevisionOutput struct { + + // The Amazon Resource Name (ARN) of the configuration. + Arn *string + + // The time when the configuration was created. + CreationTime *time.Time + + // The description of the configuration. + Description *string + + // The revision number. + Revision *int64 + + // Contents of the server.properties file. When using the API, you must ensure + // that the contents of the file are base64 encoded. When using the AWS Management + // Console, the SDK, or the AWS CLI, the contents of server.properties can be in + // plaintext. + ServerProperties []byte + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDescribeConfigurationRevisionMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpDescribeConfigurationRevision{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpDescribeConfigurationRevision{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "DescribeConfigurationRevision"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDescribeConfigurationRevisionValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opDescribeConfigurationRevision(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opDescribeConfigurationRevision(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "DescribeConfigurationRevision", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeReplicator.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeReplicator.go new file mode 100644 index 000000000..d822cfebf --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeReplicator.go @@ -0,0 +1,197 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" + "time" +) + +// Describes a replicator. +func (c *Client) DescribeReplicator(ctx context.Context, params *DescribeReplicatorInput, optFns ...func(*Options)) (*DescribeReplicatorOutput, error) { + if params == nil { + params = &DescribeReplicatorInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DescribeReplicator", params, optFns, c.addOperationDescribeReplicatorMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DescribeReplicatorOutput) + out.ResultMetadata = metadata + return out, nil +} + +type DescribeReplicatorInput struct { + + // The Amazon Resource Name (ARN) of the replicator to be described. + // + // This member is required. + ReplicatorArn *string + + noSmithyDocumentSerde +} + +type DescribeReplicatorOutput struct { + + // The time when the replicator was created. + CreationTime *time.Time + + // The current version number of the replicator. + CurrentVersion *string + + // Whether this resource is a replicator reference. + IsReplicatorReference *bool + + // Kafka Clusters used in setting up sources / targets for replication. + KafkaClusters []types.KafkaClusterDescription + + // A list of replication configurations, where each configuration targets a given + // source cluster to target cluster replication flow. + ReplicationInfoList []types.ReplicationInfoDescription + + // The Amazon Resource Name (ARN) of the replicator. + ReplicatorArn *string + + // The description of the replicator. + ReplicatorDescription *string + + // The name of the replicator. + ReplicatorName *string + + // The Amazon Resource Name (ARN) of the replicator resource in the region where + // the replicator was created. + ReplicatorResourceArn *string + + // State of the replicator. + ReplicatorState types.ReplicatorState + + // The Amazon Resource Name (ARN) of the IAM role used by the replicator to access + // resources in the customer's account (e.g source and target clusters) + ServiceExecutionRoleArn *string + + // Details about the state of the replicator. + StateInfo *types.ReplicationStateInfo + + // List of tags attached to the Replicator. + Tags map[string]string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDescribeReplicatorMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpDescribeReplicator{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpDescribeReplicator{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "DescribeReplicator"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDescribeReplicatorValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opDescribeReplicator(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opDescribeReplicator(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "DescribeReplicator", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeTopic.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeTopic.go new file mode 100644 index 000000000..4b0af7e77 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeTopic.go @@ -0,0 +1,177 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Returns topic details of this topic on a MSK cluster. +func (c *Client) DescribeTopic(ctx context.Context, params *DescribeTopicInput, optFns ...func(*Options)) (*DescribeTopicOutput, error) { + if params == nil { + params = &DescribeTopicInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DescribeTopic", params, optFns, c.addOperationDescribeTopicMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DescribeTopicOutput) + out.ResultMetadata = metadata + return out, nil +} + +type DescribeTopicInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies the cluster. + // + // This member is required. + ClusterArn *string + + // The Kafka topic name that uniquely identifies the topic. + // + // This member is required. + TopicName *string + + noSmithyDocumentSerde +} + +type DescribeTopicOutput struct { + + // Topic configurations encoded as a Base64 string. + Configs *string + + // The partition count of the topic. + PartitionCount *int32 + + // The replication factor of the topic. + ReplicationFactor *int32 + + // The status of the topic. + Status types.TopicState + + // The Amazon Resource Name (ARN) of the topic. + TopicArn *string + + // The Kafka topic name of the topic. + TopicName *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDescribeTopicMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpDescribeTopic{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpDescribeTopic{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "DescribeTopic"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDescribeTopicValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opDescribeTopic(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opDescribeTopic(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "DescribeTopic", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeTopicPartitions.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeTopicPartitions.go new file mode 100644 index 000000000..01967c25a --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeTopicPartitions.go @@ -0,0 +1,272 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Returns partition details of this topic on a MSK cluster. +func (c *Client) DescribeTopicPartitions(ctx context.Context, params *DescribeTopicPartitionsInput, optFns ...func(*Options)) (*DescribeTopicPartitionsOutput, error) { + if params == nil { + params = &DescribeTopicPartitionsInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DescribeTopicPartitions", params, optFns, c.addOperationDescribeTopicPartitionsMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DescribeTopicPartitionsOutput) + out.ResultMetadata = metadata + return out, nil +} + +type DescribeTopicPartitionsInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies the cluster. + // + // This member is required. + ClusterArn *string + + // The Kafka topic name that uniquely identifies the topic. + // + // This member is required. + TopicName *string + + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + MaxResults *int32 + + // The paginated results marker. When the result of the operation is truncated, + // the call returns NextToken in the response. To get the next batch, provide this + // token in your next request. + NextToken *string + + noSmithyDocumentSerde +} + +type DescribeTopicPartitionsOutput struct { + + // The paginated results marker. When the result of a DescribeTopicPartitions + // operation is truncated, the call returns NextToken in the response. To get + // another batch of configurations, provide this token in your next request. + NextToken *string + + // The list of partition information for the topic. + Partitions []types.TopicPartitionInfo + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDescribeTopicPartitionsMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpDescribeTopicPartitions{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpDescribeTopicPartitions{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "DescribeTopicPartitions"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDescribeTopicPartitionsValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opDescribeTopicPartitions(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +// DescribeTopicPartitionsPaginatorOptions is the paginator options for +// DescribeTopicPartitions +type DescribeTopicPartitionsPaginatorOptions struct { + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + Limit int32 + + // Set to true if pagination should stop if the service returns a pagination token + // that matches the most recent token provided to the service. + StopOnDuplicateToken bool +} + +// DescribeTopicPartitionsPaginator is a paginator for DescribeTopicPartitions +type DescribeTopicPartitionsPaginator struct { + options DescribeTopicPartitionsPaginatorOptions + client DescribeTopicPartitionsAPIClient + params *DescribeTopicPartitionsInput + nextToken *string + firstPage bool +} + +// NewDescribeTopicPartitionsPaginator returns a new +// DescribeTopicPartitionsPaginator +func NewDescribeTopicPartitionsPaginator(client DescribeTopicPartitionsAPIClient, params *DescribeTopicPartitionsInput, optFns ...func(*DescribeTopicPartitionsPaginatorOptions)) *DescribeTopicPartitionsPaginator { + if params == nil { + params = &DescribeTopicPartitionsInput{} + } + + options := DescribeTopicPartitionsPaginatorOptions{} + if params.MaxResults != nil { + options.Limit = *params.MaxResults + } + + for _, fn := range optFns { + fn(&options) + } + + return &DescribeTopicPartitionsPaginator{ + options: options, + client: client, + params: params, + firstPage: true, + nextToken: params.NextToken, + } +} + +// HasMorePages returns a boolean indicating whether more pages are available +func (p *DescribeTopicPartitionsPaginator) HasMorePages() bool { + return p.firstPage || (p.nextToken != nil && len(*p.nextToken) != 0) +} + +// NextPage retrieves the next DescribeTopicPartitions page. +func (p *DescribeTopicPartitionsPaginator) NextPage(ctx context.Context, optFns ...func(*Options)) (*DescribeTopicPartitionsOutput, error) { + if !p.HasMorePages() { + return nil, fmt.Errorf("no more pages available") + } + + params := *p.params + params.NextToken = p.nextToken + + var limit *int32 + if p.options.Limit > 0 { + limit = &p.options.Limit + } + params.MaxResults = limit + + optFns = append([]func(*Options){ + addIsPaginatorUserAgent, + }, optFns...) + result, err := p.client.DescribeTopicPartitions(ctx, ¶ms, optFns...) + if err != nil { + return nil, err + } + p.firstPage = false + + prevToken := p.nextToken + p.nextToken = result.NextToken + + if p.options.StopOnDuplicateToken && + prevToken != nil && + p.nextToken != nil && + *prevToken == *p.nextToken { + p.nextToken = nil + } + + return result, nil +} + +// DescribeTopicPartitionsAPIClient is a client that implements the +// DescribeTopicPartitions operation. +type DescribeTopicPartitionsAPIClient interface { + DescribeTopicPartitions(context.Context, *DescribeTopicPartitionsInput, ...func(*Options)) (*DescribeTopicPartitionsOutput, error) +} + +var _ DescribeTopicPartitionsAPIClient = (*Client)(nil) + +func newServiceMetadataMiddleware_opDescribeTopicPartitions(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "DescribeTopicPartitions", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeVpcConnection.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeVpcConnection.go new file mode 100644 index 000000000..015ce8a45 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_DescribeVpcConnection.go @@ -0,0 +1,182 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" + "time" +) + +// Returns a description of this MSK VPC connection. +func (c *Client) DescribeVpcConnection(ctx context.Context, params *DescribeVpcConnectionInput, optFns ...func(*Options)) (*DescribeVpcConnectionOutput, error) { + if params == nil { + params = &DescribeVpcConnectionInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DescribeVpcConnection", params, optFns, c.addOperationDescribeVpcConnectionMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DescribeVpcConnectionOutput) + out.ResultMetadata = metadata + return out, nil +} + +type DescribeVpcConnectionInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies a MSK VPC connection. + // + // This member is required. + Arn *string + + noSmithyDocumentSerde +} + +type DescribeVpcConnectionOutput struct { + + // The authentication type of VPC connection. + Authentication *string + + // The creation time of the VPC connection. + CreationTime *time.Time + + // The list of security groups for the VPC connection. + SecurityGroups []string + + // The state of VPC connection. + State types.VpcConnectionState + + // The list of subnets for the VPC connection. + Subnets []string + + // A map of tags for the VPC connection. + Tags map[string]string + + // The Amazon Resource Name (ARN) that uniquely identifies an MSK cluster. + TargetClusterArn *string + + // The Amazon Resource Name (ARN) that uniquely identifies a MSK VPC connection. + VpcConnectionArn *string + + // The VPC Id for the VPC connection. + VpcId *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDescribeVpcConnectionMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpDescribeVpcConnection{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpDescribeVpcConnection{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "DescribeVpcConnection"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDescribeVpcConnectionValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opDescribeVpcConnection(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opDescribeVpcConnection(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "DescribeVpcConnection", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_GetBootstrapBrokers.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_GetBootstrapBrokers.go new file mode 100644 index 000000000..97f2f0627 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_GetBootstrapBrokers.go @@ -0,0 +1,193 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// A list of brokers that a client application can use to bootstrap. This list +// doesn't necessarily include all of the brokers in the cluster. The following +// Python 3.6 example shows how you can use the Amazon Resource Name (ARN) of a +// cluster to get its bootstrap brokers. If you don't know the ARN of your cluster, +// you can use the ListClusters operation to get the ARNs of all the clusters in +// this account and Region. +func (c *Client) GetBootstrapBrokers(ctx context.Context, params *GetBootstrapBrokersInput, optFns ...func(*Options)) (*GetBootstrapBrokersOutput, error) { + if params == nil { + params = &GetBootstrapBrokersInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "GetBootstrapBrokers", params, optFns, c.addOperationGetBootstrapBrokersMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*GetBootstrapBrokersOutput) + out.ResultMetadata = metadata + return out, nil +} + +type GetBootstrapBrokersInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies the cluster. + // + // This member is required. + ClusterArn *string + + noSmithyDocumentSerde +} + +type GetBootstrapBrokersOutput struct { + + // A string containing one or more hostname:port pairs. + BootstrapBrokerString *string + + // A string that contains one or more DNS names (or IP addresses) and SASL IAM + // port pairs. + BootstrapBrokerStringPublicSaslIam *string + + // A string containing one or more DNS names (or IP) and Sasl Scram port pairs. + BootstrapBrokerStringPublicSaslScram *string + + // A string containing one or more DNS names (or IP) and TLS port pairs. + BootstrapBrokerStringPublicTls *string + + // A string that contains one or more DNS names (or IP addresses) and SASL IAM + // port pairs. + BootstrapBrokerStringSaslIam *string + + // A string containing one or more DNS names (or IP) and Sasl Scram port pairs. + BootstrapBrokerStringSaslScram *string + + // A string containing one or more DNS names (or IP) and TLS port pairs. + BootstrapBrokerStringTls *string + + // A string containing one or more DNS names (or IP) and SASL/IAM port pairs for + // VPC connectivity. + BootstrapBrokerStringVpcConnectivitySaslIam *string + + // A string containing one or more DNS names (or IP) and SASL/SCRAM port pairs for + // VPC connectivity. + BootstrapBrokerStringVpcConnectivitySaslScram *string + + // A string containing one or more DNS names (or IP) and TLS port pairs for VPC + // connectivity. + BootstrapBrokerStringVpcConnectivityTls *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationGetBootstrapBrokersMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpGetBootstrapBrokers{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpGetBootstrapBrokers{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "GetBootstrapBrokers"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpGetBootstrapBrokersValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opGetBootstrapBrokers(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opGetBootstrapBrokers(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "GetBootstrapBrokers", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_GetClusterPolicy.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_GetClusterPolicy.go new file mode 100644 index 000000000..ab9fedda1 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_GetClusterPolicy.go @@ -0,0 +1,160 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Get the MSK cluster policy specified by the Amazon Resource Name (ARN) in the +// request. +func (c *Client) GetClusterPolicy(ctx context.Context, params *GetClusterPolicyInput, optFns ...func(*Options)) (*GetClusterPolicyOutput, error) { + if params == nil { + params = &GetClusterPolicyInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "GetClusterPolicy", params, optFns, c.addOperationGetClusterPolicyMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*GetClusterPolicyOutput) + out.ResultMetadata = metadata + return out, nil +} + +type GetClusterPolicyInput struct { + + // The Amazon Resource Name (ARN) of the cluster. + // + // This member is required. + ClusterArn *string + + noSmithyDocumentSerde +} + +type GetClusterPolicyOutput struct { + + // The version of cluster policy. + CurrentVersion *string + + // The cluster policy. + Policy *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationGetClusterPolicyMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpGetClusterPolicy{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpGetClusterPolicy{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "GetClusterPolicy"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpGetClusterPolicyValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opGetClusterPolicy(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opGetClusterPolicy(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "GetClusterPolicy", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_GetCompatibleKafkaVersions.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_GetCompatibleKafkaVersions.go new file mode 100644 index 000000000..fe6e08534 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_GetCompatibleKafkaVersions.go @@ -0,0 +1,152 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Gets the Apache Kafka versions to which you can update the MSK cluster. +func (c *Client) GetCompatibleKafkaVersions(ctx context.Context, params *GetCompatibleKafkaVersionsInput, optFns ...func(*Options)) (*GetCompatibleKafkaVersionsOutput, error) { + if params == nil { + params = &GetCompatibleKafkaVersionsInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "GetCompatibleKafkaVersions", params, optFns, c.addOperationGetCompatibleKafkaVersionsMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*GetCompatibleKafkaVersionsOutput) + out.ResultMetadata = metadata + return out, nil +} + +type GetCompatibleKafkaVersionsInput struct { + + // The Amazon Resource Name (ARN) of the cluster check. + ClusterArn *string + + noSmithyDocumentSerde +} + +type GetCompatibleKafkaVersionsOutput struct { + + // A list of CompatibleKafkaVersion objects. + CompatibleKafkaVersions []types.CompatibleKafkaVersion + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationGetCompatibleKafkaVersionsMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpGetCompatibleKafkaVersions{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpGetCompatibleKafkaVersions{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "GetCompatibleKafkaVersions"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opGetCompatibleKafkaVersions(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opGetCompatibleKafkaVersions(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "GetCompatibleKafkaVersions", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListClientVpcConnections.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListClientVpcConnections.go new file mode 100644 index 000000000..e1ccf927e --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListClientVpcConnections.go @@ -0,0 +1,267 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Returns a list of all the VPC connections in this Region. +func (c *Client) ListClientVpcConnections(ctx context.Context, params *ListClientVpcConnectionsInput, optFns ...func(*Options)) (*ListClientVpcConnectionsOutput, error) { + if params == nil { + params = &ListClientVpcConnectionsInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "ListClientVpcConnections", params, optFns, c.addOperationListClientVpcConnectionsMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*ListClientVpcConnectionsOutput) + out.ResultMetadata = metadata + return out, nil +} + +type ListClientVpcConnectionsInput struct { + + // The Amazon Resource Name (ARN) of the cluster. + // + // This member is required. + ClusterArn *string + + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + MaxResults *int32 + + // The paginated results marker. When the result of the operation is truncated, + // the call returns NextToken in the response. To get the next batch, provide this + // token in your next request. + NextToken *string + + noSmithyDocumentSerde +} + +type ListClientVpcConnectionsOutput struct { + + // List of client VPC connections. + ClientVpcConnections []types.ClientVpcConnection + + // The paginated results marker. When the result of a ListClientVpcConnections + // operation is truncated, the call returns NextToken in the response. To get + // another batch of configurations, provide this token in your next request. + NextToken *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationListClientVpcConnectionsMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpListClientVpcConnections{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpListClientVpcConnections{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "ListClientVpcConnections"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpListClientVpcConnectionsValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListClientVpcConnections(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +// ListClientVpcConnectionsPaginatorOptions is the paginator options for +// ListClientVpcConnections +type ListClientVpcConnectionsPaginatorOptions struct { + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + Limit int32 + + // Set to true if pagination should stop if the service returns a pagination token + // that matches the most recent token provided to the service. + StopOnDuplicateToken bool +} + +// ListClientVpcConnectionsPaginator is a paginator for ListClientVpcConnections +type ListClientVpcConnectionsPaginator struct { + options ListClientVpcConnectionsPaginatorOptions + client ListClientVpcConnectionsAPIClient + params *ListClientVpcConnectionsInput + nextToken *string + firstPage bool +} + +// NewListClientVpcConnectionsPaginator returns a new +// ListClientVpcConnectionsPaginator +func NewListClientVpcConnectionsPaginator(client ListClientVpcConnectionsAPIClient, params *ListClientVpcConnectionsInput, optFns ...func(*ListClientVpcConnectionsPaginatorOptions)) *ListClientVpcConnectionsPaginator { + if params == nil { + params = &ListClientVpcConnectionsInput{} + } + + options := ListClientVpcConnectionsPaginatorOptions{} + if params.MaxResults != nil { + options.Limit = *params.MaxResults + } + + for _, fn := range optFns { + fn(&options) + } + + return &ListClientVpcConnectionsPaginator{ + options: options, + client: client, + params: params, + firstPage: true, + nextToken: params.NextToken, + } +} + +// HasMorePages returns a boolean indicating whether more pages are available +func (p *ListClientVpcConnectionsPaginator) HasMorePages() bool { + return p.firstPage || (p.nextToken != nil && len(*p.nextToken) != 0) +} + +// NextPage retrieves the next ListClientVpcConnections page. +func (p *ListClientVpcConnectionsPaginator) NextPage(ctx context.Context, optFns ...func(*Options)) (*ListClientVpcConnectionsOutput, error) { + if !p.HasMorePages() { + return nil, fmt.Errorf("no more pages available") + } + + params := *p.params + params.NextToken = p.nextToken + + var limit *int32 + if p.options.Limit > 0 { + limit = &p.options.Limit + } + params.MaxResults = limit + + optFns = append([]func(*Options){ + addIsPaginatorUserAgent, + }, optFns...) + result, err := p.client.ListClientVpcConnections(ctx, ¶ms, optFns...) + if err != nil { + return nil, err + } + p.firstPage = false + + prevToken := p.nextToken + p.nextToken = result.NextToken + + if p.options.StopOnDuplicateToken && + prevToken != nil && + p.nextToken != nil && + *prevToken == *p.nextToken { + p.nextToken = nil + } + + return result, nil +} + +// ListClientVpcConnectionsAPIClient is a client that implements the +// ListClientVpcConnections operation. +type ListClientVpcConnectionsAPIClient interface { + ListClientVpcConnections(context.Context, *ListClientVpcConnectionsInput, ...func(*Options)) (*ListClientVpcConnectionsOutput, error) +} + +var _ ListClientVpcConnectionsAPIClient = (*Client)(nil) + +func newServiceMetadataMiddleware_opListClientVpcConnections(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "ListClientVpcConnections", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListClusterOperations.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListClusterOperations.go new file mode 100644 index 000000000..f6cfe4e35 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListClusterOperations.go @@ -0,0 +1,267 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Returns a list of all the operations that have been performed on the specified +// MSK cluster. +func (c *Client) ListClusterOperations(ctx context.Context, params *ListClusterOperationsInput, optFns ...func(*Options)) (*ListClusterOperationsOutput, error) { + if params == nil { + params = &ListClusterOperationsInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "ListClusterOperations", params, optFns, c.addOperationListClusterOperationsMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*ListClusterOperationsOutput) + out.ResultMetadata = metadata + return out, nil +} + +type ListClusterOperationsInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies the cluster. + // + // This member is required. + ClusterArn *string + + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + MaxResults *int32 + + // The paginated results marker. When the result of the operation is truncated, + // the call returns NextToken in the response. To get the next batch, provide this + // token in your next request. + NextToken *string + + noSmithyDocumentSerde +} + +type ListClusterOperationsOutput struct { + + // An array of cluster operation information objects. + ClusterOperationInfoList []types.ClusterOperationInfo + + // If the response of ListClusterOperations is truncated, it returns a NextToken + // in the response. This Nexttoken should be sent in the subsequent request to + // ListClusterOperations. + NextToken *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationListClusterOperationsMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpListClusterOperations{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpListClusterOperations{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "ListClusterOperations"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpListClusterOperationsValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListClusterOperations(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +// ListClusterOperationsPaginatorOptions is the paginator options for +// ListClusterOperations +type ListClusterOperationsPaginatorOptions struct { + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + Limit int32 + + // Set to true if pagination should stop if the service returns a pagination token + // that matches the most recent token provided to the service. + StopOnDuplicateToken bool +} + +// ListClusterOperationsPaginator is a paginator for ListClusterOperations +type ListClusterOperationsPaginator struct { + options ListClusterOperationsPaginatorOptions + client ListClusterOperationsAPIClient + params *ListClusterOperationsInput + nextToken *string + firstPage bool +} + +// NewListClusterOperationsPaginator returns a new ListClusterOperationsPaginator +func NewListClusterOperationsPaginator(client ListClusterOperationsAPIClient, params *ListClusterOperationsInput, optFns ...func(*ListClusterOperationsPaginatorOptions)) *ListClusterOperationsPaginator { + if params == nil { + params = &ListClusterOperationsInput{} + } + + options := ListClusterOperationsPaginatorOptions{} + if params.MaxResults != nil { + options.Limit = *params.MaxResults + } + + for _, fn := range optFns { + fn(&options) + } + + return &ListClusterOperationsPaginator{ + options: options, + client: client, + params: params, + firstPage: true, + nextToken: params.NextToken, + } +} + +// HasMorePages returns a boolean indicating whether more pages are available +func (p *ListClusterOperationsPaginator) HasMorePages() bool { + return p.firstPage || (p.nextToken != nil && len(*p.nextToken) != 0) +} + +// NextPage retrieves the next ListClusterOperations page. +func (p *ListClusterOperationsPaginator) NextPage(ctx context.Context, optFns ...func(*Options)) (*ListClusterOperationsOutput, error) { + if !p.HasMorePages() { + return nil, fmt.Errorf("no more pages available") + } + + params := *p.params + params.NextToken = p.nextToken + + var limit *int32 + if p.options.Limit > 0 { + limit = &p.options.Limit + } + params.MaxResults = limit + + optFns = append([]func(*Options){ + addIsPaginatorUserAgent, + }, optFns...) + result, err := p.client.ListClusterOperations(ctx, ¶ms, optFns...) + if err != nil { + return nil, err + } + p.firstPage = false + + prevToken := p.nextToken + p.nextToken = result.NextToken + + if p.options.StopOnDuplicateToken && + prevToken != nil && + p.nextToken != nil && + *prevToken == *p.nextToken { + p.nextToken = nil + } + + return result, nil +} + +// ListClusterOperationsAPIClient is a client that implements the +// ListClusterOperations operation. +type ListClusterOperationsAPIClient interface { + ListClusterOperations(context.Context, *ListClusterOperationsInput, ...func(*Options)) (*ListClusterOperationsOutput, error) +} + +var _ ListClusterOperationsAPIClient = (*Client)(nil) + +func newServiceMetadataMiddleware_opListClusterOperations(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "ListClusterOperations", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListClusterOperationsV2.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListClusterOperationsV2.go new file mode 100644 index 000000000..e2c18ee18 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListClusterOperationsV2.go @@ -0,0 +1,264 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Returns a list of all the operations that have been performed on the specified +// MSK cluster. +func (c *Client) ListClusterOperationsV2(ctx context.Context, params *ListClusterOperationsV2Input, optFns ...func(*Options)) (*ListClusterOperationsV2Output, error) { + if params == nil { + params = &ListClusterOperationsV2Input{} + } + + result, metadata, err := c.invokeOperation(ctx, "ListClusterOperationsV2", params, optFns, c.addOperationListClusterOperationsV2Middlewares) + if err != nil { + return nil, err + } + + out := result.(*ListClusterOperationsV2Output) + out.ResultMetadata = metadata + return out, nil +} + +type ListClusterOperationsV2Input struct { + + // The arn of the cluster whose operations are being requested. + // + // This member is required. + ClusterArn *string + + // The maxResults of the query. + MaxResults *int32 + + // The nextToken of the query. + NextToken *string + + noSmithyDocumentSerde +} + +type ListClusterOperationsV2Output struct { + + // An array of cluster operation information objects. + ClusterOperationInfoList []types.ClusterOperationV2Summary + + // If the response of ListClusterOperationsV2 is truncated, it returns a NextToken + // in the response. This NextToken should be sent in the subsequent request to + // ListClusterOperationsV2. + NextToken *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationListClusterOperationsV2Middlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpListClusterOperationsV2{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpListClusterOperationsV2{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "ListClusterOperationsV2"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpListClusterOperationsV2ValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListClusterOperationsV2(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +// ListClusterOperationsV2PaginatorOptions is the paginator options for +// ListClusterOperationsV2 +type ListClusterOperationsV2PaginatorOptions struct { + // The maxResults of the query. + Limit int32 + + // Set to true if pagination should stop if the service returns a pagination token + // that matches the most recent token provided to the service. + StopOnDuplicateToken bool +} + +// ListClusterOperationsV2Paginator is a paginator for ListClusterOperationsV2 +type ListClusterOperationsV2Paginator struct { + options ListClusterOperationsV2PaginatorOptions + client ListClusterOperationsV2APIClient + params *ListClusterOperationsV2Input + nextToken *string + firstPage bool +} + +// NewListClusterOperationsV2Paginator returns a new +// ListClusterOperationsV2Paginator +func NewListClusterOperationsV2Paginator(client ListClusterOperationsV2APIClient, params *ListClusterOperationsV2Input, optFns ...func(*ListClusterOperationsV2PaginatorOptions)) *ListClusterOperationsV2Paginator { + if params == nil { + params = &ListClusterOperationsV2Input{} + } + + options := ListClusterOperationsV2PaginatorOptions{} + if params.MaxResults != nil { + options.Limit = *params.MaxResults + } + + for _, fn := range optFns { + fn(&options) + } + + return &ListClusterOperationsV2Paginator{ + options: options, + client: client, + params: params, + firstPage: true, + nextToken: params.NextToken, + } +} + +// HasMorePages returns a boolean indicating whether more pages are available +func (p *ListClusterOperationsV2Paginator) HasMorePages() bool { + return p.firstPage || (p.nextToken != nil && len(*p.nextToken) != 0) +} + +// NextPage retrieves the next ListClusterOperationsV2 page. +func (p *ListClusterOperationsV2Paginator) NextPage(ctx context.Context, optFns ...func(*Options)) (*ListClusterOperationsV2Output, error) { + if !p.HasMorePages() { + return nil, fmt.Errorf("no more pages available") + } + + params := *p.params + params.NextToken = p.nextToken + + var limit *int32 + if p.options.Limit > 0 { + limit = &p.options.Limit + } + params.MaxResults = limit + + optFns = append([]func(*Options){ + addIsPaginatorUserAgent, + }, optFns...) + result, err := p.client.ListClusterOperationsV2(ctx, ¶ms, optFns...) + if err != nil { + return nil, err + } + p.firstPage = false + + prevToken := p.nextToken + p.nextToken = result.NextToken + + if p.options.StopOnDuplicateToken && + prevToken != nil && + p.nextToken != nil && + *prevToken == *p.nextToken { + p.nextToken = nil + } + + return result, nil +} + +// ListClusterOperationsV2APIClient is a client that implements the +// ListClusterOperationsV2 operation. +type ListClusterOperationsV2APIClient interface { + ListClusterOperationsV2(context.Context, *ListClusterOperationsV2Input, ...func(*Options)) (*ListClusterOperationsV2Output, error) +} + +var _ ListClusterOperationsV2APIClient = (*Client)(nil) + +func newServiceMetadataMiddleware_opListClusterOperationsV2(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "ListClusterOperationsV2", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListClusters.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListClusters.go new file mode 100644 index 000000000..0aa8ee044 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListClusters.go @@ -0,0 +1,260 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Returns a list of all the MSK clusters in the current Region. +func (c *Client) ListClusters(ctx context.Context, params *ListClustersInput, optFns ...func(*Options)) (*ListClustersOutput, error) { + if params == nil { + params = &ListClustersInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "ListClusters", params, optFns, c.addOperationListClustersMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*ListClustersOutput) + out.ResultMetadata = metadata + return out, nil +} + +type ListClustersInput struct { + + // Specify a prefix of the name of the clusters that you want to list. The service + // lists all the clusters whose names start with this prefix. + ClusterNameFilter *string + + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + MaxResults *int32 + + // The paginated results marker. When the result of the operation is truncated, + // the call returns NextToken in the response. To get the next batch, provide this + // token in your next request. + NextToken *string + + noSmithyDocumentSerde +} + +type ListClustersOutput struct { + + // Information on each of the MSK clusters in the response. + ClusterInfoList []types.ClusterInfo + + // The paginated results marker. When the result of a ListClusters operation is + // truncated, the call returns NextToken in the response. To get another batch of + // clusters, provide this token in your next request. + NextToken *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationListClustersMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpListClusters{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpListClusters{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "ListClusters"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListClusters(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +// ListClustersPaginatorOptions is the paginator options for ListClusters +type ListClustersPaginatorOptions struct { + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + Limit int32 + + // Set to true if pagination should stop if the service returns a pagination token + // that matches the most recent token provided to the service. + StopOnDuplicateToken bool +} + +// ListClustersPaginator is a paginator for ListClusters +type ListClustersPaginator struct { + options ListClustersPaginatorOptions + client ListClustersAPIClient + params *ListClustersInput + nextToken *string + firstPage bool +} + +// NewListClustersPaginator returns a new ListClustersPaginator +func NewListClustersPaginator(client ListClustersAPIClient, params *ListClustersInput, optFns ...func(*ListClustersPaginatorOptions)) *ListClustersPaginator { + if params == nil { + params = &ListClustersInput{} + } + + options := ListClustersPaginatorOptions{} + if params.MaxResults != nil { + options.Limit = *params.MaxResults + } + + for _, fn := range optFns { + fn(&options) + } + + return &ListClustersPaginator{ + options: options, + client: client, + params: params, + firstPage: true, + nextToken: params.NextToken, + } +} + +// HasMorePages returns a boolean indicating whether more pages are available +func (p *ListClustersPaginator) HasMorePages() bool { + return p.firstPage || (p.nextToken != nil && len(*p.nextToken) != 0) +} + +// NextPage retrieves the next ListClusters page. +func (p *ListClustersPaginator) NextPage(ctx context.Context, optFns ...func(*Options)) (*ListClustersOutput, error) { + if !p.HasMorePages() { + return nil, fmt.Errorf("no more pages available") + } + + params := *p.params + params.NextToken = p.nextToken + + var limit *int32 + if p.options.Limit > 0 { + limit = &p.options.Limit + } + params.MaxResults = limit + + optFns = append([]func(*Options){ + addIsPaginatorUserAgent, + }, optFns...) + result, err := p.client.ListClusters(ctx, ¶ms, optFns...) + if err != nil { + return nil, err + } + p.firstPage = false + + prevToken := p.nextToken + p.nextToken = result.NextToken + + if p.options.StopOnDuplicateToken && + prevToken != nil && + p.nextToken != nil && + *prevToken == *p.nextToken { + p.nextToken = nil + } + + return result, nil +} + +// ListClustersAPIClient is a client that implements the ListClusters operation. +type ListClustersAPIClient interface { + ListClusters(context.Context, *ListClustersInput, ...func(*Options)) (*ListClustersOutput, error) +} + +var _ ListClustersAPIClient = (*Client)(nil) + +func newServiceMetadataMiddleware_opListClusters(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "ListClusters", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListClustersV2.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListClustersV2.go new file mode 100644 index 000000000..b89aa8d7c --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListClustersV2.go @@ -0,0 +1,264 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Returns a list of all the MSK clusters in the current Region. +func (c *Client) ListClustersV2(ctx context.Context, params *ListClustersV2Input, optFns ...func(*Options)) (*ListClustersV2Output, error) { + if params == nil { + params = &ListClustersV2Input{} + } + + result, metadata, err := c.invokeOperation(ctx, "ListClustersV2", params, optFns, c.addOperationListClustersV2Middlewares) + if err != nil { + return nil, err + } + + out := result.(*ListClustersV2Output) + out.ResultMetadata = metadata + return out, nil +} + +type ListClustersV2Input struct { + + // Specify a prefix of the names of the clusters that you want to list. The + // service lists all the clusters whose names start with this prefix. + ClusterNameFilter *string + + // Specify either PROVISIONED or SERVERLESS. + ClusterTypeFilter *string + + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + MaxResults *int32 + + // The paginated results marker. When the result of the operation is truncated, + // the call returns NextToken in the response. To get the next batch, provide this + // token in your next request. + NextToken *string + + noSmithyDocumentSerde +} + +type ListClustersV2Output struct { + + // Information on each of the MSK clusters in the response. + ClusterInfoList []types.Cluster + + // The paginated results marker. When the result of a ListClusters operation is + // truncated, the call returns NextToken in the response. To get another batch of + // clusters, provide this token in your next request. + NextToken *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationListClustersV2Middlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpListClustersV2{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpListClustersV2{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "ListClustersV2"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListClustersV2(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +// ListClustersV2PaginatorOptions is the paginator options for ListClustersV2 +type ListClustersV2PaginatorOptions struct { + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + Limit int32 + + // Set to true if pagination should stop if the service returns a pagination token + // that matches the most recent token provided to the service. + StopOnDuplicateToken bool +} + +// ListClustersV2Paginator is a paginator for ListClustersV2 +type ListClustersV2Paginator struct { + options ListClustersV2PaginatorOptions + client ListClustersV2APIClient + params *ListClustersV2Input + nextToken *string + firstPage bool +} + +// NewListClustersV2Paginator returns a new ListClustersV2Paginator +func NewListClustersV2Paginator(client ListClustersV2APIClient, params *ListClustersV2Input, optFns ...func(*ListClustersV2PaginatorOptions)) *ListClustersV2Paginator { + if params == nil { + params = &ListClustersV2Input{} + } + + options := ListClustersV2PaginatorOptions{} + if params.MaxResults != nil { + options.Limit = *params.MaxResults + } + + for _, fn := range optFns { + fn(&options) + } + + return &ListClustersV2Paginator{ + options: options, + client: client, + params: params, + firstPage: true, + nextToken: params.NextToken, + } +} + +// HasMorePages returns a boolean indicating whether more pages are available +func (p *ListClustersV2Paginator) HasMorePages() bool { + return p.firstPage || (p.nextToken != nil && len(*p.nextToken) != 0) +} + +// NextPage retrieves the next ListClustersV2 page. +func (p *ListClustersV2Paginator) NextPage(ctx context.Context, optFns ...func(*Options)) (*ListClustersV2Output, error) { + if !p.HasMorePages() { + return nil, fmt.Errorf("no more pages available") + } + + params := *p.params + params.NextToken = p.nextToken + + var limit *int32 + if p.options.Limit > 0 { + limit = &p.options.Limit + } + params.MaxResults = limit + + optFns = append([]func(*Options){ + addIsPaginatorUserAgent, + }, optFns...) + result, err := p.client.ListClustersV2(ctx, ¶ms, optFns...) + if err != nil { + return nil, err + } + p.firstPage = false + + prevToken := p.nextToken + p.nextToken = result.NextToken + + if p.options.StopOnDuplicateToken && + prevToken != nil && + p.nextToken != nil && + *prevToken == *p.nextToken { + p.nextToken = nil + } + + return result, nil +} + +// ListClustersV2APIClient is a client that implements the ListClustersV2 +// operation. +type ListClustersV2APIClient interface { + ListClustersV2(context.Context, *ListClustersV2Input, ...func(*Options)) (*ListClustersV2Output, error) +} + +var _ ListClustersV2APIClient = (*Client)(nil) + +func newServiceMetadataMiddleware_opListClustersV2(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "ListClustersV2", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListConfigurationRevisions.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListConfigurationRevisions.go new file mode 100644 index 000000000..af053ba83 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListConfigurationRevisions.go @@ -0,0 +1,267 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Returns a list of all the MSK configurations in this Region. +func (c *Client) ListConfigurationRevisions(ctx context.Context, params *ListConfigurationRevisionsInput, optFns ...func(*Options)) (*ListConfigurationRevisionsOutput, error) { + if params == nil { + params = &ListConfigurationRevisionsInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "ListConfigurationRevisions", params, optFns, c.addOperationListConfigurationRevisionsMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*ListConfigurationRevisionsOutput) + out.ResultMetadata = metadata + return out, nil +} + +type ListConfigurationRevisionsInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies an MSK configuration + // and all of its revisions. + // + // This member is required. + Arn *string + + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + MaxResults *int32 + + // The paginated results marker. When the result of the operation is truncated, + // the call returns NextToken in the response. To get the next batch, provide this + // token in your next request. + NextToken *string + + noSmithyDocumentSerde +} + +type ListConfigurationRevisionsOutput struct { + + // Paginated results marker. + NextToken *string + + // List of ConfigurationRevision objects. + Revisions []types.ConfigurationRevision + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationListConfigurationRevisionsMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpListConfigurationRevisions{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpListConfigurationRevisions{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "ListConfigurationRevisions"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpListConfigurationRevisionsValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListConfigurationRevisions(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +// ListConfigurationRevisionsPaginatorOptions is the paginator options for +// ListConfigurationRevisions +type ListConfigurationRevisionsPaginatorOptions struct { + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + Limit int32 + + // Set to true if pagination should stop if the service returns a pagination token + // that matches the most recent token provided to the service. + StopOnDuplicateToken bool +} + +// ListConfigurationRevisionsPaginator is a paginator for +// ListConfigurationRevisions +type ListConfigurationRevisionsPaginator struct { + options ListConfigurationRevisionsPaginatorOptions + client ListConfigurationRevisionsAPIClient + params *ListConfigurationRevisionsInput + nextToken *string + firstPage bool +} + +// NewListConfigurationRevisionsPaginator returns a new +// ListConfigurationRevisionsPaginator +func NewListConfigurationRevisionsPaginator(client ListConfigurationRevisionsAPIClient, params *ListConfigurationRevisionsInput, optFns ...func(*ListConfigurationRevisionsPaginatorOptions)) *ListConfigurationRevisionsPaginator { + if params == nil { + params = &ListConfigurationRevisionsInput{} + } + + options := ListConfigurationRevisionsPaginatorOptions{} + if params.MaxResults != nil { + options.Limit = *params.MaxResults + } + + for _, fn := range optFns { + fn(&options) + } + + return &ListConfigurationRevisionsPaginator{ + options: options, + client: client, + params: params, + firstPage: true, + nextToken: params.NextToken, + } +} + +// HasMorePages returns a boolean indicating whether more pages are available +func (p *ListConfigurationRevisionsPaginator) HasMorePages() bool { + return p.firstPage || (p.nextToken != nil && len(*p.nextToken) != 0) +} + +// NextPage retrieves the next ListConfigurationRevisions page. +func (p *ListConfigurationRevisionsPaginator) NextPage(ctx context.Context, optFns ...func(*Options)) (*ListConfigurationRevisionsOutput, error) { + if !p.HasMorePages() { + return nil, fmt.Errorf("no more pages available") + } + + params := *p.params + params.NextToken = p.nextToken + + var limit *int32 + if p.options.Limit > 0 { + limit = &p.options.Limit + } + params.MaxResults = limit + + optFns = append([]func(*Options){ + addIsPaginatorUserAgent, + }, optFns...) + result, err := p.client.ListConfigurationRevisions(ctx, ¶ms, optFns...) + if err != nil { + return nil, err + } + p.firstPage = false + + prevToken := p.nextToken + p.nextToken = result.NextToken + + if p.options.StopOnDuplicateToken && + prevToken != nil && + p.nextToken != nil && + *prevToken == *p.nextToken { + p.nextToken = nil + } + + return result, nil +} + +// ListConfigurationRevisionsAPIClient is a client that implements the +// ListConfigurationRevisions operation. +type ListConfigurationRevisionsAPIClient interface { + ListConfigurationRevisions(context.Context, *ListConfigurationRevisionsInput, ...func(*Options)) (*ListConfigurationRevisionsOutput, error) +} + +var _ ListConfigurationRevisionsAPIClient = (*Client)(nil) + +func newServiceMetadataMiddleware_opListConfigurationRevisions(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "ListConfigurationRevisions", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListConfigurations.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListConfigurations.go new file mode 100644 index 000000000..ab3be6a7c --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListConfigurations.go @@ -0,0 +1,258 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Returns a list of all the MSK configurations in this Region. +func (c *Client) ListConfigurations(ctx context.Context, params *ListConfigurationsInput, optFns ...func(*Options)) (*ListConfigurationsOutput, error) { + if params == nil { + params = &ListConfigurationsInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "ListConfigurations", params, optFns, c.addOperationListConfigurationsMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*ListConfigurationsOutput) + out.ResultMetadata = metadata + return out, nil +} + +type ListConfigurationsInput struct { + + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + MaxResults *int32 + + // The paginated results marker. When the result of the operation is truncated, + // the call returns NextToken in the response. To get the next batch, provide this + // token in your next request. + NextToken *string + + noSmithyDocumentSerde +} + +type ListConfigurationsOutput struct { + + // An array of MSK configurations. + Configurations []types.Configuration + + // The paginated results marker. When the result of a ListConfigurations operation + // is truncated, the call returns NextToken in the response. To get another batch + // of configurations, provide this token in your next request. + NextToken *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationListConfigurationsMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpListConfigurations{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpListConfigurations{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "ListConfigurations"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListConfigurations(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +// ListConfigurationsPaginatorOptions is the paginator options for +// ListConfigurations +type ListConfigurationsPaginatorOptions struct { + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + Limit int32 + + // Set to true if pagination should stop if the service returns a pagination token + // that matches the most recent token provided to the service. + StopOnDuplicateToken bool +} + +// ListConfigurationsPaginator is a paginator for ListConfigurations +type ListConfigurationsPaginator struct { + options ListConfigurationsPaginatorOptions + client ListConfigurationsAPIClient + params *ListConfigurationsInput + nextToken *string + firstPage bool +} + +// NewListConfigurationsPaginator returns a new ListConfigurationsPaginator +func NewListConfigurationsPaginator(client ListConfigurationsAPIClient, params *ListConfigurationsInput, optFns ...func(*ListConfigurationsPaginatorOptions)) *ListConfigurationsPaginator { + if params == nil { + params = &ListConfigurationsInput{} + } + + options := ListConfigurationsPaginatorOptions{} + if params.MaxResults != nil { + options.Limit = *params.MaxResults + } + + for _, fn := range optFns { + fn(&options) + } + + return &ListConfigurationsPaginator{ + options: options, + client: client, + params: params, + firstPage: true, + nextToken: params.NextToken, + } +} + +// HasMorePages returns a boolean indicating whether more pages are available +func (p *ListConfigurationsPaginator) HasMorePages() bool { + return p.firstPage || (p.nextToken != nil && len(*p.nextToken) != 0) +} + +// NextPage retrieves the next ListConfigurations page. +func (p *ListConfigurationsPaginator) NextPage(ctx context.Context, optFns ...func(*Options)) (*ListConfigurationsOutput, error) { + if !p.HasMorePages() { + return nil, fmt.Errorf("no more pages available") + } + + params := *p.params + params.NextToken = p.nextToken + + var limit *int32 + if p.options.Limit > 0 { + limit = &p.options.Limit + } + params.MaxResults = limit + + optFns = append([]func(*Options){ + addIsPaginatorUserAgent, + }, optFns...) + result, err := p.client.ListConfigurations(ctx, ¶ms, optFns...) + if err != nil { + return nil, err + } + p.firstPage = false + + prevToken := p.nextToken + p.nextToken = result.NextToken + + if p.options.StopOnDuplicateToken && + prevToken != nil && + p.nextToken != nil && + *prevToken == *p.nextToken { + p.nextToken = nil + } + + return result, nil +} + +// ListConfigurationsAPIClient is a client that implements the ListConfigurations +// operation. +type ListConfigurationsAPIClient interface { + ListConfigurations(context.Context, *ListConfigurationsInput, ...func(*Options)) (*ListConfigurationsOutput, error) +} + +var _ ListConfigurationsAPIClient = (*Client)(nil) + +func newServiceMetadataMiddleware_opListConfigurations(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "ListConfigurations", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListKafkaVersions.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListKafkaVersions.go new file mode 100644 index 000000000..e9178268b --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListKafkaVersions.go @@ -0,0 +1,252 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Returns a list of Apache Kafka versions. +func (c *Client) ListKafkaVersions(ctx context.Context, params *ListKafkaVersionsInput, optFns ...func(*Options)) (*ListKafkaVersionsOutput, error) { + if params == nil { + params = &ListKafkaVersionsInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "ListKafkaVersions", params, optFns, c.addOperationListKafkaVersionsMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*ListKafkaVersionsOutput) + out.ResultMetadata = metadata + return out, nil +} + +type ListKafkaVersionsInput struct { + + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + MaxResults *int32 + + // The paginated results marker. When the result of the operation is truncated, + // the call returns NextToken in the response. To get the next batch, provide this + // token in your next request. + NextToken *string + + noSmithyDocumentSerde +} + +type ListKafkaVersionsOutput struct { + KafkaVersions []types.KafkaVersion + + NextToken *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationListKafkaVersionsMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpListKafkaVersions{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpListKafkaVersions{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "ListKafkaVersions"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListKafkaVersions(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +// ListKafkaVersionsPaginatorOptions is the paginator options for ListKafkaVersions +type ListKafkaVersionsPaginatorOptions struct { + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + Limit int32 + + // Set to true if pagination should stop if the service returns a pagination token + // that matches the most recent token provided to the service. + StopOnDuplicateToken bool +} + +// ListKafkaVersionsPaginator is a paginator for ListKafkaVersions +type ListKafkaVersionsPaginator struct { + options ListKafkaVersionsPaginatorOptions + client ListKafkaVersionsAPIClient + params *ListKafkaVersionsInput + nextToken *string + firstPage bool +} + +// NewListKafkaVersionsPaginator returns a new ListKafkaVersionsPaginator +func NewListKafkaVersionsPaginator(client ListKafkaVersionsAPIClient, params *ListKafkaVersionsInput, optFns ...func(*ListKafkaVersionsPaginatorOptions)) *ListKafkaVersionsPaginator { + if params == nil { + params = &ListKafkaVersionsInput{} + } + + options := ListKafkaVersionsPaginatorOptions{} + if params.MaxResults != nil { + options.Limit = *params.MaxResults + } + + for _, fn := range optFns { + fn(&options) + } + + return &ListKafkaVersionsPaginator{ + options: options, + client: client, + params: params, + firstPage: true, + nextToken: params.NextToken, + } +} + +// HasMorePages returns a boolean indicating whether more pages are available +func (p *ListKafkaVersionsPaginator) HasMorePages() bool { + return p.firstPage || (p.nextToken != nil && len(*p.nextToken) != 0) +} + +// NextPage retrieves the next ListKafkaVersions page. +func (p *ListKafkaVersionsPaginator) NextPage(ctx context.Context, optFns ...func(*Options)) (*ListKafkaVersionsOutput, error) { + if !p.HasMorePages() { + return nil, fmt.Errorf("no more pages available") + } + + params := *p.params + params.NextToken = p.nextToken + + var limit *int32 + if p.options.Limit > 0 { + limit = &p.options.Limit + } + params.MaxResults = limit + + optFns = append([]func(*Options){ + addIsPaginatorUserAgent, + }, optFns...) + result, err := p.client.ListKafkaVersions(ctx, ¶ms, optFns...) + if err != nil { + return nil, err + } + p.firstPage = false + + prevToken := p.nextToken + p.nextToken = result.NextToken + + if p.options.StopOnDuplicateToken && + prevToken != nil && + p.nextToken != nil && + *prevToken == *p.nextToken { + p.nextToken = nil + } + + return result, nil +} + +// ListKafkaVersionsAPIClient is a client that implements the ListKafkaVersions +// operation. +type ListKafkaVersionsAPIClient interface { + ListKafkaVersions(context.Context, *ListKafkaVersionsInput, ...func(*Options)) (*ListKafkaVersionsOutput, error) +} + +var _ ListKafkaVersionsAPIClient = (*Client)(nil) + +func newServiceMetadataMiddleware_opListKafkaVersions(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "ListKafkaVersions", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListNodes.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListNodes.go new file mode 100644 index 000000000..6c7ca6354 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListNodes.go @@ -0,0 +1,264 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Returns a list of the broker nodes in the cluster. +func (c *Client) ListNodes(ctx context.Context, params *ListNodesInput, optFns ...func(*Options)) (*ListNodesOutput, error) { + if params == nil { + params = &ListNodesInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "ListNodes", params, optFns, c.addOperationListNodesMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*ListNodesOutput) + out.ResultMetadata = metadata + return out, nil +} + +type ListNodesInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies the cluster. + // + // This member is required. + ClusterArn *string + + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + MaxResults *int32 + + // The paginated results marker. When the result of the operation is truncated, + // the call returns NextToken in the response. To get the next batch, provide this + // token in your next request. + NextToken *string + + noSmithyDocumentSerde +} + +type ListNodesOutput struct { + + // The paginated results marker. When the result of a ListNodes operation is + // truncated, the call returns NextToken in the response. To get another batch of + // nodes, provide this token in your next request. + NextToken *string + + // List containing a NodeInfo object. + NodeInfoList []types.NodeInfo + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationListNodesMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpListNodes{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpListNodes{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "ListNodes"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpListNodesValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListNodes(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +// ListNodesPaginatorOptions is the paginator options for ListNodes +type ListNodesPaginatorOptions struct { + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + Limit int32 + + // Set to true if pagination should stop if the service returns a pagination token + // that matches the most recent token provided to the service. + StopOnDuplicateToken bool +} + +// ListNodesPaginator is a paginator for ListNodes +type ListNodesPaginator struct { + options ListNodesPaginatorOptions + client ListNodesAPIClient + params *ListNodesInput + nextToken *string + firstPage bool +} + +// NewListNodesPaginator returns a new ListNodesPaginator +func NewListNodesPaginator(client ListNodesAPIClient, params *ListNodesInput, optFns ...func(*ListNodesPaginatorOptions)) *ListNodesPaginator { + if params == nil { + params = &ListNodesInput{} + } + + options := ListNodesPaginatorOptions{} + if params.MaxResults != nil { + options.Limit = *params.MaxResults + } + + for _, fn := range optFns { + fn(&options) + } + + return &ListNodesPaginator{ + options: options, + client: client, + params: params, + firstPage: true, + nextToken: params.NextToken, + } +} + +// HasMorePages returns a boolean indicating whether more pages are available +func (p *ListNodesPaginator) HasMorePages() bool { + return p.firstPage || (p.nextToken != nil && len(*p.nextToken) != 0) +} + +// NextPage retrieves the next ListNodes page. +func (p *ListNodesPaginator) NextPage(ctx context.Context, optFns ...func(*Options)) (*ListNodesOutput, error) { + if !p.HasMorePages() { + return nil, fmt.Errorf("no more pages available") + } + + params := *p.params + params.NextToken = p.nextToken + + var limit *int32 + if p.options.Limit > 0 { + limit = &p.options.Limit + } + params.MaxResults = limit + + optFns = append([]func(*Options){ + addIsPaginatorUserAgent, + }, optFns...) + result, err := p.client.ListNodes(ctx, ¶ms, optFns...) + if err != nil { + return nil, err + } + p.firstPage = false + + prevToken := p.nextToken + p.nextToken = result.NextToken + + if p.options.StopOnDuplicateToken && + prevToken != nil && + p.nextToken != nil && + *prevToken == *p.nextToken { + p.nextToken = nil + } + + return result, nil +} + +// ListNodesAPIClient is a client that implements the ListNodes operation. +type ListNodesAPIClient interface { + ListNodes(context.Context, *ListNodesInput, ...func(*Options)) (*ListNodesOutput, error) +} + +var _ ListNodesAPIClient = (*Client)(nil) + +func newServiceMetadataMiddleware_opListNodes(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "ListNodes", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListReplicators.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListReplicators.go new file mode 100644 index 000000000..140e20949 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListReplicators.go @@ -0,0 +1,260 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Lists the replicators. +func (c *Client) ListReplicators(ctx context.Context, params *ListReplicatorsInput, optFns ...func(*Options)) (*ListReplicatorsOutput, error) { + if params == nil { + params = &ListReplicatorsInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "ListReplicators", params, optFns, c.addOperationListReplicatorsMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*ListReplicatorsOutput) + out.ResultMetadata = metadata + return out, nil +} + +type ListReplicatorsInput struct { + + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + MaxResults *int32 + + // If the response of ListReplicators is truncated, it returns a NextToken in the + // response. This NextToken should be sent in the subsequent request to + // ListReplicators. + NextToken *string + + // Returns replicators starting with given name. + ReplicatorNameFilter *string + + noSmithyDocumentSerde +} + +type ListReplicatorsOutput struct { + + // If the response of ListReplicators is truncated, it returns a NextToken in the + // response. This NextToken should be sent in the subsequent request to + // ListReplicators. + NextToken *string + + // List containing information of each of the replicators in the account. + Replicators []types.ReplicatorSummary + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationListReplicatorsMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpListReplicators{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpListReplicators{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "ListReplicators"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListReplicators(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +// ListReplicatorsPaginatorOptions is the paginator options for ListReplicators +type ListReplicatorsPaginatorOptions struct { + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + Limit int32 + + // Set to true if pagination should stop if the service returns a pagination token + // that matches the most recent token provided to the service. + StopOnDuplicateToken bool +} + +// ListReplicatorsPaginator is a paginator for ListReplicators +type ListReplicatorsPaginator struct { + options ListReplicatorsPaginatorOptions + client ListReplicatorsAPIClient + params *ListReplicatorsInput + nextToken *string + firstPage bool +} + +// NewListReplicatorsPaginator returns a new ListReplicatorsPaginator +func NewListReplicatorsPaginator(client ListReplicatorsAPIClient, params *ListReplicatorsInput, optFns ...func(*ListReplicatorsPaginatorOptions)) *ListReplicatorsPaginator { + if params == nil { + params = &ListReplicatorsInput{} + } + + options := ListReplicatorsPaginatorOptions{} + if params.MaxResults != nil { + options.Limit = *params.MaxResults + } + + for _, fn := range optFns { + fn(&options) + } + + return &ListReplicatorsPaginator{ + options: options, + client: client, + params: params, + firstPage: true, + nextToken: params.NextToken, + } +} + +// HasMorePages returns a boolean indicating whether more pages are available +func (p *ListReplicatorsPaginator) HasMorePages() bool { + return p.firstPage || (p.nextToken != nil && len(*p.nextToken) != 0) +} + +// NextPage retrieves the next ListReplicators page. +func (p *ListReplicatorsPaginator) NextPage(ctx context.Context, optFns ...func(*Options)) (*ListReplicatorsOutput, error) { + if !p.HasMorePages() { + return nil, fmt.Errorf("no more pages available") + } + + params := *p.params + params.NextToken = p.nextToken + + var limit *int32 + if p.options.Limit > 0 { + limit = &p.options.Limit + } + params.MaxResults = limit + + optFns = append([]func(*Options){ + addIsPaginatorUserAgent, + }, optFns...) + result, err := p.client.ListReplicators(ctx, ¶ms, optFns...) + if err != nil { + return nil, err + } + p.firstPage = false + + prevToken := p.nextToken + p.nextToken = result.NextToken + + if p.options.StopOnDuplicateToken && + prevToken != nil && + p.nextToken != nil && + *prevToken == *p.nextToken { + p.nextToken = nil + } + + return result, nil +} + +// ListReplicatorsAPIClient is a client that implements the ListReplicators +// operation. +type ListReplicatorsAPIClient interface { + ListReplicators(context.Context, *ListReplicatorsInput, ...func(*Options)) (*ListReplicatorsOutput, error) +} + +var _ ListReplicatorsAPIClient = (*Client)(nil) + +func newServiceMetadataMiddleware_opListReplicators(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "ListReplicators", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListScramSecrets.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListScramSecrets.go new file mode 100644 index 000000000..44141d503 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListScramSecrets.go @@ -0,0 +1,258 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Returns a list of the Scram Secrets associated with an Amazon MSK cluster. +func (c *Client) ListScramSecrets(ctx context.Context, params *ListScramSecretsInput, optFns ...func(*Options)) (*ListScramSecretsOutput, error) { + if params == nil { + params = &ListScramSecretsInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "ListScramSecrets", params, optFns, c.addOperationListScramSecretsMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*ListScramSecretsOutput) + out.ResultMetadata = metadata + return out, nil +} + +type ListScramSecretsInput struct { + + // The arn of the cluster. + // + // This member is required. + ClusterArn *string + + // The maxResults of the query. + MaxResults *int32 + + // The nextToken of the query. + NextToken *string + + noSmithyDocumentSerde +} + +type ListScramSecretsOutput struct { + + // Paginated results marker. + NextToken *string + + // The list of scram secrets associated with the cluster. + SecretArnList []string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationListScramSecretsMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpListScramSecrets{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpListScramSecrets{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "ListScramSecrets"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpListScramSecretsValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListScramSecrets(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +// ListScramSecretsPaginatorOptions is the paginator options for ListScramSecrets +type ListScramSecretsPaginatorOptions struct { + // The maxResults of the query. + Limit int32 + + // Set to true if pagination should stop if the service returns a pagination token + // that matches the most recent token provided to the service. + StopOnDuplicateToken bool +} + +// ListScramSecretsPaginator is a paginator for ListScramSecrets +type ListScramSecretsPaginator struct { + options ListScramSecretsPaginatorOptions + client ListScramSecretsAPIClient + params *ListScramSecretsInput + nextToken *string + firstPage bool +} + +// NewListScramSecretsPaginator returns a new ListScramSecretsPaginator +func NewListScramSecretsPaginator(client ListScramSecretsAPIClient, params *ListScramSecretsInput, optFns ...func(*ListScramSecretsPaginatorOptions)) *ListScramSecretsPaginator { + if params == nil { + params = &ListScramSecretsInput{} + } + + options := ListScramSecretsPaginatorOptions{} + if params.MaxResults != nil { + options.Limit = *params.MaxResults + } + + for _, fn := range optFns { + fn(&options) + } + + return &ListScramSecretsPaginator{ + options: options, + client: client, + params: params, + firstPage: true, + nextToken: params.NextToken, + } +} + +// HasMorePages returns a boolean indicating whether more pages are available +func (p *ListScramSecretsPaginator) HasMorePages() bool { + return p.firstPage || (p.nextToken != nil && len(*p.nextToken) != 0) +} + +// NextPage retrieves the next ListScramSecrets page. +func (p *ListScramSecretsPaginator) NextPage(ctx context.Context, optFns ...func(*Options)) (*ListScramSecretsOutput, error) { + if !p.HasMorePages() { + return nil, fmt.Errorf("no more pages available") + } + + params := *p.params + params.NextToken = p.nextToken + + var limit *int32 + if p.options.Limit > 0 { + limit = &p.options.Limit + } + params.MaxResults = limit + + optFns = append([]func(*Options){ + addIsPaginatorUserAgent, + }, optFns...) + result, err := p.client.ListScramSecrets(ctx, ¶ms, optFns...) + if err != nil { + return nil, err + } + p.firstPage = false + + prevToken := p.nextToken + p.nextToken = result.NextToken + + if p.options.StopOnDuplicateToken && + prevToken != nil && + p.nextToken != nil && + *prevToken == *p.nextToken { + p.nextToken = nil + } + + return result, nil +} + +// ListScramSecretsAPIClient is a client that implements the ListScramSecrets +// operation. +type ListScramSecretsAPIClient interface { + ListScramSecrets(context.Context, *ListScramSecretsInput, ...func(*Options)) (*ListScramSecretsOutput, error) +} + +var _ ListScramSecretsAPIClient = (*Client)(nil) + +func newServiceMetadataMiddleware_opListScramSecrets(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "ListScramSecrets", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListTagsForResource.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListTagsForResource.go new file mode 100644 index 000000000..399434cc6 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListTagsForResource.go @@ -0,0 +1,157 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Returns a list of the tags associated with the specified resource. +func (c *Client) ListTagsForResource(ctx context.Context, params *ListTagsForResourceInput, optFns ...func(*Options)) (*ListTagsForResourceOutput, error) { + if params == nil { + params = &ListTagsForResourceInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "ListTagsForResource", params, optFns, c.addOperationListTagsForResourceMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*ListTagsForResourceOutput) + out.ResultMetadata = metadata + return out, nil +} + +type ListTagsForResourceInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies the resource that's + // associated with the tags. + // + // This member is required. + ResourceArn *string + + noSmithyDocumentSerde +} + +type ListTagsForResourceOutput struct { + + // The key-value pair for the resource tag. + Tags map[string]string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationListTagsForResourceMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpListTagsForResource{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpListTagsForResource{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "ListTagsForResource"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpListTagsForResourceValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListTagsForResource(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opListTagsForResource(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "ListTagsForResource", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListTopics.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListTopics.go new file mode 100644 index 000000000..d46a09ac5 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListTopics.go @@ -0,0 +1,267 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// List topics in a MSK cluster. +func (c *Client) ListTopics(ctx context.Context, params *ListTopicsInput, optFns ...func(*Options)) (*ListTopicsOutput, error) { + if params == nil { + params = &ListTopicsInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "ListTopics", params, optFns, c.addOperationListTopicsMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*ListTopicsOutput) + out.ResultMetadata = metadata + return out, nil +} + +type ListTopicsInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies the cluster. + // + // This member is required. + ClusterArn *string + + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + MaxResults *int32 + + // The paginated results marker. When the result of the operation is truncated, + // the call returns NextToken in the response. To get the next batch, provide this + // token in your next request. + NextToken *string + + // Returns topics starting with given name. + TopicNameFilter *string + + noSmithyDocumentSerde +} + +type ListTopicsOutput struct { + + // The paginated results marker. When the result of a ListTopics operation is + // truncated, the call returns NextToken in the response. To get another batch of + // configurations, provide this token in your next request. + NextToken *string + + // List containing topics info. + Topics []types.TopicInfo + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationListTopicsMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpListTopics{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpListTopics{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "ListTopics"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpListTopicsValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListTopics(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +// ListTopicsPaginatorOptions is the paginator options for ListTopics +type ListTopicsPaginatorOptions struct { + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + Limit int32 + + // Set to true if pagination should stop if the service returns a pagination token + // that matches the most recent token provided to the service. + StopOnDuplicateToken bool +} + +// ListTopicsPaginator is a paginator for ListTopics +type ListTopicsPaginator struct { + options ListTopicsPaginatorOptions + client ListTopicsAPIClient + params *ListTopicsInput + nextToken *string + firstPage bool +} + +// NewListTopicsPaginator returns a new ListTopicsPaginator +func NewListTopicsPaginator(client ListTopicsAPIClient, params *ListTopicsInput, optFns ...func(*ListTopicsPaginatorOptions)) *ListTopicsPaginator { + if params == nil { + params = &ListTopicsInput{} + } + + options := ListTopicsPaginatorOptions{} + if params.MaxResults != nil { + options.Limit = *params.MaxResults + } + + for _, fn := range optFns { + fn(&options) + } + + return &ListTopicsPaginator{ + options: options, + client: client, + params: params, + firstPage: true, + nextToken: params.NextToken, + } +} + +// HasMorePages returns a boolean indicating whether more pages are available +func (p *ListTopicsPaginator) HasMorePages() bool { + return p.firstPage || (p.nextToken != nil && len(*p.nextToken) != 0) +} + +// NextPage retrieves the next ListTopics page. +func (p *ListTopicsPaginator) NextPage(ctx context.Context, optFns ...func(*Options)) (*ListTopicsOutput, error) { + if !p.HasMorePages() { + return nil, fmt.Errorf("no more pages available") + } + + params := *p.params + params.NextToken = p.nextToken + + var limit *int32 + if p.options.Limit > 0 { + limit = &p.options.Limit + } + params.MaxResults = limit + + optFns = append([]func(*Options){ + addIsPaginatorUserAgent, + }, optFns...) + result, err := p.client.ListTopics(ctx, ¶ms, optFns...) + if err != nil { + return nil, err + } + p.firstPage = false + + prevToken := p.nextToken + p.nextToken = result.NextToken + + if p.options.StopOnDuplicateToken && + prevToken != nil && + p.nextToken != nil && + *prevToken == *p.nextToken { + p.nextToken = nil + } + + return result, nil +} + +// ListTopicsAPIClient is a client that implements the ListTopics operation. +type ListTopicsAPIClient interface { + ListTopics(context.Context, *ListTopicsInput, ...func(*Options)) (*ListTopicsOutput, error) +} + +var _ ListTopicsAPIClient = (*Client)(nil) + +func newServiceMetadataMiddleware_opListTopics(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "ListTopics", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListVpcConnections.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListVpcConnections.go new file mode 100644 index 000000000..b6d7a63f2 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_ListVpcConnections.go @@ -0,0 +1,258 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Returns a list of all the VPC connections in this Region. +func (c *Client) ListVpcConnections(ctx context.Context, params *ListVpcConnectionsInput, optFns ...func(*Options)) (*ListVpcConnectionsOutput, error) { + if params == nil { + params = &ListVpcConnectionsInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "ListVpcConnections", params, optFns, c.addOperationListVpcConnectionsMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*ListVpcConnectionsOutput) + out.ResultMetadata = metadata + return out, nil +} + +type ListVpcConnectionsInput struct { + + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + MaxResults *int32 + + // The paginated results marker. When the result of the operation is truncated, + // the call returns NextToken in the response. To get the next batch, provide this + // token in your next request. + NextToken *string + + noSmithyDocumentSerde +} + +type ListVpcConnectionsOutput struct { + + // The paginated results marker. When the result of a ListClientVpcConnections + // operation is truncated, the call returns NextToken in the response. To get + // another batch of configurations, provide this token in your next request. + NextToken *string + + // List of VPC connections. + VpcConnections []types.VpcConnection + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationListVpcConnectionsMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpListVpcConnections{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpListVpcConnections{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "ListVpcConnections"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListVpcConnections(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +// ListVpcConnectionsPaginatorOptions is the paginator options for +// ListVpcConnections +type ListVpcConnectionsPaginatorOptions struct { + // The maximum number of results to return in the response. If there are more + // results, the response includes a NextToken parameter. + Limit int32 + + // Set to true if pagination should stop if the service returns a pagination token + // that matches the most recent token provided to the service. + StopOnDuplicateToken bool +} + +// ListVpcConnectionsPaginator is a paginator for ListVpcConnections +type ListVpcConnectionsPaginator struct { + options ListVpcConnectionsPaginatorOptions + client ListVpcConnectionsAPIClient + params *ListVpcConnectionsInput + nextToken *string + firstPage bool +} + +// NewListVpcConnectionsPaginator returns a new ListVpcConnectionsPaginator +func NewListVpcConnectionsPaginator(client ListVpcConnectionsAPIClient, params *ListVpcConnectionsInput, optFns ...func(*ListVpcConnectionsPaginatorOptions)) *ListVpcConnectionsPaginator { + if params == nil { + params = &ListVpcConnectionsInput{} + } + + options := ListVpcConnectionsPaginatorOptions{} + if params.MaxResults != nil { + options.Limit = *params.MaxResults + } + + for _, fn := range optFns { + fn(&options) + } + + return &ListVpcConnectionsPaginator{ + options: options, + client: client, + params: params, + firstPage: true, + nextToken: params.NextToken, + } +} + +// HasMorePages returns a boolean indicating whether more pages are available +func (p *ListVpcConnectionsPaginator) HasMorePages() bool { + return p.firstPage || (p.nextToken != nil && len(*p.nextToken) != 0) +} + +// NextPage retrieves the next ListVpcConnections page. +func (p *ListVpcConnectionsPaginator) NextPage(ctx context.Context, optFns ...func(*Options)) (*ListVpcConnectionsOutput, error) { + if !p.HasMorePages() { + return nil, fmt.Errorf("no more pages available") + } + + params := *p.params + params.NextToken = p.nextToken + + var limit *int32 + if p.options.Limit > 0 { + limit = &p.options.Limit + } + params.MaxResults = limit + + optFns = append([]func(*Options){ + addIsPaginatorUserAgent, + }, optFns...) + result, err := p.client.ListVpcConnections(ctx, ¶ms, optFns...) + if err != nil { + return nil, err + } + p.firstPage = false + + prevToken := p.nextToken + p.nextToken = result.NextToken + + if p.options.StopOnDuplicateToken && + prevToken != nil && + p.nextToken != nil && + *prevToken == *p.nextToken { + p.nextToken = nil + } + + return result, nil +} + +// ListVpcConnectionsAPIClient is a client that implements the ListVpcConnections +// operation. +type ListVpcConnectionsAPIClient interface { + ListVpcConnections(context.Context, *ListVpcConnectionsInput, ...func(*Options)) (*ListVpcConnectionsOutput, error) +} + +var _ ListVpcConnectionsAPIClient = (*Client)(nil) + +func newServiceMetadataMiddleware_opListVpcConnections(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "ListVpcConnections", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_PutClusterPolicy.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_PutClusterPolicy.go new file mode 100644 index 000000000..40765792d --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_PutClusterPolicy.go @@ -0,0 +1,165 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Creates or updates the MSK cluster policy specified by the cluster Amazon +// Resource Name (ARN) in the request. +func (c *Client) PutClusterPolicy(ctx context.Context, params *PutClusterPolicyInput, optFns ...func(*Options)) (*PutClusterPolicyOutput, error) { + if params == nil { + params = &PutClusterPolicyInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "PutClusterPolicy", params, optFns, c.addOperationPutClusterPolicyMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*PutClusterPolicyOutput) + out.ResultMetadata = metadata + return out, nil +} + +type PutClusterPolicyInput struct { + + // The Amazon Resource Name (ARN) of the cluster. + // + // This member is required. + ClusterArn *string + + // The policy. + // + // This member is required. + Policy *string + + // The policy version. + CurrentVersion *string + + noSmithyDocumentSerde +} + +type PutClusterPolicyOutput struct { + + // The policy version. + CurrentVersion *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationPutClusterPolicyMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpPutClusterPolicy{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpPutClusterPolicy{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "PutClusterPolicy"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpPutClusterPolicyValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opPutClusterPolicy(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opPutClusterPolicy(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "PutClusterPolicy", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_RebootBroker.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_RebootBroker.go new file mode 100644 index 000000000..25dc1c9be --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_RebootBroker.go @@ -0,0 +1,166 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Reboots brokers. +func (c *Client) RebootBroker(ctx context.Context, params *RebootBrokerInput, optFns ...func(*Options)) (*RebootBrokerOutput, error) { + if params == nil { + params = &RebootBrokerInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "RebootBroker", params, optFns, c.addOperationRebootBrokerMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*RebootBrokerOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Reboots a node. +type RebootBrokerInput struct { + + // The list of broker IDs to be rebooted. The reboot-broker operation supports + // rebooting one broker at a time. + // + // This member is required. + BrokerIds []string + + // The Amazon Resource Name (ARN) of the cluster to be updated. + // + // This member is required. + ClusterArn *string + + noSmithyDocumentSerde +} + +type RebootBrokerOutput struct { + + // The Amazon Resource Name (ARN) of the cluster. + ClusterArn *string + + // The Amazon Resource Name (ARN) of the cluster operation. + ClusterOperationArn *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationRebootBrokerMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpRebootBroker{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpRebootBroker{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "RebootBroker"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpRebootBrokerValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opRebootBroker(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opRebootBroker(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "RebootBroker", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_RejectClientVpcConnection.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_RejectClientVpcConnection.go new file mode 100644 index 000000000..cb53934c0 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_RejectClientVpcConnection.go @@ -0,0 +1,157 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Returns empty response. +func (c *Client) RejectClientVpcConnection(ctx context.Context, params *RejectClientVpcConnectionInput, optFns ...func(*Options)) (*RejectClientVpcConnectionOutput, error) { + if params == nil { + params = &RejectClientVpcConnectionInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "RejectClientVpcConnection", params, optFns, c.addOperationRejectClientVpcConnectionMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*RejectClientVpcConnectionOutput) + out.ResultMetadata = metadata + return out, nil +} + +type RejectClientVpcConnectionInput struct { + + // The Amazon Resource Name (ARN) of the cluster. + // + // This member is required. + ClusterArn *string + + // The VPC connection ARN. + // + // This member is required. + VpcConnectionArn *string + + noSmithyDocumentSerde +} + +type RejectClientVpcConnectionOutput struct { + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationRejectClientVpcConnectionMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpRejectClientVpcConnection{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpRejectClientVpcConnection{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "RejectClientVpcConnection"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpRejectClientVpcConnectionValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opRejectClientVpcConnection(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opRejectClientVpcConnection(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "RejectClientVpcConnection", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_TagResource.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_TagResource.go new file mode 100644 index 000000000..af72045c5 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_TagResource.go @@ -0,0 +1,158 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Adds tags to the specified MSK resource. +func (c *Client) TagResource(ctx context.Context, params *TagResourceInput, optFns ...func(*Options)) (*TagResourceOutput, error) { + if params == nil { + params = &TagResourceInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "TagResource", params, optFns, c.addOperationTagResourceMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*TagResourceOutput) + out.ResultMetadata = metadata + return out, nil +} + +type TagResourceInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies the resource that's + // associated with the tags. + // + // This member is required. + ResourceArn *string + + // The key-value pair for the resource tag. + // + // This member is required. + Tags map[string]string + + noSmithyDocumentSerde +} + +type TagResourceOutput struct { + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationTagResourceMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpTagResource{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpTagResource{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "TagResource"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpTagResourceValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opTagResource(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opTagResource(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "TagResource", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UntagResource.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UntagResource.go new file mode 100644 index 000000000..9aec496db --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UntagResource.go @@ -0,0 +1,171 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Removes the tags associated with the keys that are provided in the query. +func (c *Client) UntagResource(ctx context.Context, params *UntagResourceInput, optFns ...func(*Options)) (*UntagResourceOutput, error) { + if params == nil { + params = &UntagResourceInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "UntagResource", params, optFns, c.addOperationUntagResourceMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*UntagResourceOutput) + out.ResultMetadata = metadata + return out, nil +} + +type UntagResourceInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies the resource that's + // associated with the tags. + // + // This member is required. + ResourceArn *string + + // Tag keys must be unique for a given cluster. In addition, the following + // restrictions apply: + // + // - Each tag key must be unique. If you add a tag with a key that's already in + // use, your new tag overwrites the existing key-value pair. + // + // - You can't start a tag key with aws: because this prefix is reserved for use + // by AWS. AWS creates tags that begin with this prefix on your behalf, but you + // can't edit or delete them. + // + // - Tag keys must be between 1 and 128 Unicode characters in length. + // + // - Tag keys must consist of the following characters: Unicode letters, digits, + // white space, and the following special characters: _ . / = + - @. + // + // This member is required. + TagKeys []string + + noSmithyDocumentSerde +} + +type UntagResourceOutput struct { + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationUntagResourceMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpUntagResource{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpUntagResource{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "UntagResource"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpUntagResourceValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opUntagResource(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opUntagResource(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "UntagResource", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateBrokerCount.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateBrokerCount.go new file mode 100644 index 000000000..af8b0d4f6 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateBrokerCount.go @@ -0,0 +1,171 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Updates the number of broker nodes in the cluster. +func (c *Client) UpdateBrokerCount(ctx context.Context, params *UpdateBrokerCountInput, optFns ...func(*Options)) (*UpdateBrokerCountOutput, error) { + if params == nil { + params = &UpdateBrokerCountInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "UpdateBrokerCount", params, optFns, c.addOperationUpdateBrokerCountMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*UpdateBrokerCountOutput) + out.ResultMetadata = metadata + return out, nil +} + +type UpdateBrokerCountInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies the cluster. + // + // This member is required. + ClusterArn *string + + // The version of cluster to update from. A successful operation will then + // generate a new version. + // + // This member is required. + CurrentVersion *string + + // The number of broker nodes that you want the cluster to have after this + // operation completes successfully. + // + // This member is required. + TargetNumberOfBrokerNodes *int32 + + noSmithyDocumentSerde +} + +type UpdateBrokerCountOutput struct { + + // The Amazon Resource Name (ARN) of the cluster. + ClusterArn *string + + // The Amazon Resource Name (ARN) of the cluster operation. + ClusterOperationArn *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationUpdateBrokerCountMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpUpdateBrokerCount{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpUpdateBrokerCount{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "UpdateBrokerCount"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpUpdateBrokerCountValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opUpdateBrokerCount(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opUpdateBrokerCount(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "UpdateBrokerCount", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateBrokerStorage.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateBrokerStorage.go new file mode 100644 index 000000000..cec30e3a5 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateBrokerStorage.go @@ -0,0 +1,172 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Updates the EBS storage associated with MSK brokers. +func (c *Client) UpdateBrokerStorage(ctx context.Context, params *UpdateBrokerStorageInput, optFns ...func(*Options)) (*UpdateBrokerStorageOutput, error) { + if params == nil { + params = &UpdateBrokerStorageInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "UpdateBrokerStorage", params, optFns, c.addOperationUpdateBrokerStorageMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*UpdateBrokerStorageOutput) + out.ResultMetadata = metadata + return out, nil +} + +type UpdateBrokerStorageInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies the cluster. + // + // This member is required. + ClusterArn *string + + // The version of cluster to update from. A successful operation will then + // generate a new version. + // + // This member is required. + CurrentVersion *string + + // Describes the target volume size and the ID of the broker to apply the update + // to. + // + // This member is required. + TargetBrokerEBSVolumeInfo []types.BrokerEBSVolumeInfo + + noSmithyDocumentSerde +} + +type UpdateBrokerStorageOutput struct { + + // The Amazon Resource Name (ARN) of the cluster. + ClusterArn *string + + // The Amazon Resource Name (ARN) of the cluster operation. + ClusterOperationArn *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationUpdateBrokerStorageMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpUpdateBrokerStorage{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpUpdateBrokerStorage{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "UpdateBrokerStorage"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpUpdateBrokerStorageValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opUpdateBrokerStorage(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opUpdateBrokerStorage(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "UpdateBrokerStorage", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateBrokerType.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateBrokerType.go new file mode 100644 index 000000000..a38178134 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateBrokerType.go @@ -0,0 +1,171 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Updates EC2 instance type. +func (c *Client) UpdateBrokerType(ctx context.Context, params *UpdateBrokerTypeInput, optFns ...func(*Options)) (*UpdateBrokerTypeOutput, error) { + if params == nil { + params = &UpdateBrokerTypeInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "UpdateBrokerType", params, optFns, c.addOperationUpdateBrokerTypeMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*UpdateBrokerTypeOutput) + out.ResultMetadata = metadata + return out, nil +} + +type UpdateBrokerTypeInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies the cluster. + // + // This member is required. + ClusterArn *string + + // The cluster version that you want to change. After this operation completes + // successfully, the cluster will have a new version. + // + // This member is required. + CurrentVersion *string + + // The Amazon MSK broker type that you want all of the brokers in this cluster to + // be. + // + // This member is required. + TargetInstanceType *string + + noSmithyDocumentSerde +} + +type UpdateBrokerTypeOutput struct { + + // The Amazon Resource Name (ARN) of the cluster. + ClusterArn *string + + // The Amazon Resource Name (ARN) of the cluster operation. + ClusterOperationArn *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationUpdateBrokerTypeMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpUpdateBrokerType{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpUpdateBrokerType{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "UpdateBrokerType"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpUpdateBrokerTypeValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opUpdateBrokerType(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opUpdateBrokerType(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "UpdateBrokerType", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateClusterConfiguration.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateClusterConfiguration.go new file mode 100644 index 000000000..b5a6843a3 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateClusterConfiguration.go @@ -0,0 +1,172 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Updates the cluster with the configuration that is specified in the request +// body. +func (c *Client) UpdateClusterConfiguration(ctx context.Context, params *UpdateClusterConfigurationInput, optFns ...func(*Options)) (*UpdateClusterConfigurationOutput, error) { + if params == nil { + params = &UpdateClusterConfigurationInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "UpdateClusterConfiguration", params, optFns, c.addOperationUpdateClusterConfigurationMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*UpdateClusterConfigurationOutput) + out.ResultMetadata = metadata + return out, nil +} + +type UpdateClusterConfigurationInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies the cluster. + // + // This member is required. + ClusterArn *string + + // Represents the configuration that you want MSK to use for the brokers in a + // cluster. + // + // This member is required. + ConfigurationInfo *types.ConfigurationInfo + + // The version of the cluster that needs to be updated. + // + // This member is required. + CurrentVersion *string + + noSmithyDocumentSerde +} + +type UpdateClusterConfigurationOutput struct { + + // The Amazon Resource Name (ARN) of the cluster. + ClusterArn *string + + // The Amazon Resource Name (ARN) of the cluster operation. + ClusterOperationArn *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationUpdateClusterConfigurationMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpUpdateClusterConfiguration{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpUpdateClusterConfiguration{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "UpdateClusterConfiguration"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpUpdateClusterConfigurationValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opUpdateClusterConfiguration(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opUpdateClusterConfiguration(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "UpdateClusterConfiguration", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateClusterKafkaVersion.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateClusterKafkaVersion.go new file mode 100644 index 000000000..adb5fd66d --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateClusterKafkaVersion.go @@ -0,0 +1,173 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Updates the Apache Kafka version for the cluster. +func (c *Client) UpdateClusterKafkaVersion(ctx context.Context, params *UpdateClusterKafkaVersionInput, optFns ...func(*Options)) (*UpdateClusterKafkaVersionOutput, error) { + if params == nil { + params = &UpdateClusterKafkaVersionInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "UpdateClusterKafkaVersion", params, optFns, c.addOperationUpdateClusterKafkaVersionMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*UpdateClusterKafkaVersionOutput) + out.ResultMetadata = metadata + return out, nil +} + +type UpdateClusterKafkaVersionInput struct { + + // The Amazon Resource Name (ARN) of the cluster to be updated. + // + // This member is required. + ClusterArn *string + + // Current cluster version. + // + // This member is required. + CurrentVersion *string + + // Target Kafka version. + // + // This member is required. + TargetKafkaVersion *string + + // The custom configuration that should be applied on the new version of cluster. + ConfigurationInfo *types.ConfigurationInfo + + noSmithyDocumentSerde +} + +type UpdateClusterKafkaVersionOutput struct { + + // The Amazon Resource Name (ARN) of the cluster. + ClusterArn *string + + // The Amazon Resource Name (ARN) of the cluster operation. + ClusterOperationArn *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationUpdateClusterKafkaVersionMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpUpdateClusterKafkaVersion{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpUpdateClusterKafkaVersion{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "UpdateClusterKafkaVersion"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpUpdateClusterKafkaVersionValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opUpdateClusterKafkaVersion(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opUpdateClusterKafkaVersion(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "UpdateClusterKafkaVersion", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateConfiguration.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateConfiguration.go new file mode 100644 index 000000000..1e8afef07 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateConfiguration.go @@ -0,0 +1,171 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Updates an MSK configuration. +func (c *Client) UpdateConfiguration(ctx context.Context, params *UpdateConfigurationInput, optFns ...func(*Options)) (*UpdateConfigurationOutput, error) { + if params == nil { + params = &UpdateConfigurationInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "UpdateConfiguration", params, optFns, c.addOperationUpdateConfigurationMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*UpdateConfigurationOutput) + out.ResultMetadata = metadata + return out, nil +} + +type UpdateConfigurationInput struct { + + // The Amazon Resource Name (ARN) of the configuration. + // + // This member is required. + Arn *string + + // Contents of the server.properties file. When using the API, you must ensure + // that the contents of the file are base64 encoded. When using the AWS Management + // Console, the SDK, or the AWS CLI, the contents of server.properties can be in + // plaintext. + // + // This member is required. + ServerProperties []byte + + // The description of the configuration revision. + Description *string + + noSmithyDocumentSerde +} + +type UpdateConfigurationOutput struct { + + // The Amazon Resource Name (ARN) of the configuration. + Arn *string + + // Latest revision of the configuration. + LatestRevision *types.ConfigurationRevision + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationUpdateConfigurationMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpUpdateConfiguration{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpUpdateConfiguration{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "UpdateConfiguration"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpUpdateConfigurationValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opUpdateConfiguration(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opUpdateConfiguration(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "UpdateConfiguration", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateConnectivity.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateConnectivity.go new file mode 100644 index 000000000..14ea6b98a --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateConnectivity.go @@ -0,0 +1,173 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Updates the cluster's connectivity configuration. +func (c *Client) UpdateConnectivity(ctx context.Context, params *UpdateConnectivityInput, optFns ...func(*Options)) (*UpdateConnectivityOutput, error) { + if params == nil { + params = &UpdateConnectivityInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "UpdateConnectivity", params, optFns, c.addOperationUpdateConnectivityMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*UpdateConnectivityOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Request body for UpdateConnectivity. +type UpdateConnectivityInput struct { + + // The Amazon Resource Name (ARN) of the configuration. + // + // This member is required. + ClusterArn *string + + // Information about the broker access configuration. + // + // This member is required. + ConnectivityInfo *types.ConnectivityInfo + + // The version of the MSK cluster to update. Cluster versions aren't simple + // numbers. You can describe an MSK cluster to find its version. When this update + // operation is successful, it generates a new cluster version. + // + // This member is required. + CurrentVersion *string + + noSmithyDocumentSerde +} + +type UpdateConnectivityOutput struct { + + // The Amazon Resource Name (ARN) of the cluster. + ClusterArn *string + + // The Amazon Resource Name (ARN) of the cluster operation. + ClusterOperationArn *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationUpdateConnectivityMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpUpdateConnectivity{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpUpdateConnectivity{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "UpdateConnectivity"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpUpdateConnectivityValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opUpdateConnectivity(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opUpdateConnectivity(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "UpdateConnectivity", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateMonitoring.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateMonitoring.go new file mode 100644 index 000000000..684ca07f0 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateMonitoring.go @@ -0,0 +1,179 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Updates the monitoring settings for the cluster. You can use this operation to +// specify which Apache Kafka metrics you want Amazon MSK to send to Amazon +// CloudWatch. You can also specify settings for open monitoring with Prometheus. +func (c *Client) UpdateMonitoring(ctx context.Context, params *UpdateMonitoringInput, optFns ...func(*Options)) (*UpdateMonitoringOutput, error) { + if params == nil { + params = &UpdateMonitoringInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "UpdateMonitoring", params, optFns, c.addOperationUpdateMonitoringMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*UpdateMonitoringOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Request body for UpdateMonitoring. +type UpdateMonitoringInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies the cluster. + // + // This member is required. + ClusterArn *string + + // The version of the MSK cluster to update. Cluster versions aren't simple + // numbers. You can describe an MSK cluster to find its version. When this update + // operation is successful, it generates a new cluster version. + // + // This member is required. + CurrentVersion *string + + // Specifies which Apache Kafka metrics Amazon MSK gathers and sends to Amazon + // CloudWatch for this cluster. + EnhancedMonitoring types.EnhancedMonitoring + + LoggingInfo *types.LoggingInfo + + // The settings for open monitoring. + OpenMonitoring *types.OpenMonitoringInfo + + noSmithyDocumentSerde +} + +type UpdateMonitoringOutput struct { + + // The Amazon Resource Name (ARN) of the cluster. + ClusterArn *string + + // The Amazon Resource Name (ARN) of the cluster operation. + ClusterOperationArn *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationUpdateMonitoringMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpUpdateMonitoring{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpUpdateMonitoring{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "UpdateMonitoring"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpUpdateMonitoringValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opUpdateMonitoring(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opUpdateMonitoring(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "UpdateMonitoring", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateRebalancing.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateRebalancing.go new file mode 100644 index 000000000..effbdd3a3 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateRebalancing.go @@ -0,0 +1,174 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Use this resource to update the intelligent rebalancing status of an Amazon MSK +// Provisioned cluster with Express brokers. +func (c *Client) UpdateRebalancing(ctx context.Context, params *UpdateRebalancingInput, optFns ...func(*Options)) (*UpdateRebalancingOutput, error) { + if params == nil { + params = &UpdateRebalancingInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "UpdateRebalancing", params, optFns, c.addOperationUpdateRebalancingMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*UpdateRebalancingOutput) + out.ResultMetadata = metadata + return out, nil +} + +type UpdateRebalancingInput struct { + + // The Amazon Resource Name (ARN) of the cluster. + // + // This member is required. + ClusterArn *string + + // The current version of the cluster. + // + // This member is required. + CurrentVersion *string + + // Specifies if intelligent rebalancing should be turned on for your cluster. The + // default intelligent rebalancing status is ACTIVE for all new MSK Provisioned + // clusters that you create with Express brokers. + // + // This member is required. + Rebalancing *types.Rebalancing + + noSmithyDocumentSerde +} + +type UpdateRebalancingOutput struct { + + // The Amazon Resource Name (ARN) of the cluster whose intelligent rebalancing + // status you've updated. + ClusterArn *string + + // The Amazon Resource Name (ARN) of the cluster operation. + ClusterOperationArn *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationUpdateRebalancingMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpUpdateRebalancing{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpUpdateRebalancing{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "UpdateRebalancing"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpUpdateRebalancingValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opUpdateRebalancing(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opUpdateRebalancing(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "UpdateRebalancing", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateReplicationInfo.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateReplicationInfo.go new file mode 100644 index 000000000..3c7707b0d --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateReplicationInfo.go @@ -0,0 +1,183 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Updates replication info of a replicator. +func (c *Client) UpdateReplicationInfo(ctx context.Context, params *UpdateReplicationInfoInput, optFns ...func(*Options)) (*UpdateReplicationInfoOutput, error) { + if params == nil { + params = &UpdateReplicationInfoInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "UpdateReplicationInfo", params, optFns, c.addOperationUpdateReplicationInfoMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*UpdateReplicationInfoOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Update information relating to replication between a given source and target +// Kafka cluster. +type UpdateReplicationInfoInput struct { + + // Current replicator version. + // + // This member is required. + CurrentVersion *string + + // The Amazon Resource Name (ARN) of the replicator to be updated. + // + // This member is required. + ReplicatorArn *string + + // The ARN of the source Kafka cluster. + // + // This member is required. + SourceKafkaClusterArn *string + + // The ARN of the target Kafka cluster. + // + // This member is required. + TargetKafkaClusterArn *string + + // Updated consumer group replication information. + ConsumerGroupReplication *types.ConsumerGroupReplicationUpdate + + // Updated topic replication information. + TopicReplication *types.TopicReplicationUpdate + + noSmithyDocumentSerde +} + +type UpdateReplicationInfoOutput struct { + + // The Amazon Resource Name (ARN) of the replicator. + ReplicatorArn *string + + // State of the replicator. + ReplicatorState types.ReplicatorState + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationUpdateReplicationInfoMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpUpdateReplicationInfo{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpUpdateReplicationInfo{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "UpdateReplicationInfo"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpUpdateReplicationInfoValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opUpdateReplicationInfo(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opUpdateReplicationInfo(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "UpdateReplicationInfo", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateSecurity.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateSecurity.go new file mode 100644 index 000000000..6591ae53e --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateSecurity.go @@ -0,0 +1,174 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Updates the security settings for the cluster. You can use this operation to +// specify encryption and authentication on existing clusters. +func (c *Client) UpdateSecurity(ctx context.Context, params *UpdateSecurityInput, optFns ...func(*Options)) (*UpdateSecurityOutput, error) { + if params == nil { + params = &UpdateSecurityInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "UpdateSecurity", params, optFns, c.addOperationUpdateSecurityMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*UpdateSecurityOutput) + out.ResultMetadata = metadata + return out, nil +} + +type UpdateSecurityInput struct { + + // The Amazon Resource Name (ARN) that uniquely identifies the cluster. + // + // This member is required. + ClusterArn *string + + // The version of the MSK cluster to update. Cluster versions aren't simple + // numbers. You can describe an MSK cluster to find its version. When this update + // operation is successful, it generates a new cluster version. + // + // This member is required. + CurrentVersion *string + + // Includes all client authentication related information. + ClientAuthentication *types.ClientAuthentication + + // Includes all encryption-related information. + EncryptionInfo *types.EncryptionInfo + + noSmithyDocumentSerde +} + +type UpdateSecurityOutput struct { + + // The Amazon Resource Name (ARN) of the cluster. + ClusterArn *string + + // The Amazon Resource Name (ARN) of the cluster operation. + ClusterOperationArn *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationUpdateSecurityMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpUpdateSecurity{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpUpdateSecurity{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "UpdateSecurity"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpUpdateSecurityValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opUpdateSecurity(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opUpdateSecurity(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "UpdateSecurity", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateStorage.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateStorage.go new file mode 100644 index 000000000..0f0df2713 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/api_op_UpdateStorage.go @@ -0,0 +1,177 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Updates cluster broker volume size (or) sets cluster storage mode to TIERED. +func (c *Client) UpdateStorage(ctx context.Context, params *UpdateStorageInput, optFns ...func(*Options)) (*UpdateStorageOutput, error) { + if params == nil { + params = &UpdateStorageInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "UpdateStorage", params, optFns, c.addOperationUpdateStorageMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*UpdateStorageOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Request object for UpdateStorage api. Its used to update the storage attributes +// for the cluster. +type UpdateStorageInput struct { + + // The Amazon Resource Name (ARN) of the cluster to be updated. + // + // This member is required. + ClusterArn *string + + // The version of cluster to update from. A successful operation will then + // generate a new version. + // + // This member is required. + CurrentVersion *string + + // EBS volume provisioned throughput information. + ProvisionedThroughput *types.ProvisionedThroughput + + // Controls storage mode for supported storage tiers. + StorageMode types.StorageMode + + // size of the EBS volume to update. + VolumeSizeGB *int32 + + noSmithyDocumentSerde +} + +type UpdateStorageOutput struct { + + // The Amazon Resource Name (ARN) of the cluster. + ClusterArn *string + + // The Amazon Resource Name (ARN) of the cluster operation. + ClusterOperationArn *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationUpdateStorageMiddlewares(stack *middleware.Stack, options Options) (err error) { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + err = stack.Serialize.Add(&awsRestjson1_serializeOpUpdateStorage{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpUpdateStorage{}, middleware.After) + if err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, "UpdateStorage"); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = addClientRequestID(stack); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRetry(stack, options); err != nil { + return err + } + if err = addRawResponseToMetadata(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack); err != nil { + return err + } + if err = addSpanRetryLoop(stack, options); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err = addTimeOffsetBuild(stack, c); err != nil { + return err + } + if err = addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpUpdateStorageValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opUpdateStorage(options.Region), middleware.Before); err != nil { + return err + } + if err = addRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err = addInterceptAttempt(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opUpdateStorage(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: "UpdateStorage", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/auth.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/auth.go new file mode 100644 index 000000000..0ce94b547 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/auth.go @@ -0,0 +1,345 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + smithy "github.com/aws/smithy-go" + smithyauth "github.com/aws/smithy-go/auth" + "github.com/aws/smithy-go/metrics" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/tracing" + smithyhttp "github.com/aws/smithy-go/transport/http" + "slices" + "strings" +) + +func bindAuthParamsRegion(_ interface{}, params *AuthResolverParameters, _ interface{}, options Options) error { + params.Region = options.Region + return nil +} + +type setLegacyContextSigningOptionsMiddleware struct { +} + +func (*setLegacyContextSigningOptionsMiddleware) ID() string { + return "setLegacyContextSigningOptions" +} + +func (m *setLegacyContextSigningOptionsMiddleware) HandleFinalize(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) ( + out middleware.FinalizeOutput, metadata middleware.Metadata, err error, +) { + rscheme := getResolvedAuthScheme(ctx) + schemeID := rscheme.Scheme.SchemeID() + + if sn := awsmiddleware.GetSigningName(ctx); sn != "" { + if schemeID == "aws.auth#sigv4" { + smithyhttp.SetSigV4SigningName(&rscheme.SignerProperties, sn) + } else if schemeID == "aws.auth#sigv4a" { + smithyhttp.SetSigV4ASigningName(&rscheme.SignerProperties, sn) + } + } + + if sr := awsmiddleware.GetSigningRegion(ctx); sr != "" { + if schemeID == "aws.auth#sigv4" { + smithyhttp.SetSigV4SigningRegion(&rscheme.SignerProperties, sr) + } else if schemeID == "aws.auth#sigv4a" { + smithyhttp.SetSigV4ASigningRegions(&rscheme.SignerProperties, []string{sr}) + } + } + + return next.HandleFinalize(ctx, in) +} + +func addSetLegacyContextSigningOptionsMiddleware(stack *middleware.Stack) error { + return stack.Finalize.Insert(&setLegacyContextSigningOptionsMiddleware{}, "Signing", middleware.Before) +} + +type withAnonymous struct { + resolver AuthSchemeResolver +} + +var _ AuthSchemeResolver = (*withAnonymous)(nil) + +func (v *withAnonymous) ResolveAuthSchemes(ctx context.Context, params *AuthResolverParameters) ([]*smithyauth.Option, error) { + opts, err := v.resolver.ResolveAuthSchemes(ctx, params) + if err != nil { + return nil, err + } + + opts = append(opts, &smithyauth.Option{ + SchemeID: smithyauth.SchemeIDAnonymous, + }) + return opts, nil +} + +func wrapWithAnonymousAuth(options *Options) { + if _, ok := options.AuthSchemeResolver.(*defaultAuthSchemeResolver); !ok { + return + } + + options.AuthSchemeResolver = &withAnonymous{ + resolver: options.AuthSchemeResolver, + } +} + +// AuthResolverParameters contains the set of inputs necessary for auth scheme +// resolution. +type AuthResolverParameters struct { + // The name of the operation being invoked. + Operation string + + // The region in which the operation is being invoked. + Region string +} + +func bindAuthResolverParams(ctx context.Context, operation string, input interface{}, options Options) (*AuthResolverParameters, error) { + params := &AuthResolverParameters{ + Operation: operation, + } + + if err := bindAuthParamsRegion(ctx, params, input, options); err != nil { + return nil, err + } + + return params, nil +} + +// AuthSchemeResolver returns a set of possible authentication options for an +// operation. +type AuthSchemeResolver interface { + ResolveAuthSchemes(context.Context, *AuthResolverParameters) ([]*smithyauth.Option, error) +} + +type defaultAuthSchemeResolver struct{} + +var _ AuthSchemeResolver = (*defaultAuthSchemeResolver)(nil) + +func (*defaultAuthSchemeResolver) ResolveAuthSchemes(ctx context.Context, params *AuthResolverParameters) ([]*smithyauth.Option, error) { + if overrides, ok := operationAuthOptions[params.Operation]; ok { + return overrides(params), nil + } + return serviceAuthOptions(params), nil +} + +var operationAuthOptions = map[string]func(*AuthResolverParameters) []*smithyauth.Option{} + +func serviceAuthOptions(params *AuthResolverParameters) []*smithyauth.Option { + return []*smithyauth.Option{ + { + SchemeID: smithyauth.SchemeIDSigV4, + SignerProperties: func() smithy.Properties { + var props smithy.Properties + smithyhttp.SetSigV4SigningName(&props, "kafka") + smithyhttp.SetSigV4SigningRegion(&props, params.Region) + return props + }(), + }, + } +} + +type resolveAuthSchemeMiddleware struct { + operation string + options Options +} + +func (*resolveAuthSchemeMiddleware) ID() string { + return "ResolveAuthScheme" +} + +func (m *resolveAuthSchemeMiddleware) HandleFinalize(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) ( + out middleware.FinalizeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "ResolveAuthScheme") + defer span.End() + + params, err := bindAuthResolverParams(ctx, m.operation, getOperationInput(ctx), m.options) + if err != nil { + return out, metadata, fmt.Errorf("bind auth scheme params: %w", err) + } + options, err := m.options.AuthSchemeResolver.ResolveAuthSchemes(ctx, params) + if err != nil { + return out, metadata, fmt.Errorf("resolve auth scheme: %w", err) + } + + scheme, ok := m.selectScheme(options) + if !ok { + return out, metadata, fmt.Errorf("could not select an auth scheme") + } + + ctx = setResolvedAuthScheme(ctx, scheme) + + span.SetProperty("auth.scheme_id", scheme.Scheme.SchemeID()) + span.End() + return next.HandleFinalize(ctx, in) +} + +func (m *resolveAuthSchemeMiddleware) selectScheme(options []*smithyauth.Option) (*resolvedAuthScheme, bool) { + sorted := sortAuthOptions(options, m.options.AuthSchemePreference) + for _, option := range sorted { + if option.SchemeID == smithyauth.SchemeIDAnonymous { + return newResolvedAuthScheme(smithyhttp.NewAnonymousScheme(), option), true + } + + for _, scheme := range m.options.AuthSchemes { + if scheme.SchemeID() != option.SchemeID { + continue + } + + if scheme.IdentityResolver(m.options) != nil { + return newResolvedAuthScheme(scheme, option), true + } + } + } + + return nil, false +} + +func sortAuthOptions(options []*smithyauth.Option, preferred []string) []*smithyauth.Option { + byPriority := make([]*smithyauth.Option, 0, len(options)) + for _, prefName := range preferred { + for _, option := range options { + optName := option.SchemeID + if parts := strings.Split(option.SchemeID, "#"); len(parts) == 2 { + optName = parts[1] + } + if prefName == optName { + byPriority = append(byPriority, option) + } + } + } + for _, option := range options { + if !slices.ContainsFunc(byPriority, func(o *smithyauth.Option) bool { + return o.SchemeID == option.SchemeID + }) { + byPriority = append(byPriority, option) + } + } + return byPriority +} + +type resolvedAuthSchemeKey struct{} + +type resolvedAuthScheme struct { + Scheme smithyhttp.AuthScheme + IdentityProperties smithy.Properties + SignerProperties smithy.Properties +} + +func newResolvedAuthScheme(scheme smithyhttp.AuthScheme, option *smithyauth.Option) *resolvedAuthScheme { + return &resolvedAuthScheme{ + Scheme: scheme, + IdentityProperties: option.IdentityProperties, + SignerProperties: option.SignerProperties, + } +} + +func setResolvedAuthScheme(ctx context.Context, scheme *resolvedAuthScheme) context.Context { + return middleware.WithStackValue(ctx, resolvedAuthSchemeKey{}, scheme) +} + +func getResolvedAuthScheme(ctx context.Context) *resolvedAuthScheme { + v, _ := middleware.GetStackValue(ctx, resolvedAuthSchemeKey{}).(*resolvedAuthScheme) + return v +} + +type getIdentityMiddleware struct { + options Options +} + +func (*getIdentityMiddleware) ID() string { + return "GetIdentity" +} + +func (m *getIdentityMiddleware) HandleFinalize(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) ( + out middleware.FinalizeOutput, metadata middleware.Metadata, err error, +) { + innerCtx, span := tracing.StartSpan(ctx, "GetIdentity") + defer span.End() + + rscheme := getResolvedAuthScheme(innerCtx) + if rscheme == nil { + return out, metadata, fmt.Errorf("no resolved auth scheme") + } + + resolver := rscheme.Scheme.IdentityResolver(m.options) + if resolver == nil { + return out, metadata, fmt.Errorf("no identity resolver") + } + + identity, err := timeOperationMetric(ctx, "client.call.resolve_identity_duration", + func() (smithyauth.Identity, error) { + return resolver.GetIdentity(innerCtx, rscheme.IdentityProperties) + }, + func(o *metrics.RecordMetricOptions) { + o.Properties.Set("auth.scheme_id", rscheme.Scheme.SchemeID()) + }) + if err != nil { + return out, metadata, fmt.Errorf("get identity: %w", err) + } + + ctx = setIdentity(ctx, identity) + + span.End() + return next.HandleFinalize(ctx, in) +} + +type identityKey struct{} + +func setIdentity(ctx context.Context, identity smithyauth.Identity) context.Context { + return middleware.WithStackValue(ctx, identityKey{}, identity) +} + +func getIdentity(ctx context.Context) smithyauth.Identity { + v, _ := middleware.GetStackValue(ctx, identityKey{}).(smithyauth.Identity) + return v +} + +type signRequestMiddleware struct { + options Options +} + +func (*signRequestMiddleware) ID() string { + return "Signing" +} + +func (m *signRequestMiddleware) HandleFinalize(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) ( + out middleware.FinalizeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "SignRequest") + defer span.End() + + req, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, fmt.Errorf("unexpected transport type %T", in.Request) + } + + rscheme := getResolvedAuthScheme(ctx) + if rscheme == nil { + return out, metadata, fmt.Errorf("no resolved auth scheme") + } + + identity := getIdentity(ctx) + if identity == nil { + return out, metadata, fmt.Errorf("no identity") + } + + signer := rscheme.Scheme.Signer() + if signer == nil { + return out, metadata, fmt.Errorf("no signer") + } + + _, err = timeOperationMetric(ctx, "client.call.signing_duration", func() (any, error) { + return nil, signer.SignRequest(ctx, req, identity, rscheme.SignerProperties) + }, func(o *metrics.RecordMetricOptions) { + o.Properties.Set("auth.scheme_id", rscheme.Scheme.SchemeID()) + }) + if err != nil { + return out, metadata, fmt.Errorf("sign request: %w", err) + } + + span.End() + return next.HandleFinalize(ctx, in) +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/deserializers.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/deserializers.go new file mode 100644 index 000000000..e5cff2bac --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/deserializers.go @@ -0,0 +1,16545 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "bytes" + "context" + "encoding/base64" + "encoding/json" + "fmt" + "github.com/aws/aws-sdk-go-v2/aws/protocol/restjson" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + smithy "github.com/aws/smithy-go" + smithyio "github.com/aws/smithy-go/io" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/ptr" + smithytime "github.com/aws/smithy-go/time" + "github.com/aws/smithy-go/tracing" + smithyhttp "github.com/aws/smithy-go/transport/http" + "io" + "io/ioutil" + "math" + "strings" +) + +type awsRestjson1_deserializeOpBatchAssociateScramSecret struct { +} + +func (*awsRestjson1_deserializeOpBatchAssociateScramSecret) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpBatchAssociateScramSecret) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorBatchAssociateScramSecret(response, &metadata) + } + output := &BatchAssociateScramSecretOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentBatchAssociateScramSecretOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorBatchAssociateScramSecret(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("TooManyRequestsException", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentBatchAssociateScramSecretOutput(v **BatchAssociateScramSecretOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *BatchAssociateScramSecretOutput + if *v == nil { + sv = &BatchAssociateScramSecretOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterArn = ptr.String(jtv) + } + + case "unprocessedScramSecrets": + if err := awsRestjson1_deserializeDocument__listOfUnprocessedScramSecret(&sv.UnprocessedScramSecrets, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpBatchDisassociateScramSecret struct { +} + +func (*awsRestjson1_deserializeOpBatchDisassociateScramSecret) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpBatchDisassociateScramSecret) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorBatchDisassociateScramSecret(response, &metadata) + } + output := &BatchDisassociateScramSecretOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentBatchDisassociateScramSecretOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorBatchDisassociateScramSecret(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("TooManyRequestsException", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentBatchDisassociateScramSecretOutput(v **BatchDisassociateScramSecretOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *BatchDisassociateScramSecretOutput + if *v == nil { + sv = &BatchDisassociateScramSecretOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterArn = ptr.String(jtv) + } + + case "unprocessedScramSecrets": + if err := awsRestjson1_deserializeDocument__listOfUnprocessedScramSecret(&sv.UnprocessedScramSecrets, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpCreateCluster struct { +} + +func (*awsRestjson1_deserializeOpCreateCluster) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpCreateCluster) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorCreateCluster(response, &metadata) + } + output := &CreateClusterOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentCreateClusterOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorCreateCluster(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ConflictException", errorCode): + return awsRestjson1_deserializeErrorConflictException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("TooManyRequestsException", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentCreateClusterOutput(v **CreateClusterOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *CreateClusterOutput + if *v == nil { + sv = &CreateClusterOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterArn = ptr.String(jtv) + } + + case "clusterName": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterName = ptr.String(jtv) + } + + case "state": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected ClusterState to be of type string, got %T instead", value) + } + sv.State = types.ClusterState(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpCreateClusterV2 struct { +} + +func (*awsRestjson1_deserializeOpCreateClusterV2) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpCreateClusterV2) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorCreateClusterV2(response, &metadata) + } + output := &CreateClusterV2Output{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentCreateClusterV2Output(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorCreateClusterV2(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ConflictException", errorCode): + return awsRestjson1_deserializeErrorConflictException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("TooManyRequestsException", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentCreateClusterV2Output(v **CreateClusterV2Output, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *CreateClusterV2Output + if *v == nil { + sv = &CreateClusterV2Output{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterArn = ptr.String(jtv) + } + + case "clusterName": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterName = ptr.String(jtv) + } + + case "clusterType": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected ClusterType to be of type string, got %T instead", value) + } + sv.ClusterType = types.ClusterType(jtv) + } + + case "state": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected ClusterState to be of type string, got %T instead", value) + } + sv.State = types.ClusterState(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpCreateConfiguration struct { +} + +func (*awsRestjson1_deserializeOpCreateConfiguration) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpCreateConfiguration) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorCreateConfiguration(response, &metadata) + } + output := &CreateConfigurationOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentCreateConfigurationOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorCreateConfiguration(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ConflictException", errorCode): + return awsRestjson1_deserializeErrorConflictException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("TooManyRequestsException", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentCreateConfigurationOutput(v **CreateConfigurationOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *CreateConfigurationOutput + if *v == nil { + sv = &CreateConfigurationOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "arn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Arn = ptr.String(jtv) + } + + case "creationTime": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __timestampIso8601 to be of type string, got %T instead", value) + } + t, err := smithytime.ParseDateTime(jtv) + if err != nil { + return err + } + sv.CreationTime = ptr.Time(t) + } + + case "latestRevision": + if err := awsRestjson1_deserializeDocumentConfigurationRevision(&sv.LatestRevision, value); err != nil { + return err + } + + case "name": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Name = ptr.String(jtv) + } + + case "state": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected ConfigurationState to be of type string, got %T instead", value) + } + sv.State = types.ConfigurationState(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpCreateReplicator struct { +} + +func (*awsRestjson1_deserializeOpCreateReplicator) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpCreateReplicator) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorCreateReplicator(response, &metadata) + } + output := &CreateReplicatorOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentCreateReplicatorOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorCreateReplicator(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ConflictException", errorCode): + return awsRestjson1_deserializeErrorConflictException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("TooManyRequestsException", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentCreateReplicatorOutput(v **CreateReplicatorOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *CreateReplicatorOutput + if *v == nil { + sv = &CreateReplicatorOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "replicatorArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ReplicatorArn = ptr.String(jtv) + } + + case "replicatorName": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ReplicatorName = ptr.String(jtv) + } + + case "replicatorState": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected ReplicatorState to be of type string, got %T instead", value) + } + sv.ReplicatorState = types.ReplicatorState(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpCreateVpcConnection struct { +} + +func (*awsRestjson1_deserializeOpCreateVpcConnection) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpCreateVpcConnection) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorCreateVpcConnection(response, &metadata) + } + output := &CreateVpcConnectionOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentCreateVpcConnectionOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorCreateVpcConnection(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("TooManyRequestsException", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentCreateVpcConnectionOutput(v **CreateVpcConnectionOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *CreateVpcConnectionOutput + if *v == nil { + sv = &CreateVpcConnectionOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "authentication": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Authentication = ptr.String(jtv) + } + + case "clientSubnets": + if err := awsRestjson1_deserializeDocument__listOf__string(&sv.ClientSubnets, value); err != nil { + return err + } + + case "creationTime": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __timestampIso8601 to be of type string, got %T instead", value) + } + t, err := smithytime.ParseDateTime(jtv) + if err != nil { + return err + } + sv.CreationTime = ptr.Time(t) + } + + case "securityGroups": + if err := awsRestjson1_deserializeDocument__listOf__string(&sv.SecurityGroups, value); err != nil { + return err + } + + case "state": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected VpcConnectionState to be of type string, got %T instead", value) + } + sv.State = types.VpcConnectionState(jtv) + } + + case "tags": + if err := awsRestjson1_deserializeDocument__mapOf__string(&sv.Tags, value); err != nil { + return err + } + + case "vpcConnectionArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.VpcConnectionArn = ptr.String(jtv) + } + + case "vpcId": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.VpcId = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpDeleteCluster struct { +} + +func (*awsRestjson1_deserializeOpDeleteCluster) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpDeleteCluster) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorDeleteCluster(response, &metadata) + } + output := &DeleteClusterOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentDeleteClusterOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorDeleteCluster(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentDeleteClusterOutput(v **DeleteClusterOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *DeleteClusterOutput + if *v == nil { + sv = &DeleteClusterOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterArn = ptr.String(jtv) + } + + case "state": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected ClusterState to be of type string, got %T instead", value) + } + sv.State = types.ClusterState(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpDeleteClusterPolicy struct { +} + +func (*awsRestjson1_deserializeOpDeleteClusterPolicy) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpDeleteClusterPolicy) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorDeleteClusterPolicy(response, &metadata) + } + output := &DeleteClusterPolicyOutput{} + out.Result = output + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorDeleteClusterPolicy(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsRestjson1_deserializeOpDeleteConfiguration struct { +} + +func (*awsRestjson1_deserializeOpDeleteConfiguration) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpDeleteConfiguration) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorDeleteConfiguration(response, &metadata) + } + output := &DeleteConfigurationOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentDeleteConfigurationOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorDeleteConfiguration(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentDeleteConfigurationOutput(v **DeleteConfigurationOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *DeleteConfigurationOutput + if *v == nil { + sv = &DeleteConfigurationOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "arn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Arn = ptr.String(jtv) + } + + case "state": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected ConfigurationState to be of type string, got %T instead", value) + } + sv.State = types.ConfigurationState(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpDeleteReplicator struct { +} + +func (*awsRestjson1_deserializeOpDeleteReplicator) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpDeleteReplicator) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorDeleteReplicator(response, &metadata) + } + output := &DeleteReplicatorOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentDeleteReplicatorOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorDeleteReplicator(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("TooManyRequestsException", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentDeleteReplicatorOutput(v **DeleteReplicatorOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *DeleteReplicatorOutput + if *v == nil { + sv = &DeleteReplicatorOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "replicatorArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ReplicatorArn = ptr.String(jtv) + } + + case "replicatorState": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected ReplicatorState to be of type string, got %T instead", value) + } + sv.ReplicatorState = types.ReplicatorState(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpDeleteVpcConnection struct { +} + +func (*awsRestjson1_deserializeOpDeleteVpcConnection) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpDeleteVpcConnection) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorDeleteVpcConnection(response, &metadata) + } + output := &DeleteVpcConnectionOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentDeleteVpcConnectionOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorDeleteVpcConnection(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentDeleteVpcConnectionOutput(v **DeleteVpcConnectionOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *DeleteVpcConnectionOutput + if *v == nil { + sv = &DeleteVpcConnectionOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "state": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected VpcConnectionState to be of type string, got %T instead", value) + } + sv.State = types.VpcConnectionState(jtv) + } + + case "vpcConnectionArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.VpcConnectionArn = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpDescribeCluster struct { +} + +func (*awsRestjson1_deserializeOpDescribeCluster) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpDescribeCluster) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorDescribeCluster(response, &metadata) + } + output := &DescribeClusterOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentDescribeClusterOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorDescribeCluster(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentDescribeClusterOutput(v **DescribeClusterOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *DescribeClusterOutput + if *v == nil { + sv = &DescribeClusterOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterInfo": + if err := awsRestjson1_deserializeDocumentClusterInfo(&sv.ClusterInfo, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpDescribeClusterOperation struct { +} + +func (*awsRestjson1_deserializeOpDescribeClusterOperation) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpDescribeClusterOperation) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorDescribeClusterOperation(response, &metadata) + } + output := &DescribeClusterOperationOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentDescribeClusterOperationOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorDescribeClusterOperation(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentDescribeClusterOperationOutput(v **DescribeClusterOperationOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *DescribeClusterOperationOutput + if *v == nil { + sv = &DescribeClusterOperationOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterOperationInfo": + if err := awsRestjson1_deserializeDocumentClusterOperationInfo(&sv.ClusterOperationInfo, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpDescribeClusterOperationV2 struct { +} + +func (*awsRestjson1_deserializeOpDescribeClusterOperationV2) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpDescribeClusterOperationV2) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorDescribeClusterOperationV2(response, &metadata) + } + output := &DescribeClusterOperationV2Output{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentDescribeClusterOperationV2Output(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorDescribeClusterOperationV2(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("TooManyRequestsException", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentDescribeClusterOperationV2Output(v **DescribeClusterOperationV2Output, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *DescribeClusterOperationV2Output + if *v == nil { + sv = &DescribeClusterOperationV2Output{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterOperationInfo": + if err := awsRestjson1_deserializeDocumentClusterOperationV2(&sv.ClusterOperationInfo, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpDescribeClusterV2 struct { +} + +func (*awsRestjson1_deserializeOpDescribeClusterV2) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpDescribeClusterV2) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorDescribeClusterV2(response, &metadata) + } + output := &DescribeClusterV2Output{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentDescribeClusterV2Output(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorDescribeClusterV2(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentDescribeClusterV2Output(v **DescribeClusterV2Output, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *DescribeClusterV2Output + if *v == nil { + sv = &DescribeClusterV2Output{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterInfo": + if err := awsRestjson1_deserializeDocumentCluster(&sv.ClusterInfo, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpDescribeConfiguration struct { +} + +func (*awsRestjson1_deserializeOpDescribeConfiguration) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpDescribeConfiguration) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorDescribeConfiguration(response, &metadata) + } + output := &DescribeConfigurationOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentDescribeConfigurationOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorDescribeConfiguration(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentDescribeConfigurationOutput(v **DescribeConfigurationOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *DescribeConfigurationOutput + if *v == nil { + sv = &DescribeConfigurationOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "arn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Arn = ptr.String(jtv) + } + + case "creationTime": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __timestampIso8601 to be of type string, got %T instead", value) + } + t, err := smithytime.ParseDateTime(jtv) + if err != nil { + return err + } + sv.CreationTime = ptr.Time(t) + } + + case "description": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Description = ptr.String(jtv) + } + + case "kafkaVersions": + if err := awsRestjson1_deserializeDocument__listOf__string(&sv.KafkaVersions, value); err != nil { + return err + } + + case "latestRevision": + if err := awsRestjson1_deserializeDocumentConfigurationRevision(&sv.LatestRevision, value); err != nil { + return err + } + + case "name": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Name = ptr.String(jtv) + } + + case "state": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected ConfigurationState to be of type string, got %T instead", value) + } + sv.State = types.ConfigurationState(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpDescribeConfigurationRevision struct { +} + +func (*awsRestjson1_deserializeOpDescribeConfigurationRevision) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpDescribeConfigurationRevision) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorDescribeConfigurationRevision(response, &metadata) + } + output := &DescribeConfigurationRevisionOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentDescribeConfigurationRevisionOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorDescribeConfigurationRevision(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentDescribeConfigurationRevisionOutput(v **DescribeConfigurationRevisionOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *DescribeConfigurationRevisionOutput + if *v == nil { + sv = &DescribeConfigurationRevisionOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "arn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Arn = ptr.String(jtv) + } + + case "creationTime": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __timestampIso8601 to be of type string, got %T instead", value) + } + t, err := smithytime.ParseDateTime(jtv) + if err != nil { + return err + } + sv.CreationTime = ptr.Time(t) + } + + case "description": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Description = ptr.String(jtv) + } + + case "revision": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected __long to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + sv.Revision = ptr.Int64(i64) + } + + case "serverProperties": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __blob to be []byte, got %T instead", value) + } + dv, err := base64.StdEncoding.DecodeString(jtv) + if err != nil { + return fmt.Errorf("failed to base64 decode __blob, %w", err) + } + sv.ServerProperties = dv + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpDescribeReplicator struct { +} + +func (*awsRestjson1_deserializeOpDescribeReplicator) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpDescribeReplicator) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorDescribeReplicator(response, &metadata) + } + output := &DescribeReplicatorOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentDescribeReplicatorOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorDescribeReplicator(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("TooManyRequestsException", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentDescribeReplicatorOutput(v **DescribeReplicatorOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *DescribeReplicatorOutput + if *v == nil { + sv = &DescribeReplicatorOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "creationTime": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __timestampIso8601 to be of type string, got %T instead", value) + } + t, err := smithytime.ParseDateTime(jtv) + if err != nil { + return err + } + sv.CreationTime = ptr.Time(t) + } + + case "currentVersion": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.CurrentVersion = ptr.String(jtv) + } + + case "isReplicatorReference": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.IsReplicatorReference = ptr.Bool(jtv) + } + + case "kafkaClusters": + if err := awsRestjson1_deserializeDocument__listOfKafkaClusterDescription(&sv.KafkaClusters, value); err != nil { + return err + } + + case "replicationInfoList": + if err := awsRestjson1_deserializeDocument__listOfReplicationInfoDescription(&sv.ReplicationInfoList, value); err != nil { + return err + } + + case "replicatorArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ReplicatorArn = ptr.String(jtv) + } + + case "replicatorDescription": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ReplicatorDescription = ptr.String(jtv) + } + + case "replicatorName": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ReplicatorName = ptr.String(jtv) + } + + case "replicatorResourceArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ReplicatorResourceArn = ptr.String(jtv) + } + + case "replicatorState": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected ReplicatorState to be of type string, got %T instead", value) + } + sv.ReplicatorState = types.ReplicatorState(jtv) + } + + case "serviceExecutionRoleArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ServiceExecutionRoleArn = ptr.String(jtv) + } + + case "stateInfo": + if err := awsRestjson1_deserializeDocumentReplicationStateInfo(&sv.StateInfo, value); err != nil { + return err + } + + case "tags": + if err := awsRestjson1_deserializeDocument__mapOf__string(&sv.Tags, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpDescribeTopic struct { +} + +func (*awsRestjson1_deserializeOpDescribeTopic) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpDescribeTopic) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorDescribeTopic(response, &metadata) + } + output := &DescribeTopicOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentDescribeTopicOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorDescribeTopic(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentDescribeTopicOutput(v **DescribeTopicOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *DescribeTopicOutput + if *v == nil { + sv = &DescribeTopicOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "configs": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Configs = ptr.String(jtv) + } + + case "partitionCount": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected __integer to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + sv.PartitionCount = ptr.Int32(int32(i64)) + } + + case "replicationFactor": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected __integer to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + sv.ReplicationFactor = ptr.Int32(int32(i64)) + } + + case "status": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected TopicState to be of type string, got %T instead", value) + } + sv.Status = types.TopicState(jtv) + } + + case "topicArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.TopicArn = ptr.String(jtv) + } + + case "topicName": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.TopicName = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpDescribeTopicPartitions struct { +} + +func (*awsRestjson1_deserializeOpDescribeTopicPartitions) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpDescribeTopicPartitions) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorDescribeTopicPartitions(response, &metadata) + } + output := &DescribeTopicPartitionsOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentDescribeTopicPartitionsOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorDescribeTopicPartitions(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentDescribeTopicPartitionsOutput(v **DescribeTopicPartitionsOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *DescribeTopicPartitionsOutput + if *v == nil { + sv = &DescribeTopicPartitionsOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "nextToken": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.NextToken = ptr.String(jtv) + } + + case "partitions": + if err := awsRestjson1_deserializeDocument__listOfTopicPartitionInfo(&sv.Partitions, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpDescribeVpcConnection struct { +} + +func (*awsRestjson1_deserializeOpDescribeVpcConnection) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpDescribeVpcConnection) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorDescribeVpcConnection(response, &metadata) + } + output := &DescribeVpcConnectionOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentDescribeVpcConnectionOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorDescribeVpcConnection(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentDescribeVpcConnectionOutput(v **DescribeVpcConnectionOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *DescribeVpcConnectionOutput + if *v == nil { + sv = &DescribeVpcConnectionOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "authentication": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Authentication = ptr.String(jtv) + } + + case "creationTime": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __timestampIso8601 to be of type string, got %T instead", value) + } + t, err := smithytime.ParseDateTime(jtv) + if err != nil { + return err + } + sv.CreationTime = ptr.Time(t) + } + + case "securityGroups": + if err := awsRestjson1_deserializeDocument__listOf__string(&sv.SecurityGroups, value); err != nil { + return err + } + + case "state": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected VpcConnectionState to be of type string, got %T instead", value) + } + sv.State = types.VpcConnectionState(jtv) + } + + case "subnets": + if err := awsRestjson1_deserializeDocument__listOf__string(&sv.Subnets, value); err != nil { + return err + } + + case "tags": + if err := awsRestjson1_deserializeDocument__mapOf__string(&sv.Tags, value); err != nil { + return err + } + + case "targetClusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.TargetClusterArn = ptr.String(jtv) + } + + case "vpcConnectionArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.VpcConnectionArn = ptr.String(jtv) + } + + case "vpcId": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.VpcId = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpGetBootstrapBrokers struct { +} + +func (*awsRestjson1_deserializeOpGetBootstrapBrokers) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpGetBootstrapBrokers) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorGetBootstrapBrokers(response, &metadata) + } + output := &GetBootstrapBrokersOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentGetBootstrapBrokersOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorGetBootstrapBrokers(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ConflictException", errorCode): + return awsRestjson1_deserializeErrorConflictException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentGetBootstrapBrokersOutput(v **GetBootstrapBrokersOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *GetBootstrapBrokersOutput + if *v == nil { + sv = &GetBootstrapBrokersOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "bootstrapBrokerString": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.BootstrapBrokerString = ptr.String(jtv) + } + + case "bootstrapBrokerStringPublicSaslIam": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.BootstrapBrokerStringPublicSaslIam = ptr.String(jtv) + } + + case "bootstrapBrokerStringPublicSaslScram": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.BootstrapBrokerStringPublicSaslScram = ptr.String(jtv) + } + + case "bootstrapBrokerStringPublicTls": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.BootstrapBrokerStringPublicTls = ptr.String(jtv) + } + + case "bootstrapBrokerStringSaslIam": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.BootstrapBrokerStringSaslIam = ptr.String(jtv) + } + + case "bootstrapBrokerStringSaslScram": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.BootstrapBrokerStringSaslScram = ptr.String(jtv) + } + + case "bootstrapBrokerStringTls": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.BootstrapBrokerStringTls = ptr.String(jtv) + } + + case "bootstrapBrokerStringVpcConnectivitySaslIam": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.BootstrapBrokerStringVpcConnectivitySaslIam = ptr.String(jtv) + } + + case "bootstrapBrokerStringVpcConnectivitySaslScram": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.BootstrapBrokerStringVpcConnectivitySaslScram = ptr.String(jtv) + } + + case "bootstrapBrokerStringVpcConnectivityTls": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.BootstrapBrokerStringVpcConnectivityTls = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpGetClusterPolicy struct { +} + +func (*awsRestjson1_deserializeOpGetClusterPolicy) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpGetClusterPolicy) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorGetClusterPolicy(response, &metadata) + } + output := &GetClusterPolicyOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentGetClusterPolicyOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorGetClusterPolicy(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentGetClusterPolicyOutput(v **GetClusterPolicyOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *GetClusterPolicyOutput + if *v == nil { + sv = &GetClusterPolicyOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "currentVersion": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.CurrentVersion = ptr.String(jtv) + } + + case "policy": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Policy = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpGetCompatibleKafkaVersions struct { +} + +func (*awsRestjson1_deserializeOpGetCompatibleKafkaVersions) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpGetCompatibleKafkaVersions) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorGetCompatibleKafkaVersions(response, &metadata) + } + output := &GetCompatibleKafkaVersionsOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentGetCompatibleKafkaVersionsOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorGetCompatibleKafkaVersions(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("TooManyRequestsException", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentGetCompatibleKafkaVersionsOutput(v **GetCompatibleKafkaVersionsOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *GetCompatibleKafkaVersionsOutput + if *v == nil { + sv = &GetCompatibleKafkaVersionsOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "compatibleKafkaVersions": + if err := awsRestjson1_deserializeDocument__listOfCompatibleKafkaVersion(&sv.CompatibleKafkaVersions, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpListClientVpcConnections struct { +} + +func (*awsRestjson1_deserializeOpListClientVpcConnections) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpListClientVpcConnections) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorListClientVpcConnections(response, &metadata) + } + output := &ListClientVpcConnectionsOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentListClientVpcConnectionsOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorListClientVpcConnections(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentListClientVpcConnectionsOutput(v **ListClientVpcConnectionsOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *ListClientVpcConnectionsOutput + if *v == nil { + sv = &ListClientVpcConnectionsOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clientVpcConnections": + if err := awsRestjson1_deserializeDocument__listOfClientVpcConnection(&sv.ClientVpcConnections, value); err != nil { + return err + } + + case "nextToken": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.NextToken = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpListClusterOperations struct { +} + +func (*awsRestjson1_deserializeOpListClusterOperations) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpListClusterOperations) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorListClusterOperations(response, &metadata) + } + output := &ListClusterOperationsOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentListClusterOperationsOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorListClusterOperations(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentListClusterOperationsOutput(v **ListClusterOperationsOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *ListClusterOperationsOutput + if *v == nil { + sv = &ListClusterOperationsOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterOperationInfoList": + if err := awsRestjson1_deserializeDocument__listOfClusterOperationInfo(&sv.ClusterOperationInfoList, value); err != nil { + return err + } + + case "nextToken": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.NextToken = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpListClusterOperationsV2 struct { +} + +func (*awsRestjson1_deserializeOpListClusterOperationsV2) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpListClusterOperationsV2) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorListClusterOperationsV2(response, &metadata) + } + output := &ListClusterOperationsV2Output{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentListClusterOperationsV2Output(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorListClusterOperationsV2(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("TooManyRequestsException", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentListClusterOperationsV2Output(v **ListClusterOperationsV2Output, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *ListClusterOperationsV2Output + if *v == nil { + sv = &ListClusterOperationsV2Output{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterOperationInfoList": + if err := awsRestjson1_deserializeDocument__listOfClusterOperationV2Summary(&sv.ClusterOperationInfoList, value); err != nil { + return err + } + + case "nextToken": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.NextToken = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpListClusters struct { +} + +func (*awsRestjson1_deserializeOpListClusters) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpListClusters) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorListClusters(response, &metadata) + } + output := &ListClustersOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentListClustersOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorListClusters(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentListClustersOutput(v **ListClustersOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *ListClustersOutput + if *v == nil { + sv = &ListClustersOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterInfoList": + if err := awsRestjson1_deserializeDocument__listOfClusterInfo(&sv.ClusterInfoList, value); err != nil { + return err + } + + case "nextToken": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.NextToken = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpListClustersV2 struct { +} + +func (*awsRestjson1_deserializeOpListClustersV2) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpListClustersV2) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorListClustersV2(response, &metadata) + } + output := &ListClustersV2Output{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentListClustersV2Output(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorListClustersV2(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentListClustersV2Output(v **ListClustersV2Output, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *ListClustersV2Output + if *v == nil { + sv = &ListClustersV2Output{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterInfoList": + if err := awsRestjson1_deserializeDocument__listOfCluster(&sv.ClusterInfoList, value); err != nil { + return err + } + + case "nextToken": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.NextToken = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpListConfigurationRevisions struct { +} + +func (*awsRestjson1_deserializeOpListConfigurationRevisions) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpListConfigurationRevisions) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorListConfigurationRevisions(response, &metadata) + } + output := &ListConfigurationRevisionsOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentListConfigurationRevisionsOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorListConfigurationRevisions(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentListConfigurationRevisionsOutput(v **ListConfigurationRevisionsOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *ListConfigurationRevisionsOutput + if *v == nil { + sv = &ListConfigurationRevisionsOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "nextToken": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.NextToken = ptr.String(jtv) + } + + case "revisions": + if err := awsRestjson1_deserializeDocument__listOfConfigurationRevision(&sv.Revisions, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpListConfigurations struct { +} + +func (*awsRestjson1_deserializeOpListConfigurations) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpListConfigurations) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorListConfigurations(response, &metadata) + } + output := &ListConfigurationsOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentListConfigurationsOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorListConfigurations(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentListConfigurationsOutput(v **ListConfigurationsOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *ListConfigurationsOutput + if *v == nil { + sv = &ListConfigurationsOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "configurations": + if err := awsRestjson1_deserializeDocument__listOfConfiguration(&sv.Configurations, value); err != nil { + return err + } + + case "nextToken": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.NextToken = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpListKafkaVersions struct { +} + +func (*awsRestjson1_deserializeOpListKafkaVersions) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpListKafkaVersions) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorListKafkaVersions(response, &metadata) + } + output := &ListKafkaVersionsOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentListKafkaVersionsOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorListKafkaVersions(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentListKafkaVersionsOutput(v **ListKafkaVersionsOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *ListKafkaVersionsOutput + if *v == nil { + sv = &ListKafkaVersionsOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "kafkaVersions": + if err := awsRestjson1_deserializeDocument__listOfKafkaVersion(&sv.KafkaVersions, value); err != nil { + return err + } + + case "nextToken": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.NextToken = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpListNodes struct { +} + +func (*awsRestjson1_deserializeOpListNodes) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpListNodes) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorListNodes(response, &metadata) + } + output := &ListNodesOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentListNodesOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorListNodes(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentListNodesOutput(v **ListNodesOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *ListNodesOutput + if *v == nil { + sv = &ListNodesOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "nextToken": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.NextToken = ptr.String(jtv) + } + + case "nodeInfoList": + if err := awsRestjson1_deserializeDocument__listOfNodeInfo(&sv.NodeInfoList, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpListReplicators struct { +} + +func (*awsRestjson1_deserializeOpListReplicators) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpListReplicators) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorListReplicators(response, &metadata) + } + output := &ListReplicatorsOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentListReplicatorsOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorListReplicators(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("TooManyRequestsException", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentListReplicatorsOutput(v **ListReplicatorsOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *ListReplicatorsOutput + if *v == nil { + sv = &ListReplicatorsOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "nextToken": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.NextToken = ptr.String(jtv) + } + + case "replicators": + if err := awsRestjson1_deserializeDocument__listOfReplicatorSummary(&sv.Replicators, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpListScramSecrets struct { +} + +func (*awsRestjson1_deserializeOpListScramSecrets) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpListScramSecrets) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorListScramSecrets(response, &metadata) + } + output := &ListScramSecretsOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentListScramSecretsOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorListScramSecrets(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("TooManyRequestsException", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentListScramSecretsOutput(v **ListScramSecretsOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *ListScramSecretsOutput + if *v == nil { + sv = &ListScramSecretsOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "nextToken": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.NextToken = ptr.String(jtv) + } + + case "secretArnList": + if err := awsRestjson1_deserializeDocument__listOf__string(&sv.SecretArnList, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpListTagsForResource struct { +} + +func (*awsRestjson1_deserializeOpListTagsForResource) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpListTagsForResource) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorListTagsForResource(response, &metadata) + } + output := &ListTagsForResourceOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentListTagsForResourceOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorListTagsForResource(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentListTagsForResourceOutput(v **ListTagsForResourceOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *ListTagsForResourceOutput + if *v == nil { + sv = &ListTagsForResourceOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "tags": + if err := awsRestjson1_deserializeDocument__mapOf__string(&sv.Tags, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpListTopics struct { +} + +func (*awsRestjson1_deserializeOpListTopics) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpListTopics) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorListTopics(response, &metadata) + } + output := &ListTopicsOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentListTopicsOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorListTopics(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentListTopicsOutput(v **ListTopicsOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *ListTopicsOutput + if *v == nil { + sv = &ListTopicsOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "nextToken": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.NextToken = ptr.String(jtv) + } + + case "topics": + if err := awsRestjson1_deserializeDocument__listOfTopicInfo(&sv.Topics, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpListVpcConnections struct { +} + +func (*awsRestjson1_deserializeOpListVpcConnections) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpListVpcConnections) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorListVpcConnections(response, &metadata) + } + output := &ListVpcConnectionsOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentListVpcConnectionsOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorListVpcConnections(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentListVpcConnectionsOutput(v **ListVpcConnectionsOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *ListVpcConnectionsOutput + if *v == nil { + sv = &ListVpcConnectionsOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "nextToken": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.NextToken = ptr.String(jtv) + } + + case "vpcConnections": + if err := awsRestjson1_deserializeDocument__listOfVpcConnection(&sv.VpcConnections, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpPutClusterPolicy struct { +} + +func (*awsRestjson1_deserializeOpPutClusterPolicy) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpPutClusterPolicy) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorPutClusterPolicy(response, &metadata) + } + output := &PutClusterPolicyOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentPutClusterPolicyOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorPutClusterPolicy(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentPutClusterPolicyOutput(v **PutClusterPolicyOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *PutClusterPolicyOutput + if *v == nil { + sv = &PutClusterPolicyOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "currentVersion": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.CurrentVersion = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpRebootBroker struct { +} + +func (*awsRestjson1_deserializeOpRebootBroker) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpRebootBroker) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorRebootBroker(response, &metadata) + } + output := &RebootBrokerOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentRebootBrokerOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorRebootBroker(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("TooManyRequestsException", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentRebootBrokerOutput(v **RebootBrokerOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *RebootBrokerOutput + if *v == nil { + sv = &RebootBrokerOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterArn = ptr.String(jtv) + } + + case "clusterOperationArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterOperationArn = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpRejectClientVpcConnection struct { +} + +func (*awsRestjson1_deserializeOpRejectClientVpcConnection) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpRejectClientVpcConnection) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorRejectClientVpcConnection(response, &metadata) + } + output := &RejectClientVpcConnectionOutput{} + out.Result = output + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorRejectClientVpcConnection(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsRestjson1_deserializeOpTagResource struct { +} + +func (*awsRestjson1_deserializeOpTagResource) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpTagResource) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorTagResource(response, &metadata) + } + output := &TagResourceOutput{} + out.Result = output + + if _, err = io.Copy(ioutil.Discard, response.Body); err != nil { + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to discard response body, %w", err), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorTagResource(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsRestjson1_deserializeOpUntagResource struct { +} + +func (*awsRestjson1_deserializeOpUntagResource) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpUntagResource) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorUntagResource(response, &metadata) + } + output := &UntagResourceOutput{} + out.Result = output + + if _, err = io.Copy(ioutil.Discard, response.Body); err != nil { + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to discard response body, %w", err), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorUntagResource(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsRestjson1_deserializeOpUpdateBrokerCount struct { +} + +func (*awsRestjson1_deserializeOpUpdateBrokerCount) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpUpdateBrokerCount) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorUpdateBrokerCount(response, &metadata) + } + output := &UpdateBrokerCountOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentUpdateBrokerCountOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorUpdateBrokerCount(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentUpdateBrokerCountOutput(v **UpdateBrokerCountOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *UpdateBrokerCountOutput + if *v == nil { + sv = &UpdateBrokerCountOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterArn = ptr.String(jtv) + } + + case "clusterOperationArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterOperationArn = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpUpdateBrokerStorage struct { +} + +func (*awsRestjson1_deserializeOpUpdateBrokerStorage) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpUpdateBrokerStorage) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorUpdateBrokerStorage(response, &metadata) + } + output := &UpdateBrokerStorageOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentUpdateBrokerStorageOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorUpdateBrokerStorage(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentUpdateBrokerStorageOutput(v **UpdateBrokerStorageOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *UpdateBrokerStorageOutput + if *v == nil { + sv = &UpdateBrokerStorageOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterArn = ptr.String(jtv) + } + + case "clusterOperationArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterOperationArn = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpUpdateBrokerType struct { +} + +func (*awsRestjson1_deserializeOpUpdateBrokerType) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpUpdateBrokerType) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorUpdateBrokerType(response, &metadata) + } + output := &UpdateBrokerTypeOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentUpdateBrokerTypeOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorUpdateBrokerType(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("TooManyRequestsException", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentUpdateBrokerTypeOutput(v **UpdateBrokerTypeOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *UpdateBrokerTypeOutput + if *v == nil { + sv = &UpdateBrokerTypeOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterArn = ptr.String(jtv) + } + + case "clusterOperationArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterOperationArn = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpUpdateClusterConfiguration struct { +} + +func (*awsRestjson1_deserializeOpUpdateClusterConfiguration) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpUpdateClusterConfiguration) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorUpdateClusterConfiguration(response, &metadata) + } + output := &UpdateClusterConfigurationOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentUpdateClusterConfigurationOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorUpdateClusterConfiguration(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentUpdateClusterConfigurationOutput(v **UpdateClusterConfigurationOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *UpdateClusterConfigurationOutput + if *v == nil { + sv = &UpdateClusterConfigurationOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterArn = ptr.String(jtv) + } + + case "clusterOperationArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterOperationArn = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpUpdateClusterKafkaVersion struct { +} + +func (*awsRestjson1_deserializeOpUpdateClusterKafkaVersion) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpUpdateClusterKafkaVersion) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorUpdateClusterKafkaVersion(response, &metadata) + } + output := &UpdateClusterKafkaVersionOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentUpdateClusterKafkaVersionOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorUpdateClusterKafkaVersion(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("TooManyRequestsException", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentUpdateClusterKafkaVersionOutput(v **UpdateClusterKafkaVersionOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *UpdateClusterKafkaVersionOutput + if *v == nil { + sv = &UpdateClusterKafkaVersionOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterArn = ptr.String(jtv) + } + + case "clusterOperationArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterOperationArn = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpUpdateConfiguration struct { +} + +func (*awsRestjson1_deserializeOpUpdateConfiguration) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpUpdateConfiguration) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorUpdateConfiguration(response, &metadata) + } + output := &UpdateConfigurationOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentUpdateConfigurationOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorUpdateConfiguration(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentUpdateConfigurationOutput(v **UpdateConfigurationOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *UpdateConfigurationOutput + if *v == nil { + sv = &UpdateConfigurationOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "arn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Arn = ptr.String(jtv) + } + + case "latestRevision": + if err := awsRestjson1_deserializeDocumentConfigurationRevision(&sv.LatestRevision, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpUpdateConnectivity struct { +} + +func (*awsRestjson1_deserializeOpUpdateConnectivity) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpUpdateConnectivity) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorUpdateConnectivity(response, &metadata) + } + output := &UpdateConnectivityOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentUpdateConnectivityOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorUpdateConnectivity(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentUpdateConnectivityOutput(v **UpdateConnectivityOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *UpdateConnectivityOutput + if *v == nil { + sv = &UpdateConnectivityOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterArn = ptr.String(jtv) + } + + case "clusterOperationArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterOperationArn = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpUpdateMonitoring struct { +} + +func (*awsRestjson1_deserializeOpUpdateMonitoring) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpUpdateMonitoring) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorUpdateMonitoring(response, &metadata) + } + output := &UpdateMonitoringOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentUpdateMonitoringOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorUpdateMonitoring(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentUpdateMonitoringOutput(v **UpdateMonitoringOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *UpdateMonitoringOutput + if *v == nil { + sv = &UpdateMonitoringOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterArn = ptr.String(jtv) + } + + case "clusterOperationArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterOperationArn = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpUpdateRebalancing struct { +} + +func (*awsRestjson1_deserializeOpUpdateRebalancing) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpUpdateRebalancing) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorUpdateRebalancing(response, &metadata) + } + output := &UpdateRebalancingOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentUpdateRebalancingOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorUpdateRebalancing(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("TooManyRequestsException", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentUpdateRebalancingOutput(v **UpdateRebalancingOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *UpdateRebalancingOutput + if *v == nil { + sv = &UpdateRebalancingOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterArn = ptr.String(jtv) + } + + case "clusterOperationArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterOperationArn = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpUpdateReplicationInfo struct { +} + +func (*awsRestjson1_deserializeOpUpdateReplicationInfo) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpUpdateReplicationInfo) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorUpdateReplicationInfo(response, &metadata) + } + output := &UpdateReplicationInfoOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentUpdateReplicationInfoOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorUpdateReplicationInfo(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("TooManyRequestsException", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentUpdateReplicationInfoOutput(v **UpdateReplicationInfoOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *UpdateReplicationInfoOutput + if *v == nil { + sv = &UpdateReplicationInfoOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "replicatorArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ReplicatorArn = ptr.String(jtv) + } + + case "replicatorState": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected ReplicatorState to be of type string, got %T instead", value) + } + sv.ReplicatorState = types.ReplicatorState(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpUpdateSecurity struct { +} + +func (*awsRestjson1_deserializeOpUpdateSecurity) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpUpdateSecurity) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorUpdateSecurity(response, &metadata) + } + output := &UpdateSecurityOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentUpdateSecurityOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorUpdateSecurity(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("TooManyRequestsException", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentUpdateSecurityOutput(v **UpdateSecurityOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *UpdateSecurityOutput + if *v == nil { + sv = &UpdateSecurityOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterArn = ptr.String(jtv) + } + + case "clusterOperationArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterOperationArn = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpUpdateStorage struct { +} + +func (*awsRestjson1_deserializeOpUpdateStorage) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpUpdateStorage) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorUpdateStorage(response, &metadata) + } + output := &UpdateStorageOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentUpdateStorageOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorUpdateStorage(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("BadRequestException", errorCode): + return awsRestjson1_deserializeErrorBadRequestException(response, errorBody) + + case strings.EqualFold("ForbiddenException", errorCode): + return awsRestjson1_deserializeErrorForbiddenException(response, errorBody) + + case strings.EqualFold("InternalServerErrorException", errorCode): + return awsRestjson1_deserializeErrorInternalServerErrorException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsRestjson1_deserializeErrorNotFoundException(response, errorBody) + + case strings.EqualFold("ServiceUnavailableException", errorCode): + return awsRestjson1_deserializeErrorServiceUnavailableException(response, errorBody) + + case strings.EqualFold("TooManyRequestsException", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsException(response, errorBody) + + case strings.EqualFold("UnauthorizedException", errorCode): + return awsRestjson1_deserializeErrorUnauthorizedException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentUpdateStorageOutput(v **UpdateStorageOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *UpdateStorageOutput + if *v == nil { + sv = &UpdateStorageOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterArn = ptr.String(jtv) + } + + case "clusterOperationArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterOperationArn = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeErrorBadRequestException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.BadRequestException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + err := awsRestjson1_deserializeDocumentBadRequestException(&output, shape) + + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + + return output +} + +func awsRestjson1_deserializeErrorConflictException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.ConflictException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + err := awsRestjson1_deserializeDocumentConflictException(&output, shape) + + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + + return output +} + +func awsRestjson1_deserializeErrorForbiddenException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.ForbiddenException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + err := awsRestjson1_deserializeDocumentForbiddenException(&output, shape) + + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + + return output +} + +func awsRestjson1_deserializeErrorInternalServerErrorException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.InternalServerErrorException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + err := awsRestjson1_deserializeDocumentInternalServerErrorException(&output, shape) + + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + + return output +} + +func awsRestjson1_deserializeErrorNotFoundException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.NotFoundException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + err := awsRestjson1_deserializeDocumentNotFoundException(&output, shape) + + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + + return output +} + +func awsRestjson1_deserializeErrorServiceUnavailableException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.ServiceUnavailableException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + err := awsRestjson1_deserializeDocumentServiceUnavailableException(&output, shape) + + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + + return output +} + +func awsRestjson1_deserializeErrorTooManyRequestsException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.TooManyRequestsException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + err := awsRestjson1_deserializeDocumentTooManyRequestsException(&output, shape) + + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + + return output +} + +func awsRestjson1_deserializeErrorUnauthorizedException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.UnauthorizedException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + err := awsRestjson1_deserializeDocumentUnauthorizedException(&output, shape) + + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + + return output +} + +func awsRestjson1_deserializeDocument__listOf__double(v *[]float64, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []float64 + if *v == nil { + cv = []float64{} + } else { + cv = *v + } + + for _, value := range shape { + var col float64 + if value != nil { + switch jtv := value.(type) { + case json.Number: + f64, err := jtv.Float64() + if err != nil { + return err + } + col = f64 + + case string: + var f64 float64 + switch { + case strings.EqualFold(jtv, "NaN"): + f64 = math.NaN() + + case strings.EqualFold(jtv, "Infinity"): + f64 = math.Inf(1) + + case strings.EqualFold(jtv, "-Infinity"): + f64 = math.Inf(-1) + + default: + return fmt.Errorf("unknown JSON number value: %s", jtv) + + } + col = f64 + + default: + return fmt.Errorf("expected __double to be a JSON Number, got %T instead", value) + + } + } + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOf__integer(v *[]int32, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []int32 + if *v == nil { + cv = []int32{} + } else { + cv = *v + } + + for _, value := range shape { + var col int32 + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected __integer to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + col = int32(i64) + } + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOf__string(v *[]string, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []string + if *v == nil { + cv = []string{} + } else { + cv = *v + } + + for _, value := range shape { + var col string + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + col = jtv + } + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOf__stringMax249(v *[]string, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []string + if *v == nil { + cv = []string{} + } else { + cv = *v + } + + for _, value := range shape { + var col string + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __stringMax249 to be of type string, got %T instead", value) + } + col = jtv + } + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOf__stringMax256(v *[]string, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []string + if *v == nil { + cv = []string{} + } else { + cv = *v + } + + for _, value := range shape { + var col string + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __stringMax256 to be of type string, got %T instead", value) + } + col = jtv + } + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOfBrokerEBSVolumeInfo(v *[]types.BrokerEBSVolumeInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.BrokerEBSVolumeInfo + if *v == nil { + cv = []types.BrokerEBSVolumeInfo{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.BrokerEBSVolumeInfo + destAddr := &col + if err := awsRestjson1_deserializeDocumentBrokerEBSVolumeInfo(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOfClientVpcConnection(v *[]types.ClientVpcConnection, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.ClientVpcConnection + if *v == nil { + cv = []types.ClientVpcConnection{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.ClientVpcConnection + destAddr := &col + if err := awsRestjson1_deserializeDocumentClientVpcConnection(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOfCluster(v *[]types.Cluster, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.Cluster + if *v == nil { + cv = []types.Cluster{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.Cluster + destAddr := &col + if err := awsRestjson1_deserializeDocumentCluster(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOfClusterInfo(v *[]types.ClusterInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.ClusterInfo + if *v == nil { + cv = []types.ClusterInfo{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.ClusterInfo + destAddr := &col + if err := awsRestjson1_deserializeDocumentClusterInfo(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOfClusterOperationInfo(v *[]types.ClusterOperationInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.ClusterOperationInfo + if *v == nil { + cv = []types.ClusterOperationInfo{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.ClusterOperationInfo + destAddr := &col + if err := awsRestjson1_deserializeDocumentClusterOperationInfo(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOfClusterOperationStep(v *[]types.ClusterOperationStep, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.ClusterOperationStep + if *v == nil { + cv = []types.ClusterOperationStep{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.ClusterOperationStep + destAddr := &col + if err := awsRestjson1_deserializeDocumentClusterOperationStep(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOfClusterOperationV2Summary(v *[]types.ClusterOperationV2Summary, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.ClusterOperationV2Summary + if *v == nil { + cv = []types.ClusterOperationV2Summary{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.ClusterOperationV2Summary + destAddr := &col + if err := awsRestjson1_deserializeDocumentClusterOperationV2Summary(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOfCompatibleKafkaVersion(v *[]types.CompatibleKafkaVersion, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.CompatibleKafkaVersion + if *v == nil { + cv = []types.CompatibleKafkaVersion{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.CompatibleKafkaVersion + destAddr := &col + if err := awsRestjson1_deserializeDocumentCompatibleKafkaVersion(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOfConfiguration(v *[]types.Configuration, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.Configuration + if *v == nil { + cv = []types.Configuration{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.Configuration + destAddr := &col + if err := awsRestjson1_deserializeDocumentConfiguration(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOfConfigurationRevision(v *[]types.ConfigurationRevision, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.ConfigurationRevision + if *v == nil { + cv = []types.ConfigurationRevision{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.ConfigurationRevision + destAddr := &col + if err := awsRestjson1_deserializeDocumentConfigurationRevision(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOfKafkaClusterDescription(v *[]types.KafkaClusterDescription, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.KafkaClusterDescription + if *v == nil { + cv = []types.KafkaClusterDescription{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.KafkaClusterDescription + destAddr := &col + if err := awsRestjson1_deserializeDocumentKafkaClusterDescription(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOfKafkaClusterSummary(v *[]types.KafkaClusterSummary, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.KafkaClusterSummary + if *v == nil { + cv = []types.KafkaClusterSummary{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.KafkaClusterSummary + destAddr := &col + if err := awsRestjson1_deserializeDocumentKafkaClusterSummary(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOfKafkaVersion(v *[]types.KafkaVersion, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.KafkaVersion + if *v == nil { + cv = []types.KafkaVersion{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.KafkaVersion + destAddr := &col + if err := awsRestjson1_deserializeDocumentKafkaVersion(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOfNodeInfo(v *[]types.NodeInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.NodeInfo + if *v == nil { + cv = []types.NodeInfo{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.NodeInfo + destAddr := &col + if err := awsRestjson1_deserializeDocumentNodeInfo(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOfReplicationInfoDescription(v *[]types.ReplicationInfoDescription, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.ReplicationInfoDescription + if *v == nil { + cv = []types.ReplicationInfoDescription{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.ReplicationInfoDescription + destAddr := &col + if err := awsRestjson1_deserializeDocumentReplicationInfoDescription(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOfReplicationInfoSummary(v *[]types.ReplicationInfoSummary, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.ReplicationInfoSummary + if *v == nil { + cv = []types.ReplicationInfoSummary{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.ReplicationInfoSummary + destAddr := &col + if err := awsRestjson1_deserializeDocumentReplicationInfoSummary(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOfReplicatorSummary(v *[]types.ReplicatorSummary, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.ReplicatorSummary + if *v == nil { + cv = []types.ReplicatorSummary{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.ReplicatorSummary + destAddr := &col + if err := awsRestjson1_deserializeDocumentReplicatorSummary(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOfTopicInfo(v *[]types.TopicInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.TopicInfo + if *v == nil { + cv = []types.TopicInfo{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.TopicInfo + destAddr := &col + if err := awsRestjson1_deserializeDocumentTopicInfo(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOfTopicPartitionInfo(v *[]types.TopicPartitionInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.TopicPartitionInfo + if *v == nil { + cv = []types.TopicPartitionInfo{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.TopicPartitionInfo + destAddr := &col + if err := awsRestjson1_deserializeDocumentTopicPartitionInfo(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOfUnprocessedScramSecret(v *[]types.UnprocessedScramSecret, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.UnprocessedScramSecret + if *v == nil { + cv = []types.UnprocessedScramSecret{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.UnprocessedScramSecret + destAddr := &col + if err := awsRestjson1_deserializeDocumentUnprocessedScramSecret(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOfVpcConfig(v *[]types.VpcConfig, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.VpcConfig + if *v == nil { + cv = []types.VpcConfig{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.VpcConfig + destAddr := &col + if err := awsRestjson1_deserializeDocumentVpcConfig(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__listOfVpcConnection(v *[]types.VpcConnection, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.VpcConnection + if *v == nil { + cv = []types.VpcConnection{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.VpcConnection + destAddr := &col + if err := awsRestjson1_deserializeDocumentVpcConnection(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocument__mapOf__string(v *map[string]string, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var mv map[string]string + if *v == nil { + mv = map[string]string{} + } else { + mv = *v + } + + for key, value := range shape { + var parsedVal string + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + parsedVal = jtv + } + mv[key] = parsedVal + + } + *v = mv + return nil +} + +func awsRestjson1_deserializeDocumentAmazonMskCluster(v **types.AmazonMskCluster, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.AmazonMskCluster + if *v == nil { + sv = &types.AmazonMskCluster{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "mskClusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.MskClusterArn = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentBadRequestException(v **types.BadRequestException, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.BadRequestException + if *v == nil { + sv = &types.BadRequestException{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "invalidParameter": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.InvalidParameter = ptr.String(jtv) + } + + case "message", "Message": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Message = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentBrokerCountUpdateInfo(v **types.BrokerCountUpdateInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.BrokerCountUpdateInfo + if *v == nil { + sv = &types.BrokerCountUpdateInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "createdBrokerIds": + if err := awsRestjson1_deserializeDocument__listOf__double(&sv.CreatedBrokerIds, value); err != nil { + return err + } + + case "deletedBrokerIds": + if err := awsRestjson1_deserializeDocument__listOf__double(&sv.DeletedBrokerIds, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentBrokerEBSVolumeInfo(v **types.BrokerEBSVolumeInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.BrokerEBSVolumeInfo + if *v == nil { + sv = &types.BrokerEBSVolumeInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "kafkaBrokerNodeId": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.KafkaBrokerNodeId = ptr.String(jtv) + } + + case "provisionedThroughput": + if err := awsRestjson1_deserializeDocumentProvisionedThroughput(&sv.ProvisionedThroughput, value); err != nil { + return err + } + + case "volumeSizeGB": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected __integer to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + sv.VolumeSizeGB = ptr.Int32(int32(i64)) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentBrokerLogs(v **types.BrokerLogs, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.BrokerLogs + if *v == nil { + sv = &types.BrokerLogs{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "cloudWatchLogs": + if err := awsRestjson1_deserializeDocumentCloudWatchLogs(&sv.CloudWatchLogs, value); err != nil { + return err + } + + case "firehose": + if err := awsRestjson1_deserializeDocumentFirehose(&sv.Firehose, value); err != nil { + return err + } + + case "s3": + if err := awsRestjson1_deserializeDocumentS3(&sv.S3, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentBrokerNodeGroupInfo(v **types.BrokerNodeGroupInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.BrokerNodeGroupInfo + if *v == nil { + sv = &types.BrokerNodeGroupInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "brokerAZDistribution": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected BrokerAZDistribution to be of type string, got %T instead", value) + } + sv.BrokerAZDistribution = types.BrokerAZDistribution(jtv) + } + + case "clientSubnets": + if err := awsRestjson1_deserializeDocument__listOf__string(&sv.ClientSubnets, value); err != nil { + return err + } + + case "connectivityInfo": + if err := awsRestjson1_deserializeDocumentConnectivityInfo(&sv.ConnectivityInfo, value); err != nil { + return err + } + + case "instanceType": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __stringMin5Max32 to be of type string, got %T instead", value) + } + sv.InstanceType = ptr.String(jtv) + } + + case "securityGroups": + if err := awsRestjson1_deserializeDocument__listOf__string(&sv.SecurityGroups, value); err != nil { + return err + } + + case "storageInfo": + if err := awsRestjson1_deserializeDocumentStorageInfo(&sv.StorageInfo, value); err != nil { + return err + } + + case "zoneIds": + if err := awsRestjson1_deserializeDocument__listOf__string(&sv.ZoneIds, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentBrokerNodeInfo(v **types.BrokerNodeInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.BrokerNodeInfo + if *v == nil { + sv = &types.BrokerNodeInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "attachedENIId": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.AttachedENIId = ptr.String(jtv) + } + + case "brokerId": + if value != nil { + switch jtv := value.(type) { + case json.Number: + f64, err := jtv.Float64() + if err != nil { + return err + } + sv.BrokerId = ptr.Float64(f64) + + case string: + var f64 float64 + switch { + case strings.EqualFold(jtv, "NaN"): + f64 = math.NaN() + + case strings.EqualFold(jtv, "Infinity"): + f64 = math.Inf(1) + + case strings.EqualFold(jtv, "-Infinity"): + f64 = math.Inf(-1) + + default: + return fmt.Errorf("unknown JSON number value: %s", jtv) + + } + sv.BrokerId = ptr.Float64(f64) + + default: + return fmt.Errorf("expected __double to be a JSON Number, got %T instead", value) + + } + } + + case "clientSubnet": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClientSubnet = ptr.String(jtv) + } + + case "clientVpcIpAddress": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClientVpcIpAddress = ptr.String(jtv) + } + + case "currentBrokerSoftwareInfo": + if err := awsRestjson1_deserializeDocumentBrokerSoftwareInfo(&sv.CurrentBrokerSoftwareInfo, value); err != nil { + return err + } + + case "endpoints": + if err := awsRestjson1_deserializeDocument__listOf__string(&sv.Endpoints, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentBrokerSoftwareInfo(v **types.BrokerSoftwareInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.BrokerSoftwareInfo + if *v == nil { + sv = &types.BrokerSoftwareInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "configurationArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ConfigurationArn = ptr.String(jtv) + } + + case "configurationRevision": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected __long to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + sv.ConfigurationRevision = ptr.Int64(i64) + } + + case "kafkaVersion": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.KafkaVersion = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentClientAuthentication(v **types.ClientAuthentication, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ClientAuthentication + if *v == nil { + sv = &types.ClientAuthentication{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "sasl": + if err := awsRestjson1_deserializeDocumentSasl(&sv.Sasl, value); err != nil { + return err + } + + case "tls": + if err := awsRestjson1_deserializeDocumentTls(&sv.Tls, value); err != nil { + return err + } + + case "unauthenticated": + if err := awsRestjson1_deserializeDocumentUnauthenticated(&sv.Unauthenticated, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentClientVpcConnection(v **types.ClientVpcConnection, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ClientVpcConnection + if *v == nil { + sv = &types.ClientVpcConnection{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "authentication": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Authentication = ptr.String(jtv) + } + + case "creationTime": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __timestampIso8601 to be of type string, got %T instead", value) + } + t, err := smithytime.ParseDateTime(jtv) + if err != nil { + return err + } + sv.CreationTime = ptr.Time(t) + } + + case "owner": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Owner = ptr.String(jtv) + } + + case "state": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected VpcConnectionState to be of type string, got %T instead", value) + } + sv.State = types.VpcConnectionState(jtv) + } + + case "vpcConnectionArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.VpcConnectionArn = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentCloudWatchLogs(v **types.CloudWatchLogs, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.CloudWatchLogs + if *v == nil { + sv = &types.CloudWatchLogs{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "enabled": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.Enabled = ptr.Bool(jtv) + } + + case "logGroup": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.LogGroup = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentCluster(v **types.Cluster, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.Cluster + if *v == nil { + sv = &types.Cluster{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "activeOperationArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ActiveOperationArn = ptr.String(jtv) + } + + case "clusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterArn = ptr.String(jtv) + } + + case "clusterName": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterName = ptr.String(jtv) + } + + case "clusterType": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected ClusterType to be of type string, got %T instead", value) + } + sv.ClusterType = types.ClusterType(jtv) + } + + case "creationTime": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __timestampIso8601 to be of type string, got %T instead", value) + } + t, err := smithytime.ParseDateTime(jtv) + if err != nil { + return err + } + sv.CreationTime = ptr.Time(t) + } + + case "currentVersion": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.CurrentVersion = ptr.String(jtv) + } + + case "provisioned": + if err := awsRestjson1_deserializeDocumentProvisioned(&sv.Provisioned, value); err != nil { + return err + } + + case "serverless": + if err := awsRestjson1_deserializeDocumentServerless(&sv.Serverless, value); err != nil { + return err + } + + case "state": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected ClusterState to be of type string, got %T instead", value) + } + sv.State = types.ClusterState(jtv) + } + + case "stateInfo": + if err := awsRestjson1_deserializeDocumentStateInfo(&sv.StateInfo, value); err != nil { + return err + } + + case "tags": + if err := awsRestjson1_deserializeDocument__mapOf__string(&sv.Tags, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentClusterInfo(v **types.ClusterInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ClusterInfo + if *v == nil { + sv = &types.ClusterInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "activeOperationArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ActiveOperationArn = ptr.String(jtv) + } + + case "brokerNodeGroupInfo": + if err := awsRestjson1_deserializeDocumentBrokerNodeGroupInfo(&sv.BrokerNodeGroupInfo, value); err != nil { + return err + } + + case "clientAuthentication": + if err := awsRestjson1_deserializeDocumentClientAuthentication(&sv.ClientAuthentication, value); err != nil { + return err + } + + case "clusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterArn = ptr.String(jtv) + } + + case "clusterName": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterName = ptr.String(jtv) + } + + case "creationTime": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __timestampIso8601 to be of type string, got %T instead", value) + } + t, err := smithytime.ParseDateTime(jtv) + if err != nil { + return err + } + sv.CreationTime = ptr.Time(t) + } + + case "currentBrokerSoftwareInfo": + if err := awsRestjson1_deserializeDocumentBrokerSoftwareInfo(&sv.CurrentBrokerSoftwareInfo, value); err != nil { + return err + } + + case "currentVersion": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.CurrentVersion = ptr.String(jtv) + } + + case "customerActionStatus": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected CustomerActionStatus to be of type string, got %T instead", value) + } + sv.CustomerActionStatus = types.CustomerActionStatus(jtv) + } + + case "encryptionInfo": + if err := awsRestjson1_deserializeDocumentEncryptionInfo(&sv.EncryptionInfo, value); err != nil { + return err + } + + case "enhancedMonitoring": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected EnhancedMonitoring to be of type string, got %T instead", value) + } + sv.EnhancedMonitoring = types.EnhancedMonitoring(jtv) + } + + case "loggingInfo": + if err := awsRestjson1_deserializeDocumentLoggingInfo(&sv.LoggingInfo, value); err != nil { + return err + } + + case "numberOfBrokerNodes": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected __integer to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + sv.NumberOfBrokerNodes = ptr.Int32(int32(i64)) + } + + case "openMonitoring": + if err := awsRestjson1_deserializeDocumentOpenMonitoring(&sv.OpenMonitoring, value); err != nil { + return err + } + + case "rebalancing": + if err := awsRestjson1_deserializeDocumentRebalancing(&sv.Rebalancing, value); err != nil { + return err + } + + case "state": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected ClusterState to be of type string, got %T instead", value) + } + sv.State = types.ClusterState(jtv) + } + + case "stateInfo": + if err := awsRestjson1_deserializeDocumentStateInfo(&sv.StateInfo, value); err != nil { + return err + } + + case "storageMode": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected StorageMode to be of type string, got %T instead", value) + } + sv.StorageMode = types.StorageMode(jtv) + } + + case "tags": + if err := awsRestjson1_deserializeDocument__mapOf__string(&sv.Tags, value); err != nil { + return err + } + + case "zookeeperConnectString": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ZookeeperConnectString = ptr.String(jtv) + } + + case "zookeeperConnectStringTls": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ZookeeperConnectStringTls = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentClusterOperationInfo(v **types.ClusterOperationInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ClusterOperationInfo + if *v == nil { + sv = &types.ClusterOperationInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clientRequestId": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClientRequestId = ptr.String(jtv) + } + + case "clusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterArn = ptr.String(jtv) + } + + case "creationTime": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __timestampIso8601 to be of type string, got %T instead", value) + } + t, err := smithytime.ParseDateTime(jtv) + if err != nil { + return err + } + sv.CreationTime = ptr.Time(t) + } + + case "endTime": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __timestampIso8601 to be of type string, got %T instead", value) + } + t, err := smithytime.ParseDateTime(jtv) + if err != nil { + return err + } + sv.EndTime = ptr.Time(t) + } + + case "errorInfo": + if err := awsRestjson1_deserializeDocumentErrorInfo(&sv.ErrorInfo, value); err != nil { + return err + } + + case "operationArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.OperationArn = ptr.String(jtv) + } + + case "operationState": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.OperationState = ptr.String(jtv) + } + + case "operationSteps": + if err := awsRestjson1_deserializeDocument__listOfClusterOperationStep(&sv.OperationSteps, value); err != nil { + return err + } + + case "operationType": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.OperationType = ptr.String(jtv) + } + + case "sourceClusterInfo": + if err := awsRestjson1_deserializeDocumentMutableClusterInfo(&sv.SourceClusterInfo, value); err != nil { + return err + } + + case "targetClusterInfo": + if err := awsRestjson1_deserializeDocumentMutableClusterInfo(&sv.TargetClusterInfo, value); err != nil { + return err + } + + case "vpcConnectionInfo": + if err := awsRestjson1_deserializeDocumentVpcConnectionInfo(&sv.VpcConnectionInfo, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentClusterOperationStep(v **types.ClusterOperationStep, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ClusterOperationStep + if *v == nil { + sv = &types.ClusterOperationStep{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "stepInfo": + if err := awsRestjson1_deserializeDocumentClusterOperationStepInfo(&sv.StepInfo, value); err != nil { + return err + } + + case "stepName": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.StepName = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentClusterOperationStepInfo(v **types.ClusterOperationStepInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ClusterOperationStepInfo + if *v == nil { + sv = &types.ClusterOperationStepInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "stepStatus": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.StepStatus = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentClusterOperationV2(v **types.ClusterOperationV2, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ClusterOperationV2 + if *v == nil { + sv = &types.ClusterOperationV2{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterArn = ptr.String(jtv) + } + + case "clusterType": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected ClusterType to be of type string, got %T instead", value) + } + sv.ClusterType = types.ClusterType(jtv) + } + + case "endTime": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __timestampIso8601 to be of type string, got %T instead", value) + } + t, err := smithytime.ParseDateTime(jtv) + if err != nil { + return err + } + sv.EndTime = ptr.Time(t) + } + + case "errorInfo": + if err := awsRestjson1_deserializeDocumentErrorInfo(&sv.ErrorInfo, value); err != nil { + return err + } + + case "operationArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.OperationArn = ptr.String(jtv) + } + + case "operationState": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.OperationState = ptr.String(jtv) + } + + case "operationType": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.OperationType = ptr.String(jtv) + } + + case "provisioned": + if err := awsRestjson1_deserializeDocumentClusterOperationV2Provisioned(&sv.Provisioned, value); err != nil { + return err + } + + case "serverless": + if err := awsRestjson1_deserializeDocumentClusterOperationV2Serverless(&sv.Serverless, value); err != nil { + return err + } + + case "startTime": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __timestampIso8601 to be of type string, got %T instead", value) + } + t, err := smithytime.ParseDateTime(jtv) + if err != nil { + return err + } + sv.StartTime = ptr.Time(t) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentClusterOperationV2Provisioned(v **types.ClusterOperationV2Provisioned, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ClusterOperationV2Provisioned + if *v == nil { + sv = &types.ClusterOperationV2Provisioned{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "operationSteps": + if err := awsRestjson1_deserializeDocument__listOfClusterOperationStep(&sv.OperationSteps, value); err != nil { + return err + } + + case "sourceClusterInfo": + if err := awsRestjson1_deserializeDocumentMutableClusterInfo(&sv.SourceClusterInfo, value); err != nil { + return err + } + + case "targetClusterInfo": + if err := awsRestjson1_deserializeDocumentMutableClusterInfo(&sv.TargetClusterInfo, value); err != nil { + return err + } + + case "vpcConnectionInfo": + if err := awsRestjson1_deserializeDocumentVpcConnectionInfo(&sv.VpcConnectionInfo, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentClusterOperationV2Serverless(v **types.ClusterOperationV2Serverless, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ClusterOperationV2Serverless + if *v == nil { + sv = &types.ClusterOperationV2Serverless{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "vpcConnectionInfo": + if err := awsRestjson1_deserializeDocumentVpcConnectionInfoServerless(&sv.VpcConnectionInfo, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentClusterOperationV2Summary(v **types.ClusterOperationV2Summary, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ClusterOperationV2Summary + if *v == nil { + sv = &types.ClusterOperationV2Summary{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClusterArn = ptr.String(jtv) + } + + case "clusterType": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected ClusterType to be of type string, got %T instead", value) + } + sv.ClusterType = types.ClusterType(jtv) + } + + case "endTime": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __timestampIso8601 to be of type string, got %T instead", value) + } + t, err := smithytime.ParseDateTime(jtv) + if err != nil { + return err + } + sv.EndTime = ptr.Time(t) + } + + case "operationArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.OperationArn = ptr.String(jtv) + } + + case "operationState": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.OperationState = ptr.String(jtv) + } + + case "operationType": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.OperationType = ptr.String(jtv) + } + + case "startTime": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __timestampIso8601 to be of type string, got %T instead", value) + } + t, err := smithytime.ParseDateTime(jtv) + if err != nil { + return err + } + sv.StartTime = ptr.Time(t) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentCompatibleKafkaVersion(v **types.CompatibleKafkaVersion, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.CompatibleKafkaVersion + if *v == nil { + sv = &types.CompatibleKafkaVersion{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "sourceVersion": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.SourceVersion = ptr.String(jtv) + } + + case "targetVersions": + if err := awsRestjson1_deserializeDocument__listOf__string(&sv.TargetVersions, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentConfiguration(v **types.Configuration, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.Configuration + if *v == nil { + sv = &types.Configuration{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "arn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Arn = ptr.String(jtv) + } + + case "creationTime": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __timestampIso8601 to be of type string, got %T instead", value) + } + t, err := smithytime.ParseDateTime(jtv) + if err != nil { + return err + } + sv.CreationTime = ptr.Time(t) + } + + case "description": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Description = ptr.String(jtv) + } + + case "kafkaVersions": + if err := awsRestjson1_deserializeDocument__listOf__string(&sv.KafkaVersions, value); err != nil { + return err + } + + case "latestRevision": + if err := awsRestjson1_deserializeDocumentConfigurationRevision(&sv.LatestRevision, value); err != nil { + return err + } + + case "name": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Name = ptr.String(jtv) + } + + case "state": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected ConfigurationState to be of type string, got %T instead", value) + } + sv.State = types.ConfigurationState(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentConfigurationInfo(v **types.ConfigurationInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ConfigurationInfo + if *v == nil { + sv = &types.ConfigurationInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "arn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Arn = ptr.String(jtv) + } + + case "revision": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected __long to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + sv.Revision = ptr.Int64(i64) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentConfigurationRevision(v **types.ConfigurationRevision, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ConfigurationRevision + if *v == nil { + sv = &types.ConfigurationRevision{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "creationTime": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __timestampIso8601 to be of type string, got %T instead", value) + } + t, err := smithytime.ParseDateTime(jtv) + if err != nil { + return err + } + sv.CreationTime = ptr.Time(t) + } + + case "description": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Description = ptr.String(jtv) + } + + case "revision": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected __long to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + sv.Revision = ptr.Int64(i64) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentConflictException(v **types.ConflictException, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ConflictException + if *v == nil { + sv = &types.ConflictException{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "invalidParameter": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.InvalidParameter = ptr.String(jtv) + } + + case "message", "Message": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Message = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentConnectivityInfo(v **types.ConnectivityInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ConnectivityInfo + if *v == nil { + sv = &types.ConnectivityInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "publicAccess": + if err := awsRestjson1_deserializeDocumentPublicAccess(&sv.PublicAccess, value); err != nil { + return err + } + + case "vpcConnectivity": + if err := awsRestjson1_deserializeDocumentVpcConnectivity(&sv.VpcConnectivity, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentConsumerGroupReplication(v **types.ConsumerGroupReplication, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ConsumerGroupReplication + if *v == nil { + sv = &types.ConsumerGroupReplication{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "consumerGroupsToExclude": + if err := awsRestjson1_deserializeDocument__listOf__stringMax256(&sv.ConsumerGroupsToExclude, value); err != nil { + return err + } + + case "consumerGroupsToReplicate": + if err := awsRestjson1_deserializeDocument__listOf__stringMax256(&sv.ConsumerGroupsToReplicate, value); err != nil { + return err + } + + case "detectAndCopyNewConsumerGroups": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.DetectAndCopyNewConsumerGroups = ptr.Bool(jtv) + } + + case "synchroniseConsumerGroupOffsets": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.SynchroniseConsumerGroupOffsets = ptr.Bool(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentControllerNodeInfo(v **types.ControllerNodeInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ControllerNodeInfo + if *v == nil { + sv = &types.ControllerNodeInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "endpoints": + if err := awsRestjson1_deserializeDocument__listOf__string(&sv.Endpoints, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentEBSStorageInfo(v **types.EBSStorageInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.EBSStorageInfo + if *v == nil { + sv = &types.EBSStorageInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "provisionedThroughput": + if err := awsRestjson1_deserializeDocumentProvisionedThroughput(&sv.ProvisionedThroughput, value); err != nil { + return err + } + + case "volumeSize": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected __integerMin1Max16384 to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + sv.VolumeSize = ptr.Int32(int32(i64)) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentEncryptionAtRest(v **types.EncryptionAtRest, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.EncryptionAtRest + if *v == nil { + sv = &types.EncryptionAtRest{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "dataVolumeKMSKeyId": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.DataVolumeKMSKeyId = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentEncryptionInfo(v **types.EncryptionInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.EncryptionInfo + if *v == nil { + sv = &types.EncryptionInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "encryptionAtRest": + if err := awsRestjson1_deserializeDocumentEncryptionAtRest(&sv.EncryptionAtRest, value); err != nil { + return err + } + + case "encryptionInTransit": + if err := awsRestjson1_deserializeDocumentEncryptionInTransit(&sv.EncryptionInTransit, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentEncryptionInTransit(v **types.EncryptionInTransit, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.EncryptionInTransit + if *v == nil { + sv = &types.EncryptionInTransit{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clientBroker": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected ClientBroker to be of type string, got %T instead", value) + } + sv.ClientBroker = types.ClientBroker(jtv) + } + + case "inCluster": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.InCluster = ptr.Bool(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentErrorInfo(v **types.ErrorInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ErrorInfo + if *v == nil { + sv = &types.ErrorInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "errorCode": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ErrorCode = ptr.String(jtv) + } + + case "errorString": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ErrorString = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentFirehose(v **types.Firehose, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.Firehose + if *v == nil { + sv = &types.Firehose{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "deliveryStream": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.DeliveryStream = ptr.String(jtv) + } + + case "enabled": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.Enabled = ptr.Bool(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentForbiddenException(v **types.ForbiddenException, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ForbiddenException + if *v == nil { + sv = &types.ForbiddenException{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "invalidParameter": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.InvalidParameter = ptr.String(jtv) + } + + case "message", "Message": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Message = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentIam(v **types.Iam, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.Iam + if *v == nil { + sv = &types.Iam{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "enabled": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.Enabled = ptr.Bool(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentInternalServerErrorException(v **types.InternalServerErrorException, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.InternalServerErrorException + if *v == nil { + sv = &types.InternalServerErrorException{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "invalidParameter": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.InvalidParameter = ptr.String(jtv) + } + + case "message", "Message": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Message = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentJmxExporter(v **types.JmxExporter, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.JmxExporter + if *v == nil { + sv = &types.JmxExporter{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "enabledInBroker": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.EnabledInBroker = ptr.Bool(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentJmxExporterInfo(v **types.JmxExporterInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.JmxExporterInfo + if *v == nil { + sv = &types.JmxExporterInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "enabledInBroker": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.EnabledInBroker = ptr.Bool(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentKafkaClusterClientVpcConfig(v **types.KafkaClusterClientVpcConfig, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.KafkaClusterClientVpcConfig + if *v == nil { + sv = &types.KafkaClusterClientVpcConfig{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "securityGroupIds": + if err := awsRestjson1_deserializeDocument__listOf__string(&sv.SecurityGroupIds, value); err != nil { + return err + } + + case "subnetIds": + if err := awsRestjson1_deserializeDocument__listOf__string(&sv.SubnetIds, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentKafkaClusterDescription(v **types.KafkaClusterDescription, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.KafkaClusterDescription + if *v == nil { + sv = &types.KafkaClusterDescription{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "amazonMskCluster": + if err := awsRestjson1_deserializeDocumentAmazonMskCluster(&sv.AmazonMskCluster, value); err != nil { + return err + } + + case "kafkaClusterAlias": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.KafkaClusterAlias = ptr.String(jtv) + } + + case "vpcConfig": + if err := awsRestjson1_deserializeDocumentKafkaClusterClientVpcConfig(&sv.VpcConfig, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentKafkaClusterSummary(v **types.KafkaClusterSummary, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.KafkaClusterSummary + if *v == nil { + sv = &types.KafkaClusterSummary{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "amazonMskCluster": + if err := awsRestjson1_deserializeDocumentAmazonMskCluster(&sv.AmazonMskCluster, value); err != nil { + return err + } + + case "kafkaClusterAlias": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.KafkaClusterAlias = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentKafkaVersion(v **types.KafkaVersion, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.KafkaVersion + if *v == nil { + sv = &types.KafkaVersion{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "status": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected KafkaVersionStatus to be of type string, got %T instead", value) + } + sv.Status = types.KafkaVersionStatus(jtv) + } + + case "version": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Version = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentLoggingInfo(v **types.LoggingInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.LoggingInfo + if *v == nil { + sv = &types.LoggingInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "brokerLogs": + if err := awsRestjson1_deserializeDocumentBrokerLogs(&sv.BrokerLogs, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentMutableClusterInfo(v **types.MutableClusterInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.MutableClusterInfo + if *v == nil { + sv = &types.MutableClusterInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "brokerCountUpdateInfo": + if err := awsRestjson1_deserializeDocumentBrokerCountUpdateInfo(&sv.BrokerCountUpdateInfo, value); err != nil { + return err + } + + case "brokerEBSVolumeInfo": + if err := awsRestjson1_deserializeDocument__listOfBrokerEBSVolumeInfo(&sv.BrokerEBSVolumeInfo, value); err != nil { + return err + } + + case "clientAuthentication": + if err := awsRestjson1_deserializeDocumentClientAuthentication(&sv.ClientAuthentication, value); err != nil { + return err + } + + case "configurationInfo": + if err := awsRestjson1_deserializeDocumentConfigurationInfo(&sv.ConfigurationInfo, value); err != nil { + return err + } + + case "connectivityInfo": + if err := awsRestjson1_deserializeDocumentConnectivityInfo(&sv.ConnectivityInfo, value); err != nil { + return err + } + + case "encryptionInfo": + if err := awsRestjson1_deserializeDocumentEncryptionInfo(&sv.EncryptionInfo, value); err != nil { + return err + } + + case "enhancedMonitoring": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected EnhancedMonitoring to be of type string, got %T instead", value) + } + sv.EnhancedMonitoring = types.EnhancedMonitoring(jtv) + } + + case "instanceType": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __stringMin5Max32 to be of type string, got %T instead", value) + } + sv.InstanceType = ptr.String(jtv) + } + + case "kafkaVersion": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.KafkaVersion = ptr.String(jtv) + } + + case "loggingInfo": + if err := awsRestjson1_deserializeDocumentLoggingInfo(&sv.LoggingInfo, value); err != nil { + return err + } + + case "numberOfBrokerNodes": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected __integer to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + sv.NumberOfBrokerNodes = ptr.Int32(int32(i64)) + } + + case "openMonitoring": + if err := awsRestjson1_deserializeDocumentOpenMonitoring(&sv.OpenMonitoring, value); err != nil { + return err + } + + case "rebalancing": + if err := awsRestjson1_deserializeDocumentRebalancing(&sv.Rebalancing, value); err != nil { + return err + } + + case "storageMode": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected StorageMode to be of type string, got %T instead", value) + } + sv.StorageMode = types.StorageMode(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentNodeExporter(v **types.NodeExporter, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.NodeExporter + if *v == nil { + sv = &types.NodeExporter{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "enabledInBroker": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.EnabledInBroker = ptr.Bool(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentNodeExporterInfo(v **types.NodeExporterInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.NodeExporterInfo + if *v == nil { + sv = &types.NodeExporterInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "enabledInBroker": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.EnabledInBroker = ptr.Bool(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentNodeInfo(v **types.NodeInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.NodeInfo + if *v == nil { + sv = &types.NodeInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "addedToClusterTime": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.AddedToClusterTime = ptr.String(jtv) + } + + case "brokerNodeInfo": + if err := awsRestjson1_deserializeDocumentBrokerNodeInfo(&sv.BrokerNodeInfo, value); err != nil { + return err + } + + case "controllerNodeInfo": + if err := awsRestjson1_deserializeDocumentControllerNodeInfo(&sv.ControllerNodeInfo, value); err != nil { + return err + } + + case "instanceType": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.InstanceType = ptr.String(jtv) + } + + case "nodeARN": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.NodeARN = ptr.String(jtv) + } + + case "nodeType": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected NodeType to be of type string, got %T instead", value) + } + sv.NodeType = types.NodeType(jtv) + } + + case "zookeeperNodeInfo": + if err := awsRestjson1_deserializeDocumentZookeeperNodeInfo(&sv.ZookeeperNodeInfo, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentNotFoundException(v **types.NotFoundException, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.NotFoundException + if *v == nil { + sv = &types.NotFoundException{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "invalidParameter": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.InvalidParameter = ptr.String(jtv) + } + + case "message", "Message": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Message = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentOpenMonitoring(v **types.OpenMonitoring, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.OpenMonitoring + if *v == nil { + sv = &types.OpenMonitoring{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "prometheus": + if err := awsRestjson1_deserializeDocumentPrometheus(&sv.Prometheus, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentOpenMonitoringInfo(v **types.OpenMonitoringInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.OpenMonitoringInfo + if *v == nil { + sv = &types.OpenMonitoringInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "prometheus": + if err := awsRestjson1_deserializeDocumentPrometheusInfo(&sv.Prometheus, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentPrometheus(v **types.Prometheus, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.Prometheus + if *v == nil { + sv = &types.Prometheus{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "jmxExporter": + if err := awsRestjson1_deserializeDocumentJmxExporter(&sv.JmxExporter, value); err != nil { + return err + } + + case "nodeExporter": + if err := awsRestjson1_deserializeDocumentNodeExporter(&sv.NodeExporter, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentPrometheusInfo(v **types.PrometheusInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.PrometheusInfo + if *v == nil { + sv = &types.PrometheusInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "jmxExporter": + if err := awsRestjson1_deserializeDocumentJmxExporterInfo(&sv.JmxExporter, value); err != nil { + return err + } + + case "nodeExporter": + if err := awsRestjson1_deserializeDocumentNodeExporterInfo(&sv.NodeExporter, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentProvisioned(v **types.Provisioned, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.Provisioned + if *v == nil { + sv = &types.Provisioned{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "brokerNodeGroupInfo": + if err := awsRestjson1_deserializeDocumentBrokerNodeGroupInfo(&sv.BrokerNodeGroupInfo, value); err != nil { + return err + } + + case "clientAuthentication": + if err := awsRestjson1_deserializeDocumentClientAuthentication(&sv.ClientAuthentication, value); err != nil { + return err + } + + case "currentBrokerSoftwareInfo": + if err := awsRestjson1_deserializeDocumentBrokerSoftwareInfo(&sv.CurrentBrokerSoftwareInfo, value); err != nil { + return err + } + + case "customerActionStatus": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected CustomerActionStatus to be of type string, got %T instead", value) + } + sv.CustomerActionStatus = types.CustomerActionStatus(jtv) + } + + case "encryptionInfo": + if err := awsRestjson1_deserializeDocumentEncryptionInfo(&sv.EncryptionInfo, value); err != nil { + return err + } + + case "enhancedMonitoring": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected EnhancedMonitoring to be of type string, got %T instead", value) + } + sv.EnhancedMonitoring = types.EnhancedMonitoring(jtv) + } + + case "loggingInfo": + if err := awsRestjson1_deserializeDocumentLoggingInfo(&sv.LoggingInfo, value); err != nil { + return err + } + + case "numberOfBrokerNodes": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected __integerMin1Max15 to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + sv.NumberOfBrokerNodes = ptr.Int32(int32(i64)) + } + + case "openMonitoring": + if err := awsRestjson1_deserializeDocumentOpenMonitoringInfo(&sv.OpenMonitoring, value); err != nil { + return err + } + + case "rebalancing": + if err := awsRestjson1_deserializeDocumentRebalancing(&sv.Rebalancing, value); err != nil { + return err + } + + case "storageMode": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected StorageMode to be of type string, got %T instead", value) + } + sv.StorageMode = types.StorageMode(jtv) + } + + case "zookeeperConnectString": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ZookeeperConnectString = ptr.String(jtv) + } + + case "zookeeperConnectStringTls": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ZookeeperConnectStringTls = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentProvisionedThroughput(v **types.ProvisionedThroughput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ProvisionedThroughput + if *v == nil { + sv = &types.ProvisionedThroughput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "enabled": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.Enabled = ptr.Bool(jtv) + } + + case "volumeThroughput": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected __integer to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + sv.VolumeThroughput = ptr.Int32(int32(i64)) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentPublicAccess(v **types.PublicAccess, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.PublicAccess + if *v == nil { + sv = &types.PublicAccess{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "type": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Type = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentRebalancing(v **types.Rebalancing, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.Rebalancing + if *v == nil { + sv = &types.Rebalancing{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "status": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected RebalancingStatus to be of type string, got %T instead", value) + } + sv.Status = types.RebalancingStatus(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentReplicationInfoDescription(v **types.ReplicationInfoDescription, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ReplicationInfoDescription + if *v == nil { + sv = &types.ReplicationInfoDescription{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "consumerGroupReplication": + if err := awsRestjson1_deserializeDocumentConsumerGroupReplication(&sv.ConsumerGroupReplication, value); err != nil { + return err + } + + case "sourceKafkaClusterAlias": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.SourceKafkaClusterAlias = ptr.String(jtv) + } + + case "targetCompressionType": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected TargetCompressionType to be of type string, got %T instead", value) + } + sv.TargetCompressionType = types.TargetCompressionType(jtv) + } + + case "targetKafkaClusterAlias": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.TargetKafkaClusterAlias = ptr.String(jtv) + } + + case "topicReplication": + if err := awsRestjson1_deserializeDocumentTopicReplication(&sv.TopicReplication, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentReplicationInfoSummary(v **types.ReplicationInfoSummary, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ReplicationInfoSummary + if *v == nil { + sv = &types.ReplicationInfoSummary{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "sourceKafkaClusterAlias": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.SourceKafkaClusterAlias = ptr.String(jtv) + } + + case "targetKafkaClusterAlias": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.TargetKafkaClusterAlias = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentReplicationStartingPosition(v **types.ReplicationStartingPosition, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ReplicationStartingPosition + if *v == nil { + sv = &types.ReplicationStartingPosition{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "type": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected ReplicationStartingPositionType to be of type string, got %T instead", value) + } + sv.Type = types.ReplicationStartingPositionType(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentReplicationStateInfo(v **types.ReplicationStateInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ReplicationStateInfo + if *v == nil { + sv = &types.ReplicationStateInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "code": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Code = ptr.String(jtv) + } + + case "message": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Message = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentReplicationTopicNameConfiguration(v **types.ReplicationTopicNameConfiguration, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ReplicationTopicNameConfiguration + if *v == nil { + sv = &types.ReplicationTopicNameConfiguration{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "type": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected ReplicationTopicNameConfigurationType to be of type string, got %T instead", value) + } + sv.Type = types.ReplicationTopicNameConfigurationType(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentReplicatorSummary(v **types.ReplicatorSummary, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ReplicatorSummary + if *v == nil { + sv = &types.ReplicatorSummary{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "creationTime": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __timestampIso8601 to be of type string, got %T instead", value) + } + t, err := smithytime.ParseDateTime(jtv) + if err != nil { + return err + } + sv.CreationTime = ptr.Time(t) + } + + case "currentVersion": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.CurrentVersion = ptr.String(jtv) + } + + case "isReplicatorReference": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.IsReplicatorReference = ptr.Bool(jtv) + } + + case "kafkaClustersSummary": + if err := awsRestjson1_deserializeDocument__listOfKafkaClusterSummary(&sv.KafkaClustersSummary, value); err != nil { + return err + } + + case "replicationInfoSummaryList": + if err := awsRestjson1_deserializeDocument__listOfReplicationInfoSummary(&sv.ReplicationInfoSummaryList, value); err != nil { + return err + } + + case "replicatorArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ReplicatorArn = ptr.String(jtv) + } + + case "replicatorName": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ReplicatorName = ptr.String(jtv) + } + + case "replicatorResourceArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ReplicatorResourceArn = ptr.String(jtv) + } + + case "replicatorState": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected ReplicatorState to be of type string, got %T instead", value) + } + sv.ReplicatorState = types.ReplicatorState(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentS3(v **types.S3, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.S3 + if *v == nil { + sv = &types.S3{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "bucket": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Bucket = ptr.String(jtv) + } + + case "enabled": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.Enabled = ptr.Bool(jtv) + } + + case "prefix": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Prefix = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentSasl(v **types.Sasl, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.Sasl + if *v == nil { + sv = &types.Sasl{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "iam": + if err := awsRestjson1_deserializeDocumentIam(&sv.Iam, value); err != nil { + return err + } + + case "scram": + if err := awsRestjson1_deserializeDocumentScram(&sv.Scram, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentScram(v **types.Scram, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.Scram + if *v == nil { + sv = &types.Scram{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "enabled": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.Enabled = ptr.Bool(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentServerless(v **types.Serverless, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.Serverless + if *v == nil { + sv = &types.Serverless{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clientAuthentication": + if err := awsRestjson1_deserializeDocumentServerlessClientAuthentication(&sv.ClientAuthentication, value); err != nil { + return err + } + + case "vpcConfigs": + if err := awsRestjson1_deserializeDocument__listOfVpcConfig(&sv.VpcConfigs, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentServerlessClientAuthentication(v **types.ServerlessClientAuthentication, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ServerlessClientAuthentication + if *v == nil { + sv = &types.ServerlessClientAuthentication{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "sasl": + if err := awsRestjson1_deserializeDocumentServerlessSasl(&sv.Sasl, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentServerlessSasl(v **types.ServerlessSasl, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ServerlessSasl + if *v == nil { + sv = &types.ServerlessSasl{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "iam": + if err := awsRestjson1_deserializeDocumentIam(&sv.Iam, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentServiceUnavailableException(v **types.ServiceUnavailableException, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ServiceUnavailableException + if *v == nil { + sv = &types.ServiceUnavailableException{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "invalidParameter": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.InvalidParameter = ptr.String(jtv) + } + + case "message", "Message": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Message = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentStateInfo(v **types.StateInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.StateInfo + if *v == nil { + sv = &types.StateInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "code": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Code = ptr.String(jtv) + } + + case "message": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Message = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentStorageInfo(v **types.StorageInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.StorageInfo + if *v == nil { + sv = &types.StorageInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "ebsStorageInfo": + if err := awsRestjson1_deserializeDocumentEBSStorageInfo(&sv.EbsStorageInfo, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentTls(v **types.Tls, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.Tls + if *v == nil { + sv = &types.Tls{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "certificateAuthorityArnList": + if err := awsRestjson1_deserializeDocument__listOf__string(&sv.CertificateAuthorityArnList, value); err != nil { + return err + } + + case "enabled": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.Enabled = ptr.Bool(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentTooManyRequestsException(v **types.TooManyRequestsException, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.TooManyRequestsException + if *v == nil { + sv = &types.TooManyRequestsException{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "invalidParameter": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.InvalidParameter = ptr.String(jtv) + } + + case "message", "Message": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Message = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentTopicInfo(v **types.TopicInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.TopicInfo + if *v == nil { + sv = &types.TopicInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "outOfSyncReplicaCount": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected __integer to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + sv.OutOfSyncReplicaCount = ptr.Int32(int32(i64)) + } + + case "partitionCount": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected __integer to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + sv.PartitionCount = ptr.Int32(int32(i64)) + } + + case "replicationFactor": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected __integer to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + sv.ReplicationFactor = ptr.Int32(int32(i64)) + } + + case "topicArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.TopicArn = ptr.String(jtv) + } + + case "topicName": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.TopicName = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentTopicPartitionInfo(v **types.TopicPartitionInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.TopicPartitionInfo + if *v == nil { + sv = &types.TopicPartitionInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "isr": + if err := awsRestjson1_deserializeDocument__listOf__integer(&sv.Isr, value); err != nil { + return err + } + + case "leader": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected __integer to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + sv.Leader = ptr.Int32(int32(i64)) + } + + case "partition": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected __integer to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + sv.Partition = ptr.Int32(int32(i64)) + } + + case "replicas": + if err := awsRestjson1_deserializeDocument__listOf__integer(&sv.Replicas, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentTopicReplication(v **types.TopicReplication, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.TopicReplication + if *v == nil { + sv = &types.TopicReplication{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "copyAccessControlListsForTopics": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.CopyAccessControlListsForTopics = ptr.Bool(jtv) + } + + case "copyTopicConfigurations": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.CopyTopicConfigurations = ptr.Bool(jtv) + } + + case "detectAndCopyNewTopics": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.DetectAndCopyNewTopics = ptr.Bool(jtv) + } + + case "startingPosition": + if err := awsRestjson1_deserializeDocumentReplicationStartingPosition(&sv.StartingPosition, value); err != nil { + return err + } + + case "topicNameConfiguration": + if err := awsRestjson1_deserializeDocumentReplicationTopicNameConfiguration(&sv.TopicNameConfiguration, value); err != nil { + return err + } + + case "topicsToExclude": + if err := awsRestjson1_deserializeDocument__listOf__stringMax249(&sv.TopicsToExclude, value); err != nil { + return err + } + + case "topicsToReplicate": + if err := awsRestjson1_deserializeDocument__listOf__stringMax249(&sv.TopicsToReplicate, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentUnauthenticated(v **types.Unauthenticated, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.Unauthenticated + if *v == nil { + sv = &types.Unauthenticated{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "enabled": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.Enabled = ptr.Bool(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentUnauthorizedException(v **types.UnauthorizedException, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.UnauthorizedException + if *v == nil { + sv = &types.UnauthorizedException{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "invalidParameter": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.InvalidParameter = ptr.String(jtv) + } + + case "message", "Message": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Message = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentUnprocessedScramSecret(v **types.UnprocessedScramSecret, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.UnprocessedScramSecret + if *v == nil { + sv = &types.UnprocessedScramSecret{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "errorCode": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ErrorCode = ptr.String(jtv) + } + + case "errorMessage": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ErrorMessage = ptr.String(jtv) + } + + case "secretArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.SecretArn = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentUserIdentity(v **types.UserIdentity, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.UserIdentity + if *v == nil { + sv = &types.UserIdentity{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "principalId": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.PrincipalId = ptr.String(jtv) + } + + case "type": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected UserIdentityType to be of type string, got %T instead", value) + } + sv.Type = types.UserIdentityType(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentVpcConfig(v **types.VpcConfig, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.VpcConfig + if *v == nil { + sv = &types.VpcConfig{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "securityGroupIds": + if err := awsRestjson1_deserializeDocument__listOf__string(&sv.SecurityGroupIds, value); err != nil { + return err + } + + case "subnetIds": + if err := awsRestjson1_deserializeDocument__listOf__string(&sv.SubnetIds, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentVpcConnection(v **types.VpcConnection, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.VpcConnection + if *v == nil { + sv = &types.VpcConnection{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "authentication": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Authentication = ptr.String(jtv) + } + + case "creationTime": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __timestampIso8601 to be of type string, got %T instead", value) + } + t, err := smithytime.ParseDateTime(jtv) + if err != nil { + return err + } + sv.CreationTime = ptr.Time(t) + } + + case "state": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected VpcConnectionState to be of type string, got %T instead", value) + } + sv.State = types.VpcConnectionState(jtv) + } + + case "targetClusterArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.TargetClusterArn = ptr.String(jtv) + } + + case "vpcConnectionArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.VpcConnectionArn = ptr.String(jtv) + } + + case "vpcId": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.VpcId = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentVpcConnectionInfo(v **types.VpcConnectionInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.VpcConnectionInfo + if *v == nil { + sv = &types.VpcConnectionInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "creationTime": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __timestampIso8601 to be of type string, got %T instead", value) + } + t, err := smithytime.ParseDateTime(jtv) + if err != nil { + return err + } + sv.CreationTime = ptr.Time(t) + } + + case "owner": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Owner = ptr.String(jtv) + } + + case "userIdentity": + if err := awsRestjson1_deserializeDocumentUserIdentity(&sv.UserIdentity, value); err != nil { + return err + } + + case "vpcConnectionArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.VpcConnectionArn = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentVpcConnectionInfoServerless(v **types.VpcConnectionInfoServerless, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.VpcConnectionInfoServerless + if *v == nil { + sv = &types.VpcConnectionInfoServerless{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "creationTime": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __timestampIso8601 to be of type string, got %T instead", value) + } + t, err := smithytime.ParseDateTime(jtv) + if err != nil { + return err + } + sv.CreationTime = ptr.Time(t) + } + + case "owner": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.Owner = ptr.String(jtv) + } + + case "userIdentity": + if err := awsRestjson1_deserializeDocumentUserIdentity(&sv.UserIdentity, value); err != nil { + return err + } + + case "vpcConnectionArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.VpcConnectionArn = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentVpcConnectivity(v **types.VpcConnectivity, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.VpcConnectivity + if *v == nil { + sv = &types.VpcConnectivity{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "clientAuthentication": + if err := awsRestjson1_deserializeDocumentVpcConnectivityClientAuthentication(&sv.ClientAuthentication, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentVpcConnectivityClientAuthentication(v **types.VpcConnectivityClientAuthentication, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.VpcConnectivityClientAuthentication + if *v == nil { + sv = &types.VpcConnectivityClientAuthentication{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "sasl": + if err := awsRestjson1_deserializeDocumentVpcConnectivitySasl(&sv.Sasl, value); err != nil { + return err + } + + case "tls": + if err := awsRestjson1_deserializeDocumentVpcConnectivityTls(&sv.Tls, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentVpcConnectivityIam(v **types.VpcConnectivityIam, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.VpcConnectivityIam + if *v == nil { + sv = &types.VpcConnectivityIam{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "enabled": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.Enabled = ptr.Bool(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentVpcConnectivitySasl(v **types.VpcConnectivitySasl, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.VpcConnectivitySasl + if *v == nil { + sv = &types.VpcConnectivitySasl{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "iam": + if err := awsRestjson1_deserializeDocumentVpcConnectivityIam(&sv.Iam, value); err != nil { + return err + } + + case "scram": + if err := awsRestjson1_deserializeDocumentVpcConnectivityScram(&sv.Scram, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentVpcConnectivityScram(v **types.VpcConnectivityScram, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.VpcConnectivityScram + if *v == nil { + sv = &types.VpcConnectivityScram{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "enabled": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.Enabled = ptr.Bool(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentVpcConnectivityTls(v **types.VpcConnectivityTls, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.VpcConnectivityTls + if *v == nil { + sv = &types.VpcConnectivityTls{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "enabled": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected __boolean to be of type *bool, got %T instead", value) + } + sv.Enabled = ptr.Bool(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentZookeeperNodeInfo(v **types.ZookeeperNodeInfo, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ZookeeperNodeInfo + if *v == nil { + sv = &types.ZookeeperNodeInfo{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "attachedENIId": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.AttachedENIId = ptr.String(jtv) + } + + case "clientVpcIpAddress": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ClientVpcIpAddress = ptr.String(jtv) + } + + case "endpoints": + if err := awsRestjson1_deserializeDocument__listOf__string(&sv.Endpoints, value); err != nil { + return err + } + + case "zookeeperId": + if value != nil { + switch jtv := value.(type) { + case json.Number: + f64, err := jtv.Float64() + if err != nil { + return err + } + sv.ZookeeperId = ptr.Float64(f64) + + case string: + var f64 float64 + switch { + case strings.EqualFold(jtv, "NaN"): + f64 = math.NaN() + + case strings.EqualFold(jtv, "Infinity"): + f64 = math.Inf(1) + + case strings.EqualFold(jtv, "-Infinity"): + f64 = math.Inf(-1) + + default: + return fmt.Errorf("unknown JSON number value: %s", jtv) + + } + sv.ZookeeperId = ptr.Float64(f64) + + default: + return fmt.Errorf("expected __double to be a JSON Number, got %T instead", value) + + } + } + + case "zookeeperVersion": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected __string to be of type string, got %T instead", value) + } + sv.ZookeeperVersion = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/doc.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/doc.go new file mode 100644 index 000000000..4a0c8c88b --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/doc.go @@ -0,0 +1,7 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +// Package kafka provides the API client, operations, and parameter types for +// Managed Streaming for Kafka. +// +// The operations for managing an Amazon MSK cluster. +package kafka diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/endpoints.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/endpoints.go new file mode 100644 index 000000000..a0415731b --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/endpoints.go @@ -0,0 +1,571 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "errors" + "fmt" + "github.com/aws/aws-sdk-go-v2/aws" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + internalConfig "github.com/aws/aws-sdk-go-v2/internal/configsources" + "github.com/aws/aws-sdk-go-v2/internal/endpoints" + "github.com/aws/aws-sdk-go-v2/internal/endpoints/awsrulesfn" + internalendpoints "github.com/aws/aws-sdk-go-v2/service/kafka/internal/endpoints" + smithyauth "github.com/aws/smithy-go/auth" + smithyendpoints "github.com/aws/smithy-go/endpoints" + "github.com/aws/smithy-go/endpoints/private/rulesfn" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/ptr" + "github.com/aws/smithy-go/tracing" + smithyhttp "github.com/aws/smithy-go/transport/http" + "net/http" + "net/url" + "os" + "strings" +) + +// EndpointResolverOptions is the service endpoint resolver options +type EndpointResolverOptions = internalendpoints.Options + +// EndpointResolver interface for resolving service endpoints. +type EndpointResolver interface { + ResolveEndpoint(region string, options EndpointResolverOptions) (aws.Endpoint, error) +} + +var _ EndpointResolver = &internalendpoints.Resolver{} + +// NewDefaultEndpointResolver constructs a new service endpoint resolver +func NewDefaultEndpointResolver() *internalendpoints.Resolver { + return internalendpoints.New() +} + +// EndpointResolverFunc is a helper utility that wraps a function so it satisfies +// the EndpointResolver interface. This is useful when you want to add additional +// endpoint resolving logic, or stub out specific endpoints with custom values. +type EndpointResolverFunc func(region string, options EndpointResolverOptions) (aws.Endpoint, error) + +func (fn EndpointResolverFunc) ResolveEndpoint(region string, options EndpointResolverOptions) (endpoint aws.Endpoint, err error) { + return fn(region, options) +} + +// EndpointResolverFromURL returns an EndpointResolver configured using the +// provided endpoint url. By default, the resolved endpoint resolver uses the +// client region as signing region, and the endpoint source is set to +// EndpointSourceCustom.You can provide functional options to configure endpoint +// values for the resolved endpoint. +func EndpointResolverFromURL(url string, optFns ...func(*aws.Endpoint)) EndpointResolver { + e := aws.Endpoint{URL: url, Source: aws.EndpointSourceCustom} + for _, fn := range optFns { + fn(&e) + } + + return EndpointResolverFunc( + func(region string, options EndpointResolverOptions) (aws.Endpoint, error) { + if len(e.SigningRegion) == 0 { + e.SigningRegion = region + } + return e, nil + }, + ) +} + +type ResolveEndpoint struct { + Resolver EndpointResolver + Options EndpointResolverOptions +} + +func (*ResolveEndpoint) ID() string { + return "ResolveEndpoint" +} + +func (m *ResolveEndpoint) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + if !awsmiddleware.GetRequiresLegacyEndpoints(ctx) { + return next.HandleSerialize(ctx, in) + } + + req, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, fmt.Errorf("unknown transport type %T", in.Request) + } + + if m.Resolver == nil { + return out, metadata, fmt.Errorf("expected endpoint resolver to not be nil") + } + + eo := m.Options + eo.Logger = middleware.GetLogger(ctx) + + var endpoint aws.Endpoint + endpoint, err = m.Resolver.ResolveEndpoint(awsmiddleware.GetRegion(ctx), eo) + if err != nil { + nf := (&aws.EndpointNotFoundError{}) + if errors.As(err, &nf) { + ctx = awsmiddleware.SetRequiresLegacyEndpoints(ctx, false) + return next.HandleSerialize(ctx, in) + } + return out, metadata, fmt.Errorf("failed to resolve service endpoint, %w", err) + } + + req.URL, err = url.Parse(endpoint.URL) + if err != nil { + return out, metadata, fmt.Errorf("failed to parse endpoint URL: %w", err) + } + + if len(awsmiddleware.GetSigningName(ctx)) == 0 { + signingName := endpoint.SigningName + if len(signingName) == 0 { + signingName = "kafka" + } + ctx = awsmiddleware.SetSigningName(ctx, signingName) + } + ctx = awsmiddleware.SetEndpointSource(ctx, endpoint.Source) + ctx = smithyhttp.SetHostnameImmutable(ctx, endpoint.HostnameImmutable) + ctx = awsmiddleware.SetSigningRegion(ctx, endpoint.SigningRegion) + ctx = awsmiddleware.SetPartitionID(ctx, endpoint.PartitionID) + return next.HandleSerialize(ctx, in) +} +func addResolveEndpointMiddleware(stack *middleware.Stack, o Options) error { + return stack.Serialize.Insert(&ResolveEndpoint{ + Resolver: o.EndpointResolver, + Options: o.EndpointOptions, + }, "OperationSerializer", middleware.Before) +} + +func removeResolveEndpointMiddleware(stack *middleware.Stack) error { + _, err := stack.Serialize.Remove((&ResolveEndpoint{}).ID()) + return err +} + +type wrappedEndpointResolver struct { + awsResolver aws.EndpointResolverWithOptions +} + +func (w *wrappedEndpointResolver) ResolveEndpoint(region string, options EndpointResolverOptions) (endpoint aws.Endpoint, err error) { + return w.awsResolver.ResolveEndpoint(ServiceID, region, options) +} + +type awsEndpointResolverAdaptor func(service, region string) (aws.Endpoint, error) + +func (a awsEndpointResolverAdaptor) ResolveEndpoint(service, region string, options ...interface{}) (aws.Endpoint, error) { + return a(service, region) +} + +var _ aws.EndpointResolverWithOptions = awsEndpointResolverAdaptor(nil) + +// withEndpointResolver returns an aws.EndpointResolverWithOptions that first delegates endpoint resolution to the awsResolver. +// If awsResolver returns aws.EndpointNotFoundError error, the v1 resolver middleware will swallow the error, +// and set an appropriate context flag such that fallback will occur when EndpointResolverV2 is invoked +// via its middleware. +// +// If another error (besides aws.EndpointNotFoundError) is returned, then that error will be propagated. +func withEndpointResolver(awsResolver aws.EndpointResolver, awsResolverWithOptions aws.EndpointResolverWithOptions) EndpointResolver { + var resolver aws.EndpointResolverWithOptions + + if awsResolverWithOptions != nil { + resolver = awsResolverWithOptions + } else if awsResolver != nil { + resolver = awsEndpointResolverAdaptor(awsResolver.ResolveEndpoint) + } + + return &wrappedEndpointResolver{ + awsResolver: resolver, + } +} + +func finalizeClientEndpointResolverOptions(options *Options) { + options.EndpointOptions.LogDeprecated = options.ClientLogMode.IsDeprecatedUsage() + + if len(options.EndpointOptions.ResolvedRegion) == 0 { + const fipsInfix = "-fips-" + const fipsPrefix = "fips-" + const fipsSuffix = "-fips" + + if strings.Contains(options.Region, fipsInfix) || + strings.Contains(options.Region, fipsPrefix) || + strings.Contains(options.Region, fipsSuffix) { + options.EndpointOptions.ResolvedRegion = strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll( + options.Region, fipsInfix, "-"), fipsPrefix, ""), fipsSuffix, "") + options.EndpointOptions.UseFIPSEndpoint = aws.FIPSEndpointStateEnabled + } + } + +} + +func resolveEndpointResolverV2(options *Options) { + if options.EndpointResolverV2 == nil { + options.EndpointResolverV2 = NewDefaultEndpointResolverV2() + } +} + +func resolveBaseEndpoint(cfg aws.Config, o *Options) { + if cfg.BaseEndpoint != nil { + o.BaseEndpoint = cfg.BaseEndpoint + } + + _, g := os.LookupEnv("AWS_ENDPOINT_URL") + _, s := os.LookupEnv("AWS_ENDPOINT_URL_KAFKA") + + if g && !s { + return + } + + value, found, err := internalConfig.ResolveServiceBaseEndpoint(context.Background(), "Kafka", cfg.ConfigSources) + if found && err == nil { + o.BaseEndpoint = &value + } +} + +func bindRegion(region string) (*string, error) { + if region == "" { + return nil, nil + } + if !rulesfn.IsValidHostLabel(region, true) { + return nil, fmt.Errorf("invalid input region %s", region) + } + + return aws.String(endpoints.MapFIPSRegion(region)), nil +} + +// EndpointParameters provides the parameters that influence how endpoints are +// resolved. +type EndpointParameters struct { + // The AWS region used to dispatch the request. + // + // Parameter is + // required. + // + // AWS::Region + Region *string + + // When true, use the dual-stack endpoint. If the configured endpoint does not + // support dual-stack, dispatching the request MAY return an error. + // + // Defaults to + // false if no value is provided. + // + // AWS::UseDualStack + UseDualStack *bool + + // When true, send this request to the FIPS-compliant regional endpoint. If the + // configured endpoint does not have a FIPS compliant endpoint, dispatching the + // request will return an error. + // + // Defaults to false if no value is + // provided. + // + // AWS::UseFIPS + UseFIPS *bool + + // Override the endpoint used to send this request + // + // Parameter is + // required. + // + // SDK::Endpoint + Endpoint *string +} + +// ValidateRequired validates required parameters are set. +func (p EndpointParameters) ValidateRequired() error { + if p.UseDualStack == nil { + return fmt.Errorf("parameter UseDualStack is required") + } + + if p.UseFIPS == nil { + return fmt.Errorf("parameter UseFIPS is required") + } + + return nil +} + +// WithDefaults returns a shallow copy of EndpointParameterswith default values +// applied to members where applicable. +func (p EndpointParameters) WithDefaults() EndpointParameters { + if p.UseDualStack == nil { + p.UseDualStack = ptr.Bool(false) + } + + if p.UseFIPS == nil { + p.UseFIPS = ptr.Bool(false) + } + return p +} + +type stringSlice []string + +func (s stringSlice) Get(i int) *string { + if i < 0 || i >= len(s) { + return nil + } + + v := s[i] + return &v +} + +// EndpointResolverV2 provides the interface for resolving service endpoints. +type EndpointResolverV2 interface { + // ResolveEndpoint attempts to resolve the endpoint with the provided options, + // returning the endpoint if found. Otherwise an error is returned. + ResolveEndpoint(ctx context.Context, params EndpointParameters) ( + smithyendpoints.Endpoint, error, + ) +} + +// resolver provides the implementation for resolving endpoints. +type resolver struct{} + +func NewDefaultEndpointResolverV2() EndpointResolverV2 { + return &resolver{} +} + +// ResolveEndpoint attempts to resolve the endpoint with the provided options, +// returning the endpoint if found. Otherwise an error is returned. +func (r *resolver) ResolveEndpoint( + ctx context.Context, params EndpointParameters, +) ( + endpoint smithyendpoints.Endpoint, err error, +) { + params = params.WithDefaults() + if err = params.ValidateRequired(); err != nil { + return endpoint, fmt.Errorf("endpoint parameters are not valid, %w", err) + } + _UseDualStack := *params.UseDualStack + _ = _UseDualStack + _UseFIPS := *params.UseFIPS + _ = _UseFIPS + + if exprVal := params.Endpoint; exprVal != nil { + _Endpoint := *exprVal + _ = _Endpoint + if _UseFIPS == true { + return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: FIPS and custom endpoint are not supported") + } + if _UseDualStack == true { + return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Dualstack and custom endpoint are not supported") + } + uriString := _Endpoint + + uri, err := url.Parse(uriString) + if err != nil { + return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) + } + + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + } + if exprVal := params.Region; exprVal != nil { + _Region := *exprVal + _ = _Region + if exprVal := awsrulesfn.GetPartition(_Region); exprVal != nil { + _PartitionResult := *exprVal + _ = _PartitionResult + if _UseFIPS == true { + if _UseDualStack == true { + if true == _PartitionResult.SupportsFIPS { + if true == _PartitionResult.SupportsDualStack { + uriString := func() string { + var out strings.Builder + out.WriteString("https://kafka-fips.") + out.WriteString(_Region) + out.WriteString(".") + out.WriteString(_PartitionResult.DualStackDnsSuffix) + return out.String() + }() + + uri, err := url.Parse(uriString) + if err != nil { + return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) + } + + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + } + } + return endpoint, fmt.Errorf("endpoint rule error, %s", "FIPS and DualStack are enabled, but this partition does not support one or both") + } + } + if _UseFIPS == true { + if _PartitionResult.SupportsFIPS == true { + if _PartitionResult.Name == "aws-us-gov" { + uriString := func() string { + var out strings.Builder + out.WriteString("https://kafka.") + out.WriteString(_Region) + out.WriteString(".amazonaws.com") + return out.String() + }() + + uri, err := url.Parse(uriString) + if err != nil { + return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) + } + + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + } + uriString := func() string { + var out strings.Builder + out.WriteString("https://kafka-fips.") + out.WriteString(_Region) + out.WriteString(".") + out.WriteString(_PartitionResult.DnsSuffix) + return out.String() + }() + + uri, err := url.Parse(uriString) + if err != nil { + return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) + } + + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + } + return endpoint, fmt.Errorf("endpoint rule error, %s", "FIPS is enabled but this partition does not support FIPS") + } + if _UseDualStack == true { + if true == _PartitionResult.SupportsDualStack { + uriString := func() string { + var out strings.Builder + out.WriteString("https://kafka.") + out.WriteString(_Region) + out.WriteString(".") + out.WriteString(_PartitionResult.DualStackDnsSuffix) + return out.String() + }() + + uri, err := url.Parse(uriString) + if err != nil { + return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) + } + + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + } + return endpoint, fmt.Errorf("endpoint rule error, %s", "DualStack is enabled but this partition does not support DualStack") + } + uriString := func() string { + var out strings.Builder + out.WriteString("https://kafka.") + out.WriteString(_Region) + out.WriteString(".") + out.WriteString(_PartitionResult.DnsSuffix) + return out.String() + }() + + uri, err := url.Parse(uriString) + if err != nil { + return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) + } + + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + } + return endpoint, fmt.Errorf("Endpoint resolution failed. Invalid operation or environment input.") + } + return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Missing Region") +} + +type endpointParamsBinder interface { + bindEndpointParams(*EndpointParameters) +} + +func bindEndpointParams(ctx context.Context, input interface{}, options Options) (*EndpointParameters, error) { + params := &EndpointParameters{} + + region, err := bindRegion(options.Region) + if err != nil { + return nil, err + } + params.Region = region + + params.UseDualStack = aws.Bool(options.EndpointOptions.UseDualStackEndpoint == aws.DualStackEndpointStateEnabled) + params.UseFIPS = aws.Bool(options.EndpointOptions.UseFIPSEndpoint == aws.FIPSEndpointStateEnabled) + params.Endpoint = options.BaseEndpoint + + if b, ok := input.(endpointParamsBinder); ok { + b.bindEndpointParams(params) + } + + return params, nil +} + +type resolveEndpointV2Middleware struct { + options Options +} + +func (*resolveEndpointV2Middleware) ID() string { + return "ResolveEndpointV2" +} + +func (m *resolveEndpointV2Middleware) HandleFinalize(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) ( + out middleware.FinalizeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "ResolveEndpoint") + defer span.End() + + if awsmiddleware.GetRequiresLegacyEndpoints(ctx) { + return next.HandleFinalize(ctx, in) + } + + req, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, fmt.Errorf("unknown transport type %T", in.Request) + } + + if m.options.EndpointResolverV2 == nil { + return out, metadata, fmt.Errorf("expected endpoint resolver to not be nil") + } + + params, err := bindEndpointParams(ctx, getOperationInput(ctx), m.options) + if err != nil { + return out, metadata, fmt.Errorf("failed to bind endpoint params, %w", err) + } + endpt, err := timeOperationMetric(ctx, "client.call.resolve_endpoint_duration", + func() (smithyendpoints.Endpoint, error) { + return m.options.EndpointResolverV2.ResolveEndpoint(ctx, *params) + }) + if err != nil { + return out, metadata, fmt.Errorf("failed to resolve service endpoint, %w", err) + } + + span.SetProperty("client.call.resolved_endpoint", endpt.URI.String()) + + if endpt.URI.RawPath == "" && req.URL.RawPath != "" { + endpt.URI.RawPath = endpt.URI.Path + } + req.URL.Scheme = endpt.URI.Scheme + req.URL.Host = endpt.URI.Host + req.URL.Path = smithyhttp.JoinPath(endpt.URI.Path, req.URL.Path) + req.URL.RawPath = smithyhttp.JoinPath(endpt.URI.RawPath, req.URL.RawPath) + for k := range endpt.Headers { + req.Header.Set(k, endpt.Headers.Get(k)) + } + + rscheme := getResolvedAuthScheme(ctx) + if rscheme == nil { + return out, metadata, fmt.Errorf("no resolved auth scheme") + } + + opts, _ := smithyauth.GetAuthOptions(&endpt.Properties) + for _, o := range opts { + rscheme.SignerProperties.SetAll(&o.SignerProperties) + } + + span.End() + return next.HandleFinalize(ctx, in) +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/generated.json b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/generated.json new file mode 100644 index 000000000..35ba5cc38 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/generated.json @@ -0,0 +1,89 @@ +{ + "dependencies": { + "github.com/aws/aws-sdk-go-v2": "v1.4.0", + "github.com/aws/aws-sdk-go-v2/internal/configsources": "v0.0.0-00010101000000-000000000000", + "github.com/aws/aws-sdk-go-v2/internal/endpoints/v2": "v2.0.0-00010101000000-000000000000", + "github.com/aws/smithy-go": "v1.4.0" + }, + "files": [ + "api_client.go", + "api_client_test.go", + "api_op_BatchAssociateScramSecret.go", + "api_op_BatchDisassociateScramSecret.go", + "api_op_CreateCluster.go", + "api_op_CreateClusterV2.go", + "api_op_CreateConfiguration.go", + "api_op_CreateReplicator.go", + "api_op_CreateVpcConnection.go", + "api_op_DeleteCluster.go", + "api_op_DeleteClusterPolicy.go", + "api_op_DeleteConfiguration.go", + "api_op_DeleteReplicator.go", + "api_op_DeleteVpcConnection.go", + "api_op_DescribeCluster.go", + "api_op_DescribeClusterOperation.go", + "api_op_DescribeClusterOperationV2.go", + "api_op_DescribeClusterV2.go", + "api_op_DescribeConfiguration.go", + "api_op_DescribeConfigurationRevision.go", + "api_op_DescribeReplicator.go", + "api_op_DescribeTopic.go", + "api_op_DescribeTopicPartitions.go", + "api_op_DescribeVpcConnection.go", + "api_op_GetBootstrapBrokers.go", + "api_op_GetClusterPolicy.go", + "api_op_GetCompatibleKafkaVersions.go", + "api_op_ListClientVpcConnections.go", + "api_op_ListClusterOperations.go", + "api_op_ListClusterOperationsV2.go", + "api_op_ListClusters.go", + "api_op_ListClustersV2.go", + "api_op_ListConfigurationRevisions.go", + "api_op_ListConfigurations.go", + "api_op_ListKafkaVersions.go", + "api_op_ListNodes.go", + "api_op_ListReplicators.go", + "api_op_ListScramSecrets.go", + "api_op_ListTagsForResource.go", + "api_op_ListTopics.go", + "api_op_ListVpcConnections.go", + "api_op_PutClusterPolicy.go", + "api_op_RebootBroker.go", + "api_op_RejectClientVpcConnection.go", + "api_op_TagResource.go", + "api_op_UntagResource.go", + "api_op_UpdateBrokerCount.go", + "api_op_UpdateBrokerStorage.go", + "api_op_UpdateBrokerType.go", + "api_op_UpdateClusterConfiguration.go", + "api_op_UpdateClusterKafkaVersion.go", + "api_op_UpdateConfiguration.go", + "api_op_UpdateConnectivity.go", + "api_op_UpdateMonitoring.go", + "api_op_UpdateRebalancing.go", + "api_op_UpdateReplicationInfo.go", + "api_op_UpdateSecurity.go", + "api_op_UpdateStorage.go", + "auth.go", + "deserializers.go", + "doc.go", + "endpoints.go", + "endpoints_config_test.go", + "endpoints_test.go", + "generated.json", + "internal/endpoints/endpoints.go", + "internal/endpoints/endpoints_test.go", + "options.go", + "protocol_test.go", + "serializers.go", + "snapshot_test.go", + "sra_operation_order_test.go", + "types/enums.go", + "types/errors.go", + "types/types.go", + "validators.go" + ], + "go": "1.23", + "module": "github.com/aws/aws-sdk-go-v2/service/kafka", + "unstable": false +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/go_module_metadata.go new file mode 100644 index 000000000..381fc46f6 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/go_module_metadata.go @@ -0,0 +1,6 @@ +// Code generated by internal/repotools/cmd/updatemodulemeta DO NOT EDIT. + +package kafka + +// goModuleVersion is the tagged release for this module +const goModuleVersion = "1.46.7" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/internal/endpoints/endpoints.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/internal/endpoints/endpoints.go new file mode 100644 index 000000000..e696fde18 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/internal/endpoints/endpoints.go @@ -0,0 +1,594 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package endpoints + +import ( + "github.com/aws/aws-sdk-go-v2/aws" + endpoints "github.com/aws/aws-sdk-go-v2/internal/endpoints/v2" + "github.com/aws/smithy-go/logging" + "regexp" +) + +// Options is the endpoint resolver configuration options +type Options struct { + // Logger is a logging implementation that log events should be sent to. + Logger logging.Logger + + // LogDeprecated indicates that deprecated endpoints should be logged to the + // provided logger. + LogDeprecated bool + + // ResolvedRegion is used to override the region to be resolved, rather then the + // using the value passed to the ResolveEndpoint method. This value is used by the + // SDK to translate regions like fips-us-east-1 or us-east-1-fips to an alternative + // name. You must not set this value directly in your application. + ResolvedRegion string + + // DisableHTTPS informs the resolver to return an endpoint that does not use the + // HTTPS scheme. + DisableHTTPS bool + + // UseDualStackEndpoint specifies the resolver must resolve a dual-stack endpoint. + UseDualStackEndpoint aws.DualStackEndpointState + + // UseFIPSEndpoint specifies the resolver must resolve a FIPS endpoint. + UseFIPSEndpoint aws.FIPSEndpointState +} + +func (o Options) GetResolvedRegion() string { + return o.ResolvedRegion +} + +func (o Options) GetDisableHTTPS() bool { + return o.DisableHTTPS +} + +func (o Options) GetUseDualStackEndpoint() aws.DualStackEndpointState { + return o.UseDualStackEndpoint +} + +func (o Options) GetUseFIPSEndpoint() aws.FIPSEndpointState { + return o.UseFIPSEndpoint +} + +func transformToSharedOptions(options Options) endpoints.Options { + return endpoints.Options{ + Logger: options.Logger, + LogDeprecated: options.LogDeprecated, + ResolvedRegion: options.ResolvedRegion, + DisableHTTPS: options.DisableHTTPS, + UseDualStackEndpoint: options.UseDualStackEndpoint, + UseFIPSEndpoint: options.UseFIPSEndpoint, + } +} + +// Resolver Kafka endpoint resolver +type Resolver struct { + partitions endpoints.Partitions +} + +// ResolveEndpoint resolves the service endpoint for the given region and options +func (r *Resolver) ResolveEndpoint(region string, options Options) (endpoint aws.Endpoint, err error) { + if len(region) == 0 { + return endpoint, &aws.MissingRegionError{} + } + + opt := transformToSharedOptions(options) + return r.partitions.ResolveEndpoint(region, opt) +} + +// New returns a new Resolver +func New() *Resolver { + return &Resolver{ + partitions: defaultPartitions, + } +} + +var partitionRegexp = struct { + Aws *regexp.Regexp + AwsCn *regexp.Regexp + AwsEusc *regexp.Regexp + AwsIso *regexp.Regexp + AwsIsoB *regexp.Regexp + AwsIsoE *regexp.Regexp + AwsIsoF *regexp.Regexp + AwsUsGov *regexp.Regexp +}{ + + Aws: regexp.MustCompile("^(us|eu|ap|sa|ca|me|af|il|mx)\\-\\w+\\-\\d+$"), + AwsCn: regexp.MustCompile("^cn\\-\\w+\\-\\d+$"), + AwsEusc: regexp.MustCompile("^eusc\\-(de)\\-\\w+\\-\\d+$"), + AwsIso: regexp.MustCompile("^us\\-iso\\-\\w+\\-\\d+$"), + AwsIsoB: regexp.MustCompile("^us\\-isob\\-\\w+\\-\\d+$"), + AwsIsoE: regexp.MustCompile("^eu\\-isoe\\-\\w+\\-\\d+$"), + AwsIsoF: regexp.MustCompile("^us\\-isof\\-\\w+\\-\\d+$"), + AwsUsGov: regexp.MustCompile("^us\\-gov\\-\\w+\\-\\d+$"), +} + +var defaultPartitions = endpoints.Partitions{ + { + ID: "aws", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.DualStackVariant, + }: { + Hostname: "kafka.{region}.api.aws", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "kafka-fips.{region}.amazonaws.com", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: endpoints.FIPSVariant | endpoints.DualStackVariant, + }: { + Hostname: "kafka-fips.{region}.api.aws", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: 0, + }: { + Hostname: "kafka.{region}.amazonaws.com", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + }, + RegionRegex: partitionRegexp.Aws, + IsRegionalized: true, + Endpoints: endpoints.Endpoints{ + endpoints.EndpointKey{ + Region: "af-south-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-east-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-east-2", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-northeast-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-northeast-2", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-northeast-3", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-south-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-south-2", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-southeast-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-southeast-2", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-southeast-3", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-southeast-4", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-southeast-5", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-southeast-6", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-southeast-7", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ca-central-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ca-central-1", + Variant: endpoints.FIPSVariant, + }: { + Hostname: "kafka-fips.ca-central-1.amazonaws.com", + }, + endpoints.EndpointKey{ + Region: "ca-west-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ca-west-1", + Variant: endpoints.FIPSVariant, + }: { + Hostname: "kafka-fips.ca-west-1.amazonaws.com", + }, + endpoints.EndpointKey{ + Region: "eu-central-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "eu-central-2", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "eu-north-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "eu-south-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "eu-south-2", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "eu-west-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "eu-west-2", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "eu-west-3", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "fips-ca-central-1", + }: endpoints.Endpoint{ + Hostname: "kafka-fips.ca-central-1.amazonaws.com", + CredentialScope: endpoints.CredentialScope{ + Region: "ca-central-1", + }, + Deprecated: aws.TrueTernary, + }, + endpoints.EndpointKey{ + Region: "fips-ca-west-1", + }: endpoints.Endpoint{ + Hostname: "kafka-fips.ca-west-1.amazonaws.com", + CredentialScope: endpoints.CredentialScope{ + Region: "ca-west-1", + }, + Deprecated: aws.TrueTernary, + }, + endpoints.EndpointKey{ + Region: "fips-us-east-1", + }: endpoints.Endpoint{ + Hostname: "kafka-fips.us-east-1.amazonaws.com", + CredentialScope: endpoints.CredentialScope{ + Region: "us-east-1", + }, + Deprecated: aws.TrueTernary, + }, + endpoints.EndpointKey{ + Region: "fips-us-east-2", + }: endpoints.Endpoint{ + Hostname: "kafka-fips.us-east-2.amazonaws.com", + CredentialScope: endpoints.CredentialScope{ + Region: "us-east-2", + }, + Deprecated: aws.TrueTernary, + }, + endpoints.EndpointKey{ + Region: "fips-us-west-1", + }: endpoints.Endpoint{ + Hostname: "kafka-fips.us-west-1.amazonaws.com", + CredentialScope: endpoints.CredentialScope{ + Region: "us-west-1", + }, + Deprecated: aws.TrueTernary, + }, + endpoints.EndpointKey{ + Region: "fips-us-west-2", + }: endpoints.Endpoint{ + Hostname: "kafka-fips.us-west-2.amazonaws.com", + CredentialScope: endpoints.CredentialScope{ + Region: "us-west-2", + }, + Deprecated: aws.TrueTernary, + }, + endpoints.EndpointKey{ + Region: "il-central-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "me-central-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "me-south-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "mx-central-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "sa-east-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "us-east-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "us-east-1", + Variant: endpoints.FIPSVariant, + }: { + Hostname: "kafka-fips.us-east-1.amazonaws.com", + }, + endpoints.EndpointKey{ + Region: "us-east-2", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "us-east-2", + Variant: endpoints.FIPSVariant, + }: { + Hostname: "kafka-fips.us-east-2.amazonaws.com", + }, + endpoints.EndpointKey{ + Region: "us-west-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "us-west-1", + Variant: endpoints.FIPSVariant, + }: { + Hostname: "kafka-fips.us-west-1.amazonaws.com", + }, + endpoints.EndpointKey{ + Region: "us-west-2", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "us-west-2", + Variant: endpoints.FIPSVariant, + }: { + Hostname: "kafka-fips.us-west-2.amazonaws.com", + }, + }, + }, + { + ID: "aws-cn", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.DualStackVariant, + }: { + Hostname: "kafka.{region}.api.amazonwebservices.com.cn", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "kafka-fips.{region}.amazonaws.com.cn", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: endpoints.FIPSVariant | endpoints.DualStackVariant, + }: { + Hostname: "kafka-fips.{region}.api.amazonwebservices.com.cn", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: 0, + }: { + Hostname: "kafka.{region}.amazonaws.com.cn", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + }, + RegionRegex: partitionRegexp.AwsCn, + IsRegionalized: true, + Endpoints: endpoints.Endpoints{ + endpoints.EndpointKey{ + Region: "cn-north-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "cn-northwest-1", + }: endpoints.Endpoint{}, + }, + }, + { + ID: "aws-eusc", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.DualStackVariant, + }: { + Hostname: "kafka.{region}.api.amazonwebservices.eu", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "kafka-fips.{region}.amazonaws.eu", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: endpoints.FIPSVariant | endpoints.DualStackVariant, + }: { + Hostname: "kafka-fips.{region}.api.amazonwebservices.eu", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: 0, + }: { + Hostname: "kafka.{region}.amazonaws.eu", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + }, + RegionRegex: partitionRegexp.AwsEusc, + IsRegionalized: true, + Endpoints: endpoints.Endpoints{ + endpoints.EndpointKey{ + Region: "eusc-de-east-1", + }: endpoints.Endpoint{}, + }, + }, + { + ID: "aws-iso", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "kafka-fips.{region}.c2s.ic.gov", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: 0, + }: { + Hostname: "kafka.{region}.c2s.ic.gov", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + }, + RegionRegex: partitionRegexp.AwsIso, + IsRegionalized: true, + }, + { + ID: "aws-iso-b", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "kafka-fips.{region}.sc2s.sgov.gov", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: 0, + }: { + Hostname: "kafka.{region}.sc2s.sgov.gov", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + }, + RegionRegex: partitionRegexp.AwsIsoB, + IsRegionalized: true, + }, + { + ID: "aws-iso-e", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "kafka-fips.{region}.cloud.adc-e.uk", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: 0, + }: { + Hostname: "kafka.{region}.cloud.adc-e.uk", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + }, + RegionRegex: partitionRegexp.AwsIsoE, + IsRegionalized: true, + }, + { + ID: "aws-iso-f", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "kafka-fips.{region}.csp.hci.ic.gov", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: 0, + }: { + Hostname: "kafka.{region}.csp.hci.ic.gov", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + }, + RegionRegex: partitionRegexp.AwsIsoF, + IsRegionalized: true, + }, + { + ID: "aws-us-gov", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.DualStackVariant, + }: { + Hostname: "kafka.{region}.api.aws", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "kafka-fips.{region}.amazonaws.com", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: endpoints.FIPSVariant | endpoints.DualStackVariant, + }: { + Hostname: "kafka-fips.{region}.api.aws", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: 0, + }: { + Hostname: "kafka.{region}.amazonaws.com", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + }, + RegionRegex: partitionRegexp.AwsUsGov, + IsRegionalized: true, + Endpoints: endpoints.Endpoints{ + endpoints.EndpointKey{ + Region: "us-gov-east-1", + }: endpoints.Endpoint{ + Hostname: "kafka.us-gov-east-1.amazonaws.com", + CredentialScope: endpoints.CredentialScope{ + Region: "us-gov-east-1", + }, + }, + endpoints.EndpointKey{ + Region: "us-gov-east-1", + Variant: endpoints.FIPSVariant, + }: { + Hostname: "kafka.us-gov-east-1.amazonaws.com", + CredentialScope: endpoints.CredentialScope{ + Region: "us-gov-east-1", + }, + }, + endpoints.EndpointKey{ + Region: "us-gov-east-1-fips", + }: endpoints.Endpoint{ + Hostname: "kafka.us-gov-east-1.amazonaws.com", + CredentialScope: endpoints.CredentialScope{ + Region: "us-gov-east-1", + }, + Deprecated: aws.TrueTernary, + }, + endpoints.EndpointKey{ + Region: "us-gov-west-1", + }: endpoints.Endpoint{ + Hostname: "kafka.us-gov-west-1.amazonaws.com", + CredentialScope: endpoints.CredentialScope{ + Region: "us-gov-west-1", + }, + }, + endpoints.EndpointKey{ + Region: "us-gov-west-1", + Variant: endpoints.FIPSVariant, + }: { + Hostname: "kafka.us-gov-west-1.amazonaws.com", + CredentialScope: endpoints.CredentialScope{ + Region: "us-gov-west-1", + }, + }, + endpoints.EndpointKey{ + Region: "us-gov-west-1-fips", + }: endpoints.Endpoint{ + Hostname: "kafka.us-gov-west-1.amazonaws.com", + CredentialScope: endpoints.CredentialScope{ + Region: "us-gov-west-1", + }, + Deprecated: aws.TrueTernary, + }, + }, + }, +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/options.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/options.go new file mode 100644 index 000000000..0dbdf2435 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/options.go @@ -0,0 +1,239 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "github.com/aws/aws-sdk-go-v2/aws" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + internalauthsmithy "github.com/aws/aws-sdk-go-v2/internal/auth/smithy" + smithyauth "github.com/aws/smithy-go/auth" + "github.com/aws/smithy-go/logging" + "github.com/aws/smithy-go/metrics" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/tracing" + smithyhttp "github.com/aws/smithy-go/transport/http" + "net/http" +) + +type HTTPClient interface { + Do(*http.Request) (*http.Response, error) +} + +type Options struct { + // Set of options to modify how an operation is invoked. These apply to all + // operations invoked for this client. Use functional options on operation call to + // modify this list for per operation behavior. + APIOptions []func(*middleware.Stack) error + + // The optional application specific identifier appended to the User-Agent header. + AppID string + + // This endpoint will be given as input to an EndpointResolverV2. It is used for + // providing a custom base endpoint that is subject to modifications by the + // processing EndpointResolverV2. + BaseEndpoint *string + + // Configures the events that will be sent to the configured logger. + ClientLogMode aws.ClientLogMode + + // The credentials object to use when signing requests. + Credentials aws.CredentialsProvider + + // The configuration DefaultsMode that the SDK should use when constructing the + // clients initial default settings. + DefaultsMode aws.DefaultsMode + + // The endpoint options to be used when attempting to resolve an endpoint. + EndpointOptions EndpointResolverOptions + + // The service endpoint resolver. + // + // Deprecated: Deprecated: EndpointResolver and WithEndpointResolver. Providing a + // value for this field will likely prevent you from using any endpoint-related + // service features released after the introduction of EndpointResolverV2 and + // BaseEndpoint. + // + // To migrate an EndpointResolver implementation that uses a custom endpoint, set + // the client option BaseEndpoint instead. + EndpointResolver EndpointResolver + + // Resolves the endpoint used for a particular service operation. This should be + // used over the deprecated EndpointResolver. + EndpointResolverV2 EndpointResolverV2 + + // Signature Version 4 (SigV4) Signer + HTTPSignerV4 HTTPSignerV4 + + // The logger writer interface to write logging messages to. + Logger logging.Logger + + // The client meter provider. + MeterProvider metrics.MeterProvider + + // The region to send requests to. (Required) + Region string + + // RetryMaxAttempts specifies the maximum number attempts an API client will call + // an operation that fails with a retryable error. A value of 0 is ignored, and + // will not be used to configure the API client created default retryer, or modify + // per operation call's retry max attempts. + // + // If specified in an operation call's functional options with a value that is + // different than the constructed client's Options, the Client's Retryer will be + // wrapped to use the operation's specific RetryMaxAttempts value. + RetryMaxAttempts int + + // RetryMode specifies the retry mode the API client will be created with, if + // Retryer option is not also specified. + // + // When creating a new API Clients this member will only be used if the Retryer + // Options member is nil. This value will be ignored if Retryer is not nil. + // + // Currently does not support per operation call overrides, may in the future. + RetryMode aws.RetryMode + + // Retryer guides how HTTP requests should be retried in case of recoverable + // failures. When nil the API client will use a default retryer. The kind of + // default retry created by the API client can be changed with the RetryMode + // option. + Retryer aws.Retryer + + // The RuntimeEnvironment configuration, only populated if the DefaultsMode is set + // to DefaultsModeAuto and is initialized using config.LoadDefaultConfig . You + // should not populate this structure programmatically, or rely on the values here + // within your applications. + RuntimeEnvironment aws.RuntimeEnvironment + + // The client tracer provider. + TracerProvider tracing.TracerProvider + + // The initial DefaultsMode used when the client options were constructed. If the + // DefaultsMode was set to aws.DefaultsModeAuto this will store what the resolved + // value was at that point in time. + // + // Currently does not support per operation call overrides, may in the future. + resolvedDefaultsMode aws.DefaultsMode + + // The HTTP client to invoke API calls with. Defaults to client's default HTTP + // implementation if nil. + HTTPClient HTTPClient + + // Client registry of operation interceptors. + Interceptors smithyhttp.InterceptorRegistry + + // The auth scheme resolver which determines how to authenticate for each + // operation. + AuthSchemeResolver AuthSchemeResolver + + // The list of auth schemes supported by the client. + AuthSchemes []smithyhttp.AuthScheme + + // Priority list of preferred auth scheme names (e.g. sigv4a). + AuthSchemePreference []string +} + +// Copy creates a clone where the APIOptions list is deep copied. +func (o Options) Copy() Options { + to := o + to.APIOptions = make([]func(*middleware.Stack) error, len(o.APIOptions)) + copy(to.APIOptions, o.APIOptions) + to.Interceptors = o.Interceptors.Copy() + + return to +} + +func (o Options) GetIdentityResolver(schemeID string) smithyauth.IdentityResolver { + if schemeID == "aws.auth#sigv4" { + return getSigV4IdentityResolver(o) + } + if schemeID == "smithy.api#noAuth" { + return &smithyauth.AnonymousIdentityResolver{} + } + return nil +} + +// WithAPIOptions returns a functional option for setting the Client's APIOptions +// option. +func WithAPIOptions(optFns ...func(*middleware.Stack) error) func(*Options) { + return func(o *Options) { + o.APIOptions = append(o.APIOptions, optFns...) + } +} + +// Deprecated: EndpointResolver and WithEndpointResolver. Providing a value for +// this field will likely prevent you from using any endpoint-related service +// features released after the introduction of EndpointResolverV2 and BaseEndpoint. +// +// To migrate an EndpointResolver implementation that uses a custom endpoint, set +// the client option BaseEndpoint instead. +func WithEndpointResolver(v EndpointResolver) func(*Options) { + return func(o *Options) { + o.EndpointResolver = v + } +} + +// WithEndpointResolverV2 returns a functional option for setting the Client's +// EndpointResolverV2 option. +func WithEndpointResolverV2(v EndpointResolverV2) func(*Options) { + return func(o *Options) { + o.EndpointResolverV2 = v + } +} + +func getSigV4IdentityResolver(o Options) smithyauth.IdentityResolver { + if o.Credentials != nil { + return &internalauthsmithy.CredentialsProviderAdapter{Provider: o.Credentials} + } + return nil +} + +// WithSigV4SigningName applies an override to the authentication workflow to +// use the given signing name for SigV4-authenticated operations. +// +// This is an advanced setting. The value here is FINAL, taking precedence over +// the resolved signing name from both auth scheme resolution and endpoint +// resolution. +func WithSigV4SigningName(name string) func(*Options) { + fn := func(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, + ) { + return next.HandleInitialize(awsmiddleware.SetSigningName(ctx, name), in) + } + return func(o *Options) { + o.APIOptions = append(o.APIOptions, func(s *middleware.Stack) error { + return s.Initialize.Add( + middleware.InitializeMiddlewareFunc("withSigV4SigningName", fn), + middleware.Before, + ) + }) + } +} + +// WithSigV4SigningRegion applies an override to the authentication workflow to +// use the given signing region for SigV4-authenticated operations. +// +// This is an advanced setting. The value here is FINAL, taking precedence over +// the resolved signing region from both auth scheme resolution and endpoint +// resolution. +func WithSigV4SigningRegion(region string) func(*Options) { + fn := func(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, + ) { + return next.HandleInitialize(awsmiddleware.SetSigningRegion(ctx, region), in) + } + return func(o *Options) { + o.APIOptions = append(o.APIOptions, func(s *middleware.Stack) error { + return s.Initialize.Add( + middleware.InitializeMiddlewareFunc("withSigV4SigningRegion", fn), + middleware.Before, + ) + }) + } +} + +func ignoreAnonymousAuth(options *Options) { + if aws.IsCredentialsProvider(options.Credentials, (*aws.AnonymousCredentials)(nil)) { + options.Credentials = nil + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/serializers.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/serializers.go new file mode 100644 index 000000000..53fe6a611 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/serializers.go @@ -0,0 +1,5983 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "bytes" + "context" + "fmt" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + smithy "github.com/aws/smithy-go" + "github.com/aws/smithy-go/encoding/httpbinding" + smithyjson "github.com/aws/smithy-go/encoding/json" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/tracing" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +type awsRestjson1_serializeOpBatchAssociateScramSecret struct { +} + +func (*awsRestjson1_serializeOpBatchAssociateScramSecret) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpBatchAssociateScramSecret) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*BatchAssociateScramSecretInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/scram-secrets") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "POST" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsBatchAssociateScramSecretInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentBatchAssociateScramSecretInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsBatchAssociateScramSecretInput(v *BatchAssociateScramSecretInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeOpDocumentBatchAssociateScramSecretInput(v *BatchAssociateScramSecretInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.SecretArnList != nil { + ok := object.Key("secretArnList") + if err := awsRestjson1_serializeDocument__listOf__string(v.SecretArnList, ok); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpBatchDisassociateScramSecret struct { +} + +func (*awsRestjson1_serializeOpBatchDisassociateScramSecret) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpBatchDisassociateScramSecret) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*BatchDisassociateScramSecretInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/scram-secrets") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "PATCH" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsBatchDisassociateScramSecretInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentBatchDisassociateScramSecretInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsBatchDisassociateScramSecretInput(v *BatchDisassociateScramSecretInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeOpDocumentBatchDisassociateScramSecretInput(v *BatchDisassociateScramSecretInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.SecretArnList != nil { + ok := object.Key("secretArnList") + if err := awsRestjson1_serializeDocument__listOf__string(v.SecretArnList, ok); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpCreateCluster struct { +} + +func (*awsRestjson1_serializeOpCreateCluster) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpCreateCluster) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*CreateClusterInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "POST" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentCreateClusterInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsCreateClusterInput(v *CreateClusterInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + return nil +} + +func awsRestjson1_serializeOpDocumentCreateClusterInput(v *CreateClusterInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.BrokerNodeGroupInfo != nil { + ok := object.Key("brokerNodeGroupInfo") + if err := awsRestjson1_serializeDocumentBrokerNodeGroupInfo(v.BrokerNodeGroupInfo, ok); err != nil { + return err + } + } + + if v.ClientAuthentication != nil { + ok := object.Key("clientAuthentication") + if err := awsRestjson1_serializeDocumentClientAuthentication(v.ClientAuthentication, ok); err != nil { + return err + } + } + + if v.ClusterName != nil { + ok := object.Key("clusterName") + ok.String(*v.ClusterName) + } + + if v.ConfigurationInfo != nil { + ok := object.Key("configurationInfo") + if err := awsRestjson1_serializeDocumentConfigurationInfo(v.ConfigurationInfo, ok); err != nil { + return err + } + } + + if v.EncryptionInfo != nil { + ok := object.Key("encryptionInfo") + if err := awsRestjson1_serializeDocumentEncryptionInfo(v.EncryptionInfo, ok); err != nil { + return err + } + } + + if len(v.EnhancedMonitoring) > 0 { + ok := object.Key("enhancedMonitoring") + ok.String(string(v.EnhancedMonitoring)) + } + + if v.KafkaVersion != nil { + ok := object.Key("kafkaVersion") + ok.String(*v.KafkaVersion) + } + + if v.LoggingInfo != nil { + ok := object.Key("loggingInfo") + if err := awsRestjson1_serializeDocumentLoggingInfo(v.LoggingInfo, ok); err != nil { + return err + } + } + + if v.NumberOfBrokerNodes != nil { + ok := object.Key("numberOfBrokerNodes") + ok.Integer(*v.NumberOfBrokerNodes) + } + + if v.OpenMonitoring != nil { + ok := object.Key("openMonitoring") + if err := awsRestjson1_serializeDocumentOpenMonitoringInfo(v.OpenMonitoring, ok); err != nil { + return err + } + } + + if v.Rebalancing != nil { + ok := object.Key("rebalancing") + if err := awsRestjson1_serializeDocumentRebalancing(v.Rebalancing, ok); err != nil { + return err + } + } + + if len(v.StorageMode) > 0 { + ok := object.Key("storageMode") + ok.String(string(v.StorageMode)) + } + + if v.Tags != nil { + ok := object.Key("tags") + if err := awsRestjson1_serializeDocument__mapOf__string(v.Tags, ok); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpCreateClusterV2 struct { +} + +func (*awsRestjson1_serializeOpCreateClusterV2) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpCreateClusterV2) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*CreateClusterV2Input) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/api/v2/clusters") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "POST" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentCreateClusterV2Input(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsCreateClusterV2Input(v *CreateClusterV2Input, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + return nil +} + +func awsRestjson1_serializeOpDocumentCreateClusterV2Input(v *CreateClusterV2Input, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.ClusterName != nil { + ok := object.Key("clusterName") + ok.String(*v.ClusterName) + } + + if v.Provisioned != nil { + ok := object.Key("provisioned") + if err := awsRestjson1_serializeDocumentProvisionedRequest(v.Provisioned, ok); err != nil { + return err + } + } + + if v.Serverless != nil { + ok := object.Key("serverless") + if err := awsRestjson1_serializeDocumentServerlessRequest(v.Serverless, ok); err != nil { + return err + } + } + + if v.Tags != nil { + ok := object.Key("tags") + if err := awsRestjson1_serializeDocument__mapOf__string(v.Tags, ok); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpCreateConfiguration struct { +} + +func (*awsRestjson1_serializeOpCreateConfiguration) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpCreateConfiguration) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*CreateConfigurationInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/configurations") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "POST" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentCreateConfigurationInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsCreateConfigurationInput(v *CreateConfigurationInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + return nil +} + +func awsRestjson1_serializeOpDocumentCreateConfigurationInput(v *CreateConfigurationInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Description != nil { + ok := object.Key("description") + ok.String(*v.Description) + } + + if v.KafkaVersions != nil { + ok := object.Key("kafkaVersions") + if err := awsRestjson1_serializeDocument__listOf__string(v.KafkaVersions, ok); err != nil { + return err + } + } + + if v.Name != nil { + ok := object.Key("name") + ok.String(*v.Name) + } + + if v.ServerProperties != nil { + ok := object.Key("serverProperties") + ok.Base64EncodeBytes(v.ServerProperties) + } + + return nil +} + +type awsRestjson1_serializeOpCreateReplicator struct { +} + +func (*awsRestjson1_serializeOpCreateReplicator) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpCreateReplicator) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*CreateReplicatorInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/replication/v1/replicators") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "POST" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentCreateReplicatorInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsCreateReplicatorInput(v *CreateReplicatorInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + return nil +} + +func awsRestjson1_serializeOpDocumentCreateReplicatorInput(v *CreateReplicatorInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Description != nil { + ok := object.Key("description") + ok.String(*v.Description) + } + + if v.KafkaClusters != nil { + ok := object.Key("kafkaClusters") + if err := awsRestjson1_serializeDocument__listOfKafkaCluster(v.KafkaClusters, ok); err != nil { + return err + } + } + + if v.ReplicationInfoList != nil { + ok := object.Key("replicationInfoList") + if err := awsRestjson1_serializeDocument__listOfReplicationInfo(v.ReplicationInfoList, ok); err != nil { + return err + } + } + + if v.ReplicatorName != nil { + ok := object.Key("replicatorName") + ok.String(*v.ReplicatorName) + } + + if v.ServiceExecutionRoleArn != nil { + ok := object.Key("serviceExecutionRoleArn") + ok.String(*v.ServiceExecutionRoleArn) + } + + if v.Tags != nil { + ok := object.Key("tags") + if err := awsRestjson1_serializeDocument__mapOf__string(v.Tags, ok); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpCreateVpcConnection struct { +} + +func (*awsRestjson1_serializeOpCreateVpcConnection) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpCreateVpcConnection) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*CreateVpcConnectionInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/vpc-connection") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "POST" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentCreateVpcConnectionInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsCreateVpcConnectionInput(v *CreateVpcConnectionInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + return nil +} + +func awsRestjson1_serializeOpDocumentCreateVpcConnectionInput(v *CreateVpcConnectionInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Authentication != nil { + ok := object.Key("authentication") + ok.String(*v.Authentication) + } + + if v.ClientSubnets != nil { + ok := object.Key("clientSubnets") + if err := awsRestjson1_serializeDocument__listOf__string(v.ClientSubnets, ok); err != nil { + return err + } + } + + if v.SecurityGroups != nil { + ok := object.Key("securityGroups") + if err := awsRestjson1_serializeDocument__listOf__string(v.SecurityGroups, ok); err != nil { + return err + } + } + + if v.Tags != nil { + ok := object.Key("tags") + if err := awsRestjson1_serializeDocument__mapOf__string(v.Tags, ok); err != nil { + return err + } + } + + if v.TargetClusterArn != nil { + ok := object.Key("targetClusterArn") + ok.String(*v.TargetClusterArn) + } + + if v.VpcId != nil { + ok := object.Key("vpcId") + ok.String(*v.VpcId) + } + + return nil +} + +type awsRestjson1_serializeOpDeleteCluster struct { +} + +func (*awsRestjson1_serializeOpDeleteCluster) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpDeleteCluster) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DeleteClusterInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "DELETE" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsDeleteClusterInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsDeleteClusterInput(v *DeleteClusterInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + if v.CurrentVersion != nil { + encoder.SetQuery("currentVersion").String(*v.CurrentVersion) + } + + return nil +} + +type awsRestjson1_serializeOpDeleteClusterPolicy struct { +} + +func (*awsRestjson1_serializeOpDeleteClusterPolicy) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpDeleteClusterPolicy) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DeleteClusterPolicyInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/policy") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "DELETE" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsDeleteClusterPolicyInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsDeleteClusterPolicyInput(v *DeleteClusterPolicyInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpDeleteConfiguration struct { +} + +func (*awsRestjson1_serializeOpDeleteConfiguration) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpDeleteConfiguration) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DeleteConfigurationInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/configurations/{Arn}") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "DELETE" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsDeleteConfigurationInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsDeleteConfigurationInput(v *DeleteConfigurationInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.Arn == nil || len(*v.Arn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member Arn must not be empty")} + } + if v.Arn != nil { + if err := encoder.SetURI("Arn").String(*v.Arn); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpDeleteReplicator struct { +} + +func (*awsRestjson1_serializeOpDeleteReplicator) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpDeleteReplicator) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DeleteReplicatorInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/replication/v1/replicators/{ReplicatorArn}") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "DELETE" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsDeleteReplicatorInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsDeleteReplicatorInput(v *DeleteReplicatorInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.CurrentVersion != nil { + encoder.SetQuery("currentVersion").String(*v.CurrentVersion) + } + + if v.ReplicatorArn == nil || len(*v.ReplicatorArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ReplicatorArn must not be empty")} + } + if v.ReplicatorArn != nil { + if err := encoder.SetURI("ReplicatorArn").String(*v.ReplicatorArn); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpDeleteVpcConnection struct { +} + +func (*awsRestjson1_serializeOpDeleteVpcConnection) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpDeleteVpcConnection) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DeleteVpcConnectionInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/vpc-connection/{Arn}") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "DELETE" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsDeleteVpcConnectionInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsDeleteVpcConnectionInput(v *DeleteVpcConnectionInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.Arn == nil || len(*v.Arn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member Arn must not be empty")} + } + if v.Arn != nil { + if err := encoder.SetURI("Arn").String(*v.Arn); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpDescribeCluster struct { +} + +func (*awsRestjson1_serializeOpDescribeCluster) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpDescribeCluster) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DescribeClusterInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsDescribeClusterInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsDescribeClusterInput(v *DescribeClusterInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpDescribeClusterOperation struct { +} + +func (*awsRestjson1_serializeOpDescribeClusterOperation) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpDescribeClusterOperation) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DescribeClusterOperationInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/operations/{ClusterOperationArn}") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsDescribeClusterOperationInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsDescribeClusterOperationInput(v *DescribeClusterOperationInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterOperationArn == nil || len(*v.ClusterOperationArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterOperationArn must not be empty")} + } + if v.ClusterOperationArn != nil { + if err := encoder.SetURI("ClusterOperationArn").String(*v.ClusterOperationArn); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpDescribeClusterOperationV2 struct { +} + +func (*awsRestjson1_serializeOpDescribeClusterOperationV2) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpDescribeClusterOperationV2) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DescribeClusterOperationV2Input) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/api/v2/operations/{ClusterOperationArn}") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsDescribeClusterOperationV2Input(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsDescribeClusterOperationV2Input(v *DescribeClusterOperationV2Input, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterOperationArn == nil || len(*v.ClusterOperationArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterOperationArn must not be empty")} + } + if v.ClusterOperationArn != nil { + if err := encoder.SetURI("ClusterOperationArn").String(*v.ClusterOperationArn); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpDescribeClusterV2 struct { +} + +func (*awsRestjson1_serializeOpDescribeClusterV2) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpDescribeClusterV2) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DescribeClusterV2Input) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/api/v2/clusters/{ClusterArn}") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsDescribeClusterV2Input(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsDescribeClusterV2Input(v *DescribeClusterV2Input, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpDescribeConfiguration struct { +} + +func (*awsRestjson1_serializeOpDescribeConfiguration) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpDescribeConfiguration) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DescribeConfigurationInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/configurations/{Arn}") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsDescribeConfigurationInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsDescribeConfigurationInput(v *DescribeConfigurationInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.Arn == nil || len(*v.Arn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member Arn must not be empty")} + } + if v.Arn != nil { + if err := encoder.SetURI("Arn").String(*v.Arn); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpDescribeConfigurationRevision struct { +} + +func (*awsRestjson1_serializeOpDescribeConfigurationRevision) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpDescribeConfigurationRevision) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DescribeConfigurationRevisionInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/configurations/{Arn}/revisions/{Revision}") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsDescribeConfigurationRevisionInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsDescribeConfigurationRevisionInput(v *DescribeConfigurationRevisionInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.Arn == nil || len(*v.Arn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member Arn must not be empty")} + } + if v.Arn != nil { + if err := encoder.SetURI("Arn").String(*v.Arn); err != nil { + return err + } + } + + if v.Revision == nil { + return &smithy.SerializationError{Err: fmt.Errorf("input member Revision must not be empty")} + } + if v.Revision != nil { + if err := encoder.SetURI("Revision").Long(*v.Revision); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpDescribeReplicator struct { +} + +func (*awsRestjson1_serializeOpDescribeReplicator) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpDescribeReplicator) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DescribeReplicatorInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/replication/v1/replicators/{ReplicatorArn}") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsDescribeReplicatorInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsDescribeReplicatorInput(v *DescribeReplicatorInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ReplicatorArn == nil || len(*v.ReplicatorArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ReplicatorArn must not be empty")} + } + if v.ReplicatorArn != nil { + if err := encoder.SetURI("ReplicatorArn").String(*v.ReplicatorArn); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpDescribeTopic struct { +} + +func (*awsRestjson1_serializeOpDescribeTopic) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpDescribeTopic) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DescribeTopicInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/topics/{TopicName}") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsDescribeTopicInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsDescribeTopicInput(v *DescribeTopicInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + if v.TopicName == nil || len(*v.TopicName) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member TopicName must not be empty")} + } + if v.TopicName != nil { + if err := encoder.SetURI("TopicName").String(*v.TopicName); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpDescribeTopicPartitions struct { +} + +func (*awsRestjson1_serializeOpDescribeTopicPartitions) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpDescribeTopicPartitions) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DescribeTopicPartitionsInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/topics/{TopicName}/partitions") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsDescribeTopicPartitionsInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsDescribeTopicPartitionsInput(v *DescribeTopicPartitionsInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + if v.MaxResults != nil { + encoder.SetQuery("maxResults").Integer(*v.MaxResults) + } + + if v.NextToken != nil { + encoder.SetQuery("nextToken").String(*v.NextToken) + } + + if v.TopicName == nil || len(*v.TopicName) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member TopicName must not be empty")} + } + if v.TopicName != nil { + if err := encoder.SetURI("TopicName").String(*v.TopicName); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpDescribeVpcConnection struct { +} + +func (*awsRestjson1_serializeOpDescribeVpcConnection) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpDescribeVpcConnection) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DescribeVpcConnectionInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/vpc-connection/{Arn}") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsDescribeVpcConnectionInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsDescribeVpcConnectionInput(v *DescribeVpcConnectionInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.Arn == nil || len(*v.Arn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member Arn must not be empty")} + } + if v.Arn != nil { + if err := encoder.SetURI("Arn").String(*v.Arn); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpGetBootstrapBrokers struct { +} + +func (*awsRestjson1_serializeOpGetBootstrapBrokers) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpGetBootstrapBrokers) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*GetBootstrapBrokersInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/bootstrap-brokers") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsGetBootstrapBrokersInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsGetBootstrapBrokersInput(v *GetBootstrapBrokersInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpGetClusterPolicy struct { +} + +func (*awsRestjson1_serializeOpGetClusterPolicy) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpGetClusterPolicy) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*GetClusterPolicyInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/policy") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsGetClusterPolicyInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsGetClusterPolicyInput(v *GetClusterPolicyInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpGetCompatibleKafkaVersions struct { +} + +func (*awsRestjson1_serializeOpGetCompatibleKafkaVersions) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpGetCompatibleKafkaVersions) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*GetCompatibleKafkaVersionsInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/compatible-kafka-versions") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsGetCompatibleKafkaVersionsInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsGetCompatibleKafkaVersionsInput(v *GetCompatibleKafkaVersionsInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn != nil { + encoder.SetQuery("clusterArn").String(*v.ClusterArn) + } + + return nil +} + +type awsRestjson1_serializeOpListClientVpcConnections struct { +} + +func (*awsRestjson1_serializeOpListClientVpcConnections) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpListClientVpcConnections) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*ListClientVpcConnectionsInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/client-vpc-connections") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsListClientVpcConnectionsInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsListClientVpcConnectionsInput(v *ListClientVpcConnectionsInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + if v.MaxResults != nil { + encoder.SetQuery("maxResults").Integer(*v.MaxResults) + } + + if v.NextToken != nil { + encoder.SetQuery("nextToken").String(*v.NextToken) + } + + return nil +} + +type awsRestjson1_serializeOpListClusterOperations struct { +} + +func (*awsRestjson1_serializeOpListClusterOperations) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpListClusterOperations) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*ListClusterOperationsInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/operations") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsListClusterOperationsInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsListClusterOperationsInput(v *ListClusterOperationsInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + if v.MaxResults != nil { + encoder.SetQuery("maxResults").Integer(*v.MaxResults) + } + + if v.NextToken != nil { + encoder.SetQuery("nextToken").String(*v.NextToken) + } + + return nil +} + +type awsRestjson1_serializeOpListClusterOperationsV2 struct { +} + +func (*awsRestjson1_serializeOpListClusterOperationsV2) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpListClusterOperationsV2) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*ListClusterOperationsV2Input) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/api/v2/clusters/{ClusterArn}/operations") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsListClusterOperationsV2Input(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsListClusterOperationsV2Input(v *ListClusterOperationsV2Input, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + if v.MaxResults != nil { + encoder.SetQuery("maxResults").Integer(*v.MaxResults) + } + + if v.NextToken != nil { + encoder.SetQuery("nextToken").String(*v.NextToken) + } + + return nil +} + +type awsRestjson1_serializeOpListClusters struct { +} + +func (*awsRestjson1_serializeOpListClusters) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpListClusters) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*ListClustersInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsListClustersInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsListClustersInput(v *ListClustersInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterNameFilter != nil { + encoder.SetQuery("clusterNameFilter").String(*v.ClusterNameFilter) + } + + if v.MaxResults != nil { + encoder.SetQuery("maxResults").Integer(*v.MaxResults) + } + + if v.NextToken != nil { + encoder.SetQuery("nextToken").String(*v.NextToken) + } + + return nil +} + +type awsRestjson1_serializeOpListClustersV2 struct { +} + +func (*awsRestjson1_serializeOpListClustersV2) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpListClustersV2) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*ListClustersV2Input) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/api/v2/clusters") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsListClustersV2Input(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsListClustersV2Input(v *ListClustersV2Input, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterNameFilter != nil { + encoder.SetQuery("clusterNameFilter").String(*v.ClusterNameFilter) + } + + if v.ClusterTypeFilter != nil { + encoder.SetQuery("clusterTypeFilter").String(*v.ClusterTypeFilter) + } + + if v.MaxResults != nil { + encoder.SetQuery("maxResults").Integer(*v.MaxResults) + } + + if v.NextToken != nil { + encoder.SetQuery("nextToken").String(*v.NextToken) + } + + return nil +} + +type awsRestjson1_serializeOpListConfigurationRevisions struct { +} + +func (*awsRestjson1_serializeOpListConfigurationRevisions) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpListConfigurationRevisions) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*ListConfigurationRevisionsInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/configurations/{Arn}/revisions") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsListConfigurationRevisionsInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsListConfigurationRevisionsInput(v *ListConfigurationRevisionsInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.Arn == nil || len(*v.Arn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member Arn must not be empty")} + } + if v.Arn != nil { + if err := encoder.SetURI("Arn").String(*v.Arn); err != nil { + return err + } + } + + if v.MaxResults != nil { + encoder.SetQuery("maxResults").Integer(*v.MaxResults) + } + + if v.NextToken != nil { + encoder.SetQuery("nextToken").String(*v.NextToken) + } + + return nil +} + +type awsRestjson1_serializeOpListConfigurations struct { +} + +func (*awsRestjson1_serializeOpListConfigurations) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpListConfigurations) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*ListConfigurationsInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/configurations") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsListConfigurationsInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsListConfigurationsInput(v *ListConfigurationsInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.MaxResults != nil { + encoder.SetQuery("maxResults").Integer(*v.MaxResults) + } + + if v.NextToken != nil { + encoder.SetQuery("nextToken").String(*v.NextToken) + } + + return nil +} + +type awsRestjson1_serializeOpListKafkaVersions struct { +} + +func (*awsRestjson1_serializeOpListKafkaVersions) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpListKafkaVersions) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*ListKafkaVersionsInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/kafka-versions") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsListKafkaVersionsInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsListKafkaVersionsInput(v *ListKafkaVersionsInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.MaxResults != nil { + encoder.SetQuery("maxResults").Integer(*v.MaxResults) + } + + if v.NextToken != nil { + encoder.SetQuery("nextToken").String(*v.NextToken) + } + + return nil +} + +type awsRestjson1_serializeOpListNodes struct { +} + +func (*awsRestjson1_serializeOpListNodes) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpListNodes) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*ListNodesInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/nodes") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsListNodesInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsListNodesInput(v *ListNodesInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + if v.MaxResults != nil { + encoder.SetQuery("maxResults").Integer(*v.MaxResults) + } + + if v.NextToken != nil { + encoder.SetQuery("nextToken").String(*v.NextToken) + } + + return nil +} + +type awsRestjson1_serializeOpListReplicators struct { +} + +func (*awsRestjson1_serializeOpListReplicators) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpListReplicators) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*ListReplicatorsInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/replication/v1/replicators") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsListReplicatorsInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsListReplicatorsInput(v *ListReplicatorsInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.MaxResults != nil { + encoder.SetQuery("maxResults").Integer(*v.MaxResults) + } + + if v.NextToken != nil { + encoder.SetQuery("nextToken").String(*v.NextToken) + } + + if v.ReplicatorNameFilter != nil { + encoder.SetQuery("replicatorNameFilter").String(*v.ReplicatorNameFilter) + } + + return nil +} + +type awsRestjson1_serializeOpListScramSecrets struct { +} + +func (*awsRestjson1_serializeOpListScramSecrets) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpListScramSecrets) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*ListScramSecretsInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/scram-secrets") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsListScramSecretsInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsListScramSecretsInput(v *ListScramSecretsInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + if v.MaxResults != nil { + encoder.SetQuery("maxResults").Integer(*v.MaxResults) + } + + if v.NextToken != nil { + encoder.SetQuery("nextToken").String(*v.NextToken) + } + + return nil +} + +type awsRestjson1_serializeOpListTagsForResource struct { +} + +func (*awsRestjson1_serializeOpListTagsForResource) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpListTagsForResource) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*ListTagsForResourceInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/tags/{ResourceArn}") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsListTagsForResourceInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsListTagsForResourceInput(v *ListTagsForResourceInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ResourceArn == nil || len(*v.ResourceArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ResourceArn must not be empty")} + } + if v.ResourceArn != nil { + if err := encoder.SetURI("ResourceArn").String(*v.ResourceArn); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpListTopics struct { +} + +func (*awsRestjson1_serializeOpListTopics) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpListTopics) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*ListTopicsInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/topics") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsListTopicsInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsListTopicsInput(v *ListTopicsInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + if v.MaxResults != nil { + encoder.SetQuery("maxResults").Integer(*v.MaxResults) + } + + if v.NextToken != nil { + encoder.SetQuery("nextToken").String(*v.NextToken) + } + + if v.TopicNameFilter != nil { + encoder.SetQuery("topicNameFilter").String(*v.TopicNameFilter) + } + + return nil +} + +type awsRestjson1_serializeOpListVpcConnections struct { +} + +func (*awsRestjson1_serializeOpListVpcConnections) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpListVpcConnections) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*ListVpcConnectionsInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/vpc-connections") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "GET" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsListVpcConnectionsInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsListVpcConnectionsInput(v *ListVpcConnectionsInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.MaxResults != nil { + encoder.SetQuery("maxResults").Integer(*v.MaxResults) + } + + if v.NextToken != nil { + encoder.SetQuery("nextToken").String(*v.NextToken) + } + + return nil +} + +type awsRestjson1_serializeOpPutClusterPolicy struct { +} + +func (*awsRestjson1_serializeOpPutClusterPolicy) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpPutClusterPolicy) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*PutClusterPolicyInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/policy") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "PUT" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsPutClusterPolicyInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentPutClusterPolicyInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsPutClusterPolicyInput(v *PutClusterPolicyInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeOpDocumentPutClusterPolicyInput(v *PutClusterPolicyInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.CurrentVersion != nil { + ok := object.Key("currentVersion") + ok.String(*v.CurrentVersion) + } + + if v.Policy != nil { + ok := object.Key("policy") + ok.String(*v.Policy) + } + + return nil +} + +type awsRestjson1_serializeOpRebootBroker struct { +} + +func (*awsRestjson1_serializeOpRebootBroker) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpRebootBroker) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*RebootBrokerInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/reboot-broker") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "PUT" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsRebootBrokerInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentRebootBrokerInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsRebootBrokerInput(v *RebootBrokerInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeOpDocumentRebootBrokerInput(v *RebootBrokerInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.BrokerIds != nil { + ok := object.Key("brokerIds") + if err := awsRestjson1_serializeDocument__listOf__string(v.BrokerIds, ok); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpRejectClientVpcConnection struct { +} + +func (*awsRestjson1_serializeOpRejectClientVpcConnection) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpRejectClientVpcConnection) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*RejectClientVpcConnectionInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/client-vpc-connection") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "PUT" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsRejectClientVpcConnectionInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentRejectClientVpcConnectionInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsRejectClientVpcConnectionInput(v *RejectClientVpcConnectionInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeOpDocumentRejectClientVpcConnectionInput(v *RejectClientVpcConnectionInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.VpcConnectionArn != nil { + ok := object.Key("vpcConnectionArn") + ok.String(*v.VpcConnectionArn) + } + + return nil +} + +type awsRestjson1_serializeOpTagResource struct { +} + +func (*awsRestjson1_serializeOpTagResource) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpTagResource) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*TagResourceInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/tags/{ResourceArn}") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "POST" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsTagResourceInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentTagResourceInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsTagResourceInput(v *TagResourceInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ResourceArn == nil || len(*v.ResourceArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ResourceArn must not be empty")} + } + if v.ResourceArn != nil { + if err := encoder.SetURI("ResourceArn").String(*v.ResourceArn); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeOpDocumentTagResourceInput(v *TagResourceInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Tags != nil { + ok := object.Key("tags") + if err := awsRestjson1_serializeDocument__mapOf__string(v.Tags, ok); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpUntagResource struct { +} + +func (*awsRestjson1_serializeOpUntagResource) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpUntagResource) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*UntagResourceInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/tags/{ResourceArn}") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "DELETE" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsUntagResourceInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsUntagResourceInput(v *UntagResourceInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ResourceArn == nil || len(*v.ResourceArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ResourceArn must not be empty")} + } + if v.ResourceArn != nil { + if err := encoder.SetURI("ResourceArn").String(*v.ResourceArn); err != nil { + return err + } + } + + if v.TagKeys != nil { + for i := range v.TagKeys { + encoder.AddQuery("tagKeys").String(v.TagKeys[i]) + } + } + + return nil +} + +type awsRestjson1_serializeOpUpdateBrokerCount struct { +} + +func (*awsRestjson1_serializeOpUpdateBrokerCount) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpUpdateBrokerCount) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*UpdateBrokerCountInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/nodes/count") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "PUT" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsUpdateBrokerCountInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentUpdateBrokerCountInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsUpdateBrokerCountInput(v *UpdateBrokerCountInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeOpDocumentUpdateBrokerCountInput(v *UpdateBrokerCountInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.CurrentVersion != nil { + ok := object.Key("currentVersion") + ok.String(*v.CurrentVersion) + } + + if v.TargetNumberOfBrokerNodes != nil { + ok := object.Key("targetNumberOfBrokerNodes") + ok.Integer(*v.TargetNumberOfBrokerNodes) + } + + return nil +} + +type awsRestjson1_serializeOpUpdateBrokerStorage struct { +} + +func (*awsRestjson1_serializeOpUpdateBrokerStorage) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpUpdateBrokerStorage) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*UpdateBrokerStorageInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/nodes/storage") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "PUT" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsUpdateBrokerStorageInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentUpdateBrokerStorageInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsUpdateBrokerStorageInput(v *UpdateBrokerStorageInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeOpDocumentUpdateBrokerStorageInput(v *UpdateBrokerStorageInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.CurrentVersion != nil { + ok := object.Key("currentVersion") + ok.String(*v.CurrentVersion) + } + + if v.TargetBrokerEBSVolumeInfo != nil { + ok := object.Key("targetBrokerEBSVolumeInfo") + if err := awsRestjson1_serializeDocument__listOfBrokerEBSVolumeInfo(v.TargetBrokerEBSVolumeInfo, ok); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpUpdateBrokerType struct { +} + +func (*awsRestjson1_serializeOpUpdateBrokerType) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpUpdateBrokerType) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*UpdateBrokerTypeInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/nodes/type") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "PUT" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsUpdateBrokerTypeInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentUpdateBrokerTypeInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsUpdateBrokerTypeInput(v *UpdateBrokerTypeInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeOpDocumentUpdateBrokerTypeInput(v *UpdateBrokerTypeInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.CurrentVersion != nil { + ok := object.Key("currentVersion") + ok.String(*v.CurrentVersion) + } + + if v.TargetInstanceType != nil { + ok := object.Key("targetInstanceType") + ok.String(*v.TargetInstanceType) + } + + return nil +} + +type awsRestjson1_serializeOpUpdateClusterConfiguration struct { +} + +func (*awsRestjson1_serializeOpUpdateClusterConfiguration) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpUpdateClusterConfiguration) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*UpdateClusterConfigurationInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/configuration") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "PUT" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsUpdateClusterConfigurationInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentUpdateClusterConfigurationInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsUpdateClusterConfigurationInput(v *UpdateClusterConfigurationInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeOpDocumentUpdateClusterConfigurationInput(v *UpdateClusterConfigurationInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.ConfigurationInfo != nil { + ok := object.Key("configurationInfo") + if err := awsRestjson1_serializeDocumentConfigurationInfo(v.ConfigurationInfo, ok); err != nil { + return err + } + } + + if v.CurrentVersion != nil { + ok := object.Key("currentVersion") + ok.String(*v.CurrentVersion) + } + + return nil +} + +type awsRestjson1_serializeOpUpdateClusterKafkaVersion struct { +} + +func (*awsRestjson1_serializeOpUpdateClusterKafkaVersion) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpUpdateClusterKafkaVersion) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*UpdateClusterKafkaVersionInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/version") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "PUT" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsUpdateClusterKafkaVersionInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentUpdateClusterKafkaVersionInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsUpdateClusterKafkaVersionInput(v *UpdateClusterKafkaVersionInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeOpDocumentUpdateClusterKafkaVersionInput(v *UpdateClusterKafkaVersionInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.ConfigurationInfo != nil { + ok := object.Key("configurationInfo") + if err := awsRestjson1_serializeDocumentConfigurationInfo(v.ConfigurationInfo, ok); err != nil { + return err + } + } + + if v.CurrentVersion != nil { + ok := object.Key("currentVersion") + ok.String(*v.CurrentVersion) + } + + if v.TargetKafkaVersion != nil { + ok := object.Key("targetKafkaVersion") + ok.String(*v.TargetKafkaVersion) + } + + return nil +} + +type awsRestjson1_serializeOpUpdateConfiguration struct { +} + +func (*awsRestjson1_serializeOpUpdateConfiguration) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpUpdateConfiguration) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*UpdateConfigurationInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/configurations/{Arn}") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "PUT" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsUpdateConfigurationInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentUpdateConfigurationInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsUpdateConfigurationInput(v *UpdateConfigurationInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.Arn == nil || len(*v.Arn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member Arn must not be empty")} + } + if v.Arn != nil { + if err := encoder.SetURI("Arn").String(*v.Arn); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeOpDocumentUpdateConfigurationInput(v *UpdateConfigurationInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Description != nil { + ok := object.Key("description") + ok.String(*v.Description) + } + + if v.ServerProperties != nil { + ok := object.Key("serverProperties") + ok.Base64EncodeBytes(v.ServerProperties) + } + + return nil +} + +type awsRestjson1_serializeOpUpdateConnectivity struct { +} + +func (*awsRestjson1_serializeOpUpdateConnectivity) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpUpdateConnectivity) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*UpdateConnectivityInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/connectivity") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "PUT" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsUpdateConnectivityInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentUpdateConnectivityInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsUpdateConnectivityInput(v *UpdateConnectivityInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeOpDocumentUpdateConnectivityInput(v *UpdateConnectivityInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.ConnectivityInfo != nil { + ok := object.Key("connectivityInfo") + if err := awsRestjson1_serializeDocumentConnectivityInfo(v.ConnectivityInfo, ok); err != nil { + return err + } + } + + if v.CurrentVersion != nil { + ok := object.Key("currentVersion") + ok.String(*v.CurrentVersion) + } + + return nil +} + +type awsRestjson1_serializeOpUpdateMonitoring struct { +} + +func (*awsRestjson1_serializeOpUpdateMonitoring) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpUpdateMonitoring) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*UpdateMonitoringInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/monitoring") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "PUT" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsUpdateMonitoringInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentUpdateMonitoringInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsUpdateMonitoringInput(v *UpdateMonitoringInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeOpDocumentUpdateMonitoringInput(v *UpdateMonitoringInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.CurrentVersion != nil { + ok := object.Key("currentVersion") + ok.String(*v.CurrentVersion) + } + + if len(v.EnhancedMonitoring) > 0 { + ok := object.Key("enhancedMonitoring") + ok.String(string(v.EnhancedMonitoring)) + } + + if v.LoggingInfo != nil { + ok := object.Key("loggingInfo") + if err := awsRestjson1_serializeDocumentLoggingInfo(v.LoggingInfo, ok); err != nil { + return err + } + } + + if v.OpenMonitoring != nil { + ok := object.Key("openMonitoring") + if err := awsRestjson1_serializeDocumentOpenMonitoringInfo(v.OpenMonitoring, ok); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpUpdateRebalancing struct { +} + +func (*awsRestjson1_serializeOpUpdateRebalancing) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpUpdateRebalancing) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*UpdateRebalancingInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/rebalancing") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "PUT" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsUpdateRebalancingInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentUpdateRebalancingInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsUpdateRebalancingInput(v *UpdateRebalancingInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeOpDocumentUpdateRebalancingInput(v *UpdateRebalancingInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.CurrentVersion != nil { + ok := object.Key("currentVersion") + ok.String(*v.CurrentVersion) + } + + if v.Rebalancing != nil { + ok := object.Key("rebalancing") + if err := awsRestjson1_serializeDocumentRebalancing(v.Rebalancing, ok); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpUpdateReplicationInfo struct { +} + +func (*awsRestjson1_serializeOpUpdateReplicationInfo) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpUpdateReplicationInfo) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*UpdateReplicationInfoInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/replication/v1/replicators/{ReplicatorArn}/replication-info") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "PUT" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsUpdateReplicationInfoInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentUpdateReplicationInfoInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsUpdateReplicationInfoInput(v *UpdateReplicationInfoInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ReplicatorArn == nil || len(*v.ReplicatorArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ReplicatorArn must not be empty")} + } + if v.ReplicatorArn != nil { + if err := encoder.SetURI("ReplicatorArn").String(*v.ReplicatorArn); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeOpDocumentUpdateReplicationInfoInput(v *UpdateReplicationInfoInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.ConsumerGroupReplication != nil { + ok := object.Key("consumerGroupReplication") + if err := awsRestjson1_serializeDocumentConsumerGroupReplicationUpdate(v.ConsumerGroupReplication, ok); err != nil { + return err + } + } + + if v.CurrentVersion != nil { + ok := object.Key("currentVersion") + ok.String(*v.CurrentVersion) + } + + if v.SourceKafkaClusterArn != nil { + ok := object.Key("sourceKafkaClusterArn") + ok.String(*v.SourceKafkaClusterArn) + } + + if v.TargetKafkaClusterArn != nil { + ok := object.Key("targetKafkaClusterArn") + ok.String(*v.TargetKafkaClusterArn) + } + + if v.TopicReplication != nil { + ok := object.Key("topicReplication") + if err := awsRestjson1_serializeDocumentTopicReplicationUpdate(v.TopicReplication, ok); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpUpdateSecurity struct { +} + +func (*awsRestjson1_serializeOpUpdateSecurity) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpUpdateSecurity) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*UpdateSecurityInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/security") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "PATCH" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsUpdateSecurityInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentUpdateSecurityInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsUpdateSecurityInput(v *UpdateSecurityInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeOpDocumentUpdateSecurityInput(v *UpdateSecurityInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.ClientAuthentication != nil { + ok := object.Key("clientAuthentication") + if err := awsRestjson1_serializeDocumentClientAuthentication(v.ClientAuthentication, ok); err != nil { + return err + } + } + + if v.CurrentVersion != nil { + ok := object.Key("currentVersion") + ok.String(*v.CurrentVersion) + } + + if v.EncryptionInfo != nil { + ok := object.Key("encryptionInfo") + if err := awsRestjson1_serializeDocumentEncryptionInfo(v.EncryptionInfo, ok); err != nil { + return err + } + } + + return nil +} + +type awsRestjson1_serializeOpUpdateStorage struct { +} + +func (*awsRestjson1_serializeOpUpdateStorage) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpUpdateStorage) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*UpdateStorageInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/clusters/{ClusterArn}/storage") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "PUT" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if err := awsRestjson1_serializeOpHttpBindingsUpdateStorageInput(input, restEncoder); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentUpdateStorageInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsUpdateStorageInput(v *UpdateStorageInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + if v.ClusterArn == nil || len(*v.ClusterArn) == 0 { + return &smithy.SerializationError{Err: fmt.Errorf("input member ClusterArn must not be empty")} + } + if v.ClusterArn != nil { + if err := encoder.SetURI("ClusterArn").String(*v.ClusterArn); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeOpDocumentUpdateStorageInput(v *UpdateStorageInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.CurrentVersion != nil { + ok := object.Key("currentVersion") + ok.String(*v.CurrentVersion) + } + + if v.ProvisionedThroughput != nil { + ok := object.Key("provisionedThroughput") + if err := awsRestjson1_serializeDocumentProvisionedThroughput(v.ProvisionedThroughput, ok); err != nil { + return err + } + } + + if len(v.StorageMode) > 0 { + ok := object.Key("storageMode") + ok.String(string(v.StorageMode)) + } + + if v.VolumeSizeGB != nil { + ok := object.Key("volumeSizeGB") + ok.Integer(*v.VolumeSizeGB) + } + + return nil +} + +func awsRestjson1_serializeDocument__listOf__string(v []string, value smithyjson.Value) error { + array := value.Array() + defer array.Close() + + for i := range v { + av := array.Value() + av.String(v[i]) + } + return nil +} + +func awsRestjson1_serializeDocument__listOf__stringMax249(v []string, value smithyjson.Value) error { + array := value.Array() + defer array.Close() + + for i := range v { + av := array.Value() + av.String(v[i]) + } + return nil +} + +func awsRestjson1_serializeDocument__listOf__stringMax256(v []string, value smithyjson.Value) error { + array := value.Array() + defer array.Close() + + for i := range v { + av := array.Value() + av.String(v[i]) + } + return nil +} + +func awsRestjson1_serializeDocument__listOfBrokerEBSVolumeInfo(v []types.BrokerEBSVolumeInfo, value smithyjson.Value) error { + array := value.Array() + defer array.Close() + + for i := range v { + av := array.Value() + if err := awsRestjson1_serializeDocumentBrokerEBSVolumeInfo(&v[i], av); err != nil { + return err + } + } + return nil +} + +func awsRestjson1_serializeDocument__listOfKafkaCluster(v []types.KafkaCluster, value smithyjson.Value) error { + array := value.Array() + defer array.Close() + + for i := range v { + av := array.Value() + if err := awsRestjson1_serializeDocumentKafkaCluster(&v[i], av); err != nil { + return err + } + } + return nil +} + +func awsRestjson1_serializeDocument__listOfReplicationInfo(v []types.ReplicationInfo, value smithyjson.Value) error { + array := value.Array() + defer array.Close() + + for i := range v { + av := array.Value() + if err := awsRestjson1_serializeDocumentReplicationInfo(&v[i], av); err != nil { + return err + } + } + return nil +} + +func awsRestjson1_serializeDocument__listOfVpcConfig(v []types.VpcConfig, value smithyjson.Value) error { + array := value.Array() + defer array.Close() + + for i := range v { + av := array.Value() + if err := awsRestjson1_serializeDocumentVpcConfig(&v[i], av); err != nil { + return err + } + } + return nil +} + +func awsRestjson1_serializeDocument__mapOf__string(v map[string]string, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + for key := range v { + om := object.Key(key) + om.String(v[key]) + } + return nil +} + +func awsRestjson1_serializeDocumentAmazonMskCluster(v *types.AmazonMskCluster, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.MskClusterArn != nil { + ok := object.Key("mskClusterArn") + ok.String(*v.MskClusterArn) + } + + return nil +} + +func awsRestjson1_serializeDocumentBrokerEBSVolumeInfo(v *types.BrokerEBSVolumeInfo, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.KafkaBrokerNodeId != nil { + ok := object.Key("kafkaBrokerNodeId") + ok.String(*v.KafkaBrokerNodeId) + } + + if v.ProvisionedThroughput != nil { + ok := object.Key("provisionedThroughput") + if err := awsRestjson1_serializeDocumentProvisionedThroughput(v.ProvisionedThroughput, ok); err != nil { + return err + } + } + + if v.VolumeSizeGB != nil { + ok := object.Key("volumeSizeGB") + ok.Integer(*v.VolumeSizeGB) + } + + return nil +} + +func awsRestjson1_serializeDocumentBrokerLogs(v *types.BrokerLogs, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.CloudWatchLogs != nil { + ok := object.Key("cloudWatchLogs") + if err := awsRestjson1_serializeDocumentCloudWatchLogs(v.CloudWatchLogs, ok); err != nil { + return err + } + } + + if v.Firehose != nil { + ok := object.Key("firehose") + if err := awsRestjson1_serializeDocumentFirehose(v.Firehose, ok); err != nil { + return err + } + } + + if v.S3 != nil { + ok := object.Key("s3") + if err := awsRestjson1_serializeDocumentS3(v.S3, ok); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeDocumentBrokerNodeGroupInfo(v *types.BrokerNodeGroupInfo, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if len(v.BrokerAZDistribution) > 0 { + ok := object.Key("brokerAZDistribution") + ok.String(string(v.BrokerAZDistribution)) + } + + if v.ClientSubnets != nil { + ok := object.Key("clientSubnets") + if err := awsRestjson1_serializeDocument__listOf__string(v.ClientSubnets, ok); err != nil { + return err + } + } + + if v.ConnectivityInfo != nil { + ok := object.Key("connectivityInfo") + if err := awsRestjson1_serializeDocumentConnectivityInfo(v.ConnectivityInfo, ok); err != nil { + return err + } + } + + if v.InstanceType != nil { + ok := object.Key("instanceType") + ok.String(*v.InstanceType) + } + + if v.SecurityGroups != nil { + ok := object.Key("securityGroups") + if err := awsRestjson1_serializeDocument__listOf__string(v.SecurityGroups, ok); err != nil { + return err + } + } + + if v.StorageInfo != nil { + ok := object.Key("storageInfo") + if err := awsRestjson1_serializeDocumentStorageInfo(v.StorageInfo, ok); err != nil { + return err + } + } + + if v.ZoneIds != nil { + ok := object.Key("zoneIds") + if err := awsRestjson1_serializeDocument__listOf__string(v.ZoneIds, ok); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeDocumentClientAuthentication(v *types.ClientAuthentication, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Sasl != nil { + ok := object.Key("sasl") + if err := awsRestjson1_serializeDocumentSasl(v.Sasl, ok); err != nil { + return err + } + } + + if v.Tls != nil { + ok := object.Key("tls") + if err := awsRestjson1_serializeDocumentTls(v.Tls, ok); err != nil { + return err + } + } + + if v.Unauthenticated != nil { + ok := object.Key("unauthenticated") + if err := awsRestjson1_serializeDocumentUnauthenticated(v.Unauthenticated, ok); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeDocumentCloudWatchLogs(v *types.CloudWatchLogs, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Enabled != nil { + ok := object.Key("enabled") + ok.Boolean(*v.Enabled) + } + + if v.LogGroup != nil { + ok := object.Key("logGroup") + ok.String(*v.LogGroup) + } + + return nil +} + +func awsRestjson1_serializeDocumentConfigurationInfo(v *types.ConfigurationInfo, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Arn != nil { + ok := object.Key("arn") + ok.String(*v.Arn) + } + + if v.Revision != nil { + ok := object.Key("revision") + ok.Long(*v.Revision) + } + + return nil +} + +func awsRestjson1_serializeDocumentConnectivityInfo(v *types.ConnectivityInfo, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.PublicAccess != nil { + ok := object.Key("publicAccess") + if err := awsRestjson1_serializeDocumentPublicAccess(v.PublicAccess, ok); err != nil { + return err + } + } + + if v.VpcConnectivity != nil { + ok := object.Key("vpcConnectivity") + if err := awsRestjson1_serializeDocumentVpcConnectivity(v.VpcConnectivity, ok); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeDocumentConsumerGroupReplication(v *types.ConsumerGroupReplication, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.ConsumerGroupsToExclude != nil { + ok := object.Key("consumerGroupsToExclude") + if err := awsRestjson1_serializeDocument__listOf__stringMax256(v.ConsumerGroupsToExclude, ok); err != nil { + return err + } + } + + if v.ConsumerGroupsToReplicate != nil { + ok := object.Key("consumerGroupsToReplicate") + if err := awsRestjson1_serializeDocument__listOf__stringMax256(v.ConsumerGroupsToReplicate, ok); err != nil { + return err + } + } + + if v.DetectAndCopyNewConsumerGroups != nil { + ok := object.Key("detectAndCopyNewConsumerGroups") + ok.Boolean(*v.DetectAndCopyNewConsumerGroups) + } + + if v.SynchroniseConsumerGroupOffsets != nil { + ok := object.Key("synchroniseConsumerGroupOffsets") + ok.Boolean(*v.SynchroniseConsumerGroupOffsets) + } + + return nil +} + +func awsRestjson1_serializeDocumentConsumerGroupReplicationUpdate(v *types.ConsumerGroupReplicationUpdate, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.ConsumerGroupsToExclude != nil { + ok := object.Key("consumerGroupsToExclude") + if err := awsRestjson1_serializeDocument__listOf__stringMax256(v.ConsumerGroupsToExclude, ok); err != nil { + return err + } + } + + if v.ConsumerGroupsToReplicate != nil { + ok := object.Key("consumerGroupsToReplicate") + if err := awsRestjson1_serializeDocument__listOf__stringMax256(v.ConsumerGroupsToReplicate, ok); err != nil { + return err + } + } + + if v.DetectAndCopyNewConsumerGroups != nil { + ok := object.Key("detectAndCopyNewConsumerGroups") + ok.Boolean(*v.DetectAndCopyNewConsumerGroups) + } + + if v.SynchroniseConsumerGroupOffsets != nil { + ok := object.Key("synchroniseConsumerGroupOffsets") + ok.Boolean(*v.SynchroniseConsumerGroupOffsets) + } + + return nil +} + +func awsRestjson1_serializeDocumentEBSStorageInfo(v *types.EBSStorageInfo, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.ProvisionedThroughput != nil { + ok := object.Key("provisionedThroughput") + if err := awsRestjson1_serializeDocumentProvisionedThroughput(v.ProvisionedThroughput, ok); err != nil { + return err + } + } + + if v.VolumeSize != nil { + ok := object.Key("volumeSize") + ok.Integer(*v.VolumeSize) + } + + return nil +} + +func awsRestjson1_serializeDocumentEncryptionAtRest(v *types.EncryptionAtRest, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.DataVolumeKMSKeyId != nil { + ok := object.Key("dataVolumeKMSKeyId") + ok.String(*v.DataVolumeKMSKeyId) + } + + return nil +} + +func awsRestjson1_serializeDocumentEncryptionInfo(v *types.EncryptionInfo, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.EncryptionAtRest != nil { + ok := object.Key("encryptionAtRest") + if err := awsRestjson1_serializeDocumentEncryptionAtRest(v.EncryptionAtRest, ok); err != nil { + return err + } + } + + if v.EncryptionInTransit != nil { + ok := object.Key("encryptionInTransit") + if err := awsRestjson1_serializeDocumentEncryptionInTransit(v.EncryptionInTransit, ok); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeDocumentEncryptionInTransit(v *types.EncryptionInTransit, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if len(v.ClientBroker) > 0 { + ok := object.Key("clientBroker") + ok.String(string(v.ClientBroker)) + } + + if v.InCluster != nil { + ok := object.Key("inCluster") + ok.Boolean(*v.InCluster) + } + + return nil +} + +func awsRestjson1_serializeDocumentFirehose(v *types.Firehose, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.DeliveryStream != nil { + ok := object.Key("deliveryStream") + ok.String(*v.DeliveryStream) + } + + if v.Enabled != nil { + ok := object.Key("enabled") + ok.Boolean(*v.Enabled) + } + + return nil +} + +func awsRestjson1_serializeDocumentIam(v *types.Iam, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Enabled != nil { + ok := object.Key("enabled") + ok.Boolean(*v.Enabled) + } + + return nil +} + +func awsRestjson1_serializeDocumentJmxExporterInfo(v *types.JmxExporterInfo, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.EnabledInBroker != nil { + ok := object.Key("enabledInBroker") + ok.Boolean(*v.EnabledInBroker) + } + + return nil +} + +func awsRestjson1_serializeDocumentKafkaCluster(v *types.KafkaCluster, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.AmazonMskCluster != nil { + ok := object.Key("amazonMskCluster") + if err := awsRestjson1_serializeDocumentAmazonMskCluster(v.AmazonMskCluster, ok); err != nil { + return err + } + } + + if v.VpcConfig != nil { + ok := object.Key("vpcConfig") + if err := awsRestjson1_serializeDocumentKafkaClusterClientVpcConfig(v.VpcConfig, ok); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeDocumentKafkaClusterClientVpcConfig(v *types.KafkaClusterClientVpcConfig, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.SecurityGroupIds != nil { + ok := object.Key("securityGroupIds") + if err := awsRestjson1_serializeDocument__listOf__string(v.SecurityGroupIds, ok); err != nil { + return err + } + } + + if v.SubnetIds != nil { + ok := object.Key("subnetIds") + if err := awsRestjson1_serializeDocument__listOf__string(v.SubnetIds, ok); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeDocumentLoggingInfo(v *types.LoggingInfo, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.BrokerLogs != nil { + ok := object.Key("brokerLogs") + if err := awsRestjson1_serializeDocumentBrokerLogs(v.BrokerLogs, ok); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeDocumentNodeExporterInfo(v *types.NodeExporterInfo, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.EnabledInBroker != nil { + ok := object.Key("enabledInBroker") + ok.Boolean(*v.EnabledInBroker) + } + + return nil +} + +func awsRestjson1_serializeDocumentOpenMonitoringInfo(v *types.OpenMonitoringInfo, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Prometheus != nil { + ok := object.Key("prometheus") + if err := awsRestjson1_serializeDocumentPrometheusInfo(v.Prometheus, ok); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeDocumentPrometheusInfo(v *types.PrometheusInfo, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.JmxExporter != nil { + ok := object.Key("jmxExporter") + if err := awsRestjson1_serializeDocumentJmxExporterInfo(v.JmxExporter, ok); err != nil { + return err + } + } + + if v.NodeExporter != nil { + ok := object.Key("nodeExporter") + if err := awsRestjson1_serializeDocumentNodeExporterInfo(v.NodeExporter, ok); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeDocumentProvisionedRequest(v *types.ProvisionedRequest, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.BrokerNodeGroupInfo != nil { + ok := object.Key("brokerNodeGroupInfo") + if err := awsRestjson1_serializeDocumentBrokerNodeGroupInfo(v.BrokerNodeGroupInfo, ok); err != nil { + return err + } + } + + if v.ClientAuthentication != nil { + ok := object.Key("clientAuthentication") + if err := awsRestjson1_serializeDocumentClientAuthentication(v.ClientAuthentication, ok); err != nil { + return err + } + } + + if v.ConfigurationInfo != nil { + ok := object.Key("configurationInfo") + if err := awsRestjson1_serializeDocumentConfigurationInfo(v.ConfigurationInfo, ok); err != nil { + return err + } + } + + if v.EncryptionInfo != nil { + ok := object.Key("encryptionInfo") + if err := awsRestjson1_serializeDocumentEncryptionInfo(v.EncryptionInfo, ok); err != nil { + return err + } + } + + if len(v.EnhancedMonitoring) > 0 { + ok := object.Key("enhancedMonitoring") + ok.String(string(v.EnhancedMonitoring)) + } + + if v.KafkaVersion != nil { + ok := object.Key("kafkaVersion") + ok.String(*v.KafkaVersion) + } + + if v.LoggingInfo != nil { + ok := object.Key("loggingInfo") + if err := awsRestjson1_serializeDocumentLoggingInfo(v.LoggingInfo, ok); err != nil { + return err + } + } + + if v.NumberOfBrokerNodes != nil { + ok := object.Key("numberOfBrokerNodes") + ok.Integer(*v.NumberOfBrokerNodes) + } + + if v.OpenMonitoring != nil { + ok := object.Key("openMonitoring") + if err := awsRestjson1_serializeDocumentOpenMonitoringInfo(v.OpenMonitoring, ok); err != nil { + return err + } + } + + if v.Rebalancing != nil { + ok := object.Key("rebalancing") + if err := awsRestjson1_serializeDocumentRebalancing(v.Rebalancing, ok); err != nil { + return err + } + } + + if len(v.StorageMode) > 0 { + ok := object.Key("storageMode") + ok.String(string(v.StorageMode)) + } + + return nil +} + +func awsRestjson1_serializeDocumentProvisionedThroughput(v *types.ProvisionedThroughput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Enabled != nil { + ok := object.Key("enabled") + ok.Boolean(*v.Enabled) + } + + if v.VolumeThroughput != nil { + ok := object.Key("volumeThroughput") + ok.Integer(*v.VolumeThroughput) + } + + return nil +} + +func awsRestjson1_serializeDocumentPublicAccess(v *types.PublicAccess, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Type != nil { + ok := object.Key("type") + ok.String(*v.Type) + } + + return nil +} + +func awsRestjson1_serializeDocumentRebalancing(v *types.Rebalancing, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if len(v.Status) > 0 { + ok := object.Key("status") + ok.String(string(v.Status)) + } + + return nil +} + +func awsRestjson1_serializeDocumentReplicationInfo(v *types.ReplicationInfo, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.ConsumerGroupReplication != nil { + ok := object.Key("consumerGroupReplication") + if err := awsRestjson1_serializeDocumentConsumerGroupReplication(v.ConsumerGroupReplication, ok); err != nil { + return err + } + } + + if v.SourceKafkaClusterArn != nil { + ok := object.Key("sourceKafkaClusterArn") + ok.String(*v.SourceKafkaClusterArn) + } + + if len(v.TargetCompressionType) > 0 { + ok := object.Key("targetCompressionType") + ok.String(string(v.TargetCompressionType)) + } + + if v.TargetKafkaClusterArn != nil { + ok := object.Key("targetKafkaClusterArn") + ok.String(*v.TargetKafkaClusterArn) + } + + if v.TopicReplication != nil { + ok := object.Key("topicReplication") + if err := awsRestjson1_serializeDocumentTopicReplication(v.TopicReplication, ok); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeDocumentReplicationStartingPosition(v *types.ReplicationStartingPosition, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if len(v.Type) > 0 { + ok := object.Key("type") + ok.String(string(v.Type)) + } + + return nil +} + +func awsRestjson1_serializeDocumentReplicationTopicNameConfiguration(v *types.ReplicationTopicNameConfiguration, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if len(v.Type) > 0 { + ok := object.Key("type") + ok.String(string(v.Type)) + } + + return nil +} + +func awsRestjson1_serializeDocumentS3(v *types.S3, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Bucket != nil { + ok := object.Key("bucket") + ok.String(*v.Bucket) + } + + if v.Enabled != nil { + ok := object.Key("enabled") + ok.Boolean(*v.Enabled) + } + + if v.Prefix != nil { + ok := object.Key("prefix") + ok.String(*v.Prefix) + } + + return nil +} + +func awsRestjson1_serializeDocumentSasl(v *types.Sasl, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Iam != nil { + ok := object.Key("iam") + if err := awsRestjson1_serializeDocumentIam(v.Iam, ok); err != nil { + return err + } + } + + if v.Scram != nil { + ok := object.Key("scram") + if err := awsRestjson1_serializeDocumentScram(v.Scram, ok); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeDocumentScram(v *types.Scram, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Enabled != nil { + ok := object.Key("enabled") + ok.Boolean(*v.Enabled) + } + + return nil +} + +func awsRestjson1_serializeDocumentServerlessClientAuthentication(v *types.ServerlessClientAuthentication, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Sasl != nil { + ok := object.Key("sasl") + if err := awsRestjson1_serializeDocumentServerlessSasl(v.Sasl, ok); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeDocumentServerlessRequest(v *types.ServerlessRequest, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.ClientAuthentication != nil { + ok := object.Key("clientAuthentication") + if err := awsRestjson1_serializeDocumentServerlessClientAuthentication(v.ClientAuthentication, ok); err != nil { + return err + } + } + + if v.VpcConfigs != nil { + ok := object.Key("vpcConfigs") + if err := awsRestjson1_serializeDocument__listOfVpcConfig(v.VpcConfigs, ok); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeDocumentServerlessSasl(v *types.ServerlessSasl, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Iam != nil { + ok := object.Key("iam") + if err := awsRestjson1_serializeDocumentIam(v.Iam, ok); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeDocumentStorageInfo(v *types.StorageInfo, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.EbsStorageInfo != nil { + ok := object.Key("ebsStorageInfo") + if err := awsRestjson1_serializeDocumentEBSStorageInfo(v.EbsStorageInfo, ok); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeDocumentTls(v *types.Tls, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.CertificateAuthorityArnList != nil { + ok := object.Key("certificateAuthorityArnList") + if err := awsRestjson1_serializeDocument__listOf__string(v.CertificateAuthorityArnList, ok); err != nil { + return err + } + } + + if v.Enabled != nil { + ok := object.Key("enabled") + ok.Boolean(*v.Enabled) + } + + return nil +} + +func awsRestjson1_serializeDocumentTopicReplication(v *types.TopicReplication, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.CopyAccessControlListsForTopics != nil { + ok := object.Key("copyAccessControlListsForTopics") + ok.Boolean(*v.CopyAccessControlListsForTopics) + } + + if v.CopyTopicConfigurations != nil { + ok := object.Key("copyTopicConfigurations") + ok.Boolean(*v.CopyTopicConfigurations) + } + + if v.DetectAndCopyNewTopics != nil { + ok := object.Key("detectAndCopyNewTopics") + ok.Boolean(*v.DetectAndCopyNewTopics) + } + + if v.StartingPosition != nil { + ok := object.Key("startingPosition") + if err := awsRestjson1_serializeDocumentReplicationStartingPosition(v.StartingPosition, ok); err != nil { + return err + } + } + + if v.TopicNameConfiguration != nil { + ok := object.Key("topicNameConfiguration") + if err := awsRestjson1_serializeDocumentReplicationTopicNameConfiguration(v.TopicNameConfiguration, ok); err != nil { + return err + } + } + + if v.TopicsToExclude != nil { + ok := object.Key("topicsToExclude") + if err := awsRestjson1_serializeDocument__listOf__stringMax249(v.TopicsToExclude, ok); err != nil { + return err + } + } + + if v.TopicsToReplicate != nil { + ok := object.Key("topicsToReplicate") + if err := awsRestjson1_serializeDocument__listOf__stringMax249(v.TopicsToReplicate, ok); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeDocumentTopicReplicationUpdate(v *types.TopicReplicationUpdate, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.CopyAccessControlListsForTopics != nil { + ok := object.Key("copyAccessControlListsForTopics") + ok.Boolean(*v.CopyAccessControlListsForTopics) + } + + if v.CopyTopicConfigurations != nil { + ok := object.Key("copyTopicConfigurations") + ok.Boolean(*v.CopyTopicConfigurations) + } + + if v.DetectAndCopyNewTopics != nil { + ok := object.Key("detectAndCopyNewTopics") + ok.Boolean(*v.DetectAndCopyNewTopics) + } + + if v.TopicsToExclude != nil { + ok := object.Key("topicsToExclude") + if err := awsRestjson1_serializeDocument__listOf__stringMax249(v.TopicsToExclude, ok); err != nil { + return err + } + } + + if v.TopicsToReplicate != nil { + ok := object.Key("topicsToReplicate") + if err := awsRestjson1_serializeDocument__listOf__stringMax249(v.TopicsToReplicate, ok); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeDocumentUnauthenticated(v *types.Unauthenticated, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Enabled != nil { + ok := object.Key("enabled") + ok.Boolean(*v.Enabled) + } + + return nil +} + +func awsRestjson1_serializeDocumentVpcConfig(v *types.VpcConfig, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.SecurityGroupIds != nil { + ok := object.Key("securityGroupIds") + if err := awsRestjson1_serializeDocument__listOf__string(v.SecurityGroupIds, ok); err != nil { + return err + } + } + + if v.SubnetIds != nil { + ok := object.Key("subnetIds") + if err := awsRestjson1_serializeDocument__listOf__string(v.SubnetIds, ok); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeDocumentVpcConnectivity(v *types.VpcConnectivity, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.ClientAuthentication != nil { + ok := object.Key("clientAuthentication") + if err := awsRestjson1_serializeDocumentVpcConnectivityClientAuthentication(v.ClientAuthentication, ok); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeDocumentVpcConnectivityClientAuthentication(v *types.VpcConnectivityClientAuthentication, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Sasl != nil { + ok := object.Key("sasl") + if err := awsRestjson1_serializeDocumentVpcConnectivitySasl(v.Sasl, ok); err != nil { + return err + } + } + + if v.Tls != nil { + ok := object.Key("tls") + if err := awsRestjson1_serializeDocumentVpcConnectivityTls(v.Tls, ok); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeDocumentVpcConnectivityIam(v *types.VpcConnectivityIam, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Enabled != nil { + ok := object.Key("enabled") + ok.Boolean(*v.Enabled) + } + + return nil +} + +func awsRestjson1_serializeDocumentVpcConnectivitySasl(v *types.VpcConnectivitySasl, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Iam != nil { + ok := object.Key("iam") + if err := awsRestjson1_serializeDocumentVpcConnectivityIam(v.Iam, ok); err != nil { + return err + } + } + + if v.Scram != nil { + ok := object.Key("scram") + if err := awsRestjson1_serializeDocumentVpcConnectivityScram(v.Scram, ok); err != nil { + return err + } + } + + return nil +} + +func awsRestjson1_serializeDocumentVpcConnectivityScram(v *types.VpcConnectivityScram, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Enabled != nil { + ok := object.Key("enabled") + ok.Boolean(*v.Enabled) + } + + return nil +} + +func awsRestjson1_serializeDocumentVpcConnectivityTls(v *types.VpcConnectivityTls, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Enabled != nil { + ok := object.Key("enabled") + ok.Boolean(*v.Enabled) + } + + return nil +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/types/enums.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/types/enums.go new file mode 100644 index 000000000..7d18f203d --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/types/enums.go @@ -0,0 +1,393 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package types + +type BrokerAZDistribution string + +// Enum values for BrokerAZDistribution +const ( + BrokerAZDistributionDefault BrokerAZDistribution = "DEFAULT" +) + +// Values returns all known values for BrokerAZDistribution. Note that this can be +// expanded in the future, and so it is only as up to date as the client. +// +// The ordering of this slice is not guaranteed to be stable across updates. +func (BrokerAZDistribution) Values() []BrokerAZDistribution { + return []BrokerAZDistribution{ + "DEFAULT", + } +} + +type ClientBroker string + +// Enum values for ClientBroker +const ( + ClientBrokerTls ClientBroker = "TLS" + ClientBrokerTlsPlaintext ClientBroker = "TLS_PLAINTEXT" + ClientBrokerPlaintext ClientBroker = "PLAINTEXT" +) + +// Values returns all known values for ClientBroker. Note that this can be +// expanded in the future, and so it is only as up to date as the client. +// +// The ordering of this slice is not guaranteed to be stable across updates. +func (ClientBroker) Values() []ClientBroker { + return []ClientBroker{ + "TLS", + "TLS_PLAINTEXT", + "PLAINTEXT", + } +} + +type ClusterState string + +// Enum values for ClusterState +const ( + ClusterStateActive ClusterState = "ACTIVE" + ClusterStateCreating ClusterState = "CREATING" + ClusterStateDeleting ClusterState = "DELETING" + ClusterStateFailed ClusterState = "FAILED" + ClusterStateHealing ClusterState = "HEALING" + ClusterStateMaintenance ClusterState = "MAINTENANCE" + ClusterStateRebootingBroker ClusterState = "REBOOTING_BROKER" + ClusterStateUpdating ClusterState = "UPDATING" +) + +// Values returns all known values for ClusterState. Note that this can be +// expanded in the future, and so it is only as up to date as the client. +// +// The ordering of this slice is not guaranteed to be stable across updates. +func (ClusterState) Values() []ClusterState { + return []ClusterState{ + "ACTIVE", + "CREATING", + "DELETING", + "FAILED", + "HEALING", + "MAINTENANCE", + "REBOOTING_BROKER", + "UPDATING", + } +} + +type ClusterType string + +// Enum values for ClusterType +const ( + ClusterTypeProvisioned ClusterType = "PROVISIONED" + ClusterTypeServerless ClusterType = "SERVERLESS" +) + +// Values returns all known values for ClusterType. Note that this can be expanded +// in the future, and so it is only as up to date as the client. +// +// The ordering of this slice is not guaranteed to be stable across updates. +func (ClusterType) Values() []ClusterType { + return []ClusterType{ + "PROVISIONED", + "SERVERLESS", + } +} + +type ConfigurationState string + +// Enum values for ConfigurationState +const ( + ConfigurationStateActive ConfigurationState = "ACTIVE" + ConfigurationStateDeleting ConfigurationState = "DELETING" + ConfigurationStateDeleteFailed ConfigurationState = "DELETE_FAILED" +) + +// Values returns all known values for ConfigurationState. Note that this can be +// expanded in the future, and so it is only as up to date as the client. +// +// The ordering of this slice is not guaranteed to be stable across updates. +func (ConfigurationState) Values() []ConfigurationState { + return []ConfigurationState{ + "ACTIVE", + "DELETING", + "DELETE_FAILED", + } +} + +type CustomerActionStatus string + +// Enum values for CustomerActionStatus +const ( + CustomerActionStatusCriticalActionRequired CustomerActionStatus = "CRITICAL_ACTION_REQUIRED" + CustomerActionStatusActionRecommended CustomerActionStatus = "ACTION_RECOMMENDED" + CustomerActionStatusNone CustomerActionStatus = "NONE" +) + +// Values returns all known values for CustomerActionStatus. Note that this can be +// expanded in the future, and so it is only as up to date as the client. +// +// The ordering of this slice is not guaranteed to be stable across updates. +func (CustomerActionStatus) Values() []CustomerActionStatus { + return []CustomerActionStatus{ + "CRITICAL_ACTION_REQUIRED", + "ACTION_RECOMMENDED", + "NONE", + } +} + +type EnhancedMonitoring string + +// Enum values for EnhancedMonitoring +const ( + EnhancedMonitoringDefault EnhancedMonitoring = "DEFAULT" + EnhancedMonitoringPerBroker EnhancedMonitoring = "PER_BROKER" + EnhancedMonitoringPerTopicPerBroker EnhancedMonitoring = "PER_TOPIC_PER_BROKER" + EnhancedMonitoringPerTopicPerPartition EnhancedMonitoring = "PER_TOPIC_PER_PARTITION" +) + +// Values returns all known values for EnhancedMonitoring. Note that this can be +// expanded in the future, and so it is only as up to date as the client. +// +// The ordering of this slice is not guaranteed to be stable across updates. +func (EnhancedMonitoring) Values() []EnhancedMonitoring { + return []EnhancedMonitoring{ + "DEFAULT", + "PER_BROKER", + "PER_TOPIC_PER_BROKER", + "PER_TOPIC_PER_PARTITION", + } +} + +type KafkaVersionStatus string + +// Enum values for KafkaVersionStatus +const ( + KafkaVersionStatusActive KafkaVersionStatus = "ACTIVE" + KafkaVersionStatusDeprecated KafkaVersionStatus = "DEPRECATED" +) + +// Values returns all known values for KafkaVersionStatus. Note that this can be +// expanded in the future, and so it is only as up to date as the client. +// +// The ordering of this slice is not guaranteed to be stable across updates. +func (KafkaVersionStatus) Values() []KafkaVersionStatus { + return []KafkaVersionStatus{ + "ACTIVE", + "DEPRECATED", + } +} + +type NodeType string + +// Enum values for NodeType +const ( + NodeTypeBroker NodeType = "BROKER" +) + +// Values returns all known values for NodeType. Note that this can be expanded in +// the future, and so it is only as up to date as the client. +// +// The ordering of this slice is not guaranteed to be stable across updates. +func (NodeType) Values() []NodeType { + return []NodeType{ + "BROKER", + } +} + +type RebalancingStatus string + +// Enum values for RebalancingStatus +const ( + RebalancingStatusPaused RebalancingStatus = "PAUSED" + RebalancingStatusActive RebalancingStatus = "ACTIVE" +) + +// Values returns all known values for RebalancingStatus. Note that this can be +// expanded in the future, and so it is only as up to date as the client. +// +// The ordering of this slice is not guaranteed to be stable across updates. +func (RebalancingStatus) Values() []RebalancingStatus { + return []RebalancingStatus{ + "PAUSED", + "ACTIVE", + } +} + +type ReplicationStartingPositionType string + +// Enum values for ReplicationStartingPositionType +const ( + ReplicationStartingPositionTypeLatest ReplicationStartingPositionType = "LATEST" + ReplicationStartingPositionTypeEarliest ReplicationStartingPositionType = "EARLIEST" +) + +// Values returns all known values for ReplicationStartingPositionType. Note that +// this can be expanded in the future, and so it is only as up to date as the +// client. +// +// The ordering of this slice is not guaranteed to be stable across updates. +func (ReplicationStartingPositionType) Values() []ReplicationStartingPositionType { + return []ReplicationStartingPositionType{ + "LATEST", + "EARLIEST", + } +} + +type ReplicationTopicNameConfigurationType string + +// Enum values for ReplicationTopicNameConfigurationType +const ( + ReplicationTopicNameConfigurationTypePrefixedWithSourceClusterAlias ReplicationTopicNameConfigurationType = "PREFIXED_WITH_SOURCE_CLUSTER_ALIAS" + ReplicationTopicNameConfigurationTypeIdentical ReplicationTopicNameConfigurationType = "IDENTICAL" +) + +// Values returns all known values for ReplicationTopicNameConfigurationType. Note +// that this can be expanded in the future, and so it is only as up to date as the +// client. +// +// The ordering of this slice is not guaranteed to be stable across updates. +func (ReplicationTopicNameConfigurationType) Values() []ReplicationTopicNameConfigurationType { + return []ReplicationTopicNameConfigurationType{ + "PREFIXED_WITH_SOURCE_CLUSTER_ALIAS", + "IDENTICAL", + } +} + +type ReplicatorState string + +// Enum values for ReplicatorState +const ( + ReplicatorStateRunning ReplicatorState = "RUNNING" + ReplicatorStateCreating ReplicatorState = "CREATING" + ReplicatorStateUpdating ReplicatorState = "UPDATING" + ReplicatorStateDeleting ReplicatorState = "DELETING" + ReplicatorStateFailed ReplicatorState = "FAILED" +) + +// Values returns all known values for ReplicatorState. Note that this can be +// expanded in the future, and so it is only as up to date as the client. +// +// The ordering of this slice is not guaranteed to be stable across updates. +func (ReplicatorState) Values() []ReplicatorState { + return []ReplicatorState{ + "RUNNING", + "CREATING", + "UPDATING", + "DELETING", + "FAILED", + } +} + +type StorageMode string + +// Enum values for StorageMode +const ( + StorageModeLocal StorageMode = "LOCAL" + StorageModeTiered StorageMode = "TIERED" +) + +// Values returns all known values for StorageMode. Note that this can be expanded +// in the future, and so it is only as up to date as the client. +// +// The ordering of this slice is not guaranteed to be stable across updates. +func (StorageMode) Values() []StorageMode { + return []StorageMode{ + "LOCAL", + "TIERED", + } +} + +type TargetCompressionType string + +// Enum values for TargetCompressionType +const ( + TargetCompressionTypeNone TargetCompressionType = "NONE" + TargetCompressionTypeGzip TargetCompressionType = "GZIP" + TargetCompressionTypeSnappy TargetCompressionType = "SNAPPY" + TargetCompressionTypeLz4 TargetCompressionType = "LZ4" + TargetCompressionTypeZstd TargetCompressionType = "ZSTD" +) + +// Values returns all known values for TargetCompressionType. Note that this can +// be expanded in the future, and so it is only as up to date as the client. +// +// The ordering of this slice is not guaranteed to be stable across updates. +func (TargetCompressionType) Values() []TargetCompressionType { + return []TargetCompressionType{ + "NONE", + "GZIP", + "SNAPPY", + "LZ4", + "ZSTD", + } +} + +type TopicState string + +// Enum values for TopicState +const ( + TopicStateCreating TopicState = "CREATING" + TopicStateUpdating TopicState = "UPDATING" + TopicStateDeleting TopicState = "DELETING" + TopicStateActive TopicState = "ACTIVE" +) + +// Values returns all known values for TopicState. Note that this can be expanded +// in the future, and so it is only as up to date as the client. +// +// The ordering of this slice is not guaranteed to be stable across updates. +func (TopicState) Values() []TopicState { + return []TopicState{ + "CREATING", + "UPDATING", + "DELETING", + "ACTIVE", + } +} + +type UserIdentityType string + +// Enum values for UserIdentityType +const ( + UserIdentityTypeAwsaccount UserIdentityType = "AWSACCOUNT" + UserIdentityTypeAwsservice UserIdentityType = "AWSSERVICE" +) + +// Values returns all known values for UserIdentityType. Note that this can be +// expanded in the future, and so it is only as up to date as the client. +// +// The ordering of this slice is not guaranteed to be stable across updates. +func (UserIdentityType) Values() []UserIdentityType { + return []UserIdentityType{ + "AWSACCOUNT", + "AWSSERVICE", + } +} + +type VpcConnectionState string + +// Enum values for VpcConnectionState +const ( + VpcConnectionStateCreating VpcConnectionState = "CREATING" + VpcConnectionStateAvailable VpcConnectionState = "AVAILABLE" + VpcConnectionStateInactive VpcConnectionState = "INACTIVE" + VpcConnectionStateDeactivating VpcConnectionState = "DEACTIVATING" + VpcConnectionStateDeleting VpcConnectionState = "DELETING" + VpcConnectionStateFailed VpcConnectionState = "FAILED" + VpcConnectionStateRejected VpcConnectionState = "REJECTED" + VpcConnectionStateRejecting VpcConnectionState = "REJECTING" +) + +// Values returns all known values for VpcConnectionState. Note that this can be +// expanded in the future, and so it is only as up to date as the client. +// +// The ordering of this slice is not guaranteed to be stable across updates. +func (VpcConnectionState) Values() []VpcConnectionState { + return []VpcConnectionState{ + "CREATING", + "AVAILABLE", + "INACTIVE", + "DEACTIVATING", + "DELETING", + "FAILED", + "REJECTED", + "REJECTING", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/types/errors.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/types/errors.go new file mode 100644 index 000000000..14230c706 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/types/errors.go @@ -0,0 +1,232 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package types + +import ( + "fmt" + smithy "github.com/aws/smithy-go" +) + +// Returns information about an error. +type BadRequestException struct { + Message *string + + ErrorCodeOverride *string + + InvalidParameter *string + + noSmithyDocumentSerde +} + +func (e *BadRequestException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *BadRequestException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *BadRequestException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "BadRequestException" + } + return *e.ErrorCodeOverride +} +func (e *BadRequestException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// Returns information about an error. +type ConflictException struct { + Message *string + + ErrorCodeOverride *string + + InvalidParameter *string + + noSmithyDocumentSerde +} + +func (e *ConflictException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *ConflictException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *ConflictException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "ConflictException" + } + return *e.ErrorCodeOverride +} +func (e *ConflictException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// Returns information about an error. +type ForbiddenException struct { + Message *string + + ErrorCodeOverride *string + + InvalidParameter *string + + noSmithyDocumentSerde +} + +func (e *ForbiddenException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *ForbiddenException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *ForbiddenException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "ForbiddenException" + } + return *e.ErrorCodeOverride +} +func (e *ForbiddenException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// Returns information about an error. +type InternalServerErrorException struct { + Message *string + + ErrorCodeOverride *string + + InvalidParameter *string + + noSmithyDocumentSerde +} + +func (e *InternalServerErrorException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *InternalServerErrorException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *InternalServerErrorException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "InternalServerErrorException" + } + return *e.ErrorCodeOverride +} +func (e *InternalServerErrorException) ErrorFault() smithy.ErrorFault { return smithy.FaultServer } + +// Returns information about an error. +type NotFoundException struct { + Message *string + + ErrorCodeOverride *string + + InvalidParameter *string + + noSmithyDocumentSerde +} + +func (e *NotFoundException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *NotFoundException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *NotFoundException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "NotFoundException" + } + return *e.ErrorCodeOverride +} +func (e *NotFoundException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// Returns information about an error. +type ServiceUnavailableException struct { + Message *string + + ErrorCodeOverride *string + + InvalidParameter *string + + noSmithyDocumentSerde +} + +func (e *ServiceUnavailableException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *ServiceUnavailableException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *ServiceUnavailableException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "ServiceUnavailableException" + } + return *e.ErrorCodeOverride +} +func (e *ServiceUnavailableException) ErrorFault() smithy.ErrorFault { return smithy.FaultServer } + +// Returns information about an error. +type TooManyRequestsException struct { + Message *string + + ErrorCodeOverride *string + + InvalidParameter *string + + noSmithyDocumentSerde +} + +func (e *TooManyRequestsException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *TooManyRequestsException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *TooManyRequestsException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "TooManyRequestsException" + } + return *e.ErrorCodeOverride +} +func (e *TooManyRequestsException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// Returns information about an error. +type UnauthorizedException struct { + Message *string + + ErrorCodeOverride *string + + InvalidParameter *string + + noSmithyDocumentSerde +} + +func (e *UnauthorizedException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *UnauthorizedException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *UnauthorizedException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "UnauthorizedException" + } + return *e.ErrorCodeOverride +} +func (e *UnauthorizedException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/types/types.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/types/types.go new file mode 100644 index 000000000..1c9749f71 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/types/types.go @@ -0,0 +1,1657 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package types + +import ( + smithydocument "github.com/aws/smithy-go/document" + "time" +) + +// Details of an Amazon MSK Cluster. +type AmazonMskCluster struct { + + // The Amazon Resource Name (ARN) of an Amazon MSK cluster. + // + // This member is required. + MskClusterArn *string + + noSmithyDocumentSerde +} + +// Information regarding UpdateBrokerCount. +type BrokerCountUpdateInfo struct { + + // Kafka Broker IDs of brokers being created. + CreatedBrokerIds []float64 + + // Kafka Broker IDs of brokers being deleted. + DeletedBrokerIds []float64 + + noSmithyDocumentSerde +} + +// Specifies the EBS volume upgrade information. The broker identifier must be set +// to the keyword ALL. This means the changes apply to all the brokers in the +// cluster. +type BrokerEBSVolumeInfo struct { + + // The ID of the broker to update. + // + // This member is required. + KafkaBrokerNodeId *string + + // EBS volume provisioned throughput information. + ProvisionedThroughput *ProvisionedThroughput + + // Size of the EBS volume to update. + VolumeSizeGB *int32 + + noSmithyDocumentSerde +} + +type BrokerLogs struct { + CloudWatchLogs *CloudWatchLogs + + Firehose *Firehose + + S3 *S3 + + noSmithyDocumentSerde +} + +// Describes the setup to be used for Apache Kafka broker nodes in the cluster. +type BrokerNodeGroupInfo struct { + + // The list of subnets to connect to in the client virtual private cloud (VPC). + // AWS creates elastic network interfaces inside these subnets. Client applications + // use elastic network interfaces to produce and consume data. Client subnets can't + // occupy the Availability Zone with ID use use1-az3. + // + // This member is required. + ClientSubnets []string + + // The type of Amazon EC2 instances to use for Apache Kafka brokers. The following + // instance types are allowed: kafka.m5.large, kafka.m5.xlarge, kafka.m5.2xlarge, + // kafka.m5.4xlarge, kafka.m5.12xlarge, and kafka.m5.24xlarge. + // + // This member is required. + InstanceType *string + + // The distribution of broker nodes across Availability Zones. This is an optional + // parameter. If you don't specify it, Amazon MSK gives it the value DEFAULT. You + // can also explicitly set this parameter to the value DEFAULT. No other values are + // currently allowed. + // + // Amazon MSK distributes the broker nodes evenly across the Availability Zones + // that correspond to the subnets you provide when you create the cluster. + BrokerAZDistribution BrokerAZDistribution + + // Information about the broker access configuration. + ConnectivityInfo *ConnectivityInfo + + // The AWS security groups to associate with the elastic network interfaces in + // order to specify who can connect to and communicate with the Amazon MSK cluster. + // If you don't specify a security group, Amazon MSK uses the default security + // group associated with the VPC. + SecurityGroups []string + + // Contains information about storage volumes attached to MSK broker nodes. + StorageInfo *StorageInfo + + // The list of zoneIds for the cluster in the virtual private cloud (VPC). + ZoneIds []string + + noSmithyDocumentSerde +} + +// BrokerNodeInfo +type BrokerNodeInfo struct { + + // The attached elastic network interface of the broker. + AttachedENIId *string + + // The ID of the broker. + BrokerId *float64 + + // The client subnet to which this broker node belongs. + ClientSubnet *string + + // The virtual private cloud (VPC) of the client. + ClientVpcIpAddress *string + + // Information about the version of software currently deployed on the Apache + // Kafka brokers in the cluster. + CurrentBrokerSoftwareInfo *BrokerSoftwareInfo + + // Endpoints for accessing the broker. + Endpoints []string + + noSmithyDocumentSerde +} + +// Information about the current software installed on the cluster. +type BrokerSoftwareInfo struct { + + // The Amazon Resource Name (ARN) of the configuration used for the cluster. This + // field isn't visible in this preview release. + ConfigurationArn *string + + // The revision of the configuration to use. This field isn't visible in this + // preview release. + ConfigurationRevision *int64 + + // The version of Apache Kafka. + KafkaVersion *string + + noSmithyDocumentSerde +} + +// Includes all client authentication information. +type ClientAuthentication struct { + + // Details for ClientAuthentication using SASL. + Sasl *Sasl + + // Details for ClientAuthentication using TLS. + Tls *Tls + + // Contains information about unauthenticated traffic to the cluster. + Unauthenticated *Unauthenticated + + noSmithyDocumentSerde +} + +// The client VPC connection object. +type ClientVpcConnection struct { + + // The ARN that identifies the Vpc Connection. + // + // This member is required. + VpcConnectionArn *string + + // Information about the auth scheme of Vpc Connection. + Authentication *string + + // Creation time of the Vpc Connection. + CreationTime *time.Time + + // The Owner of the Vpc Connection. + Owner *string + + // State of the Vpc Connection. + State VpcConnectionState + + noSmithyDocumentSerde +} + +type CloudWatchLogs struct { + + // This member is required. + Enabled *bool + + LogGroup *string + + noSmithyDocumentSerde +} + +// Returns information about a cluster. +type Cluster struct { + + // The Amazon Resource Name (ARN) that uniquely identifies a cluster operation. + ActiveOperationArn *string + + // The Amazon Resource Name (ARN) that uniquely identifies the cluster. + ClusterArn *string + + // The name of the cluster. + ClusterName *string + + // Cluster Type. + ClusterType ClusterType + + // The time when the cluster was created. + CreationTime *time.Time + + // The current version of the MSK cluster. + CurrentVersion *string + + // Information about the provisioned cluster. + Provisioned *Provisioned + + // Information about the serverless cluster. + Serverless *Serverless + + // The state of the cluster. The possible states are ACTIVE, CREATING, DELETING, + // FAILED, HEALING, MAINTENANCE, REBOOTING_BROKER, and UPDATING. + State ClusterState + + // State Info for the Amazon MSK cluster. + StateInfo *StateInfo + + // Tags attached to the cluster. + Tags map[string]string + + noSmithyDocumentSerde +} + +// Returns information about a cluster. +type ClusterInfo struct { + + // Arn of active cluster operation. + ActiveOperationArn *string + + // Information about the broker nodes. + BrokerNodeGroupInfo *BrokerNodeGroupInfo + + // Includes all client authentication information. + ClientAuthentication *ClientAuthentication + + // The Amazon Resource Name (ARN) that uniquely identifies the cluster. + ClusterArn *string + + // The name of the cluster. + ClusterName *string + + // The time when the cluster was created. + CreationTime *time.Time + + // Information about the version of software currently deployed on the Apache + // Kafka brokers in the cluster. + CurrentBrokerSoftwareInfo *BrokerSoftwareInfo + + // The current version of the MSK cluster. + CurrentVersion *string + + // Determines if there is an action required from the customer. + CustomerActionStatus CustomerActionStatus + + // Includes all encryption-related information. + EncryptionInfo *EncryptionInfo + + // Specifies which metrics are gathered for the MSK cluster. This property has the + // following possible values: DEFAULT, PER_BROKER, PER_TOPIC_PER_BROKER, and + // PER_TOPIC_PER_PARTITION. For a list of the metrics associated with each of these + // levels of monitoring, see [Monitoring]. + // + // [Monitoring]: https://docs.aws.amazon.com/msk/latest/developerguide/monitoring.html + EnhancedMonitoring EnhancedMonitoring + + LoggingInfo *LoggingInfo + + // The number of broker nodes in the cluster. + NumberOfBrokerNodes *int32 + + // Settings for open monitoring using Prometheus. + OpenMonitoring *OpenMonitoring + + // Contains information about intelligent rebalancing for new MSK Provisioned + // clusters with Express brokers. By default, intelligent rebalancing status is + // ACTIVE. + Rebalancing *Rebalancing + + // The state of the cluster. The possible states are ACTIVE, CREATING, DELETING, + // FAILED, HEALING, MAINTENANCE, REBOOTING_BROKER, and UPDATING. + State ClusterState + + StateInfo *StateInfo + + // This controls storage mode for supported storage tiers. + StorageMode StorageMode + + // Tags attached to the cluster. + Tags map[string]string + + // The connection string to use to connect to the Apache ZooKeeper cluster. + ZookeeperConnectString *string + + // The connection string to use to connect to zookeeper cluster on Tls port. + ZookeeperConnectStringTls *string + + noSmithyDocumentSerde +} + +// Returns information about a cluster operation. +type ClusterOperationInfo struct { + + // The ID of the API request that triggered this operation. + ClientRequestId *string + + // ARN of the cluster. + ClusterArn *string + + // The time that the operation was created. + CreationTime *time.Time + + // The time at which the operation finished. + EndTime *time.Time + + // Describes the error if the operation fails. + ErrorInfo *ErrorInfo + + // ARN of the cluster operation. + OperationArn *string + + // State of the cluster operation. + OperationState *string + + // Steps completed during the operation. + OperationSteps []ClusterOperationStep + + // Type of the cluster operation. + OperationType *string + + // Information about cluster attributes before a cluster is updated. + SourceClusterInfo *MutableClusterInfo + + // Information about cluster attributes after a cluster is updated. + TargetClusterInfo *MutableClusterInfo + + // Description of the VPC connection for CreateVpcConnection and + // DeleteVpcConnection operations. + VpcConnectionInfo *VpcConnectionInfo + + noSmithyDocumentSerde +} + +// Step taken during a cluster operation. +type ClusterOperationStep struct { + + // Information about the step and its status. + StepInfo *ClusterOperationStepInfo + + // The name of the step. + StepName *string + + noSmithyDocumentSerde +} + +// State information about the operation step. +type ClusterOperationStepInfo struct { + + // The steps current status. + StepStatus *string + + noSmithyDocumentSerde +} + +// Returns information about a cluster operation. +type ClusterOperationV2 struct { + + // ARN of the cluster. + ClusterArn *string + + // Type of the backend cluster. + ClusterType ClusterType + + // The time at which the operation finished. + EndTime *time.Time + + // If cluster operation failed from an error, it describes the error. + ErrorInfo *ErrorInfo + + // ARN of the cluster operation. + OperationArn *string + + // State of the cluster operation. + OperationState *string + + // Type of the cluster operation. + OperationType *string + + // Properties of a provisioned cluster. + Provisioned *ClusterOperationV2Provisioned + + // Properties of a serverless cluster. + Serverless *ClusterOperationV2Serverless + + // The time at which operation was started. + StartTime *time.Time + + noSmithyDocumentSerde +} + +// Returns information about a provisioned cluster operation. +type ClusterOperationV2Provisioned struct { + + // Steps completed during the operation. + OperationSteps []ClusterOperationStep + + // Information about cluster attributes before a cluster is updated. + SourceClusterInfo *MutableClusterInfo + + // Information about cluster attributes after a cluster is updated. + TargetClusterInfo *MutableClusterInfo + + // Description of the VPC connection for CreateVpcConnection and + // DeleteVpcConnection operations. + VpcConnectionInfo *VpcConnectionInfo + + noSmithyDocumentSerde +} + +// Returns information about a serverless cluster operation. +type ClusterOperationV2Serverless struct { + + // Description of the VPC connection for CreateVpcConnection and + // DeleteVpcConnection operations. + VpcConnectionInfo *VpcConnectionInfoServerless + + noSmithyDocumentSerde +} + +// Returns information about a cluster operation. +type ClusterOperationV2Summary struct { + + // ARN of the cluster. + ClusterArn *string + + // Type of the backend cluster. + ClusterType ClusterType + + // The time at which the operation finished. + EndTime *time.Time + + // ARN of the cluster operation. + OperationArn *string + + // State of the cluster operation. + OperationState *string + + // Type of the cluster operation. + OperationType *string + + // The time at which operation was started. + StartTime *time.Time + + noSmithyDocumentSerde +} + +// Contains source Apache Kafka versions and compatible target Apache Kafka +// versions. +type CompatibleKafkaVersion struct { + + // An Apache Kafka version. + SourceVersion *string + + // A list of Apache Kafka versions. + TargetVersions []string + + noSmithyDocumentSerde +} + +// Represents an MSK Configuration. +type Configuration struct { + + // The Amazon Resource Name (ARN) of the configuration. + // + // This member is required. + Arn *string + + // The time when the configuration was created. + // + // This member is required. + CreationTime *time.Time + + // The description of the configuration. + // + // This member is required. + Description *string + + // An array of the versions of Apache Kafka with which you can use this MSK + // configuration. You can use this configuration for an MSK cluster only if the + // Apache Kafka version specified for the cluster appears in this array. + // + // This member is required. + KafkaVersions []string + + // Latest revision of the configuration. + // + // This member is required. + LatestRevision *ConfigurationRevision + + // The name of the configuration. + // + // This member is required. + Name *string + + // The state of the configuration. The possible states are ACTIVE, DELETING, and + // DELETE_FAILED. + // + // This member is required. + State ConfigurationState + + noSmithyDocumentSerde +} + +// Specifies the configuration to use for the brokers. +type ConfigurationInfo struct { + + // ARN of the configuration to use. + // + // This member is required. + Arn *string + + // The revision of the configuration to use. + // + // This member is required. + Revision *int64 + + noSmithyDocumentSerde +} + +// Describes a configuration revision. +type ConfigurationRevision struct { + + // The time when the configuration revision was created. + // + // This member is required. + CreationTime *time.Time + + // The revision number. + // + // This member is required. + Revision *int64 + + // The description of the configuration revision. + Description *string + + noSmithyDocumentSerde +} + +// Information about the broker access configuration. +type ConnectivityInfo struct { + + // Public access control for brokers. + PublicAccess *PublicAccess + + // VPC connectivity access control for brokers. + VpcConnectivity *VpcConnectivity + + noSmithyDocumentSerde +} + +// Details about consumer group replication. +type ConsumerGroupReplication struct { + + // List of regular expression patterns indicating the consumer groups to copy. + // + // This member is required. + ConsumerGroupsToReplicate []string + + // List of regular expression patterns indicating the consumer groups that should + // not be replicated. + ConsumerGroupsToExclude []string + + // Enables synchronization of consumer groups to target cluster. + DetectAndCopyNewConsumerGroups *bool + + // Enables synchronization of consumer group offsets to target cluster. The + // translated offsets will be written to topic __consumer_offsets. + SynchroniseConsumerGroupOffsets *bool + + noSmithyDocumentSerde +} + +// Details about consumer group replication. +type ConsumerGroupReplicationUpdate struct { + + // List of regular expression patterns indicating the consumer groups that should + // not be replicated. + // + // This member is required. + ConsumerGroupsToExclude []string + + // List of regular expression patterns indicating the consumer groups to copy. + // + // This member is required. + ConsumerGroupsToReplicate []string + + // Enables synchronization of consumer groups to target cluster. + // + // This member is required. + DetectAndCopyNewConsumerGroups *bool + + // Enables synchronization of consumer group offsets to target cluster. The + // translated offsets will be written to topic __consumer_offsets. + // + // This member is required. + SynchroniseConsumerGroupOffsets *bool + + noSmithyDocumentSerde +} + +// Controller node information. +type ControllerNodeInfo struct { + + // Endpoints for accessing the Controller. + Endpoints []string + + noSmithyDocumentSerde +} + +// Contains information about the EBS storage volumes attached to Apache Kafka +// broker nodes. +type EBSStorageInfo struct { + + // EBS volume provisioned throughput information. + ProvisionedThroughput *ProvisionedThroughput + + // The size in GiB of the EBS volume for the data drive on each broker node. + VolumeSize *int32 + + noSmithyDocumentSerde +} + +// The data-volume encryption details. +type EncryptionAtRest struct { + + // The ARN of the AWS KMS key for encrypting data at rest. If you don't specify a + // KMS key, MSK creates one for you and uses it. + // + // This member is required. + DataVolumeKMSKeyId *string + + noSmithyDocumentSerde +} + +// Includes encryption-related information, such as the AWS KMS key used for +// encrypting data at rest and whether you want MSK to encrypt your data in +// transit. +type EncryptionInfo struct { + + // The data-volume encryption details. + EncryptionAtRest *EncryptionAtRest + + // The details for encryption in transit. + EncryptionInTransit *EncryptionInTransit + + noSmithyDocumentSerde +} + +// The settings for encrypting data in transit. +type EncryptionInTransit struct { + + // Indicates the encryption setting for data in transit between clients and + // brokers. The following are the possible values. + // + // TLS means that client-broker communication is enabled with TLS only. + // + // TLS_PLAINTEXT means that client-broker communication is enabled for both + // TLS-encrypted, as well as plaintext data. + // + // PLAINTEXT means that client-broker communication is enabled in plaintext only. + // + // The default value is TLS_PLAINTEXT. + ClientBroker ClientBroker + + // When set to true, it indicates that data communication among the broker nodes + // of the cluster is encrypted. When set to false, the communication happens in + // plaintext. + // + // The default value is true. + InCluster *bool + + noSmithyDocumentSerde +} + +// Returns information about an error state of the cluster. +type ErrorInfo struct { + + // A number describing the error programmatically. + ErrorCode *string + + // An optional field to provide more details about the error. + ErrorString *string + + noSmithyDocumentSerde +} + +type Firehose struct { + + // This member is required. + Enabled *bool + + DeliveryStream *string + + noSmithyDocumentSerde +} + +// Details for IAM access control. +type Iam struct { + + // Indicates whether IAM access control is enabled. + Enabled *bool + + noSmithyDocumentSerde +} + +// Indicates whether you want to turn on or turn off the JMX Exporter. +type JmxExporter struct { + + // Indicates whether you want to turn on or turn off the JMX Exporter. + // + // This member is required. + EnabledInBroker *bool + + noSmithyDocumentSerde +} + +// Indicates whether you want to turn on or turn off the JMX Exporter. +type JmxExporterInfo struct { + + // Indicates whether you want to turn on or turn off the JMX Exporter. + // + // This member is required. + EnabledInBroker *bool + + noSmithyDocumentSerde +} + +// Information about Kafka Cluster to be used as source / target for replication. +type KafkaCluster struct { + + // Details of an Amazon MSK Cluster. + // + // This member is required. + AmazonMskCluster *AmazonMskCluster + + // Details of an Amazon VPC which has network connectivity to the Apache Kafka + // cluster. + // + // This member is required. + VpcConfig *KafkaClusterClientVpcConfig + + noSmithyDocumentSerde +} + +// Details of an Amazon VPC which has network connectivity to the Apache Kafka +// cluster. +type KafkaClusterClientVpcConfig struct { + + // The list of subnets in the client VPC to connect to. + // + // This member is required. + SubnetIds []string + + // The security groups to attach to the ENIs for the broker nodes. + SecurityGroupIds []string + + noSmithyDocumentSerde +} + +// Information about Kafka Cluster used as source / target for replication. +type KafkaClusterDescription struct { + + // Details of an Amazon MSK Cluster. + AmazonMskCluster *AmazonMskCluster + + // The alias of the Kafka cluster. Used to prefix names of replicated topics. + KafkaClusterAlias *string + + // Details of an Amazon VPC which has network connectivity to the Apache Kafka + // cluster. + VpcConfig *KafkaClusterClientVpcConfig + + noSmithyDocumentSerde +} + +// Summarized information about Kafka Cluster used as source / target for +// replication. +type KafkaClusterSummary struct { + + // Details of an Amazon MSK Cluster. + AmazonMskCluster *AmazonMskCluster + + // The alias of the Kafka cluster. Used to prefix names of replicated topics. + KafkaClusterAlias *string + + noSmithyDocumentSerde +} + +type KafkaVersion struct { + Status KafkaVersionStatus + + Version *string + + noSmithyDocumentSerde +} + +type LoggingInfo struct { + + // This member is required. + BrokerLogs *BrokerLogs + + noSmithyDocumentSerde +} + +// Information about cluster attributes that can be updated via update APIs. +type MutableClusterInfo struct { + + // Describes brokers being changed during a broker count update. + BrokerCountUpdateInfo *BrokerCountUpdateInfo + + // Specifies the size of the EBS volume and the ID of the associated broker. + BrokerEBSVolumeInfo []BrokerEBSVolumeInfo + + // Includes all client authentication information. + ClientAuthentication *ClientAuthentication + + // Information about the changes in the configuration of the brokers. + ConfigurationInfo *ConfigurationInfo + + // Information about the broker access configuration. + ConnectivityInfo *ConnectivityInfo + + // Includes all encryption-related information. + EncryptionInfo *EncryptionInfo + + // Specifies which Apache Kafka metrics Amazon MSK gathers and sends to Amazon + // CloudWatch for this cluster. + EnhancedMonitoring EnhancedMonitoring + + // Information about the Amazon MSK broker type. + InstanceType *string + + // The Apache Kafka version. + KafkaVersion *string + + // You can configure your MSK cluster to send broker logs to different destination + // types. This is a container for the configuration details related to broker logs. + LoggingInfo *LoggingInfo + + // The number of broker nodes in the cluster. + NumberOfBrokerNodes *int32 + + // The settings for open monitoring. + OpenMonitoring *OpenMonitoring + + // Describes the intelligent rebalancing configuration of an MSK Provisioned + // cluster with Express brokers. + Rebalancing *Rebalancing + + // This controls storage mode for supported storage tiers. + StorageMode StorageMode + + noSmithyDocumentSerde +} + +// Indicates whether you want to turn on or turn off the Node Exporter. +type NodeExporter struct { + + // Indicates whether you want to turn on or turn off the Node Exporter. + // + // This member is required. + EnabledInBroker *bool + + noSmithyDocumentSerde +} + +// Indicates whether you want to turn on or turn off the Node Exporter. +type NodeExporterInfo struct { + + // Indicates whether you want to turn on or turn off the Node Exporter. + // + // This member is required. + EnabledInBroker *bool + + noSmithyDocumentSerde +} + +// The node information object. +type NodeInfo struct { + + // The start time. + AddedToClusterTime *string + + // The broker node info. + BrokerNodeInfo *BrokerNodeInfo + + // The ControllerNodeInfo. + ControllerNodeInfo *ControllerNodeInfo + + // The instance type. + InstanceType *string + + // The Amazon Resource Name (ARN) of the node. + NodeARN *string + + // The node type. + NodeType NodeType + + // The ZookeeperNodeInfo. + ZookeeperNodeInfo *ZookeeperNodeInfo + + noSmithyDocumentSerde +} + +// JMX and Node monitoring for the MSK cluster. +type OpenMonitoring struct { + + // Prometheus settings. + // + // This member is required. + Prometheus *Prometheus + + noSmithyDocumentSerde +} + +// JMX and Node monitoring for the MSK cluster. +type OpenMonitoringInfo struct { + + // Prometheus settings. + // + // This member is required. + Prometheus *PrometheusInfo + + noSmithyDocumentSerde +} + +// Prometheus settings. +type Prometheus struct { + + // Indicates whether you want to turn on or turn off the JMX Exporter. + JmxExporter *JmxExporter + + // Indicates whether you want to turn on or turn off the Node Exporter. + NodeExporter *NodeExporter + + noSmithyDocumentSerde +} + +// Prometheus settings. +type PrometheusInfo struct { + + // Indicates whether you want to turn on or turn off the JMX Exporter. + JmxExporter *JmxExporterInfo + + // Indicates whether you want to turn on or turn off the Node Exporter. + NodeExporter *NodeExporterInfo + + noSmithyDocumentSerde +} + +// Provisioned cluster. +type Provisioned struct { + + // Information about the brokers. + // + // This member is required. + BrokerNodeGroupInfo *BrokerNodeGroupInfo + + // The number of broker nodes in the cluster. + // + // This member is required. + NumberOfBrokerNodes *int32 + + // Includes all client authentication information. + ClientAuthentication *ClientAuthentication + + // Information about the Apache Kafka version deployed on the brokers. + CurrentBrokerSoftwareInfo *BrokerSoftwareInfo + + // Determines if there is an action required from the customer. + CustomerActionStatus CustomerActionStatus + + // Includes all encryption-related information. + EncryptionInfo *EncryptionInfo + + // Specifies the level of monitoring for the MSK cluster. The possible values are + // DEFAULT, PER_BROKER, PER_TOPIC_PER_BROKER, and PER_TOPIC_PER_PARTITION. + EnhancedMonitoring EnhancedMonitoring + + // Log delivery information for the cluster. + LoggingInfo *LoggingInfo + + // The settings for open monitoring. + OpenMonitoring *OpenMonitoringInfo + + // Specifies whether or not intelligent rebalancing is turned on for a newly + // created MSK Provisioned cluster with Express brokers. Intelligent rebalancing + // performs automatic partition balancing operations when you scale your clusters + // up or down. By default, intelligent rebalancing is ACTIVE for all new + // Express-based clusters. + Rebalancing *Rebalancing + + // This controls storage mode for supported storage tiers. + StorageMode StorageMode + + // The connection string to use to connect to the Apache ZooKeeper cluster. + ZookeeperConnectString *string + + // The connection string to use to connect to the Apache ZooKeeper cluster on a + // TLS port. + ZookeeperConnectStringTls *string + + noSmithyDocumentSerde +} + +// Provisioned cluster request. +type ProvisionedRequest struct { + + // Information about the brokers. + // + // This member is required. + BrokerNodeGroupInfo *BrokerNodeGroupInfo + + // The Apache Kafka version that you want for the cluster. + // + // This member is required. + KafkaVersion *string + + // The number of broker nodes in the cluster. + // + // This member is required. + NumberOfBrokerNodes *int32 + + // Includes all client authentication information. + ClientAuthentication *ClientAuthentication + + // Represents the configuration that you want Amazon MSK to use for the brokers in + // a cluster. + ConfigurationInfo *ConfigurationInfo + + // Includes all encryption-related information. + EncryptionInfo *EncryptionInfo + + // Specifies the level of monitoring for the MSK cluster. The possible values are + // DEFAULT, PER_BROKER, PER_TOPIC_PER_BROKER, and PER_TOPIC_PER_PARTITION. + EnhancedMonitoring EnhancedMonitoring + + // Log delivery information for the cluster. + LoggingInfo *LoggingInfo + + // The settings for open monitoring. + OpenMonitoring *OpenMonitoringInfo + + // Specifies if intelligent rebalancing is turned on for your MSK Provisioned + // cluster with Express brokers. For all new Express-based clusters that you + // create, intelligent rebalancing is turned on by default. + Rebalancing *Rebalancing + + // This controls storage mode for supported storage tiers. + StorageMode StorageMode + + noSmithyDocumentSerde +} + +// Contains information about provisioned throughput for EBS storage volumes +// attached to kafka broker nodes. +type ProvisionedThroughput struct { + + // Provisioned throughput is enabled or not. + Enabled *bool + + // Throughput value of the EBS volumes for the data drive on each kafka broker + // node in MiB per second. + VolumeThroughput *int32 + + noSmithyDocumentSerde +} + +// Public access control for brokers. +type PublicAccess struct { + + // The value DISABLED indicates that public access is turned off. + // SERVICE_PROVIDED_EIPS indicates that public access is turned on. + Type *string + + noSmithyDocumentSerde +} + +// Specifies whether or not intelligent rebalancing is turned on for a newly +// created MSK Provisioned cluster with Express brokers. Intelligent rebalancing +// performs automatic partition balancing operations when you scale your clusters +// up or down. By default, intelligent rebalancing is ACTIVE for all new +// Express-based clusters. +type Rebalancing struct { + + // Intelligent rebalancing status. The default intelligent rebalancing status is + // ACTIVE for all new Express-based clusters. + Status RebalancingStatus + + noSmithyDocumentSerde +} + +// Specifies configuration for replication between a source and target Kafka +// cluster. +type ReplicationInfo struct { + + // Configuration relating to consumer group replication. + // + // This member is required. + ConsumerGroupReplication *ConsumerGroupReplication + + // The ARN of the source Kafka cluster. + // + // This member is required. + SourceKafkaClusterArn *string + + // The compression type to use when producing records to target cluster. + // + // This member is required. + TargetCompressionType TargetCompressionType + + // The ARN of the target Kafka cluster. + // + // This member is required. + TargetKafkaClusterArn *string + + // Configuration relating to topic replication. + // + // This member is required. + TopicReplication *TopicReplication + + noSmithyDocumentSerde +} + +// Specifies configuration for replication between a source and target Kafka +// cluster (sourceKafkaClusterAlias -> targetKafkaClusterAlias) +type ReplicationInfoDescription struct { + + // Configuration relating to consumer group replication. + ConsumerGroupReplication *ConsumerGroupReplication + + // The alias of the source Kafka cluster. + SourceKafkaClusterAlias *string + + // The compression type to use when producing records to target cluster. + TargetCompressionType TargetCompressionType + + // The alias of the target Kafka cluster. + TargetKafkaClusterAlias *string + + // Configuration relating to topic replication. + TopicReplication *TopicReplication + + noSmithyDocumentSerde +} + +// Summarized information of replication between clusters. +type ReplicationInfoSummary struct { + + // The alias of the source Kafka cluster. + SourceKafkaClusterAlias *string + + // The alias of the target Kafka cluster. + TargetKafkaClusterAlias *string + + noSmithyDocumentSerde +} + +// Configuration for specifying the position in the topics to start replicating +// from. +type ReplicationStartingPosition struct { + + // The type of replication starting position. + Type ReplicationStartingPositionType + + noSmithyDocumentSerde +} + +// Details about the state of a replicator +type ReplicationStateInfo struct { + + // Code that describes the current state of the replicator. + Code *string + + // Message that describes the state of the replicator. + Message *string + + noSmithyDocumentSerde +} + +// Configuration for specifying replicated topic names should be the same as their +// corresponding upstream topics or prefixed with source cluster alias. +type ReplicationTopicNameConfiguration struct { + + // The type of replicated topic name. + Type ReplicationTopicNameConfigurationType + + noSmithyDocumentSerde +} + +// Information about a replicator. +type ReplicatorSummary struct { + + // The time the replicator was created. + CreationTime *time.Time + + // The current version of the replicator. + CurrentVersion *string + + // Whether this resource is a replicator reference. + IsReplicatorReference *bool + + // Kafka Clusters used in setting up sources / targets for replication. + KafkaClustersSummary []KafkaClusterSummary + + // A list of summarized information of replications between clusters. + ReplicationInfoSummaryList []ReplicationInfoSummary + + // The Amazon Resource Name (ARN) of the replicator. + ReplicatorArn *string + + // The name of the replicator. + ReplicatorName *string + + // The Amazon Resource Name (ARN) of the replicator resource in the region where + // the replicator was created. + ReplicatorResourceArn *string + + // State of the replicator. + ReplicatorState ReplicatorState + + noSmithyDocumentSerde +} + +type S3 struct { + + // This member is required. + Enabled *bool + + Bucket *string + + Prefix *string + + noSmithyDocumentSerde +} + +// Details for client authentication using SASL. +type Sasl struct { + + // Indicates whether IAM access control is enabled. + Iam *Iam + + // Details for SASL/SCRAM client authentication. + Scram *Scram + + noSmithyDocumentSerde +} + +// Details for SASL/SCRAM client authentication. +type Scram struct { + + // SASL/SCRAM authentication is enabled or not. + Enabled *bool + + noSmithyDocumentSerde +} + +// Serverless cluster. +type Serverless struct { + + // The configuration of the Amazon VPCs for the cluster. + // + // This member is required. + VpcConfigs []VpcConfig + + // Includes all client authentication information. + ClientAuthentication *ServerlessClientAuthentication + + noSmithyDocumentSerde +} + +// Includes all client authentication information. +type ServerlessClientAuthentication struct { + + // Details for ClientAuthentication using SASL. + Sasl *ServerlessSasl + + noSmithyDocumentSerde +} + +// Serverless cluster request. +type ServerlessRequest struct { + + // The configuration of the Amazon VPCs for the cluster. + // + // This member is required. + VpcConfigs []VpcConfig + + // Includes all client authentication information. + ClientAuthentication *ServerlessClientAuthentication + + noSmithyDocumentSerde +} + +// Details for client authentication using SASL. +type ServerlessSasl struct { + + // Indicates whether IAM access control is enabled. + Iam *Iam + + noSmithyDocumentSerde +} + +type StateInfo struct { + Code *string + + Message *string + + noSmithyDocumentSerde +} + +// Contains information about storage volumes attached to MSK broker nodes. +type StorageInfo struct { + + // EBS volume information. + EbsStorageInfo *EBSStorageInfo + + noSmithyDocumentSerde +} + +// Details for client authentication using TLS. +type Tls struct { + + // List of ACM Certificate Authority ARNs. + CertificateAuthorityArnList []string + + // Specifies whether you want to turn on or turn off TLS authentication. + Enabled *bool + + noSmithyDocumentSerde +} + +// Includes identification info about the topic. +type TopicInfo struct { + + // Number of out-of-sync replicas for a topic. + OutOfSyncReplicaCount *int32 + + // Partition count for a topic. + PartitionCount *int32 + + // Replication factor for a topic. + ReplicationFactor *int32 + + // The Amazon Resource Name (ARN) of the topic. + TopicArn *string + + // Name for a topic. + TopicName *string + + noSmithyDocumentSerde +} + +// Contains information about a topic partition. +type TopicPartitionInfo struct { + + // The list of in-sync replica broker IDs for the partition. + Isr []int32 + + // The leader broker ID for the partition. + Leader *int32 + + // The partition ID. + Partition *int32 + + // The list of replica broker IDs for the partition. + Replicas []int32 + + noSmithyDocumentSerde +} + +// Details about topic replication. +type TopicReplication struct { + + // List of regular expression patterns indicating the topics to copy. + // + // This member is required. + TopicsToReplicate []string + + // Whether to periodically configure remote topic ACLs to match their + // corresponding upstream topics. + CopyAccessControlListsForTopics *bool + + // Whether to periodically configure remote topics to match their corresponding + // upstream topics. + CopyTopicConfigurations *bool + + // Whether to periodically check for new topics and partitions. + DetectAndCopyNewTopics *bool + + // Configuration for specifying the position in the topics to start replicating + // from. + StartingPosition *ReplicationStartingPosition + + // Configuration for specifying replicated topic names should be the same as their + // corresponding upstream topics or prefixed with source cluster alias. + TopicNameConfiguration *ReplicationTopicNameConfiguration + + // List of regular expression patterns indicating the topics that should not be + // replicated. + TopicsToExclude []string + + noSmithyDocumentSerde +} + +// Details for updating the topic replication of a replicator. +type TopicReplicationUpdate struct { + + // Whether to periodically configure remote topic ACLs to match their + // corresponding upstream topics. + // + // This member is required. + CopyAccessControlListsForTopics *bool + + // Whether to periodically configure remote topics to match their corresponding + // upstream topics. + // + // This member is required. + CopyTopicConfigurations *bool + + // Whether to periodically check for new topics and partitions. + // + // This member is required. + DetectAndCopyNewTopics *bool + + // List of regular expression patterns indicating the topics that should not be + // replicated. + // + // This member is required. + TopicsToExclude []string + + // List of regular expression patterns indicating the topics to copy. + // + // This member is required. + TopicsToReplicate []string + + noSmithyDocumentSerde +} + +type Unauthenticated struct { + + // Specifies whether you want to turn on or turn off unauthenticated traffic to + // your cluster. + Enabled *bool + + noSmithyDocumentSerde +} + +// Error info for scram secret associate/disassociate failure. +type UnprocessedScramSecret struct { + + // Error code for associate/disassociate failure. + ErrorCode *string + + // Error message for associate/disassociate failure. + ErrorMessage *string + + // AWS Secrets Manager secret ARN. + SecretArn *string + + noSmithyDocumentSerde +} + +// Description of the requester that calls the API operation. +type UserIdentity struct { + + // A unique identifier for the requester that calls the API operation. + PrincipalId *string + + // The identity type of the requester that calls the API operation. + Type UserIdentityType + + noSmithyDocumentSerde +} + +// The configuration of the Amazon VPCs for the cluster. +type VpcConfig struct { + + // The IDs of the subnets associated with the cluster. + // + // This member is required. + SubnetIds []string + + // The IDs of the security groups associated with the cluster. + SecurityGroupIds []string + + noSmithyDocumentSerde +} + +// The VPC connection object. +type VpcConnection struct { + + // The ARN that identifies the Cluster which the Vpc Connection belongs to. + // + // This member is required. + TargetClusterArn *string + + // The ARN that identifies the Vpc Connection. + // + // This member is required. + VpcConnectionArn *string + + // Information about the auth scheme of Vpc Connection. + Authentication *string + + // Creation time of the Vpc Connection. + CreationTime *time.Time + + // State of the Vpc Connection. + State VpcConnectionState + + // The vpcId that belongs to the Vpc Connection. + VpcId *string + + noSmithyDocumentSerde +} + +// Description of the VPC connection. +type VpcConnectionInfo struct { + + // The time when Amazon MSK creates the VPC Connnection. + CreationTime *time.Time + + // The owner of the VPC Connection. + Owner *string + + // Description of the requester that calls the API operation. + UserIdentity *UserIdentity + + // The Amazon Resource Name (ARN) of the VPC connection. + VpcConnectionArn *string + + noSmithyDocumentSerde +} + +// Description of the VPC connection. +type VpcConnectionInfoServerless struct { + + // The time when Amazon MSK creates the VPC Connnection. + CreationTime *time.Time + + // The owner of the VPC Connection. + Owner *string + + // Description of the requester that calls the API operation. + UserIdentity *UserIdentity + + // The Amazon Resource Name (ARN) of the VPC connection. + VpcConnectionArn *string + + noSmithyDocumentSerde +} + +// VPC connectivity access control for brokers. +type VpcConnectivity struct { + + // Includes all client authentication information for VPC connectivity. + ClientAuthentication *VpcConnectivityClientAuthentication + + noSmithyDocumentSerde +} + +// Includes all client authentication information for VPC connectivity. +type VpcConnectivityClientAuthentication struct { + + // SASL authentication type details for VPC connectivity. + Sasl *VpcConnectivitySasl + + // TLS authentication type details for VPC connectivity. + Tls *VpcConnectivityTls + + noSmithyDocumentSerde +} + +// Details for IAM access control for VPC connectivity. +type VpcConnectivityIam struct { + + // SASL/IAM authentication is on or off for VPC connectivity. + Enabled *bool + + noSmithyDocumentSerde +} + +// Details for SASL client authentication for VPC connectivity. +type VpcConnectivitySasl struct { + + // Details for SASL/IAM client authentication for VPC connectivity. + Iam *VpcConnectivityIam + + // Details for SASL/SCRAM client authentication for VPC connectivity. + Scram *VpcConnectivityScram + + noSmithyDocumentSerde +} + +// Details for SASL/SCRAM client authentication for VPC connectivity. +type VpcConnectivityScram struct { + + // SASL/SCRAM authentication is on or off for VPC connectivity. + Enabled *bool + + noSmithyDocumentSerde +} + +// Details for TLS client authentication for VPC connectivity. +type VpcConnectivityTls struct { + + // TLS authentication is on or off for VPC connectivity. + Enabled *bool + + noSmithyDocumentSerde +} + +// Zookeeper node information. +type ZookeeperNodeInfo struct { + + // The attached elastic network interface of the broker. + AttachedENIId *string + + // The virtual private cloud (VPC) IP address of the client. + ClientVpcIpAddress *string + + // Endpoints for accessing the ZooKeeper. + Endpoints []string + + // The role-specific ID for Zookeeper. + ZookeeperId *float64 + + // The version of Zookeeper. + ZookeeperVersion *string + + noSmithyDocumentSerde +} + +type noSmithyDocumentSerde = smithydocument.NoSerde diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/validators.go b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/validators.go new file mode 100644 index 000000000..eb9537172 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/kafka/validators.go @@ -0,0 +1,2692 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package kafka + +import ( + "context" + "fmt" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + smithy "github.com/aws/smithy-go" + "github.com/aws/smithy-go/middleware" +) + +type validateOpBatchAssociateScramSecret struct { +} + +func (*validateOpBatchAssociateScramSecret) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpBatchAssociateScramSecret) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*BatchAssociateScramSecretInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpBatchAssociateScramSecretInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpBatchDisassociateScramSecret struct { +} + +func (*validateOpBatchDisassociateScramSecret) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpBatchDisassociateScramSecret) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*BatchDisassociateScramSecretInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpBatchDisassociateScramSecretInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpCreateCluster struct { +} + +func (*validateOpCreateCluster) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpCreateCluster) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*CreateClusterInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpCreateClusterInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpCreateClusterV2 struct { +} + +func (*validateOpCreateClusterV2) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpCreateClusterV2) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*CreateClusterV2Input) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpCreateClusterV2Input(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpCreateConfiguration struct { +} + +func (*validateOpCreateConfiguration) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpCreateConfiguration) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*CreateConfigurationInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpCreateConfigurationInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpCreateReplicator struct { +} + +func (*validateOpCreateReplicator) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpCreateReplicator) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*CreateReplicatorInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpCreateReplicatorInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpCreateVpcConnection struct { +} + +func (*validateOpCreateVpcConnection) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpCreateVpcConnection) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*CreateVpcConnectionInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpCreateVpcConnectionInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDeleteCluster struct { +} + +func (*validateOpDeleteCluster) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDeleteCluster) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DeleteClusterInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDeleteClusterInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDeleteClusterPolicy struct { +} + +func (*validateOpDeleteClusterPolicy) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDeleteClusterPolicy) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DeleteClusterPolicyInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDeleteClusterPolicyInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDeleteConfiguration struct { +} + +func (*validateOpDeleteConfiguration) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDeleteConfiguration) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DeleteConfigurationInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDeleteConfigurationInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDeleteReplicator struct { +} + +func (*validateOpDeleteReplicator) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDeleteReplicator) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DeleteReplicatorInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDeleteReplicatorInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDeleteVpcConnection struct { +} + +func (*validateOpDeleteVpcConnection) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDeleteVpcConnection) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DeleteVpcConnectionInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDeleteVpcConnectionInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDescribeCluster struct { +} + +func (*validateOpDescribeCluster) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDescribeCluster) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DescribeClusterInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDescribeClusterInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDescribeClusterOperation struct { +} + +func (*validateOpDescribeClusterOperation) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDescribeClusterOperation) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DescribeClusterOperationInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDescribeClusterOperationInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDescribeClusterOperationV2 struct { +} + +func (*validateOpDescribeClusterOperationV2) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDescribeClusterOperationV2) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DescribeClusterOperationV2Input) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDescribeClusterOperationV2Input(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDescribeClusterV2 struct { +} + +func (*validateOpDescribeClusterV2) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDescribeClusterV2) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DescribeClusterV2Input) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDescribeClusterV2Input(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDescribeConfiguration struct { +} + +func (*validateOpDescribeConfiguration) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDescribeConfiguration) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DescribeConfigurationInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDescribeConfigurationInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDescribeConfigurationRevision struct { +} + +func (*validateOpDescribeConfigurationRevision) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDescribeConfigurationRevision) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DescribeConfigurationRevisionInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDescribeConfigurationRevisionInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDescribeReplicator struct { +} + +func (*validateOpDescribeReplicator) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDescribeReplicator) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DescribeReplicatorInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDescribeReplicatorInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDescribeTopic struct { +} + +func (*validateOpDescribeTopic) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDescribeTopic) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DescribeTopicInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDescribeTopicInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDescribeTopicPartitions struct { +} + +func (*validateOpDescribeTopicPartitions) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDescribeTopicPartitions) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DescribeTopicPartitionsInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDescribeTopicPartitionsInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDescribeVpcConnection struct { +} + +func (*validateOpDescribeVpcConnection) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDescribeVpcConnection) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DescribeVpcConnectionInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDescribeVpcConnectionInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpGetBootstrapBrokers struct { +} + +func (*validateOpGetBootstrapBrokers) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpGetBootstrapBrokers) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*GetBootstrapBrokersInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpGetBootstrapBrokersInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpGetClusterPolicy struct { +} + +func (*validateOpGetClusterPolicy) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpGetClusterPolicy) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*GetClusterPolicyInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpGetClusterPolicyInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpListClientVpcConnections struct { +} + +func (*validateOpListClientVpcConnections) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpListClientVpcConnections) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*ListClientVpcConnectionsInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpListClientVpcConnectionsInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpListClusterOperations struct { +} + +func (*validateOpListClusterOperations) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpListClusterOperations) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*ListClusterOperationsInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpListClusterOperationsInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpListClusterOperationsV2 struct { +} + +func (*validateOpListClusterOperationsV2) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpListClusterOperationsV2) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*ListClusterOperationsV2Input) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpListClusterOperationsV2Input(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpListConfigurationRevisions struct { +} + +func (*validateOpListConfigurationRevisions) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpListConfigurationRevisions) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*ListConfigurationRevisionsInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpListConfigurationRevisionsInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpListNodes struct { +} + +func (*validateOpListNodes) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpListNodes) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*ListNodesInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpListNodesInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpListScramSecrets struct { +} + +func (*validateOpListScramSecrets) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpListScramSecrets) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*ListScramSecretsInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpListScramSecretsInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpListTagsForResource struct { +} + +func (*validateOpListTagsForResource) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpListTagsForResource) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*ListTagsForResourceInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpListTagsForResourceInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpListTopics struct { +} + +func (*validateOpListTopics) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpListTopics) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*ListTopicsInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpListTopicsInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpPutClusterPolicy struct { +} + +func (*validateOpPutClusterPolicy) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpPutClusterPolicy) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*PutClusterPolicyInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpPutClusterPolicyInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpRebootBroker struct { +} + +func (*validateOpRebootBroker) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpRebootBroker) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*RebootBrokerInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpRebootBrokerInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpRejectClientVpcConnection struct { +} + +func (*validateOpRejectClientVpcConnection) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpRejectClientVpcConnection) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*RejectClientVpcConnectionInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpRejectClientVpcConnectionInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpTagResource struct { +} + +func (*validateOpTagResource) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpTagResource) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*TagResourceInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpTagResourceInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpUntagResource struct { +} + +func (*validateOpUntagResource) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpUntagResource) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*UntagResourceInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpUntagResourceInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpUpdateBrokerCount struct { +} + +func (*validateOpUpdateBrokerCount) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpUpdateBrokerCount) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*UpdateBrokerCountInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpUpdateBrokerCountInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpUpdateBrokerStorage struct { +} + +func (*validateOpUpdateBrokerStorage) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpUpdateBrokerStorage) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*UpdateBrokerStorageInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpUpdateBrokerStorageInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpUpdateBrokerType struct { +} + +func (*validateOpUpdateBrokerType) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpUpdateBrokerType) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*UpdateBrokerTypeInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpUpdateBrokerTypeInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpUpdateClusterConfiguration struct { +} + +func (*validateOpUpdateClusterConfiguration) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpUpdateClusterConfiguration) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*UpdateClusterConfigurationInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpUpdateClusterConfigurationInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpUpdateClusterKafkaVersion struct { +} + +func (*validateOpUpdateClusterKafkaVersion) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpUpdateClusterKafkaVersion) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*UpdateClusterKafkaVersionInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpUpdateClusterKafkaVersionInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpUpdateConfiguration struct { +} + +func (*validateOpUpdateConfiguration) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpUpdateConfiguration) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*UpdateConfigurationInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpUpdateConfigurationInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpUpdateConnectivity struct { +} + +func (*validateOpUpdateConnectivity) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpUpdateConnectivity) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*UpdateConnectivityInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpUpdateConnectivityInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpUpdateMonitoring struct { +} + +func (*validateOpUpdateMonitoring) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpUpdateMonitoring) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*UpdateMonitoringInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpUpdateMonitoringInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpUpdateRebalancing struct { +} + +func (*validateOpUpdateRebalancing) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpUpdateRebalancing) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*UpdateRebalancingInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpUpdateRebalancingInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpUpdateReplicationInfo struct { +} + +func (*validateOpUpdateReplicationInfo) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpUpdateReplicationInfo) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*UpdateReplicationInfoInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpUpdateReplicationInfoInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpUpdateSecurity struct { +} + +func (*validateOpUpdateSecurity) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpUpdateSecurity) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*UpdateSecurityInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpUpdateSecurityInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpUpdateStorage struct { +} + +func (*validateOpUpdateStorage) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpUpdateStorage) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*UpdateStorageInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpUpdateStorageInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +func addOpBatchAssociateScramSecretValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpBatchAssociateScramSecret{}, middleware.After) +} + +func addOpBatchDisassociateScramSecretValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpBatchDisassociateScramSecret{}, middleware.After) +} + +func addOpCreateClusterValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpCreateCluster{}, middleware.After) +} + +func addOpCreateClusterV2ValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpCreateClusterV2{}, middleware.After) +} + +func addOpCreateConfigurationValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpCreateConfiguration{}, middleware.After) +} + +func addOpCreateReplicatorValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpCreateReplicator{}, middleware.After) +} + +func addOpCreateVpcConnectionValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpCreateVpcConnection{}, middleware.After) +} + +func addOpDeleteClusterValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDeleteCluster{}, middleware.After) +} + +func addOpDeleteClusterPolicyValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDeleteClusterPolicy{}, middleware.After) +} + +func addOpDeleteConfigurationValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDeleteConfiguration{}, middleware.After) +} + +func addOpDeleteReplicatorValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDeleteReplicator{}, middleware.After) +} + +func addOpDeleteVpcConnectionValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDeleteVpcConnection{}, middleware.After) +} + +func addOpDescribeClusterValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDescribeCluster{}, middleware.After) +} + +func addOpDescribeClusterOperationValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDescribeClusterOperation{}, middleware.After) +} + +func addOpDescribeClusterOperationV2ValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDescribeClusterOperationV2{}, middleware.After) +} + +func addOpDescribeClusterV2ValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDescribeClusterV2{}, middleware.After) +} + +func addOpDescribeConfigurationValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDescribeConfiguration{}, middleware.After) +} + +func addOpDescribeConfigurationRevisionValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDescribeConfigurationRevision{}, middleware.After) +} + +func addOpDescribeReplicatorValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDescribeReplicator{}, middleware.After) +} + +func addOpDescribeTopicValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDescribeTopic{}, middleware.After) +} + +func addOpDescribeTopicPartitionsValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDescribeTopicPartitions{}, middleware.After) +} + +func addOpDescribeVpcConnectionValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDescribeVpcConnection{}, middleware.After) +} + +func addOpGetBootstrapBrokersValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpGetBootstrapBrokers{}, middleware.After) +} + +func addOpGetClusterPolicyValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpGetClusterPolicy{}, middleware.After) +} + +func addOpListClientVpcConnectionsValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpListClientVpcConnections{}, middleware.After) +} + +func addOpListClusterOperationsValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpListClusterOperations{}, middleware.After) +} + +func addOpListClusterOperationsV2ValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpListClusterOperationsV2{}, middleware.After) +} + +func addOpListConfigurationRevisionsValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpListConfigurationRevisions{}, middleware.After) +} + +func addOpListNodesValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpListNodes{}, middleware.After) +} + +func addOpListScramSecretsValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpListScramSecrets{}, middleware.After) +} + +func addOpListTagsForResourceValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpListTagsForResource{}, middleware.After) +} + +func addOpListTopicsValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpListTopics{}, middleware.After) +} + +func addOpPutClusterPolicyValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpPutClusterPolicy{}, middleware.After) +} + +func addOpRebootBrokerValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpRebootBroker{}, middleware.After) +} + +func addOpRejectClientVpcConnectionValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpRejectClientVpcConnection{}, middleware.After) +} + +func addOpTagResourceValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpTagResource{}, middleware.After) +} + +func addOpUntagResourceValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpUntagResource{}, middleware.After) +} + +func addOpUpdateBrokerCountValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpUpdateBrokerCount{}, middleware.After) +} + +func addOpUpdateBrokerStorageValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpUpdateBrokerStorage{}, middleware.After) +} + +func addOpUpdateBrokerTypeValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpUpdateBrokerType{}, middleware.After) +} + +func addOpUpdateClusterConfigurationValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpUpdateClusterConfiguration{}, middleware.After) +} + +func addOpUpdateClusterKafkaVersionValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpUpdateClusterKafkaVersion{}, middleware.After) +} + +func addOpUpdateConfigurationValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpUpdateConfiguration{}, middleware.After) +} + +func addOpUpdateConnectivityValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpUpdateConnectivity{}, middleware.After) +} + +func addOpUpdateMonitoringValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpUpdateMonitoring{}, middleware.After) +} + +func addOpUpdateRebalancingValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpUpdateRebalancing{}, middleware.After) +} + +func addOpUpdateReplicationInfoValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpUpdateReplicationInfo{}, middleware.After) +} + +func addOpUpdateSecurityValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpUpdateSecurity{}, middleware.After) +} + +func addOpUpdateStorageValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpUpdateStorage{}, middleware.After) +} + +func validate__listOfBrokerEBSVolumeInfo(v []types.BrokerEBSVolumeInfo) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ListOfBrokerEBSVolumeInfo"} + for i := range v { + if err := validateBrokerEBSVolumeInfo(&v[i]); err != nil { + invalidParams.AddNested(fmt.Sprintf("[%d]", i), err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validate__listOfKafkaCluster(v []types.KafkaCluster) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ListOfKafkaCluster"} + for i := range v { + if err := validateKafkaCluster(&v[i]); err != nil { + invalidParams.AddNested(fmt.Sprintf("[%d]", i), err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validate__listOfReplicationInfo(v []types.ReplicationInfo) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ListOfReplicationInfo"} + for i := range v { + if err := validateReplicationInfo(&v[i]); err != nil { + invalidParams.AddNested(fmt.Sprintf("[%d]", i), err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validate__listOfVpcConfig(v []types.VpcConfig) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ListOfVpcConfig"} + for i := range v { + if err := validateVpcConfig(&v[i]); err != nil { + invalidParams.AddNested(fmt.Sprintf("[%d]", i), err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateAmazonMskCluster(v *types.AmazonMskCluster) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "AmazonMskCluster"} + if v.MskClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("MskClusterArn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateBrokerEBSVolumeInfo(v *types.BrokerEBSVolumeInfo) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "BrokerEBSVolumeInfo"} + if v.KafkaBrokerNodeId == nil { + invalidParams.Add(smithy.NewErrParamRequired("KafkaBrokerNodeId")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateBrokerLogs(v *types.BrokerLogs) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "BrokerLogs"} + if v.CloudWatchLogs != nil { + if err := validateCloudWatchLogs(v.CloudWatchLogs); err != nil { + invalidParams.AddNested("CloudWatchLogs", err.(smithy.InvalidParamsError)) + } + } + if v.Firehose != nil { + if err := validateFirehose(v.Firehose); err != nil { + invalidParams.AddNested("Firehose", err.(smithy.InvalidParamsError)) + } + } + if v.S3 != nil { + if err := validateS3(v.S3); err != nil { + invalidParams.AddNested("S3", err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateBrokerNodeGroupInfo(v *types.BrokerNodeGroupInfo) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "BrokerNodeGroupInfo"} + if v.ClientSubnets == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClientSubnets")) + } + if v.InstanceType == nil { + invalidParams.Add(smithy.NewErrParamRequired("InstanceType")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateCloudWatchLogs(v *types.CloudWatchLogs) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "CloudWatchLogs"} + if v.Enabled == nil { + invalidParams.Add(smithy.NewErrParamRequired("Enabled")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateConfigurationInfo(v *types.ConfigurationInfo) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ConfigurationInfo"} + if v.Arn == nil { + invalidParams.Add(smithy.NewErrParamRequired("Arn")) + } + if v.Revision == nil { + invalidParams.Add(smithy.NewErrParamRequired("Revision")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateConsumerGroupReplication(v *types.ConsumerGroupReplication) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ConsumerGroupReplication"} + if v.ConsumerGroupsToReplicate == nil { + invalidParams.Add(smithy.NewErrParamRequired("ConsumerGroupsToReplicate")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateConsumerGroupReplicationUpdate(v *types.ConsumerGroupReplicationUpdate) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ConsumerGroupReplicationUpdate"} + if v.ConsumerGroupsToExclude == nil { + invalidParams.Add(smithy.NewErrParamRequired("ConsumerGroupsToExclude")) + } + if v.ConsumerGroupsToReplicate == nil { + invalidParams.Add(smithy.NewErrParamRequired("ConsumerGroupsToReplicate")) + } + if v.DetectAndCopyNewConsumerGroups == nil { + invalidParams.Add(smithy.NewErrParamRequired("DetectAndCopyNewConsumerGroups")) + } + if v.SynchroniseConsumerGroupOffsets == nil { + invalidParams.Add(smithy.NewErrParamRequired("SynchroniseConsumerGroupOffsets")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateEncryptionAtRest(v *types.EncryptionAtRest) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "EncryptionAtRest"} + if v.DataVolumeKMSKeyId == nil { + invalidParams.Add(smithy.NewErrParamRequired("DataVolumeKMSKeyId")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateEncryptionInfo(v *types.EncryptionInfo) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "EncryptionInfo"} + if v.EncryptionAtRest != nil { + if err := validateEncryptionAtRest(v.EncryptionAtRest); err != nil { + invalidParams.AddNested("EncryptionAtRest", err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateFirehose(v *types.Firehose) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "Firehose"} + if v.Enabled == nil { + invalidParams.Add(smithy.NewErrParamRequired("Enabled")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateJmxExporterInfo(v *types.JmxExporterInfo) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "JmxExporterInfo"} + if v.EnabledInBroker == nil { + invalidParams.Add(smithy.NewErrParamRequired("EnabledInBroker")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateKafkaCluster(v *types.KafkaCluster) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "KafkaCluster"} + if v.AmazonMskCluster == nil { + invalidParams.Add(smithy.NewErrParamRequired("AmazonMskCluster")) + } else if v.AmazonMskCluster != nil { + if err := validateAmazonMskCluster(v.AmazonMskCluster); err != nil { + invalidParams.AddNested("AmazonMskCluster", err.(smithy.InvalidParamsError)) + } + } + if v.VpcConfig == nil { + invalidParams.Add(smithy.NewErrParamRequired("VpcConfig")) + } else if v.VpcConfig != nil { + if err := validateKafkaClusterClientVpcConfig(v.VpcConfig); err != nil { + invalidParams.AddNested("VpcConfig", err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateKafkaClusterClientVpcConfig(v *types.KafkaClusterClientVpcConfig) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "KafkaClusterClientVpcConfig"} + if v.SubnetIds == nil { + invalidParams.Add(smithy.NewErrParamRequired("SubnetIds")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateLoggingInfo(v *types.LoggingInfo) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "LoggingInfo"} + if v.BrokerLogs == nil { + invalidParams.Add(smithy.NewErrParamRequired("BrokerLogs")) + } else if v.BrokerLogs != nil { + if err := validateBrokerLogs(v.BrokerLogs); err != nil { + invalidParams.AddNested("BrokerLogs", err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateNodeExporterInfo(v *types.NodeExporterInfo) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "NodeExporterInfo"} + if v.EnabledInBroker == nil { + invalidParams.Add(smithy.NewErrParamRequired("EnabledInBroker")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpenMonitoringInfo(v *types.OpenMonitoringInfo) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "OpenMonitoringInfo"} + if v.Prometheus == nil { + invalidParams.Add(smithy.NewErrParamRequired("Prometheus")) + } else if v.Prometheus != nil { + if err := validatePrometheusInfo(v.Prometheus); err != nil { + invalidParams.AddNested("Prometheus", err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validatePrometheusInfo(v *types.PrometheusInfo) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "PrometheusInfo"} + if v.JmxExporter != nil { + if err := validateJmxExporterInfo(v.JmxExporter); err != nil { + invalidParams.AddNested("JmxExporter", err.(smithy.InvalidParamsError)) + } + } + if v.NodeExporter != nil { + if err := validateNodeExporterInfo(v.NodeExporter); err != nil { + invalidParams.AddNested("NodeExporter", err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateProvisionedRequest(v *types.ProvisionedRequest) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ProvisionedRequest"} + if v.BrokerNodeGroupInfo == nil { + invalidParams.Add(smithy.NewErrParamRequired("BrokerNodeGroupInfo")) + } else if v.BrokerNodeGroupInfo != nil { + if err := validateBrokerNodeGroupInfo(v.BrokerNodeGroupInfo); err != nil { + invalidParams.AddNested("BrokerNodeGroupInfo", err.(smithy.InvalidParamsError)) + } + } + if v.ConfigurationInfo != nil { + if err := validateConfigurationInfo(v.ConfigurationInfo); err != nil { + invalidParams.AddNested("ConfigurationInfo", err.(smithy.InvalidParamsError)) + } + } + if v.EncryptionInfo != nil { + if err := validateEncryptionInfo(v.EncryptionInfo); err != nil { + invalidParams.AddNested("EncryptionInfo", err.(smithy.InvalidParamsError)) + } + } + if v.OpenMonitoring != nil { + if err := validateOpenMonitoringInfo(v.OpenMonitoring); err != nil { + invalidParams.AddNested("OpenMonitoring", err.(smithy.InvalidParamsError)) + } + } + if v.KafkaVersion == nil { + invalidParams.Add(smithy.NewErrParamRequired("KafkaVersion")) + } + if v.LoggingInfo != nil { + if err := validateLoggingInfo(v.LoggingInfo); err != nil { + invalidParams.AddNested("LoggingInfo", err.(smithy.InvalidParamsError)) + } + } + if v.NumberOfBrokerNodes == nil { + invalidParams.Add(smithy.NewErrParamRequired("NumberOfBrokerNodes")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateReplicationInfo(v *types.ReplicationInfo) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ReplicationInfo"} + if v.ConsumerGroupReplication == nil { + invalidParams.Add(smithy.NewErrParamRequired("ConsumerGroupReplication")) + } else if v.ConsumerGroupReplication != nil { + if err := validateConsumerGroupReplication(v.ConsumerGroupReplication); err != nil { + invalidParams.AddNested("ConsumerGroupReplication", err.(smithy.InvalidParamsError)) + } + } + if v.SourceKafkaClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("SourceKafkaClusterArn")) + } + if len(v.TargetCompressionType) == 0 { + invalidParams.Add(smithy.NewErrParamRequired("TargetCompressionType")) + } + if v.TargetKafkaClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("TargetKafkaClusterArn")) + } + if v.TopicReplication == nil { + invalidParams.Add(smithy.NewErrParamRequired("TopicReplication")) + } else if v.TopicReplication != nil { + if err := validateTopicReplication(v.TopicReplication); err != nil { + invalidParams.AddNested("TopicReplication", err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateS3(v *types.S3) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "S3"} + if v.Enabled == nil { + invalidParams.Add(smithy.NewErrParamRequired("Enabled")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateServerlessRequest(v *types.ServerlessRequest) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ServerlessRequest"} + if v.VpcConfigs == nil { + invalidParams.Add(smithy.NewErrParamRequired("VpcConfigs")) + } else if v.VpcConfigs != nil { + if err := validate__listOfVpcConfig(v.VpcConfigs); err != nil { + invalidParams.AddNested("VpcConfigs", err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateTopicReplication(v *types.TopicReplication) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "TopicReplication"} + if v.TopicsToReplicate == nil { + invalidParams.Add(smithy.NewErrParamRequired("TopicsToReplicate")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateTopicReplicationUpdate(v *types.TopicReplicationUpdate) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "TopicReplicationUpdate"} + if v.CopyAccessControlListsForTopics == nil { + invalidParams.Add(smithy.NewErrParamRequired("CopyAccessControlListsForTopics")) + } + if v.CopyTopicConfigurations == nil { + invalidParams.Add(smithy.NewErrParamRequired("CopyTopicConfigurations")) + } + if v.DetectAndCopyNewTopics == nil { + invalidParams.Add(smithy.NewErrParamRequired("DetectAndCopyNewTopics")) + } + if v.TopicsToExclude == nil { + invalidParams.Add(smithy.NewErrParamRequired("TopicsToExclude")) + } + if v.TopicsToReplicate == nil { + invalidParams.Add(smithy.NewErrParamRequired("TopicsToReplicate")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateVpcConfig(v *types.VpcConfig) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "VpcConfig"} + if v.SubnetIds == nil { + invalidParams.Add(smithy.NewErrParamRequired("SubnetIds")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpBatchAssociateScramSecretInput(v *BatchAssociateScramSecretInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "BatchAssociateScramSecretInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if v.SecretArnList == nil { + invalidParams.Add(smithy.NewErrParamRequired("SecretArnList")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpBatchDisassociateScramSecretInput(v *BatchDisassociateScramSecretInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "BatchDisassociateScramSecretInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if v.SecretArnList == nil { + invalidParams.Add(smithy.NewErrParamRequired("SecretArnList")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpCreateClusterInput(v *CreateClusterInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "CreateClusterInput"} + if v.BrokerNodeGroupInfo == nil { + invalidParams.Add(smithy.NewErrParamRequired("BrokerNodeGroupInfo")) + } else if v.BrokerNodeGroupInfo != nil { + if err := validateBrokerNodeGroupInfo(v.BrokerNodeGroupInfo); err != nil { + invalidParams.AddNested("BrokerNodeGroupInfo", err.(smithy.InvalidParamsError)) + } + } + if v.ClusterName == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterName")) + } + if v.ConfigurationInfo != nil { + if err := validateConfigurationInfo(v.ConfigurationInfo); err != nil { + invalidParams.AddNested("ConfigurationInfo", err.(smithy.InvalidParamsError)) + } + } + if v.EncryptionInfo != nil { + if err := validateEncryptionInfo(v.EncryptionInfo); err != nil { + invalidParams.AddNested("EncryptionInfo", err.(smithy.InvalidParamsError)) + } + } + if v.OpenMonitoring != nil { + if err := validateOpenMonitoringInfo(v.OpenMonitoring); err != nil { + invalidParams.AddNested("OpenMonitoring", err.(smithy.InvalidParamsError)) + } + } + if v.KafkaVersion == nil { + invalidParams.Add(smithy.NewErrParamRequired("KafkaVersion")) + } + if v.LoggingInfo != nil { + if err := validateLoggingInfo(v.LoggingInfo); err != nil { + invalidParams.AddNested("LoggingInfo", err.(smithy.InvalidParamsError)) + } + } + if v.NumberOfBrokerNodes == nil { + invalidParams.Add(smithy.NewErrParamRequired("NumberOfBrokerNodes")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpCreateClusterV2Input(v *CreateClusterV2Input) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "CreateClusterV2Input"} + if v.ClusterName == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterName")) + } + if v.Provisioned != nil { + if err := validateProvisionedRequest(v.Provisioned); err != nil { + invalidParams.AddNested("Provisioned", err.(smithy.InvalidParamsError)) + } + } + if v.Serverless != nil { + if err := validateServerlessRequest(v.Serverless); err != nil { + invalidParams.AddNested("Serverless", err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpCreateConfigurationInput(v *CreateConfigurationInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "CreateConfigurationInput"} + if v.Name == nil { + invalidParams.Add(smithy.NewErrParamRequired("Name")) + } + if v.ServerProperties == nil { + invalidParams.Add(smithy.NewErrParamRequired("ServerProperties")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpCreateReplicatorInput(v *CreateReplicatorInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "CreateReplicatorInput"} + if v.KafkaClusters == nil { + invalidParams.Add(smithy.NewErrParamRequired("KafkaClusters")) + } else if v.KafkaClusters != nil { + if err := validate__listOfKafkaCluster(v.KafkaClusters); err != nil { + invalidParams.AddNested("KafkaClusters", err.(smithy.InvalidParamsError)) + } + } + if v.ReplicationInfoList == nil { + invalidParams.Add(smithy.NewErrParamRequired("ReplicationInfoList")) + } else if v.ReplicationInfoList != nil { + if err := validate__listOfReplicationInfo(v.ReplicationInfoList); err != nil { + invalidParams.AddNested("ReplicationInfoList", err.(smithy.InvalidParamsError)) + } + } + if v.ReplicatorName == nil { + invalidParams.Add(smithy.NewErrParamRequired("ReplicatorName")) + } + if v.ServiceExecutionRoleArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ServiceExecutionRoleArn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpCreateVpcConnectionInput(v *CreateVpcConnectionInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "CreateVpcConnectionInput"} + if v.TargetClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("TargetClusterArn")) + } + if v.Authentication == nil { + invalidParams.Add(smithy.NewErrParamRequired("Authentication")) + } + if v.VpcId == nil { + invalidParams.Add(smithy.NewErrParamRequired("VpcId")) + } + if v.ClientSubnets == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClientSubnets")) + } + if v.SecurityGroups == nil { + invalidParams.Add(smithy.NewErrParamRequired("SecurityGroups")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDeleteClusterInput(v *DeleteClusterInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DeleteClusterInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDeleteClusterPolicyInput(v *DeleteClusterPolicyInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DeleteClusterPolicyInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDeleteConfigurationInput(v *DeleteConfigurationInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DeleteConfigurationInput"} + if v.Arn == nil { + invalidParams.Add(smithy.NewErrParamRequired("Arn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDeleteReplicatorInput(v *DeleteReplicatorInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DeleteReplicatorInput"} + if v.ReplicatorArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ReplicatorArn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDeleteVpcConnectionInput(v *DeleteVpcConnectionInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DeleteVpcConnectionInput"} + if v.Arn == nil { + invalidParams.Add(smithy.NewErrParamRequired("Arn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDescribeClusterInput(v *DescribeClusterInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DescribeClusterInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDescribeClusterOperationInput(v *DescribeClusterOperationInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DescribeClusterOperationInput"} + if v.ClusterOperationArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterOperationArn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDescribeClusterOperationV2Input(v *DescribeClusterOperationV2Input) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DescribeClusterOperationV2Input"} + if v.ClusterOperationArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterOperationArn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDescribeClusterV2Input(v *DescribeClusterV2Input) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DescribeClusterV2Input"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDescribeConfigurationInput(v *DescribeConfigurationInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DescribeConfigurationInput"} + if v.Arn == nil { + invalidParams.Add(smithy.NewErrParamRequired("Arn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDescribeConfigurationRevisionInput(v *DescribeConfigurationRevisionInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DescribeConfigurationRevisionInput"} + if v.Arn == nil { + invalidParams.Add(smithy.NewErrParamRequired("Arn")) + } + if v.Revision == nil { + invalidParams.Add(smithy.NewErrParamRequired("Revision")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDescribeReplicatorInput(v *DescribeReplicatorInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DescribeReplicatorInput"} + if v.ReplicatorArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ReplicatorArn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDescribeTopicInput(v *DescribeTopicInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DescribeTopicInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if v.TopicName == nil { + invalidParams.Add(smithy.NewErrParamRequired("TopicName")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDescribeTopicPartitionsInput(v *DescribeTopicPartitionsInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DescribeTopicPartitionsInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if v.TopicName == nil { + invalidParams.Add(smithy.NewErrParamRequired("TopicName")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDescribeVpcConnectionInput(v *DescribeVpcConnectionInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DescribeVpcConnectionInput"} + if v.Arn == nil { + invalidParams.Add(smithy.NewErrParamRequired("Arn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpGetBootstrapBrokersInput(v *GetBootstrapBrokersInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "GetBootstrapBrokersInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpGetClusterPolicyInput(v *GetClusterPolicyInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "GetClusterPolicyInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpListClientVpcConnectionsInput(v *ListClientVpcConnectionsInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ListClientVpcConnectionsInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpListClusterOperationsInput(v *ListClusterOperationsInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ListClusterOperationsInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpListClusterOperationsV2Input(v *ListClusterOperationsV2Input) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ListClusterOperationsV2Input"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpListConfigurationRevisionsInput(v *ListConfigurationRevisionsInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ListConfigurationRevisionsInput"} + if v.Arn == nil { + invalidParams.Add(smithy.NewErrParamRequired("Arn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpListNodesInput(v *ListNodesInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ListNodesInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpListScramSecretsInput(v *ListScramSecretsInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ListScramSecretsInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpListTagsForResourceInput(v *ListTagsForResourceInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ListTagsForResourceInput"} + if v.ResourceArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ResourceArn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpListTopicsInput(v *ListTopicsInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ListTopicsInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpPutClusterPolicyInput(v *PutClusterPolicyInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "PutClusterPolicyInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if v.Policy == nil { + invalidParams.Add(smithy.NewErrParamRequired("Policy")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpRebootBrokerInput(v *RebootBrokerInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "RebootBrokerInput"} + if v.BrokerIds == nil { + invalidParams.Add(smithy.NewErrParamRequired("BrokerIds")) + } + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpRejectClientVpcConnectionInput(v *RejectClientVpcConnectionInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "RejectClientVpcConnectionInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if v.VpcConnectionArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("VpcConnectionArn")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpTagResourceInput(v *TagResourceInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "TagResourceInput"} + if v.ResourceArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ResourceArn")) + } + if v.Tags == nil { + invalidParams.Add(smithy.NewErrParamRequired("Tags")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpUntagResourceInput(v *UntagResourceInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "UntagResourceInput"} + if v.ResourceArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ResourceArn")) + } + if v.TagKeys == nil { + invalidParams.Add(smithy.NewErrParamRequired("TagKeys")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpUpdateBrokerCountInput(v *UpdateBrokerCountInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "UpdateBrokerCountInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if v.CurrentVersion == nil { + invalidParams.Add(smithy.NewErrParamRequired("CurrentVersion")) + } + if v.TargetNumberOfBrokerNodes == nil { + invalidParams.Add(smithy.NewErrParamRequired("TargetNumberOfBrokerNodes")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpUpdateBrokerStorageInput(v *UpdateBrokerStorageInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "UpdateBrokerStorageInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if v.CurrentVersion == nil { + invalidParams.Add(smithy.NewErrParamRequired("CurrentVersion")) + } + if v.TargetBrokerEBSVolumeInfo == nil { + invalidParams.Add(smithy.NewErrParamRequired("TargetBrokerEBSVolumeInfo")) + } else if v.TargetBrokerEBSVolumeInfo != nil { + if err := validate__listOfBrokerEBSVolumeInfo(v.TargetBrokerEBSVolumeInfo); err != nil { + invalidParams.AddNested("TargetBrokerEBSVolumeInfo", err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpUpdateBrokerTypeInput(v *UpdateBrokerTypeInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "UpdateBrokerTypeInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if v.CurrentVersion == nil { + invalidParams.Add(smithy.NewErrParamRequired("CurrentVersion")) + } + if v.TargetInstanceType == nil { + invalidParams.Add(smithy.NewErrParamRequired("TargetInstanceType")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpUpdateClusterConfigurationInput(v *UpdateClusterConfigurationInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "UpdateClusterConfigurationInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if v.ConfigurationInfo == nil { + invalidParams.Add(smithy.NewErrParamRequired("ConfigurationInfo")) + } else if v.ConfigurationInfo != nil { + if err := validateConfigurationInfo(v.ConfigurationInfo); err != nil { + invalidParams.AddNested("ConfigurationInfo", err.(smithy.InvalidParamsError)) + } + } + if v.CurrentVersion == nil { + invalidParams.Add(smithy.NewErrParamRequired("CurrentVersion")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpUpdateClusterKafkaVersionInput(v *UpdateClusterKafkaVersionInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "UpdateClusterKafkaVersionInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if v.ConfigurationInfo != nil { + if err := validateConfigurationInfo(v.ConfigurationInfo); err != nil { + invalidParams.AddNested("ConfigurationInfo", err.(smithy.InvalidParamsError)) + } + } + if v.CurrentVersion == nil { + invalidParams.Add(smithy.NewErrParamRequired("CurrentVersion")) + } + if v.TargetKafkaVersion == nil { + invalidParams.Add(smithy.NewErrParamRequired("TargetKafkaVersion")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpUpdateConfigurationInput(v *UpdateConfigurationInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "UpdateConfigurationInput"} + if v.Arn == nil { + invalidParams.Add(smithy.NewErrParamRequired("Arn")) + } + if v.ServerProperties == nil { + invalidParams.Add(smithy.NewErrParamRequired("ServerProperties")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpUpdateConnectivityInput(v *UpdateConnectivityInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "UpdateConnectivityInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if v.ConnectivityInfo == nil { + invalidParams.Add(smithy.NewErrParamRequired("ConnectivityInfo")) + } + if v.CurrentVersion == nil { + invalidParams.Add(smithy.NewErrParamRequired("CurrentVersion")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpUpdateMonitoringInput(v *UpdateMonitoringInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "UpdateMonitoringInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if v.CurrentVersion == nil { + invalidParams.Add(smithy.NewErrParamRequired("CurrentVersion")) + } + if v.OpenMonitoring != nil { + if err := validateOpenMonitoringInfo(v.OpenMonitoring); err != nil { + invalidParams.AddNested("OpenMonitoring", err.(smithy.InvalidParamsError)) + } + } + if v.LoggingInfo != nil { + if err := validateLoggingInfo(v.LoggingInfo); err != nil { + invalidParams.AddNested("LoggingInfo", err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpUpdateRebalancingInput(v *UpdateRebalancingInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "UpdateRebalancingInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if v.CurrentVersion == nil { + invalidParams.Add(smithy.NewErrParamRequired("CurrentVersion")) + } + if v.Rebalancing == nil { + invalidParams.Add(smithy.NewErrParamRequired("Rebalancing")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpUpdateReplicationInfoInput(v *UpdateReplicationInfoInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "UpdateReplicationInfoInput"} + if v.ConsumerGroupReplication != nil { + if err := validateConsumerGroupReplicationUpdate(v.ConsumerGroupReplication); err != nil { + invalidParams.AddNested("ConsumerGroupReplication", err.(smithy.InvalidParamsError)) + } + } + if v.CurrentVersion == nil { + invalidParams.Add(smithy.NewErrParamRequired("CurrentVersion")) + } + if v.ReplicatorArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ReplicatorArn")) + } + if v.SourceKafkaClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("SourceKafkaClusterArn")) + } + if v.TargetKafkaClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("TargetKafkaClusterArn")) + } + if v.TopicReplication != nil { + if err := validateTopicReplicationUpdate(v.TopicReplication); err != nil { + invalidParams.AddNested("TopicReplication", err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpUpdateSecurityInput(v *UpdateSecurityInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "UpdateSecurityInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if v.CurrentVersion == nil { + invalidParams.Add(smithy.NewErrParamRequired("CurrentVersion")) + } + if v.EncryptionInfo != nil { + if err := validateEncryptionInfo(v.EncryptionInfo); err != nil { + invalidParams.AddNested("EncryptionInfo", err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpUpdateStorageInput(v *UpdateStorageInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "UpdateStorageInput"} + if v.ClusterArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("ClusterArn")) + } + if v.CurrentVersion == nil { + invalidParams.Add(smithy.NewErrParamRequired("CurrentVersion")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/lightsail/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/lightsail/CHANGELOG.md index 627d954cd..78844c3f6 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/lightsail/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/lightsail/CHANGELOG.md @@ -1,3 +1,7 @@ +# v1.50.11 (2026-01-09) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.50.10 (2025-12-08) * **Dependency Update**: Updated to the latest SDK module versions diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/lightsail/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/lightsail/go_module_metadata.go index 65eae7010..91b9f8801 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/lightsail/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/lightsail/go_module_metadata.go @@ -3,4 +3,4 @@ package lightsail // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.50.10" +const goModuleVersion = "1.50.11" diff --git a/vendor/github.com/bahlo/generic-list-go/LICENSE b/vendor/github.com/bahlo/generic-list-go/LICENSE new file mode 100644 index 000000000..6a66aea5e --- /dev/null +++ b/vendor/github.com/bahlo/generic-list-go/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/bahlo/generic-list-go/README.md b/vendor/github.com/bahlo/generic-list-go/README.md new file mode 100644 index 000000000..68bbce9fb --- /dev/null +++ b/vendor/github.com/bahlo/generic-list-go/README.md @@ -0,0 +1,5 @@ +# generic-list-go [![CI](https://github.com/bahlo/generic-list-go/actions/workflows/ci.yml/badge.svg)](https://github.com/bahlo/generic-list-go/actions/workflows/ci.yml) + +Go [container/list](https://pkg.go.dev/container/list) but with generics. + +The code is based on `container/list` in `go1.18beta2`. diff --git a/vendor/github.com/bahlo/generic-list-go/list.go b/vendor/github.com/bahlo/generic-list-go/list.go new file mode 100644 index 000000000..a06a7c612 --- /dev/null +++ b/vendor/github.com/bahlo/generic-list-go/list.go @@ -0,0 +1,235 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package list implements a doubly linked list. +// +// To iterate over a list (where l is a *List): +// for e := l.Front(); e != nil; e = e.Next() { +// // do something with e.Value +// } +// +package list + +// Element is an element of a linked list. +type Element[T any] struct { + // Next and previous pointers in the doubly-linked list of elements. + // To simplify the implementation, internally a list l is implemented + // as a ring, such that &l.root is both the next element of the last + // list element (l.Back()) and the previous element of the first list + // element (l.Front()). + next, prev *Element[T] + + // The list to which this element belongs. + list *List[T] + + // The value stored with this element. + Value T +} + +// Next returns the next list element or nil. +func (e *Element[T]) Next() *Element[T] { + if p := e.next; e.list != nil && p != &e.list.root { + return p + } + return nil +} + +// Prev returns the previous list element or nil. +func (e *Element[T]) Prev() *Element[T] { + if p := e.prev; e.list != nil && p != &e.list.root { + return p + } + return nil +} + +// List represents a doubly linked list. +// The zero value for List is an empty list ready to use. +type List[T any] struct { + root Element[T] // sentinel list element, only &root, root.prev, and root.next are used + len int // current list length excluding (this) sentinel element +} + +// Init initializes or clears list l. +func (l *List[T]) Init() *List[T] { + l.root.next = &l.root + l.root.prev = &l.root + l.len = 0 + return l +} + +// New returns an initialized list. +func New[T any]() *List[T] { return new(List[T]).Init() } + +// Len returns the number of elements of list l. +// The complexity is O(1). +func (l *List[T]) Len() int { return l.len } + +// Front returns the first element of list l or nil if the list is empty. +func (l *List[T]) Front() *Element[T] { + if l.len == 0 { + return nil + } + return l.root.next +} + +// Back returns the last element of list l or nil if the list is empty. +func (l *List[T]) Back() *Element[T] { + if l.len == 0 { + return nil + } + return l.root.prev +} + +// lazyInit lazily initializes a zero List value. +func (l *List[T]) lazyInit() { + if l.root.next == nil { + l.Init() + } +} + +// insert inserts e after at, increments l.len, and returns e. +func (l *List[T]) insert(e, at *Element[T]) *Element[T] { + e.prev = at + e.next = at.next + e.prev.next = e + e.next.prev = e + e.list = l + l.len++ + return e +} + +// insertValue is a convenience wrapper for insert(&Element{Value: v}, at). +func (l *List[T]) insertValue(v T, at *Element[T]) *Element[T] { + return l.insert(&Element[T]{Value: v}, at) +} + +// remove removes e from its list, decrements l.len +func (l *List[T]) remove(e *Element[T]) { + e.prev.next = e.next + e.next.prev = e.prev + e.next = nil // avoid memory leaks + e.prev = nil // avoid memory leaks + e.list = nil + l.len-- +} + +// move moves e to next to at. +func (l *List[T]) move(e, at *Element[T]) { + if e == at { + return + } + e.prev.next = e.next + e.next.prev = e.prev + + e.prev = at + e.next = at.next + e.prev.next = e + e.next.prev = e +} + +// Remove removes e from l if e is an element of list l. +// It returns the element value e.Value. +// The element must not be nil. +func (l *List[T]) Remove(e *Element[T]) T { + if e.list == l { + // if e.list == l, l must have been initialized when e was inserted + // in l or l == nil (e is a zero Element) and l.remove will crash + l.remove(e) + } + return e.Value +} + +// PushFront inserts a new element e with value v at the front of list l and returns e. +func (l *List[T]) PushFront(v T) *Element[T] { + l.lazyInit() + return l.insertValue(v, &l.root) +} + +// PushBack inserts a new element e with value v at the back of list l and returns e. +func (l *List[T]) PushBack(v T) *Element[T] { + l.lazyInit() + return l.insertValue(v, l.root.prev) +} + +// InsertBefore inserts a new element e with value v immediately before mark and returns e. +// If mark is not an element of l, the list is not modified. +// The mark must not be nil. +func (l *List[T]) InsertBefore(v T, mark *Element[T]) *Element[T] { + if mark.list != l { + return nil + } + // see comment in List.Remove about initialization of l + return l.insertValue(v, mark.prev) +} + +// InsertAfter inserts a new element e with value v immediately after mark and returns e. +// If mark is not an element of l, the list is not modified. +// The mark must not be nil. +func (l *List[T]) InsertAfter(v T, mark *Element[T]) *Element[T] { + if mark.list != l { + return nil + } + // see comment in List.Remove about initialization of l + return l.insertValue(v, mark) +} + +// MoveToFront moves element e to the front of list l. +// If e is not an element of l, the list is not modified. +// The element must not be nil. +func (l *List[T]) MoveToFront(e *Element[T]) { + if e.list != l || l.root.next == e { + return + } + // see comment in List.Remove about initialization of l + l.move(e, &l.root) +} + +// MoveToBack moves element e to the back of list l. +// If e is not an element of l, the list is not modified. +// The element must not be nil. +func (l *List[T]) MoveToBack(e *Element[T]) { + if e.list != l || l.root.prev == e { + return + } + // see comment in List.Remove about initialization of l + l.move(e, l.root.prev) +} + +// MoveBefore moves element e to its new position before mark. +// If e or mark is not an element of l, or e == mark, the list is not modified. +// The element and mark must not be nil. +func (l *List[T]) MoveBefore(e, mark *Element[T]) { + if e.list != l || e == mark || mark.list != l { + return + } + l.move(e, mark.prev) +} + +// MoveAfter moves element e to its new position after mark. +// If e or mark is not an element of l, or e == mark, the list is not modified. +// The element and mark must not be nil. +func (l *List[T]) MoveAfter(e, mark *Element[T]) { + if e.list != l || e == mark || mark.list != l { + return + } + l.move(e, mark) +} + +// PushBackList inserts a copy of another list at the back of list l. +// The lists l and other may be the same. They must not be nil. +func (l *List[T]) PushBackList(other *List[T]) { + l.lazyInit() + for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() { + l.insertValue(e.Value, l.root.prev) + } +} + +// PushFrontList inserts a copy of another list at the front of list l. +// The lists l and other may be the same. They must not be nil. +func (l *List[T]) PushFrontList(other *List[T]) { + l.lazyInit() + for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() { + l.insertValue(e.Value, &l.root) + } +} diff --git a/vendor/github.com/basgys/goxml2json/.gitignore b/vendor/github.com/basgys/goxml2json/.gitignore new file mode 100644 index 000000000..6bfad5422 --- /dev/null +++ b/vendor/github.com/basgys/goxml2json/.gitignore @@ -0,0 +1,25 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof +/.tags diff --git a/vendor/github.com/basgys/goxml2json/LICENSE b/vendor/github.com/basgys/goxml2json/LICENSE new file mode 100644 index 000000000..dc5a2e3eb --- /dev/null +++ b/vendor/github.com/basgys/goxml2json/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Bastien Gysler + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/basgys/goxml2json/README.md b/vendor/github.com/basgys/goxml2json/README.md new file mode 100644 index 000000000..0abdfcda4 --- /dev/null +++ b/vendor/github.com/basgys/goxml2json/README.md @@ -0,0 +1,107 @@ +# goxml2json [![CircleCI](https://circleci.com/gh/basgys/goxml2json.svg?style=svg)](https://circleci.com/gh/basgys/goxml2json) + +Go package that converts XML to JSON + +### Install + + go get -u github.com/basgys/goxml2json + +### Importing + + import github.com/basgys/goxml2json + +### Usage + +**Code example** + +```go + package main + + import ( + "fmt" + "strings" + + xj "github.com/basgys/goxml2json" + ) + + func main() { + // xml is an io.Reader + xml := strings.NewReader(`world`) + json, err := xj.Convert(xml) + if err != nil { + panic("That's embarrassing...") + } + + fmt.Println(json.String()) + // {"hello": "world"} + } + +``` + +**Input** + +```xml + + + + bar + +``` + +**Output** + +```json + { + "osm": { + "-version": 0.6, + "-generator": "CGImap 0.0.2", + "bounds": { + "-minlat": "54.0889580", + "-minlon": "12.2487570", + "-maxlat": "54.0913900", + "-maxlon": "12.2524800" + }, + "foo": "bar" + } + } +``` + +**With type conversion** + +```go + package main + + import ( + "fmt" + "strings" + + xj "github.com/basgys/goxml2json" + ) + + func main() { + // xml is an io.Reader + xml := strings.NewReader(`19.95`) + json, err := xj.Convert(xml, xj.WithTypeConverter(xj.Float)) + if err != nil { + panic("That's embarrassing...") + } + + fmt.Println(json.String()) + // {"price": 19.95} + } +``` + +### Contributing +Feel free to contribute to this project if you want to fix/extend/improve it. + +### Contributors + + - [DirectX](https://github.com/directx) + - [powerslacker](https://github.com/powerslacker) + - [samuelhug](https://github.com/samuelhug) + +### TODO + + * Categorise errors + * Option to prettify the JSON output + * Benchmark diff --git a/vendor/github.com/basgys/goxml2json/converter.go b/vendor/github.com/basgys/goxml2json/converter.go new file mode 100644 index 000000000..a1311ab80 --- /dev/null +++ b/vendor/github.com/basgys/goxml2json/converter.go @@ -0,0 +1,26 @@ +package xml2json + +import ( + "bytes" + "io" +) + +// Convert converts the given XML document to JSON +func Convert(r io.Reader, ps ...plugin) (*bytes.Buffer, error) { + // Decode XML document + root := &Node{} + err := NewDecoder(r, ps...).Decode(root) + if err != nil { + return nil, err + } + + // Then encode it in JSON + buf := new(bytes.Buffer) + e := NewEncoder(buf, ps...) + err = e.Encode(root) + if err != nil { + return nil, err + } + + return buf, nil +} diff --git a/vendor/github.com/basgys/goxml2json/decoder.go b/vendor/github.com/basgys/goxml2json/decoder.go new file mode 100644 index 000000000..a45079f47 --- /dev/null +++ b/vendor/github.com/basgys/goxml2json/decoder.go @@ -0,0 +1,155 @@ +package xml2json + +import ( + "encoding/xml" + "io" + "unicode" + + "golang.org/x/net/html/charset" +) + +const ( + attrPrefix = "-" + contentPrefix = "#" +) + +// A Decoder reads and decodes XML objects from an input stream. +type Decoder struct { + r io.Reader + err error + attributePrefix string + contentPrefix string + excludeAttrs map[string]bool + formatters []nodeFormatter +} + +type element struct { + parent *element + n *Node + label string +} + +func (dec *Decoder) SetAttributePrefix(prefix string) { + dec.attributePrefix = prefix +} + +func (dec *Decoder) SetContentPrefix(prefix string) { + dec.contentPrefix = prefix +} + +func (dec *Decoder) AddFormatters(formatters []nodeFormatter) { + dec.formatters = formatters +} + +func (dec *Decoder) ExcludeAttributes(attrs []string) { + for _, attr := range attrs { + dec.excludeAttrs[attr] = true + } +} + +func (dec *Decoder) DecodeWithCustomPrefixes(root *Node, contentPrefix string, attributePrefix string) error { + dec.contentPrefix = contentPrefix + dec.attributePrefix = attributePrefix + return dec.Decode(root) +} + +// NewDecoder returns a new decoder that reads from r. +func NewDecoder(r io.Reader, plugins ...plugin) *Decoder { + d := &Decoder{r: r, contentPrefix: contentPrefix, attributePrefix: attrPrefix, excludeAttrs: map[string]bool{}} + for _, p := range plugins { + d = p.AddToDecoder(d) + } + return d +} + +// Decode reads the next JSON-encoded value from its +// input and stores it in the value pointed to by v. +func (dec *Decoder) Decode(root *Node) error { + xmlDec := xml.NewDecoder(dec.r) + + // That will convert the charset if the provided XML is non-UTF-8 + xmlDec.CharsetReader = charset.NewReaderLabel + + // Create first element from the root node + elem := &element{ + parent: nil, + n: root, + } + + for { + t, _ := xmlDec.Token() + if t == nil { + break + } + + switch se := t.(type) { + case xml.StartElement: + // Build new a new current element and link it to its parent + elem = &element{ + parent: elem, + n: &Node{}, + label: se.Name.Local, + } + + // Extract attributes as children + for _, a := range se.Attr { + if _, ok := dec.excludeAttrs[a.Name.Local]; ok { + continue + } + elem.n.AddChild(dec.attributePrefix+a.Name.Local, &Node{Data: a.Value}) + } + case xml.CharData: + // Extract XML data (if any) + elem.n.Data = trimNonGraphic(string(xml.CharData(se))) + case xml.EndElement: + // And add it to its parent list + if elem.parent != nil { + elem.parent.n.AddChild(elem.label, elem.n) + } + + // Then change the current element to its parent + elem = elem.parent + } + } + + for _, formatter := range dec.formatters { + formatter.Format(root) + } + + return nil +} + +// trimNonGraphic returns a slice of the string s, with all leading and trailing +// non graphic characters and spaces removed. +// +// Graphic characters include letters, marks, numbers, punctuation, symbols, +// and spaces, from categories L, M, N, P, S, Zs. +// Spacing characters are set by category Z and property Pattern_White_Space. +func trimNonGraphic(s string) string { + if s == "" { + return s + } + + var first *int + var last int + for i, r := range []rune(s) { + if !unicode.IsGraphic(r) || unicode.IsSpace(r) { + continue + } + + if first == nil { + f := i // copy i + first = &f + last = i + } else { + last = i + } + } + + // If first is nil, it means there are no graphic characters + if first == nil { + return "" + } + + return string([]rune(s)[*first : last+1]) +} diff --git a/vendor/github.com/basgys/goxml2json/doc.go b/vendor/github.com/basgys/goxml2json/doc.go new file mode 100644 index 000000000..8a68bd30f --- /dev/null +++ b/vendor/github.com/basgys/goxml2json/doc.go @@ -0,0 +1,2 @@ +// Package xml2json is an XML to JSON converter +package xml2json diff --git a/vendor/github.com/basgys/goxml2json/encoder.go b/vendor/github.com/basgys/goxml2json/encoder.go new file mode 100644 index 000000000..61fafc57f --- /dev/null +++ b/vendor/github.com/basgys/goxml2json/encoder.go @@ -0,0 +1,191 @@ +package xml2json + +import ( + "bytes" + "io" + "unicode/utf8" +) + +// An Encoder writes JSON objects to an output stream. +type Encoder struct { + w io.Writer + err error + contentPrefix string + attributePrefix string + tc encoderTypeConverter +} + +// NewEncoder returns a new encoder that writes to w. +func NewEncoder(w io.Writer, plugins ...plugin) *Encoder { + e := &Encoder{w: w, contentPrefix: contentPrefix, attributePrefix: attrPrefix} + for _, p := range plugins { + e = p.AddToEncoder(e) + } + return e +} + +// Encode writes the JSON encoding of v to the stream +func (enc *Encoder) Encode(root *Node) error { + if enc.err != nil { + return enc.err + } + if root == nil { + return nil + } + + enc.err = enc.format(root, 0) + + // Terminate each value with a newline. + // This makes the output look a little nicer + // when debugging, and some kind of space + // is required if the encoded value was a number, + // so that the reader knows there aren't more + // digits coming. + enc.write("\n") + + return enc.err +} + +func (enc *Encoder) format(n *Node, lvl int) error { + if n.IsComplex() { + enc.write("{") + + // Add data as an additional attibute (if any) + if len(n.Data) > 0 { + enc.write("\"") + enc.write(enc.contentPrefix) + enc.write("content") + enc.write("\": ") + enc.write(sanitiseString(n.Data)) + enc.write(", ") + } + + i := 0 + tot := len(n.Children) + for label, children := range n.Children { + enc.write("\"") + enc.write(label) + enc.write("\": ") + + if n.ChildrenAlwaysAsArray || len(children) > 1 { + // Array + enc.write("[") + for j, c := range children { + enc.format(c, lvl+1) + + if j < len(children)-1 { + enc.write(", ") + } + } + enc.write("]") + } else { + // Map + enc.format(children[0], lvl+1) + } + + if i < tot-1 { + enc.write(", ") + } + i++ + } + + enc.write("}") + } else { + s := sanitiseString(n.Data) + if enc.tc == nil { + // do nothing + } else { + s = enc.tc.Convert(s) + } + enc.write(s) + + } + + return nil +} + +func (enc *Encoder) write(s string) { + enc.w.Write([]byte(s)) +} + +// https://golang.org/src/encoding/json/encode.go?s=5584:5627#L788 +var hex = "0123456789abcdef" + +func sanitiseString(s string) string { + var buf bytes.Buffer + + buf.WriteByte('"') + + start := 0 + for i := 0; i < len(s); { + if b := s[i]; b < utf8.RuneSelf { + if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' { + i++ + continue + } + if start < i { + buf.WriteString(s[start:i]) + } + switch b { + case '\\', '"': + buf.WriteByte('\\') + buf.WriteByte(b) + case '\n': + buf.WriteByte('\\') + buf.WriteByte('n') + case '\r': + buf.WriteByte('\\') + buf.WriteByte('r') + case '\t': + buf.WriteByte('\\') + buf.WriteByte('t') + default: + // This encodes bytes < 0x20 except for \n and \r, + // as well as <, > and &. The latter are escaped because they + // can lead to security holes when user-controlled strings + // are rendered into JSON and served to some browsers. + buf.WriteString(`\u00`) + buf.WriteByte(hex[b>>4]) + buf.WriteByte(hex[b&0xF]) + } + i++ + start = i + continue + } + c, size := utf8.DecodeRuneInString(s[i:]) + if c == utf8.RuneError && size == 1 { + if start < i { + buf.WriteString(s[start:i]) + } + buf.WriteString(`\ufffd`) + i += size + start = i + continue + } + // U+2028 is LINE SEPARATOR. + // U+2029 is PARAGRAPH SEPARATOR. + // They are both technically valid characters in JSON strings, + // but don't work in JSONP, which has to be evaluated as JavaScript, + // and can lead to security holes there. It is valid JSON to + // escape them, so we do so unconditionally. + // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion. + if c == '\u2028' || c == '\u2029' { + if start < i { + buf.WriteString(s[start:i]) + } + buf.WriteString(`\u202`) + buf.WriteByte(hex[c&0xF]) + i += size + start = i + continue + } + i += size + } + if start < len(s) { + buf.WriteString(s[start:]) + } + + buf.WriteByte('"') + + return buf.String() +} diff --git a/vendor/github.com/basgys/goxml2json/jstype.go b/vendor/github.com/basgys/goxml2json/jstype.go new file mode 100644 index 000000000..c0c08cb5f --- /dev/null +++ b/vendor/github.com/basgys/goxml2json/jstype.go @@ -0,0 +1,74 @@ +package xml2json + +import ( + "strconv" + "strings" +) + +// https://cswr.github.io/JsonSchema/spec/basic_types/ +// JSType is a JavaScript extracted from a string +type JSType int + +const ( + Bool JSType = iota + Int + Float + String + Null +) + +// Str2JSType extract a JavaScript type from a string +func Str2JSType(s string) JSType { + var ( + output JSType + ) + s = strings.TrimSpace(s) // santize the given string + switch { + case isBool(s): + output = Bool + case isFloat(s): + output = Float + case isInt(s): + output = Int + case isNull(s): + output = Null + default: + output = String // if all alternatives have been eliminated, the input is a string + } + return output +} + +func isBool(s string) bool { + return s == "true" || s == "false" +} + +func isFloat(s string) bool { + var output = false + if strings.Contains(s, ".") { + _, err := strconv.ParseFloat(s, 64) + if err == nil { // the string successfully converts to a decimal + output = true + } + } + return output +} + +func isInt(s string) bool { + var output = false + if len(s) >= 1 { + _, err := strconv.Atoi(s) + if err == nil { // the string successfully converts to an int + if s != "0" && s[0] == '0' { + // if the first rune is '0' and there is more than 1 rune, then the input is most likely a float or intended to be + // a string value -- such as in the case of a guid, or an international phone number + } else { + output = true + } + } + } + return output +} + +func isNull(s string) bool { + return s == "null" +} diff --git a/vendor/github.com/basgys/goxml2json/plugins.go b/vendor/github.com/basgys/goxml2json/plugins.go new file mode 100644 index 000000000..60137f05d --- /dev/null +++ b/vendor/github.com/basgys/goxml2json/plugins.go @@ -0,0 +1,161 @@ +package xml2json + +import ( + "strings" +) + +type ( + // an plugin is added to an encoder or/and to an decoder to allow custom functionality at runtime + plugin interface { + AddToEncoder(*Encoder) *Encoder + AddToDecoder(*Decoder) *Decoder + } + // a type converter overides the default string sanitization for encoding json + encoderTypeConverter interface { + Convert(string) string + } + // customTypeConverter converts strings to JSON types using a best guess approach, only parses the JSON types given + // when initialized via WithTypeConverter + customTypeConverter struct { + parseTypes []JSType + } + + attrPrefixer string + contentPrefixer string + + excluder []string + + nodesFormatter struct { + list []nodeFormatter + } + nodeFormatter struct { + path string + plugin nodePlugin + } + + nodePlugin interface { + AddTo(*Node) + } + + arrayFormatter struct{} +) + +// WithTypeConverter allows customized js type conversion behavior by passing in the desired JSTypes +func WithTypeConverter(ts ...JSType) *customTypeConverter { + return &customTypeConverter{parseTypes: ts} +} + +func (tc *customTypeConverter) parseAsString(t JSType) bool { + if t == String { + return true + } + for i := 0; i < len(tc.parseTypes); i++ { + if tc.parseTypes[i] == t { + return false + } + } + return true +} + +// Adds the type converter to the encoder +func (tc *customTypeConverter) AddToEncoder(e *Encoder) *Encoder { + e.tc = tc + return e +} + +func (tc *customTypeConverter) AddToDecoder(d *Decoder) *Decoder { + return d +} + +func (tc *customTypeConverter) Convert(s string) string { + // remove quotes if they exists + if strings.HasPrefix(s, `"`) && strings.HasSuffix(s, `"`) { + s = s[1 : len(s)-1] + } + jsType := Str2JSType(s) + if tc.parseAsString(jsType) { + // add the quotes removed at the start of this func + s = `"` + s + `"` + } + return s +} + +// WithAttrPrefix appends the given prefix to the json output of xml attribute fields to preserve namespaces +func WithAttrPrefix(prefix string) *attrPrefixer { + ap := attrPrefixer(prefix) + return &ap +} + +func (a *attrPrefixer) AddToEncoder(e *Encoder) *Encoder { + e.attributePrefix = string((*a)) + return e +} + +func (a *attrPrefixer) AddToDecoder(d *Decoder) *Decoder { + d.attributePrefix = string((*a)) + return d +} + +// WithContentPrefix appends the given prefix to the json output of xml content fields to preserve namespaces +func WithContentPrefix(prefix string) *contentPrefixer { + c := contentPrefixer(prefix) + return &c +} + +func (c *contentPrefixer) AddToEncoder(e *Encoder) *Encoder { + e.contentPrefix = string((*c)) + return e +} + +func (c *contentPrefixer) AddToDecoder(d *Decoder) *Decoder { + d.contentPrefix = string((*c)) + return d +} + +// ExcludeAttributes excludes some xml attributes, for example, xmlns:xsi, xsi:noNamespaceSchemaLocation +func ExcludeAttributes(attrs []string) *excluder { + ex := excluder(attrs) + return &ex +} + +func (ex *excluder) AddToEncoder(e *Encoder) *Encoder { + return e +} + +func (ex *excluder) AddToDecoder(d *Decoder) *Decoder { + d.ExcludeAttributes([]string((*ex))) + return d +} + +// WithNodes formats specific nodes +func WithNodes(n ...nodeFormatter) *nodesFormatter { + return &nodesFormatter{list: n} +} + +func (nf *nodesFormatter) AddToEncoder(e *Encoder) *Encoder { + return e +} + +func (nf *nodesFormatter) AddToDecoder(d *Decoder) *Decoder { + d.AddFormatters(nf.list) + return d +} + +func NodePlugin(path string, plugin nodePlugin) nodeFormatter { + return nodeFormatter{path: path, plugin: plugin} +} + +func (nf *nodeFormatter) Format(node *Node) { + child := node.GetChild(nf.path) + if child != nil { + nf.plugin.AddTo(child) + } +} + +func ToArray() *arrayFormatter { + return &arrayFormatter{} +} + +func (af *arrayFormatter) AddTo(n *Node) { + n.ChildrenAlwaysAsArray = true +} diff --git a/vendor/github.com/basgys/goxml2json/struct.go b/vendor/github.com/basgys/goxml2json/struct.go new file mode 100644 index 000000000..350e1ac72 --- /dev/null +++ b/vendor/github.com/basgys/goxml2json/struct.go @@ -0,0 +1,47 @@ +package xml2json + +import ( + "strings" +) + +// Node is a data element on a tree +type Node struct { + Children map[string]Nodes + Data string + ChildrenAlwaysAsArray bool +} + +// Nodes is a list of nodes +type Nodes []*Node + +// AddChild appends a node to the list of children +func (n *Node) AddChild(s string, c *Node) { + // Lazy lazy + if n.Children == nil { + n.Children = map[string]Nodes{} + } + + n.Children[s] = append(n.Children[s], c) +} + +// IsComplex returns whether it is a complex type (has children) +func (n *Node) IsComplex() bool { + return len(n.Children) > 0 +} + +// GetChild returns child by path if exists. Path looks like "grandparent.parent.child.grandchild" +func (n *Node) GetChild(path string) *Node { + result := n + names := strings.Split(path, ".") + for _, name := range names { + children, exists := result.Children[name] + if !exists { + return nil + } + if len(children) == 0 { + return nil + } + result = children[0] + } + return result +} diff --git a/vendor/github.com/digitalocean/godo/CHANGELOG.md b/vendor/github.com/digitalocean/godo/CHANGELOG.md index 2ebf4827f..503029664 100644 --- a/vendor/github.com/digitalocean/godo/CHANGELOG.md +++ b/vendor/github.com/digitalocean/godo/CHANGELOG.md @@ -1,5 +1,16 @@ # Change Log +## [1.173.0] - 2026-01-22 + +- #942 - @anup-deka - Fix data type +- #941 - @v-amanjain-afk - removed deprecated region from nfs api + +## [1.172.0] - 2026-01-13 + +- #932 - @fumblehool - APPS-12654: Add CancelJobInvocation API +- #939 - @anup-deka - Rebranding GenAI to Gradient AI +- #914 - @brianteeman - Typo cannnot/cannot + ## [1.171.0] - 2025-12-17 - #936 - @dillonledoux - Add fields to AgentCreateRequest for Gradient Agents diff --git a/vendor/github.com/digitalocean/godo/apps.go b/vendor/github.com/digitalocean/godo/apps.go index 53e78eb90..e1fa86d56 100644 --- a/vendor/github.com/digitalocean/godo/apps.go +++ b/vendor/github.com/digitalocean/godo/apps.go @@ -80,6 +80,7 @@ type AppsService interface { ListJobInvocations(ctx context.Context, appID string, opts *ListJobInvocationsOptions) ([]*JobInvocation, *Response, error) GetJobInvocation(ctx context.Context, appID string, jobInvocationId string, opts *GetJobInvocationOptions) (*JobInvocation, *Response, error) GetJobInvocationLogs(ctx context.Context, appID, jobInvocationId string, opts *GetJobInvocationLogsOptions) (*AppLogs, *Response, error) + CancelJobInvocation(ctx context.Context, appID, jobInvocationID string, opts *CancelJobInvocationOptions) (*JobInvocation, *Response, error) } // AppLogs represent app logs. @@ -132,6 +133,10 @@ type ListJobInvocationsOptions struct { JobNames []string `url:"job_names,omitempty"` } +type CancelJobInvocationOptions struct { + JobName string `url:"job_name,omitempty"` +} + // DeploymentCreateRequest represents a request to create a deployment. type DeploymentCreateRequest struct { ForceBuild bool `json:"force_build"` @@ -519,6 +524,28 @@ func (s *AppsServiceOp) GetJobInvocationLogs(ctx context.Context, appID, jobInvo return logs, resp, nil } +// CancelJobInvocation cancels a specific job invocation for a given app. +func (s *AppsServiceOp) CancelJobInvocation(ctx context.Context, appID string, jobInvocationId string, opts *CancelJobInvocationOptions) (*JobInvocation, *Response, error) { + url := fmt.Sprintf("%s/%s/job-invocations/%s/cancel", appsBasePath, appID, jobInvocationId) + + url, err := addOptions(url, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, url, nil) + if err != nil { + return nil, nil, err + } + + root := new(jobInvocationRoot) + resp, err := s.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + return root.JobInvocation, resp, nil +} + // GetLogs retrieves app logs. func (s *AppsServiceOp) GetLogs(ctx context.Context, appID, deploymentID, component string, logType AppLogType, follow bool, tailLines int) (*AppLogs, *Response, error) { var url string diff --git a/vendor/github.com/digitalocean/godo/godo.go b/vendor/github.com/digitalocean/godo/godo.go index b9d98fa7d..8be2b35e3 100644 --- a/vendor/github.com/digitalocean/godo/godo.go +++ b/vendor/github.com/digitalocean/godo/godo.go @@ -21,7 +21,7 @@ import ( ) const ( - libraryVersion = "1.171.0" + libraryVersion = "1.173.0" defaultBaseURL = "https://api.digitalocean.com/" userAgent = "godo/" + libraryVersion mediaType = "application/json" @@ -98,7 +98,7 @@ type Client struct { UptimeChecks UptimeChecksService VPCs VPCsService PartnerAttachment PartnerAttachmentService - GenAI GenAIService + GradientAI GradientAIService BYOIPPrefixes BYOIPPrefixesService // Optional function called after every successful request made to the DO APIs onRequestCompleted RequestCompletionCallback @@ -328,7 +328,7 @@ func NewClient(httpClient *http.Client) *Client { c.UptimeChecks = &UptimeChecksServiceOp{client: c} c.VPCs = &VPCsServiceOp{client: c} c.PartnerAttachment = &PartnerAttachmentServiceOp{client: c} - c.GenAI = &GenAIServiceOp{client: c} + c.GradientAI = &GradientAIServiceOp{client: c} c.headers = make(map[string]string) diff --git a/vendor/github.com/digitalocean/godo/genai.go b/vendor/github.com/digitalocean/godo/gradientai.go similarity index 85% rename from vendor/github.com/digitalocean/godo/genai.go rename to vendor/github.com/digitalocean/godo/gradientai.go index faea1afee..74207ff6f 100644 --- a/vendor/github.com/digitalocean/godo/genai.go +++ b/vendor/github.com/digitalocean/godo/gradientai.go @@ -9,12 +9,12 @@ import ( ) const ( - genAIBasePath = "/v2/gen-ai/agents" + gradientBasePath = "/v2/gen-ai/agents" agentModelBasePath = "/v2/gen-ai/models" datacenterRegionsPath = "/v2/gen-ai/regions" - agentRouteBasePath = genAIBasePath + "/%s/child_agents/%s" + agentRouteBasePath = gradientBasePath + "/%s/child_agents/%s" KnowledgeBasePath = "/v2/gen-ai/knowledge_bases" - functionRouteBasePath = genAIBasePath + "/%s/functions" + functionRouteBasePath = gradientBasePath + "/%s/functions" KnowledgeBaseDataSourcesPath = KnowledgeBasePath + "/%s/data_sources" GetKnowledgeBaseByIDPath = KnowledgeBasePath + "/%s" UpdateKnowledgeBaseByIDPath = KnowledgeBasePath + "/%s" @@ -32,10 +32,10 @@ const ( DeleteFunctionRoutePath = functionRouteBasePath + "/%s" ) -// GenAIService is an interface for interfacing with the Gen AI Agent endpoints +// GradientAIService is an interface for interfacing with the Gradient AI Agent endpoints // of the DigitalOcean API. -// See https://docs.digitalocean.com/reference/api/digitalocean/#tag/GenAI-Platform-(Public-Preview) for more details. -type GenAIService interface { +// See https://docs.digitalocean.com/reference/api/digitalocean/#tag/GradientAI-Platform for more details. +type GradientAIService interface { ListAgents(context.Context, *ListOptions) ([]*Agent, *Response, error) CreateAgent(context.Context, *AgentCreateRequest) (*Agent, *Response, error) ListAgentAPIKeys(context.Context, string, *ListOptions) ([]*ApiKeyInfo, *Response, error) @@ -85,24 +85,24 @@ type GenAIService interface { ListDatacenterRegions(context.Context, *bool, *bool) ([]*DatacenterRegions, *Response, error) } -var _ GenAIService = &GenAIServiceOp{} +var _ GradientAIService = &GradientAIServiceOp{} -// GenAIServiceOp interfaces with the Gen AI Service endpoints in the DigitalOcean API. -type GenAIServiceOp struct { +// GradientAIServiceOp interfaces with the Gradient AI Service endpoints in the DigitalOcean API. +type GradientAIServiceOp struct { client *Client } -type genAIAgentsRoot struct { +type gradientAgentsRoot struct { Agents []*Agent `json:"agents"` Links *Links `json:"links"` Meta *Meta `json:"meta"` } -type genAIAgentRoot struct { +type gradientAgentRoot struct { Agent *Agent `json:"agent"` } -type genAIModelsRoot struct { +type gradientModelsRoot struct { Models []*Model `json:"models"` Links *Links `json:"links"` Meta *Meta `json:"meta"` @@ -143,7 +143,7 @@ type openaiAPIKeyRoot struct { OpenAIAPIKey *OpenAiApiKey `json:"api_key_info,omitempty"` } -// Agent represents a Gen AI Agent +// Agent represents a Gradient AI Agent type Agent struct { AnthropicApiKey *AnthropicApiKeyInfo `json:"anthropic_api_key,omitempty"` ApiKeyInfos []*ApiKeyInfo `json:"api_key_infos,omitempty"` @@ -188,7 +188,7 @@ type Agent struct { Workspace Workspace `json:"workspace,omitempty"` } -// AgentVersion represents a version of a Gen AI Agent +// AgentVersion represents a version of a Gradient AI Agent type AgentVersion struct { AgentUuid string `json:"agent_uuid,omitempty"` AttachedChildAgents []*AttachedChildAgent `json:"attached_child_agents,omitempty"` @@ -239,13 +239,13 @@ type AuditHeader struct { UserUUID string `json:"user_uuid,omitempty"` } -// RollbackVersionRequest represents the request to rollback a Gen AI Agent to a previous version +// RollbackVersionRequest represents the request to rollback a Gradient AI Agent to a previous version type RollbackVersionRequest struct { AgentUuid string `json:"uuid,omitempty"` VersionHash string `json:"version_hash,omitempty"` } -// AgentFunction represents a Gen AI Agent Function +// AgentFunction represents a Gradient AI Agent Function type AgentFunction struct { ApiKey string `json:"api_key,omitempty"` CreatedAt *Timestamp `json:"created_at,omitempty"` @@ -260,7 +260,7 @@ type AgentFunction struct { IsDeleted bool `json:"is_deleted,omitempty"` } -// AgentGuardrail represents a Guardrail attached to Gen AI Agent +// AgentGuardrail represents a Guardrail attached to Gradient AI Agent type AgentGuardrail struct { AgentUuid string `json:"agent_uuid,omitempty"` CreatedAt *Timestamp `json:"created_at,omitempty"` @@ -318,7 +318,7 @@ type AgentVisibilityUpdateRequest struct { Visibility string `json:"visibility,omitempty"` } -// AgentTemplate represents the template of a Gen AI Agent +// AgentTemplate represents the template of a Gradient AI Agent type AgentTemplate struct { CreatedAt *Timestamp `json:"created_at,omitempty"` Instruction string `json:"instruction,omitempty"` @@ -371,7 +371,7 @@ type Workspace struct { Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` CreatedByEmail string `json:"created_by_email,omitempty"` - CreatedBy uint64 `json:"created_by,omitempty"` + CreatedBy string `json:"created_by,omitempty"` CreatedAt *Timestamp `json:"created_at,omitempty"` UpdatedAt *Timestamp `json:"updated_at,omitempty"` DeletedAt *Timestamp `json:"deleted_at,omitempty"` @@ -434,7 +434,7 @@ type StarMetric struct { SuccessThreshold *float32 `json:"success_threshold,omitempty"` } -// KnowledgeBase represents a Gen AI Knowledge Base +// KnowledgeBase represents a Gradient AI Knowledge Base type KnowledgeBase struct { AddedToAgentAt *Timestamp `json:"added_to_agent_at,omitempty"` CreatedAt *Timestamp `json:"created_at,omitempty"` @@ -452,7 +452,7 @@ type KnowledgeBase struct { IsDeleted bool `json:"is_deleted,omitempty"` } -// LastIndexingJob represents the last indexing job description of a Gen AI Knowledge Base +// LastIndexingJob represents the last indexing job description of a Gradient AI Knowledge Base type LastIndexingJob struct { CompletedDatasources int `json:"completed_datasources,omitempty"` CreatedAt *Timestamp `json:"created_at,omitempty"` @@ -515,7 +515,7 @@ type AgentChatbotIdentifier struct { AgentChatbotIdentifier string `json:"agent_chatbot_identifier,omitempty"` } -// AgentDeployment represents the deployment information of a Gen AI Agent +// AgentDeployment represents the deployment information of a Gradient AI Agent type AgentDeployment struct { CreatedAt *Timestamp `json:"created_at,omitempty"` Name string `json:"name,omitempty"` @@ -526,7 +526,7 @@ type AgentDeployment struct { Visibility string `json:"visibility,omitempty"` } -// ChatBot represents the chatbot information of a Gen AI Agent +// ChatBot represents the chatbot information of a Gradient AI Agent type ChatBot struct { ButtonBackgroundColor string `json:"button_background_color,omitempty"` Logo string `json:"logo,omitempty"` @@ -536,7 +536,7 @@ type ChatBot struct { StartingMessage string `json:"starting_message,omitempty"` } -// Model represents a Gen AI Model +// Model represents a Gradient AI Model type Model struct { Agreement *Agreement `json:"agreement,omitempty"` CreatedAt *Timestamp `json:"created_at,omitempty"` @@ -554,7 +554,7 @@ type Model struct { Version *ModelVersion `json:"version,omitempty"` } -// Agreement represents the agreement information of a Gen AI Model +// Agreement represents the agreement information of a Gradient AI Model type Agreement struct { Description string `json:"description,omitempty"` Name string `json:"name,omitempty"` @@ -568,7 +568,7 @@ type ModelVersion struct { Patch int `json:"patch,omitempty"` } -// AgentCreateRequest represents the request to create a new Gen AI Agent +// AgentCreateRequest represents the request to create a new Gradient AI Agent type AgentCreateRequest struct { AnthropicKeyUuid string `json:"anthropic_key_uuid,omitempty"` Description string `json:"description,omitempty"` @@ -584,13 +584,13 @@ type AgentCreateRequest struct { WorkspaceUuid string `json:"workspace_uuid,omitempty"` } -// AgentAPIKeyCreateRequest represents the request to create a new Gen AI Agent API Key +// AgentAPIKeyCreateRequest represents the request to create a new Gradient AI Agent API Key type AgentAPIKeyCreateRequest struct { AgentUuid string `json:"agent_uuid,omitempty"` Name string `json:"name,omitempty"` } -// AgentUpdateRequest represents the request to update an existing Gen AI Agent +// AgentUpdateRequest represents the request to update an existing Gradient AI Agent type AgentUpdateRequest struct { AnthropicKeyUuid string `json:"anthropic_key_uuid,omitempty"` Description string `json:"description,omitempty"` @@ -610,7 +610,7 @@ type AgentUpdateRequest struct { ProvideCitations bool `json:"provide_citations,omitempty"` } -// AgentAPIKeyUpdateRequest represents the request to update an existing Gen AI Agent API Key +// AgentAPIKeyUpdateRequest represents the request to update an existing Gradient AI Agent API Key type AgentAPIKeyUpdateRequest struct { AgentUuid string `json:"agent_uuid,omitempty"` APIKeyUuid string `json:"api_key_uuid,omitempty"` @@ -650,7 +650,7 @@ type KnowledgeBaseCreateRequest struct { VPCUuid string `json:"vpc_uuid"` } -// KnowledgeBaseDataSource represents a Gen AI Knowledge Base Data Source +// KnowledgeBaseDataSource represents a Gradient AI Knowledge Base Data Source type KnowledgeBaseDataSource struct { CreatedAt *Timestamp `json:"created_at,omitempty"` FileUploadDataSource *FileUploadDataSource `json:"file_upload_data_source,omitempty"` @@ -815,13 +815,13 @@ type datacenterRegionsRoot struct { DatacenterRegions []*DatacenterRegions `json:"regions"` } -type genAIAgentKBRoot struct { +type gradientAgentKBRoot struct { Agent *Agent `json:"agent"` } -// ListAgents returns a list of Gen AI Agents -func (s *GenAIServiceOp) ListAgents(ctx context.Context, opt *ListOptions) ([]*Agent, *Response, error) { - path, err := addOptions(genAIBasePath, opt) +// ListAgents returns a list of Gradient AI Agents +func (s *GradientAIServiceOp) ListAgents(ctx context.Context, opt *ListOptions) ([]*Agent, *Response, error) { + path, err := addOptions(gradientBasePath, opt) if err != nil { return nil, nil, err } @@ -831,7 +831,7 @@ func (s *GenAIServiceOp) ListAgents(ctx context.Context, opt *ListOptions) ([]*A return nil, nil, err } - root := new(genAIAgentsRoot) + root := new(gradientAgentsRoot) resp, err := s.client.Do(ctx, req, root) if err != nil { return nil, resp, err @@ -845,9 +845,9 @@ func (s *GenAIServiceOp) ListAgents(ctx context.Context, opt *ListOptions) ([]*A return root.Agents, resp, nil } -// CreateAgent creates a new Gen AI Agent by providing the AgentCreateRequest object -func (s *GenAIServiceOp) CreateAgent(ctx context.Context, create *AgentCreateRequest) (*Agent, *Response, error) { - path := genAIBasePath +// CreateAgent creates a new Gradient AI Agent by providing the AgentCreateRequest object +func (s *GradientAIServiceOp) CreateAgent(ctx context.Context, create *AgentCreateRequest) (*Agent, *Response, error) { + path := gradientBasePath if create.ProjectId == "" { return nil, nil, fmt.Errorf("Project ID is required") } @@ -868,7 +868,7 @@ func (s *GenAIServiceOp) CreateAgent(ctx context.Context, create *AgentCreateReq return nil, nil, err } - root := new(genAIAgentRoot) + root := new(gradientAgentRoot) resp, err := s.client.Do(ctx, req, root) if err != nil { @@ -878,9 +878,9 @@ func (s *GenAIServiceOp) CreateAgent(ctx context.Context, create *AgentCreateReq return root.Agent, resp, nil } -// ListAgentAPIKeys retrieves list of API Keys associated with the specified GenAI agent -func (s *GenAIServiceOp) ListAgentAPIKeys(ctx context.Context, agentId string, opt *ListOptions) ([]*ApiKeyInfo, *Response, error) { - path := fmt.Sprintf("%s/%s/api_keys", genAIBasePath, agentId) +// ListAgentAPIKeys retrieves list of API Keys associated with the specified Gradient AI agent +func (s *GradientAIServiceOp) ListAgentAPIKeys(ctx context.Context, agentId string, opt *ListOptions) ([]*ApiKeyInfo, *Response, error) { + path := fmt.Sprintf("%s/%s/api_keys", gradientBasePath, agentId) req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil) if err != nil { return nil, nil, err @@ -902,9 +902,9 @@ func (s *GenAIServiceOp) ListAgentAPIKeys(ctx context.Context, agentId string, o return root.ApiKeys, resp, nil } -// CreateAgentAPIKey creates a new API key for the specified GenAI agent -func (s *GenAIServiceOp) CreateAgentAPIKey(ctx context.Context, agentId string, createRequest *AgentAPIKeyCreateRequest) (*ApiKeyInfo, *Response, error) { - path := fmt.Sprintf("%s/%s/api_keys", genAIBasePath, agentId) +// CreateAgentAPIKey creates a new API key for the specified Gradient AI agent +func (s *GradientAIServiceOp) CreateAgentAPIKey(ctx context.Context, agentId string, createRequest *AgentAPIKeyCreateRequest) (*ApiKeyInfo, *Response, error) { + path := fmt.Sprintf("%s/%s/api_keys", gradientBasePath, agentId) createRequest.AgentUuid = agentId @@ -922,9 +922,9 @@ func (s *GenAIServiceOp) CreateAgentAPIKey(ctx context.Context, agentId string, return root.ApiKey, resp, err } -// UpdateAgentAPIKey updates an existing API key for the specified GenAI agent -func (s *GenAIServiceOp) UpdateAgentAPIKey(ctx context.Context, agentId, apiKeyId string, updateRequest *AgentAPIKeyUpdateRequest) (*ApiKeyInfo, *Response, error) { - path := fmt.Sprintf("%s/%s/api_keys/%s", genAIBasePath, agentId, apiKeyId) +// UpdateAgentAPIKey updates an existing API key for the specified Gradient AI agent +func (s *GradientAIServiceOp) UpdateAgentAPIKey(ctx context.Context, agentId, apiKeyId string, updateRequest *AgentAPIKeyUpdateRequest) (*ApiKeyInfo, *Response, error) { + path := fmt.Sprintf("%s/%s/api_keys/%s", gradientBasePath, agentId, apiKeyId) updateRequest.AgentUuid = agentId @@ -942,9 +942,9 @@ func (s *GenAIServiceOp) UpdateAgentAPIKey(ctx context.Context, agentId, apiKeyI return root.ApiKey, resp, nil } -// DeleteAgentAPIKey deletes an existing API key for the specified GenAI agent -func (s *GenAIServiceOp) DeleteAgentAPIKey(ctx context.Context, agentId, apiKeyId string) (*ApiKeyInfo, *Response, error) { - path := fmt.Sprintf("%s/%s/api_keys/%s", genAIBasePath, agentId, apiKeyId) +// DeleteAgentAPIKey deletes an existing API key for the specified Gradient AI agent +func (s *GradientAIServiceOp) DeleteAgentAPIKey(ctx context.Context, agentId, apiKeyId string) (*ApiKeyInfo, *Response, error) { + path := fmt.Sprintf("%s/%s/api_keys/%s", gradientBasePath, agentId, apiKeyId) req, err := s.client.NewRequest(ctx, http.MethodDelete, path, nil) if err != nil { @@ -960,9 +960,9 @@ func (s *GenAIServiceOp) DeleteAgentAPIKey(ctx context.Context, agentId, apiKeyI return root.ApiKey, resp, nil } -// RegenerateAgentAPIKey regenerates an API key for the specified GenAI agent -func (s *GenAIServiceOp) RegenerateAgentAPIKey(ctx context.Context, agentId, apiKeyId string) (*ApiKeyInfo, *Response, error) { - path := fmt.Sprintf("%s/%s/api_keys/%s/regenerate", genAIBasePath, agentId, apiKeyId) +// RegenerateAgentAPIKey regenerates an API key for the specified Gradient AI agent +func (s *GradientAIServiceOp) RegenerateAgentAPIKey(ctx context.Context, agentId, apiKeyId string) (*ApiKeyInfo, *Response, error) { + path := fmt.Sprintf("%s/%s/api_keys/%s/regenerate", gradientBasePath, agentId, apiKeyId) req, err := s.client.NewRequest(ctx, http.MethodPut, path, nil) if err != nil { @@ -978,16 +978,16 @@ func (s *GenAIServiceOp) RegenerateAgentAPIKey(ctx context.Context, agentId, api return root.ApiKey, resp, nil } -// GetAgent returns the details of a Gen AI Agent based on the Agent UUID -func (s *GenAIServiceOp) GetAgent(ctx context.Context, id string) (*Agent, *Response, error) { - path := fmt.Sprintf("%s/%s", genAIBasePath, id) +// GetAgent returns the details of a Gradient AI Agent based on the Agent UUID +func (s *GradientAIServiceOp) GetAgent(ctx context.Context, id string) (*Agent, *Response, error) { + path := fmt.Sprintf("%s/%s", gradientBasePath, id) req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil) if err != nil { return nil, nil, err } - root := new(genAIAgentRoot) + root := new(gradientAgentRoot) resp, err := s.client.Do(ctx, req, root) if err != nil { return nil, resp, err @@ -996,15 +996,15 @@ func (s *GenAIServiceOp) GetAgent(ctx context.Context, id string) (*Agent, *Resp return root.Agent, resp, nil } -// UpdateAgent function updates a Gen AI Agent properties for the given UUID -func (s *GenAIServiceOp) UpdateAgent(ctx context.Context, id string, update *AgentUpdateRequest) (*Agent, *Response, error) { - path := fmt.Sprintf("%s/%s", genAIBasePath, id) +// UpdateAgent function updates a Gradient AI Agent properties for the given UUID +func (s *GradientAIServiceOp) UpdateAgent(ctx context.Context, id string, update *AgentUpdateRequest) (*Agent, *Response, error) { + path := fmt.Sprintf("%s/%s", gradientBasePath, id) req, err := s.client.NewRequest(ctx, http.MethodPut, path, update) if err != nil { return nil, nil, err } - root := new(genAIAgentRoot) + root := new(gradientAgentRoot) resp, err := s.client.Do(ctx, req, root) if err != nil { return nil, resp, err @@ -1013,15 +1013,15 @@ func (s *GenAIServiceOp) UpdateAgent(ctx context.Context, id string, update *Age return root.Agent, resp, nil } -// DeleteAgent function deletes a Gen AI Agent by its corresponding UUID -func (s *GenAIServiceOp) DeleteAgent(ctx context.Context, id string) (*Agent, *Response, error) { - path := fmt.Sprintf("%s/%s", genAIBasePath, id) +// DeleteAgent function deletes a Gradient AI Agent by its corresponding UUID +func (s *GradientAIServiceOp) DeleteAgent(ctx context.Context, id string) (*Agent, *Response, error) { + path := fmt.Sprintf("%s/%s", gradientBasePath, id) req, err := s.client.NewRequest(ctx, http.MethodDelete, path, nil) if err != nil { return nil, nil, err } - root := new(genAIAgentRoot) + root := new(gradientAgentRoot) resp, err := s.client.Do(ctx, req, root) if err != nil { return nil, resp, err @@ -1030,15 +1030,15 @@ func (s *GenAIServiceOp) DeleteAgent(ctx context.Context, id string) (*Agent, *R return root.Agent, resp, nil } -// UpdateAgentVisibility function updates a Gen AI Agent status by changing visibility to public or private. -func (s *GenAIServiceOp) UpdateAgentVisibility(ctx context.Context, id string, update *AgentVisibilityUpdateRequest) (*Agent, *Response, error) { - path := fmt.Sprintf("%s/%s/deployment_visibility", genAIBasePath, id) +// UpdateAgentVisibility function updates a Gradient AI Agent status by changing visibility to public or private. +func (s *GradientAIServiceOp) UpdateAgentVisibility(ctx context.Context, id string, update *AgentVisibilityUpdateRequest) (*Agent, *Response, error) { + path := fmt.Sprintf("%s/%s/deployment_visibility", gradientBasePath, id) req, err := s.client.NewRequest(ctx, http.MethodPut, path, update) if err != nil { return nil, nil, err } - root := new(genAIAgentRoot) + root := new(gradientAgentRoot) resp, err := s.client.Do(ctx, req, root) if err != nil { return nil, resp, err @@ -1048,7 +1048,7 @@ func (s *GenAIServiceOp) UpdateAgentVisibility(ctx context.Context, id string, u } // List all knowledge bases -func (s *GenAIServiceOp) ListKnowledgeBases(ctx context.Context, opt *ListOptions) ([]KnowledgeBase, *Response, error) { +func (s *GradientAIServiceOp) ListKnowledgeBases(ctx context.Context, opt *ListOptions) ([]KnowledgeBase, *Response, error) { path := KnowledgeBasePath path, err := addOptions(path, opt) @@ -1076,7 +1076,7 @@ func (s *GenAIServiceOp) ListKnowledgeBases(ctx context.Context, opt *ListOption } // ListIndexingJobs returns a list of all indexing jobs for knowledge bases -func (s *GenAIServiceOp) ListIndexingJobs(ctx context.Context, opt *ListOptions) (*IndexingJobsResponse, *Response, error) { +func (s *GradientAIServiceOp) ListIndexingJobs(ctx context.Context, opt *ListOptions) (*IndexingJobsResponse, *Response, error) { path := IndexingJobsPath path, err := addOptions(path, opt) if err != nil { @@ -1110,7 +1110,7 @@ func (s *GenAIServiceOp) ListIndexingJobs(ctx context.Context, opt *ListOptions) } // ListIndexingJobDataSources returns the data sources for a specific indexing job -func (s *GenAIServiceOp) ListIndexingJobDataSources(ctx context.Context, indexingJobUUID string) (*IndexingJobDataSourcesResponse, *Response, error) { +func (s *GradientAIServiceOp) ListIndexingJobDataSources(ctx context.Context, indexingJobUUID string) (*IndexingJobDataSourcesResponse, *Response, error) { path := fmt.Sprintf(IndexingJobDataSourcesPath, indexingJobUUID) req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil) if err != nil { @@ -1127,7 +1127,7 @@ func (s *GenAIServiceOp) ListIndexingJobDataSources(ctx context.Context, indexin } // GetIndexingJob retrieves the status of a specific indexing job for a knowledge base -func (s *GenAIServiceOp) GetIndexingJob(ctx context.Context, indexingJobUUID string) (*IndexingJobResponse, *Response, error) { +func (s *GradientAIServiceOp) GetIndexingJob(ctx context.Context, indexingJobUUID string) (*IndexingJobResponse, *Response, error) { path := fmt.Sprintf(IndexingJobByIDPath, indexingJobUUID) req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil) if err != nil { @@ -1144,7 +1144,7 @@ func (s *GenAIServiceOp) GetIndexingJob(ctx context.Context, indexingJobUUID str } // CancelIndexingJob cancels a specific indexing job for a knowledge base -func (s *GenAIServiceOp) CancelIndexingJob(ctx context.Context, indexingJobUUID string) (*IndexingJobResponse, *Response, error) { +func (s *GradientAIServiceOp) CancelIndexingJob(ctx context.Context, indexingJobUUID string) (*IndexingJobResponse, *Response, error) { path := fmt.Sprintf(IndexingJobCancelPath, indexingJobUUID) // Create the request payload @@ -1167,7 +1167,7 @@ func (s *GenAIServiceOp) CancelIndexingJob(ctx context.Context, indexingJobUUID } // Create a knowledge base -func (s *GenAIServiceOp) CreateKnowledgeBase(ctx context.Context, knowledgeBaseCreate *KnowledgeBaseCreateRequest) (*KnowledgeBase, *Response, error) { +func (s *GradientAIServiceOp) CreateKnowledgeBase(ctx context.Context, knowledgeBaseCreate *KnowledgeBaseCreateRequest) (*KnowledgeBase, *Response, error) { path := KnowledgeBasePath @@ -1209,7 +1209,7 @@ func (s *GenAIServiceOp) CreateKnowledgeBase(ctx context.Context, knowledgeBaseC } // List Data Sources for a Knowledge Base -func (s *GenAIServiceOp) ListKnowledgeBaseDataSources(ctx context.Context, knowledgeBaseID string, opt *ListOptions) ([]KnowledgeBaseDataSource, *Response, error) { +func (s *GradientAIServiceOp) ListKnowledgeBaseDataSources(ctx context.Context, knowledgeBaseID string, opt *ListOptions) ([]KnowledgeBaseDataSource, *Response, error) { path := fmt.Sprintf(KnowledgeBaseDataSourcesPath, knowledgeBaseID) path, err := addOptions(path, opt) @@ -1235,7 +1235,7 @@ func (s *GenAIServiceOp) ListKnowledgeBaseDataSources(ctx context.Context, knowl } // Add Data Source to a Knowledge Base -func (s *GenAIServiceOp) AddKnowledgeBaseDataSource(ctx context.Context, knowledgeBaseID string, addDataSource *AddKnowledgeBaseDataSourceRequest) (*KnowledgeBaseDataSource, *Response, error) { +func (s *GradientAIServiceOp) AddKnowledgeBaseDataSource(ctx context.Context, knowledgeBaseID string, addDataSource *AddKnowledgeBaseDataSourceRequest) (*KnowledgeBaseDataSource, *Response, error) { path := fmt.Sprintf(KnowledgeBaseDataSourcesPath, knowledgeBaseID) req, err := s.client.NewRequest(ctx, http.MethodPost, path, addDataSource) if err != nil { @@ -1250,7 +1250,7 @@ func (s *GenAIServiceOp) AddKnowledgeBaseDataSource(ctx context.Context, knowled } // Deletes data source from a knowledge base -func (s *GenAIServiceOp) DeleteKnowledgeBaseDataSource(ctx context.Context, knowledgeBaseID string, dataSourceID string) (string, string, *Response, error) { +func (s *GradientAIServiceOp) DeleteKnowledgeBaseDataSource(ctx context.Context, knowledgeBaseID string, dataSourceID string) (string, string, *Response, error) { path := fmt.Sprintf(DeleteDataSourcePath, knowledgeBaseID, dataSourceID) req, err := s.client.NewRequest(ctx, http.MethodDelete, path, nil) @@ -1270,7 +1270,7 @@ func (s *GenAIServiceOp) DeleteKnowledgeBaseDataSource(ctx context.Context, know // Get information about a KnowledgeBase and its Database status // Database status can be "CREATING","ONLINE","POWEROFF","REBUILDING","REBALANCING","DECOMMISSIONED","FORKING","MIGRATING","RESIZING","RESTORING","POWERING_ON","UNHEALTHY" -func (s *GenAIServiceOp) GetKnowledgeBase(ctx context.Context, knowledgeBaseID string) (*KnowledgeBase, string, *Response, error) { +func (s *GradientAIServiceOp) GetKnowledgeBase(ctx context.Context, knowledgeBaseID string) (*KnowledgeBase, string, *Response, error) { path := fmt.Sprintf(GetKnowledgeBaseByIDPath, knowledgeBaseID) req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil) @@ -1287,7 +1287,7 @@ func (s *GenAIServiceOp) GetKnowledgeBase(ctx context.Context, knowledgeBaseID s } // Update a knowledge base -func (s *GenAIServiceOp) UpdateKnowledgeBase(ctx context.Context, knowledgeBaseID string, update *UpdateKnowledgeBaseRequest) (*KnowledgeBase, *Response, error) { +func (s *GradientAIServiceOp) UpdateKnowledgeBase(ctx context.Context, knowledgeBaseID string, update *UpdateKnowledgeBaseRequest) (*KnowledgeBase, *Response, error) { path := fmt.Sprintf(UpdateKnowledgeBaseByIDPath, knowledgeBaseID) req, err := s.client.NewRequest(ctx, http.MethodPut, path, update) if err != nil { @@ -1304,7 +1304,7 @@ func (s *GenAIServiceOp) UpdateKnowledgeBase(ctx context.Context, knowledgeBaseI } // Deletes a knowledge base by its corresponding UUID and returns the UUID of the deleted knowledge base -func (s *GenAIServiceOp) DeleteKnowledgeBase(ctx context.Context, knowledgeBaseID string) (string, *Response, error) { +func (s *GradientAIServiceOp) DeleteKnowledgeBase(ctx context.Context, knowledgeBaseID string) (string, *Response, error) { path := fmt.Sprintf(DeleteKnowledgeBaseByIDPath, knowledgeBaseID) req, err := s.client.NewRequest(ctx, http.MethodDelete, path, nil) @@ -1322,7 +1322,7 @@ func (s *GenAIServiceOp) DeleteKnowledgeBase(ctx context.Context, knowledgeBaseI } // Attach a knowledge base to an agent -func (s *GenAIServiceOp) AttachKnowledgeBaseToAgent(ctx context.Context, agentID string, knowledgeBaseID string) (*Agent, *Response, error) { +func (s *GradientAIServiceOp) AttachKnowledgeBaseToAgent(ctx context.Context, agentID string, knowledgeBaseID string) (*Agent, *Response, error) { path := fmt.Sprintf(AgentKnowledgeBasePath, agentID, knowledgeBaseID) req, err := s.client.NewRequest(ctx, http.MethodPost, path, nil) @@ -1330,7 +1330,7 @@ func (s *GenAIServiceOp) AttachKnowledgeBaseToAgent(ctx context.Context, agentID return nil, nil, err } - root := new(genAIAgentKBRoot) + root := new(gradientAgentKBRoot) resp, err := s.client.Do(ctx, req, root) if err != nil { return nil, resp, err @@ -1340,7 +1340,7 @@ func (s *GenAIServiceOp) AttachKnowledgeBaseToAgent(ctx context.Context, agentID } // Detach a knowledge base from an agent -func (s *GenAIServiceOp) DetachKnowledgeBaseToAgent(ctx context.Context, agentID string, knowledgeBaseID string) (*Agent, *Response, error) { +func (s *GradientAIServiceOp) DetachKnowledgeBaseToAgent(ctx context.Context, agentID string, knowledgeBaseID string) (*Agent, *Response, error) { path := fmt.Sprintf(AgentKnowledgeBasePath, agentID, knowledgeBaseID) @@ -1348,7 +1348,7 @@ func (s *GenAIServiceOp) DetachKnowledgeBaseToAgent(ctx context.Context, agentID if err != nil { return nil, nil, err } - root := new(genAIAgentKBRoot) + root := new(gradientAgentKBRoot) resp, err := s.client.Do(ctx, req, root) if err != nil { return nil, resp, err @@ -1357,7 +1357,7 @@ func (s *GenAIServiceOp) DetachKnowledgeBaseToAgent(ctx context.Context, agentID } // AddAgentRoute function adds a route between a parent and child agent. -func (s *GenAIServiceOp) AddAgentRoute(ctx context.Context, parentId string, childId string, route *AgentRouteCreateRequest) (*AgentRouteResponse, *Response, error) { +func (s *GradientAIServiceOp) AddAgentRoute(ctx context.Context, parentId string, childId string, route *AgentRouteCreateRequest) (*AgentRouteResponse, *Response, error) { path := fmt.Sprintf(agentRouteBasePath, parentId, childId) req, err := s.client.NewRequest(ctx, http.MethodPost, path, route) if err != nil { @@ -1374,7 +1374,7 @@ func (s *GenAIServiceOp) AddAgentRoute(ctx context.Context, parentId string, chi } // UpdateAgentRoute function updates a route between a parent and child agent. -func (s *GenAIServiceOp) UpdateAgentRoute(ctx context.Context, parentId string, childId string, route *AgentRouteUpdateRequest) (*AgentRouteResponse, *Response, error) { +func (s *GradientAIServiceOp) UpdateAgentRoute(ctx context.Context, parentId string, childId string, route *AgentRouteUpdateRequest) (*AgentRouteResponse, *Response, error) { path := fmt.Sprintf(agentRouteBasePath, parentId, childId) req, err := s.client.NewRequest(ctx, http.MethodPut, path, route) if err != nil { @@ -1391,7 +1391,7 @@ func (s *GenAIServiceOp) UpdateAgentRoute(ctx context.Context, parentId string, } // DeleteAgentRoute function deletes a route between a parent and child agent. -func (s *GenAIServiceOp) DeleteAgentRoute(ctx context.Context, parentId string, childId string) (*AgentRouteResponse, *Response, error) { +func (s *GradientAIServiceOp) DeleteAgentRoute(ctx context.Context, parentId string, childId string) (*AgentRouteResponse, *Response, error) { path := fmt.Sprintf(agentRouteBasePath, parentId, childId) req, err := s.client.NewRequest(ctx, http.MethodDelete, path, nil) if err != nil { @@ -1407,9 +1407,9 @@ func (s *GenAIServiceOp) DeleteAgentRoute(ctx context.Context, parentId string, return root, resp, nil } -// ListAgentVersions retrieves a list of versions for the specified GenAI agent -func (s *GenAIServiceOp) ListAgentVersions(ctx context.Context, agentId string, opt *ListOptions) ([]*AgentVersion, *Response, error) { - path := fmt.Sprintf("%s/%s/versions", genAIBasePath, agentId) +// ListAgentVersions retrieves a list of versions for the specified GradientAI agent +func (s *GradientAIServiceOp) ListAgentVersions(ctx context.Context, agentId string, opt *ListOptions) ([]*AgentVersion, *Response, error) { + path := fmt.Sprintf("%s/%s/versions", gradientBasePath, agentId) path, err := addOptions(path, opt) if err != nil { return nil, nil, err @@ -1429,8 +1429,8 @@ func (s *GenAIServiceOp) ListAgentVersions(ctx context.Context, agentId string, return root.AgentVersions, resp, nil } -func (s *GenAIServiceOp) RollbackAgentVersion(ctx context.Context, agentId string, versionId string) (string, *Response, error) { - path := fmt.Sprintf("%s/%s/versions", genAIBasePath, agentId) +func (s *GradientAIServiceOp) RollbackAgentVersion(ctx context.Context, agentId string, versionId string) (string, *Response, error) { + path := fmt.Sprintf("%s/%s/versions", gradientBasePath, agentId) req, err := s.client.NewRequest(ctx, http.MethodPut, path, RollbackVersionRequest{ AgentUuid: agentId, VersionHash: versionId, @@ -1449,7 +1449,7 @@ func (s *GenAIServiceOp) RollbackAgentVersion(ctx context.Context, agentId strin } // ListAnthropicAPIKeys retrieves a list of Anthropic API Keys -func (s *GenAIServiceOp) ListAnthropicAPIKeys(ctx context.Context, opt *ListOptions) ([]*AnthropicApiKeyInfo, *Response, error) { +func (s *GradientAIServiceOp) ListAnthropicAPIKeys(ctx context.Context, opt *ListOptions) ([]*AnthropicApiKeyInfo, *Response, error) { path := AnthropicAPIKeysPath path, err := addOptions(path, opt) if err != nil { @@ -1474,7 +1474,7 @@ func (s *GenAIServiceOp) ListAnthropicAPIKeys(ctx context.Context, opt *ListOpti return root.AnthropicApiKeys, resp, nil } -func (s *GenAIServiceOp) CreateAnthropicAPIKey(ctx context.Context, anthropicAPIKeyCreate *AnthropicAPIKeyCreateRequest) (*AnthropicApiKeyInfo, *Response, error) { +func (s *GradientAIServiceOp) CreateAnthropicAPIKey(ctx context.Context, anthropicAPIKeyCreate *AnthropicAPIKeyCreateRequest) (*AnthropicApiKeyInfo, *Response, error) { path := AnthropicAPIKeysPath if anthropicAPIKeyCreate.Name == "" { @@ -1498,7 +1498,7 @@ func (s *GenAIServiceOp) CreateAnthropicAPIKey(ctx context.Context, anthropicAPI return root.AnthropicApiKey, resp, nil } -func (s *GenAIServiceOp) GetAnthropicAPIKey(ctx context.Context, anthropicApiKeyId string) (*AnthropicApiKeyInfo, *Response, error) { +func (s *GradientAIServiceOp) GetAnthropicAPIKey(ctx context.Context, anthropicApiKeyId string) (*AnthropicApiKeyInfo, *Response, error) { path := AnthropicAPIKeysPath + "/" + anthropicApiKeyId req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil) @@ -1515,7 +1515,7 @@ func (s *GenAIServiceOp) GetAnthropicAPIKey(ctx context.Context, anthropicApiKey return root.AnthropicApiKey, resp, nil } -func (s *GenAIServiceOp) UpdateAnthropicAPIKey(ctx context.Context, anthropicApiKeyId string, anthropicAPIKeyUpdate *AnthropicAPIKeyUpdateRequest) (*AnthropicApiKeyInfo, *Response, error) { +func (s *GradientAIServiceOp) UpdateAnthropicAPIKey(ctx context.Context, anthropicApiKeyId string, anthropicAPIKeyUpdate *AnthropicAPIKeyUpdateRequest) (*AnthropicApiKeyInfo, *Response, error) { path := AnthropicAPIKeysPath + "/" + anthropicApiKeyId if anthropicAPIKeyUpdate.ApiKeyUuid == "" { @@ -1539,7 +1539,7 @@ func (s *GenAIServiceOp) UpdateAnthropicAPIKey(ctx context.Context, anthropicApi return root.AnthropicApiKey, resp, nil } -func (s *GenAIServiceOp) DeleteAnthropicAPIKey(ctx context.Context, anthropicApiKeyId string) (*AnthropicApiKeyInfo, *Response, error) { +func (s *GradientAIServiceOp) DeleteAnthropicAPIKey(ctx context.Context, anthropicApiKeyId string) (*AnthropicApiKeyInfo, *Response, error) { path := AnthropicAPIKeysPath + "/" + anthropicApiKeyId req, err := s.client.NewRequest(ctx, http.MethodDelete, path, nil) @@ -1556,7 +1556,7 @@ func (s *GenAIServiceOp) DeleteAnthropicAPIKey(ctx context.Context, anthropicApi return root.AnthropicApiKey, resp, nil } -func (s *GenAIServiceOp) ListAgentsByAnthropicAPIKey(ctx context.Context, anthropicApiKeyId string, opt *ListOptions) ([]*Agent, *Response, error) { +func (s *GradientAIServiceOp) ListAgentsByAnthropicAPIKey(ctx context.Context, anthropicApiKeyId string, opt *ListOptions) ([]*Agent, *Response, error) { path := fmt.Sprintf("%s/%s/agents", AnthropicAPIKeysPath, anthropicApiKeyId) path, err := addOptions(path, opt) if err != nil { @@ -1567,7 +1567,7 @@ func (s *GenAIServiceOp) ListAgentsByAnthropicAPIKey(ctx context.Context, anthro return nil, nil, err } - root := new(genAIAgentsRoot) + root := new(gradientAgentsRoot) resp, err := s.client.Do(ctx, req, root) if err != nil { return nil, resp, err @@ -1581,7 +1581,7 @@ func (s *GenAIServiceOp) ListAgentsByAnthropicAPIKey(ctx context.Context, anthro return root.Agents, resp, nil } -func (s *GenAIServiceOp) ListOpenAIAPIKeys(ctx context.Context, opt *ListOptions) ([]*OpenAiApiKey, *Response, error) { +func (s *GradientAIServiceOp) ListOpenAIAPIKeys(ctx context.Context, opt *ListOptions) ([]*OpenAiApiKey, *Response, error) { path := OpenAIAPIKeysPath path, err := addOptions(path, opt) if err != nil { @@ -1607,7 +1607,7 @@ func (s *GenAIServiceOp) ListOpenAIAPIKeys(ctx context.Context, opt *ListOptions return root.OpenAIApiKeys, resp, nil } -func (s *GenAIServiceOp) CreateOpenAIAPIKey(ctx context.Context, openaiAPIKeyCreate *OpenAIAPIKeyCreateRequest) (*OpenAiApiKey, *Response, error) { +func (s *GradientAIServiceOp) CreateOpenAIAPIKey(ctx context.Context, openaiAPIKeyCreate *OpenAIAPIKeyCreateRequest) (*OpenAiApiKey, *Response, error) { path := OpenAIAPIKeysPath if openaiAPIKeyCreate.Name == "" { @@ -1631,7 +1631,7 @@ func (s *GenAIServiceOp) CreateOpenAIAPIKey(ctx context.Context, openaiAPIKeyCre return root.OpenAIAPIKey, resp, nil } -func (s *GenAIServiceOp) GetOpenAIAPIKey(ctx context.Context, openaiApiKeyId string) (*OpenAiApiKey, *Response, error) { +func (s *GradientAIServiceOp) GetOpenAIAPIKey(ctx context.Context, openaiApiKeyId string) (*OpenAiApiKey, *Response, error) { path := OpenAIAPIKeysPath + "/" + openaiApiKeyId req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil) @@ -1648,7 +1648,7 @@ func (s *GenAIServiceOp) GetOpenAIAPIKey(ctx context.Context, openaiApiKeyId str return root.OpenAIAPIKey, resp, nil } -func (s *GenAIServiceOp) UpdateOpenAIAPIKey(ctx context.Context, openaiApiKeyId string, openaiAPIKeyUpdate *OpenAIAPIKeyUpdateRequest) (*OpenAiApiKey, *Response, error) { +func (s *GradientAIServiceOp) UpdateOpenAIAPIKey(ctx context.Context, openaiApiKeyId string, openaiAPIKeyUpdate *OpenAIAPIKeyUpdateRequest) (*OpenAiApiKey, *Response, error) { path := OpenAIAPIKeysPath + "/" + openaiApiKeyId if openaiAPIKeyUpdate.ApiKeyUuid == "" { @@ -1672,7 +1672,7 @@ func (s *GenAIServiceOp) UpdateOpenAIAPIKey(ctx context.Context, openaiApiKeyId return root.OpenAIAPIKey, resp, nil } -func (s *GenAIServiceOp) DeleteOpenAIAPIKey(ctx context.Context, openaiApiKeyId string) (*OpenAiApiKey, *Response, error) { +func (s *GradientAIServiceOp) DeleteOpenAIAPIKey(ctx context.Context, openaiApiKeyId string) (*OpenAiApiKey, *Response, error) { path := OpenAIAPIKeysPath + "/" + openaiApiKeyId req, err := s.client.NewRequest(ctx, http.MethodDelete, path, nil) @@ -1689,7 +1689,7 @@ func (s *GenAIServiceOp) DeleteOpenAIAPIKey(ctx context.Context, openaiApiKeyId return root.OpenAIAPIKey, resp, nil } -func (s *GenAIServiceOp) ListAgentsByOpenAIAPIKey(ctx context.Context, openaiApiKeyId string, opt *ListOptions) ([]*Agent, *Response, error) { +func (s *GradientAIServiceOp) ListAgentsByOpenAIAPIKey(ctx context.Context, openaiApiKeyId string, opt *ListOptions) ([]*Agent, *Response, error) { path := fmt.Sprintf("%s/%s/agents", OpenAIAPIKeysPath, openaiApiKeyId) path, err := addOptions(path, opt) if err != nil { @@ -1700,7 +1700,7 @@ func (s *GenAIServiceOp) ListAgentsByOpenAIAPIKey(ctx context.Context, openaiApi return nil, nil, err } - root := new(genAIAgentsRoot) + root := new(gradientAgentsRoot) resp, err := s.client.Do(ctx, req, root) if err != nil { return nil, resp, err @@ -1715,7 +1715,7 @@ func (s *GenAIServiceOp) ListAgentsByOpenAIAPIKey(ctx context.Context, openaiApi } // Attaches a functionroute to an agent. -func (g *GenAIServiceOp) CreateFunctionRoute(ctx context.Context, id string, create *FunctionRouteCreateRequest) (*Agent, *Response, error) { +func (g *GradientAIServiceOp) CreateFunctionRoute(ctx context.Context, id string, create *FunctionRouteCreateRequest) (*Agent, *Response, error) { path := fmt.Sprintf(functionRouteBasePath, id) if create.AgentUuid == "" { @@ -1742,7 +1742,7 @@ func (g *GenAIServiceOp) CreateFunctionRoute(ctx context.Context, id string, cre return nil, nil, err } - root := new(genAIAgentRoot) + root := new(gradientAgentRoot) resp, err := g.client.Do(ctx, req, root) if err != nil { @@ -1753,14 +1753,14 @@ func (g *GenAIServiceOp) CreateFunctionRoute(ctx context.Context, id string, cre } // Deletes a functionroute to an agent. -func (g *GenAIServiceOp) DeleteFunctionRoute(ctx context.Context, agent_id string, function_id string) (*Agent, *Response, error) { +func (g *GradientAIServiceOp) DeleteFunctionRoute(ctx context.Context, agent_id string, function_id string) (*Agent, *Response, error) { path := fmt.Sprintf(UpdateFunctionRoutePath, agent_id, function_id) req, err := g.client.NewRequest(ctx, http.MethodDelete, path, nil) if err != nil { return nil, nil, err } - root := new(genAIAgentRoot) + root := new(gradientAgentRoot) resp, err := g.client.Do(ctx, req, root) if err != nil { @@ -1771,14 +1771,14 @@ func (g *GenAIServiceOp) DeleteFunctionRoute(ctx context.Context, agent_id strin } // Updates a functionroute to an agent. -func (g *GenAIServiceOp) UpdateFunctionRoute(ctx context.Context, agent_id string, function_id string, update *FunctionRouteUpdateRequest) (*Agent, *Response, error) { +func (g *GradientAIServiceOp) UpdateFunctionRoute(ctx context.Context, agent_id string, function_id string, update *FunctionRouteUpdateRequest) (*Agent, *Response, error) { path := fmt.Sprintf(UpdateFunctionRoutePath, agent_id, function_id) req, err := g.client.NewRequest(ctx, http.MethodPut, path, update) if err != nil { return nil, nil, err } - root := new(genAIAgentRoot) + root := new(gradientAgentRoot) resp, err := g.client.Do(ctx, req, root) if err != nil { @@ -1788,8 +1788,8 @@ func (g *GenAIServiceOp) UpdateFunctionRoute(ctx context.Context, agent_id strin return root.Agent, resp, nil } -// ListAvailableModels returns a list of available Gen AI models -func (g *GenAIServiceOp) ListAvailableModels(ctx context.Context, opt *ListOptions) ([]*Model, *Response, error) { +// ListAvailableModels returns a list of available Gradient AI models +func (g *GradientAIServiceOp) ListAvailableModels(ctx context.Context, opt *ListOptions) ([]*Model, *Response, error) { path, err := addOptions(agentModelBasePath, opt) if err != nil { return nil, nil, err @@ -1800,7 +1800,7 @@ func (g *GenAIServiceOp) ListAvailableModels(ctx context.Context, opt *ListOptio return nil, nil, err } - root := new(genAIModelsRoot) + root := new(gradientModelsRoot) resp, err := g.client.Do(ctx, req, root) if err != nil { return nil, resp, err @@ -1812,8 +1812,8 @@ func (g *GenAIServiceOp) ListAvailableModels(ctx context.Context, opt *ListOptio return root.Models, resp, nil } -// ListDatacenterRegions returns a list of available datacenter regions for Gen AI services -func (g *GenAIServiceOp) ListDatacenterRegions(ctx context.Context, servesInference, servesBatch *bool) ([]*DatacenterRegions, *Response, error) { +// ListDatacenterRegions returns a list of available datacenter regions for Gradient AI services +func (g *GradientAIServiceOp) ListDatacenterRegions(ctx context.Context, servesInference, servesBatch *bool) ([]*DatacenterRegions, *Response, error) { path := datacenterRegionsPath var params []string diff --git a/vendor/github.com/digitalocean/godo/image_actions.go b/vendor/github.com/digitalocean/godo/image_actions.go index 2ee508c7a..5a79e914d 100644 --- a/vendor/github.com/digitalocean/godo/image_actions.go +++ b/vendor/github.com/digitalocean/godo/image_actions.go @@ -54,7 +54,7 @@ func (i *ImageActionsServiceOp) Transfer(ctx context.Context, imageID int, trans // Convert an image to a snapshot func (i *ImageActionsServiceOp) Convert(ctx context.Context, imageID int) (*Action, *Response, error) { if imageID < 1 { - return nil, nil, NewArgError("imageID", "cannont be less than 1") + return nil, nil, NewArgError("imageID", "cannot be less than 1") } path := fmt.Sprintf("v2/images/%d/actions", imageID) diff --git a/vendor/github.com/digitalocean/godo/nfs.go b/vendor/github.com/digitalocean/godo/nfs.go index 1ef6aae92..0bd2d4b40 100644 --- a/vendor/github.com/digitalocean/godo/nfs.go +++ b/vendor/github.com/digitalocean/godo/nfs.go @@ -166,9 +166,6 @@ func (s *NfsServiceOp) Get(ctx context.Context, nfsShareId string, region string if nfsShareId == "" { return nil, nil, NewArgError("id", "cannot be empty") } - if region == "" { - return nil, nil, NewArgError("region", "cannot be empty") - } path := fmt.Sprintf("%s/%s", nfsBasePath, nfsShareId) @@ -194,10 +191,6 @@ func (s *NfsServiceOp) Get(ctx context.Context, nfsShareId string, region string // List returns a list of NFS shares. func (s *NfsServiceOp) List(ctx context.Context, opts *ListOptions, region string) ([]*Nfs, *Response, error) { - if region == "" { - return nil, nil, NewArgError("region", "cannot be empty") - } - path, err := addOptions(nfsBasePath, opts) if err != nil { return nil, nil, err @@ -235,10 +228,6 @@ func (s *NfsServiceOp) Delete(ctx context.Context, nfsShareId string, region str if nfsShareId == "" { return nil, NewArgError("id", "cannot be empty") } - if region == "" { - return nil, NewArgError("region", "cannot be empty") - } - path := fmt.Sprintf("%s/%s", nfsBasePath, nfsShareId) deleteOpts := &nfsOptions{Region: region} @@ -265,9 +254,6 @@ func (s *NfsServiceOp) GetSnapshot(ctx context.Context, nfsSnapshotID string, re if nfsSnapshotID == "" { return nil, nil, NewArgError("snapshotID", "cannot be empty") } - if region == "" { - return nil, nil, NewArgError("region", "cannot be empty") - } path := fmt.Sprintf("%s/%s", nfsSnapshotsBasePath, nfsSnapshotID) @@ -293,9 +279,6 @@ func (s *NfsServiceOp) GetSnapshot(ctx context.Context, nfsSnapshotID string, re // List returns a list of NFS snapshots. func (s *NfsServiceOp) ListSnapshots(ctx context.Context, opts *ListOptions, nfsShareId, region string) ([]*NfsSnapshot, *Response, error) { - if region == "" { - return nil, nil, NewArgError("region", "cannot be empty") - } path, err := addOptions(nfsSnapshotsBasePath, opts) if err != nil { @@ -334,10 +317,6 @@ func (s *NfsServiceOp) DeleteSnapshot(ctx context.Context, nfsSnapshotID string, if nfsSnapshotID == "" { return nil, NewArgError("snapshotID", "cannot be empty") } - if region == "" { - return nil, NewArgError("region", "cannot be empty") - } - path := fmt.Sprintf("%s/%s", nfsSnapshotsBasePath, nfsSnapshotID) deleteOpts := &nfsOptions{Region: region} diff --git a/vendor/github.com/digitalocean/godo/nfs_actions.go b/vendor/github.com/digitalocean/godo/nfs_actions.go index 7c68cca8e..0618d9fbd 100644 --- a/vendor/github.com/digitalocean/godo/nfs_actions.go +++ b/vendor/github.com/digitalocean/godo/nfs_actions.go @@ -72,8 +72,7 @@ type NfsDetachParams struct { // Resize an NFS share func (s *NfsActionsServiceOp) Resize(ctx context.Context, nfsShareId string, size uint64, region string) (*NfsAction, *Response, error) { request := &NfsActionRequest{ - Type: "resize", - Region: region, + Type: "resize", Params: &NfsResizeParams{ SizeGib: size, }, @@ -85,8 +84,7 @@ func (s *NfsActionsServiceOp) Resize(ctx context.Context, nfsShareId string, siz // Snapshot an NFS share func (s *NfsActionsServiceOp) Snapshot(ctx context.Context, nfsShareId, nfsSnapshotName, region string) (*NfsAction, *Response, error) { request := &NfsActionRequest{ - Type: "snapshot", - Region: region, + Type: "snapshot", Params: &NfsSnapshotParams{ Name: nfsSnapshotName, }, @@ -98,8 +96,7 @@ func (s *NfsActionsServiceOp) Snapshot(ctx context.Context, nfsShareId, nfsSnaps // Attach an NFS share func (s *NfsActionsServiceOp) Attach(ctx context.Context, nfsShareId, vpcID, region string) (*NfsAction, *Response, error) { request := &NfsActionRequest{ - Type: "attach", - Region: region, + Type: "attach", Params: &NfsAttachParams{ VpcID: vpcID, }, @@ -111,8 +108,7 @@ func (s *NfsActionsServiceOp) Attach(ctx context.Context, nfsShareId, vpcID, reg // Detach an NFS share func (s *NfsActionsServiceOp) Detach(ctx context.Context, nfsShareId, vpcID, region string) (*NfsAction, *Response, error) { request := &NfsActionRequest{ - Type: "detach", - Region: region, + Type: "detach", Params: &NfsAttachParams{ VpcID: vpcID, }, diff --git a/vendor/github.com/go-viper/mapstructure/v2/.editorconfig b/vendor/github.com/go-viper/mapstructure/v2/.editorconfig index faef0c91e..c37602a02 100644 --- a/vendor/github.com/go-viper/mapstructure/v2/.editorconfig +++ b/vendor/github.com/go-viper/mapstructure/v2/.editorconfig @@ -19,3 +19,6 @@ indent_size = 2 [.golangci.yaml] indent_size = 2 + +[devenv.yaml] +indent_size = 2 diff --git a/vendor/github.com/go-viper/mapstructure/v2/.envrc b/vendor/github.com/go-viper/mapstructure/v2/.envrc index 2e0f9f5f7..e2be8891c 100644 --- a/vendor/github.com/go-viper/mapstructure/v2/.envrc +++ b/vendor/github.com/go-viper/mapstructure/v2/.envrc @@ -1,4 +1,7 @@ -if ! has nix_direnv_version || ! nix_direnv_version 3.0.4; then - source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.4/direnvrc" "sha256-DzlYZ33mWF/Gs8DDeyjr8mnVmQGx7ASYqA5WlxwvBG4=" -fi -use flake . --impure +#!/usr/bin/env bash + +export DIRENV_WARN_TIMEOUT=20s + +eval "$(devenv direnvrc)" + +use devenv diff --git a/vendor/github.com/go-viper/mapstructure/v2/.gitignore b/vendor/github.com/go-viper/mapstructure/v2/.gitignore index 470e7ca2b..71caea19f 100644 --- a/vendor/github.com/go-viper/mapstructure/v2/.gitignore +++ b/vendor/github.com/go-viper/mapstructure/v2/.gitignore @@ -1,6 +1,10 @@ -/.devenv/ -/.direnv/ -/.pre-commit-config.yaml /bin/ /build/ /var/ + +# Devenv +.devenv* +devenv.local.nix +devenv.local.yaml +.direnv +.pre-commit-config.yaml diff --git a/vendor/github.com/go-viper/mapstructure/v2/devenv.lock b/vendor/github.com/go-viper/mapstructure/v2/devenv.lock new file mode 100644 index 000000000..72c2c9b4d --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/devenv.lock @@ -0,0 +1,103 @@ +{ + "nodes": { + "devenv": { + "locked": { + "dir": "src/modules", + "lastModified": 1765288076, + "owner": "cachix", + "repo": "devenv", + "rev": "93c055af1e8fcac49251f1b2e1c57f78620ad351", + "type": "github" + }, + "original": { + "dir": "src/modules", + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1765121682, + "owner": "edolstra", + "repo": "flake-compat", + "rev": "65f23138d8d09a92e30f1e5c87611b23ef451bf3", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "git-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1765016596, + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "548fc44fca28a5e81c5d6b846e555e6b9c2a5a3c", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "git-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1762808025, + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "cb5e3fdca1de58ccbc3ef53de65bd372b48f567c", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1764580874, + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "dcf61356c3ab25f1362b4a4428a6d871e84f1d1d", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "git-hooks": "git-hooks", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": [ + "git-hooks" + ] + } + } + }, + "root": "root", + "version": 7 +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/devenv.nix b/vendor/github.com/go-viper/mapstructure/v2/devenv.nix new file mode 100644 index 000000000..b31ab7a1f --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/devenv.nix @@ -0,0 +1,14 @@ +{ + pkgs, + ... +}: + +{ + languages = { + go.enable = true; + }; + + packages = with pkgs; [ + golangci-lint + ]; +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/devenv.yaml b/vendor/github.com/go-viper/mapstructure/v2/devenv.yaml new file mode 100644 index 000000000..68616a49c --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/devenv.yaml @@ -0,0 +1,4 @@ +# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json +inputs: + nixpkgs: + url: github:cachix/devenv-nixpkgs/rolling diff --git a/vendor/github.com/go-viper/mapstructure/v2/flake.lock b/vendor/github.com/go-viper/mapstructure/v2/flake.lock deleted file mode 100644 index 5e67bdd6b..000000000 --- a/vendor/github.com/go-viper/mapstructure/v2/flake.lock +++ /dev/null @@ -1,294 +0,0 @@ -{ - "nodes": { - "cachix": { - "inputs": { - "devenv": [ - "devenv" - ], - "flake-compat": [ - "devenv" - ], - "git-hooks": [ - "devenv" - ], - "nixpkgs": "nixpkgs" - }, - "locked": { - "lastModified": 1742042642, - "narHash": "sha256-D0gP8srrX0qj+wNYNPdtVJsQuFzIng3q43thnHXQ/es=", - "owner": "cachix", - "repo": "cachix", - "rev": "a624d3eaf4b1d225f918de8543ed739f2f574203", - "type": "github" - }, - "original": { - "owner": "cachix", - "ref": "latest", - "repo": "cachix", - "type": "github" - } - }, - "devenv": { - "inputs": { - "cachix": "cachix", - "flake-compat": "flake-compat", - "git-hooks": "git-hooks", - "nix": "nix", - "nixpkgs": "nixpkgs_3" - }, - "locked": { - "lastModified": 1744876578, - "narHash": "sha256-8MTBj2REB8t29sIBLpxbR0+AEGJ7f+RkzZPAGsFd40c=", - "owner": "cachix", - "repo": "devenv", - "rev": "7ff7c351bba20d0615be25ecdcbcf79b57b85fe1", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "devenv", - "type": "github" - } - }, - "flake-compat": { - "flake": false, - "locked": { - "lastModified": 1733328505, - "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "flake-parts": { - "inputs": { - "nixpkgs-lib": [ - "devenv", - "nix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1712014858, - "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "flake-parts_2": { - "inputs": { - "nixpkgs-lib": "nixpkgs-lib" - }, - "locked": { - "lastModified": 1743550720, - "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "c621e8422220273271f52058f618c94e405bb0f5", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "git-hooks": { - "inputs": { - "flake-compat": [ - "devenv" - ], - "gitignore": "gitignore", - "nixpkgs": [ - "devenv", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1742649964, - "narHash": "sha256-DwOTp7nvfi8mRfuL1escHDXabVXFGT1VlPD1JHrtrco=", - "owner": "cachix", - "repo": "git-hooks.nix", - "rev": "dcf5072734cb576d2b0c59b2ac44f5050b5eac82", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "git-hooks.nix", - "type": "github" - } - }, - "gitignore": { - "inputs": { - "nixpkgs": [ - "devenv", - "git-hooks", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1709087332, - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", - "type": "github" - } - }, - "libgit2": { - "flake": false, - "locked": { - "lastModified": 1697646580, - "narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=", - "owner": "libgit2", - "repo": "libgit2", - "rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5", - "type": "github" - }, - "original": { - "owner": "libgit2", - "repo": "libgit2", - "type": "github" - } - }, - "nix": { - "inputs": { - "flake-compat": [ - "devenv" - ], - "flake-parts": "flake-parts", - "libgit2": "libgit2", - "nixpkgs": "nixpkgs_2", - "nixpkgs-23-11": [ - "devenv" - ], - "nixpkgs-regression": [ - "devenv" - ], - "pre-commit-hooks": [ - "devenv" - ] - }, - "locked": { - "lastModified": 1741798497, - "narHash": "sha256-E3j+3MoY8Y96mG1dUIiLFm2tZmNbRvSiyN7CrSKuAVg=", - "owner": "domenkozar", - "repo": "nix", - "rev": "f3f44b2baaf6c4c6e179de8cbb1cc6db031083cd", - "type": "github" - }, - "original": { - "owner": "domenkozar", - "ref": "devenv-2.24", - "repo": "nix", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1733212471, - "narHash": "sha256-M1+uCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "55d15ad12a74eb7d4646254e13638ad0c4128776", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-lib": { - "locked": { - "lastModified": 1743296961, - "narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=", - "owner": "nix-community", - "repo": "nixpkgs.lib", - "rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nixpkgs.lib", - "type": "github" - } - }, - "nixpkgs_2": { - "locked": { - "lastModified": 1717432640, - "narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "88269ab3044128b7c2f4c7d68448b2fb50456870", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "release-24.05", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_3": { - "locked": { - "lastModified": 1733477122, - "narHash": "sha256-qamMCz5mNpQmgBwc8SB5tVMlD5sbwVIToVZtSxMph9s=", - "owner": "cachix", - "repo": "devenv-nixpkgs", - "rev": "7bd9e84d0452f6d2e63b6e6da29fe73fac951857", - "type": "github" - }, - "original": { - "owner": "cachix", - "ref": "rolling", - "repo": "devenv-nixpkgs", - "type": "github" - } - }, - "nixpkgs_4": { - "locked": { - "lastModified": 1744536153, - "narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "devenv": "devenv", - "flake-parts": "flake-parts_2", - "nixpkgs": "nixpkgs_4" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/vendor/github.com/go-viper/mapstructure/v2/flake.nix b/vendor/github.com/go-viper/mapstructure/v2/flake.nix deleted file mode 100644 index 3b116f426..000000000 --- a/vendor/github.com/go-viper/mapstructure/v2/flake.nix +++ /dev/null @@ -1,46 +0,0 @@ -{ - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; - flake-parts.url = "github:hercules-ci/flake-parts"; - devenv.url = "github:cachix/devenv"; - }; - - outputs = - inputs@{ flake-parts, ... }: - flake-parts.lib.mkFlake { inherit inputs; } { - imports = [ - inputs.devenv.flakeModule - ]; - - systems = [ - "x86_64-linux" - "x86_64-darwin" - "aarch64-darwin" - ]; - - perSystem = - { pkgs, ... }: - rec { - devenv.shells = { - default = { - languages = { - go.enable = true; - }; - - pre-commit.hooks = { - nixpkgs-fmt.enable = true; - }; - - packages = with pkgs; [ - golangci-lint - ]; - - # https://github.com/cachix/devenv/issues/528#issuecomment-1556108767 - containers = pkgs.lib.mkForce { }; - }; - - ci = devenv.shells.default; - }; - }; - }; -} diff --git a/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go b/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go index 7c35bce02..9087fd96c 100644 --- a/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go +++ b/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go @@ -173,6 +173,25 @@ // Public: "I made it through!" // } // +// # Custom Decoding with Unmarshaler +// +// Types can implement the Unmarshaler interface to control their own decoding. The interface +// behaves similarly to how UnmarshalJSON does in the standard library. It can be used as an +// alternative or companion to a DecodeHook. +// +// type TrimmedString string +// +// func (t *TrimmedString) UnmarshalMapstructure(input any) error { +// str, ok := input.(string) +// if !ok { +// return fmt.Errorf("expected string, got %T", input) +// } +// *t = TrimmedString(strings.TrimSpace(str)) +// return nil +// } +// +// See the Unmarshaler interface documentation for more details. +// // # Other Configuration // // mapstructure is highly configurable. See the DecoderConfig struct @@ -218,6 +237,17 @@ type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, any) (any, error) // values. type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (any, error) +// Unmarshaler is the interface implemented by types that can unmarshal +// themselves. UnmarshalMapstructure receives the input data (potentially +// transformed by DecodeHook) and should populate the receiver with the +// decoded values. +// +// The Unmarshaler interface takes precedence over the default decoding +// logic for any type (structs, slices, maps, primitives, etc.). +type Unmarshaler interface { + UnmarshalMapstructure(any) error +} + // DecoderConfig is the configuration that is used to create a new decoder // and allows customization of various aspects of decoding. type DecoderConfig struct { @@ -281,6 +311,13 @@ type DecoderConfig struct { // } Squash bool + // Deep will map structures in slices instead of copying them + // + // type Parent struct { + // Children []Child `mapstructure:",deep"` + // } + Deep bool + // Metadata is the struct that will contain extra metadata about // the decoding. If this is nil, then no metadata will be tracked. Metadata *Metadata @@ -290,9 +327,15 @@ type DecoderConfig struct { Result any // The tag name that mapstructure reads for field names. This - // defaults to "mapstructure" + // defaults to "mapstructure". Multiple tag names can be specified + // as a comma-separated list (e.g., "yaml,json"), and the first + // matching non-empty tag will be used. TagName string + // RootName specifies the name to use for the root element in error messages. For example: + // '' has unset fields: + RootName string + // The option of the value in the tag that indicates a field should // be squashed. This defaults to "squash". SquashTagOption string @@ -304,11 +347,34 @@ type DecoderConfig struct { // MatchName is the function used to match the map key to the struct // field name or tag. Defaults to `strings.EqualFold`. This can be used // to implement case-sensitive tag values, support snake casing, etc. + // + // MatchName is used as a fallback comparison when the direct key lookup fails. + // See also MapFieldName for transforming field names before lookup. MatchName func(mapKey, fieldName string) bool // DecodeNil, if set to true, will cause the DecodeHook (if present) to run // even if the input is nil. This can be used to provide default values. DecodeNil bool + + // MapFieldName is the function used to convert the struct field name to the map's key name. + // + // This is useful for automatically converting between naming conventions without + // explicitly tagging each field. For example, to convert Go's PascalCase field names + // to snake_case map keys: + // + // MapFieldName: func(s string) string { + // return strcase.ToSnake(s) + // } + // + // When decoding from a map to a struct, the transformed field name is used for + // the initial lookup. If not found, MatchName is used as a fallback comparison. + // Explicit struct tags always take precedence over MapFieldName. + MapFieldName func(string) string + + // DisableUnmarshaler, if set to true, disables the use of the Unmarshaler + // interface. Types implementing Unmarshaler will be decoded using the + // standard struct decoding logic instead. + DisableUnmarshaler bool } // A Decoder takes a raw interface value and turns it into structured @@ -445,6 +511,12 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) { config.MatchName = strings.EqualFold } + if config.MapFieldName == nil { + config.MapFieldName = func(s string) string { + return s + } + } + result := &Decoder{ config: config, } @@ -458,7 +530,7 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) { // Decode decodes the given raw interface to the target pointer specified // by the configuration. func (d *Decoder) Decode(input any) error { - err := d.decode("", input, reflect.ValueOf(d.config.Result).Elem()) + err := d.decode(d.config.RootName, input, reflect.ValueOf(d.config.Result).Elem()) // Retain some of the original behavior when multiple errors ocurr var joinedErr interface{ Unwrap() []error } @@ -540,36 +612,50 @@ func (d *Decoder) decode(name string, input any, outVal reflect.Value) error { var err error addMetaKey := true - switch outputKind { - case reflect.Bool: - err = d.decodeBool(name, input, outVal) - case reflect.Interface: - err = d.decodeBasic(name, input, outVal) - case reflect.String: - err = d.decodeString(name, input, outVal) - case reflect.Int: - err = d.decodeInt(name, input, outVal) - case reflect.Uint: - err = d.decodeUint(name, input, outVal) - case reflect.Float32: - err = d.decodeFloat(name, input, outVal) - case reflect.Complex64: - err = d.decodeComplex(name, input, outVal) - case reflect.Struct: - err = d.decodeStruct(name, input, outVal) - case reflect.Map: - err = d.decodeMap(name, input, outVal) - case reflect.Ptr: - addMetaKey, err = d.decodePtr(name, input, outVal) - case reflect.Slice: - err = d.decodeSlice(name, input, outVal) - case reflect.Array: - err = d.decodeArray(name, input, outVal) - case reflect.Func: - err = d.decodeFunc(name, input, outVal) - default: - // If we reached this point then we weren't able to decode it - return newDecodeError(name, fmt.Errorf("unsupported type: %s", outputKind)) + + // Check if the target implements Unmarshaler and use it if not disabled + unmarshaled := false + if !d.config.DisableUnmarshaler { + if unmarshaler, ok := getUnmarshaler(outVal); ok { + if err = unmarshaler.UnmarshalMapstructure(input); err != nil { + err = newDecodeError(name, err) + } + unmarshaled = true + } + } + + if !unmarshaled { + switch outputKind { + case reflect.Bool: + err = d.decodeBool(name, input, outVal) + case reflect.Interface: + err = d.decodeBasic(name, input, outVal) + case reflect.String: + err = d.decodeString(name, input, outVal) + case reflect.Int: + err = d.decodeInt(name, input, outVal) + case reflect.Uint: + err = d.decodeUint(name, input, outVal) + case reflect.Float32: + err = d.decodeFloat(name, input, outVal) + case reflect.Complex64: + err = d.decodeComplex(name, input, outVal) + case reflect.Struct: + err = d.decodeStruct(name, input, outVal) + case reflect.Map: + err = d.decodeMap(name, input, outVal) + case reflect.Ptr: + addMetaKey, err = d.decodePtr(name, input, outVal) + case reflect.Slice: + err = d.decodeSlice(name, input, outVal) + case reflect.Array: + err = d.decodeArray(name, input, outVal) + case reflect.Func: + err = d.decodeFunc(name, input, outVal) + default: + // If we reached this point then we weren't able to decode it + return newDecodeError(name, fmt.Errorf("unsupported type: %s", outputKind)) + } } // If we reached here, then we successfully decoded SOMETHING, so @@ -668,7 +754,7 @@ func (d *Decoder) decodeString(name string, data any, val reflect.Value) error { case reflect.Uint8: var uints []uint8 if dataKind == reflect.Array { - uints = make([]uint8, dataVal.Len(), dataVal.Len()) + uints = make([]uint8, dataVal.Len()) for i := range uints { uints[i] = dataVal.Index(i).Interface().(uint8) } @@ -1060,8 +1146,8 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re ) } - tagValue := f.Tag.Get(d.config.TagName) - keyName := f.Name + tagValue, _ := getTagValue(f, d.config.TagName) + keyName := d.config.MapFieldName(f.Name) if tagValue == "" && d.config.IgnoreUntaggedFields { continue @@ -1070,6 +1156,9 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re // If Squash is set in the config, we squash the field down. squash := d.config.Squash && v.Kind() == reflect.Struct && f.Anonymous + // If Deep is set in the config, set as default value. + deep := d.config.Deep + v = dereferencePtrToStructIfNeeded(v, d.config.TagName) // Determine the name of the key in the map @@ -1078,12 +1167,12 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re continue } // If "omitempty" is specified in the tag, it ignores empty values. - if strings.Index(tagValue[index+1:], "omitempty") != -1 && isEmptyValue(v) { + if strings.Contains(tagValue[index+1:], "omitempty") && isEmptyValue(v) { continue } // If "omitzero" is specified in the tag, it ignores zero values. - if strings.Index(tagValue[index+1:], "omitzero") != -1 && v.IsZero() { + if strings.Contains(tagValue[index+1:], "omitzero") && v.IsZero() { continue } @@ -1103,7 +1192,7 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re ) } } else { - if strings.Index(tagValue[index+1:], "remain") != -1 { + if strings.Contains(tagValue[index+1:], "remain") { if v.Kind() != reflect.Map { return newDecodeError( name+"."+f.Name, @@ -1118,6 +1207,9 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re continue } } + + deep = deep || strings.Contains(tagValue[index+1:], "deep") + if keyNameTagValue := tagValue[:index]; keyNameTagValue != "" { keyName = keyNameTagValue } @@ -1164,6 +1256,41 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re valMap.SetMapIndex(reflect.ValueOf(keyName), vMap) } + case reflect.Slice: + if deep { + var childType reflect.Type + switch v.Type().Elem().Kind() { + case reflect.Struct: + childType = reflect.TypeOf(map[string]any{}) + default: + childType = v.Type().Elem() + } + + sType := reflect.SliceOf(childType) + + addrVal := reflect.New(sType) + + vSlice := reflect.MakeSlice(sType, v.Len(), v.Cap()) + + if v.Len() > 0 { + reflect.Indirect(addrVal).Set(vSlice) + + err := d.decode(keyName, v.Interface(), reflect.Indirect(addrVal)) + if err != nil { + return err + } + } + + vSlice = reflect.Indirect(addrVal) + + valMap.SetMapIndex(reflect.ValueOf(keyName), vSlice) + + break + } + + // When deep mapping is not needed, fallthrough to normal copy + fallthrough + default: valMap.SetMapIndex(reflect.ValueOf(keyName), v) } @@ -1471,7 +1598,10 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e remain := false // We always parse the tags cause we're looking for other tags too - tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",") + tagParts := getTagParts(fieldType, d.config.TagName) + if len(tagParts) == 0 { + tagParts = []string{""} + } for _, tag := range tagParts[1:] { if tag == d.config.SquashTagOption { squash = true @@ -1492,6 +1622,18 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e if !fieldVal.IsNil() { structs = append(structs, fieldVal.Elem().Elem()) } + case reflect.Ptr: + if fieldVal.Type().Elem().Kind() == reflect.Struct { + if fieldVal.IsNil() { + fieldVal.Set(reflect.New(fieldVal.Type().Elem())) + } + structs = append(structs, fieldVal.Elem()) + } else { + errs = append(errs, newDecodeError( + name+"."+fieldType.Name, + fmt.Errorf("unsupported type for squashed pointer: %s", fieldVal.Type().Elem().Kind()), + )) + } default: errs = append(errs, newDecodeError( name+"."+fieldType.Name, @@ -1516,13 +1658,15 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e field, fieldValue := f.field, f.val fieldName := field.Name - tagValue := field.Tag.Get(d.config.TagName) + tagValue, _ := getTagValue(field, d.config.TagName) if tagValue == "" && d.config.IgnoreUntaggedFields { continue } tagValue = strings.SplitN(tagValue, ",", 2)[0] if tagValue != "" { fieldName = tagValue + } else { + fieldName = d.config.MapFieldName(fieldName) } rawMapKey := reflect.ValueOf(fieldName) @@ -1605,8 +1749,14 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e } sort.Strings(keys) + // Improve error message when name is empty by showing the target struct type + // in the case where it is empty for embedded structs. + errorName := name + if errorName == "" { + errorName = val.Type().String() + } errs = append(errs, newDecodeError( - name, + errorName, fmt.Errorf("has invalid keys: %s", strings.Join(keys, ", ")), )) } @@ -1692,7 +1842,7 @@ func isStructTypeConvertibleToMap(typ reflect.Type, checkMapstructureTags bool, if f.PkgPath == "" && !checkMapstructureTags { // check for unexported fields return true } - if checkMapstructureTags && f.Tag.Get(tagName) != "" { // check for mapstructure tags inside + if checkMapstructureTags && hasAnyTag(f, tagName) { // check for mapstructure tags inside return true } } @@ -1700,13 +1850,99 @@ func isStructTypeConvertibleToMap(typ reflect.Type, checkMapstructureTags bool, } func dereferencePtrToStructIfNeeded(v reflect.Value, tagName string) reflect.Value { - if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { + if v.Kind() != reflect.Ptr { return v } - deref := v.Elem() - derefT := deref.Type() - if isStructTypeConvertibleToMap(derefT, true, tagName) { - return deref + + switch v.Elem().Kind() { + case reflect.Slice: + return v.Elem() + + case reflect.Struct: + deref := v.Elem() + derefT := deref.Type() + if isStructTypeConvertibleToMap(derefT, true, tagName) { + return deref + } + return v + + default: + return v } - return v +} + +func hasAnyTag(field reflect.StructField, tagName string) bool { + _, ok := getTagValue(field, tagName) + return ok +} + +func getTagParts(field reflect.StructField, tagName string) []string { + tagValue, ok := getTagValue(field, tagName) + if !ok { + return nil + } + return strings.Split(tagValue, ",") +} + +func getTagValue(field reflect.StructField, tagName string) (string, bool) { + for _, name := range splitTagNames(tagName) { + if tag := field.Tag.Get(name); tag != "" { + return tag, true + } + } + return "", false +} + +func splitTagNames(tagName string) []string { + if tagName == "" { + return []string{"mapstructure"} + } + parts := strings.Split(tagName, ",") + result := make([]string, 0, len(parts)) + + for _, name := range parts { + name = strings.TrimSpace(name) + if name != "" { + result = append(result, name) + } + } + + return result +} + +// unmarshalerType is cached for performance +var unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem() + +// getUnmarshaler checks if the value implements Unmarshaler and returns +// the Unmarshaler and a boolean indicating if it was found. It handles both +// pointer and value receivers. +func getUnmarshaler(val reflect.Value) (Unmarshaler, bool) { + // Skip invalid or nil values + if !val.IsValid() { + return nil, false + } + + switch val.Kind() { + case reflect.Pointer, reflect.Interface: + if val.IsNil() { + return nil, false + } + } + + // Check pointer receiver first (most common case) + if val.CanAddr() { + ptrVal := val.Addr() + // Quick check: if no methods, can't implement any interface + if ptrVal.Type().NumMethod() > 0 && ptrVal.Type().Implements(unmarshalerType) { + return ptrVal.Interface().(Unmarshaler), true + } + } + + // Check value receiver + // Quick check: if no methods, can't implement any interface + if val.Type().NumMethod() > 0 && val.CanInterface() && val.Type().Implements(unmarshalerType) { + return val.Interface().(Unmarshaler), true + } + + return nil, false } diff --git a/vendor/github.com/golang-jwt/jwt/v5/README.md b/vendor/github.com/golang-jwt/jwt/v5/README.md index 0bb636f22..17e7ea766 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/README.md +++ b/vendor/github.com/golang-jwt/jwt/v5/README.md @@ -140,11 +140,12 @@ A common use case would be integrating with different 3rd party signature providers, like key management services from various cloud providers or Hardware Security Modules (HSMs) or to implement additional standards. -| Extension | Purpose | Repo | -| --------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------ | -| GCP | Integrates with multiple Google Cloud Platform signing tools (AppEngine, IAM API, Cloud KMS) | https://github.com/someone1/gcp-jwt-go | -| AWS | Integrates with AWS Key Management Service, KMS | https://github.com/matelang/jwt-go-aws-kms | -| JWKS | Provides support for JWKS ([RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517)) as a `jwt.Keyfunc` | https://github.com/MicahParks/keyfunc | +| Extension | Purpose | Repo | +| --------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------- | +| GCP | Integrates with multiple Google Cloud Platform signing tools (AppEngine, IAM API, Cloud KMS) | https://github.com/someone1/gcp-jwt-go | +| AWS | Integrates with AWS Key Management Service, KMS | https://github.com/matelang/jwt-go-aws-kms | +| JWKS | Provides support for JWKS ([RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517)) as a `jwt.Keyfunc` | https://github.com/MicahParks/keyfunc | +| TPM | Integrates with Trusted Platform Module (TPM) | https://github.com/salrashid123/golang-jwt-tpm | *Disclaimer*: Unless otherwise specified, these integrations are maintained by third parties and should not be considered as a primary offer by any of the diff --git a/vendor/github.com/golang-jwt/jwt/v5/VERSION_HISTORY.md b/vendor/github.com/golang-jwt/jwt/v5/VERSION_HISTORY.md index b5039e49c..e39ca8efc 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/VERSION_HISTORY.md +++ b/vendor/github.com/golang-jwt/jwt/v5/VERSION_HISTORY.md @@ -97,7 +97,7 @@ Backwards compatible API change that was missed in 2.0.0. There were two major reasons for breaking backwards compatibility with this update. The first was a refactor required to expand the width of the RSA and HMAC-SHA signing implementations. There will likely be no required code changes to support this change. -The second update, while unfortunately requiring a small change in integration, is required to open up this library to other signing methods. Not all keys used for all signing methods have a single standard on-disk representation. Requiring `[]byte` as the type for all keys proved too limiting. Additionally, this implementation allows for pre-parsed tokens to be reused, which might matter in an application that parses a high volume of tokens with a small set of keys. Backwards compatibilty has been maintained for passing `[]byte` to the RSA signing methods, but they will also accept `*rsa.PublicKey` and `*rsa.PrivateKey`. +The second update, while unfortunately requiring a small change in integration, is required to open up this library to other signing methods. Not all keys used for all signing methods have a single standard on-disk representation. Requiring `[]byte` as the type for all keys proved too limiting. Additionally, this implementation allows for pre-parsed tokens to be reused, which might matter in an application that parses a high volume of tokens with a small set of keys. Backwards compatibility has been maintained for passing `[]byte` to the RSA signing methods, but they will also accept `*rsa.PublicKey` and `*rsa.PrivateKey`. It is likely the only integration change required here will be to change `func(t *jwt.Token) ([]byte, error)` to `func(t *jwt.Token) (interface{}, error)` when calling `Parse`. diff --git a/vendor/github.com/golang-jwt/jwt/v5/parser.go b/vendor/github.com/golang-jwt/jwt/v5/parser.go index 054c7eb6f..5f803965c 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/parser.go +++ b/vendor/github.com/golang-jwt/jwt/v5/parser.go @@ -76,13 +76,6 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf } } - // Decode signature - token.Signature, err = p.DecodeSegment(parts[2]) - if err != nil { - return token, newError("could not base64 decode signature", ErrTokenMalformed, err) - } - text := strings.Join(parts[0:2], ".") - // Lookup key(s) if keyFunc == nil { // keyFunc was not provided. short circuiting validation @@ -94,11 +87,14 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf return token, newError("error while executing keyfunc", ErrTokenUnverifiable, err) } + // Join together header and claims in order to verify them with the signature + text := strings.Join(parts[0:2], ".") switch have := got.(type) { case VerificationKeySet: if len(have.Keys) == 0 { return token, newError("keyfunc returned empty verification key set", ErrTokenUnverifiable) } + // Iterate through keys and verify signature, skipping the rest when a match is found. // Return the last error if no match is found. for _, key := range have.Keys { @@ -131,7 +127,7 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf return token, nil } -// ParseUnverified parses the token but doesn't validate the signature. +// ParseUnverified parses the token but does not validate the signature. // // WARNING: Don't use this method unless you know what you're doing. // @@ -146,7 +142,7 @@ func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Toke token = &Token{Raw: tokenString} - // parse Header + // Parse Header var headerBytes []byte if headerBytes, err = p.DecodeSegment(parts[0]); err != nil { return token, parts, newError("could not base64 decode header", ErrTokenMalformed, err) @@ -155,7 +151,7 @@ func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Toke return token, parts, newError("could not JSON decode header", ErrTokenMalformed, err) } - // parse Claims + // Parse Claims token.Claims = claims claimBytes, err := p.DecodeSegment(parts[1]) @@ -196,6 +192,12 @@ func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Toke return token, parts, newError("signing method (alg) is unspecified", ErrTokenUnverifiable) } + // Parse token signature + token.Signature, err = p.DecodeSegment(parts[2]) + if err != nil { + return token, parts, newError("could not base64 decode signature", ErrTokenMalformed, err) + } + return token, parts, nil } @@ -216,7 +218,7 @@ func splitToken(token string) ([]string, bool) { parts[1] = claims // One more cut to ensure the signature is the last part of the token and there are no more // delimiters. This avoids an issue where malicious input could contain additional delimiters - // causing unecessary overhead parsing tokens. + // causing unnecessary overhead parsing tokens. signature, _, unexpected := strings.Cut(remain, tokenDelimiter) if unexpected { return nil, false diff --git a/vendor/github.com/golang-jwt/jwt/v5/parser_option.go b/vendor/github.com/golang-jwt/jwt/v5/parser_option.go index 431573557..af42fd3a7 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/parser_option.go +++ b/vendor/github.com/golang-jwt/jwt/v5/parser_option.go @@ -3,9 +3,7 @@ package jwt import "time" // ParserOption is used to implement functional-style options that modify the -// behavior of the parser. To add new options, just create a function (ideally -// beginning with With or Without) that returns an anonymous function that takes -// a *Parser type as input and manipulates its configuration accordingly. +// behavior of the parser. type ParserOption func(*Parser) // WithValidMethods is an option to supply algorithm methods that the parser @@ -66,6 +64,14 @@ func WithExpirationRequired() ParserOption { } } +// WithNotBeforeRequired returns the ParserOption to make nbf claim required. +// By default nbf claim is optional. +func WithNotBeforeRequired() ParserOption { + return func(p *Parser) { + p.validator.requireNbf = true + } +} + // WithAudience configures the validator to require any of the specified // audiences in the `aud` claim. Validation will fail if the audience is not // listed in the token or the `aud` claim is missing. diff --git a/vendor/github.com/golang-jwt/jwt/v5/token.go b/vendor/github.com/golang-jwt/jwt/v5/token.go index 3f7155888..d9f6c9d25 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/token.go +++ b/vendor/github.com/golang-jwt/jwt/v5/token.go @@ -32,8 +32,8 @@ type Token struct { Method SigningMethod // Method is the signing method used or to be used Header map[string]any // Header is the first segment of the token in decoded form Claims Claims // Claims is the second segment of the token in decoded form - Signature []byte // Signature is the third segment of the token in decoded form. Populated when you Parse a token - Valid bool // Valid specifies if the token is valid. Populated when you Parse/Verify a token + Signature []byte // Signature is the third segment of the token in decoded form. Populated when you [Parse] or sign a token + Valid bool // Valid specifies if the token is valid. Populated when you [Parse] a token } // New creates a new [Token] with the specified signing method and an empty map @@ -71,6 +71,8 @@ func (t *Token) SignedString(key any) (string, error) { return "", err } + t.Signature = sig + return sstr + "." + t.EncodeSegment(sig), nil } diff --git a/vendor/github.com/golang-jwt/jwt/v5/validator.go b/vendor/github.com/golang-jwt/jwt/v5/validator.go index 92b5c057c..c82dfcae6 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/validator.go +++ b/vendor/github.com/golang-jwt/jwt/v5/validator.go @@ -44,6 +44,9 @@ type Validator struct { // requireExp specifies whether the exp claim is required requireExp bool + // requireNbf specifies whether the nbf claim is required + requireNbf bool + // verifyIat specifies whether the iat (Issued At) claim will be verified. // According to https://www.rfc-editor.org/rfc/rfc7519#section-4.1.6 this // only specifies the age of the token, but no validation check is @@ -111,8 +114,9 @@ func (v *Validator) Validate(claims Claims) error { } // We always need to check not-before, but usage of the claim itself is - // OPTIONAL. - if err = v.verifyNotBefore(claims, now, false); err != nil { + // OPTIONAL by default. requireNbf overrides this behavior and makes + // the nbf claim mandatory. + if err = v.verifyNotBefore(claims, now, v.requireNbf); err != nil { errs = append(errs, err) } diff --git a/vendor/github.com/google/go-querystring/query/encode.go b/vendor/github.com/google/go-querystring/query/encode.go index 91198f819..c93695403 100644 --- a/vendor/github.com/google/go-querystring/query/encode.go +++ b/vendor/github.com/google/go-querystring/query/encode.go @@ -6,22 +6,21 @@ // // As a simple example: // -// type Options struct { -// Query string `url:"q"` -// ShowAll bool `url:"all"` -// Page int `url:"page"` -// } +// type Options struct { +// Query string `url:"q"` +// ShowAll bool `url:"all"` +// Page int `url:"page"` +// } // -// opt := Options{ "foo", true, 2 } -// v, _ := query.Values(opt) -// fmt.Print(v.Encode()) // will output: "q=foo&all=true&page=2" +// opt := Options{ "foo", true, 2 } +// v, _ := query.Values(opt) +// fmt.Print(v.Encode()) // will output: "q=foo&all=true&page=2" // // The exact mapping between Go values and url.Values is described in the // documentation for the Values() function. package query import ( - "bytes" "fmt" "net/url" "reflect" @@ -47,8 +46,8 @@ type Encoder interface { // // Each exported struct field is encoded as a URL parameter unless // -// - the field's tag is "-", or -// - the field is empty and its tag specifies the "omitempty" option +// - the field's tag is "-", or +// - the field is empty and its tag specifies the "omitempty" option // // The empty values are false, 0, any nil pointer or interface value, any array // slice, map, or string of length zero, and any type (such as time.Time) that @@ -59,19 +58,19 @@ type Encoder interface { // field's tag value is the key name, followed by an optional comma and // options. For example: // -// // Field is ignored by this package. -// Field int `url:"-"` +// // Field is ignored by this package. +// Field int `url:"-"` // -// // Field appears as URL parameter "myName". -// Field int `url:"myName"` +// // Field appears as URL parameter "myName". +// Field int `url:"myName"` // -// // Field appears as URL parameter "myName" and the field is omitted if -// // its value is empty -// Field int `url:"myName,omitempty"` +// // Field appears as URL parameter "myName" and the field is omitted if +// // its value is empty +// Field int `url:"myName,omitempty"` // -// // Field appears as URL parameter "Field" (the default), but the field -// // is skipped if empty. Note the leading comma. -// Field int `url:",omitempty"` +// // Field appears as URL parameter "Field" (the default), but the field +// // is skipped if empty. Note the leading comma. +// Field int `url:",omitempty"` // // For encoding individual field values, the following type-dependent rules // apply: @@ -88,8 +87,8 @@ type Encoder interface { // "url" tag) will use the value of the "layout" tag as a layout passed to // time.Format. For example: // -// // Encode a time.Time as YYYY-MM-DD -// Field time.Time `layout:"2006-01-02"` +// // Encode a time.Time as YYYY-MM-DD +// Field time.Time `layout:"2006-01-02"` // // Slice and Array values default to encoding as multiple URL values of the // same name. Including the "comma" option signals that the field should be @@ -103,9 +102,9 @@ type Encoder interface { // from the "url" tag) will use the value of the "del" tag as the delimiter. // For example: // -// // Encode a slice of bools as ints ("1" for true, "0" for false), -// // separated by exclamation points "!". -// Field []bool `url:",int" del:"!"` +// // Encode a slice of bools as ints ("1" for true, "0" for false), +// // separated by exclamation points "!". +// Field []bool `url:",int" del:"!"` // // Anonymous struct fields are usually encoded as if their inner exported // fields were fields in the outer struct, subject to the standard Go @@ -114,10 +113,10 @@ type Encoder interface { // // Non-nil pointer values are encoded as the value pointed to. // -// Nested structs are encoded including parent fields in value names for -// scoping. e.g: +// Nested structs have their fields processed recursively and are encoded +// including parent fields in value names for scoping. For example, // -// "user[name]=acme&user[addr][postcode]=1234&user[addr][city]=SFO" +// "user[name]=acme&user[addr][postcode]=1234&user[addr][city]=SFO" // // All other values are encoded using their default string representation. // @@ -125,6 +124,11 @@ type Encoder interface { // as multiple URL values of the same name. func Values(v interface{}) (url.Values, error) { values := make(url.Values) + + if v == nil { + return values, nil + } + val := reflect.ValueOf(v) for val.Kind() == reflect.Ptr { if val.IsNil() { @@ -133,10 +137,6 @@ func Values(v interface{}) (url.Values, error) { val = val.Elem() } - if v == nil { - return values, nil - } - if val.Kind() != reflect.Struct { return nil, fmt.Errorf("query: Values() expects struct input. Got %v", val.Kind()) } @@ -209,6 +209,11 @@ func reflectValue(values url.Values, val reflect.Value, scope string) error { } if sv.Kind() == reflect.Slice || sv.Kind() == reflect.Array { + if sv.Len() == 0 { + // skip if slice or array is empty + continue + } + var del string if opts.Contains("comma") { del = "," @@ -223,7 +228,7 @@ func reflectValue(values url.Values, val reflect.Value, scope string) error { } if del != "" { - s := new(bytes.Buffer) + s := new(strings.Builder) first := true for i := 0; i < sv.Len(); i++ { if first { diff --git a/vendor/github.com/google/pprof/profile/profile.go b/vendor/github.com/google/pprof/profile/profile.go index 43f561d44..18df65a8d 100644 --- a/vendor/github.com/google/pprof/profile/profile.go +++ b/vendor/github.com/google/pprof/profile/profile.go @@ -278,7 +278,7 @@ func (p *Profile) massageMappings() { // Use heuristics to identify main binary and move it to the top of the list of mappings for i, m := range p.Mapping { - file := strings.TrimSpace(strings.Replace(m.File, "(deleted)", "", -1)) + file := strings.TrimSpace(strings.ReplaceAll(m.File, "(deleted)", "")) if len(file) == 0 { continue } diff --git a/vendor/github.com/googleapis/gax-go/v2/CHANGES.md b/vendor/github.com/googleapis/gax-go/v2/CHANGES.md index fec6b1da9..2c3300e0a 100644 --- a/vendor/github.com/googleapis/gax-go/v2/CHANGES.md +++ b/vendor/github.com/googleapis/gax-go/v2/CHANGES.md @@ -1,4 +1,10 @@ -# Changelog +# Changes + +## [2.16.0](https://github.com/googleapis/google-cloud-go/releases/tag/v2.16.0) (2025-12-17) + +### Features + +* add IsFeatureEnabled (#454) ([2700b8a](https://github.com/googleapis/google-cloud-go/commit/2700b8ab3062c6c6c5a26d0fc6ba1fc064a8fc04)) ## [2.15.0](https://github.com/googleapis/gax-go/compare/v2.14.2...v2.15.0) (2025-07-09) diff --git a/vendor/github.com/googleapis/gax-go/v2/feature.go b/vendor/github.com/googleapis/gax-go/v2/feature.go new file mode 100644 index 000000000..32e05a323 --- /dev/null +++ b/vendor/github.com/googleapis/gax-go/v2/feature.go @@ -0,0 +1,75 @@ +// Copyright 2025, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package gax + +import ( + "os" + "strings" + "sync" +) + +var ( + // featureEnabledOnce caches results for IsFeatureEnabled. + featureEnabledOnce sync.Once + featureEnabledStore map[string]bool +) + +// IsFeatureEnabled checks if an experimental feature is enabled via +// environment variable. The environment variable must be prefixed with +// "GOOGLE_SDK_GO_EXPERIMENTAL_". The feature name passed to this +// function must be the suffix (e.g., "FOO" for "GOOGLE_SDK_GO_EXPERIMENTAL_FOO"). +// To enable the feature, the environment variable's value must be "true", +// case-insensitive. The result for each name is cached on the first call. +func IsFeatureEnabled(name string) bool { + featureEnabledOnce.Do(func() { + featureEnabledStore = make(map[string]bool) + for _, env := range os.Environ() { + if strings.HasPrefix(env, "GOOGLE_SDK_GO_EXPERIMENTAL_") { + // Parse "KEY=VALUE" + kv := strings.SplitN(env, "=", 2) + if len(kv) == 2 && strings.ToLower(kv[1]) == "true" { + key := strings.TrimPrefix(kv[0], "GOOGLE_SDK_GO_EXPERIMENTAL_") + featureEnabledStore[key] = true + } + } + } + }) + return featureEnabledStore[name] +} + +// TestOnlyResetIsFeatureEnabled is for testing purposes only. It resets the cached +// feature flags, allowing environment variables to be re-read on the next call to IsFeatureEnabled. +// This function is not thread-safe; if another goroutine reads a feature after this +// function is called but before the `featureEnabledOnce` is re-initialized by IsFeatureEnabled, +// it may see an inconsistent state. +func TestOnlyResetIsFeatureEnabled() { + featureEnabledOnce = sync.Once{} + featureEnabledStore = nil +} diff --git a/vendor/github.com/googleapis/gax-go/v2/internal/version.go b/vendor/github.com/googleapis/gax-go/v2/internal/version.go index 0ab1bce59..fa0e1007a 100644 --- a/vendor/github.com/googleapis/gax-go/v2/internal/version.go +++ b/vendor/github.com/googleapis/gax-go/v2/internal/version.go @@ -1,33 +1,20 @@ -// Copyright 2022, Google Inc. -// All rights reserved. +// Copyright 2025 Google LLC // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. +// http://www.apache.org/licenses/LICENSE-2.0 // -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by gapicgen. DO NOT EDIT. package internal // Version is the current tagged release of the library. -const Version = "2.15.0" +const Version = "2.16.0" diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options/openapiv2.pb.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options/openapiv2.pb.go index 3a34e664e..5121dce38 100644 --- a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options/openapiv2.pb.go +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options/openapiv2.pb.go @@ -3476,6 +3476,9 @@ type JSONSchema_FieldConfiguration struct { // parameter. Use this to avoid having auto generated path parameter names // for overlapping paths. PathParamName string `protobuf:"bytes,47,opt,name=path_param_name,json=pathParamName,proto3" json:"path_param_name,omitempty"` + // Declares this field to be deprecated. Allows for the generated OpenAPI + // parameter to be marked as deprecated without affecting the proto field. + Deprecated bool `protobuf:"varint,49,opt,name=deprecated,proto3" json:"deprecated,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -3512,10 +3515,21 @@ func (x *JSONSchema_FieldConfiguration) GetPathParamName() string { return "" } +func (x *JSONSchema_FieldConfiguration) GetDeprecated() bool { + if x != nil { + return x.Deprecated + } + return false +} + func (x *JSONSchema_FieldConfiguration) SetPathParamName(v string) { x.PathParamName = v } +func (x *JSONSchema_FieldConfiguration) SetDeprecated(v bool) { + x.Deprecated = v +} + type JSONSchema_FieldConfiguration_builder struct { _ [0]func() // Prevents comparability and use of unkeyed literals for the builder. @@ -3524,6 +3538,9 @@ type JSONSchema_FieldConfiguration_builder struct { // parameter. Use this to avoid having auto generated path parameter names // for overlapping paths. PathParamName string + // Declares this field to be deprecated. Allows for the generated OpenAPI + // parameter to be marked as deprecated without affecting the proto field. + Deprecated bool } func (b0 JSONSchema_FieldConfiguration_builder) Build() *JSONSchema_FieldConfiguration { @@ -3531,6 +3548,7 @@ func (b0 JSONSchema_FieldConfiguration_builder) Build() *JSONSchema_FieldConfigu b, x := &b0, m0 _, _ = b, x x.PathParamName = b.PathParamName + x.Deprecated = b.Deprecated return m0 } @@ -3904,7 +3922,7 @@ var file_protoc_gen_openapiv2_options_openapiv2_proto_rawDesc = []byte{ 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd7, + 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf7, 0x0a, 0x0a, 0x0a, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x65, 0x66, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, @@ -3968,11 +3986,13 @@ var file_protoc_gen_openapiv2_options_openapiv2_proto_rawDesc = []byte{ 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3c, 0x0a, 0x12, + 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x5c, 0x0a, 0x12, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x2f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, 0x74, - 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78, + 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, + 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x31, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, + 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options/openapiv2.proto b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options/openapiv2.proto index 5313f0818..444a5687a 100644 --- a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options/openapiv2.proto +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options/openapiv2.proto @@ -612,6 +612,9 @@ message JSONSchema { // parameter. Use this to avoid having auto generated path parameter names // for overlapping paths. string path_param_name = 47; + // Declares this field to be deprecated. Allows for the generated OpenAPI + // parameter to be marked as deprecated without affecting the proto field. + bool deprecated = 49; } // Custom properties that start with "x-" such as "x-foo" used to describe // extra functionality that is not covered by the standard OpenAPI Specification. diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options/openapiv2_protoopaque.pb.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options/openapiv2_protoopaque.pb.go index 1f0e0c269..5316ed619 100644 --- a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options/openapiv2_protoopaque.pb.go +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options/openapiv2_protoopaque.pb.go @@ -3268,6 +3268,7 @@ func (b0 Scopes_builder) Build() *Scopes { type JSONSchema_FieldConfiguration struct { state protoimpl.MessageState `protogen:"opaque.v1"` xxx_hidden_PathParamName string `protobuf:"bytes,47,opt,name=path_param_name,json=pathParamName,proto3" json:"path_param_name,omitempty"` + xxx_hidden_Deprecated bool `protobuf:"varint,49,opt,name=deprecated,proto3" json:"deprecated,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -3304,10 +3305,21 @@ func (x *JSONSchema_FieldConfiguration) GetPathParamName() string { return "" } +func (x *JSONSchema_FieldConfiguration) GetDeprecated() bool { + if x != nil { + return x.xxx_hidden_Deprecated + } + return false +} + func (x *JSONSchema_FieldConfiguration) SetPathParamName(v string) { x.xxx_hidden_PathParamName = v } +func (x *JSONSchema_FieldConfiguration) SetDeprecated(v bool) { + x.xxx_hidden_Deprecated = v +} + type JSONSchema_FieldConfiguration_builder struct { _ [0]func() // Prevents comparability and use of unkeyed literals for the builder. @@ -3316,6 +3328,9 @@ type JSONSchema_FieldConfiguration_builder struct { // parameter. Use this to avoid having auto generated path parameter names // for overlapping paths. PathParamName string + // Declares this field to be deprecated. Allows for the generated OpenAPI + // parameter to be marked as deprecated without affecting the proto field. + Deprecated bool } func (b0 JSONSchema_FieldConfiguration_builder) Build() *JSONSchema_FieldConfiguration { @@ -3323,6 +3338,7 @@ func (b0 JSONSchema_FieldConfiguration_builder) Build() *JSONSchema_FieldConfigu b, x := &b0, m0 _, _ = b, x x.xxx_hidden_PathParamName = b.PathParamName + x.xxx_hidden_Deprecated = b.Deprecated return m0 } @@ -3696,7 +3712,7 @@ var file_protoc_gen_openapiv2_options_openapiv2_proto_rawDesc = []byte{ 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd7, + 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf7, 0x0a, 0x0a, 0x0a, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x65, 0x66, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, @@ -3760,11 +3776,13 @@ var file_protoc_gen_openapiv2_options_openapiv2_proto_rawDesc = []byte{ 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3c, 0x0a, 0x12, + 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x5c, 0x0a, 0x12, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x2f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, 0x74, - 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78, + 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, + 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x31, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, + 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, diff --git a/vendor/github.com/knadh/koanf/v2/README.md b/vendor/github.com/knadh/koanf/v2/README.md index 3130a2870..c94dbfbf4 100644 --- a/vendor/github.com/knadh/koanf/v2/README.md +++ b/vendor/github.com/knadh/koanf/v2/README.md @@ -52,8 +52,8 @@ go get -u github.com/knadh/koanf/parsers/toml ### Concepts -- `koanf.Provider` is a generic interface that provides configuration, for example, from files, environment variables, HTTP sources, or anywhere. The configuration can either be raw bytes that a parser can parse, or it can be a nested `map[string]interface{}` that can be directly loaded. -- `koanf.Parser` is a generic interface that takes raw bytes, parses, and returns a nested `map[string]interface{}`. For example, JSON and YAML parsers. +- `koanf.Provider` is a generic interface that provides configuration, for example, from files, environment variables, HTTP sources, or anywhere. The configuration can either be raw bytes that a parser can parse, or it can be a nested `map[string]any` that can be directly loaded. +- `koanf.Parser` is a generic interface that takes raw bytes, parses, and returns a nested `map[string]any`. For example, JSON and YAML parsers. - Once loaded into koanf, configuration are values queried by a delimited key path syntax. eg: `app.server.port`. Any delimiter can be chosen. - Configuration from multiple sources can be loaded and merged into a koanf instance, for example, load from a file first and override certain values with flags from the command line. @@ -133,7 +133,7 @@ func main() { // Watch the file and get a callback on change. The callback can do whatever, // like re-load the configuration. // File provider always returns a nil `event`. - f.Watch(func(event interface{}, err error) { + f.Watch(func(event any, err error) { if err != nil { log.Printf("watch error: %v", err) return @@ -430,7 +430,7 @@ func main() { #### Reading from nested maps -The bundled `confmap` provider takes a `map[string]interface{}` that can be loaded into a koanf instance. +The bundled `confmap` provider takes a `map[string]any` that can be loaded into a koanf instance. ```go package main @@ -453,7 +453,7 @@ func main() { // Load default values using the confmap provider. // We provide a flat map with the "." delimiter. // A nested map can be loaded by setting the delimiter to an empty string "". - k.Load(confmap.Provider(map[string]interface{}{ + k.Load(confmap.Provider(map[string]any{ "parent1.name": "Default Name", "parent3.name": "New name here", }, "."), nil) @@ -596,11 +596,11 @@ For example: merging JSON and YAML will most likely fail because JSON treats int ### Custom Providers and Parsers -A Provider returns a nested `map[string]interface{}` config that can be loaded directly into koanf with `koanf.Load()` or it can return raw bytes that can be parsed with a Parser (again, loaded using `koanf.Load()`. Writing Providers and Parsers are easy. See the bundled implementations in the [providers](https://github.com/knadh/koanf/tree/master/providers) and [parsers](https://github.com/knadh/koanf/tree/master/parsers) directories. +A Provider returns a nested `map[string]any` config that can be loaded directly into koanf with `koanf.Load()` or it can return raw bytes that can be parsed with a Parser (again, loaded using `koanf.Load()`. Writing Providers and Parsers are easy. See the bundled implementations in the [providers](https://github.com/knadh/koanf/tree/master/providers) and [parsers](https://github.com/knadh/koanf/tree/master/parsers) directories. ### Custom merge strategies -By default, when merging two config sources using `Load()`, koanf recursively merges keys of nested maps (`map[string]interface{}`), +By default, when merging two config sources using `Load()`, koanf recursively merges keys of nested maps (`map[string]any`), while static values are overwritten (slices, strings, etc). This behaviour can be changed by providing a custom merge function with the `WithMergeFunc` option. ```go @@ -630,7 +630,7 @@ func main() { } jsonPath := "mock/mock.json" - if err := k.Load(file.Provider(jsonPath), json.Parser(), koanf.WithMergeFunc(func(src, dest map[string]interface{}) error { + if err := k.Load(file.Provider(jsonPath), json.Parser(), koanf.WithMergeFunc(func(src, dest map[string]any) error { // Your custom logic, copying values from src into dst return nil })); err != nil { @@ -654,8 +654,8 @@ Install with `go get -u github.com/knadh/koanf/providers/$provider` | basicflag | `basicflag.Provider(f *flag.FlagSet, delim string)` | Takes a stdlib `flag.FlagSet` | | posflag | `posflag.Provider(f *pflag.FlagSet, delim string)` | Takes an `spf13/pflag.FlagSet` (advanced POSIX compatible flags with multiple types) and provides a nested config map based on delim. | | env/v2 | `env.Provider(prefix, delim string, f func(s string) string)` | Takes an optional prefix to filter env variables by, an optional function that takes and returns a string to transform env variables, and returns a nested config map based on delim. | -| confmap | `confmap.Provider(mp map[string]interface{}, delim string)` | Takes a premade `map[string]interface{}` conf map. If delim is provided, the keys are assumed to be flattened, thus unflattened using delim. | -| structs | `structs.Provider(s interface{}, tag string)` | Takes a struct and struct tag. | +| confmap | `confmap.Provider(mp map[string]any, delim string)` | Takes a premade `map[string]any` conf map. If delim is provided, the keys are assumed to be flattened, thus unflattened using delim. | +| structs | `structs.Provider(s any, tag string)` | Takes a struct and struct tag. | | s3 | `s3.Provider(s3.S3Config{})` | Takes a s3 config struct. | | rawbytes | `rawbytes.Provider(b []byte)` | Takes a raw `[]byte` slice to be parsed with a koanf.Parser | | vault/v2 | `vault.Provider(vault.Config{})` | Hashicorp Vault provider | diff --git a/vendor/github.com/knadh/koanf/v2/getters.go b/vendor/github.com/knadh/koanf/v2/getters.go index 266230f74..6a50ac37c 100644 --- a/vendor/github.com/knadh/koanf/v2/getters.go +++ b/vendor/github.com/knadh/koanf/v2/getters.go @@ -51,7 +51,7 @@ func (ko *Koanf) Int64s(path string) []int64 { out = append(out, i) } return out - case []interface{}: + case []any: out = make([]int64, 0, len(v)) for _, vi := range v { i, err := toInt64(vi) @@ -91,7 +91,7 @@ func (ko *Koanf) Int64Map(path string) map[string]int64 { return out } - mp, ok := o.(map[string]interface{}) + mp, ok := o.(map[string]any) if !ok { return out } @@ -158,7 +158,7 @@ func (ko *Koanf) Ints(path string) []int { out = append(out, int(vi)) } return out - case []interface{}: + case []any: out = make([]int, 0, len(v)) for _, vi := range v { i, err := toInt64(vi) @@ -243,7 +243,7 @@ func (ko *Koanf) Float64s(path string) []float64 { switch v := o.(type) { case []float64: return v - case []interface{}: + case []any: out = make([]float64, 0, len(v)) for _, vi := range v { i, err := toFloat64(vi) @@ -283,7 +283,7 @@ func (ko *Koanf) Float64Map(path string) map[string]float64 { return out } - mp, ok := o.(map[string]interface{}) + mp, ok := o.(map[string]any) if !ok { return out } @@ -402,7 +402,7 @@ func (ko *Koanf) Strings(path string) []string { var out []string switch v := o.(type) { - case []interface{}: + case []any: out = make([]string, 0, len(v)) for _, u := range v { if s, ok := u.(string); ok { @@ -449,7 +449,7 @@ func (ko *Koanf) StringMap(path string) map[string]string { for k, v := range mp { out[k] = v } - case map[string]interface{}: + case map[string]any: out = make(map[string]string, len(mp)) for k, v := range mp { switch s := v.(type) { @@ -493,7 +493,7 @@ func (ko *Koanf) StringsMap(path string) map[string][]string { for k, v := range mp { out[k] = append(out[k], v...) } - case map[string][]interface{}: + case map[string][]any: out = make(map[string][]string, len(mp)) for k, v := range mp { for _, v := range v { @@ -505,13 +505,13 @@ func (ko *Koanf) StringsMap(path string) map[string][]string { } } } - case map[string]interface{}: + case map[string]any: out = make(map[string][]string, len(mp)) for k, v := range mp { switch s := v.(type) { case []string: out[k] = append(out[k], s...) - case []interface{}: + case []any: for _, v := range s { switch sv := v.(type) { case string: @@ -578,7 +578,7 @@ func (ko *Koanf) Bools(path string) []bool { var out []bool switch v := o.(type) { - case []interface{}: + case []any: out = make([]bool, 0, len(v)) for _, u := range v { b, err := toBool(u) @@ -616,7 +616,7 @@ func (ko *Koanf) BoolMap(path string) map[string]bool { return out } - mp, ok := o.(map[string]interface{}) + mp, ok := o.(map[string]any) if !ok { return out } diff --git a/vendor/github.com/knadh/koanf/v2/go.work.sum b/vendor/github.com/knadh/koanf/v2/go.work.sum index e19681eab..f6f501f9a 100644 --- a/vendor/github.com/knadh/koanf/v2/go.work.sum +++ b/vendor/github.com/knadh/koanf/v2/go.work.sum @@ -602,6 +602,7 @@ golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= +golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -618,6 +619,7 @@ golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -633,6 +635,9 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= @@ -649,6 +654,8 @@ golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -668,9 +675,11 @@ golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457 h1:zf5N6UOrA487eEFacMePxjXAJctxKmyjKUsjA11Uzuk= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= +golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= @@ -679,12 +688,16 @@ golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -699,6 +712,8 @@ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxb golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o= diff --git a/vendor/github.com/knadh/koanf/v2/interfaces.go b/vendor/github.com/knadh/koanf/v2/interfaces.go index ba69a2443..3f99ef8c7 100644 --- a/vendor/github.com/knadh/koanf/v2/interfaces.go +++ b/vendor/github.com/knadh/koanf/v2/interfaces.go @@ -7,14 +7,14 @@ type Provider interface { // with a Parser. ReadBytes() ([]byte, error) - // Read returns the parsed configuration as a nested map[string]interface{}. + // Read returns the parsed configuration as a nested map[string]any. // It is important to note that the string keys should not be flat delimited // keys like `parent.child.key`, but nested like `{parent: {child: {key: 1}}}`. - Read() (map[string]interface{}, error) + Read() (map[string]any, error) } // Parser represents a configuration format parser. type Parser interface { - Unmarshal([]byte) (map[string]interface{}, error) - Marshal(map[string]interface{}) ([]byte, error) + Unmarshal([]byte) (map[string]any, error) + Marshal(map[string]any) ([]byte, error) } diff --git a/vendor/github.com/knadh/koanf/v2/koanf.go b/vendor/github.com/knadh/koanf/v2/koanf.go index 66fae73b5..f7475d751 100644 --- a/vendor/github.com/knadh/koanf/v2/koanf.go +++ b/vendor/github.com/knadh/koanf/v2/koanf.go @@ -16,8 +16,8 @@ import ( // Koanf is the configuration apparatus. type Koanf struct { - confMap map[string]interface{} - confMapFlat map[string]interface{} + confMap map[string]any + confMapFlat map[string]any keyMap KeyMap conf Conf mu sync.RWMutex @@ -79,20 +79,20 @@ func New(delim string) *Koanf { // NewWithConf returns a new instance of Koanf based on the Conf. func NewWithConf(conf Conf) *Koanf { return &Koanf{ - confMap: make(map[string]interface{}), - confMapFlat: make(map[string]interface{}), + confMap: make(map[string]any), + confMapFlat: make(map[string]any), keyMap: make(KeyMap), conf: conf, } } -// Load takes a Provider that either provides a parsed config map[string]interface{} +// Load takes a Provider that either provides a parsed config map[string]any // in which case pa (Parser) can be nil, or raw bytes to be parsed, where a Parser // can be provided to parse. Additionally, options can be passed which modify the // load behavior, such as passing a custom merge function. func (ko *Koanf) Load(p Provider, pa Parser, opts ...Option) error { var ( - mp map[string]interface{} + mp map[string]any err error ) @@ -151,7 +151,7 @@ func (ko *Koanf) KeyMap() KeyMap { // All returns a map of all flattened key paths and their values. // Note that it uses maps.Copy to create a copy that uses // json.Marshal which changes the numeric types to float64. -func (ko *Koanf) All() map[string]interface{} { +func (ko *Koanf) All() map[string]any { ko.mu.RLock() defer ko.mu.RUnlock() return maps.Copy(ko.confMapFlat) @@ -160,7 +160,7 @@ func (ko *Koanf) All() map[string]interface{} { // Raw returns a copy of the full raw conf map. // Note that it uses maps.Copy to create a copy that uses // json.Marshal which changes the numeric types to float64. -func (ko *Koanf) Raw() map[string]interface{} { +func (ko *Koanf) Raw() map[string]any { ko.mu.RLock() defer ko.mu.RUnlock() return maps.Copy(ko.confMap) @@ -193,10 +193,10 @@ func (ko *Koanf) Print() { // instance with the config map `sub.a.b` where everything above // `parent.child` are cut out. func (ko *Koanf) Cut(path string) *Koanf { - out := make(map[string]interface{}) + out := make(map[string]any) // Cut only makes sense if the requested key path is a map. - if v, ok := ko.Get(path).(map[string]interface{}); ok { + if v, ok := ko.Get(path).(map[string]any); ok { out = v } @@ -227,7 +227,7 @@ func (ko *Koanf) MergeAt(in *Koanf, path string) error { } // Unflatten the config map with the given key path. - n := maps.Unflatten(map[string]interface{}{ + n := maps.Unflatten(map[string]any{ path: in.Raw(), }, ko.conf.Delim) @@ -235,9 +235,9 @@ func (ko *Koanf) MergeAt(in *Koanf, path string) error { } // Set sets the value at a specific key. -func (ko *Koanf) Set(key string, val interface{}) error { +func (ko *Koanf) Set(key string, val any) error { // Unflatten the config map with the given key path. - n := maps.Unflatten(map[string]interface{}{ + n := maps.Unflatten(map[string]any{ key: val, }, ko.conf.Delim) @@ -254,14 +254,14 @@ func (ko *Koanf) Marshal(p Parser) ([]byte, error) { // the mapstructure lib. If no path is specified, the whole map is unmarshalled. // `koanf` is the struct field tag used to match field names. To customize, // use UnmarshalWithConf(). It uses the mitchellh/mapstructure package. -func (ko *Koanf) Unmarshal(path string, o interface{}) error { +func (ko *Koanf) Unmarshal(path string, o any) error { return ko.UnmarshalWithConf(path, o, UnmarshalConf{}) } // UnmarshalWithConf is like Unmarshal but takes configuration params in UnmarshalConf. // See mitchellh/mapstructure's DecoderConfig for advanced customization // of the unmarshal behaviour. -func (ko *Koanf) UnmarshalWithConf(path string, o interface{}, c UnmarshalConf) error { +func (ko *Koanf) UnmarshalWithConf(path string, o any, c UnmarshalConf) error { if c.DecoderConfig == nil { c.DecoderConfig = &mapstructure.DecoderConfig{ DecodeHook: mapstructure.ComposeDecodeHookFunc( @@ -288,7 +288,7 @@ func (ko *Koanf) UnmarshalWithConf(path string, o interface{}, c UnmarshalConf) // Unmarshal using flat key paths. mp := ko.Get(path) if c.FlatPaths { - if f, ok := mp.(map[string]interface{}); ok { + if f, ok := mp.(map[string]any); ok { fmp, _ := maps.Flatten(f, nil, ko.conf.Delim) mp = fmp } @@ -306,8 +306,8 @@ func (ko *Koanf) Delete(path string) { // No path. Erase the entire map. if path == "" { - ko.confMap = make(map[string]interface{}) - ko.confMapFlat = make(map[string]interface{}) + ko.confMap = make(map[string]any) + ko.confMapFlat = make(map[string]any) ko.keyMap = make(KeyMap) return } @@ -324,9 +324,9 @@ func (ko *Koanf) Delete(path string) { ko.keyMap = populateKeyParts(ko.keyMap, ko.conf.Delim) } -// Get returns the raw, uncast interface{} value of a given key path +// Get returns the raw, uncast any value of a given key path // in the config map. If the key path does not exist, nil is returned. -func (ko *Koanf) Get(path string) interface{} { +func (ko *Koanf) Get(path string) any { // No path. Return the whole conf map. if path == "" { return ko.Raw() @@ -349,19 +349,26 @@ func (ko *Koanf) Get(path string) interface{} { switch v := res.(type) { case int, int8, int16, int32, int64, float32, float64, string, bool: return v - case map[string]interface{}: + case map[string]any: return maps.Copy(v) + case nil: + return nil + } + + // Skil nil pointers before copying. + if rv := reflect.ValueOf(res); rv.Kind() == reflect.Ptr && rv.IsNil() { + return res } out, _ := copystructure.Copy(&res) - if ptrOut, ok := out.(*interface{}); ok { + if ptrOut, ok := out.(*any); ok { return *ptrOut } return out } // Slices returns a list of Koanf instances constructed out of a -// []map[string]interface{} interface at the given path. +// []map[string]any interface at the given path. func (ko *Koanf) Slices(path string) []*Koanf { out := []*Koanf{} if path == "" { @@ -369,13 +376,13 @@ func (ko *Koanf) Slices(path string) []*Koanf { } // Does the path exist? - sl, ok := ko.Get(path).([]interface{}) + sl, ok := ko.Get(path).([]any) if !ok { return out } for _, s := range sl { - mp, ok := s.(map[string]interface{}) + mp, ok := s.(map[string]any) if !ok { continue } @@ -408,7 +415,7 @@ func (ko *Koanf) MapKeys(path string) []string { return out } - mp, ok := o.(map[string]interface{}) + mp, ok := o.(map[string]any) if !ok { return out } @@ -425,7 +432,7 @@ func (ko *Koanf) Delim() string { return ko.conf.Delim } -func (ko *Koanf) merge(c map[string]interface{}, opts *options) error { +func (ko *Koanf) merge(c map[string]any, opts *options) error { ko.mu.Lock() defer ko.mu.Unlock() @@ -453,7 +460,7 @@ func (ko *Koanf) merge(c map[string]interface{}, opts *options) error { // converts and returns int64. If it's any other type, // forces it to a string and attempts to do a strconv.Atoi // to get an integer out. -func toInt64(v interface{}) (int64, error) { +func toInt64(v any) (int64, error) { switch i := v.(type) { case int: return int64(i), nil @@ -476,10 +483,10 @@ func toInt64(v interface{}) (int64, error) { return int64(f), nil } -// toInt64 takes a `v interface{}` value and if it is a float type, +// toInt64 takes a `v any` value and if it is a float type, // converts and returns a `float64`. If it's any other type, forces it to a // string and attempts to get a float out using `strconv.ParseFloat`. -func toFloat64(v interface{}) (float64, error) { +func toFloat64(v any) (float64, error) { switch i := v.(type) { case float32: return float64(i), nil @@ -499,7 +506,7 @@ func toFloat64(v interface{}) (float64, error) { // toBool takes an interface value and if it is a bool type, // returns it. If it's any other type, forces it to a string and attempts // to parse it as a bool using strconv.ParseBool. -func toBool(v interface{}) (bool, error) { +func toBool(v any) (bool, error) { if b, ok := v.(bool); ok { return b, nil } @@ -545,8 +552,8 @@ func textUnmarshalerHookFunc() mapstructure.DecodeHookFuncType { return func( f reflect.Type, t reflect.Type, - data interface{}, - ) (interface{}, error) { + data any, + ) (any, error) { if f.Kind() != reflect.String { return data, nil } diff --git a/vendor/github.com/knadh/koanf/v2/options.go b/vendor/github.com/knadh/koanf/v2/options.go index 63cea203e..e53579749 100644 --- a/vendor/github.com/knadh/koanf/v2/options.go +++ b/vendor/github.com/knadh/koanf/v2/options.go @@ -2,7 +2,7 @@ package koanf // options contains options to modify the behavior of Koanf.Load. type options struct { - merge func(a, b map[string]interface{}) error + merge func(a, b map[string]any) error } // newOptions creates a new options instance. @@ -26,7 +26,7 @@ func (o *options) apply(opts []Option) { // If unset, the default merge function is used. // // The merge function is expected to merge map src into dest (left to right). -func WithMergeFunc(merge func(src, dest map[string]interface{}) error) Option { +func WithMergeFunc(merge func(src, dest map[string]any) error) Option { return func(o *options) { o.merge = merge } diff --git a/vendor/github.com/miekg/dns/README.md b/vendor/github.com/miekg/dns/README.md index 2a7d8c265..8dc247236 100644 --- a/vendor/github.com/miekg/dns/README.md +++ b/vendor/github.com/miekg/dns/README.md @@ -6,7 +6,12 @@ DNS version 2 is now available at , check it out if you want to help shape the next 15 years of the Go DNS package. -The version here will see no new features and less and less development. +The version here will see no new features and less and less development, and my time (if any) will be fully +devoted towards v2. + +**December 2025**: v2 should be (already) a good replacement, the coming months would be a good time to +migrate, see [this file describing the +differences](https://codeberg.org/miekg/dns/src/branch/main/README-diff-with-v1.md), to help you get started. # Alternative (more granular) approach to a DNS library @@ -62,7 +67,7 @@ A not-so-up-to-date-list-that-may-be-actually-current: - https://www.dnsperf.com/ - https://dnssectest.net/ - https://github.com/oif/apex -- https://github.com/jedisct1/dnscrypt-proxy +- https://github.com/jedisct1/dnscrypt-proxy (migrated to v2) - https://github.com/jedisct1/rpdns - https://github.com/xor-gate/sshfp - https://github.com/rs/dnstrace diff --git a/vendor/github.com/miekg/dns/client.go b/vendor/github.com/miekg/dns/client.go index 9549fa923..a0f96187b 100644 --- a/vendor/github.com/miekg/dns/client.go +++ b/vendor/github.com/miekg/dns/client.go @@ -57,8 +57,8 @@ type Client struct { // Client.Dialer) or context.Context.Deadline (see ExchangeContext) Timeout time.Duration DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds, or net.Dialer.Timeout if expiring earlier - overridden by Timeout when that value is non-zero - ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero - WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero + ReadTimeout time.Duration // net.Conn.SetReadDeadline value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero + WriteTimeout time.Duration // net.Conn.SetWriteDeadline value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2) TsigProvider TsigProvider // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations. @@ -92,6 +92,9 @@ func (c *Client) dialTimeout() time.Duration { } func (c *Client) readTimeout() time.Duration { + if c.Timeout != 0 { + return c.Timeout + } if c.ReadTimeout != 0 { return c.ReadTimeout } @@ -99,6 +102,9 @@ func (c *Client) readTimeout() time.Duration { } func (c *Client) writeTimeout() time.Duration { + if c.Timeout != 0 { + return c.Timeout + } if c.WriteTimeout != 0 { return c.WriteTimeout } diff --git a/vendor/github.com/miekg/dns/msg.go b/vendor/github.com/miekg/dns/msg.go index edf185961..382a80838 100644 --- a/vendor/github.com/miekg/dns/msg.go +++ b/vendor/github.com/miekg/dns/msg.go @@ -338,11 +338,13 @@ loop: return off + 2, nil } + // Trailing root label if off < len(msg) { msg[off] = 0 + return off + 1, nil } - return off + 1, nil + return off, ErrBuf } // isRootLabel returns whether s or bs, from off to end, is the root diff --git a/vendor/github.com/miekg/dns/scan.go b/vendor/github.com/miekg/dns/scan.go index 31957b2ea..f7c6525dd 100644 --- a/vendor/github.com/miekg/dns/scan.go +++ b/vendor/github.com/miekg/dns/scan.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "io/fs" + "math" "os" "path" "path/filepath" @@ -1231,7 +1232,7 @@ func typeToInt(token string) (uint16, bool) { // stringToTTL parses things like 2w, 2m, etc, and returns the time in seconds. func stringToTTL(token string) (uint32, bool) { - var s, i uint32 + var s, i uint for _, c := range token { switch c { case 's', 'S': @@ -1251,12 +1252,15 @@ func stringToTTL(token string) (uint32, bool) { i = 0 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': i *= 10 - i += uint32(c) - '0' + i += uint(c) - '0' default: return 0, false } } - return s + i, true + if s+i > math.MaxUint32 { + return 0, false + } + return uint32(s + i), true } // Parse LOC records' [.][mM] into a diff --git a/vendor/github.com/miekg/dns/version.go b/vendor/github.com/miekg/dns/version.go index c53a63d7c..33cb83e5a 100644 --- a/vendor/github.com/miekg/dns/version.go +++ b/vendor/github.com/miekg/dns/version.go @@ -3,7 +3,7 @@ package dns import "fmt" // Version is current version of this library. -var Version = v{1, 1, 69} +var Version = v{1, 1, 72} // v holds the version of this library. type v struct { diff --git a/vendor/github.com/moby/term/term_unix.go b/vendor/github.com/moby/term/term_unix.go index 2ec7706a1..579ce5530 100644 --- a/vendor/github.com/moby/term/term_unix.go +++ b/vendor/github.com/moby/term/term_unix.go @@ -81,7 +81,7 @@ func setRawTerminal(fd uintptr) (*State, error) { return makeRaw(fd) } -func setRawTerminalOutput(fd uintptr) (*State, error) { +func setRawTerminalOutput(uintptr) (*State, error) { return nil, nil } diff --git a/vendor/github.com/morikuni/aec/aec.go b/vendor/github.com/morikuni/aec/aec.go index 566be6eb1..3b1652a6f 100644 --- a/vendor/github.com/morikuni/aec/aec.go +++ b/vendor/github.com/morikuni/aec/aec.go @@ -129,8 +129,10 @@ func init() { All: 2, } - Save = newAnsi(esc + "s") - Restore = newAnsi(esc + "u") + // Save use both SCO (ESC[s) and DEC (ESC7) sequences as those were never standardised as part of the ANSI + Save = newAnsi(esc + "s" + "\x1b7") + // Restore use both SCO (ESC[u) and DEC (ESC8) and DEC sequences as those were never standardised as part of the ANSI + Restore = newAnsi(esc + "u" + "\x1b8") Hide = newAnsi(esc + "?25l") Show = newAnsi(esc + "?25h") Report = newAnsi(esc + "6n") diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/config.schema.yaml b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/config.schema.yaml new file mode 100644 index 000000000..13725d5df --- /dev/null +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/config.schema.yaml @@ -0,0 +1,7 @@ +type: object +properties: + max_stale: + type: string + format: duration + max_streams: + type: integer diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/doc.go b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/doc.go index c8f961f6b..e15497a6d 100644 --- a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/doc.go +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/doc.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -//go:generate mdatagen metadata.yaml +//go:generate make mdatagen // package deltatocumulativeprocessor implements a processor which // converts metrics from delta temporality to cumulative. diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data/expo/scale.go b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data/expo/scale.go index 50fdef75c..693860b55 100644 --- a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data/expo/scale.go +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data/expo/scale.go @@ -73,20 +73,27 @@ func Downscale(bs Buckets, from, to Scale) { // area at a later time. func Collapse(bs Buckets) { counts := bs.BucketCounts() + + offsetWasOdd := bs.Offset()%2 != 0 + shift := 0 + if offsetWasOdd { + shift-- + } + size := counts.Len() / 2 - if counts.Len()%2 != 0 || bs.Offset()%2 != 0 { + if counts.Len()%2 != 0 || offsetWasOdd { size++ } - // merging needs to happen in pairs aligned to i=0. if offset is non-even, - // we need to shift the whole merging by one to make above condition true. - shift := 0 - if bs.Offset()%2 != 0 { + if offsetWasOdd { bs.SetOffset(bs.Offset() - 1) - shift-- } bs.SetOffset(bs.Offset() / 2) + if counts.Len() == 0 { + return + } + for i := 0; i < size; i++ { // size is ~half of len. we add two buckets per iteration. // k jumps in steps of 2, shifted if offset makes this necessary. diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/metadata/generated_telemetry.go b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/metadata/generated_telemetry.go index 389891365..d6b91492a 100644 --- a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/metadata/generated_telemetry.go +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/metadata/generated_telemetry.go @@ -7,11 +7,10 @@ import ( "errors" "sync" + "go.opentelemetry.io/collector/component" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/embedded" "go.opentelemetry.io/otel/trace" - - "go.opentelemetry.io/collector/component" ) func Meter(settings component.TelemetrySettings) metric.Meter { diff --git a/vendor/github.com/pb33f/jsonpath/LICENSE b/vendor/github.com/pb33f/jsonpath/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/vendor/github.com/pb33f/jsonpath/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/config/config.go b/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/config/config.go new file mode 100644 index 000000000..8e6b8a237 --- /dev/null +++ b/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/config/config.go @@ -0,0 +1,49 @@ +package config + +type Option func(*config) + +// WithPropertyNameExtension enables the use of the "~" character to access a property key. +// It is not enabled by default as this is outside of RFC 9535, but is important for several use-cases +func WithPropertyNameExtension() Option { + return func(cfg *config) { + cfg.propertyNameExtension = true + } +} + +// WithStrictRFC9535 disables JSONPath Plus extensions and enforces strict RFC 9535 compliance. +// By default, JSONPath Plus extensions are enabled as they are a true superset of RFC 9535. +// Use this option if you need to ensure pure RFC 9535 compliance. +func WithStrictRFC9535() Option { + return func(cfg *config) { + cfg.strictRFC9535 = true + } +} + +type Config interface { + PropertyNameEnabled() bool + JSONPathPlusEnabled() bool +} + +type config struct { + propertyNameExtension bool + strictRFC9535 bool +} + +func (c *config) PropertyNameEnabled() bool { + return c.propertyNameExtension +} + +// JSONPathPlusEnabled returns true if JSONPath Plus extensions are enabled. +// JSONPath Plus is ON by default (true superset, no conflicts with RFC 9535). +// Returns false only if WithStrictRFC9535() was explicitly called. +func (c *config) JSONPathPlusEnabled() bool { + return !c.strictRFC9535 +} + +func New(opts ...Option) Config { + cfg := &config{} + for _, opt := range opts { + opt(cfg) + } + return cfg +} diff --git a/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/filter.go b/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/filter.go new file mode 100644 index 000000000..67afe7e8a --- /dev/null +++ b/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/filter.go @@ -0,0 +1,529 @@ +package jsonpath + +import ( + "go.yaml.in/yaml/v4" + "strconv" + "strings" +) + +// filter-selector = "?" S logical-expr +type filterSelector struct { + // logical-expr = logical-or-expr + expression *logicalOrExpr +} + +func (s filterSelector) ToString() string { + return s.expression.ToString() +} + +// logical-or-expr = logical-and-expr *(S "||" S logical-and-expr) +type logicalOrExpr struct { + expressions []*logicalAndExpr +} + +func (e logicalOrExpr) ToString() string { + builder := strings.Builder{} + for i, expr := range e.expressions { + if i > 0 { + builder.WriteString(" || ") + } + builder.WriteString(expr.ToString()) + } + return builder.String() +} + +// logical-and-expr = basic-expr *(S "&&" S basic-expr) +type logicalAndExpr struct { + expressions []*basicExpr +} + +func (e logicalAndExpr) ToString() string { + builder := strings.Builder{} + for i, expr := range e.expressions { + if i > 0 { + builder.WriteString(" && ") + } + builder.WriteString(expr.ToString()) + } + return builder.String() +} + +// relQuery rel-query = current-node-identifier segments +// current-node-identifier = "@" +type relQuery struct { + segments []*segment +} + +func (q relQuery) ToString() string { + builder := strings.Builder{} + builder.WriteString("@") + for _, segment := range q.segments { + builder.WriteString(segment.ToString()) + } + return builder.String() +} + +// filterQuery filter-query = rel-query / jsonpath-query +type filterQuery struct { + relQuery *relQuery + jsonPathQuery *jsonPathAST +} + +func (q filterQuery) ToString() string { + if q.relQuery != nil { + return q.relQuery.ToString() + } else if q.jsonPathQuery != nil { + return q.jsonPathQuery.ToString() + } + return "" +} + +// functionArgument function-argument = literal / +// +// filter-query / ; (includes singular-query) +// logical-expr / +// function-expr +type functionArgument struct { + literal *literal + filterQuery *filterQuery + logicalExpr *logicalOrExpr + functionExpr *functionExpr + contextVar *contextVariable // JSONPath Plus context variables +} + +type functionArgType int + +const ( + functionArgTypeLiteral functionArgType = iota + functionArgTypeNodes +) + +type resolvedArgument struct { + kind functionArgType + literal *literal + nodes []*literal +} + +func (a functionArgument) Eval(idx index, node *yaml.Node, root *yaml.Node) resolvedArgument { + if a.literal != nil { + return resolvedArgument{kind: functionArgTypeLiteral, literal: a.literal} + } else if a.filterQuery != nil { + result := a.filterQuery.Query(idx, node, root) + lits := make([]*literal, len(result)) + for i, node := range result { + lit := nodeToLiteral(node) + lits[i] = &lit + } + if len(result) != 1 { + return resolvedArgument{kind: functionArgTypeNodes, nodes: lits} + } else { + return resolvedArgument{kind: functionArgTypeLiteral, literal: lits[0]} + } + } else if a.logicalExpr != nil { + res := a.logicalExpr.Matches(idx, node, root) + return resolvedArgument{kind: functionArgTypeLiteral, literal: &literal{bool: &res}} + } else if a.functionExpr != nil { + res := a.functionExpr.Evaluate(idx, node, root) + return resolvedArgument{kind: functionArgTypeLiteral, literal: &res} + } else if a.contextVar != nil { + // Evaluate context variable and return as literal + res := a.contextVar.Evaluate(idx, node, root) + return resolvedArgument{kind: functionArgTypeLiteral, literal: &res} + } + return resolvedArgument{} +} + +func (a functionArgument) ToString() string { + builder := strings.Builder{} + if a.literal != nil { + builder.WriteString(a.literal.ToString()) + } else if a.filterQuery != nil { + builder.WriteString(a.filterQuery.ToString()) + } else if a.logicalExpr != nil { + builder.WriteString(a.logicalExpr.ToString()) + } else if a.functionExpr != nil { + builder.WriteString(a.functionExpr.ToString()) + } else if a.contextVar != nil { + builder.WriteString(a.contextVar.ToString()) + } + return builder.String() +} + +//function-name = function-name-first *function-name-char +//function-name-first = LCALPHA +//function-name-char = function-name-first / "_" / DIGIT +//LCALPHA = %x61-7A ; "a".."z" +// + +type functionType int + +const ( + functionTypeLength functionType = iota + functionTypeCount + functionTypeMatch + functionTypeSearch + functionTypeValue + // JSONPath Plus type selector functions + functionTypeIsNull + functionTypeIsBoolean + functionTypeIsNumber + functionTypeIsString + functionTypeIsArray + functionTypeIsObject + functionTypeIsInteger +) + +var functionTypeMap = map[string]functionType{ + "length": functionTypeLength, + "count": functionTypeCount, + "match": functionTypeMatch, + "search": functionTypeSearch, + "value": functionTypeValue, +} + +// typeSelectorFunctionMap maps JSONPath Plus type selector function names to their types. +// These are extensions enabled when JSONPath Plus mode is active. +var typeSelectorFunctionMap = map[string]functionType{ + "isNull": functionTypeIsNull, + "isBoolean": functionTypeIsBoolean, + "isNumber": functionTypeIsNumber, + "isString": functionTypeIsString, + "isArray": functionTypeIsArray, + "isObject": functionTypeIsObject, + "isInteger": functionTypeIsInteger, +} + +func (f functionType) String() string { + for k, v := range functionTypeMap { + if v == f { + return k + } + } + for k, v := range typeSelectorFunctionMap { + if v == f { + return k + } + } + return "unknown" +} + +// functionExpr function-expr = function-name "(" S [function-argument +// *(S "," S function-argument)] S ")" +type functionExpr struct { + funcType functionType + args []*functionArgument +} + +func (e functionExpr) ToString() string { + builder := strings.Builder{} + builder.WriteString(e.funcType.String()) + builder.WriteString("(") + for i, arg := range e.args { + if i > 0 { + builder.WriteString(", ") + } + builder.WriteString(arg.ToString()) + } + builder.WriteString(")") + return builder.String() +} + +// testExpr test-expr = [logical-not-op S] +// +// (filter-query / ; existence/non-existence +// function-expr) ; LogicalType or NodesType +type testExpr struct { + not bool + filterQuery *filterQuery + functionExpr *functionExpr +} + +func (e testExpr) ToString() string { + builder := strings.Builder{} + if e.not { + builder.WriteString("!") + } + if e.filterQuery != nil { + builder.WriteString(e.filterQuery.ToString()) + } else if e.functionExpr != nil { + builder.WriteString(e.functionExpr.ToString()) + } + return builder.String() +} + +// basicExpr basic-expr = +// +// paren-expr / +// comparison-expr / +// test-expr +type basicExpr struct { + parenExpr *parenExpr + comparisonExpr *comparisonExpr + testExpr *testExpr +} + +func (e basicExpr) ToString() string { + if e.parenExpr != nil { + return e.parenExpr.ToString() + } else if e.comparisonExpr != nil { + return e.comparisonExpr.ToString() + } else if e.testExpr != nil { + return e.testExpr.ToString() + } + return "" +} + +// literal literal = number / +// . string-literal / +// . true / false / null +type literal struct { + // we generally decompose these into their component parts for easier evaluation + integer *int + float64 *float64 + string *string + bool *bool + null *bool + node *yaml.Node +} + +func (l literal) ToString() string { + if l.integer != nil { + return strconv.Itoa(*l.integer) + } else if l.float64 != nil { + return strconv.FormatFloat(*l.float64, 'f', -1, 64) + } else if l.string != nil { + builder := strings.Builder{} + builder.WriteString("'") + builder.WriteString(escapeString(*l.string)) + builder.WriteString("'") + return builder.String() + } else if l.bool != nil { + if *l.bool { + return "true" + } else { + return "false" + } + } else if l.null != nil { + if *l.null { + return "null" + } else { + return "null" + } + } else if l.node != nil { + switch l.node.Kind { + case yaml.ScalarNode: + return l.node.Value + case yaml.SequenceNode: + builder := strings.Builder{} + builder.WriteString("[") + for i, child := range l.node.Content { + if i > 0 { + builder.WriteString(",") + } + builder.WriteString(literal{node: child}.ToString()) + } + builder.WriteString("]") + return builder.String() + case yaml.MappingNode: + builder := strings.Builder{} + builder.WriteString("{") + for i, child := range l.node.Content { + if i > 0 { + builder.WriteString(",") + } + builder.WriteString(literal{node: child}.ToString()) + } + builder.WriteString("}") + return builder.String() + } + } + return "" +} + +func escapeString(value string) string { + b := strings.Builder{} + for i := 0; i < len(value); i++ { + if value[i] == '\n' { + b.WriteString("\\\\n") + } else if value[i] == '\\' { + b.WriteString("\\\\") + } else if value[i] == '\'' { + b.WriteString("\\'") + } else { + b.WriteByte(value[i]) + } + } + return b.String() +} + +type absQuery jsonPathAST + +func (q absQuery) ToString() string { + builder := strings.Builder{} + builder.WriteString("$") + for _, segment := range q.segments { + builder.WriteString(segment.ToString()) + } + return builder.String() +} + +// singularQuery singular-query = rel-singular-query / abs-singular-query +type singularQuery struct { + relQuery *relQuery + absQuery *absQuery +} + +func (q singularQuery) ToString() string { + if q.relQuery != nil { + return q.relQuery.ToString() + } else if q.absQuery != nil { + return q.absQuery.ToString() + } + return "" +} + +// contextVarKind represents the type of context variable +type contextVarKind int + +const ( + contextVarProperty contextVarKind = iota // @property - current property name + contextVarRoot // @root - root node access + contextVarParent // @parent - parent node + contextVarParentProperty // @parentProperty - parent's property name + contextVarPath // @path - absolute path to current node + contextVarIndex // @index - current array index +) + +// contextVariable represents a JSONPath Plus context variable in filter expressions. +// These provide access to metadata about the current node being evaluated. +type contextVariable struct { + kind contextVarKind +} + +func (cv contextVariable) ToString() string { + switch cv.kind { + case contextVarProperty: + return "@property" + case contextVarRoot: + return "@root" + case contextVarParent: + return "@parent" + case contextVarParentProperty: + return "@parentProperty" + case contextVarPath: + return "@path" + case contextVarIndex: + return "@index" + default: + return "@unknown" + } +} + +// comparable +// +// comparable = literal / +// singular-query / ; singular query value +// function-expr ; ValueType +// context-variable ; JSONPath Plus extension +type comparable struct { + literal *literal + singularQuery *singularQuery + functionExpr *functionExpr + contextVar *contextVariable // JSONPath Plus extension +} + +func (c comparable) ToString() string { + if c.literal != nil { + return c.literal.ToString() + } else if c.singularQuery != nil { + return c.singularQuery.ToString() + } else if c.functionExpr != nil { + return c.functionExpr.ToString() + } else if c.contextVar != nil { + return c.contextVar.ToString() + } + return "" +} + +// comparisonExpr represents a comparison expression +// +// comparison-expr = comparable S comparison-op S comparable +// literal = number / string-literal / +// true / false / null +// comparable = literal / +// singular-query / ; singular query value +// function-expr ; ValueType +// comparison-op = "==" / "!=" / +// "<=" / ">=" / +// "<" / ">" +type comparisonExpr struct { + left *comparable + op comparisonOperator + right *comparable +} + +func (e comparisonExpr) ToString() string { + builder := strings.Builder{} + builder.WriteString(e.left.ToString()) + builder.WriteString(" ") + builder.WriteString(e.op.ToString()) + builder.WriteString(" ") + builder.WriteString(e.right.ToString()) + return builder.String() +} + +// existExpr represents an existence expression +type existExpr struct { + query string +} + +// parenExpr represents a parenthesized expression +// +// paren-expr = [logical-not-op S] "(" S logical-expr S ")" +type parenExpr struct { + // "!" + not bool + // "(" logicalOrExpr ")" + expr *logicalOrExpr +} + +func (e parenExpr) ToString() string { + builder := strings.Builder{} + if e.not { + builder.WriteString("!") + } + builder.WriteString("(") + builder.WriteString(e.expr.ToString()) + builder.WriteString(")") + return builder.String() +} + +// comparisonOperator represents a comparison operator +type comparisonOperator int + +const ( + equalTo comparisonOperator = iota + notEqualTo + lessThan + lessThanEqualTo + greaterThan + greaterThanEqualTo +) + +func (o comparisonOperator) ToString() string { + switch o { + case equalTo: + return "==" + case notEqualTo: + return "!=" + case lessThan: + return "<" + case lessThanEqualTo: + return "<=" + case greaterThan: + return ">" + case greaterThanEqualTo: + return ">=" + } + return "" +} diff --git a/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/filter_context.go b/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/filter_context.go new file mode 100644 index 000000000..f698f5c36 --- /dev/null +++ b/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/filter_context.go @@ -0,0 +1,245 @@ +package jsonpath + +import ( + "strconv" + "strings" + + "go.yaml.in/yaml/v4" +) + +// FilterContext provides rich context during filter evaluation for JSONPath Plus extensions. +type FilterContext interface { + index + + PropertyName() string + SetPropertyName(name string) + + Parent() *yaml.Node + SetParent(parent *yaml.Node) + + ParentPropertyName() string + SetParentPropertyName(name string) + + Path() string + PushPathSegment(segment string) + PopPathSegment() + + // SetPendingPathSegment stores a path segment for a node (used by wildcards/slices) + SetPendingPathSegment(node *yaml.Node, segment string) + // GetAndClearPendingPathSegment retrieves and removes a pending path segment for a node + GetAndClearPendingPathSegment(node *yaml.Node) string + + // SetPendingPropertyName stores a property name for a node (used by wildcards for @parentProperty) + SetPendingPropertyName(node *yaml.Node, name string) + // GetAndClearPendingPropertyName retrieves and removes a pending property name for a node + GetAndClearPendingPropertyName(node *yaml.Node) string + + Root() *yaml.Node + SetRoot(root *yaml.Node) + + Index() int + SetIndex(idx int) + + // EnableParentTracking enables parent node tracking (for ^ and @parent) + EnableParentTracking() + // ParentTrackingEnabled returns true if parent tracking is active + ParentTrackingEnabled() bool + + Clone() FilterContext +} + +// filterContext is the concrete implementation of FilterContext +type filterContext struct { + _index + + propertyName string + parent *yaml.Node + parentPropertyName string + pathSegments []string + pendingPathSegments map[*yaml.Node]string // tracks path segments for nodes from wildcards/slices + pendingPropertyNames map[*yaml.Node]string // tracks property names for nodes from wildcards (for @parentProperty) + root *yaml.Node + arrayIndex int + parentTrackingActive bool +} + +// NewFilterContext creates a new FilterContext with the given root node +func NewFilterContext(root *yaml.Node) FilterContext { + return &filterContext{ + _index: _index{ + propertyKeys: make(map[*yaml.Node]*yaml.Node), + parentNodes: make(map[*yaml.Node]*yaml.Node), + }, + pathSegments: make([]string, 0), + pendingPathSegments: make(map[*yaml.Node]string), + pendingPropertyNames: make(map[*yaml.Node]string), + root: root, + arrayIndex: -1, + } +} + +// PropertyName returns the current property name or array index as string +func (fc *filterContext) PropertyName() string { + return fc.propertyName +} + +// SetPropertyName sets the current property name +func (fc *filterContext) SetPropertyName(name string) { + fc.propertyName = name +} + +// Parent returns the parent node +func (fc *filterContext) Parent() *yaml.Node { + return fc.parent +} + +// SetParent sets the parent node +func (fc *filterContext) SetParent(parent *yaml.Node) { + fc.parent = parent +} + +// ParentPropertyName returns the parent's property name +func (fc *filterContext) ParentPropertyName() string { + return fc.parentPropertyName +} + +// SetParentPropertyName sets the parent's property name +func (fc *filterContext) SetParentPropertyName(name string) { + fc.parentPropertyName = name +} + +// Path returns the normalized JSONPath to the current node +func (fc *filterContext) Path() string { + if len(fc.pathSegments) == 0 { + return "$" + } + return "$" + strings.Join(fc.pathSegments, "") +} + +// PushPathSegment adds a path segment (should be in normalized form like "['key']" or "[0]") +func (fc *filterContext) PushPathSegment(segment string) { + fc.pathSegments = append(fc.pathSegments, segment) +} + +// PopPathSegment removes the last path segment +func (fc *filterContext) PopPathSegment() { + if len(fc.pathSegments) > 0 { + fc.pathSegments = fc.pathSegments[:len(fc.pathSegments)-1] + } +} + +// SetPendingPathSegment stores a path segment for a node (used by wildcards/slices) +func (fc *filterContext) SetPendingPathSegment(node *yaml.Node, segment string) { + if fc.pendingPathSegments != nil { + fc.pendingPathSegments[node] = segment + } +} + +// GetAndClearPendingPathSegment retrieves and removes a pending path segment for a node +func (fc *filterContext) GetAndClearPendingPathSegment(node *yaml.Node) string { + if fc.pendingPathSegments == nil { + return "" + } + segment, ok := fc.pendingPathSegments[node] + if ok { + delete(fc.pendingPathSegments, node) + return segment + } + return "" +} + +// SetPendingPropertyName stores a property name for a node (used by wildcards for @parentProperty) +func (fc *filterContext) SetPendingPropertyName(node *yaml.Node, name string) { + if fc.pendingPropertyNames != nil { + fc.pendingPropertyNames[node] = name + } +} + +// GetAndClearPendingPropertyName retrieves and removes a pending property name for a node +func (fc *filterContext) GetAndClearPendingPropertyName(node *yaml.Node) string { + if fc.pendingPropertyNames == nil { + return "" + } + name, ok := fc.pendingPropertyNames[node] + if ok { + delete(fc.pendingPropertyNames, node) + return name + } + return "" +} + +// Root returns the root node for @root access +func (fc *filterContext) Root() *yaml.Node { + return fc.root +} + +// SetRoot sets the root node +func (fc *filterContext) SetRoot(root *yaml.Node) { + fc.root = root +} + +// Index returns the current array index (-1 if not in array context) +func (fc *filterContext) Index() int { + return fc.arrayIndex +} + +// SetIndex sets the current array index +func (fc *filterContext) SetIndex(idx int) { + fc.arrayIndex = idx +} + +// EnableParentTracking enables parent node tracking for ^ and @parent +func (fc *filterContext) EnableParentTracking() { + fc.parentTrackingActive = true +} + +// ParentTrackingEnabled returns true if parent tracking is active +func (fc *filterContext) ParentTrackingEnabled() bool { + return fc.parentTrackingActive +} + +// Clone creates a shallow copy of the context for nested evaluation +func (fc *filterContext) Clone() FilterContext { + pathCopy := make([]string, len(fc.pathSegments)) + copy(pathCopy, fc.pathSegments) + + // Share the pending maps - they're cleared on use anyway + return &filterContext{ + _index: fc._index, + propertyName: fc.propertyName, + parent: fc.parent, + parentPropertyName: fc.parentPropertyName, + pathSegments: pathCopy, + pendingPathSegments: fc.pendingPathSegments, + pendingPropertyNames: fc.pendingPropertyNames, + root: fc.root, + arrayIndex: fc.arrayIndex, + parentTrackingActive: fc.parentTrackingActive, + } +} + +// Helper function to create a normalized path segment for a property name +func normalizePathSegment(name string) string { + return "['" + escapePathSegment(name) + "']" +} + +// Helper function to create a normalized path segment for an array index +func normalizeIndexSegment(idx int) string { + return "[" + strconv.Itoa(idx) + "]" +} + +// escapePathSegment escapes special characters in path segment names +func escapePathSegment(s string) string { + var b strings.Builder + for _, r := range s { + switch r { + case '\'': + b.WriteString("\\'") + case '\\': + b.WriteString("\\\\") + default: + b.WriteRune(r) + } + } + return b.String() +} diff --git a/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/jsonpath.go b/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/jsonpath.go new file mode 100644 index 000000000..229dbebb2 --- /dev/null +++ b/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/jsonpath.go @@ -0,0 +1,35 @@ +package jsonpath + +import ( + "fmt" + "github.com/pb33f/jsonpath/pkg/jsonpath/config" + "github.com/pb33f/jsonpath/pkg/jsonpath/token" + "go.yaml.in/yaml/v4" +) + +func NewPath(input string, opts ...config.Option) (*JSONPath, error) { + tokenizer := token.NewTokenizer(input, opts...) + tokens := tokenizer.Tokenize() + for i := 0; i < len(tokens); i++ { + if tokens[i].Token == token.ILLEGAL { + return nil, fmt.Errorf("%s", tokenizer.ErrorString(&tokens[i], "unexpected token")) + } + } + parser := newParserPrivate(tokenizer, tokens, opts...) + err := parser.parse() + if err != nil { + return nil, err + } + return parser, nil +} + +func (p *JSONPath) Query(root *yaml.Node) []*yaml.Node { + return p.ast.Query(root, root) +} + +func (p *JSONPath) String() string { + if p == nil { + return "" + } + return p.ast.ToString() +} diff --git a/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/parser.go b/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/parser.go new file mode 100644 index 000000000..d9b341bd6 --- /dev/null +++ b/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/parser.go @@ -0,0 +1,789 @@ +package jsonpath + +import ( + "errors" + "fmt" + "github.com/pb33f/jsonpath/pkg/jsonpath/config" + "github.com/pb33f/jsonpath/pkg/jsonpath/token" + "strconv" + "strings" +) + +const MaxSafeFloat int64 = 9007199254740991 + +type mode int + +const ( + modeNormal mode = iota + modeSingular +) + +// contextVarTokenMap maps context variable tokens to their kinds +// CONTEXT_ROOT is handled separately as it requires path parsing +var contextVarTokenMap = map[token.Token]contextVarKind{ + token.CONTEXT_PROPERTY: contextVarProperty, + token.CONTEXT_PARENT: contextVarParent, + token.CONTEXT_PARENT_PROPERTY: contextVarParentProperty, + token.CONTEXT_PATH: contextVarPath, + token.CONTEXT_INDEX: contextVarIndex, +} + +// JSONPath represents a JSONPath parser. +type JSONPath struct { + tokenizer *token.Tokenizer + tokens []token.TokenInfo + ast jsonPathAST + current int + mode []mode + config config.Config +} + +// newParserPrivate creates a new JSONPath with the given tokens. +func newParserPrivate(tokenizer *token.Tokenizer, tokens []token.TokenInfo, opts ...config.Option) *JSONPath { + return &JSONPath{tokenizer, tokens, jsonPathAST{}, 0, []mode{modeNormal}, config.New(opts...)} +} + +// parse parses the JSONPath tokens and returns the root node of the AST. +// +// jsonpath-query = root-identifier segments +func (p *JSONPath) parse() error { + if len(p.tokens) == 0 { + return fmt.Errorf("empty JSONPath expression") + } + + if p.tokens[p.current].Token != token.ROOT { + return p.parseFailure(&p.tokens[p.current], "expected '$'") + } + p.current++ + + for p.current < len(p.tokens) { + segment, err := p.parseSegment() + if err != nil { + return err + } + p.ast.segments = append(p.ast.segments, segment) + } + return nil +} + +func (p *JSONPath) parseFailure(target *token.TokenInfo, msg string) error { + return errors.New(p.tokenizer.ErrorString(target, msg)) +} + +// peek returns true if the upcoming token matches the given token type. +func (p *JSONPath) peek(token token.Token) bool { + return p.current+1 < len(p.tokens) && p.tokens[p.current+1].Token == token +} + +// peek returns true if the upcoming token matches the given token type. +func (p *JSONPath) next(token token.Token) bool { + return p.current < len(p.tokens) && p.tokens[p.current].Token == token +} + +// expect consumes the current token if it matches the given token type. +func (p *JSONPath) expect(token token.Token) bool { + if p.peek(token) { + p.current++ + return true + } + return false +} + +// isComparisonOperator returns true if the given token is a comparison operator. +func (p *JSONPath) isComparisonOperator(tok token.Token) bool { + return tok == token.EQ || tok == token.NE || tok == token.GT || tok == token.GE || tok == token.LT || tok == token.LE +} + +func (p *JSONPath) parseSegment() (*segment, error) { + currentToken := p.tokens[p.current] + if currentToken.Token == token.RECURSIVE { + if p.mode[len(p.mode)-1] == modeSingular { + return nil, p.parseFailure(&p.tokens[p.current], "unexpected recursive descent in singular query") + } + p.current++ + child, err := p.parseInnerSegment() + if err != nil { + return nil, err + } + return &segment{kind: segmentKindDescendant, descendant: child}, nil + } else if currentToken.Token == token.CHILD || currentToken.Token == token.BRACKET_LEFT { + if currentToken.Token == token.CHILD { + p.current++ + } + child, err := p.parseInnerSegment() + if err != nil { + return nil, err + } + return &segment{kind: segmentKindChild, child: child}, nil + } else if p.config.PropertyNameEnabled() && currentToken.Token == token.PROPERTY_NAME { + p.current++ + return &segment{kind: segmentKindProperyName}, nil + } else if p.config.JSONPathPlusEnabled() && currentToken.Token == token.PARENT_SELECTOR { + // JSONPath Plus parent selector: ^ returns parent of current node + p.current++ + return &segment{kind: segmentKindParent}, nil + } + return nil, p.parseFailure(¤tToken, "unexpected token when parsing segment") +} + +func (p *JSONPath) parseInnerSegment() (retValue *innerSegment, err error) { + defer func() { + if p.mode[len(p.mode)-1] == modeSingular && retValue != nil { + if len(retValue.selectors) > 1 { + retValue = nil + err = p.parseFailure(&p.tokens[p.current], "unexpected multiple selectors in singular query") + return + } else if retValue.kind == segmentDotWildcard { + retValue = nil + err = p.parseFailure(&p.tokens[p.current], "unexpected wildcard in singular query") + return + } + } + }() + // .* + // .STRING + // [] + if p.current >= len(p.tokens) { + return nil, p.parseFailure(nil, "unexpected end of input") + } + firstToken := p.tokens[p.current] + if firstToken.Token == token.WILDCARD { + p.current += 1 + return &innerSegment{segmentDotWildcard, "", nil}, nil + } else if firstToken.Token == token.STRING { + dotName := p.tokens[p.current].Literal + p.current += 1 + return &innerSegment{segmentDotMemberName, dotName, nil}, nil + } else if firstToken.Token == token.BRACKET_LEFT { + prior := p.current + p.current += 1 + selectors := []*selector{} + for p.current < len(p.tokens) { + innerSelector, err := p.parseSelector() + if err != nil { + p.current = prior + return nil, err + } + selectors = append(selectors, innerSelector) + if len(p.tokens) <= p.current { + return nil, p.parseFailure(&p.tokens[p.current-1], "unexpected end of input") + } + if p.tokens[p.current].Token == token.BRACKET_RIGHT { + break + } else if p.tokens[p.current].Token == token.COMMA { + p.current++ + } + } + if p.tokens[p.current].Token != token.BRACKET_RIGHT { + prior = p.current + return nil, p.parseFailure(&p.tokens[p.current], "expected ']'") + } + p.current += 1 + return &innerSegment{kind: segmentLongHand, dotName: "", selectors: selectors}, nil + } + return nil, p.parseFailure(&firstToken, "unexpected token when parsing inner segment") +} + +func (p *JSONPath) parseSelector() (retSelector *selector, err error) { + //selector = name-selector / + // wildcard-selector / + // slice-selector / + // index-selector / + // filter-selector + initial := p.current + defer func() { + if p.mode[len(p.mode)-1] == modeSingular && retSelector != nil { + if retSelector.kind == selectorSubKindWildcard { + err = p.parseFailure(&p.tokens[initial], "unexpected wildcard in singular query") + retSelector = nil + } else if retSelector.kind == selectorSubKindArraySlice { + err = p.parseFailure(&p.tokens[initial], "unexpected slice in singular query") + retSelector = nil + } + } + }() + + // name-selector = string-literal + if p.tokens[p.current].Token == token.STRING_LITERAL { + name := p.tokens[p.current].Literal + p.current++ + return &selector{kind: selectorSubKindName, name: name}, nil + // wildcard-selector = "*" + } else if p.tokens[p.current].Token == token.WILDCARD { + p.current++ + return &selector{kind: selectorSubKindWildcard}, nil + } else if p.tokens[p.current].Token == token.INTEGER { + // peek ahead to see if it's a slice + if p.peek(token.ARRAY_SLICE) { + slice, err := p.parseSliceSelector() + if err != nil { + return nil, err + } + return &selector{kind: selectorSubKindArraySlice, slice: slice}, nil + } + // peek ahead to see if we close the array index properly + if !p.peek(token.BRACKET_RIGHT) && !p.peek(token.COMMA) { + return nil, p.parseFailure(&p.tokens[p.current], "expected ']' or ','") + } + // else it's an index + lit := p.tokens[p.current].Literal + // make sure it's not -0 + if lit == "-0" { + return nil, p.parseFailure(&p.tokens[p.current], "-0 unexpected") + } + // make sure lit is an integer + i, err := strconv.ParseInt(lit, 10, 64) + if err != nil { + return nil, p.parseFailure(&p.tokens[p.current], "expected an integer") + } + err = p.checkSafeInteger(i, lit) + if err != nil { + return nil, err + } + + p.current++ + + return &selector{kind: selectorSubKindArrayIndex, index: i}, nil + } else if p.tokens[p.current].Token == token.ARRAY_SLICE { + slice, err := p.parseSliceSelector() + if err != nil { + return nil, err + } + return &selector{kind: selectorSubKindArraySlice, slice: slice}, nil + } else if p.tokens[p.current].Token == token.FILTER { + return p.parseFilterSelector() + } + + return nil, p.parseFailure(&p.tokens[p.current], "unexpected token when parsing selector") +} + +func (p *JSONPath) parseSliceSelector() (*slice, error) { + // slice-selector = [start S] ":" S [end S] [":" [S step]] + var start, end, step *int64 + + // parse the start index + if p.tokens[p.current].Token == token.INTEGER { + literal := p.tokens[p.current].Literal + i, err := strconv.ParseInt(literal, 10, 64) + if err != nil { + return nil, p.parseFailure(&p.tokens[p.current], "expected an integer") + } + err = p.checkSafeInteger(i, literal) + if err != nil { + return nil, err + } + + start = &i + p.current += 1 + } + + // Expect a colon + if p.tokens[p.current].Token != token.ARRAY_SLICE { + return nil, p.parseFailure(&p.tokens[p.current], "expected ':'") + } + p.current++ + + // parse the end index + if p.tokens[p.current].Token == token.INTEGER { + literal := p.tokens[p.current].Literal + i, err := strconv.ParseInt(literal, 10, 64) + if err != nil { + return nil, p.parseFailure(&p.tokens[p.current], "expected an integer") + } + err = p.checkSafeInteger(i, literal) + if err != nil { + return nil, err + } + + end = &i + p.current++ + } + + // Check for an optional second colon and step value + if p.tokens[p.current].Token == token.ARRAY_SLICE { + p.current++ + if p.tokens[p.current].Token == token.INTEGER { + literal := p.tokens[p.current].Literal + i, err := strconv.ParseInt(literal, 10, 64) + if err != nil { + return nil, p.parseFailure(&p.tokens[p.current], "expected an integer") + } + err = p.checkSafeInteger(i, literal) + if err != nil { + return nil, err + } + + step = &i + p.current++ + } + } + if p.tokens[p.current].Token != token.BRACKET_RIGHT { + return nil, p.parseFailure(&p.tokens[p.current], "expected ']'") + } + + return &slice{start: start, end: end, step: step}, nil +} + +func (p *JSONPath) checkSafeInteger(i int64, literal string) error { + if i > MaxSafeFloat || i < -MaxSafeFloat { + return p.parseFailure(&p.tokens[p.current], "outside bounds for safe integers") + } + if literal == "-0" { + return p.parseFailure(&p.tokens[p.current], "-0 unexpected") + } + return nil +} + +func (p *JSONPath) parseFilterSelector() (*selector, error) { + + if p.tokens[p.current].Token != token.FILTER { + return nil, p.parseFailure(&p.tokens[p.current], "expected '?'") + } + p.current++ + + expr, err := p.parseLogicalOrExpr() + if err != nil { + return nil, err + } + + return &selector{kind: selectorSubKindFilter, filter: &filterSelector{expr}}, nil +} + +func (p *JSONPath) parseLogicalOrExpr() (*logicalOrExpr, error) { + var expr logicalOrExpr + + for { + andExpr, err := p.parseLogicalAndExpr() + if err != nil { + return nil, err + } + expr.expressions = append(expr.expressions, andExpr) + + if !p.next(token.OR) { + break + } + p.current++ + } + + return &expr, nil +} + +func (p *JSONPath) parseLogicalAndExpr() (*logicalAndExpr, error) { + var expr logicalAndExpr + + for { + basicExpr, err := p.parseBasicExpr() + if err != nil { + return nil, err + } + expr.expressions = append(expr.expressions, basicExpr) + + if !p.next(token.AND) { + break + } + p.current++ + } + + return &expr, nil +} + +func (p *JSONPath) parseBasicExpr() (*basicExpr, error) { + //basic-expr = paren-expr / + // comparison-expr / + // test-expr + + switch p.tokens[p.current].Token { + case token.NOT: + p.current++ + expr, err := p.parseLogicalOrExpr() + if err != nil { + return nil, err + } + // Inspect if the expr is topped by a parenExpr -- if so we can simplify + if len(expr.expressions) == 1 && len(expr.expressions[0].expressions) == 1 && expr.expressions[0].expressions[0].parenExpr != nil { + child := expr.expressions[0].expressions[0].parenExpr + child.not = !child.not + return &basicExpr{parenExpr: child}, nil + } + return &basicExpr{parenExpr: &parenExpr{not: true, expr: expr}}, nil + case token.PAREN_LEFT: + p.current++ + expr, err := p.parseLogicalOrExpr() + if err != nil { + return nil, err + } + if p.tokens[p.current].Token != token.PAREN_RIGHT { + return nil, p.parseFailure(&p.tokens[p.current], "expected ')'") + } + p.current++ + return &basicExpr{parenExpr: &parenExpr{not: false, expr: expr}}, nil + } + prevCurrent := p.current + comparisonExpr, comparisonErr := p.parseComparisonExpr() + if comparisonErr == nil { + return &basicExpr{comparisonExpr: comparisonExpr}, nil + } + p.current = prevCurrent + testExpr, testErr := p.parseTestExpr() + if testErr == nil { + return &basicExpr{testExpr: testExpr}, nil + } + p.current = prevCurrent + return nil, p.parseFailure(&p.tokens[p.current], fmt.Sprintf("could not parse query: expected either testExpr [err: %s] or comparisonExpr: [err: %s]", testErr.Error(), comparisonErr.Error())) +} + +func (p *JSONPath) parseComparisonExpr() (*comparisonExpr, error) { + left, err := p.parseComparable() + if err != nil { + return nil, err + } + + if !p.isComparisonOperator(p.tokens[p.current].Token) { + return nil, p.parseFailure(&p.tokens[p.current], "expected comparison operator") + } + operator := p.tokens[p.current].Token + var op comparisonOperator + switch operator { + case token.EQ: + op = equalTo + case token.NE: + op = notEqualTo + case token.LT: + op = lessThan + case token.LE: + op = lessThanEqualTo + case token.GT: + op = greaterThan + case token.GE: + op = greaterThanEqualTo + default: + return nil, p.parseFailure(&p.tokens[p.current], "expected comparison operator") + } + p.current++ + + right, err := p.parseComparable() + if err != nil { + return nil, err + } + + return &comparisonExpr{left: left, op: op, right: right}, nil +} + +func (p *JSONPath) parseComparable() (*comparable, error) { + // comparable = literal / + // singular-query / ; singular query value + // function-expr ; ValueType + // context-variable ; JSONPath Plus extension + if literal, err := p.parseLiteral(); err == nil { + return &comparable{literal: literal}, nil + } + if funcExpr, err := p.parseFunctionExpr(); err == nil { + if funcExpr.funcType == functionTypeMatch { + return nil, p.parseFailure(&p.tokens[p.current], "match result cannot be compared") + } else if funcExpr.funcType == functionTypeSearch { + return nil, p.parseFailure(&p.tokens[p.current], "search result cannot be compared") + } + return &comparable{functionExpr: funcExpr}, nil + } + switch p.tokens[p.current].Token { + case token.ROOT: + p.current++ + query, err := p.parseSingleQuery() + if err != nil { + return nil, err + } + return &comparable{singularQuery: &singularQuery{absQuery: &absQuery{segments: query.segments}}}, nil + case token.CURRENT: + p.current++ + query, err := p.parseSingleQuery() + if err != nil { + return nil, err + } + return &comparable{singularQuery: &singularQuery{relQuery: &relQuery{segments: query.segments}}}, nil + + case token.CONTEXT_ROOT: + // @root followed by a path - parse as a query starting from root + p.current++ + query, err := p.parseSingleQuery() + if err != nil { + return nil, err + } + return &comparable{singularQuery: &singularQuery{absQuery: &absQuery{segments: query.segments}}}, nil + + default: + // Check for JSONPath Plus context variables + if varKind, ok := contextVarTokenMap[p.tokens[p.current].Token]; ok { + p.current++ + return &comparable{contextVar: &contextVariable{kind: varKind}}, nil + } + return nil, p.parseFailure(&p.tokens[p.current], "expected literal or query") + } +} + +func (p *JSONPath) parseQuery() (*jsonPathAST, error) { + var query jsonPathAST + p.mode = append(p.mode, modeNormal) + + for p.current < len(p.tokens) { + prior := p.current + segment, err := p.parseSegment() + if err != nil { + p.current = prior + break + } + query.segments = append(query.segments, segment) + } + p.mode = p.mode[:len(p.mode)-1] + return &query, nil +} + +func (p *JSONPath) parseTestExpr() (*testExpr, error) { + //test-expr = [logical-not-op S] + // (filter-query / ; existence/non-existence + // function-expr) ; LogicalType or NodesType + //filter-query = rel-query / jsonpath-query + //rel-query = current-node-identifier segments + //current-node-identifier = "@" + not := false + if p.tokens[p.current].Token == token.NOT { + not = true + p.current++ + } + switch p.tokens[p.current].Token { + case token.CURRENT: + p.current++ + query, err := p.parseQuery() + if err != nil { + return nil, err + } + return &testExpr{filterQuery: &filterQuery{relQuery: &relQuery{segments: query.segments}}, not: not}, nil + case token.ROOT: + p.current++ + query, err := p.parseQuery() + if err != nil { + return nil, err + } + return &testExpr{filterQuery: &filterQuery{jsonPathQuery: &jsonPathAST{segments: query.segments}}, not: not}, nil + default: + funcExpr, err := p.parseFunctionExpr() + if err != nil { + return nil, err + } + if funcExpr.funcType == functionTypeCount { + return nil, p.parseFailure(&p.tokens[p.current], "count function must be compared") + } + if funcExpr.funcType == functionTypeLength { + return nil, p.parseFailure(&p.tokens[p.current], "length function must be compared") + } + if funcExpr.funcType == functionTypeValue { + return nil, p.parseFailure(&p.tokens[p.current], "length function must be compared") + } + return &testExpr{functionExpr: funcExpr, not: not}, nil + } + + return nil, p.parseFailure(&p.tokens[p.current], "unexpected token when parsing test expression") +} + +func (p *JSONPath) parseFunctionExpr() (*functionExpr, error) { + // RFC 9535: function name must be immediately followed by '(' (no whitespace) + // The tokenizer only emits FUNCTION token when function name is directly followed by '(' + if p.tokens[p.current].Token != token.FUNCTION { + return nil, p.parseFailure(&p.tokens[p.current], "expected function") + } + functionName := p.tokens[p.current].Literal + if p.current+1 >= len(p.tokens) || p.tokens[p.current+1].Token != token.PAREN_LEFT { + return nil, p.parseFailure(&p.tokens[p.current], "expected '(' after function") + } + p.current += 2 + args := []*functionArgument{} + + // Check type selector functions first (JSONPath Plus) + // These take a single argument and return boolean + if funcType, ok := typeSelectorFunctionMap[functionName]; ok { + arg, err := p.parseFunctionArgument(false) + if err != nil { + return nil, err + } + args = append(args, arg) + if p.tokens[p.current].Token != token.PAREN_RIGHT { + return nil, p.parseFailure(&p.tokens[p.current], "expected ')'") + } + p.current++ + return &functionExpr{funcType: funcType, args: args}, nil + } + + switch functionTypeMap[functionName] { + case functionTypeLength: + arg, err := p.parseFunctionArgument(true) + if err != nil { + return nil, err + } + args = append(args, arg) + case functionTypeCount: + arg, err := p.parseFunctionArgument(false) + if err != nil { + return nil, err + } + if arg.literal != nil && arg.literal.node == nil { + return nil, p.parseFailure(&p.tokens[p.current], "count function only supports containers") + } + args = append(args, arg) + case functionTypeValue: + arg, err := p.parseFunctionArgument(false) + if err != nil { + return nil, err + } + args = append(args, arg) + case functionTypeMatch: + fallthrough + case functionTypeSearch: + arg, err := p.parseFunctionArgument(false) + if err != nil { + return nil, err + } + args = append(args, arg) + if p.tokens[p.current].Token != token.COMMA { + return nil, p.parseFailure(&p.tokens[p.current], "expected ','") + } + p.current++ + arg, err = p.parseFunctionArgument(false) + if err != nil { + return nil, err + } + args = append(args, arg) + default: + return nil, p.parseFailure(&p.tokens[p.current], "unknown function: "+functionName) + } + if p.tokens[p.current].Token != token.PAREN_RIGHT { + return nil, p.parseFailure(&p.tokens[p.current], "expected ')'") + } + p.current++ + return &functionExpr{funcType: functionTypeMap[functionName], args: args}, nil +} + +func (p *JSONPath) parseSingleQuery() (*jsonPathAST, error) { + var query jsonPathAST + for p.current < len(p.tokens) { + try := p.current + p.mode = append(p.mode, modeSingular) + segment, err := p.parseSegment() + if err != nil { + // rollback + p.mode = p.mode[:len(p.mode)-1] + p.current = try + break + } + p.mode = p.mode[:len(p.mode)-1] + query.segments = append(query.segments, segment) + } + //if len(query.segments) == 0 { + // return nil, p.parseFailure(p.tokens[p.current], "expected at least one segment") + //} + return &query, nil +} + +func (p *JSONPath) parseFunctionArgument(single bool) (*functionArgument, error) { + //function-argument = literal / + // filter-query / ; (includes singular-query) + // logical-expr / + // function-expr + + if lit, err := p.parseLiteral(); err == nil { + return &functionArgument{literal: lit}, nil + } + switch p.tokens[p.current].Token { + case token.CURRENT: + p.current++ + var query *jsonPathAST + var err error + if single { + query, err = p.parseSingleQuery() + } else { + query, err = p.parseQuery() + } + if err != nil { + return nil, err + } + return &functionArgument{filterQuery: &filterQuery{relQuery: &relQuery{segments: query.segments}}}, nil + case token.ROOT: + p.current++ + var query *jsonPathAST + var err error + if single { + query, err = p.parseSingleQuery() + } else { + query, err = p.parseQuery() + } + if err != nil { + return nil, err + } + return &functionArgument{filterQuery: &filterQuery{jsonPathQuery: &jsonPathAST{segments: query.segments}}}, nil + } + + // Check for JSONPath Plus context variables as function arguments + if varKind, ok := contextVarTokenMap[p.tokens[p.current].Token]; ok { + p.current++ + return &functionArgument{contextVar: &contextVariable{kind: varKind}}, nil + } + + if expr, err := p.parseLogicalOrExpr(); err == nil { + return &functionArgument{logicalExpr: expr}, nil + } + if funcExpr, err := p.parseFunctionExpr(); err == nil { + return &functionArgument{functionExpr: funcExpr}, nil + } + + return nil, p.parseFailure(&p.tokens[p.current], "unexpected token for function argument") +} + +func (p *JSONPath) parseLiteral() (*literal, error) { + switch p.tokens[p.current].Token { + case token.STRING_LITERAL: + lit := p.tokens[p.current].Literal + p.current++ + return &literal{string: &lit}, nil + case token.INTEGER: + lit := p.tokens[p.current].Literal + p.current++ + i, err := strconv.Atoi(lit) + if err != nil { + return nil, p.parseFailure(&p.tokens[p.current], "expected integer") + } + return &literal{integer: &i}, nil + case token.FLOAT: + lit := p.tokens[p.current].Literal + p.current++ + f, err := strconv.ParseFloat(lit, 64) + if err != nil { + return nil, p.parseFailure(&p.tokens[p.current], "expected float") + } + return &literal{float64: &f}, nil + case token.TRUE: + p.current++ + res := true + return &literal{bool: &res}, nil + case token.FALSE: + p.current++ + res := false + return &literal{bool: &res}, nil + case token.NULL: + p.current++ + res := true + return &literal{null: &res}, nil + } + return nil, p.parseFailure(&p.tokens[p.current], "expected literal") +} + +type jsonPathAST struct { + // "$" + segments []*segment +} + +func (q jsonPathAST) ToString() string { + b := strings.Builder{} + b.WriteString("$") + for _, seg := range q.segments { + b.WriteString(seg.ToString()) + } + return b.String() +} diff --git a/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/segment.go b/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/segment.go new file mode 100644 index 000000000..fc6d31ef6 --- /dev/null +++ b/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/segment.go @@ -0,0 +1,86 @@ +package jsonpath + +import ( + "go.yaml.in/yaml/v4" + "strings" +) + +type segmentKind int + +const ( + segmentKindChild segmentKind = iota // . + segmentKindDescendant // .. + segmentKindProperyName // ~ (extension only) + segmentKindParent // ^ (JSONPath Plus parent selector) +) + +type segment struct { + kind segmentKind + child *innerSegment + descendant *innerSegment +} + +type segmentSubKind int + +const ( + segmentDotWildcard segmentSubKind = iota // .* + segmentDotMemberName // .property + segmentLongHand // [ selector[] ] +) + +func (s segment) ToString() string { + switch s.kind { + case segmentKindChild: + if s.child.kind != segmentLongHand { + return "." + s.child.ToString() + } else { + return s.child.ToString() + } + case segmentKindDescendant: + return ".." + s.descendant.ToString() + case segmentKindProperyName: + return "~" + case segmentKindParent: + return "^" + } + panic("unknown segment kind") +} + +type innerSegment struct { + kind segmentSubKind + dotName string + selectors []*selector +} + +func (s innerSegment) ToString() string { + builder := strings.Builder{} + switch s.kind { + case segmentDotWildcard: + builder.WriteString("*") + break + case segmentDotMemberName: + builder.WriteString(s.dotName) + break + case segmentLongHand: + builder.WriteString("[") + for i, selector := range s.selectors { + builder.WriteString(selector.ToString()) + if i < len(s.selectors)-1 { + builder.WriteString(", ") + } + } + builder.WriteString("]") + break + default: + panic("unknown child segment kind") + } + return builder.String() +} + +func descend(value *yaml.Node, root *yaml.Node) []*yaml.Node { + result := []*yaml.Node{value} + for _, child := range value.Content { + result = append(result, descend(child, root)...) + } + return result +} diff --git a/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/selector.go b/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/selector.go new file mode 100644 index 000000000..f71cb47e7 --- /dev/null +++ b/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/selector.go @@ -0,0 +1,63 @@ +package jsonpath + +import ( + "fmt" + "strconv" + "strings" +) + +type selectorSubKind int + +const ( + selectorSubKindWildcard selectorSubKind = iota + selectorSubKindName + selectorSubKindArraySlice + selectorSubKindArrayIndex + selectorSubKindFilter +) + +type slice struct { + start *int64 + end *int64 + step *int64 +} + +type selector struct { + kind selectorSubKind + name string + index int64 + slice *slice + filter *filterSelector +} + +func (s selector) ToString() string { + switch s.kind { + case selectorSubKindName: + return "'" + escapeString(s.name) + "'" + case selectorSubKindArrayIndex: + // int to string + return strconv.FormatInt(s.index, 10) + case selectorSubKindFilter: + return "?" + s.filter.ToString() + case selectorSubKindWildcard: + return "*" + case selectorSubKindArraySlice: + builder := strings.Builder{} + if s.slice.start != nil { + builder.WriteString(strconv.FormatInt(*s.slice.start, 10)) + } + builder.WriteString(":") + if s.slice.end != nil { + builder.WriteString(strconv.FormatInt(*s.slice.end, 10)) + } + + if s.slice.step != nil { + builder.WriteString(":") + builder.WriteString(strconv.FormatInt(*s.slice.step, 10)) + } + return builder.String() + default: + panic(fmt.Sprintf("unimplemented selector kind: %v", s.kind)) + } + return "" +} diff --git a/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/token/token.go b/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/token/token.go new file mode 100644 index 000000000..a9e39a420 --- /dev/null +++ b/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/token/token.go @@ -0,0 +1,887 @@ +package token + +import ( + "fmt" + "github.com/pb33f/jsonpath/pkg/jsonpath/config" + "strconv" + "strings" +) + +// ***************************************************************************** +// The Tokenizer is responsible for tokenizing the jsonpath expression. This means +// * removing whitespace +// * scanning strings +// * and detecting illegal characters +// ***************************************************************************** + +// Token represents a lexical token in a JSONPath expression. +type Token int + +// We are allowed the following tokens + +//jsonpath-query = root-identifier segments +//segments = *(segment) +//root-identifier = "$" +//selector = name-selector / +// wildcard-selector / +// slice-selector / +// index-selector / +// filter-selector +//name-selector = string-literal +//wildcard-selector = "*" +//index-selector = int ; decimal integer +// +//int = "0" / +// (["-"] DIGIT1 *DIGIT) ; - optional +//DIGIT1 = %x31-39 ; 1-9 non-zero digit +//slice-selector = [start] ":" [end] [":" [step]] +// +//start = int ; included in selection +//end = int ; not included in selection +//step = int ; default: 1 +//filter-selector = "?" logical-expr +//logical-expr = logical-or-expr +//logical-or-expr = logical-and-expr *("||" logical-and-expr) +// ; disjunction +// ; binds less tightly than conjunction +//logical-and-expr = basic-expr *("&&" basic-expr) +// ; conjunction +// ; binds more tightly than disjunction +// +//basic-expr = paren-expr / +// comparison-expr / +// test-expr +// +//paren-expr = [logical-not-op] "(" logical-expr ")" +// ; parenthesized expression +//logical-not-op = "!" ; logical NOT operator +//test-expr = [logical-not-op S] +// (filter-query / ; existence/non-existence +// function-expr) ; LogicalType or NodesType +//filter-query = rel-query / jsonpath-query +//rel-query = current-node-identifier segments +//current-node-identifier = "@" +//comparison-expr = comparable comparison-op comparable +//literal = number / string-literal / +// true / false / null +//comparable = literal / +// singular-query / ; singular query value +// function-expr ; ValueType +//comparison-op = "==" / "!=" / +// "<=" / ">=" / +// "<" / ">" +// +//singular-query = rel-singular-query / abs-singular-query +//rel-singular-query = current-node-identifier singular-query-segments +//abs-singular-query = root-identifier singular-query-segments +//singular-query-segments = *(S (name-segment / index-segment)) +//name-segment = ("[" name-selector "]") / +// ("." member-name-shorthand) +//index-segment = "[" index-selector "]" +//number = (int / "-0") [ frac ] [ exp ] ; decimal number +//frac = "." 1*DIGIT ; decimal fraction +//exp = "e" [ "-" / "+" ] 1*DIGIT ; decimal exponent +//true = %x74.72.75.65 ; true +//false = %x66.61.6c.73.65 ; false +//null = %x6e.75.6c.6c ; null +//function-name = function-name-first *function-name-char +//function-name-first = LCALPHA +//function-name-char = function-name-first / "_" / DIGIT +//LCALPHA = %x61-7A ; "a".."z" +// +//function-expr = function-name "(" [function-argument +// *(S "," function-argument)] ")" +//function-argument = literal / +// filter-query / ; (includes singular-query) +// logical-expr / +// function-expr +//segment = child-segment / descendant-segment +//child-segment = bracketed-selection / +// ("." +// (wildcard-selector / +// member-name-shorthand)) +// +//bracketed-selection = "[" selector *(S "," selector) "]" +// +//member-name-shorthand = name-first *name-char +//name-first = ALPHA / +// "_" / +// %x80-D7FF / +// ; skip surrogate code points +// %xE000-10FFFF +//name-char = name-first / DIGIT +// +//DIGIT = %x30-39 ; 0-9 +//ALPHA = %x41-5A / %x61-7A ; A-Z / a-z +//descendant-segment = ".." (bracketed-selection / +// wildcard-selector / +// member-name-shorthand) +// +// Figure 2: Collected ABNF of JSONPath Queries +// +//Figure 3 contains the collected ABNF grammar that defines the syntax +//of a JSONPath Normalized Path while also using the rules root- +//identifier, ESC, DIGIT, and DIGIT1 from Figure 2. +// +//normalized-path = root-identifier *(normal-index-segment) +//normal-index-segment = "[" normal-selector "]" +//normal-selector = normal-name-selector / normal-index-selector +//normal-name-selector = %x27 *normal-single-quoted %x27 ; 'string' +//normal-single-quoted = normal-unescaped / +// ESC normal-escapable +//normal-unescaped = ; omit %x0-1F control codes +// %x20-26 / +// ; omit 0x27 ' +// %x28-5B / +// ; omit 0x5C \ +// %x5D-D7FF / +// ; skip surrogate code points +// %xE000-10FFFF +// +//normal-escapable = %x62 / ; b BS backspace U+0008 +// %x66 / ; f FF form feed U+000C +// %x6E / ; n LF line feed U+000A +// %x72 / ; r CR carriage return U+000D +// %x74 / ; t HT horizontal tab U+0009 +// "'" / ; ' apostrophe U+0027 +// "\" / ; \ backslash (reverse solidus) U+005C +// (%x75 normal-hexchar) +// ; certain values u00xx U+00XX +//normal-hexchar = "0" "0" +// ( +// ("0" %x30-37) / ; "00"-"07" +// ; omit U+0008-U+000A BS HT LF +// ("0" %x62) / ; "0b" +// ; omit U+000C-U+000D FF CR +// ("0" %x65-66) / ; "0e"-"0f" +// ("1" normal-HEXDIG) +// ) +//normal-HEXDIG = DIGIT / %x61-66 ; "0"-"9", "a"-"f" +//normal-index-selector = "0" / (DIGIT1 *DIGIT) +// ; non-negative decimal integer + +// The list of tokens. +const ( + ILLEGAL Token = iota + STRING + INTEGER + FLOAT + STRING_LITERAL + TRUE + FALSE + NULL + ROOT + CURRENT + WILDCARD + PROPERTY_NAME + RECURSIVE + CHILD + ARRAY_SLICE + FILTER + PAREN_LEFT + PAREN_RIGHT + BRACKET_LEFT + BRACKET_RIGHT + COMMA + TILDE + AND + OR + NOT + EQ + NE + GT + GE + LT + LE + MATCHES + FUNCTION + + // JSONPath Plus context variable tokens + CONTEXT_PROPERTY // @property - current property name + CONTEXT_ROOT // @root - root node access in filter + CONTEXT_PARENT // @parent - parent node reference + CONTEXT_PARENT_PROPERTY // @parentProperty - parent's property name + CONTEXT_PATH // @path - absolute path to current node + CONTEXT_INDEX // @index - current array index + + // JSONPath Plus parent selector + PARENT_SELECTOR // ^ - select parent of current node +) + +var SimpleTokens = [...]Token{ + STRING, + INTEGER, + STRING_LITERAL, + CHILD, + BRACKET_LEFT, + BRACKET_RIGHT, + ROOT, +} + +var tokens = [...]string{ + ILLEGAL: "ILLEGAL", + STRING: "STRING", + INTEGER: "INTEGER", + FLOAT: "FLOAT", + STRING_LITERAL: "STRING_LITERAL", + TRUE: "TRUE", + FALSE: "FALSE", + NULL: "NULL", + // root node identifier (Section 2.2) + ROOT: "$", + // current node identifier (Section 2.3.5) + // (valid only within filter selectors) + CURRENT: "@", + WILDCARD: "*", + RECURSIVE: "..", + CHILD: ".", + // start:end:step array slice operator (Section 2.3.4) + ARRAY_SLICE: ":", + // filter selector (Section 2.3.5): selects + // particular children using a logical + // expression + FILTER: "?", + PAREN_LEFT: "(", + PAREN_RIGHT: ")", + BRACKET_LEFT: "[", + BRACKET_RIGHT: "]", + COMMA: ",", + TILDE: "~", + AND: "&&", + OR: "||", + NOT: "!", + EQ: "==", + NE: "!=", + GT: ">", + GE: ">=", + LT: "<", + LE: "<=", + MATCHES: "=~", + FUNCTION: "FUNCTION", + + // JSONPath Plus context variables + CONTEXT_PROPERTY: "@property", + CONTEXT_ROOT: "@root", + CONTEXT_PARENT: "@parent", + CONTEXT_PARENT_PROPERTY: "@parentProperty", + CONTEXT_PATH: "@path", + CONTEXT_INDEX: "@index", + + // JSONPath Plus parent selector + PARENT_SELECTOR: "^", +} + +// String returns the string representation of the token. +func (tok Token) String() string { + if tok >= 0 && tok < Token(len(tokens)) { + return tokens[tok] + } + return "token(" + strconv.Itoa(int(tok)) + ")" +} + +func (tok Tokens) IsSimple() bool { + if len(tok) == 0 { + return false + } + if tok[0].Token != ROOT { + return false + } + for _, token := range tok { + isSimple := false + for _, simpleToken := range SimpleTokens { + if token.Token == simpleToken { + isSimple = true + } + } + if !isSimple { + return false + } + } + return true +} + +// When there's an error in the tokenizer, this helps represent it. +func (t Tokenizer) ErrorString(target *TokenInfo, msg string) string { + var errorBuilder strings.Builder + + var token TokenInfo + if target == nil { + // grab last token (as value) + token = t.tokens[len(t.tokens)-1] + // set column to +1 + token.Column++ + target = &token + } + + // Write the error message with line and column information + errorBuilder.WriteString(fmt.Sprintf("Error at line %d, column %d: %s\n", target.Line, target.Column, msg)) + + // Find the start and end positions of the line containing the target token + lineStart := 0 + lineEnd := len(t.input) + for i := target.Line - 1; i > 0; i-- { + if pos := strings.LastIndexByte(t.input[:lineStart], '\n'); pos != -1 { + lineStart = pos + 1 + break + } + } + if pos := strings.IndexByte(t.input[lineStart:], '\n'); pos != -1 { + lineEnd = lineStart + pos + } + + // Extract the line containing the target token + line := t.input[lineStart:lineEnd] + errorBuilder.WriteString(line) + errorBuilder.WriteString("\n") + + // Calculate the number of spaces before the target token + spaces := strings.Repeat(" ", target.Column) + + // Write the caret symbol pointing to the target token + errorBuilder.WriteString(spaces) + dots := "" + if target.Len > 0 { + dots = strings.Repeat(".", target.Len-1) + } + errorBuilder.WriteString("^" + dots + "\n") + + return errorBuilder.String() +} + +// When there's an error +func (t Tokenizer) ErrorTokenString(target *TokenInfo, msg string) string { + var errorBuilder strings.Builder + var token TokenInfo + if target == nil { + // grab last token (as value) + token = t.tokens[len(t.tokens)-1] + // set column to +1 + token.Column++ + target = &token + } + // Write the error message with line and column information + errorBuilder.WriteString(t.ErrorString(target, msg)) + + // Find the start and end positions of the line containing the target token + lineStart := 0 + lineEnd := len(t.input) + for i := target.Line - 1; i > 0; i-- { + if pos := strings.LastIndexByte(t.input[:lineStart], '\n'); pos != -1 { + lineStart = pos + 1 + break + } + } + if pos := strings.IndexByte(t.input[lineStart:], '\n'); pos != -1 { + lineEnd = lineStart + pos + } + + // Extract the line containing the target token + line := t.input[lineStart:lineEnd] + + // Calculate the number of spaces before the target token + for _, token := range t.tokens { + errorBuilder.WriteString(line) + errorBuilder.WriteString("\n") + spaces := strings.Repeat(" ", token.Column) + dots := "" + if token.Len > 0 { + dots = strings.Repeat(".", token.Len-1) + } + errorBuilder.WriteString(spaces) + errorBuilder.WriteString(fmt.Sprintf("^%s %s\n", dots, tokens[token.Token])) + } + + return errorBuilder.String() +} + +// TokenInfo represents a token and its associated information. +type TokenInfo struct { + Token Token + Line int + Column int + Literal string + Len int +} + +// Tokens represents the list of tokens +type Tokens []TokenInfo + +// Tokenizer represents a JSONPath tokenizer. +type Tokenizer struct { + input string + pos int + line int + column int + tokens []TokenInfo + stack []Token + illegalWhitespace bool + config config.Config +} + +// NewTokenizer creates a new JSONPath tokenizer for the given input string. +func NewTokenizer(input string, opts ...config.Option) *Tokenizer { + cfg := config.New(opts...) + return &Tokenizer{ + input: input, + config: cfg, + line: 1, + stack: make([]Token, 0), + } +} + +// Tokenize tokenizes the input string and returns a slice of TokenInfo. +func (t *Tokenizer) Tokenize() Tokens { + for t.pos < len(t.input) { + if !t.illegalWhitespace { + t.skipWhitespace() + } + if t.pos >= len(t.input) { + break + } + + switch ch := t.input[t.pos]; { + case ch == '$': + t.addToken(ROOT, 1, "") + case ch == '@': + // Check for JSONPath Plus context variables when enabled + handled := false + if t.config.JSONPathPlusEnabled() { + if contextToken, length := t.tryContextVariable(); contextToken != ILLEGAL { + t.addToken(contextToken, length, "") + // Advance past the token (minus 1 because main loop does pos++) + t.pos += length - 1 + t.column += length - 1 + handled = true + } + } + if !handled { + t.addToken(CURRENT, 1, "") + } + case ch == '*': + t.addToken(WILDCARD, 1, "") + case ch == '~': + if t.config.PropertyNameEnabled() { + t.addToken(PROPERTY_NAME, 1, "") + } else { + t.addToken(ILLEGAL, 1, "invalid property name token without config.PropertyNameExtension set to true") + } + case ch == '^': + // JSONPath Plus parent selector + if t.config.JSONPathPlusEnabled() { + t.addToken(PARENT_SELECTOR, 1, "") + } else { + t.addToken(ILLEGAL, 1, "parent selector ^ requires JSONPath Plus mode (enabled by default, disabled with StrictRFC9535)") + } + case ch == '.': + if t.peek() == '.' { + t.addToken(RECURSIVE, 2, "") + t.pos++ + t.column++ + t.illegalWhitespace = true + } else { + t.addToken(CHILD, 1, "") + t.illegalWhitespace = true + } + case ch == ',': + t.addToken(COMMA, 1, "") + case ch == ':': + t.addToken(ARRAY_SLICE, 1, "") + case ch == '?': + t.addToken(FILTER, 1, "") + case ch == '(': + t.addToken(PAREN_LEFT, 1, "") + t.stack = append(t.stack, PAREN_LEFT) + case ch == ')': + t.addToken(PAREN_RIGHT, 1, "") + if len(t.stack) > 0 && t.stack[len(t.stack)-1] == PAREN_LEFT { + t.stack = t.stack[:len(t.stack)-1] + } else { + t.addToken(ILLEGAL, 1, "unmatched closing parenthesis") + } + case ch == '[': + t.addToken(BRACKET_LEFT, 1, "") + t.stack = append(t.stack, BRACKET_LEFT) + case ch == ']': + if len(t.stack) > 0 && t.stack[len(t.stack)-1] == BRACKET_LEFT { + t.addToken(BRACKET_RIGHT, 1, "") + t.stack = t.stack[:len(t.stack)-1] + } else { + t.addToken(ILLEGAL, 1, "unmatched closing bracket") + } + case ch == '&': + if t.peek() == '&' { + t.addToken(AND, 2, "") + t.pos++ + t.column++ + } else { + t.addToken(ILLEGAL, 1, "invalid token") + } + case ch == '|': + if t.peek() == '|' { + t.addToken(OR, 2, "") + t.pos++ + t.column++ + } else { + t.addToken(ILLEGAL, 1, "invalid token") + } + case ch == '!': + if t.peek() == '=' { + // Check for JavaScript !== (strict not-equals) - treat as RFC 9535 != + if t.pos+2 < len(t.input) && t.input[t.pos+2] == '=' { + t.addToken(NE, 3, "") // !== becomes != + t.pos += 2 + t.column += 2 + } else { + t.addToken(NE, 2, "") + t.pos++ + t.column++ + } + } else { + t.addToken(NOT, 1, "") + } + case ch == '=': + if t.peek() == '=' { + // Check for JavaScript === (strict equals) - treat as RFC 9535 == + if t.pos+2 < len(t.input) && t.input[t.pos+2] == '=' { + t.addToken(EQ, 3, "") // === becomes == + t.pos += 2 + t.column += 2 + } else { + t.addToken(EQ, 2, "") + t.pos++ + t.column++ + } + } else if t.peek() == '~' { + t.addToken(MATCHES, 2, "") + t.pos++ + t.column++ + } else { + t.addToken(ILLEGAL, 1, "invalid token") + } + case ch == '>': + if t.peek() == '=' { + t.addToken(GE, 2, "") + t.pos++ + t.column++ + } else { + t.addToken(GT, 1, "") + } + case ch == '<': + if t.peek() == '=' { + t.addToken(LE, 2, "") + t.pos++ + t.column++ + } else { + t.addToken(LT, 1, "") + } + case ch == '"' || ch == '\'': + t.scanString(rune(ch)) + case ch == '-' && isDigit(t.peek()): + fallthrough + case isDigit(ch): + t.scanNumber() + case isLiteralChar(ch): + t.scanLiteral() + default: + t.addToken(ILLEGAL, 1, string(ch)) + } + t.pos++ + t.column++ + } + + if len(t.stack) > 0 { + t.addToken(ILLEGAL, 1, fmt.Sprintf("unmatched %s", t.stack[len(t.stack)-1].String())) + } + return t.tokens +} + +func (t *Tokenizer) addToken(token Token, len int, literal string) { + t.tokens = append(t.tokens, TokenInfo{ + Token: token, + Line: t.line, + Column: t.column, + Len: len, + Literal: literal, + }) + t.illegalWhitespace = false +} + +func (t *Tokenizer) scanString(quote rune) { + start := t.pos + 1 + var literal strings.Builder +illegal: + for i := start; i < len(t.input); i++ { + b := literal.String() + _ = b + if t.input[i] == byte(quote) { + t.addToken(STRING_LITERAL, len(t.input[start:i])+2, literal.String()) + t.pos = i + t.column += i - start + 1 + return + } + if t.input[i] == '\\' { + i++ + if i >= len(t.input) { + t.addToken(ILLEGAL, len(t.input[start:]), literal.String()) + t.pos = len(t.input) - 1 + t.column = len(t.input) - 1 + return + } + switch t.input[i] { + case 'b': + literal.WriteByte('\b') + case 'f': + literal.WriteByte('\f') + case 'n': + literal.WriteByte('\n') + case 'r': + literal.WriteByte('\n') + case 't': + literal.WriteByte('\t') + case '\'': + if quote != '\'' { + // don't escape it, when we're not in a single quoted string + break illegal + } else { + literal.WriteByte(t.input[i]) + } + case '"': + if quote != '"' { + // don't escape it, when we're not in a single quoted string + break illegal + } else { + literal.WriteByte(t.input[i]) + } + case '\\', '/': + literal.WriteByte(t.input[i]) + default: + break illegal + } + } else { + literal.WriteByte(t.input[i]) + } + } + t.addToken(ILLEGAL, len(t.input[start:]), literal.String()) + t.pos = len(t.input) - 1 + t.column = len(t.input) - 1 +} + +func (t *Tokenizer) scanNumber() { + start := t.pos + tokenType := INTEGER + dotSeen := false + exponentSeen := false + + for i := start; i < len(t.input); i++ { + if i == start && t.input[i] == '-' { + continue + } + + if t.input[i] == '.' { + if dotSeen || exponentSeen { + t.addToken(ILLEGAL, len(t.input[start:i]), t.input[start:i]) + t.pos = i + t.column += i - start + return + } + tokenType = FLOAT + dotSeen = true + continue + } + + if t.input[i] == 'e' || t.input[i] == 'E' { + if exponentSeen || (len(t.input) > 0 && t.input[i-1] == '.') { + t.addToken(ILLEGAL, len(t.input[start:i]), t.input[start:i]) + t.pos = i + t.column += i - start + return + } + tokenType = FLOAT + exponentSeen = true + if i+1 < len(t.input) && (t.input[i+1] == '+' || t.input[i+1] == '-') { + i++ + } + continue + } + + if !isDigit(t.input[i]) { + literal := t.input[start:i] + // check for legal numbers + _, err := strconv.ParseFloat(literal, 64) + if err != nil { + tokenType = ILLEGAL + } + // conformance spec + if len(literal) > 1 && literal[0] == '0' && !dotSeen { + // no leading zero + tokenType = ILLEGAL + } else if len(literal) > 2 && literal[0] == '-' && literal[1] == '0' && !dotSeen { + // no trailing dot + tokenType = ILLEGAL + } else if len(literal) > 0 && literal[len(literal)-1] == '.' { + // no trailing dot + tokenType = ILLEGAL + } else if literal[len(literal)-1] == 'e' || literal[len(literal)-1] == 'E' { + // no exponent + tokenType = ILLEGAL + } + + t.addToken(tokenType, len(literal), literal) + t.pos = i - 1 + t.column += i - start - 1 + return + } + } + + if exponentSeen && !isDigit(t.input[len(t.input)-1]) { + t.addToken(ILLEGAL, len(t.input[start:]), t.input[start:]) + t.pos = len(t.input) - 1 + t.column = len(t.input) - 1 + return + } + + literal := t.input[start:] + t.addToken(tokenType, len(literal), literal) + t.pos = len(t.input) - 1 + t.column = len(t.input) - 1 +} + +func (t *Tokenizer) scanLiteral() { + start := t.pos + for i := start; i < len(t.input); i++ { + if !isLiteralChar(t.input[i]) && !isDigit(t.input[i]) { + literal := t.input[start:i] + switch literal { + case "true": + t.addToken(TRUE, len(literal), literal) + case "false": + t.addToken(FALSE, len(literal), literal) + case "null": + t.addToken(NULL, len(literal), literal) + default: + // Only treat as FUNCTION if it's a function name AND followed by '(' + // Otherwise it's a property name (STRING) + if isFunctionName(literal) && i < len(t.input) && t.input[i] == '(' { + t.addToken(FUNCTION, len(literal), literal) + t.illegalWhitespace = true + } else { + t.addToken(STRING, len(literal), literal) + } + } + t.pos = i - 1 + t.column += i - start - 1 + return + } + } + literal := t.input[start:] + switch literal { + case "true": + t.addToken(TRUE, len(literal), literal) + case "false": + t.addToken(FALSE, len(literal), literal) + case "null": + t.addToken(NULL, len(literal), literal) + default: + t.addToken(STRING, len(literal), literal) + } + t.pos = len(t.input) - 1 + t.column = len(t.input) - 1 +} + +func isFunctionName(literal string) bool { + switch literal { + // RFC 9535 standard functions + case "length", "count", "match", "search", "value": + return true + // JSONPath Plus type selector functions + case "isNull", "isBoolean", "isNumber", "isString", "isArray", "isObject", "isInteger": + return true + } + return false +} + +func (t *Tokenizer) skipWhitespace() { + // S = *B ; optional blank space + // B = %x20 / ; Space + // %x09 / ; Horizontal tab + // %x0A / ; Line feed or New line + // %x0D ; Carriage return + for len(t.tokens) > 0 && t.pos+1 < len(t.input) { + ch := t.input[t.pos] + if ch == '\n' { + t.line++ + t.pos++ + t.column = 0 + } else if !isSpace(ch) { + break + } else { + t.pos++ + t.column++ + } + } +} + +func (t *Tokenizer) peek() byte { + if t.pos+1 < len(t.input) { + return t.input[t.pos+1] + } + return 0 +} + +func isDigit(ch byte) bool { + return '0' <= ch && ch <= '9' +} + +func isLiteralChar(ch byte) bool { + // allow unicode characters + return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 +} + +func isSpace(ch byte) bool { + return ch == ' ' || ch == '\t' || ch == '\r' +} + +// contextVariableKeywords maps context variable names to their token types. +// These are JSONPath Plus extensions for accessing filter context. +var contextVariableKeywords = map[string]Token{ + "property": CONTEXT_PROPERTY, + "root": CONTEXT_ROOT, + "parent": CONTEXT_PARENT, + "parentProperty": CONTEXT_PARENT_PROPERTY, + "path": CONTEXT_PATH, + "index": CONTEXT_INDEX, +} + +// tryContextVariable checks if the current position starts a context variable. +// It returns the token type and total length (including @) if found, or ILLEGAL and 0 if not. +// Context variables are @property, @root, @parent, @parentProperty, @path, @index. +func (t *Tokenizer) tryContextVariable() (Token, int) { + // Must start with @ + if t.pos >= len(t.input) || t.input[t.pos] != '@' { + return ILLEGAL, 0 + } + + // Extract the word following @ + start := t.pos + 1 + if start >= len(t.input) { + return ILLEGAL, 0 + } + + // Find the end of the identifier + end := start + for end < len(t.input) && isLiteralChar(t.input[end]) { + end++ + } + + if end == start { + return ILLEGAL, 0 + } + + keyword := t.input[start:end] + if tok, ok := contextVariableKeywords[keyword]; ok { + // Return the token and total length including @ + return tok, end - t.pos + } + + return ILLEGAL, 0 +} diff --git a/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/yaml_eval.go b/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/yaml_eval.go new file mode 100644 index 000000000..7143ad9af --- /dev/null +++ b/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/yaml_eval.go @@ -0,0 +1,441 @@ +package jsonpath + +import ( + "fmt" + "reflect" + "regexp" + "strconv" + "unicode/utf8" + + "go.yaml.in/yaml/v4" +) + +// Pre-allocated boolean literals to avoid repeated allocations +var ( + trueLit = true + falseLit = false +) + +func (l literal) Equals(value literal) bool { + if l.integer != nil && value.integer != nil { + return *l.integer == *value.integer + } + if l.float64 != nil && value.float64 != nil { + return *l.float64 == *value.float64 + } + if l.integer != nil && value.float64 != nil { + return float64(*l.integer) == *value.float64 + } + if l.float64 != nil && value.integer != nil { + return *l.float64 == float64(*value.integer) + } + if l.string != nil && value.string != nil { + return *l.string == *value.string + } + if l.bool != nil && value.bool != nil { + return *l.bool == *value.bool + } + if l.null != nil && value.null != nil { + return *l.null == *value.null + } + if l.node != nil && value.node != nil { + return equalsNode(l.node, value.node) + } + if reflect.ValueOf(l).IsZero() && reflect.ValueOf(value).IsZero() { + return true + } + return false +} + +func equalsNode(a *yaml.Node, b *yaml.Node) bool { + // decode into interfaces, then compare + if a.Tag != b.Tag { + return false + } + switch a.Tag { + case "!!str": + return a.Value == b.Value + case "!!int": + return a.Value == b.Value + case "!!float": + return a.Value == b.Value + case "!!bool": + return a.Value == b.Value + case "!!null": + return a.Value == b.Value + case "!!seq": + if len(a.Content) != len(b.Content) { + return false + } + for i := 0; i < len(a.Content); i++ { + if !equalsNode(a.Content[i], b.Content[i]) { + return false + } + } + case "!!map": + if len(a.Content) != len(b.Content) { + return false + } + for i := 0; i < len(a.Content); i += 2 { + if !equalsNode(a.Content[i], b.Content[i]) { + return false + } + if !equalsNode(a.Content[i+1], b.Content[i+1]) { + return false + } + } + } + return true +} + +func (l literal) LessThan(value literal) bool { + if l.integer != nil && value.integer != nil { + return *l.integer < *value.integer + } + if l.float64 != nil && value.float64 != nil { + return *l.float64 < *value.float64 + } + if l.integer != nil && value.float64 != nil { + return float64(*l.integer) < *value.float64 + } + if l.float64 != nil && value.integer != nil { + return *l.float64 < float64(*value.integer) + } + if l.string != nil && value.string != nil { + return *l.string < *value.string + } + return false +} + +func (l literal) LessThanOrEqual(value literal) bool { + return l.LessThan(value) || l.Equals(value) +} + +func (c comparable) Evaluate(idx index, node *yaml.Node, root *yaml.Node) literal { + if c.literal != nil { + return *c.literal + } + if c.singularQuery != nil { + return c.singularQuery.Evaluate(idx, node, root) + } + if c.functionExpr != nil { + return c.functionExpr.Evaluate(idx, node, root) + } + if c.contextVar != nil { + return c.contextVar.Evaluate(idx, node, root) + } + return literal{} +} + +// Evaluate returns the value of a context variable from the FilterContext. +// Returns an empty literal if the idx is not a FilterContext. +func (cv contextVariable) Evaluate(idx index, node *yaml.Node, root *yaml.Node) literal { + fc, ok := idx.(FilterContext) + if !ok { + // Not in JSONPath Plus mode or no context available + return literal{} + } + + switch cv.kind { + case contextVarProperty: + propName := fc.PropertyName() + return literal{string: &propName} + case contextVarRoot: + // This case is handled in the parser - @root becomes an absQuery + // But if we get here, return the root node + return nodeToLiteral(fc.Root()) + case contextVarParent: + parent := fc.Parent() + if parent != nil { + return nodeToLiteral(parent) + } + return literal{} + case contextVarParentProperty: + parentProp := fc.ParentPropertyName() + return literal{string: &parentProp} + case contextVarPath: + path := fc.Path() + return literal{string: &path} + case contextVarIndex: + idx := fc.Index() + if idx >= 0 { + return literal{integer: &idx} + } + // Not in array context - return -1 as indication + minusOne := -1 + return literal{integer: &minusOne} + default: + return literal{} + } +} + +func (e functionExpr) length(idx index, node *yaml.Node, root *yaml.Node) literal { + args := e.args[0].Eval(idx, node, root) + if args.kind != functionArgTypeLiteral { + return literal{} + } + //* If the argument value is a string, the result is the number of + //Unicode scalar values in the string. + if args.literal != nil && args.literal.string != nil { + res := utf8.RuneCountInString(*args.literal.string) + return literal{integer: &res} + } + //* If the argument value is an array, the result is the number of + //elements in the array. + // + //* If the argument value is an object, the result is the number of + //members in the object. + // + //* For any other argument value, the result is the special result + //Nothing. + + if args.literal.node != nil { + switch args.literal.node.Kind { + case yaml.SequenceNode: + res := len(args.literal.node.Content) + return literal{integer: &res} + case yaml.MappingNode: + res := len(args.literal.node.Content) / 2 + return literal{integer: &res} + } + } + return literal{} +} + +func (e functionExpr) count(idx index, node *yaml.Node, root *yaml.Node) literal { + args := e.args[0].Eval(idx, node, root) + if args.kind == functionArgTypeNodes { + res := len(args.nodes) + return literal{integer: &res} + } + + res := 1 + return literal{integer: &res} +} + +func (e functionExpr) match(idx index, node *yaml.Node, root *yaml.Node) literal { + arg1 := e.args[0].Eval(idx, node, root) + arg2 := e.args[1].Eval(idx, node, root) + if arg1.kind != functionArgTypeLiteral || arg2.kind != functionArgTypeLiteral { + return literal{} + } + if arg1.literal.string == nil || arg2.literal.string == nil { + return literal{bool: &[]bool{false}[0]} + } + matched, _ := regexp.MatchString(fmt.Sprintf("^(%s)$", *arg2.literal.string), *arg1.literal.string) + return literal{bool: &matched} +} + +func (e functionExpr) search(idx index, node *yaml.Node, root *yaml.Node) literal { + arg1 := e.args[0].Eval(idx, node, root) + arg2 := e.args[1].Eval(idx, node, root) + if arg1.kind != functionArgTypeLiteral || arg2.kind != functionArgTypeLiteral { + return literal{} + } + if arg1.literal.string == nil || arg2.literal.string == nil { + return literal{bool: &[]bool{false}[0]} + } + matched, _ := regexp.MatchString(*arg2.literal.string, *arg1.literal.string) + return literal{bool: &matched} +} + +func (e functionExpr) value(idx index, node *yaml.Node, root *yaml.Node) literal { + // 2.4.8. value() Function Extension + // + //Parameters: + // 1. NodesType + // + //Result: ValueType + //Its only argument is an instance of NodesType (possibly taken from a + //filter-query, as in the example above). The result is an instance of + //ValueType. + // + //* If the argument contains a single node, the result is the value of + //the node. + // + //* If the argument is the empty nodelist or contains multiple nodes, + // the result is Nothing. + + nodesType := e.args[0].Eval(idx, node, root) + if nodesType.kind == functionArgTypeLiteral { + return *nodesType.literal + } else if nodesType.kind == functionArgTypeNodes && len(nodesType.nodes) == 1 { + return *nodesType.nodes[0] + } + return literal{} +} + +func nodeToLiteral(node *yaml.Node) literal { + switch node.Tag { + case "!!str": + return literal{string: &node.Value} + case "!!int": + i, _ := strconv.Atoi(node.Value) + return literal{integer: &i} + case "!!float": + f, _ := strconv.ParseFloat(node.Value, 64) + return literal{float64: &f} + case "!!bool": + b, _ := strconv.ParseBool(node.Value) + return literal{bool: &b} + case "!!null": + b := true + return literal{null: &b} + default: + return literal{node: node} + } +} + +func (e functionExpr) Evaluate(idx index, node *yaml.Node, root *yaml.Node) literal { + switch e.funcType { + case functionTypeLength: + return e.length(idx, node, root) + case functionTypeCount: + return e.count(idx, node, root) + case functionTypeMatch: + return e.match(idx, node, root) + case functionTypeSearch: + return e.search(idx, node, root) + case functionTypeValue: + return e.value(idx, node, root) + // JSONPath Plus type selector functions + case functionTypeIsNull: + return e.isNull(idx, node, root) + case functionTypeIsBoolean: + return e.isBoolean(idx, node, root) + case functionTypeIsNumber: + return e.isNumber(idx, node, root) + case functionTypeIsString: + return e.isString(idx, node, root) + case functionTypeIsArray: + return e.isArray(idx, node, root) + case functionTypeIsObject: + return e.isObject(idx, node, root) + case functionTypeIsInteger: + return e.isInteger(idx, node, root) + } + return literal{} +} + +func (q singularQuery) Evaluate(idx index, node *yaml.Node, root *yaml.Node) literal { + if q.relQuery != nil { + return q.relQuery.Evaluate(idx, node, root) + } + if q.absQuery != nil { + return q.absQuery.Evaluate(idx, node, root) + } + return literal{} +} + +func (q relQuery) Evaluate(idx index, node *yaml.Node, root *yaml.Node) literal { + result := q.Query(idx, node, root) + if len(result) == 1 { + return nodeToLiteral(result[0]) + } + return literal{} + +} + +func (q absQuery) Evaluate(idx index, node *yaml.Node, root *yaml.Node) literal { + result := q.Query(idx, root, root) + if len(result) == 1 { + return nodeToLiteral(result[0]) + } + return literal{} +} + +// Type checker functions for JSONPath Plus type selectors + +func isNullLiteral(lit *literal) bool { + if lit == nil { + return false + } + return (lit.null != nil && *lit.null) || (lit.node != nil && lit.node.Tag == "!!null") +} + +func isBoolLiteral(lit *literal) bool { + if lit == nil { + return false + } + return lit.bool != nil || (lit.node != nil && lit.node.Tag == "!!bool") +} + +func isNumberLiteral(lit *literal) bool { + if lit == nil { + return false + } + return lit.integer != nil || lit.float64 != nil || + (lit.node != nil && (lit.node.Tag == "!!int" || lit.node.Tag == "!!float")) +} + +func isStringLiteral(lit *literal) bool { + if lit == nil { + return false + } + return lit.string != nil || (lit.node != nil && lit.node.Tag == "!!str") +} + +func isArrayLiteral(lit *literal) bool { + return lit != nil && lit.node != nil && lit.node.Kind == yaml.SequenceNode +} + +func isObjectLiteral(lit *literal) bool { + return lit != nil && lit.node != nil && lit.node.Kind == yaml.MappingNode +} + +func isIntegerLiteral(lit *literal) bool { + if lit == nil { + return false + } + return lit.integer != nil || (lit.node != nil && lit.node.Tag == "!!int") +} + +// checkType is the generic type checker for all type selector functions +func (e functionExpr) checkType(idx index, node *yaml.Node, root *yaml.Node, checker func(*literal) bool) literal { + args := e.args[0].Eval(idx, node, root) + + if args.kind == functionArgTypeLiteral { + result := checker(args.literal) + return literal{bool: &result} + } + + if args.kind == functionArgTypeNodes { + for _, lit := range args.nodes { + if !checker(lit) { + return literal{bool: &falseLit} + } + } + result := len(args.nodes) > 0 + return literal{bool: &result} + } + + return literal{bool: &falseLit} +} + +func (e functionExpr) isNull(idx index, node *yaml.Node, root *yaml.Node) literal { + return e.checkType(idx, node, root, isNullLiteral) +} + +func (e functionExpr) isBoolean(idx index, node *yaml.Node, root *yaml.Node) literal { + return e.checkType(idx, node, root, isBoolLiteral) +} + +func (e functionExpr) isNumber(idx index, node *yaml.Node, root *yaml.Node) literal { + return e.checkType(idx, node, root, isNumberLiteral) +} + +func (e functionExpr) isString(idx index, node *yaml.Node, root *yaml.Node) literal { + return e.checkType(idx, node, root, isStringLiteral) +} + +func (e functionExpr) isArray(idx index, node *yaml.Node, root *yaml.Node) literal { + return e.checkType(idx, node, root, isArrayLiteral) +} + +func (e functionExpr) isObject(idx index, node *yaml.Node, root *yaml.Node) literal { + return e.checkType(idx, node, root, isObjectLiteral) +} + +func (e functionExpr) isInteger(idx index, node *yaml.Node, root *yaml.Node) literal { + return e.checkType(idx, node, root, isIntegerLiteral) +} diff --git a/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/yaml_query.go b/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/yaml_query.go new file mode 100644 index 000000000..0f7198956 --- /dev/null +++ b/vendor/github.com/pb33f/jsonpath/pkg/jsonpath/yaml_query.go @@ -0,0 +1,742 @@ +package jsonpath + +import ( + "strconv" + + "go.yaml.in/yaml/v4" +) + +type Evaluator interface { + Query(current *yaml.Node, root *yaml.Node) []*yaml.Node +} + +// index is the basic interface for tracking property key relationships. +// This is kept for backward compatibility; FilterContext extends this. +type index interface { + setPropertyKey(key *yaml.Node, value *yaml.Node) + getPropertyKey(key *yaml.Node) *yaml.Node + setParentNode(child *yaml.Node, parent *yaml.Node) + getParentNode(child *yaml.Node) *yaml.Node +} + +type _index struct { + propertyKeys map[*yaml.Node]*yaml.Node + parentNodes map[*yaml.Node]*yaml.Node // Maps child nodes to their parent nodes +} + +func (i *_index) setPropertyKey(key *yaml.Node, value *yaml.Node) { + if i != nil && i.propertyKeys != nil { + i.propertyKeys[key] = value + } +} + +func (i *_index) getPropertyKey(key *yaml.Node) *yaml.Node { + if i != nil { + return i.propertyKeys[key] + } + return nil +} + +func (i *_index) setParentNode(child *yaml.Node, parent *yaml.Node) { + if i != nil && i.parentNodes != nil { + i.parentNodes[child] = parent + } +} + +func (i *_index) getParentNode(child *yaml.Node) *yaml.Node { + if i != nil && i.parentNodes != nil { + return i.parentNodes[child] + } + return nil +} + +// jsonPathAST can be Evaluated +var _ Evaluator = jsonPathAST{} + +func (q jsonPathAST) Query(current *yaml.Node, root *yaml.Node) []*yaml.Node { + if root.Kind == yaml.DocumentNode && len(root.Content) == 1 { + root = root.Content[0] + } + + ctx := NewFilterContext(root) + + // Only enable parent tracking if the query uses ^ or @parent + if q.hasParentReferences() { + ctx.EnableParentTracking() + } + + result := make([]*yaml.Node, 0) + result = append(result, root) + + for _, segment := range q.segments { + newValue := []*yaml.Node{} + for _, value := range result { + newValue = append(newValue, segment.Query(ctx, value, root)...) + } + result = newValue + } + return result +} + +// hasParentReferences checks if the AST uses parent selectors (^) or @parent context variable +func (q jsonPathAST) hasParentReferences() bool { + for _, seg := range q.segments { + if seg.hasParentReferences() { + return true + } + } + return false +} + +func (s *segment) hasParentReferences() bool { + if s.kind == segmentKindParent { + return true + } + if s.child != nil && s.child.hasParentReferences() { + return true + } + if s.descendant != nil && s.descendant.hasParentReferences() { + return true + } + return false +} + +func (s *innerSegment) hasParentReferences() bool { + for _, sel := range s.selectors { + if sel.hasParentReferences() { + return true + } + } + return false +} + +func (s *selector) hasParentReferences() bool { + if s.filter != nil && s.filter.hasParentReferences() { + return true + } + return false +} + +func (f *filterSelector) hasParentReferences() bool { + if f.expression != nil { + return f.expression.hasParentReferences() + } + return false +} + +func (e *logicalOrExpr) hasParentReferences() bool { + for _, expr := range e.expressions { + if expr.hasParentReferences() { + return true + } + } + return false +} + +func (e *logicalAndExpr) hasParentReferences() bool { + for _, expr := range e.expressions { + if expr.hasParentReferences() { + return true + } + } + return false +} + +func (e *basicExpr) hasParentReferences() bool { + if e.parenExpr != nil && e.parenExpr.expr != nil { + return e.parenExpr.expr.hasParentReferences() + } + if e.comparisonExpr != nil { + return e.comparisonExpr.hasParentReferences() + } + if e.testExpr != nil { + return e.testExpr.hasParentReferences() + } + return false +} + +func (e *comparisonExpr) hasParentReferences() bool { + if e.left != nil && e.left.hasParentReferences() { + return true + } + if e.right != nil && e.right.hasParentReferences() { + return true + } + return false +} + +func (c *comparable) hasParentReferences() bool { + if c.contextVar != nil && c.contextVar.kind == contextVarParent { + return true + } + if c.singularQuery != nil { + if c.singularQuery.relQuery != nil { + for _, seg := range c.singularQuery.relQuery.segments { + if seg.hasParentReferences() { + return true + } + } + } + } + return false +} + +func (e *testExpr) hasParentReferences() bool { + if e.filterQuery != nil { + if e.filterQuery.relQuery != nil { + for _, seg := range e.filterQuery.relQuery.segments { + if seg.hasParentReferences() { + return true + } + } + } + } + return false +} + +// parentTrackingEnabled checks if parent tracking is enabled in the index +func parentTrackingEnabled(idx index) bool { + if fc, ok := idx.(FilterContext); ok { + return fc.ParentTrackingEnabled() + } + return false +} + +func (s segment) Query(idx index, value *yaml.Node, root *yaml.Node) []*yaml.Node { + switch s.kind { + case segmentKindChild: + return s.child.Query(idx, value, root) + case segmentKindDescendant: + // run the inner segment against this node + var result = []*yaml.Node{} + children := descend(value, root) + for _, child := range children { + result = append(result, s.descendant.Query(idx, child, root)...) + } + // make children unique by pointer value + result = unique(result) + return result + case segmentKindProperyName: + found := idx.getPropertyKey(value) + if found != nil { + return []*yaml.Node{found} + } + return []*yaml.Node{} + case segmentKindParent: + // JSONPath Plus parent selector: ^ returns the parent of the current node + parent := idx.getParentNode(value) + if parent != nil { + return []*yaml.Node{parent} + } + // No parent found (could be root node) + return []*yaml.Node{} + } + panic("no segment type") +} + +func unique(nodes []*yaml.Node) []*yaml.Node { + // stably returns a new slice containing only the unique elements from nodes + res := make([]*yaml.Node, 0) + seen := make(map[*yaml.Node]bool) + for _, node := range nodes { + if _, ok := seen[node]; !ok { + res = append(res, node) + seen[node] = true + } + } + return res +} + +func (s innerSegment) Query(idx index, value *yaml.Node, root *yaml.Node) []*yaml.Node { + result := []*yaml.Node{} + trackParents := parentTrackingEnabled(idx) + + switch s.kind { + case segmentDotWildcard: + // Check for inherited pending segment from previous wildcard/slice + var inheritedPending string + if fc, ok := idx.(FilterContext); ok { + inheritedPending = fc.GetAndClearPendingPathSegment(value) + } + + switch value.Kind { + case yaml.MappingNode: + for i, child := range value.Content { + if i%2 == 1 { + keyNode := value.Content[i-1] + idx.setPropertyKey(keyNode, value) + idx.setPropertyKey(child, keyNode) + if trackParents { + idx.setParentNode(child, value) + } + // Track pending path segment and property name for this node + if fc, ok := idx.(FilterContext); ok { + thisSegment := normalizePathSegment(keyNode.Value) + fc.SetPendingPathSegment(child, inheritedPending+thisSegment) + fc.SetPendingPropertyName(child, keyNode.Value) // For @parentProperty + } + result = append(result, child) + } + } + case yaml.SequenceNode: + for i, child := range value.Content { + if trackParents { + idx.setParentNode(child, value) + } + // Track pending path segment and property name for this node + if fc, ok := idx.(FilterContext); ok { + thisSegment := normalizeIndexSegment(i) + fc.SetPendingPathSegment(child, inheritedPending+thisSegment) + fc.SetPendingPropertyName(child, strconv.Itoa(i)) // For @parentProperty (array index as string) + } + result = append(result, child) + } + } + return result + case segmentDotMemberName: + if value.Kind == yaml.MappingNode { + // Check for inherited pending segment from wildcard/slice + var inheritedPending string + if fc, ok := idx.(FilterContext); ok { + inheritedPending = fc.GetAndClearPendingPathSegment(value) + } + + for i := 0; i < len(value.Content); i += 2 { + key := value.Content[i] + val := value.Content[i+1] + + if key.Value == s.dotName { + idx.setPropertyKey(key, value) + idx.setPropertyKey(val, key) + if trackParents { + idx.setParentNode(val, value) + } + if fc, ok := idx.(FilterContext); ok { + thisSegment := normalizePathSegment(key.Value) + if inheritedPending != "" { + // Propagate combined pending to result for later consumption + fc.SetPendingPathSegment(val, inheritedPending+thisSegment) + } else { + // No wildcard ancestry - push directly to path + fc.PushPathSegment(thisSegment) + } + fc.SetPropertyName(key.Value) + } + result = append(result, val) + break + } + } + } + + case segmentLongHand: + for _, selector := range s.selectors { + result = append(result, selector.Query(idx, value, root)...) + } + default: + panic("unknown child segment kind") + } + + return result + +} + +func (s selector) Query(idx index, value *yaml.Node, root *yaml.Node) []*yaml.Node { + trackParents := parentTrackingEnabled(idx) + + switch s.kind { + case selectorSubKindName: + if value.Kind != yaml.MappingNode { + return nil + } + // Check for inherited pending segment from wildcard/slice + var inheritedPending string + if fc, ok := idx.(FilterContext); ok { + inheritedPending = fc.GetAndClearPendingPathSegment(value) + } + + var key string + for i, child := range value.Content { + if i%2 == 0 { + key = child.Value + continue + } + if key == s.name && i%2 == 1 { + idx.setPropertyKey(value.Content[i], value.Content[i-1]) + idx.setPropertyKey(value.Content[i-1], value) + if trackParents { + idx.setParentNode(child, value) + } + if fc, ok := idx.(FilterContext); ok { + thisSegment := normalizePathSegment(key) + if inheritedPending != "" { + // Propagate combined pending to result for later consumption + fc.SetPendingPathSegment(child, inheritedPending+thisSegment) + } else { + // No wildcard ancestry - push directly to path + fc.PushPathSegment(thisSegment) + } + fc.SetPropertyName(key) + } + return []*yaml.Node{child} + } + } + case selectorSubKindArrayIndex: + if value.Kind != yaml.SequenceNode { + return nil + } + if s.index >= int64(len(value.Content)) || s.index < -int64(len(value.Content)) { + return nil + } + // Check for inherited pending segment from wildcard/slice + var inheritedPending string + if fc, ok := idx.(FilterContext); ok { + inheritedPending = fc.GetAndClearPendingPathSegment(value) + } + + var child *yaml.Node + var actualIndex int + if s.index < 0 { + actualIndex = int(int64(len(value.Content)) + s.index) + child = value.Content[actualIndex] + } else { + actualIndex = int(s.index) + child = value.Content[s.index] + } + if trackParents { + idx.setParentNode(child, value) + } + if fc, ok := idx.(FilterContext); ok { + thisSegment := normalizeIndexSegment(actualIndex) + if inheritedPending != "" { + // Propagate combined pending to result for later consumption + fc.SetPendingPathSegment(child, inheritedPending+thisSegment) + } else { + // No wildcard ancestry - push directly to path + fc.PushPathSegment(thisSegment) + } + } + return []*yaml.Node{child} + case selectorSubKindWildcard: + // Check for inherited pending segment from previous wildcard/slice + var inheritedPending string + if fc, ok := idx.(FilterContext); ok { + inheritedPending = fc.GetAndClearPendingPathSegment(value) + } + + if value.Kind == yaml.SequenceNode { + for i, child := range value.Content { + if trackParents { + idx.setParentNode(child, value) + } + // Track pending path segment and property name for this node + if fc, ok := idx.(FilterContext); ok { + thisSegment := normalizeIndexSegment(i) + fc.SetPendingPathSegment(child, inheritedPending+thisSegment) + fc.SetPendingPropertyName(child, strconv.Itoa(i)) // For @parentProperty + } + } + return value.Content + } else if value.Kind == yaml.MappingNode { + var result []*yaml.Node + for i, child := range value.Content { + if i%2 == 1 { + keyNode := value.Content[i-1] + idx.setPropertyKey(keyNode, value) + idx.setPropertyKey(child, keyNode) + if trackParents { + idx.setParentNode(child, value) + } + // Track pending path segment and property name for this node + if fc, ok := idx.(FilterContext); ok { + thisSegment := normalizePathSegment(keyNode.Value) + fc.SetPendingPathSegment(child, inheritedPending+thisSegment) + fc.SetPendingPropertyName(child, keyNode.Value) // For @parentProperty + } + result = append(result, child) + } + } + return result + } + return nil + case selectorSubKindArraySlice: + if value.Kind != yaml.SequenceNode { + return nil + } + if len(value.Content) == 0 { + return nil + } + // Check for inherited pending segment from previous wildcard/slice + var inheritedPending string + if fc, ok := idx.(FilterContext); ok { + inheritedPending = fc.GetAndClearPendingPathSegment(value) + } + + step := int64(1) + if s.slice.step != nil { + step = *s.slice.step + } + if step == 0 { + return nil + } + + start, end := s.slice.start, s.slice.end + lower, upper := bounds(start, end, step, int64(len(value.Content))) + + var result []*yaml.Node + if step > 0 { + for i := lower; i < upper; i += step { + child := value.Content[i] + if trackParents { + idx.setParentNode(child, value) + } + // Track pending path segment and property name for this node + if fc, ok := idx.(FilterContext); ok { + thisSegment := normalizeIndexSegment(int(i)) + fc.SetPendingPathSegment(child, inheritedPending+thisSegment) + fc.SetPendingPropertyName(child, strconv.Itoa(int(i))) // For @parentProperty + } + result = append(result, child) + } + } else { + for i := upper; i > lower; i += step { + child := value.Content[i] + if trackParents { + idx.setParentNode(child, value) + } + // Track pending path segment and property name for this node + if fc, ok := idx.(FilterContext); ok { + thisSegment := normalizeIndexSegment(int(i)) + fc.SetPendingPathSegment(child, inheritedPending+thisSegment) + fc.SetPendingPropertyName(child, strconv.Itoa(int(i))) // For @parentProperty + } + result = append(result, child) + } + } + + return result + case selectorSubKindFilter: + var result []*yaml.Node + // Get parent property name - prefer pending property name from wildcard/slice, + // fall back to current PropertyName + var parentPropName string + var pushedPendingSegment bool + if fc, ok := idx.(FilterContext); ok { + // First check for pending property name from wildcard/slice + if pendingPropName := fc.GetAndClearPendingPropertyName(value); pendingPropName != "" { + parentPropName = pendingPropName + } else { + parentPropName = fc.PropertyName() + } + // Check if this node has a pending path segment from a wildcard/slice + if pendingSeg := fc.GetAndClearPendingPathSegment(value); pendingSeg != "" { + fc.PushPathSegment(pendingSeg) + pushedPendingSegment = true + } + } + switch value.Kind { + case yaml.MappingNode: + for i := 1; i < len(value.Content); i += 2 { + keyNode := value.Content[i-1] + valueNode := value.Content[i] + idx.setPropertyKey(keyNode, value) + idx.setPropertyKey(valueNode, keyNode) + if trackParents { + idx.setParentNode(valueNode, value) + } + + if fc, ok := idx.(FilterContext); ok { + fc.SetParentPropertyName(parentPropName) + fc.SetPropertyName(keyNode.Value) + fc.SetParent(value) + fc.SetIndex(-1) + fc.PushPathSegment(normalizePathSegment(keyNode.Value)) + } + + if s.filter.Matches(idx, valueNode, root) { + result = append(result, valueNode) + } + + if fc, ok := idx.(FilterContext); ok { + fc.PopPathSegment() + } + } + case yaml.SequenceNode: + for i, child := range value.Content { + if trackParents { + idx.setParentNode(child, value) + } + + if fc, ok := idx.(FilterContext); ok { + fc.SetParentPropertyName(parentPropName) + fc.SetPropertyName(strconv.Itoa(i)) + fc.SetParent(value) + fc.SetIndex(i) + fc.PushPathSegment(normalizeIndexSegment(i)) + } + + if s.filter.Matches(idx, child, root) { + result = append(result, child) + } + + if fc, ok := idx.(FilterContext); ok { + fc.PopPathSegment() + } + } + } + // Pop the pending segment if we pushed one + if pushedPendingSegment { + if fc, ok := idx.(FilterContext); ok { + fc.PopPathSegment() + } + } + return result + } + return nil +} + +func normalize(i, length int64) int64 { + if i >= 0 { + return i + } + return length + i +} + +func bounds(start, end *int64, step, length int64) (int64, int64) { + var nStart, nEnd int64 + if start != nil { + nStart = normalize(*start, length) + } else if step > 0 { + nStart = 0 + } else { + nStart = length - 1 + } + if end != nil { + nEnd = normalize(*end, length) + } else if step > 0 { + nEnd = length + } else { + nEnd = -1 + } + + var lower, upper int64 + if step >= 0 { + lower = max(min(nStart, length), 0) + upper = min(max(nEnd, 0), length) + } else { + upper = min(max(nStart, -1), length-1) + lower = min(max(nEnd, -1), length-1) + } + + return lower, upper +} + +func (s filterSelector) Matches(idx index, node *yaml.Node, root *yaml.Node) bool { + return s.expression.Matches(idx, node, root) +} + +func (e logicalOrExpr) Matches(idx index, node *yaml.Node, root *yaml.Node) bool { + for _, expr := range e.expressions { + if expr.Matches(idx, node, root) { + return true + } + } + return false +} + +func (e logicalAndExpr) Matches(idx index, node *yaml.Node, root *yaml.Node) bool { + for _, expr := range e.expressions { + if !expr.Matches(idx, node, root) { + return false + } + } + return true +} + +func (e basicExpr) Matches(idx index, node *yaml.Node, root *yaml.Node) bool { + if e.parenExpr != nil { + result := e.parenExpr.expr.Matches(idx, node, root) + if e.parenExpr.not { + return !result + } + return result + } else if e.comparisonExpr != nil { + return e.comparisonExpr.Matches(idx, node, root) + } else if e.testExpr != nil { + return e.testExpr.Matches(idx, node, root) + } + return false +} + +func (e comparisonExpr) Matches(idx index, node *yaml.Node, root *yaml.Node) bool { + leftValue := e.left.Evaluate(idx, node, root) + rightValue := e.right.Evaluate(idx, node, root) + + switch e.op { + case equalTo: + return leftValue.Equals(rightValue) + case notEqualTo: + return !leftValue.Equals(rightValue) + case lessThan: + return leftValue.LessThan(rightValue) + case lessThanEqualTo: + return leftValue.LessThanOrEqual(rightValue) + case greaterThan: + return rightValue.LessThan(leftValue) + case greaterThanEqualTo: + return rightValue.LessThanOrEqual(leftValue) + default: + return false + } +} + +func (e testExpr) Matches(idx index, node *yaml.Node, root *yaml.Node) bool { + var result bool + if e.filterQuery != nil { + result = len(e.filterQuery.Query(idx, node, root)) > 0 + } else if e.functionExpr != nil { + funcResult := e.functionExpr.Evaluate(idx, node, root) + if funcResult.bool != nil { + result = *funcResult.bool + } else if funcResult.null == nil { + result = true + } + } + if e.not { + return !result + } + return result +} + +func (q filterQuery) Query(idx index, node *yaml.Node, root *yaml.Node) []*yaml.Node { + if q.relQuery != nil { + return q.relQuery.Query(idx, node, root) + } + if q.jsonPathQuery != nil { + return q.jsonPathQuery.Query(node, root) + } + return nil +} + +func (q relQuery) Query(idx index, node *yaml.Node, root *yaml.Node) []*yaml.Node { + result := []*yaml.Node{node} + for _, seg := range q.segments { + var newResult []*yaml.Node + for _, value := range result { + newResult = append(newResult, seg.Query(idx, value, root)...) + } + result = newResult + } + return result +} + +func (q absQuery) Query(idx index, node *yaml.Node, root *yaml.Node) []*yaml.Node { + result := []*yaml.Node{root} + for _, seg := range q.segments { + var newResult []*yaml.Node + for _, value := range result { + newResult = append(newResult, seg.Query(idx, value, root)...) + } + result = newResult + } + return result +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/.golangci.yml b/vendor/github.com/pb33f/libopenapi-validator/.golangci.yml new file mode 100644 index 000000000..c4bb81a5e --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/.golangci.yml @@ -0,0 +1,31 @@ +version: "2" +linters: + default: none + enable: + - asciicheck + - bidichk + - errcheck + - govet + - ineffassign + - staticcheck + - unused + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - gofumpt + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/vendor/github.com/pb33f/libopenapi-validator/LICENSE.md b/vendor/github.com/pb33f/libopenapi-validator/LICENSE.md new file mode 100644 index 000000000..8fcf9a09b --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023-2025 Princess Beef Heavy Industries, LLC / Dave Shanley + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/pb33f/libopenapi-validator/Makefile b/vendor/github.com/pb33f/libopenapi-validator/Makefile new file mode 100644 index 000000000..6d18b84c2 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/Makefile @@ -0,0 +1,15 @@ +all: gofumpt import lint + +init: + go install mvdan.cc/gofumpt@v0.7.0 + go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.61.0 + go install github.com/daixiang0/gci@v0.13.5 + +lint: + golangci-lint run ./... + +gofumpt: + gofumpt -l -w . + +import: + gci write --skip-generated -s standard -s default -s localmodule -s blank -s dot -s alias . diff --git a/vendor/github.com/pb33f/libopenapi-validator/README.md b/vendor/github.com/pb33f/libopenapi-validator/README.md new file mode 100644 index 000000000..e41641810 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/README.md @@ -0,0 +1,92 @@ +

+ libopenapi +

+ +# Enterprise grade OpenAPI validation tools for golang. + +![Pipeline](https://github.com/pb33f/libopenapi-validator/workflows/Build/badge.svg) +[![codecov](https://codecov.io/gh/pb33f/libopenapi-validator/branch/main/graph/badge.svg?)](https://codecov.io/gh/pb33f/libopenapi-validator) +[![discord](https://img.shields.io/discord/923258363540815912)](https://discord.gg/x7VACVuEGP) +[![Docs](https://img.shields.io/badge/godoc-reference-5fafd7)](https://pkg.go.dev/github.com/pb33f/libopenapi-validator) + +A validation module for [libopenapi](https://github.com/pb33f/libopenapi). + +`libopenapi-validator` will validate the following elements against an OpenAPI 3+ specification + +- *http.Request* - Validates the request against the OpenAPI specification +- *http.Response* - Validates the response against the OpenAPI specification +- *libopenapi.Document* - Validates the OpenAPI document against the OpenAPI specification +- *base.Schema* - Validates a schema against a JSON or YAML blob / unmarshalled object + +👉👉 [Check out the full documentation](https://pb33f.io/libopenapi/validation/) 👈👈 + +--- + +## Installation + +```bash +go get github.com/pb33f/libopenapi-validator +``` + +## Validate OpenAPI Document + +```bash +go run github.com/pb33f/libopenapi-validator/cmd/validate@latest [--regexengine] [--yaml2json] +``` + +### Options + +#### --regexengine +🔍 Example: Use a custom regex engine/flag (e.g., ecmascript) +```bash +go run github.com/pb33f/libopenapi-validator/cmd/validate@latest --regexengine=ecmascript +``` +🔧 Supported **--regexengine** flags/values (ℹ️ Default: re2) +- none +- ignorecase +- multiline +- explicitcapture +- compiled +- singleline +- ignorepatternwhitespace +- righttoleft +- debug +- ecmascript +- re2 +- unicode + +#### --yaml2json +🔍 Convert YAML files to JSON before validation (ℹ️ Default: false) + +[libopenapi](https://github.com/pb33f/libopenapi/blob/main/datamodel/spec_info.go#L115) passes `map[interface{}]interface{}` structures for deeply nested objects or complex mappings in the OpenAPI specification, which are not allowed in JSON. +These structures cannot be properly converted to JSON by libopenapi and cannot be validated by jsonschema, resulting in ambiguous errors. + +This flag allows pre-converting from YAML to JSON to bypass this limitation of the libopenapi. + +**When does this happen?** +- OpenAPI specs with deeply nested schema definitions +- Complex `allOf`, `oneOf`, or `anyOf` structures with multiple levels +- Specifications with intricate object mappings in examples or schema properties + +Enabling this flag pre-converts the YAML document from YAML to JSON, ensuring a clean JSON structure before validation. + +Example: +```bash +go run github.com/pb33f/libopenapi-validator/cmd/validate@latest --yaml2json +``` + +## Documentation + +- [The structure of the validator](https://pb33f.io/libopenapi/validation/#the-structure-of-the-validator) + - [Validation errors](https://pb33f.io/libopenapi/validation/#validation-errors) + - [Schema errors](https://pb33f.io/libopenapi/validation/#schema-errors) + - [High-level validation](https://pb33f.io/libopenapi/validation/#high-level-validation) +- [Validating http.Request](https://pb33f.io/libopenapi/validation/#validating-httprequest) +- [Validating http.Request and http.Response](https://pb33f.io/libopenapi/validation/#validating-httprequest-and-httpresponse) +- [Validating just http.Response](https://pb33f.io/libopenapi/validation/#validating-just-httpresponse) +- [Validating HTTP Parameters](https://pb33f.io/libopenapi/validation/#validating-http-parameters) +- [Validating an OpenAPI document](https://pb33f.io/libopenapi/validation/#validating-an-openapi-document) +- [Validating Schemas](https://pb33f.io/libopenapi/validation/#validating-schemas) + +[libopenapi](https://github.com/pb33f/libopenapi) and [libopenapi-validator](https://github.com/pb33f/libopenapi-validator) are +products of Princess Beef Heavy Industries, LLC diff --git a/vendor/github.com/pb33f/libopenapi-validator/cache/cache.go b/vendor/github.com/pb33f/libopenapi-validator/cache/cache.go new file mode 100644 index 000000000..7cbdd4934 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/cache/cache.go @@ -0,0 +1,29 @@ +// Copyright 2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package cache + +import ( + "github.com/pb33f/libopenapi/datamodel/high/base" + "github.com/santhosh-tekuri/jsonschema/v6" + "go.yaml.in/yaml/v4" +) + +// SchemaCacheEntry holds a compiled schema and its intermediate representations. +// This is stored in the cache to avoid re-rendering and re-compiling schemas on each request. +type SchemaCacheEntry struct { + Schema *base.Schema + RenderedInline []byte + ReferenceSchema string // String version of RenderedInline + RenderedJSON []byte + CompiledSchema *jsonschema.Schema + RenderedNode *yaml.Node +} + +// SchemaCache defines the interface for schema caching implementations. +// The key is a uint64 hash of the schema (from schema.GoLow().Hash()). +type SchemaCache interface { + Load(key uint64) (*SchemaCacheEntry, bool) + Store(key uint64, value *SchemaCacheEntry) + Range(f func(key uint64, value *SchemaCacheEntry) bool) +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/cache/default_cache.go b/vendor/github.com/pb33f/libopenapi-validator/cache/default_cache.go new file mode 100644 index 000000000..c27211c7e --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/cache/default_cache.go @@ -0,0 +1,54 @@ +package cache + +import "sync" + +// DefaultCache is the default cache implementation using sync.Map for thread-safe concurrent access. +type DefaultCache struct { + m *sync.Map +} + +var _ SchemaCache = &DefaultCache{} + +// NewDefaultCache creates a new DefaultCache with an initialized sync.Map. +func NewDefaultCache() *DefaultCache { + return &DefaultCache{m: &sync.Map{}} +} + +// Load retrieves a schema from the cache. +func (c *DefaultCache) Load(key uint64) (*SchemaCacheEntry, bool) { + if c == nil || c.m == nil { + return nil, false + } + val, ok := c.m.Load(key) + if !ok { + return nil, false + } + schemaCache, ok := val.(*SchemaCacheEntry) + return schemaCache, ok +} + +// Store saves a schema to the cache. +func (c *DefaultCache) Store(key uint64, value *SchemaCacheEntry) { + if c == nil || c.m == nil { + return + } + c.m.Store(key, value) +} + +// Range calls f for each entry in the cache (for testing/inspection). +func (c *DefaultCache) Range(f func(key uint64, value *SchemaCacheEntry) bool) { + if c == nil || c.m == nil { + return + } + c.m.Range(func(k, v interface{}) bool { + key, ok := k.(uint64) + if !ok { + return true + } + val, ok := v.(*SchemaCacheEntry) + if !ok { + return true + } + return f(key, val) + }) +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/config/config.go b/vendor/github.com/pb33f/libopenapi-validator/config/config.go new file mode 100644 index 000000000..f46acce09 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/config/config.go @@ -0,0 +1,247 @@ +package config + +import ( + "log/slog" + + "github.com/santhosh-tekuri/jsonschema/v6" + + "github.com/pb33f/libopenapi-validator/cache" +) + +// RegexCache can be set to enable compiled regex caching. +// It can be just a sync.Map, or a custom implementation with possible cleanup. +// +// Be aware that the cache should be thread safe +type RegexCache interface { + Load(key any) (value any, ok bool) // Get a compiled regex from the cache + Store(key, value any) // Set a compiled regex to the cache +} + +// ValidationOptions A container for validation configuration. +// +// Generally fluent With... style functions are used to establish the desired behavior. +type ValidationOptions struct { + RegexEngine jsonschema.RegexpEngine + RegexCache RegexCache // Enable compiled regex caching + FormatAssertions bool + ContentAssertions bool + SecurityValidation bool + OpenAPIMode bool // Enable OpenAPI-specific vocabulary validation + AllowScalarCoercion bool // Enable string->boolean/number coercion + Formats map[string]func(v any) error + SchemaCache cache.SchemaCache // Optional cache for compiled schemas + Logger *slog.Logger // Logger for debug/error output (nil = silent) + + // strict mode options - detect undeclared properties even when additionalProperties: true + StrictMode bool // Enable strict property validation + StrictIgnorePaths []string // Instance JSONPath patterns to exclude from strict checks + StrictIgnoredHeaders []string // Headers to always ignore in strict mode (nil = use defaults) + strictIgnoredHeadersMerge bool // Internal: true if merging with defaults +} + +// Option Enables an 'Options pattern' approach +type Option func(*ValidationOptions) + +// NewValidationOptions creates a new ValidationOptions instance with default values. +func NewValidationOptions(opts ...Option) *ValidationOptions { + // create the set of default values + o := &ValidationOptions{ + FormatAssertions: false, + ContentAssertions: false, + SecurityValidation: true, + OpenAPIMode: true, // Enable OpenAPI vocabulary by default + SchemaCache: cache.NewDefaultCache(), // Enable caching by default + } + + for _, opt := range opts { + if opt != nil { + opt(o) + } + } + return o +} + +// WithExistingOpts returns an Option that will copy the values from the supplied ValidationOptions instance +func WithExistingOpts(options *ValidationOptions) Option { + return func(o *ValidationOptions) { + if options != nil { + o.RegexEngine = options.RegexEngine + o.RegexCache = options.RegexCache + o.FormatAssertions = options.FormatAssertions + o.ContentAssertions = options.ContentAssertions + o.SecurityValidation = options.SecurityValidation + o.OpenAPIMode = options.OpenAPIMode + o.AllowScalarCoercion = options.AllowScalarCoercion + o.Formats = options.Formats + o.SchemaCache = options.SchemaCache + o.Logger = options.Logger + o.StrictMode = options.StrictMode + o.StrictIgnorePaths = options.StrictIgnorePaths + o.StrictIgnoredHeaders = options.StrictIgnoredHeaders + o.strictIgnoredHeadersMerge = options.strictIgnoredHeadersMerge + } + } +} + +// WithLogger sets the logger for validation debug/error output. +// If not set, logging is silent (nil logger is handled gracefully). +func WithLogger(logger *slog.Logger) Option { + return func(o *ValidationOptions) { + o.Logger = logger + } +} + +// WithRegexEngine Assigns a custom regular-expression engine to be used during validation. +func WithRegexEngine(engine jsonschema.RegexpEngine) Option { + return func(o *ValidationOptions) { + o.RegexEngine = engine + } +} + +// WithRegexCache assigns a cache for compiled regular expressions. +// A sync.Map should be sufficient for most use cases. It does not implement any cleanup +func WithRegexCache(regexCache RegexCache) Option { + return func(o *ValidationOptions) { + o.RegexCache = regexCache + } +} + +// WithFormatAssertions enables checks for 'format' assertions (such as date, date-time, uuid, etc) +func WithFormatAssertions() Option { + return func(o *ValidationOptions) { + o.FormatAssertions = true + } +} + +// WithContentAssertions enables checks for contentType, contentEncoding, etc +func WithContentAssertions() Option { + return func(o *ValidationOptions) { + o.ContentAssertions = true + } +} + +// WithoutSecurityValidation disables security validation for request validation +func WithoutSecurityValidation() Option { + return func(o *ValidationOptions) { + o.SecurityValidation = false + } +} + +// WithCustomFormat adds custom formats and their validators that checks for custom 'format' assertions +// When you add different validators with the same name, they will be overridden, +// and only the last registration will take effect. +func WithCustomFormat(name string, validator func(v any) error) Option { + return func(o *ValidationOptions) { + if o.Formats == nil { + o.Formats = make(map[string]func(v any) error) + } + + o.Formats[name] = validator + } +} + +// WithOpenAPIMode enables OpenAPI-specific keyword validation (default: true) +func WithOpenAPIMode() Option { + return func(o *ValidationOptions) { + o.OpenAPIMode = true + } +} + +// WithoutOpenAPIMode disables OpenAPI-specific keyword validation +func WithoutOpenAPIMode() Option { + return func(o *ValidationOptions) { + o.OpenAPIMode = false + } +} + +// WithScalarCoercion enables string to boolean/number coercion (Jackson-style) +func WithScalarCoercion() Option { + return func(o *ValidationOptions) { + o.AllowScalarCoercion = true + } +} + +// WithSchemaCache sets a custom cache implementation or disables caching if nil. +// Pass nil to disable schema caching and skip cache warming during validator initialization. +// The default cache is a thread-safe sync.Map wrapper. +func WithSchemaCache(cache cache.SchemaCache) Option { + return func(o *ValidationOptions) { + o.SchemaCache = cache + } +} + +// WithStrictMode enables strict property validation. +// In strict mode, undeclared properties are reported as errors even when +// additionalProperties: true would normally allow them. +// +// This is useful for API governance scenarios where you want to ensure +// clients only send properties that are explicitly documented in the +// OpenAPI specification. +func WithStrictMode() Option { + return func(o *ValidationOptions) { + o.StrictMode = true + } +} + +// WithStrictIgnorePaths sets JSONPath patterns for paths to exclude from strict validation. +// Patterns use glob syntax: +// - * matches a single path segment +// - ** matches any depth (zero or more segments) +// - [*] matches any array index +// - \* escapes a literal asterisk +// +// Examples: +// - "$.body.metadata.*" - any property under metadata +// - "$.body.**.x-*" - any x-* property at any depth +// - "$.headers.X-*" - any header starting with X- +func WithStrictIgnorePaths(paths ...string) Option { + return func(o *ValidationOptions) { + o.StrictIgnorePaths = paths + } +} + +// WithStrictIgnoredHeaders replaces the default ignored headers list entirely. +// Use this to fully control which headers are ignored in strict mode. +// For the default list, see the strict package's DefaultIgnoredHeaders. +func WithStrictIgnoredHeaders(headers ...string) Option { + return func(o *ValidationOptions) { + o.StrictIgnoredHeaders = headers + o.strictIgnoredHeadersMerge = false + } +} + +// WithStrictIgnoredHeadersExtra adds headers to the default ignored list. +// Unlike WithStrictIgnoredHeaders, this merges with the defaults rather +// than replacing them. +func WithStrictIgnoredHeadersExtra(headers ...string) Option { + return func(o *ValidationOptions) { + o.StrictIgnoredHeaders = headers + o.strictIgnoredHeadersMerge = true + } +} + +// defaultIgnoredHeaders contains standard HTTP headers ignored by default. +// This is the fallback list used when no custom headers are configured. +var defaultIgnoredHeaders = []string{ + "content-type", "content-length", "accept", "authorization", + "user-agent", "host", "connection", "accept-encoding", + "accept-language", "cache-control", "pragma", "origin", + "referer", "cookie", "date", "etag", "expires", + "if-match", "if-none-match", "if-modified-since", + "last-modified", "transfer-encoding", "vary", "x-forwarded-for", + "x-forwarded-proto", "x-real-ip", "x-request-id", + "request-start-time", // Added by some API clients for timing +} + +// GetEffectiveStrictIgnoredHeaders returns the list of headers to ignore +// based on configuration. Returns defaults if not configured, merged list +// if extra headers were added, or replaced list if headers were fully replaced. +func (o *ValidationOptions) GetEffectiveStrictIgnoredHeaders() []string { + if o.StrictIgnoredHeaders == nil { + return defaultIgnoredHeaders + } + if o.strictIgnoredHeadersMerge { + return append(defaultIgnoredHeaders, o.StrictIgnoredHeaders...) + } + return o.StrictIgnoredHeaders +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/errors/error_utilities.go b/vendor/github.com/pb33f/libopenapi-validator/errors/error_utilities.go new file mode 100644 index 000000000..787ec1b42 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/errors/error_utilities.go @@ -0,0 +1,16 @@ +package errors + +import ( + "net/http" +) + +// PopulateValidationErrors mutates the provided validation errors with additional useful error information, that is +// not necessarily available when the ValidationError was created and are standard for all errors. +// Specifically, the RequestPath, SpecPath and RequestMethod are populated. +func PopulateValidationErrors(validationErrors []*ValidationError, request *http.Request, path string) { + for _, validationError := range validationErrors { + validationError.SpecPath = path + validationError.RequestMethod = request.Method + validationError.RequestPath = request.URL.Path + } +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/errors/package.go b/vendor/github.com/pb33f/libopenapi-validator/errors/package.go new file mode 100644 index 000000000..19acc14e5 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/errors/package.go @@ -0,0 +1,5 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +// Package errors contains all the error types used by the validator +package errors diff --git a/vendor/github.com/pb33f/libopenapi-validator/errors/parameter_errors.go b/vendor/github.com/pb33f/libopenapi-validator/errors/parameter_errors.go new file mode 100644 index 000000000..a473d1b1d --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/errors/parameter_errors.go @@ -0,0 +1,677 @@ +package errors + +import ( + "fmt" + "net/url" + "strings" + + "github.com/pb33f/libopenapi/datamodel/high/base" + + v3 "github.com/pb33f/libopenapi/datamodel/high/v3" + + "github.com/pb33f/libopenapi-validator/helpers" +) + +func IncorrectFormEncoding(param *v3.Parameter, qp *helpers.QueryParam, i int) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationQuery, + Message: fmt.Sprintf("Query parameter '%s' is not exploded correctly", param.Name), + Reason: fmt.Sprintf("The query parameter '%s' has a default or 'form' encoding defined, "+ + "however the value '%s' is encoded as an object or an array using commas. The contract defines "+ + "the explode value to set to 'true'", param.Name, qp.Values[i]), + SpecLine: param.GoLow().Explode.ValueNode.Line, + SpecCol: param.GoLow().Explode.ValueNode.Column, + ParameterName: param.Name, + Context: param, + HowToFix: fmt.Sprintf(HowToFixParamInvalidFormEncode, + helpers.CollapseCSVIntoFormStyle(param.Name, qp.Values[i])), + } +} + +func IncorrectSpaceDelimiting(param *v3.Parameter, qp *helpers.QueryParam) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationQuery, + Message: fmt.Sprintf("Query parameter '%s' delimited incorrectly", param.Name), + Reason: fmt.Sprintf("The query parameter '%s' has 'spaceDelimited' style defined, "+ + "and explode is defined as false. There are multiple values (%d) supplied, instead of a single"+ + " space delimited value", param.Name, len(qp.Values)), + SpecLine: param.GoLow().Style.ValueNode.Line, + SpecCol: param.GoLow().Style.ValueNode.Column, + ParameterName: param.Name, + Context: param, + HowToFix: fmt.Sprintf(HowToFixParamInvalidSpaceDelimitedObjectExplode, + helpers.CollapseCSVIntoSpaceDelimitedStyle(param.Name, qp.Values)), + } +} + +func IncorrectPipeDelimiting(param *v3.Parameter, qp *helpers.QueryParam) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationQuery, + Message: fmt.Sprintf("Query parameter '%s' delimited incorrectly", param.Name), + Reason: fmt.Sprintf("The query parameter '%s' has 'pipeDelimited' style defined, "+ + "and explode is defined as false. There are multiple values (%d) supplied, instead of a single"+ + " space delimited value", param.Name, len(qp.Values)), + SpecLine: param.GoLow().Style.ValueNode.Line, + SpecCol: param.GoLow().Style.ValueNode.Column, + ParameterName: param.Name, + Context: param, + HowToFix: fmt.Sprintf(HowToFixParamInvalidPipeDelimitedObjectExplode, + helpers.CollapseCSVIntoPipeDelimitedStyle(param.Name, qp.Values)), + } +} + +func InvalidDeepObject(param *v3.Parameter, qp *helpers.QueryParam) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationQuery, + Message: fmt.Sprintf("Query parameter '%s' is not a valid deepObject", param.Name), + Reason: fmt.Sprintf("The query parameter '%s' has the 'deepObject' style defined, "+ + "There are multiple values (%d) supplied, instead of a single "+ + "value", param.Name, len(qp.Values)), + SpecLine: param.GoLow().Style.ValueNode.Line, + SpecCol: param.GoLow().Style.ValueNode.Column, + ParameterName: param.Name, + Context: param, + HowToFix: fmt.Sprintf(HowToFixParamInvalidDeepObjectMultipleValues, + helpers.CollapseCSVIntoPipeDelimitedStyle(param.Name, qp.Values)), + } +} + +func QueryParameterMissing(param *v3.Parameter) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationQuery, + Message: fmt.Sprintf("Query parameter '%s' is missing", param.Name), + Reason: fmt.Sprintf("The query parameter '%s' is defined as being required, "+ + "however it's missing from the requests", param.Name), + SpecLine: param.GoLow().Required.KeyNode.Line, + SpecCol: param.GoLow().Required.KeyNode.Column, + ParameterName: param.Name, + HowToFix: HowToFixMissingValue, + } +} + +func HeaderParameterMissing(param *v3.Parameter) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationHeader, + Message: fmt.Sprintf("Header parameter '%s' is missing", param.Name), + Reason: fmt.Sprintf("The header parameter '%s' is defined as being required, "+ + "however it's missing from the requests", param.Name), + SpecLine: param.GoLow().Required.KeyNode.Line, + SpecCol: param.GoLow().Required.KeyNode.Column, + ParameterName: param.Name, + HowToFix: HowToFixMissingValue, + } +} + +func CookieParameterMissing(param *v3.Parameter) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationCookie, + Message: fmt.Sprintf("Cookie parameter '%s' is missing", param.Name), + Reason: fmt.Sprintf("The cookie parameter '%s' is defined as being required, "+ + "however it's missing from the request", param.Name), + SpecLine: param.GoLow().Required.KeyNode.Line, + SpecCol: param.GoLow().Required.KeyNode.Column, + ParameterName: param.Name, + HowToFix: HowToFixMissingValue, + } +} + +func HeaderParameterCannotBeDecoded(param *v3.Parameter, val string) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationHeader, + Message: fmt.Sprintf("Header parameter '%s' cannot be decoded", param.Name), + Reason: fmt.Sprintf("The header parameter '%s' cannot be "+ + "extracted into an object, '%s' is malformed", param.Name, val), + SpecLine: param.GoLow().Schema.Value.Schema().Type.KeyNode.Line, + SpecCol: param.GoLow().Schema.Value.Schema().Type.KeyNode.Line, + ParameterName: param.Name, + HowToFix: HowToFixInvalidEncoding, + } +} + +func IncorrectHeaderParamEnum(param *v3.Parameter, ef string, sch *base.Schema) *ValidationError { + var enums []string + for i := range sch.Enum { + enums = append(enums, fmt.Sprint(sch.Enum[i].Value)) + } + validEnums := strings.Join(enums, ", ") + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationHeader, + Message: fmt.Sprintf("Header parameter '%s' does not match allowed values", param.Name), + Reason: fmt.Sprintf("The header parameter '%s' has pre-defined "+ + "values set via an enum. The value '%s' is not one of those values.", param.Name, ef), + SpecLine: param.GoLow().Schema.Value.Schema().Enum.KeyNode.Line, + SpecCol: param.GoLow().Schema.Value.Schema().Enum.KeyNode.Column, + ParameterName: param.Name, + Context: sch, + HowToFix: fmt.Sprintf(HowToFixParamInvalidEnum, ef, validEnums), + } +} + +func IncorrectQueryParamArrayBoolean( + param *v3.Parameter, item string, sch *base.Schema, itemsSchema *base.Schema, +) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationQuery, + Message: fmt.Sprintf("Query array parameter '%s' is not a valid boolean", param.Name), + Reason: fmt.Sprintf("The query parameter (which is an array) '%s' is defined as being a boolean, "+ + "however the value '%s' is not a valid true/false value", param.Name, item), + SpecLine: sch.Items.A.GoLow().Schema().Type.KeyNode.Line, + SpecCol: sch.Items.A.GoLow().Schema().Type.KeyNode.Column, + ParameterName: param.Name, + Context: itemsSchema, + HowToFix: fmt.Sprintf(HowToFixParamInvalidBoolean, item), + } +} + +func IncorrectParamArrayMaxNumItems(param *v3.Parameter, sch *base.Schema, expected, actual int64) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationQuery, + Message: fmt.Sprintf("Query array parameter '%s' has too many items", param.Name), + Reason: fmt.Sprintf("The query parameter (which is an array) '%s' has a maximum item length of %d, "+ + "however the request provided %d items", param.Name, expected, actual), + SpecLine: sch.Items.A.GoLow().Schema().Type.KeyNode.Line, + SpecCol: sch.Items.A.GoLow().Schema().Type.KeyNode.Column, + ParameterName: param.Name, + Context: sch, + HowToFix: fmt.Sprintf(HowToFixInvalidMaxItems, expected), + } +} + +func IncorrectParamArrayMinNumItems(param *v3.Parameter, sch *base.Schema, expected, actual int64) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationQuery, + Message: fmt.Sprintf("Query array parameter '%s' does not have enough items", param.Name), + Reason: fmt.Sprintf("The query parameter (which is an array) '%s' has a minimum items length of %d, "+ + "however the request provided %d items", param.Name, expected, actual), + SpecLine: sch.Items.A.GoLow().Schema().Type.KeyNode.Line, + SpecCol: sch.Items.A.GoLow().Schema().Type.KeyNode.Column, + ParameterName: param.Name, + Context: sch, + HowToFix: fmt.Sprintf(HowToFixInvalidMinItems, expected), + } +} + +func IncorrectParamArrayUniqueItems(param *v3.Parameter, sch *base.Schema, duplicates string) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationQuery, + Message: fmt.Sprintf("Query array parameter '%s' contains non-unique items", param.Name), + Reason: fmt.Sprintf("The query parameter (which is an array) '%s' contains the following duplicates: '%s'", param.Name, duplicates), + SpecLine: sch.Items.A.GoLow().Schema().Type.KeyNode.Line, + SpecCol: sch.Items.A.GoLow().Schema().Type.KeyNode.Column, + ParameterName: param.Name, + Context: sch, + HowToFix: "Ensure the array values are all unique", + } +} + +func IncorrectCookieParamArrayBoolean( + param *v3.Parameter, item string, sch *base.Schema, itemsSchema *base.Schema, +) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationCookie, + Message: fmt.Sprintf("Cookie array parameter '%s' is not a valid boolean", param.Name), + Reason: fmt.Sprintf("The cookie parameter (which is an array) '%s' is defined as being a boolean, "+ + "however the value '%s' is not a valid true/false value", param.Name, item), + SpecLine: sch.Items.A.GoLow().Schema().Type.KeyNode.Line, + SpecCol: sch.Items.A.GoLow().Schema().Type.KeyNode.Column, + ParameterName: param.Name, + Context: itemsSchema, + HowToFix: fmt.Sprintf(HowToFixParamInvalidBoolean, item), + } +} + +func IncorrectQueryParamArrayInteger( + param *v3.Parameter, item string, sch *base.Schema, itemsSchema *base.Schema, +) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationQuery, + Message: fmt.Sprintf("Query array parameter '%s' is not a valid integer", param.Name), + Reason: fmt.Sprintf("The query parameter (which is an array) '%s' is defined as being an integer, "+ + "however the value '%s' is not a valid integer", param.Name, item), + SpecLine: sch.Items.A.GoLow().Schema().Type.KeyNode.Line, + SpecCol: sch.Items.A.GoLow().Schema().Type.KeyNode.Column, + ParameterName: param.Name, + Context: itemsSchema, + HowToFix: fmt.Sprintf(HowToFixParamInvalidInteger, item), + } +} + +func IncorrectQueryParamArrayNumber( + param *v3.Parameter, item string, sch *base.Schema, itemsSchema *base.Schema, +) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationQuery, + Message: fmt.Sprintf("Query array parameter '%s' is not a valid number", param.Name), + Reason: fmt.Sprintf("The query parameter (which is an array) '%s' is defined as being a number, "+ + "however the value '%s' is not a valid number", param.Name, item), + SpecLine: sch.Items.A.GoLow().Schema().Type.KeyNode.Line, + SpecCol: sch.Items.A.GoLow().Schema().Type.KeyNode.Column, + ParameterName: param.Name, + Context: itemsSchema, + HowToFix: fmt.Sprintf(HowToFixParamInvalidNumber, item), + } +} + +func IncorrectCookieParamArrayNumber( + param *v3.Parameter, item string, sch *base.Schema, itemsSchema *base.Schema, +) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationCookie, + Message: fmt.Sprintf("Cookie array parameter '%s' is not a valid number", param.Name), + Reason: fmt.Sprintf("The cookie parameter (which is an array) '%s' is defined as being a number, "+ + "however the value '%s' is not a valid number", param.Name, item), + SpecLine: sch.Items.A.GoLow().Schema().Type.KeyNode.Line, + SpecCol: sch.Items.A.GoLow().Schema().Type.KeyNode.Column, + ParameterName: param.Name, + Context: itemsSchema, + HowToFix: fmt.Sprintf(HowToFixParamInvalidNumber, item), + } +} + +func IncorrectParamEncodingJSON(param *v3.Parameter, ef string, sch *base.Schema) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationQuery, + Message: fmt.Sprintf("Query parameter '%s' is not valid JSON", param.Name), + Reason: fmt.Sprintf("The query parameter '%s' is defined as being a JSON object, "+ + "however the value '%s' is not valid JSON", param.Name, ef), + SpecLine: param.GoLow().FindContent(helpers.JSONContentType).ValueNode.Line, + SpecCol: param.GoLow().FindContent(helpers.JSONContentType).ValueNode.Column, + ParameterName: param.Name, + Context: sch, + HowToFix: HowToFixInvalidJSON, + } +} + +func IncorrectQueryParamBool(param *v3.Parameter, ef string, sch *base.Schema) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationQuery, + Message: fmt.Sprintf("Query parameter '%s' is not a valid boolean", param.Name), + Reason: fmt.Sprintf("The query parameter '%s' is defined as being a boolean, "+ + "however the value '%s' is not a valid boolean", param.Name, ef), + SpecLine: param.GoLow().Schema.KeyNode.Line, + SpecCol: param.GoLow().Schema.KeyNode.Column, + ParameterName: param.Name, + Context: sch, + HowToFix: fmt.Sprintf(HowToFixParamInvalidBoolean, ef), + } +} + +func InvalidQueryParamInteger(param *v3.Parameter, ef string, sch *base.Schema) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationQuery, + Message: fmt.Sprintf("Query parameter '%s' is not a valid integer", param.Name), + Reason: fmt.Sprintf("The query parameter '%s' is defined as being an integer, "+ + "however the value '%s' is not a valid integer", param.Name, ef), + SpecLine: param.GoLow().Schema.KeyNode.Line, + SpecCol: param.GoLow().Schema.KeyNode.Column, + ParameterName: param.Name, + Context: sch, + HowToFix: fmt.Sprintf(HowToFixParamInvalidInteger, ef), + } +} + +func InvalidQueryParamNumber(param *v3.Parameter, ef string, sch *base.Schema) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationQuery, + Message: fmt.Sprintf("Query parameter '%s' is not a valid number", param.Name), + Reason: fmt.Sprintf("The query parameter '%s' is defined as being a number, "+ + "however the value '%s' is not a valid number", param.Name, ef), + SpecLine: param.GoLow().Schema.KeyNode.Line, + SpecCol: param.GoLow().Schema.KeyNode.Column, + ParameterName: param.Name, + Context: sch, + HowToFix: fmt.Sprintf(HowToFixParamInvalidNumber, ef), + } +} + +func IncorrectQueryParamEnum(param *v3.Parameter, ef string, sch *base.Schema) *ValidationError { + var enums []string + for i := range sch.Enum { + enums = append(enums, fmt.Sprint(sch.Enum[i].Value)) + } + validEnums := strings.Join(enums, ", ") + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationQuery, + Message: fmt.Sprintf("Query parameter '%s' does not match allowed values", param.Name), + Reason: fmt.Sprintf("The query parameter '%s' has pre-defined "+ + "values set via an enum. The value '%s' is not one of those values.", param.Name, ef), + SpecLine: param.GoLow().Schema.Value.Schema().Enum.KeyNode.Line, + SpecCol: param.GoLow().Schema.Value.Schema().Enum.KeyNode.Column, + ParameterName: param.Name, + Context: sch, + HowToFix: fmt.Sprintf(HowToFixParamInvalidEnum, ef, validEnums), + } +} + +func IncorrectQueryParamEnumArray(param *v3.Parameter, ef string, sch *base.Schema) *ValidationError { + var enums []string + // look at that model fly! + for i := range param.GoLow().Schema.Value.Schema().Items.Value.A.Schema().Enum.Value { + enums = append(enums, + fmt.Sprint(param.GoLow().Schema.Value.Schema().Items.Value.A.Schema().Enum.Value[i].Value.Value)) + } + validEnums := strings.Join(enums, ", ") + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationQuery, + Message: fmt.Sprintf("Query array parameter '%s' does not match allowed values", param.Name), + Reason: fmt.Sprintf("The query array parameter '%s' has pre-defined "+ + "values set via an enum. The value '%s' is not one of those values.", param.Name, ef), + SpecLine: param.GoLow().Schema.Value.Schema().Items.Value.A.Schema().Enum.KeyNode.Line, + SpecCol: param.GoLow().Schema.Value.Schema().Items.Value.A.Schema().Enum.KeyNode.Line, + ParameterName: param.Name, + Context: sch, + HowToFix: fmt.Sprintf(HowToFixParamInvalidEnum, ef, validEnums), + } +} + +func IncorrectReservedValues(param *v3.Parameter, ef string, sch *base.Schema) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationQuery, + Message: fmt.Sprintf("Query parameter '%s' value contains reserved values", param.Name), + Reason: fmt.Sprintf("The query parameter '%s' has 'allowReserved' set to false, "+ + "however the value '%s' contains one of the following characters: :/?#[]@!$&'()*+,;=", param.Name, ef), + SpecLine: param.GoLow().Schema.KeyNode.Line, + SpecCol: param.GoLow().Schema.KeyNode.Column, + ParameterName: param.Name, + Context: sch, + HowToFix: fmt.Sprintf(HowToFixReservedValues, url.QueryEscape(ef)), + } +} + +func InvalidHeaderParamInteger(param *v3.Parameter, ef string, sch *base.Schema) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationHeader, + Message: fmt.Sprintf("Header parameter '%s' is not a valid integer", param.Name), + Reason: fmt.Sprintf("The header parameter '%s' is defined as being an integer, "+ + "however the value '%s' is not a valid integer", param.Name, ef), + SpecLine: param.GoLow().Schema.KeyNode.Line, + SpecCol: param.GoLow().Schema.KeyNode.Column, + ParameterName: param.Name, + Context: sch, + HowToFix: fmt.Sprintf(HowToFixParamInvalidInteger, ef), + } +} + +func InvalidHeaderParamNumber(param *v3.Parameter, ef string, sch *base.Schema) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationHeader, + Message: fmt.Sprintf("Header parameter '%s' is not a valid number", param.Name), + Reason: fmt.Sprintf("The header parameter '%s' is defined as being a number, "+ + "however the value '%s' is not a valid number", param.Name, ef), + SpecLine: param.GoLow().Schema.KeyNode.Line, + SpecCol: param.GoLow().Schema.KeyNode.Column, + ParameterName: param.Name, + Context: sch, + HowToFix: fmt.Sprintf(HowToFixParamInvalidNumber, ef), + } +} + +func InvalidCookieParamInteger(param *v3.Parameter, ef string, sch *base.Schema) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationCookie, + Message: fmt.Sprintf("Cookie parameter '%s' is not a valid integer", param.Name), + Reason: fmt.Sprintf("The cookie parameter '%s' is defined as being an integer, "+ + "however the value '%s' is not a valid integer", param.Name, ef), + SpecLine: param.GoLow().Schema.KeyNode.Line, + SpecCol: param.GoLow().Schema.KeyNode.Column, + ParameterName: param.Name, + Context: sch, + HowToFix: fmt.Sprintf(HowToFixParamInvalidInteger, ef), + } +} + +func InvalidCookieParamNumber(param *v3.Parameter, ef string, sch *base.Schema) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationCookie, + Message: fmt.Sprintf("Cookie parameter '%s' is not a valid number", param.Name), + Reason: fmt.Sprintf("The cookie parameter '%s' is defined as being a number, "+ + "however the value '%s' is not a valid number", param.Name, ef), + SpecLine: param.GoLow().Schema.KeyNode.Line, + SpecCol: param.GoLow().Schema.KeyNode.Column, + ParameterName: param.Name, + Context: sch, + HowToFix: fmt.Sprintf(HowToFixParamInvalidNumber, ef), + } +} + +func IncorrectHeaderParamBool(param *v3.Parameter, ef string, sch *base.Schema) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationHeader, + Message: fmt.Sprintf("Header parameter '%s' is not a valid boolean", param.Name), + Reason: fmt.Sprintf("The header parameter '%s' is defined as being a boolean, "+ + "however the value '%s' is not a valid boolean", param.Name, ef), + SpecLine: param.GoLow().Schema.KeyNode.Line, + SpecCol: param.GoLow().Schema.KeyNode.Column, + ParameterName: param.Name, + Context: sch, + HowToFix: fmt.Sprintf(HowToFixParamInvalidBoolean, ef), + } +} + +func IncorrectCookieParamBool(param *v3.Parameter, ef string, sch *base.Schema) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationCookie, + Message: fmt.Sprintf("Cookie parameter '%s' is not a valid boolean", param.Name), + Reason: fmt.Sprintf("The cookie parameter '%s' is defined as being a boolean, "+ + "however the value '%s' is not a valid boolean", param.Name, ef), + SpecLine: param.GoLow().Schema.KeyNode.Line, + SpecCol: param.GoLow().Schema.KeyNode.Column, + ParameterName: param.Name, + Context: sch, + HowToFix: fmt.Sprintf(HowToFixParamInvalidBoolean, ef), + } +} + +func IncorrectCookieParamEnum(param *v3.Parameter, ef string, sch *base.Schema) *ValidationError { + var enums []string + for i := range sch.Enum { + enums = append(enums, fmt.Sprint(sch.Enum[i].Value)) + } + validEnums := strings.Join(enums, ", ") + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationCookie, + Message: fmt.Sprintf("Cookie parameter '%s' does not match allowed values", param.Name), + Reason: fmt.Sprintf("The cookie parameter '%s' has pre-defined "+ + "values set via an enum. The value '%s' is not one of those values.", param.Name, ef), + SpecLine: param.GoLow().Schema.Value.Schema().Enum.KeyNode.Line, + SpecCol: param.GoLow().Schema.Value.Schema().Enum.KeyNode.Column, + ParameterName: param.Name, + Context: sch, + HowToFix: fmt.Sprintf(HowToFixParamInvalidEnum, ef, validEnums), + } +} + +func IncorrectHeaderParamArrayBoolean( + param *v3.Parameter, item string, sch *base.Schema, itemsSchema *base.Schema, +) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationHeader, + Message: fmt.Sprintf("Header array parameter '%s' is not a valid boolean", param.Name), + Reason: fmt.Sprintf("The header parameter (which is an array) '%s' is defined as being a boolean, "+ + "however the value '%s' is not a valid true/false value", param.Name, item), + SpecLine: sch.Items.A.GoLow().Schema().Type.KeyNode.Line, + SpecCol: sch.Items.A.GoLow().Schema().Type.KeyNode.Column, + ParameterName: param.Name, + Context: itemsSchema, + HowToFix: fmt.Sprintf(HowToFixParamInvalidBoolean, item), + } +} + +func IncorrectHeaderParamArrayNumber( + param *v3.Parameter, item string, sch *base.Schema, itemsSchema *base.Schema, +) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationHeader, + Message: fmt.Sprintf("Header array parameter '%s' is not a valid number", param.Name), + Reason: fmt.Sprintf("The header parameter (which is an array) '%s' is defined as being a number, "+ + "however the value '%s' is not a valid number", param.Name, item), + SpecLine: sch.Items.A.GoLow().Schema().Type.KeyNode.Line, + SpecCol: sch.Items.A.GoLow().Schema().Type.KeyNode.Column, + ParameterName: param.Name, + Context: itemsSchema, + HowToFix: fmt.Sprintf(HowToFixParamInvalidNumber, item), + } +} + +func IncorrectPathParamBool(param *v3.Parameter, item string, sch *base.Schema) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationPath, + Message: fmt.Sprintf("Path parameter '%s' is not a valid boolean", param.Name), + Reason: fmt.Sprintf("The path parameter '%s' is defined as being a boolean, "+ + "however the value '%s' is not a valid boolean", param.Name, item), + SpecLine: param.GoLow().Schema.KeyNode.Line, + SpecCol: param.GoLow().Schema.KeyNode.Column, + ParameterName: param.Name, + Context: sch, + HowToFix: fmt.Sprintf(HowToFixParamInvalidBoolean, item), + } +} + +func IncorrectPathParamEnum(param *v3.Parameter, ef string, sch *base.Schema) *ValidationError { + var enums []string + for i := range sch.Enum { + enums = append(enums, fmt.Sprint(sch.Enum[i].Value)) + } + validEnums := strings.Join(enums, ", ") + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationPath, + ParameterName: param.Name, + Message: fmt.Sprintf("Path parameter '%s' does not match allowed values", param.Name), + Reason: fmt.Sprintf("The path parameter '%s' has pre-defined "+ + "values set via an enum. The value '%s' is not one of those values.", param.Name, ef), + SpecLine: param.GoLow().Schema.Value.Schema().Enum.KeyNode.Line, + SpecCol: param.GoLow().Schema.Value.Schema().Enum.KeyNode.Column, + Context: sch, + HowToFix: fmt.Sprintf(HowToFixParamInvalidEnum, ef, validEnums), + } +} + +func IncorrectPathParamInteger(param *v3.Parameter, item string, sch *base.Schema) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationPath, + Message: fmt.Sprintf("Path parameter '%s' is not a valid integer", param.Name), + Reason: fmt.Sprintf("The path parameter '%s' is defined as being an integer, "+ + "however the value '%s' is not a valid integer", param.Name, item), + SpecLine: param.GoLow().Schema.KeyNode.Line, + SpecCol: param.GoLow().Schema.KeyNode.Column, + ParameterName: param.Name, + Context: sch, + HowToFix: fmt.Sprintf(HowToFixParamInvalidInteger, item), + } +} + +func IncorrectPathParamNumber(param *v3.Parameter, item string, sch *base.Schema) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationPath, + Message: fmt.Sprintf("Path parameter '%s' is not a valid number", param.Name), + Reason: fmt.Sprintf("The path parameter '%s' is defined as being a number, "+ + "however the value '%s' is not a valid number", param.Name, item), + SpecLine: param.GoLow().Schema.KeyNode.Line, + SpecCol: param.GoLow().Schema.KeyNode.Column, + ParameterName: param.Name, + Context: sch, + HowToFix: fmt.Sprintf(HowToFixParamInvalidNumber, item), + } +} + +func IncorrectPathParamArrayNumber( + param *v3.Parameter, item string, sch *base.Schema, itemsSchema *base.Schema, +) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationPath, + Message: fmt.Sprintf("Path array parameter '%s' is not a valid number", param.Name), + Reason: fmt.Sprintf("The path parameter (which is an array) '%s' is defined as being a number, "+ + "however the value '%s' is not a valid number", param.Name, item), + SpecLine: sch.Items.A.GoLow().Schema().Type.KeyNode.Line, + SpecCol: sch.Items.A.GoLow().Schema().Type.KeyNode.Column, + ParameterName: param.Name, + Context: itemsSchema, + HowToFix: fmt.Sprintf(HowToFixParamInvalidNumber, item), + } +} + +func IncorrectPathParamArrayInteger( + param *v3.Parameter, item string, sch *base.Schema, itemsSchema *base.Schema, +) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationPath, + Message: fmt.Sprintf("Path array parameter '%s' is not a valid integer", param.Name), + Reason: fmt.Sprintf("The path parameter (which is an array) '%s' is defined as being an integer, "+ + "however the value '%s' is not a valid integer", param.Name, item), + SpecLine: sch.Items.A.GoLow().Schema().Type.KeyNode.Line, + SpecCol: sch.Items.A.GoLow().Schema().Type.KeyNode.Column, + ParameterName: param.Name, + Context: itemsSchema, + HowToFix: fmt.Sprintf(HowToFixParamInvalidNumber, item), + } +} + +func IncorrectPathParamArrayBoolean( + param *v3.Parameter, item string, sch *base.Schema, itemsSchema *base.Schema, +) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationPath, + Message: fmt.Sprintf("Path array parameter '%s' is not a valid boolean", param.Name), + Reason: fmt.Sprintf("The path parameter (which is an array) '%s' is defined as being a boolean, "+ + "however the value '%s' is not a valid boolean", param.Name, item), + SpecLine: sch.Items.A.GoLow().Schema().Type.KeyNode.Line, + SpecCol: sch.Items.A.GoLow().Schema().Type.KeyNode.Column, + ParameterName: param.Name, + Context: itemsSchema, + HowToFix: fmt.Sprintf(HowToFixParamInvalidBoolean, item), + } +} + +func PathParameterMissing(param *v3.Parameter) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ParameterValidation, + ValidationSubType: helpers.ParameterValidationPath, + Message: fmt.Sprintf("Path parameter '%s' is missing", param.Name), + Reason: fmt.Sprintf("The path parameter '%s' is defined as being required, "+ + "however it's missing from the requests", param.Name), + SpecLine: param.GoLow().Required.KeyNode.Line, + SpecCol: param.GoLow().Required.KeyNode.Column, + ParameterName: param.Name, + HowToFix: HowToFixMissingValue, + } +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/errors/parameters_howtofix.go b/vendor/github.com/pb33f/libopenapi-validator/errors/parameters_howtofix.go new file mode 100644 index 000000000..e20a15080 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/errors/parameters_howtofix.go @@ -0,0 +1,33 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package errors + +const ( + HowToFixReservedValues string = "parameter values need to URL Encoded to ensure reserved " + + "values are correctly encoded, for example: '%s'" + HowToFixParamInvalidInteger string = "Convert the value '%s' into an integer" + HowToFixParamInvalidNumber string = "Convert the value '%s' into a number" + HowToFixParamInvalidString string = "Convert the value '%s' into a string (cannot start with a number, or be a floating point)" + HowToFixParamInvalidBoolean string = "Convert the value '%s' into a true/false value" + HowToFixParamInvalidEnum string = "Instead of '%s', use one of the allowed values: '%s'" + HowToFixParamInvalidFormEncode string = "Use a form style encoding for parameter values, for example: '%s'" + HowToFixInvalidSchema string = "Ensure that the object being submitted, matches the schema correctly" + HowToFixParamInvalidSpaceDelimitedObjectExplode string = "When using 'explode' with space delimited parameters, " + + "they should be separated by spaces. For example: '%s'" + HowToFixParamInvalidPipeDelimitedObjectExplode string = "When using 'explode' with pipe delimited parameters, " + + "they should be separated by pipes '|'. For example: '%s'" + HowToFixParamInvalidDeepObjectMultipleValues string = "There can only be a single value per property name, " + + "deepObject parameters should contain the property key in square brackets next to the parameter name. For example: '%s'" + HowToFixInvalidJSON string = "The JSON submitted is invalid, please check the syntax" + HowToFixDecodingError = "The object can't be decoded, so make sure it's being encoded correctly according to the spec." + HowToFixInvalidContentType = "The content type is invalid, Use one of the %d supported types for this operation: %s" + HowToFixInvalidResponseCode = "The service is responding with a code that is not defined in the spec, fix the service or add the code to the specification" + HowToFixInvalidEncoding = "Ensure the correct encoding has been used on the object" + HowToFixMissingValue = "Ensure the value has been set" + HowToFixPath = "Check the path is correct, and check that the correct HTTP method has been used (e.g. GET, POST, PUT, DELETE)" + HowToFixPathMethod = "Add the missing operation to the contract for the path" + HowToFixInvalidMaxItems = "Reduce the number of items in the array to %d or less" + HowToFixInvalidMinItems = "Increase the number of items in the array to %d or more" + HowToFixMissingHeader = "Make sure the service responding sets the required headers with this response code" +) diff --git a/vendor/github.com/pb33f/libopenapi-validator/errors/request_errors.go b/vendor/github.com/pb33f/libopenapi-validator/errors/request_errors.go new file mode 100644 index 000000000..f53c2b8ed --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/errors/request_errors.go @@ -0,0 +1,56 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package errors + +import ( + "fmt" + "net/http" + "strings" + + "github.com/pb33f/libopenapi/orderedmap" + + v3 "github.com/pb33f/libopenapi/datamodel/high/v3" + + "github.com/pb33f/libopenapi-validator/helpers" +) + +func RequestContentTypeNotFound(op *v3.Operation, request *http.Request, specPath string) *ValidationError { + ct := request.Header.Get(helpers.ContentTypeHeader) + var ctypes []string + for pair := orderedmap.First(op.RequestBody.Content); pair != nil; pair = pair.Next() { + ctypes = append(ctypes, pair.Key()) + } + return &ValidationError{ + ValidationType: helpers.RequestBodyValidation, + ValidationSubType: helpers.RequestBodyContentType, + Message: fmt.Sprintf("%s operation request content type '%s' does not exist", + request.Method, ct), + Reason: fmt.Sprintf("The content type '%s' of the %s request submitted has not "+ + "been defined, it's an unknown type", ct, request.Method), + SpecLine: op.RequestBody.GoLow().Content.KeyNode.Line, + SpecCol: op.RequestBody.GoLow().Content.KeyNode.Column, + Context: op, + HowToFix: fmt.Sprintf(HowToFixInvalidContentType, orderedmap.Len(op.RequestBody.Content), strings.Join(ctypes, ", ")), + RequestPath: request.URL.Path, + RequestMethod: request.Method, + SpecPath: specPath, + } +} + +func OperationNotFound(pathItem *v3.PathItem, request *http.Request, method string, specPath string) *ValidationError { + return &ValidationError{ + ValidationType: helpers.RequestValidation, + ValidationSubType: helpers.ValidationMissingOperation, + Message: fmt.Sprintf("%s operation request content type '%s' does not exist", + request.Method, method), + Reason: fmt.Sprintf("The path was found, but there was no '%s' method found in the spec", request.Method), + SpecLine: pathItem.GoLow().KeyNode.Line, + SpecCol: pathItem.GoLow().KeyNode.Column, + Context: pathItem, + HowToFix: HowToFixPathMethod, + RequestPath: request.URL.Path, + RequestMethod: request.Method, + SpecPath: specPath, + } +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/errors/response_errors.go b/vendor/github.com/pb33f/libopenapi-validator/errors/response_errors.go new file mode 100644 index 000000000..a519e8db8 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/errors/response_errors.go @@ -0,0 +1,74 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package errors + +import ( + "fmt" + "net/http" + "strings" + + "github.com/pb33f/libopenapi/orderedmap" + + v3 "github.com/pb33f/libopenapi/datamodel/high/v3" + + "github.com/pb33f/libopenapi-validator/helpers" +) + +func ResponseContentTypeNotFound(op *v3.Operation, + request *http.Request, + response *http.Response, + code string, + isDefault bool, +) *ValidationError { + ct := response.Header.Get(helpers.ContentTypeHeader) + mediaTypeString, _, _ := helpers.ExtractContentType(ct) + var ctypes []string + var specLine, specCol int + var contentMap *orderedmap.Map[string, *v3.MediaType] + + // check for a default type (applies to all codes without a match) + if !isDefault { + for pair := orderedmap.First(op.Responses.Codes.GetOrZero(code).Content); pair != nil; pair = pair.Next() { + ctypes = append(ctypes, pair.Key()) + } + specLine = op.Responses.Codes.GetOrZero(code).GoLow().Content.KeyNode.Line + specCol = op.Responses.Codes.GetOrZero(code).GoLow().Content.KeyNode.Column + contentMap = op.Responses.Codes.GetOrZero(code).Content + } else { + for pair := orderedmap.First(op.Responses.Default.Content); pair != nil; pair = pair.Next() { + ctypes = append(ctypes, pair.Key()) + } + specLine = op.Responses.Default.GoLow().Content.KeyNode.Line + specCol = op.Responses.Default.GoLow().Content.KeyNode.Column + contentMap = op.Responses.Default.Content + } + return &ValidationError{ + ValidationType: helpers.ResponseBodyValidation, + ValidationSubType: helpers.RequestBodyContentType, + Message: fmt.Sprintf("%s / %s operation response content type '%s' does not exist", + request.Method, code, mediaTypeString), + Reason: fmt.Sprintf("The content type '%s' of the %s response received has not "+ + "been defined, it's an unknown type", mediaTypeString, request.Method), + SpecLine: specLine, + SpecCol: specCol, + Context: op, + HowToFix: fmt.Sprintf(HowToFixInvalidContentType, + orderedmap.Len(contentMap), strings.Join(ctypes, ", ")), + } +} + +func ResponseCodeNotFound(op *v3.Operation, request *http.Request, code int) *ValidationError { + return &ValidationError{ + ValidationType: helpers.ResponseBodyValidation, + ValidationSubType: helpers.ResponseBodyResponseCode, + Message: fmt.Sprintf("%s operation request response code '%d' does not exist", + request.Method, code), + Reason: fmt.Sprintf("The response code '%d' of the %s request submitted has not "+ + "been defined, it's an unknown type", code, request.Method), + SpecLine: op.GoLow().Responses.KeyNode.Line, + SpecCol: op.GoLow().Responses.KeyNode.Column, + Context: op, + HowToFix: HowToFixInvalidResponseCode, + } +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/errors/strict_errors.go b/vendor/github.com/pb33f/libopenapi-validator/errors/strict_errors.go new file mode 100644 index 000000000..aac6e3c57 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/errors/strict_errors.go @@ -0,0 +1,154 @@ +// Copyright 2023-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package errors + +import ( + "fmt" + "strings" +) + +// StrictValidationType is the validation type for strict mode errors. +const StrictValidationType = "strict" + +// StrictValidationSubTypes for different kinds of undeclared values. +const ( + StrictSubTypeProperty = "undeclared-property" + StrictSubTypeHeader = "undeclared-header" + StrictSubTypeQuery = "undeclared-query-param" + StrictSubTypeCookie = "undeclared-cookie" +) + +// UndeclaredPropertyError creates a ValidationError for an undeclared property. +func UndeclaredPropertyError( + path string, + name string, + value any, + declaredProperties []string, + direction string, + requestPath string, + requestMethod string, + specLine int, + specCol int, +) *ValidationError { + dirStr := direction + if dirStr == "" { + dirStr = "request" + } + + return &ValidationError{ + ValidationType: StrictValidationType, + ValidationSubType: StrictSubTypeProperty, + Message: fmt.Sprintf("%s property '%s' at '%s' is not declared in schema", + dirStr, name, path), + Reason: fmt.Sprintf("Strict mode: found property not in schema. "+ + "Declared properties: [%s]", strings.Join(declaredProperties, ", ")), + HowToFix: fmt.Sprintf("Add '%s' to the schema, remove it from the %s, "+ + "or add '%s' to StrictIgnorePaths", name, dirStr, path), + RequestPath: requestPath, + RequestMethod: requestMethod, + ParameterName: name, + Context: truncateForContext(value), + SpecLine: specLine, + SpecCol: specCol, + } +} + +// UndeclaredHeaderError creates a ValidationError for an undeclared header. +func UndeclaredHeaderError( + name string, + value string, + declaredHeaders []string, + direction string, + requestPath string, + requestMethod string, +) *ValidationError { + dirStr := direction + if dirStr == "" { + dirStr = "request" + } + + return &ValidationError{ + ValidationType: StrictValidationType, + ValidationSubType: StrictSubTypeHeader, + Message: fmt.Sprintf("%s header '%s' is not declared in specification", + dirStr, name), + Reason: fmt.Sprintf("Strict mode: found header not in spec. "+ + "Declared headers: [%s]", strings.Join(declaredHeaders, ", ")), + HowToFix: fmt.Sprintf("Add '%s' to the operation's parameters, remove it from the %s, "+ + "or add it to StrictIgnoredHeaders", name, dirStr), + RequestPath: requestPath, + RequestMethod: requestMethod, + ParameterName: name, + Context: value, + } +} + +// UndeclaredQueryParamError creates a ValidationError for an undeclared query parameter. +func UndeclaredQueryParamError( + path string, + name string, + value any, + declaredParams []string, + requestPath string, + requestMethod string, +) *ValidationError { + return &ValidationError{ + ValidationType: StrictValidationType, + ValidationSubType: StrictSubTypeQuery, + Message: fmt.Sprintf("query parameter '%s' at '%s' is not declared in specification", name, path), + Reason: fmt.Sprintf("Strict mode: found query parameter not in spec. "+ + "Declared parameters: [%s]", strings.Join(declaredParams, ", ")), + HowToFix: fmt.Sprintf("Add '%s' to the operation's query parameters, remove it from the request, "+ + "or add '%s' to StrictIgnorePaths", name, path), + RequestPath: requestPath, + RequestMethod: requestMethod, + ParameterName: name, + Context: truncateForContext(value), + } +} + +// UndeclaredCookieError creates a ValidationError for an undeclared cookie. +func UndeclaredCookieError( + path string, + name string, + value any, + declaredCookies []string, + requestPath string, + requestMethod string, +) *ValidationError { + return &ValidationError{ + ValidationType: StrictValidationType, + ValidationSubType: StrictSubTypeCookie, + Message: fmt.Sprintf("cookie '%s' at '%s' is not declared in specification", name, path), + Reason: fmt.Sprintf("Strict mode: found cookie not in spec. "+ + "Declared cookies: [%s]", strings.Join(declaredCookies, ", ")), + HowToFix: fmt.Sprintf("Add '%s' to the operation's cookie parameters, remove it from the request, "+ + "or add '%s' to StrictIgnorePaths", name, path), + RequestPath: requestPath, + RequestMethod: requestMethod, + ParameterName: name, + Context: truncateForContext(value), + } +} + +// truncateForContext creates a truncated string representation for error context. +func truncateForContext(v any) string { + switch val := v.(type) { + case string: + if len(val) > 50 { + return val[:47] + "..." + } + return val + case map[string]any: + return "{...}" + case []any: + return "[...]" + default: + s := fmt.Sprintf("%v", v) + if len(s) > 50 { + return s[:47] + "..." + } + return s + } +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/errors/validation_error.go b/vendor/github.com/pb33f/libopenapi-validator/errors/validation_error.go new file mode 100644 index 000000000..ec273d6c5 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/errors/validation_error.go @@ -0,0 +1,138 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package errors + +import ( + "fmt" + + "github.com/pb33f/libopenapi-validator/helpers" + "github.com/santhosh-tekuri/jsonschema/v6" +) + +// SchemaValidationFailure is a wrapper around the jsonschema.ValidationError object, to provide a more +// user-friendly way to break down what went wrong. +type SchemaValidationFailure struct { + // Reason is a human-readable message describing the reason for the error. + Reason string `json:"reason,omitempty" yaml:"reason,omitempty"` + + // Location is the XPath-like location of the validation failure + Location string `json:"location,omitempty" yaml:"location,omitempty"` + + // FieldName is the name of the specific field that failed validation (last segment of the path) + FieldName string `json:"fieldName,omitempty" yaml:"fieldName,omitempty"` + + // FieldPath is the JSONPath representation of the field location (e.g., "$.user.email") + FieldPath string `json:"fieldPath,omitempty" yaml:"fieldPath,omitempty"` + + // InstancePath is the raw path segments from the root to the failing field + InstancePath []string `json:"instancePath,omitempty" yaml:"instancePath,omitempty"` + + // DeepLocation is the path to the validation failure as exposed by the jsonschema library. + DeepLocation string `json:"deepLocation,omitempty" yaml:"deepLocation,omitempty"` + + // AbsoluteLocation is the absolute path to the validation failure as exposed by the jsonschema library. + AbsoluteLocation string `json:"absoluteLocation,omitempty" yaml:"absoluteLocation,omitempty"` + + // Line is the line number where the violation occurred. This may a local line number + // if the validation is a schema (only schemas are validated locally, so the line number will be relative to + // the Context object held by the ValidationError object). + Line int `json:"line,omitempty" yaml:"line,omitempty"` + + // Column is the column number where the violation occurred. This may a local column number + // if the validation is a schema (only schemas are validated locally, so the column number will be relative to + // the Context object held by the ValidationError object). + Column int `json:"column,omitempty" yaml:"column,omitempty"` + + // ReferenceSchema is the schema that was referenced in the validation failure. + ReferenceSchema string `json:"referenceSchema,omitempty" yaml:"referenceSchema,omitempty"` + + // ReferenceObject is the object that was referenced in the validation failure. + ReferenceObject string `json:"referenceObject,omitempty" yaml:"referenceObject,omitempty"` + + // ReferenceExample is an example object generated from the schema that was referenced in the validation failure. + ReferenceExample string `json:"referenceExample,omitempty" yaml:"referenceExample,omitempty"` + + // The original error object, which is a jsonschema.ValidationError object. + OriginalError *jsonschema.ValidationError `json:"-" yaml:"-"` +} + +// Error returns a string representation of the error +func (s *SchemaValidationFailure) Error() string { + return fmt.Sprintf("Reason: %s, Location: %s", s.Reason, s.Location) +} + +// ValidationError is a struct that contains all the information about a validation error. +type ValidationError struct { + // Message is a human-readable message describing the error. + Message string `json:"message" yaml:"message"` + + // Reason is a human-readable message describing the reason for the error. + Reason string `json:"reason" yaml:"reason"` + + // ValidationType is a string that describes the type of validation that failed. + ValidationType string `json:"validationType" yaml:"validationType"` + + // ValidationSubType is a string that describes the subtype of validation that failed. + ValidationSubType string `json:"validationSubType" yaml:"validationSubType"` + + // SpecLine is the line number in the spec where the error occurred. + SpecLine int `json:"specLine" yaml:"specLine"` + + // SpecCol is the column number in the spec where the error occurred. + SpecCol int `json:"specColumn" yaml:"specColumn"` + + // HowToFix is a human-readable message describing how to fix the error. + HowToFix string `json:"howToFix" yaml:"howToFix"` + + // RequestPath is the path of the request + RequestPath string `json:"requestPath" yaml:"requestPath"` + + // SpecPath is the path from the specification that corresponds to the request + SpecPath string `json:"specPath" yaml:"specPath"` + + // RequestMethod is the HTTP method of the request + RequestMethod string `json:"requestMethod" yaml:"requestMethod"` + + // ParameterName is the name of the parameter that failed validation (for parameter validation errors) + ParameterName string `json:"parameterName,omitempty" yaml:"parameterName,omitempty"` + + // SchemaValidationErrors is a slice of SchemaValidationFailure objects that describe the validation errors + // This is only populated whe the validation type is against a schema. + SchemaValidationErrors []*SchemaValidationFailure `json:"validationErrors,omitempty" yaml:"validationErrors,omitempty"` + + // Context is the object that the validation error occurred on. This is usually a pointer to a schema + // or a parameter object. + Context interface{} `json:"-" yaml:"-"` +} + +// Error returns a string representation of the error +func (v *ValidationError) Error() string { + if v.SchemaValidationErrors != nil { + if v.SpecLine > 0 && v.SpecCol > 0 { + return fmt.Sprintf("Error: %s, Reason: %s, Validation Errors: %s, Line: %d, Column: %d", + v.Message, v.Reason, v.SchemaValidationErrors, v.SpecLine, v.SpecCol) + } else { + return fmt.Sprintf("Error: %s, Reason: %s, Validation Errors: %s", + v.Message, v.Reason, v.SchemaValidationErrors) + } + } else { + if v.SpecLine > 0 && v.SpecCol > 0 { + return fmt.Sprintf("Error: %s, Reason: %s, Line: %d, Column: %d", + v.Message, v.Reason, v.SpecLine, v.SpecCol) + } else { + return fmt.Sprintf("Error: %s, Reason: %s", + v.Message, v.Reason) + } + } +} + +// IsPathMissingError returns true if the error has a ValidationType of "path" and a ValidationSubType of "missing" +func (v *ValidationError) IsPathMissingError() bool { + return v.ValidationType == helpers.PathValidation && v.ValidationSubType == helpers.ValidationMissing +} + +// IsOperationMissingError returns true if the error has a ValidationType of "request" and a ValidationSubType of "missingOperation" +func (v *ValidationError) IsOperationMissingError() bool { + return v.ValidationType == helpers.PathValidation && v.ValidationSubType == helpers.ValidationMissingOperation +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/helpers/constants.go b/vendor/github.com/pb33f/libopenapi-validator/helpers/constants.go new file mode 100644 index 000000000..1faecff0c --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/helpers/constants.go @@ -0,0 +1,58 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package helpers + +const ( + ParameterValidation = "parameter" + ParameterValidationPath = "path" + ParameterValidationQuery = "query" + ParameterValidationHeader = "header" + ParameterValidationCookie = "cookie" + RequestValidation = "request" + RequestBodyValidation = "requestBody" + Schema = "schema" + ResponseBodyValidation = "response" + RequestBodyContentType = "contentType" + // Deprecated: use ValidationMissingOperation + RequestMissingOperation = "missingOperation" + PathValidation = "path" + ValidationMissing = "missing" + ValidationMissingOperation = "missingOperation" + ResponseBodyResponseCode = "statusCode" + SecurityValidation = "security" + DocumentValidation = "document" + SpaceDelimited = "spaceDelimited" + PipeDelimited = "pipeDelimited" + DefaultDelimited = "default" + MatrixStyle = "matrix" + LabelStyle = "label" + Pipe = "|" + Comma = "," + Space = " " + SemiColon = ";" + Asterisk = "*" + Period = "." + Equals = "=" + Integer = "integer" + Number = "number" + Slash = "/" + Object = "object" + String = "string" + Array = "array" + Boolean = "boolean" + DeepObject = "deepObject" + Header = "header" + Cookie = "cookie" + Path = "path" + Form = "form" + Query = "query" + JSONContentType = "application/json" + JSONType = "json" + ContentTypeHeader = "Content-Type" + AuthorizationHeader = "Authorization" + Charset = "charset" + Boundary = "boundary" + Preferred = "preferred" + FailSegment = "**&&FAIL&&**" +) diff --git a/vendor/github.com/pb33f/libopenapi-validator/helpers/ignore_regex.go b/vendor/github.com/pb33f/libopenapi-validator/helpers/ignore_regex.go new file mode 100644 index 000000000..183b000d0 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/helpers/ignore_regex.go @@ -0,0 +1,19 @@ +// Copyright 2023-2024 Princess Beef Heavy Industries, LLC / Dave Shanley +// https://pb33f.io + +package helpers + +import "regexp" + +var ( + // Ignore generic poly errors that just say "none matched" since we get specific errors + // But keep errors that say which subschemas matched (for multiple match scenarios) + IgnorePattern = `^'?(anyOf|allOf|oneOf|validation)'? failed(, none matched)?$` + IgnorePolyPattern = `^'?(anyOf|allOf|oneOf)'? failed(, none matched)?$` +) + +// IgnoreRegex is a regular expression that matches the IgnorePattern +var IgnoreRegex = regexp.MustCompile(IgnorePattern) + +// IgnorePolyRegex is a regular expression that matches the IgnorePattern +var IgnorePolyRegex = regexp.MustCompile(IgnorePolyPattern) diff --git a/vendor/github.com/pb33f/libopenapi-validator/helpers/operation_utilities.go b/vendor/github.com/pb33f/libopenapi-validator/helpers/operation_utilities.go new file mode 100644 index 000000000..a4ceadd61 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/helpers/operation_utilities.go @@ -0,0 +1,45 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package helpers + +import ( + "mime" + "net/http" + + "github.com/pb33f/libopenapi/datamodel/high/v3" +) + +// ExtractOperation extracts the operation from the path item based on the request method. If there is no +// matching operation found, then nil is returned. +func ExtractOperation(request *http.Request, item *v3.PathItem) *v3.Operation { + switch request.Method { + case http.MethodGet: + return item.Get + case http.MethodPost: + return item.Post + case http.MethodPut: + return item.Put + case http.MethodDelete: + return item.Delete + case http.MethodOptions: + return item.Options + case http.MethodHead: + return item.Head + case http.MethodPatch: + return item.Patch + case http.MethodTrace: + return item.Trace + } + return nil +} + +// ExtractContentType extracts the content type from the request header. First return argument is the content type +// of the request.The second (optional) argument is the charset of the request. The third (optional) +// argument is the boundary of the type (only used with forms really). +func ExtractContentType(contentType string) (string, string, string) { + // mime.ParseMediaType: "If there is an error parsing the optional parameter, + // the media type will be returned along with the error ErrInvalidMediaParameter." + ct, params, _ := mime.ParseMediaType(contentType) + return ct, params["charset"], params["boundary"] +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/helpers/package.go b/vendor/github.com/pb33f/libopenapi-validator/helpers/package.go new file mode 100644 index 000000000..4a28fd447 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/helpers/package.go @@ -0,0 +1,7 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +// Package helpers contains helper and utility functions used by the validator. Trying to avoid using the package +// name utils anymore, as it's too generic and can cause conflicts with other packages - however I feel this pattern +// will suffer the exact same fate with time. +package helpers diff --git a/vendor/github.com/pb33f/libopenapi-validator/helpers/parameter_utilities.go b/vendor/github.com/pb33f/libopenapi-validator/helpers/parameter_utilities.go new file mode 100644 index 000000000..69d291588 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/helpers/parameter_utilities.go @@ -0,0 +1,393 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package helpers + +import ( + "fmt" + "net/http" + "slices" + "strconv" + "strings" + + "github.com/pb33f/libopenapi/datamodel/high/base" + + v3 "github.com/pb33f/libopenapi/datamodel/high/v3" +) + +// QueryParam is a struct that holds the key, values and property name for a query parameter +// it's used for complex query types that need to be parsed and tracked differently depending +// on the encoding styles used. +type QueryParam struct { + Key string + Values []string + Property string +} + +// ExtractParamsForOperation will extract the parameters for the operation based on the request method. +// Both the path level params and the method level params will be returned. +func ExtractParamsForOperation(request *http.Request, item *v3.PathItem) []*v3.Parameter { + params := item.Parameters + switch request.Method { + case http.MethodGet: + if item.Get != nil { + params = append(params, item.Get.Parameters...) + } + case http.MethodPost: + if item.Post != nil { + params = append(params, item.Post.Parameters...) + } + case http.MethodPut: + if item.Put != nil { + params = append(params, item.Put.Parameters...) + } + case http.MethodDelete: + if item.Delete != nil { + params = append(params, item.Delete.Parameters...) + } + case http.MethodOptions: + if item.Options != nil { + params = append(params, item.Options.Parameters...) + } + case http.MethodHead: + if item.Head != nil { + params = append(params, item.Head.Parameters...) + } + case http.MethodPatch: + if item.Patch != nil { + params = append(params, item.Patch.Parameters...) + } + case http.MethodTrace: + if item.Trace != nil { + params = append(params, item.Trace.Parameters...) + } + } + return params +} + +// ExtractSecurityForOperation will extract the security requirements for the operation based on the request method. +func ExtractSecurityForOperation(request *http.Request, item *v3.PathItem) []*base.SecurityRequirement { + var schemes []*base.SecurityRequirement + switch request.Method { + case http.MethodGet: + if item.Get != nil { + schemes = append(schemes, item.Get.Security...) + } + case http.MethodPost: + if item.Post != nil { + schemes = append(schemes, item.Post.Security...) + } + case http.MethodPut: + if item.Put != nil { + schemes = append(schemes, item.Put.Security...) + } + case http.MethodDelete: + if item.Delete != nil { + schemes = append(schemes, item.Delete.Security...) + } + case http.MethodOptions: + if item.Options != nil { + schemes = append(schemes, item.Options.Security...) + } + case http.MethodHead: + if item.Head != nil { + schemes = append(schemes, item.Head.Security...) + } + case http.MethodPatch: + if item.Patch != nil { + schemes = append(schemes, item.Patch.Security...) + } + case http.MethodTrace: + if item.Trace != nil { + schemes = append(schemes, item.Trace.Security...) + } + } + return schemes +} + +// ExtractSecurityHeaderNames extracts header names from applicable security schemes. +// Returns header names from apiKey schemes with in:"header", plus "Authorization" +// for http/oauth2/openIdConnect schemes. +// +// This function is used by strict mode validation to recognize security headers +// as "declared" headers that should not trigger undeclared header errors. +func ExtractSecurityHeaderNames( + security []*base.SecurityRequirement, + securitySchemes map[string]*v3.SecurityScheme, +) []string { + if security == nil || securitySchemes == nil { + return nil + } + + seen := make(map[string]bool) + var headers []string + + for _, sec := range security { + if sec == nil || sec.ContainsEmptyRequirement { + continue // No security required for this option + } + + if sec.Requirements == nil { + continue + } + + for pair := sec.Requirements.First(); pair != nil; pair = pair.Next() { + schemeName := pair.Key() + scheme, ok := securitySchemes[schemeName] + if !ok || scheme == nil { + continue + } + + var headerName string + switch strings.ToLower(scheme.Type) { + case "apikey": + if strings.ToLower(scheme.In) == Header { + headerName = scheme.Name + } + case "http", "oauth2", "openidconnect": + headerName = "Authorization" + } + + if headerName != "" && !seen[strings.ToLower(headerName)] { + seen[strings.ToLower(headerName)] = true + headers = append(headers, headerName) + } + } + } + + return headers +} + +func cast(v string) any { + if v == "true" || v == "false" { + b, _ := strconv.ParseBool(v) + return b + } + if i, err := strconv.ParseFloat(v, 64); err == nil { + // check if this is an int or not + if !strings.Contains(v, Period) { + iv, _ := strconv.ParseInt(v, 10, 64) + return iv + } + return i + } + return v +} + +// ConstructParamMapFromDeepObjectEncoding will construct a map from the query parameters that are encoded as +// deep objects. It's kind of a crazy way to do things, but hey, each to their own. +func ConstructParamMapFromDeepObjectEncoding(values []*QueryParam, sch *base.Schema) map[string]interface{} { + // deepObject encoding is a technique used to encode objects into query parameters. Kinda nuts. + decoded := make(map[string]interface{}) + for _, v := range values { + if decoded[v.Key] == nil { + + props := make(map[string]interface{}) + rawValues := make([]interface{}, len(v.Values)) + for i := range v.Values { + rawValues[i] = cast(v.Values[i]) + } + // check if the schema for the param is an array + if sch != nil && slices.Contains(sch.Type, Array) { + props[v.Property] = rawValues + } + // check if schema has additional properties defined as an array + if sch != nil && sch.AdditionalProperties != nil && + sch.AdditionalProperties.IsA() { + s := sch.AdditionalProperties.A.Schema() + if s != nil && + slices.Contains(s.Type, Array) { + props[v.Property] = rawValues + } + } + + if len(props) == 0 { + props[v.Property] = cast(v.Values[0]) + } + decoded[v.Key] = props + } else { + + added := false + rawValues := make([]interface{}, len(v.Values)) + for i := range v.Values { + rawValues[i] = cast(v.Values[i]) + } + // check if the schema for the param is an array + if sch != nil && slices.Contains(sch.Type, Array) { + decoded[v.Key].(map[string]interface{})[v.Property] = rawValues + added = true + } + // check if schema has additional properties defined as an array + if sch != nil && sch.AdditionalProperties != nil && + sch.AdditionalProperties.IsA() && + slices.Contains(sch.AdditionalProperties.A.Schema().Type, Array) { + decoded[v.Key].(map[string]interface{})[v.Property] = rawValues + added = true + } + if !added { + decoded[v.Key].(map[string]interface{})[v.Property] = cast(v.Values[0]) + } + + } + } + return decoded +} + +// ConstructParamMapFromQueryParamInput will construct a param map from an existing map of *QueryParam slices. +func ConstructParamMapFromQueryParamInput(values map[string][]*QueryParam) map[string]interface{} { + decoded := make(map[string]interface{}) + for _, q := range values { + for _, v := range q { + decoded[v.Key] = cast(v.Values[0]) + } + } + return decoded +} + +// ConstructParamMapFromPipeEncoding will construct a map from the query parameters that are encoded as +// pipe separated values. Perhaps the most sane way to delimit/encode properties. +func ConstructParamMapFromPipeEncoding(values []*QueryParam) map[string]interface{} { + // Pipes are always a good alternative to commas, personally I think they're better, if I were encoding, I would + // use pipes instead of commas, so much can go wrong with a comma, but a pipe? hardly ever. + decoded := make(map[string]interface{}) + for _, v := range values { + props := make(map[string]interface{}) + // explode PSV into array + exploded := strings.Split(v.Values[0], Pipe) + for i := range exploded { + if i%2 == 0 { + props[exploded[i]] = cast(exploded[i+1]) + } + } + decoded[v.Key] = props + } + return decoded +} + +// ConstructParamMapFromSpaceEncoding will construct a map from the query parameters that are encoded as +// space delimited values. This is perhaps the worst way to delimit anything other than a paragraph of text. +func ConstructParamMapFromSpaceEncoding(values []*QueryParam) map[string]interface{} { + // Don't use spaces to delimit anything unless you really know what the hell you're doing. Perhaps the + // easiest way to blow something up, unless you're tokenizing strings... don't do this. + decoded := make(map[string]interface{}) + for _, v := range values { + props := make(map[string]interface{}) + // explode SSV into array + exploded := strings.Split(v.Values[0], Space) + for i := range exploded { + if i%2 == 0 { + props[exploded[i]] = cast(exploded[i+1]) + } + } + decoded[v.Key] = props + } + return decoded +} + +// ConstructMapFromCSV will construct a map from a comma separated value string. +func ConstructMapFromCSV(csv string) map[string]interface{} { + decoded := make(map[string]interface{}) + // explode SSV into array + exploded := strings.Split(csv, Comma) + for i := range exploded { + if i%2 == 0 { + if len(exploded) == i+1 { + break + } + decoded[exploded[i]] = cast(exploded[i+1]) + } + } + return decoded +} + +// ConstructKVFromCSV will construct a map from a comma separated value string that denotes key value pairs. +func ConstructKVFromCSV(values string) map[string]interface{} { + props := make(map[string]interface{}) + exploded := strings.Split(values, Comma) + for i := range exploded { + obK := strings.Split(exploded[i], Equals) + if len(obK) == 2 { + props[obK[0]] = cast(obK[1]) + } + } + return props +} + +// ConstructKVFromLabelEncoding will construct a map from a comma separated value string that denotes key value pairs. +func ConstructKVFromLabelEncoding(values string) map[string]interface{} { + props := make(map[string]interface{}) + exploded := strings.Split(values, Period) + for i := range exploded { + obK := strings.Split(exploded[i], Equals) + if len(obK) == 2 { + props[obK[0]] = cast(obK[1]) + } + } + return props +} + +// ConstructKVFromMatrixCSV will construct a map from a comma separated value string that denotes key value pairs. +func ConstructKVFromMatrixCSV(values string) map[string]interface{} { + props := make(map[string]interface{}) + exploded := strings.Split(values, SemiColon) + for i := range exploded { + obK := strings.Split(exploded[i], Equals) + if len(obK) == 2 { + props[obK[0]] = cast(obK[1]) + } + } + return props +} + +// ConstructParamMapFromFormEncodingArray will construct a map from the query parameters that are encoded as +// form encoded values. +func ConstructParamMapFromFormEncodingArray(values []*QueryParam) map[string]interface{} { + decoded := make(map[string]interface{}) + for _, v := range values { + props := make(map[string]interface{}) + // explode SSV into array + exploded := strings.Split(v.Values[0], Comma) + for i := range exploded { + if i%2 == 0 { + if len(exploded) > i+1 { + props[exploded[i]] = cast(exploded[i+1]) + } + } + } + decoded[v.Key] = props + } + return decoded +} + +// DoesFormParamContainDelimiter will determine if a form parameter contains a delimiter. +func DoesFormParamContainDelimiter(value, style string) bool { + if strings.Contains(value, Comma) && (style == "" || style == Form) { + return true + } + return false +} + +// ExplodeQueryValue will explode a query value based on the style (space, pipe, or form/default). +func ExplodeQueryValue(value, style string) []string { + switch style { + case SpaceDelimited: + return strings.Split(value, Space) + case PipeDelimited: + return strings.Split(value, Pipe) + default: + return strings.Split(value, Comma) + } +} + +func CollapseCSVIntoFormStyle(key string, value string) string { + return fmt.Sprintf("&%s=%s", key, + strings.Join(strings.Split(value, ","), fmt.Sprintf("&%s=", key))) +} + +func CollapseCSVIntoSpaceDelimitedStyle(key string, values []string) string { + return fmt.Sprintf("%s=%s", key, strings.Join(values, "%20")) +} + +func CollapseCSVIntoPipeDelimitedStyle(key string, values []string) string { + return fmt.Sprintf("%s=%s", key, strings.Join(values, Pipe)) +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/helpers/path_finder.go b/vendor/github.com/pb33f/libopenapi-validator/helpers/path_finder.go new file mode 100644 index 000000000..370dd6343 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/helpers/path_finder.go @@ -0,0 +1,176 @@ +// Copyright 2023-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// https://pb33f.io + +package helpers + +import ( + "fmt" + "strings" + "unicode" + + "github.com/santhosh-tekuri/jsonschema/v6" +) + +// ExtractJSONPathFromValidationError traverses and processes a ValidationError to construct a JSONPath string representation of its instance location. +func ExtractJSONPathFromValidationError(e *jsonschema.ValidationError) string { + if len(e.Causes) > 0 { + for _, cause := range e.Causes { + ExtractJSONPathFromValidationError(cause) + } + } + + if len(e.InstanceLocation) > 0 { + + var b strings.Builder + b.WriteString("$") + + for _, seg := range e.InstanceLocation { + switch { + case isNumeric(seg): + b.WriteString(fmt.Sprintf("[%s]", seg)) + + case isSimpleIdentifier(seg): + b.WriteByte('.') + b.WriteString(seg) + + default: + esc := escapeBracketString(seg) + b.WriteString("['") + b.WriteString(esc) + b.WriteString("']") + } + } + return b.String() + } + return "" +} + +// isNumeric returns true if s is a non‐empty string of digits. +func isNumeric(s string) bool { + if s == "" { + return false + } + for _, r := range s { + if r < '0' || r > '9' { + return false + } + } + return true +} + +// isSimpleIdentifier returns true if s matches [A-Za-z_][A-Za-z0-9_]*. +func isSimpleIdentifier(s string) bool { + for i, r := range s { + if i == 0 { + if !unicode.IsLetter(r) && r != '_' { + return false + } + } else { + if !unicode.IsLetter(r) && !unicode.IsDigit(r) && r != '_' { + return false + } + } + } + return len(s) > 0 +} + +// escapeBracketString escapes backslashes and single‐quotes for inside ['...'] +func escapeBracketString(s string) string { + s = strings.ReplaceAll(s, `\`, `\\`) + s = strings.ReplaceAll(s, `'`, `\'`) + return s +} + +// ExtractJSONPathsFromValidationErrors takes a slice of ValidationError pointers and returns a slice of JSONPath strings +func ExtractJSONPathsFromValidationErrors(errors []*jsonschema.ValidationError) []string { + var paths []string + for _, err := range errors { + path := ExtractJSONPathFromValidationError(err) + if path != "" { + paths = append(paths, path) + } + } + return paths +} + +// ExtractFieldNameFromInstanceLocation returns the last segment of the instance location as the field name +func ExtractFieldNameFromInstanceLocation(instanceLocation []string) string { + if len(instanceLocation) == 0 { + return "" + } + return instanceLocation[len(instanceLocation)-1] +} + +// ExtractFieldNameFromStringLocation returns the last segment of the instance location as the field name +// when the location is provided as a string path +func ExtractFieldNameFromStringLocation(instanceLocation string) string { + if instanceLocation == "" { + return "" + } + + // Handle string format like "/properties/email" or "/0/name" + segments := strings.Split(strings.Trim(instanceLocation, "/"), "/") + if len(segments) == 0 || (len(segments) == 1 && segments[0] == "") { + return "" + } + + return segments[len(segments)-1] +} + +// ExtractJSONPathFromInstanceLocation creates a JSONPath string from instance location segments +func ExtractJSONPathFromInstanceLocation(instanceLocation []string) string { + if len(instanceLocation) == 0 { + return "" + } + + var b strings.Builder + b.WriteString("$") + + for _, seg := range instanceLocation { + switch { + case isNumeric(seg): + b.WriteString(fmt.Sprintf("[%s]", seg)) + + case isSimpleIdentifier(seg): + b.WriteByte('.') + b.WriteString(seg) + + default: + esc := escapeBracketString(seg) + b.WriteString("['") + b.WriteString(esc) + b.WriteString("']") + } + } + return b.String() +} + +// ExtractJSONPathFromStringLocation creates a JSONPath string from string-based instance location +func ExtractJSONPathFromStringLocation(instanceLocation string) string { + if instanceLocation == "" { + return "" + } + + // Convert string format like "/properties/email" to array format + segments := strings.Split(strings.Trim(instanceLocation, "/"), "/") + if len(segments) == 0 || (len(segments) == 1 && segments[0] == "") { + return "" + } + + return ExtractJSONPathFromInstanceLocation(segments) +} + +// ConvertStringLocationToPathSegments converts a string-based instance location to path segments array +// Handles edge cases like empty strings and root-only paths +func ConvertStringLocationToPathSegments(instanceLocation string) []string { + if instanceLocation == "" { + return []string{} + } + + segments := strings.Split(strings.Trim(instanceLocation, "/"), "/") + if len(segments) == 1 && segments[0] == "" { + return []string{} + } + + return segments +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/helpers/regex_maker.go b/vendor/github.com/pb33f/libopenapi-validator/helpers/regex_maker.go new file mode 100644 index 000000000..3fcbc2eae --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/helpers/regex_maker.go @@ -0,0 +1,148 @@ +package helpers + +import ( + "bytes" + "fmt" + "regexp" + "strings" +) + +var ( + baseDefaultPattern = "[^/]*" + DefaultPatternRegex = regexp.MustCompile("^([^/]*)$") + DefaultPatternRegexString = DefaultPatternRegex.String() +) + +// GetRegexForPath returns a compiled regular expression for the given path template. +// +// This function takes a path template string `tpl` and generates a regular expression +// that matches the structure of the template. The template can include placeholders +// enclosed in braces `{}` with optional custom patterns. +// +// Placeholders in the template can be defined as: +// - `{name}`: Matches any sequence of characters except '/' +// - `{name:pattern}`: Matches the specified custom pattern +// +// The function ensures that the template is well-formed, with balanced and properly +// nested braces. If the template is invalid, an error is returned. +// +// Parameters: +// - tpl: The path template string to convert into a regular expression. +// +// Returns: +// - *regexp.Regexp: A compiled regular expression that matches the template. +// - error: An error if the template is invalid or the regular expression cannot be compiled. +// +// Example: +// +// regex, err := GetRegexForPath("/orders/{id:[0-9]+}/items/{itemId}") +// // regex: ^/orders/([0-9]+)/items/([^/]+)$ +// // err: nil +func GetRegexForPath(tpl string) (*regexp.Regexp, error) { + // Check if it is well-formed. + idxs, errBraces := BraceIndices(tpl) + if errBraces != nil { + return nil, errBraces + } + + // Backup the original. + template := tpl + + pattern := bytes.NewBufferString("^") + var end int + + for i := 0; i < len(idxs); i += 2 { + + // Set all values we are interested in. + raw := tpl[end:idxs[i]] + end = idxs[i+1] + parts := strings.SplitN(tpl[idxs[i]+1:end-1], ":", 2) + name := parts[0] + patt := baseDefaultPattern + if len(parts) == 2 { + patt = parts[1] + } + + // Name or pattern can't be empty. + if name == "" || patt == "" { + return nil, fmt.Errorf("missing name or pattern in %q", tpl[idxs[i]:end]) + } + + // Build the regexp pattern. + _, err := fmt.Fprintf(pattern, "%s(%s)", regexp.QuoteMeta(raw), patt) + if err != nil { + return nil, err + } + + } + + // Add the remaining. + raw := tpl[end:] + pattern.WriteString(regexp.QuoteMeta(raw)) + + pattern.WriteByte('$') + + patternString := pattern.String() + + if patternString == DefaultPatternRegexString { + return DefaultPatternRegex, nil + } + + // Compile full regexp. + reg, errCompile := regexp.Compile(patternString) + if errCompile != nil { + return nil, errCompile + } + + // Check for capturing groups which used to work in older versions + if reg.NumSubexp() != len(idxs)/2 { + return nil, fmt.Errorf("route %s contains capture groups in its regexp. Only non-capturing groups are accepted: e.g. (?:pattern) instead of (pattern)", template) + } + + // Done! + return reg, nil +} + +// BraceIndices returns the indices of the opening and closing braces in a string. +// +// It scans the input string `s` and identifies the positions of matching pairs +// of braces ('{' and '}'). The function ensures that the braces are balanced +// and properly nested. +// +// If the braces are unbalanced or improperly nested, an error is returned. +// +// Parameters: +// - s: The input string to scan for braces. +// +// Returns: +// - []int: A slice of integers where each pair of indices represents the +// start and end positions of a matching pair of braces. +// - error: An error if the braces are unbalanced or improperly nested. +// +// Example: +// +// indices, err := BraceIndices("/orders/{id}/items/{itemId}") +// // indices: [8, 12, 19, 26] +// // err: nil +func BraceIndices(s string) ([]int, error) { + var level, idx int + var idxs []int + for i := 0; i < len(s); i++ { + switch s[i] { + case '{': + if level++; level == 1 { + idx = i + } + case '}': + if level--; level == 0 { + idxs = append(idxs, idx, i+1) + } else if level < 0 { + return nil, fmt.Errorf("unbalanced braces in %q", s) + } + } + } + if level != 0 { + return nil, fmt.Errorf("unbalanced braces in %q", s) + } + return idxs, nil +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/helpers/schema_compiler.go b/vendor/github.com/pb33f/libopenapi-validator/helpers/schema_compiler.go new file mode 100644 index 000000000..9fc6cec6d --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/helpers/schema_compiler.go @@ -0,0 +1,299 @@ +package helpers + +import ( + "bytes" + "encoding/json" + "fmt" + + "github.com/santhosh-tekuri/jsonschema/v6" + + "github.com/pb33f/libopenapi-validator/config" + "github.com/pb33f/libopenapi-validator/openapi_vocabulary" +) + +// ConfigureCompiler configures a JSON Schema compiler with the desired behavior. +func ConfigureCompiler(c *jsonschema.Compiler, o *config.ValidationOptions) { + if o == nil { + // Sanity + return + } + + // nil is the default so this is OK. + c.UseRegexpEngine(o.RegexEngine) + + if o.FormatAssertions { + c.AssertFormat() + } + + if o.ContentAssertions { + c.AssertContent() + } + + for n, v := range o.Formats { + c.RegisterFormat(&jsonschema.Format{ + Name: n, + Validate: v, + }) + } +} + +// NewCompilerWithOptions mints a new JSON schema compiler with custom configuration. +func NewCompilerWithOptions(o *config.ValidationOptions) *jsonschema.Compiler { + c := jsonschema.NewCompiler() + ConfigureCompiler(c, o) + return c +} + +// NewCompiledSchema establishes a programmatic representation of a JSON Schema document that is used for validation. +// Defaults to OpenAPI 3.1+ behavior (strict JSON Schema compliance). +func NewCompiledSchema(name string, jsonSchema []byte, o *config.ValidationOptions) (*jsonschema.Schema, error) { + return NewCompiledSchemaWithVersion(name, jsonSchema, o, 3.1) +} + +// NewCompiledSchemaWithVersion establishes a programmatic representation of a JSON Schema document that is used for validation. +// The version parameter determines which OpenAPI keywords are allowed: +// - version 3.0: Allows OpenAPI 3.0 keywords like 'nullable' +// - version 3.1+: Rejects OpenAPI 3.0 keywords like 'nullable' (strict JSON Schema compliance) +func NewCompiledSchemaWithVersion(name string, jsonSchema []byte, options *config.ValidationOptions, version float32) (*jsonschema.Schema, error) { + compiler := NewCompilerWithOptions(options) + compiler.UseLoader(NewCompilerLoader()) + + // register OpenAPI vocabulary with appropriate version and coercion settings + if options != nil && options.OpenAPIMode { + var vocabVersion openapi_vocabulary.VersionType + if version >= 3.15 { // use 3.15 to avoid floating point precision issues (3.2+) + vocabVersion = openapi_vocabulary.Version32 + } else if version >= 3.05 { // use 3.05 to avoid floating point precision issues (3.1) + vocabVersion = openapi_vocabulary.Version31 + } else { + vocabVersion = openapi_vocabulary.Version30 + } + + vocab := openapi_vocabulary.NewOpenAPIVocabularyWithCoercion(vocabVersion, options.AllowScalarCoercion) + compiler.RegisterVocabulary(vocab) + compiler.AssertVocabs() + + if version < 3.05 { + jsonSchema = transformOpenAPI30Schema(jsonSchema) + } + + if options.AllowScalarCoercion { + jsonSchema = transformSchemaForCoercion(jsonSchema) + } + } + + decodedSchema, err := jsonschema.UnmarshalJSON(bytes.NewReader(jsonSchema)) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal JSON schema: %w", err) + } + + if err = compiler.AddResource(name, decodedSchema); err != nil { + return nil, fmt.Errorf("failed to add resource to schema compiler: %w", err) + } + + jsch, err := compiler.Compile(name) + if err != nil { + return nil, fmt.Errorf("JSON schema compile failed: %s", err.Error()) + } + + return jsch, nil +} + +// transformOpenAPI30Schema transforms OpenAPI 3.0 schemas to JSON Schema compatible format +// This specifically handles the nullable keyword by converting it to proper type arrays +func transformOpenAPI30Schema(jsonSchema []byte) []byte { + var schema map[string]interface{} + if err := json.Unmarshal(jsonSchema, &schema); err != nil { + // If we can't parse it, return as-is + return jsonSchema + } + + transformed := transformNullableInSchema(schema) + + result, err := json.Marshal(transformed) + if err != nil { + // If we can't marshal the result, return original + return jsonSchema + } + + return result +} + +// transformNullableInSchema recursively transforms nullable keywords in a schema object +func transformNullableInSchema(schema interface{}) interface{} { + switch s := schema.(type) { + case map[string]interface{}: + result := make(map[string]interface{}) + + // copy all properties first + for key, value := range s { + result[key] = transformNullableInSchema(value) + } + + // check if this schema has nullable keyword + if nullable, ok := s["nullable"]; ok { + if nullableBool, ok := nullable.(bool); ok { + if nullableBool { + // Transform the schema to support null values + return transformNullableSchema(result) + } else { + // nullable: false - just remove the nullable keyword + delete(result, "nullable") + } + } + } + + return result + + case []interface{}: + result := make([]interface{}, len(s)) + for i, item := range s { + result[i] = transformNullableInSchema(item) + } + return result + + default: + return schema + } +} + +// transformNullableSchema transforms a schema with nullable: true to JSON Schema compatible format +func transformNullableSchema(schema map[string]interface{}) map[string]interface{} { + delete(schema, "nullable") + + // get the current type + currentType, hasType := schema["type"] + + if hasType { + // if there's already a type, convert it to include null + switch t := currentType.(type) { + case string: + // convert "string" to ["string", "null"] + schema["type"] = []interface{}{t, "null"} + case []interface{}: + // if it's already an array, add null if not present + found := false + for _, item := range t { + if str, ok := item.(string); ok && str == "null" { + found = true + break + } + } + if !found { + newTypes := make([]interface{}, len(t)+1) + copy(newTypes, t) + newTypes[len(t)] = "null" + schema["type"] = newTypes + } + } + } + allOf, hasAllOf := schema["allOf"] + if hasAllOf { + delete(schema, "allOf") + oneOfAdditions := []interface{}{ + map[string]interface{}{ + "allOf": allOf, + }, + map[string]interface{}{ + "type": "null", + }, + } + var oneOfSlice []interface{} + oneOf, hasOneOf := schema["oneOf"] + if hasOneOf { + oneOfSlice, _ = oneOf.([]interface{}) + } + oneOfSlice = append(oneOfSlice, oneOfAdditions...) + schema["oneOf"] = oneOfSlice + } + + return schema +} + +// transformSchemaForCoercion transforms schemas to allow scalar coercion (string->boolean/number) +func transformSchemaForCoercion(jsonSchema []byte) []byte { + var schema map[string]interface{} + if err := json.Unmarshal(jsonSchema, &schema); err != nil { + // If we can't parse it, return as-is + return jsonSchema + } + + transformed := transformCoercionInSchema(schema) + + result, err := json.Marshal(transformed) + if err != nil { + return jsonSchema + } + + return result +} + +// transformCoercionInSchema recursively transforms schemas to support scalar coercion +func transformCoercionInSchema(schema interface{}) interface{} { + switch s := schema.(type) { + case map[string]interface{}: + result := make(map[string]interface{}) + + // copy all properties first + for key, value := range s { + result[key] = transformCoercionInSchema(value) + } + + // transform type to allow string coercion for coercible types + if schemaType, hasType := s["type"]; hasType { + result["type"] = transformTypeForCoercion(schemaType) + } + + return result + + case []interface{}: + result := make([]interface{}, len(s)) + for i, item := range s { + result[i] = transformCoercionInSchema(item) + } + return result + + default: + return schema + } +} + +// transformTypeForCoercion transforms type fields to allow string coercion +func transformTypeForCoercion(schemaType interface{}) interface{} { + switch t := schemaType.(type) { + case string: + // transform scalar types to include string for coercion + if t == "boolean" || t == "number" || t == "integer" { + return []interface{}{t, "string"} + } + return t + + case []interface{}: + // if already an array, add string if it contains coercible types and doesn't already have string + hasCoercibleType := false + hasString := false + + for _, item := range t { + if str, ok := item.(string); ok { + if str == "boolean" || str == "number" || str == "integer" { + hasCoercibleType = true + } + if str == "string" { + hasString = true + } + } + } + + if hasCoercibleType && !hasString { + newTypes := make([]interface{}, len(t)+1) + copy(newTypes, t) + newTypes[len(t)] = "string" + return newTypes + } + + return t + + default: + return schemaType + } +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/helpers/url_loader.go b/vendor/github.com/pb33f/libopenapi-validator/helpers/url_loader.go new file mode 100644 index 000000000..a5cd66b6a --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/helpers/url_loader.go @@ -0,0 +1,57 @@ +// Copyright 2023-2024 Princess Beef Heavy Industries, LLC / Dave Shanley +// https://pb33f.io + +package helpers + +import ( + "crypto/tls" + "fmt" + "net/http" + "time" + + "github.com/santhosh-tekuri/jsonschema/v6" +) + +// HTTPURLLoader is a type that implements the Loader interface for loading schemas from HTTP URLs. +// this change was made in jsonschema v6. The httploader package was removed and the HTTPURLLoader +// type was introduced. +// https://github.com/santhosh-tekuri/jsonschema/blob/boon/example_http_test.go +// TODO: make all this stuff configurable, right now it's all hard wired and not very flexible. +// +// use interfaces and abstractions on all this. +type HTTPURLLoader http.Client + +func (l *HTTPURLLoader) Load(url string) (any, error) { + client := (*http.Client)(l) + resp, err := client.Get(url) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + _ = resp.Body.Close() + return nil, fmt.Errorf("%s returned status code %d", url, resp.StatusCode) + } + defer resp.Body.Close() + + return jsonschema.UnmarshalJSON(resp.Body) +} + +func NewHTTPURLLoader(insecure bool) *HTTPURLLoader { + httpLoader := HTTPURLLoader(http.Client{ + Timeout: 15 * time.Second, + }) + if insecure { + httpLoader.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + } + return &httpLoader +} + +func NewCompilerLoader() jsonschema.SchemeURLLoader { + return jsonschema.SchemeURLLoader{ + "file": jsonschema.FileLoader{}, + "http": NewHTTPURLLoader(false), + "https": NewHTTPURLLoader(false), + } +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/helpers/version.go b/vendor/github.com/pb33f/libopenapi-validator/helpers/version.go new file mode 100644 index 000000000..d59e50992 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/helpers/version.go @@ -0,0 +1,15 @@ +package helpers + +import ( + "strings" +) + +// VersionToFloat converts a version string to a float32 for easier comparison. +func VersionToFloat(version string) float32 { + switch { + case strings.HasPrefix(version, "3.0"): + return 3.0 + default: + return 3.1 + } +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/libopenapi-logo.png b/vendor/github.com/pb33f/libopenapi-validator/libopenapi-logo.png new file mode 100644 index 000000000..8336dc511 Binary files /dev/null and b/vendor/github.com/pb33f/libopenapi-validator/libopenapi-logo.png differ diff --git a/vendor/github.com/pb33f/libopenapi-validator/openapi_vocabulary/coercion.go b/vendor/github.com/pb33f/libopenapi-validator/openapi_vocabulary/coercion.go new file mode 100644 index 000000000..3ba87b841 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/openapi_vocabulary/coercion.go @@ -0,0 +1,160 @@ +// Copyright 2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package openapi_vocabulary + +import ( + "regexp" + "strconv" + + "github.com/santhosh-tekuri/jsonschema/v6" +) + +// coercionExtension handles Jackson-style scalar coercion (string->boolean/number) +type coercionExtension struct { + schemaType any // string, []string, or nil + allowCoercion bool +} + +var ( + booleanRegex = regexp.MustCompile(`^(true|false)$`) + numberRegex = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`) + integerRegex = regexp.MustCompile(`^-?(?:0|[1-9]\d*)$`) +) + +func (c *coercionExtension) Validate(ctx *jsonschema.ValidatorContext, v any) { + if !c.allowCoercion { + return // Coercion disabled - let normal validation handle it + } + + str, ok := v.(string) + if !ok { + return // Not a string - let normal validation handle it + } + + // check if we should coerce and validate the string format + if c.shouldCoerceToBoolean() { + if !c.isValidBooleanString(str) { + ctx.AddError(&CoercionError{ + SourceType: "string", + TargetType: "boolean", + Value: str, + Message: "string value cannot be coerced to boolean, must be 'true' or 'false'", + }) + } + return + } + + if c.shouldCoerceToNumber() { + if !c.isValidNumberString(str) { + ctx.AddError(&CoercionError{ + SourceType: "string", + TargetType: "number", + Value: str, + Message: "string value cannot be coerced to number, must be a valid numeric string", + }) + } + return + } + + if c.shouldCoerceToInteger() { + if !c.isValidIntegerString(str) { + ctx.AddError(&CoercionError{ + SourceType: "string", + TargetType: "integer", + Value: str, + Message: "string value cannot be coerced to integer, must be a valid integer string", + }) + } + return + } +} + +func (c *coercionExtension) shouldCoerceToBoolean() bool { + return c.hasType("boolean") +} + +func (c *coercionExtension) shouldCoerceToNumber() bool { + return c.hasType("number") +} + +func (c *coercionExtension) shouldCoerceToInteger() bool { + return c.hasType("integer") +} + +func (c *coercionExtension) hasType(targetType string) bool { + switch t := c.schemaType.(type) { + case string: + return t == targetType + case []any: + for _, item := range t { + if str, ok := item.(string); ok && str == targetType { + return true + } + } + } + return false +} + +func (c *coercionExtension) isValidBooleanString(s string) bool { + return booleanRegex.MatchString(s) +} + +func (c *coercionExtension) isValidNumberString(s string) bool { + if !numberRegex.MatchString(s) { + return false + } + // Additional validation using strconv + _, err := strconv.ParseFloat(s, 64) + return err == nil +} + +func (c *coercionExtension) isValidIntegerString(s string) bool { + if !integerRegex.MatchString(s) { + return false + } + // Additional validation using strconv + _, err := strconv.ParseInt(s, 10, 64) + return err == nil +} + +// CompileCoercion compiles the coercion extension if coercion is allowed and applicable +func CompileCoercion(ctx *jsonschema.CompilerContext, obj map[string]any, allowCoercion bool) (jsonschema.SchemaExt, error) { + if !allowCoercion { + return nil, nil // Coercion disabled + } + + // Get the type from the schema + schemaType, hasType := obj["type"] + if !hasType { + return nil, nil // No type specified - no coercion needed + } + + // Only apply coercion to scalar types + if !IsCoercibleType(schemaType) { + return nil, nil + } + + return &coercionExtension{ + schemaType: schemaType, + allowCoercion: true, + }, nil +} + +// IsCoercibleType checks if the schema type is one that supports coercion +func IsCoercibleType(schemaType any) bool { + switch t := schemaType.(type) { + case string: + return t == "boolean" || t == "number" || t == "integer" + case []any: + // for type arrays, check if any coercible type is present + for _, item := range t { + if str, ok := item.(string); ok { + if str == "boolean" || str == "number" || str == "integer" { + return true + } + } + } + } + return false +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/openapi_vocabulary/discriminator.go b/vendor/github.com/pb33f/libopenapi-validator/openapi_vocabulary/discriminator.go new file mode 100644 index 000000000..b88ddbba3 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/openapi_vocabulary/discriminator.go @@ -0,0 +1,88 @@ +// Copyright 2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package openapi_vocabulary + +import ( + "github.com/santhosh-tekuri/jsonschema/v6" +) + +// discriminatorExtension handles the OpenAPI discriminator keyword +type discriminatorExtension struct { + propertyName string + mapping map[string]string // value -> schema reference +} + +// Validate validates the discriminator property exists in the instance +func (d *discriminatorExtension) Validate(ctx *jsonschema.ValidatorContext, v any) { + obj, _ := v.(map[string]any) + + // check if discriminator property exists in the object + if d.propertyName != "" { + if _, exists := obj[d.propertyName]; !exists { + ctx.AddError(&DiscriminatorPropertyMissingError{ + PropertyName: d.propertyName, + }) + } + } +} + +// CompileDiscriminator compiles the OpenAPI discriminator keyword +func CompileDiscriminator(_ *jsonschema.CompilerContext, obj map[string]any, _ VersionType) (jsonschema.SchemaExt, error) { + v, exists := obj["discriminator"] + if !exists { + return nil, nil + } + + discriminator, ok := v.(map[string]any) + if !ok { + return nil, &OpenAPIKeywordError{ + Keyword: "discriminator", + Message: "discriminator must be an object", + } + } + + propertyNameValue, exists := discriminator["propertyName"] + if !exists { + return nil, &OpenAPIKeywordError{ + Keyword: "discriminator", + Message: "discriminator must have a propertyName field", + } + } + + propertyName, ok := propertyNameValue.(string) + if !ok { + return nil, &OpenAPIKeywordError{ + Keyword: "discriminator", + Message: "discriminator propertyName must be a string", + } + } + + var mapping map[string]string + if mappingValue, exists := discriminator["mapping"]; exists { + mappingObj, ok := mappingValue.(map[string]any) + if !ok { + return nil, &OpenAPIKeywordError{ + Keyword: "discriminator", + Message: "discriminator mapping must be an object", + } + } + + mapping = make(map[string]string) + for key, value := range mappingObj { + if strValue, ok := value.(string); ok { + mapping[key] = strValue + } else { + return nil, &OpenAPIKeywordError{ + Keyword: "discriminator", + Message: "discriminator mapping values must be strings", + } + } + } + } + + return &discriminatorExtension{ + propertyName: propertyName, + mapping: mapping, + }, nil +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/openapi_vocabulary/errors.go b/vendor/github.com/pb33f/libopenapi-validator/openapi_vocabulary/errors.go new file mode 100644 index 000000000..521a5f4f8 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/openapi_vocabulary/errors.go @@ -0,0 +1,57 @@ +// Copyright 2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package openapi_vocabulary + +import ( + "fmt" + + "golang.org/x/text/message" +) + +// OpenAPIKeywordError represents an error with an OpenAPI-specific keyword +type OpenAPIKeywordError struct { + Keyword string + Message string +} + +func (e *OpenAPIKeywordError) Error() string { + return fmt.Sprintf("OpenAPI keyword '%s': %s", e.Keyword, e.Message) +} + +// DiscriminatorPropertyMissingError represents an error when discriminator property is missing +type DiscriminatorPropertyMissingError struct { + PropertyName string +} + +func (e *DiscriminatorPropertyMissingError) KeywordPath() []string { + return []string{"discriminator"} +} + +func (e *DiscriminatorPropertyMissingError) LocalizedString(printer *message.Printer) string { + return fmt.Sprintf("discriminator property '%s' is missing", e.PropertyName) +} + +func (e *DiscriminatorPropertyMissingError) Error() string { + return fmt.Sprintf("discriminator property '%s' is missing", e.PropertyName) +} + +// CoercionError represents an error during scalar type coercion +type CoercionError struct { + SourceType string + TargetType string + Value string + Message string +} + +func (e *CoercionError) KeywordPath() []string { + return []string{"type"} +} + +func (e *CoercionError) LocalizedString(printer *message.Printer) string { + return fmt.Sprintf("cannot coerce %s '%s' to %s: %s", e.SourceType, e.Value, e.TargetType, e.Message) +} + +func (e *CoercionError) Error() string { + return fmt.Sprintf("cannot coerce %s '%s' to %s: %s", e.SourceType, e.Value, e.TargetType, e.Message) +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/openapi_vocabulary/metadata.go b/vendor/github.com/pb33f/libopenapi-validator/openapi_vocabulary/metadata.go new file mode 100644 index 000000000..04bd0b2dd --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/openapi_vocabulary/metadata.go @@ -0,0 +1,57 @@ +// Copyright 2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package openapi_vocabulary + +import ( + "github.com/santhosh-tekuri/jsonschema/v6" +) + +// exampleExtension handles the OpenAPI example keyword (metadata only) +type exampleExtension struct { + example any +} + +func (e *exampleExtension) Validate(ctx *jsonschema.ValidatorContext, v any) { + // Example keyword is metadata only - no validation needed during runtime +} + +// deprecatedExtension handles the OpenAPI deprecated keyword (metadata only) +type deprecatedExtension struct { + deprecated bool +} + +func (d *deprecatedExtension) Validate(ctx *jsonschema.ValidatorContext, v any) { + // Deprecated keyword is metadata only - no validation needed during runtime +} + +// compileExample compiles the example keyword +func CompileExample(ctx *jsonschema.CompilerContext, obj map[string]any, version VersionType) (jsonschema.SchemaExt, error) { + v, exists := obj["example"] + if !exists { + return nil, nil + } + + // Example can be any valid JSON value, so we just store it + // The main validation is that it exists and is parseable (which it is if we got here) + return &exampleExtension{example: v}, nil +} + +// compileDeprecated compiles the deprecated keyword +func CompileDeprecated(ctx *jsonschema.CompilerContext, obj map[string]any, version VersionType) (jsonschema.SchemaExt, error) { + v, exists := obj["deprecated"] + if !exists { + return nil, nil + } + + // Validate that deprecated is a boolean + deprecated, ok := v.(bool) + if !ok { + return nil, &OpenAPIKeywordError{ + Keyword: "deprecated", + Message: "deprecated must be a boolean value", + } + } + + return &deprecatedExtension{deprecated: deprecated}, nil +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/openapi_vocabulary/nullable.go b/vendor/github.com/pb33f/libopenapi-validator/openapi_vocabulary/nullable.go new file mode 100644 index 000000000..bb63c1f64 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/openapi_vocabulary/nullable.go @@ -0,0 +1,34 @@ +// Copyright 2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package openapi_vocabulary + +import ( + "github.com/santhosh-tekuri/jsonschema/v6" +) + +// compileNullable compiles the nullable keyword based on OpenAPI version +func CompileNullable(_ *jsonschema.CompilerContext, obj map[string]any, version VersionType) (jsonschema.SchemaExt, error) { + v, exists := obj["nullable"] + if !exists { + return nil, nil + } + + // check if nullable is used in OpenAPI 3.1+ (not allowed) + if version == Version31 || version == Version32 { + return nil, &OpenAPIKeywordError{ + Keyword: "nullable", + Message: "The `nullable` keyword is not supported in OpenAPI 3.1+. Use `type: ['string', 'null']` instead", + } + } + + // validate that nullable is a boolean + _, ok := v.(bool) + if !ok { + return nil, &OpenAPIKeywordError{ + Keyword: "nullable", + Message: "nullable must be a boolean value", + } + } + return nil, nil +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/openapi_vocabulary/vocabulary.go b/vendor/github.com/pb33f/libopenapi-validator/openapi_vocabulary/vocabulary.go new file mode 100644 index 000000000..452d82a7c --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/openapi_vocabulary/vocabulary.go @@ -0,0 +1,94 @@ +// Copyright 2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package openapi_vocabulary + +import ( + "github.com/santhosh-tekuri/jsonschema/v6" +) + +// OpenAPIVocabularyURL is the vocabulary URL for OpenAPI-specific keywords +const OpenAPIVocabularyURL = "https://pb33f.io/openapi-validator/vocabulary" + +// VersionType represents OpenAPI specification versions +type VersionType int + +const ( + // Version30 represents OpenAPI 3.0.x + Version30 VersionType = iota + Version31 + Version32 +) + +// NewOpenAPIVocabulary creates a vocabulary for OpenAPI-specific keywords +// version determines which keywords are allowed/forbidden +func NewOpenAPIVocabulary(version VersionType) *jsonschema.Vocabulary { + return NewOpenAPIVocabularyWithCoercion(version, false) +} + +// NewOpenAPIVocabularyWithCoercion creates a vocabulary with optional scalar coercion +func NewOpenAPIVocabularyWithCoercion(version VersionType, allowCoercion bool) *jsonschema.Vocabulary { + return &jsonschema.Vocabulary{ + URL: OpenAPIVocabularyURL, + Schema: nil, // We don't validate the vocabulary schema itself + Compile: func(ctx *jsonschema.CompilerContext, obj map[string]any) (jsonschema.SchemaExt, error) { + return compileOpenAPIKeywords(ctx, obj, version, allowCoercion) + }, + } +} + +// compileOpenAPIKeywords compiles all OpenAPI-specific keywords found in the schema object +func compileOpenAPIKeywords(ctx *jsonschema.CompilerContext, + obj map[string]any, + version VersionType, + allowCoercion bool, +) (jsonschema.SchemaExt, error) { + var extensions []jsonschema.SchemaExt + + if ext, err := CompileNullable(ctx, obj, version); err != nil { + return nil, err + } else if ext != nil { + extensions = append(extensions, ext) + } + + if ext, err := CompileDiscriminator(ctx, obj, version); err != nil { + return nil, err + } else if ext != nil { + extensions = append(extensions, ext) + } + + if ext, err := CompileExample(ctx, obj, version); err != nil { + return nil, err + } else if ext != nil { + extensions = append(extensions, ext) + } + + if ext, err := CompileDeprecated(ctx, obj, version); err != nil { + return nil, err + } else if ext != nil { + extensions = append(extensions, ext) + } + + if ext, err := CompileCoercion(ctx, obj, allowCoercion); err != nil { + return nil, err + } else if ext != nil { + extensions = append(extensions, ext) + } + + if len(extensions) == 0 { + return nil, nil + } + + return &combinedExtension{extensions: extensions}, nil +} + +// combinedExtension combines multiple OpenAPI extensions into one +type combinedExtension struct { + extensions []jsonschema.SchemaExt +} + +func (c *combinedExtension) Validate(ctx *jsonschema.ValidatorContext, v any) { + for _, ext := range c.extensions { + ext.Validate(ctx, v) + } +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/parameters/cookie_parameters.go b/vendor/github.com/pb33f/libopenapi-validator/parameters/cookie_parameters.go new file mode 100644 index 000000000..400b95898 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/parameters/cookie_parameters.go @@ -0,0 +1,207 @@ +// Copyright 2023-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package parameters + +import ( + "fmt" + "net/http" + "strconv" + "strings" + + "github.com/pb33f/libopenapi/datamodel/high/base" + + v3 "github.com/pb33f/libopenapi/datamodel/high/v3" + + "github.com/pb33f/libopenapi-validator/errors" + "github.com/pb33f/libopenapi-validator/helpers" + "github.com/pb33f/libopenapi-validator/paths" + "github.com/pb33f/libopenapi-validator/strict" +) + +func (v *paramValidator) ValidateCookieParams(request *http.Request) (bool, []*errors.ValidationError) { + pathItem, errs, foundPath := paths.FindPath(request, v.document, v.options.RegexCache) + if len(errs) > 0 { + return false, errs + } + return v.ValidateCookieParamsWithPathItem(request, pathItem, foundPath) +} + +func (v *paramValidator) ValidateCookieParamsWithPathItem(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) { + if pathItem == nil { + return false, []*errors.ValidationError{{ + ValidationType: helpers.PathValidation, + ValidationSubType: helpers.ValidationMissing, + Message: fmt.Sprintf("%s Path '%s' not found", request.Method, request.URL.Path), + Reason: fmt.Sprintf("The %s request contains a path of '%s' "+ + "however that path, or the %s method for that path does not exist in the specification", + request.Method, request.URL.Path, request.Method), + SpecLine: -1, + SpecCol: -1, + HowToFix: errors.HowToFixPath, + }} + } + // extract params for the operation + params := helpers.ExtractParamsForOperation(request, pathItem) + var validationErrors []*errors.ValidationError + + // build a map of cookies from the request for efficient lookup + cookieMap := make(map[string]*http.Cookie) + for _, cookie := range request.Cookies() { + cookieMap[cookie.Name] = cookie + } + + for _, p := range params { + if p.In == helpers.Cookie { + // look up the cookie by name (cookies are case-sensitive) + cookie, found := cookieMap[p.Name] + if !found { + // cookie not present in request - check if required + if p.Required != nil && *p.Required { + validationErrors = append(validationErrors, errors.CookieParameterMissing(p)) + } + continue + } + + var sch *base.Schema + if p.Schema != nil { + sch = p.Schema.Schema() + } + pType := sch.Type + + for _, ty := range pType { + switch ty { + case helpers.Integer: + if _, err := strconv.ParseInt(cookie.Value, 10, 64); err != nil { + validationErrors = append(validationErrors, + errors.InvalidCookieParamInteger(p, strings.ToLower(cookie.Value), sch)) + break + } + // validate value matches allowed enum values + if sch.Enum != nil { + matchFound := false + for _, enumVal := range sch.Enum { + if strings.TrimSpace(cookie.Value) == fmt.Sprint(enumVal.Value) { + matchFound = true + break + } + } + if !matchFound { + validationErrors = append(validationErrors, + errors.IncorrectCookieParamEnum(p, strings.ToLower(cookie.Value), sch)) + } + } + case helpers.Number: + if _, err := strconv.ParseFloat(cookie.Value, 64); err != nil { + validationErrors = append(validationErrors, + errors.InvalidCookieParamNumber(p, strings.ToLower(cookie.Value), sch)) + break + } + // validate value matches allowed enum values + if sch.Enum != nil { + matchFound := false + for _, enumVal := range sch.Enum { + if strings.TrimSpace(cookie.Value) == fmt.Sprint(enumVal.Value) { + matchFound = true + break + } + } + if !matchFound { + validationErrors = append(validationErrors, + errors.IncorrectCookieParamEnum(p, strings.ToLower(cookie.Value), sch)) + } + } + case helpers.Boolean: + if _, err := strconv.ParseBool(cookie.Value); err != nil { + validationErrors = append(validationErrors, + errors.IncorrectCookieParamBool(p, strings.ToLower(cookie.Value), sch)) + } + case helpers.Object: + if !p.IsExploded() { + encodedObj := helpers.ConstructMapFromCSV(cookie.Value) + + // if a schema was extracted + if sch != nil { + validationErrors = append(validationErrors, + ValidateParameterSchema(sch, encodedObj, "", + "Cookie parameter", + "The cookie parameter", + p.Name, + helpers.ParameterValidation, + helpers.ParameterValidationQuery, + v.options)...) + } + } + case helpers.Array: + + if !p.IsExploded() { + // well we're already in an array, so we need to check the items schema + // to ensure this array items matches the type + // only check if items is a schema, not a boolean + if sch.Items.IsA() { + validationErrors = append(validationErrors, + ValidateCookieArray(sch, p, cookie.Value)...) + } + } + + case helpers.String: + + // check if the schema has an enum, and if so, match the value against one of + // the defined enum values. + if sch.Enum != nil { + matchFound := false + for _, enumVal := range sch.Enum { + if strings.TrimSpace(cookie.Value) == fmt.Sprint(enumVal.Value) { + matchFound = true + break + } + } + if !matchFound { + validationErrors = append(validationErrors, + errors.IncorrectCookieParamEnum(p, strings.ToLower(cookie.Value), sch)) + break + } + } + validationErrors = append(validationErrors, + ValidateSingleParameterSchema( + sch, + cookie.Value, + "Cookie parameter", + "The cookie parameter", + p.Name, + helpers.ParameterValidation, + helpers.ParameterValidationCookie, + v.options, + )...) + } + } + } + } + + errors.PopulateValidationErrors(validationErrors, request, pathValue) + + if len(validationErrors) > 0 { + return false, validationErrors + } + + // strict mode: check for undeclared cookies + if v.options.StrictMode { + undeclaredCookies := strict.ValidateCookies(request, params, v.options) + for _, undeclared := range undeclaredCookies { + validationErrors = append(validationErrors, + errors.UndeclaredCookieError( + undeclared.Path, + undeclared.Name, + undeclared.Value, + undeclared.DeclaredProperties, + request.URL.Path, + request.Method, + )) + } + } + + if len(validationErrors) > 0 { + return false, validationErrors + } + return true, nil +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/parameters/header_parameters.go b/vendor/github.com/pb33f/libopenapi-validator/parameters/header_parameters.go new file mode 100644 index 000000000..b5dbd2be0 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/parameters/header_parameters.go @@ -0,0 +1,236 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package parameters + +import ( + "fmt" + "net/http" + "strconv" + "strings" + + "github.com/pb33f/libopenapi/datamodel/high/base" + + v3 "github.com/pb33f/libopenapi/datamodel/high/v3" + lowbase "github.com/pb33f/libopenapi/datamodel/low/base" + + "github.com/pb33f/libopenapi-validator/errors" + "github.com/pb33f/libopenapi-validator/helpers" + "github.com/pb33f/libopenapi-validator/paths" + "github.com/pb33f/libopenapi-validator/strict" +) + +func (v *paramValidator) ValidateHeaderParams(request *http.Request) (bool, []*errors.ValidationError) { + pathItem, errs, foundPath := paths.FindPath(request, v.document, v.options.RegexCache) + if len(errs) > 0 { + return false, errs + } + return v.ValidateHeaderParamsWithPathItem(request, pathItem, foundPath) +} + +func (v *paramValidator) ValidateHeaderParamsWithPathItem(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) { + if pathItem == nil { + return false, []*errors.ValidationError{{ + ValidationType: helpers.PathValidation, + ValidationSubType: helpers.ValidationMissing, + Message: fmt.Sprintf("%s Path '%s' not found", request.Method, request.URL.Path), + Reason: fmt.Sprintf("The %s request contains a path of '%s' "+ + "however that path, or the %s method for that path does not exist in the specification", + request.Method, request.URL.Path, request.Method), + SpecLine: -1, + SpecCol: -1, + HowToFix: errors.HowToFixPath, + }} + } + // extract params for the operation + params := helpers.ExtractParamsForOperation(request, pathItem) + + var validationErrors []*errors.ValidationError + seenHeaders := make(map[string]bool) + for _, p := range params { + if p.In == helpers.Header { + + seenHeaders[strings.ToLower(p.Name)] = true + if param := request.Header.Get(p.Name); param != "" { + + var sch *base.Schema + if p.Schema != nil { + sch = p.Schema.Schema() + } + pType := sch.Type + + for _, ty := range pType { + switch ty { + case helpers.Integer: + if _, err := strconv.ParseInt(param, 10, 64); err != nil { + validationErrors = append(validationErrors, + errors.InvalidHeaderParamInteger(p, strings.ToLower(param), sch)) + break + } + // check if the param is within the enum + if sch.Enum != nil { + matchFound := false + for _, enumVal := range sch.Enum { + if strings.TrimSpace(param) == fmt.Sprint(enumVal.Value) { + matchFound = true + break + } + } + if !matchFound { + validationErrors = append(validationErrors, + errors.IncorrectCookieParamEnum(p, strings.ToLower(param), sch)) + } + } + + case helpers.Number: + if _, err := strconv.ParseFloat(param, 64); err != nil { + validationErrors = append(validationErrors, + errors.InvalidHeaderParamNumber(p, strings.ToLower(param), sch)) + break + } + // check if the param is within the enum + if sch.Enum != nil { + matchFound := false + for _, enumVal := range sch.Enum { + if strings.TrimSpace(param) == fmt.Sprint(enumVal.Value) { + matchFound = true + break + } + } + if !matchFound { + validationErrors = append(validationErrors, + errors.IncorrectCookieParamEnum(p, strings.ToLower(param), sch)) + } + } + + case helpers.Boolean: + if _, err := strconv.ParseBool(param); err != nil { + validationErrors = append(validationErrors, + errors.IncorrectHeaderParamBool(p, strings.ToLower(param), sch)) + } + + case helpers.Object: + + // check if the header is default encoded or not + var encodedObj map[string]interface{} + // we have found our header, check the explode type. + if p.IsDefaultHeaderEncoding() { + encodedObj = helpers.ConstructMapFromCSV(param) + } else { + if p.IsExploded() { // only option is to be exploded for KV extraction. + encodedObj = helpers.ConstructKVFromCSV(param) + } + } + + if len(encodedObj) == 0 { + validationErrors = append(validationErrors, + errors.HeaderParameterCannotBeDecoded(p, strings.ToLower(param))) + break + } + + // if a schema was extracted + if sch != nil { + validationErrors = append(validationErrors, + ValidateParameterSchema(sch, + encodedObj, + "", + "Header parameter", + "The header parameter", + p.Name, + helpers.ParameterValidation, + helpers.ParameterValidationQuery, v.options)...) + } + + case helpers.Array: + if !p.IsExploded() { // only unexploded arrays are supported for cookie params + if sch.Items.IsA() { + validationErrors = append(validationErrors, + ValidateHeaderArray(sch, p, param)...) + } + } + + case helpers.String: + + // check if the schema has an enum, and if so, match the value against one of + // the defined enum values. + if sch.Enum != nil { + matchFound := false + for _, enumVal := range sch.Enum { + if strings.TrimSpace(param) == fmt.Sprint(enumVal.Value) { + matchFound = true + break + } + } + if !matchFound { + validationErrors = append(validationErrors, + errors.IncorrectHeaderParamEnum(p, strings.ToLower(param), sch)) + break + } + } + validationErrors = append(validationErrors, + ValidateSingleParameterSchema( + sch, + param, + "Header parameter", + "The header parameter", + p.Name, + helpers.ParameterValidation, + helpers.ParameterValidationHeader, + v.options, + )...) + } + } + if len(pType) == 0 { + // validate schema as there is no type information. + validationErrors = append(validationErrors, ValidateSingleParameterSchema(sch, + param, + p.Name, + lowbase.SchemaLabel, p.Name, helpers.ParameterValidation, helpers.ParameterValidationHeader, v.options)...) + } + } else { + if p.Required != nil && *p.Required { + validationErrors = append(validationErrors, errors.HeaderParameterMissing(p)) + } + } + } + } + + errors.PopulateValidationErrors(validationErrors, request, pathValue) + + if len(validationErrors) > 0 { + return false, validationErrors + } + + // strict mode: check for undeclared headers + if v.options.StrictMode { + // Extract security headers applicable to this operation + var securityHeaders []string + if v.document.Components != nil && v.document.Components.SecuritySchemes != nil { + security := helpers.ExtractSecurityForOperation(request, pathItem) + // Convert orderedmap to regular map for the helper + schemesMap := make(map[string]*v3.SecurityScheme) + for pair := v.document.Components.SecuritySchemes.First(); pair != nil; pair = pair.Next() { + schemesMap[pair.Key()] = pair.Value() + } + securityHeaders = helpers.ExtractSecurityHeaderNames(security, schemesMap) + } + + undeclaredHeaders := strict.ValidateRequestHeaders(request.Header, params, securityHeaders, v.options) + for _, undeclared := range undeclaredHeaders { + validationErrors = append(validationErrors, + errors.UndeclaredHeaderError( + undeclared.Name, + undeclared.Value.(string), + undeclared.DeclaredProperties, + undeclared.Direction.String(), + request.URL.Path, + request.Method, + )) + } + } + + if len(validationErrors) > 0 { + return false, validationErrors + } + return true, nil +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/parameters/package.go b/vendor/github.com/pb33f/libopenapi-validator/parameters/package.go new file mode 100644 index 000000000..b339aa775 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/parameters/package.go @@ -0,0 +1,6 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +// Package parameters contains all the logic, models and interfaces for validating OpenAPI 3+ Parameters. +// Cookie, Header, Path and Query parameters are all validated. +package parameters diff --git a/vendor/github.com/pb33f/libopenapi-validator/parameters/parameters.go b/vendor/github.com/pb33f/libopenapi-validator/parameters/parameters.go new file mode 100644 index 000000000..2fa168a14 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/parameters/parameters.go @@ -0,0 +1,79 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package parameters + +import ( + "net/http" + + v3 "github.com/pb33f/libopenapi/datamodel/high/v3" + + "github.com/pb33f/libopenapi-validator/config" + "github.com/pb33f/libopenapi-validator/errors" +) + +// ParameterValidator is an interface that defines the methods for validating parameters +// There are 4 types of parameters: query, header, cookie and path. +// +// ValidateQueryParams will validate the query parameters for the request +// ValidateHeaderParams will validate the header parameters for the request +// ValidateCookieParamsWithPathItem will validate the cookie parameters for the request +// ValidatePathParams will validate the path parameters for the request +// +// Each method accepts an *http.Request and returns true if validation passed, +// false if validation failed and a slice of ValidationError pointers. +type ParameterValidator interface { + // ValidateQueryParams accepts an *http.Request and validates the query parameters against the OpenAPI specification. + // The method will locate the correct path, and operation, based on the verb. The parameters for the operation + // will be matched and validated against what has been supplied in the http.Request query string. + ValidateQueryParams(request *http.Request) (bool, []*errors.ValidationError) + + // ValidateQueryParamsWithPathItem accepts an *http.Request and validates the query parameters against the OpenAPI specification. + // The method will locate the correct path, and operation, based on the verb. The parameters for the operation + // will be matched and validated against what has been supplied in the http.Request query string. + ValidateQueryParamsWithPathItem(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) + + // ValidateHeaderParams validates the header parameters contained within *http.Request. It returns a boolean + // stating true if validation passed (false for failed), and a slice of errors if validation failed. + ValidateHeaderParams(request *http.Request) (bool, []*errors.ValidationError) + + // ValidateHeaderParamsWithPathItem validates the header parameters contained within *http.Request. It returns a boolean + // stating true if validation passed (false for failed), and a slice of errors if validation failed. + ValidateHeaderParamsWithPathItem(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) + + // ValidateCookieParams validates the cookie parameters contained within *http.Request. + // It returns a boolean stating true if validation passed (false for failed), and a slice of errors if validation failed. + ValidateCookieParams(request *http.Request) (bool, []*errors.ValidationError) + + // ValidateCookieParamsWithPathItem validates the cookie parameters contained within *http.Request. + // It returns a boolean stating true if validation passed (false for failed), and a slice of errors if validation failed. + ValidateCookieParamsWithPathItem(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) + + // ValidatePathParams validates the path parameters contained within *http.Request. It returns a boolean stating true + // if validation passed (false for failed), and a slice of errors if validation failed. + ValidatePathParams(request *http.Request) (bool, []*errors.ValidationError) + + // ValidatePathParamsWithPathItem validates the path parameters contained within *http.Request. It returns a boolean stating true + // if validation passed (false for failed), and a slice of errors if validation failed. + ValidatePathParamsWithPathItem(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) + + // ValidateSecurity validates the security requirements for the operation. It returns a boolean stating true + // if validation passed (false for failed), and a slice of errors if validation failed. + ValidateSecurity(request *http.Request) (bool, []*errors.ValidationError) + + // ValidateSecurityWithPathItem validates the security requirements for the operation. It returns a boolean stating true + // if validation passed (false for failed), and a slice of errors if validation failed. + ValidateSecurityWithPathItem(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) +} + +// NewParameterValidator will create a new ParameterValidator from an OpenAPI 3+ document +func NewParameterValidator(document *v3.Document, opts ...config.Option) ParameterValidator { + options := config.NewValidationOptions(opts...) + + return ¶mValidator{options: options, document: document} +} + +type paramValidator struct { + options *config.ValidationOptions + document *v3.Document +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/parameters/path_parameters.go b/vendor/github.com/pb33f/libopenapi-validator/parameters/path_parameters.go new file mode 100644 index 000000000..909b50576 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/parameters/path_parameters.go @@ -0,0 +1,413 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package parameters + +import ( + "fmt" + "net/http" + "net/url" + "regexp" + "strconv" + "strings" + + "github.com/pb33f/libopenapi/datamodel/high/base" + + v3 "github.com/pb33f/libopenapi/datamodel/high/v3" + + "github.com/pb33f/libopenapi-validator/errors" + "github.com/pb33f/libopenapi-validator/helpers" + "github.com/pb33f/libopenapi-validator/paths" +) + +func (v *paramValidator) ValidatePathParams(request *http.Request) (bool, []*errors.ValidationError) { + pathItem, errs, foundPath := paths.FindPath(request, v.document, v.options.RegexCache) + if len(errs) > 0 { + return false, errs + } + return v.ValidatePathParamsWithPathItem(request, pathItem, foundPath) +} + +func (v *paramValidator) ValidatePathParamsWithPathItem(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) { + if pathItem == nil { + return false, []*errors.ValidationError{{ + ValidationType: helpers.PathValidation, + ValidationSubType: helpers.ValidationMissing, + Message: fmt.Sprintf("%s Path '%s' not found", request.Method, request.URL.Path), + Reason: fmt.Sprintf("The %s request contains a path of '%s' "+ + "however that path, or the %s method for that path does not exist in the specification", + request.Method, request.URL.Path, request.Method), + SpecLine: -1, + SpecCol: -1, + HowToFix: errors.HowToFixPath, + }} + } + // split the path into segments + submittedSegments := strings.Split(paths.StripRequestPath(request, v.document), helpers.Slash) + pathSegments := strings.Split(pathValue, helpers.Slash) + + // extract params for the operation + params := helpers.ExtractParamsForOperation(request, pathItem) + var validationErrors []*errors.ValidationError + for _, p := range params { + if p.In == helpers.Path { + // var paramTemplate string + for x := range pathSegments { + if pathSegments[x] == "" { // skip empty segments + continue + } + + var rgx *regexp.Regexp + + if v.options.RegexCache != nil { + if cachedRegex, found := v.options.RegexCache.Load(pathSegments[x]); found { + rgx = cachedRegex.(*regexp.Regexp) + } + } + + if rgx == nil { + + r, err := helpers.GetRegexForPath(pathSegments[x]) + if err != nil { + continue + } + + rgx = r + + if v.options.RegexCache != nil { + v.options.RegexCache.Store(pathSegments[x], r) + } + } + + matches := rgx.FindStringSubmatch(submittedSegments[x]) + matches = matches[1:] + + // Check if it is well-formed. + idxs, errBraces := helpers.BraceIndices(pathSegments[x]) + if errBraces != nil { + continue + } + + idx := 0 + + for _, match := range matches { + + isMatrix := false + isLabel := false + // isExplode := false + isSimple := true + paramTemplate := pathSegments[x][idxs[idx]+1 : idxs[idx+1]-1] + idx += 2 // move to the next brace pair + paramName := paramTemplate + + // check for an asterisk on the end of the parameter (explode) + if strings.HasSuffix(paramTemplate, helpers.Asterisk) { + // isExplode = true + paramName = paramTemplate[:len(paramTemplate)-1] + } + if strings.HasPrefix(paramTemplate, helpers.Period) { + isLabel = true + isSimple = false + paramName = paramName[1:] + } + if strings.HasPrefix(paramTemplate, helpers.SemiColon) { + isMatrix = true + isSimple = false + paramName = paramName[1:] + } + + // does this param name match the current path segment param name + if paramName != p.Name { + continue + } + + paramValue := match + + // URL decode the parameter value before validation + decodedParamValue, _ := url.PathUnescape(paramValue) + + if decodedParamValue == "" { + // Mandatory path parameter cannot be empty + if p.Required != nil && *p.Required { + validationErrors = append(validationErrors, errors.PathParameterMissing(p)) + break + } + continue + } + + // extract the schema from the parameter + sch := p.Schema.Schema() + + // check enum (if present) + enumCheck := func(decodedValue string) { + matchFound := false + for _, enumVal := range sch.Enum { + if strings.TrimSpace(decodedValue) == fmt.Sprint(enumVal.Value) { + matchFound = true + break + } + } + if !matchFound { + validationErrors = append(validationErrors, + errors.IncorrectPathParamEnum(p, strings.ToLower(decodedValue), sch)) + } + } + + // for each type, check the value. + if sch != nil && sch.Type != nil { + for typ := range sch.Type { + switch sch.Type[typ] { + case helpers.String: + + // TODO: label and matrix style validation + + // check if the param is within the enum + if sch.Enum != nil { + enumCheck(decodedParamValue) + break + } + validationErrors = append(validationErrors, + ValidateSingleParameterSchema( + sch, + decodedParamValue, + "Path parameter", + "The path parameter", + p.Name, + helpers.ParameterValidation, + helpers.ParameterValidationPath, + v.options, + )...) + + case helpers.Integer: + // simple use case is already handled in find param. + rawParamValue, paramValueParsed, err := v.resolveInteger(sch, p, isLabel, isMatrix, decodedParamValue) + if err != nil { + validationErrors = append(validationErrors, err...) + break + } + // check if the param is within the enum + if sch.Enum != nil { + enumCheck(rawParamValue) + break + } + validationErrors = append(validationErrors, ValidateSingleParameterSchema( + sch, + paramValueParsed, + "Path parameter", + "The path parameter", + p.Name, + helpers.ParameterValidation, + helpers.ParameterValidationPath, + v.options, + )...) + + case helpers.Number: + // simple use case is already handled in find param. + rawParamValue, paramValueParsed, err := v.resolveNumber(sch, p, isLabel, isMatrix, decodedParamValue) + if err != nil { + validationErrors = append(validationErrors, err...) + break + } + // check if the param is within the enum + if sch.Enum != nil { + enumCheck(rawParamValue) + break + } + validationErrors = append(validationErrors, ValidateSingleParameterSchema( + sch, + paramValueParsed, + "Path parameter", + "The path parameter", + p.Name, + helpers.ParameterValidation, + helpers.ParameterValidationPath, + v.options, + )...) + + case helpers.Boolean: + if isLabel && p.Style == helpers.LabelStyle { + if _, err := strconv.ParseBool(decodedParamValue[1:]); err != nil { + validationErrors = append(validationErrors, + errors.IncorrectPathParamBool(p, decodedParamValue[1:], sch)) + } + } + if isSimple { + if _, err := strconv.ParseBool(decodedParamValue); err != nil { + validationErrors = append(validationErrors, + errors.IncorrectPathParamBool(p, decodedParamValue, sch)) + } + } + if isMatrix && p.Style == helpers.MatrixStyle { + // strip off the colon and the parameter name + decodedForMatrix := strings.Replace(decodedParamValue[1:], fmt.Sprintf("%s=", p.Name), "", 1) + if _, err := strconv.ParseBool(decodedForMatrix); err != nil { + validationErrors = append(validationErrors, + errors.IncorrectPathParamBool(p, decodedForMatrix, sch)) + } + } + case helpers.Object: + var encodedObject interface{} + + if p.IsDefaultPathEncoding() { + encodedObject = helpers.ConstructMapFromCSV(decodedParamValue) + } else { + switch p.Style { + case helpers.LabelStyle: + if !p.IsExploded() { + encodedObject = helpers.ConstructMapFromCSV(decodedParamValue[1:]) + } else { + encodedObject = helpers.ConstructKVFromLabelEncoding(decodedParamValue) + } + case helpers.MatrixStyle: + if !p.IsExploded() { + decodedForMatrix := strings.Replace(decodedParamValue[1:], fmt.Sprintf("%s=", p.Name), "", 1) + encodedObject = helpers.ConstructMapFromCSV(decodedForMatrix) + } else { + decodedForMatrix := strings.Replace(decodedParamValue[1:], fmt.Sprintf("%s=", p.Name), "", 1) + encodedObject = helpers.ConstructKVFromMatrixCSV(decodedForMatrix) + } + default: + if p.IsExploded() { + encodedObject = helpers.ConstructKVFromCSV(decodedParamValue) + } + } + } + // if a schema was extracted + if sch != nil { + validationErrors = append(validationErrors, + ValidateParameterSchema(sch, + encodedObject, + "", + "Path parameter", + "The path parameter", + p.Name, + helpers.ParameterValidation, + helpers.ParameterValidationPath, v.options)...) + } + + case helpers.Array: + + // extract the items schema in order to validate the array items. + if sch.Items != nil && sch.Items.IsA() { + iSch := sch.Items.A.Schema() + for n := range iSch.Type { + // determine how to explode the array + var arrayValues []string + if isSimple { + arrayValues = strings.Split(decodedParamValue, helpers.Comma) + } + if isLabel { + if !p.IsExploded() { + arrayValues = strings.Split(decodedParamValue[1:], helpers.Comma) + } else { + arrayValues = strings.Split(decodedParamValue[1:], helpers.Period) + } + } + if isMatrix { + if !p.IsExploded() { + decodedForMatrix := strings.Replace(decodedParamValue[1:], fmt.Sprintf("%s=", p.Name), "", 1) + arrayValues = strings.Split(decodedForMatrix, helpers.Comma) + } else { + decodedForMatrix := strings.ReplaceAll(decodedParamValue[1:], fmt.Sprintf("%s=", p.Name), "") + arrayValues = strings.Split(decodedForMatrix, helpers.SemiColon) + } + } + switch iSch.Type[n] { + case helpers.Integer: + for pv := range arrayValues { + if _, err := strconv.ParseInt(arrayValues[pv], 10, 64); err != nil { + validationErrors = append(validationErrors, + errors.IncorrectPathParamArrayInteger(p, arrayValues[pv], sch, iSch)) + } + } + case helpers.Number: + for pv := range arrayValues { + if _, err := strconv.ParseFloat(arrayValues[pv], 64); err != nil { + validationErrors = append(validationErrors, + errors.IncorrectPathParamArrayNumber(p, arrayValues[pv], sch, iSch)) + } + } + case helpers.Boolean: + for pv := range arrayValues { + bc := len(validationErrors) + if _, err := strconv.ParseBool(arrayValues[pv]); err != nil { + validationErrors = append(validationErrors, + errors.IncorrectPathParamArrayBoolean(p, arrayValues[pv], sch, iSch)) + continue + } + if len(validationErrors) == bc { + // ParseBool will parse 0 or 1 as false/true to we + // need to catch this edge case. + if arrayValues[pv] == "0" || arrayValues[pv] == "1" { + validationErrors = append(validationErrors, + errors.IncorrectPathParamArrayBoolean(p, arrayValues[pv], sch, iSch)) + continue + } + } + } + } + } + } + } + } + } + } + } + } + } + + errors.PopulateValidationErrors(validationErrors, request, pathValue) + + if len(validationErrors) > 0 { + return false, validationErrors + } + return true, nil +} + +func (v *paramValidator) resolveNumber(sch *base.Schema, p *v3.Parameter, isLabel bool, isMatrix bool, paramValue string) (string, float64, []*errors.ValidationError) { + if isLabel && p.Style == helpers.LabelStyle { + paramValueParsed, err := strconv.ParseFloat(paramValue[1:], 64) + if err != nil { + return "", 0, []*errors.ValidationError{errors.IncorrectPathParamNumber(p, paramValue[1:], sch)} + } + return paramValue[1:], paramValueParsed, nil + } + if isMatrix && p.Style == helpers.MatrixStyle { + // strip off the colon and the parameter name + paramValue = strings.Replace(paramValue[1:], fmt.Sprintf("%s=", p.Name), "", 1) + paramValueParsed, err := strconv.ParseFloat(paramValue, 64) + if err != nil { + return "", 0, []*errors.ValidationError{errors.IncorrectPathParamNumber(p, paramValue[1:], sch)} + } + return paramValue, paramValueParsed, nil + } + paramValueParsed, err := strconv.ParseFloat(paramValue, 64) + if err != nil { + return "", 0, []*errors.ValidationError{errors.IncorrectPathParamNumber(p, paramValue, sch)} + } + return paramValue, paramValueParsed, nil +} + +func (v *paramValidator) resolveInteger(sch *base.Schema, p *v3.Parameter, isLabel bool, isMatrix bool, paramValue string) (string, int64, []*errors.ValidationError) { + if isLabel && p.Style == helpers.LabelStyle { + paramValueParsed, err := strconv.ParseInt(paramValue[1:], 10, 64) + if err != nil { + return "", 0, []*errors.ValidationError{errors.IncorrectPathParamInteger(p, paramValue[1:], sch)} + } + return paramValue[1:], paramValueParsed, nil + } + if isMatrix && p.Style == helpers.MatrixStyle { + // strip off the colon and the parameter name + paramValue = strings.Replace(paramValue[1:], fmt.Sprintf("%s=", p.Name), "", 1) + paramValueParsed, err := strconv.ParseInt(paramValue, 10, 64) + if err != nil { + return "", 0, []*errors.ValidationError{errors.IncorrectPathParamInteger(p, paramValue[1:], sch)} + } + return paramValue, paramValueParsed, nil + } + paramValueParsed, err := strconv.ParseInt(paramValue, 10, 64) + if err != nil { + return "", 0, []*errors.ValidationError{errors.IncorrectPathParamInteger(p, paramValue, sch)} + } + return paramValue, paramValueParsed, nil +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/parameters/query_parameters.go b/vendor/github.com/pb33f/libopenapi-validator/parameters/query_parameters.go new file mode 100644 index 000000000..f670f57d3 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/parameters/query_parameters.go @@ -0,0 +1,303 @@ +// Copyright 2023-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package parameters + +import ( + "encoding/json" + "fmt" + "net/http" + "regexp" + "strconv" + "strings" + + "github.com/pb33f/libopenapi/datamodel/high/base" + "github.com/pb33f/libopenapi/orderedmap" + + v3 "github.com/pb33f/libopenapi/datamodel/high/v3" + + "github.com/pb33f/libopenapi-validator/errors" + "github.com/pb33f/libopenapi-validator/helpers" + "github.com/pb33f/libopenapi-validator/paths" + "github.com/pb33f/libopenapi-validator/strict" +) + +const rx = `[:\/\?#\[\]\@!\$&'\(\)\*\+,;=]` + +var rxRxp = regexp.MustCompile(rx) + +func (v *paramValidator) ValidateQueryParams(request *http.Request) (bool, []*errors.ValidationError) { + pathItem, errs, foundPath := paths.FindPath(request, v.document, v.options.RegexCache) + if len(errs) > 0 { + return false, errs + } + return v.ValidateQueryParamsWithPathItem(request, pathItem, foundPath) +} + +func (v *paramValidator) ValidateQueryParamsWithPathItem(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) { + if pathItem == nil { + return false, []*errors.ValidationError{{ + ValidationType: helpers.PathValidation, + ValidationSubType: helpers.ValidationMissing, + Message: fmt.Sprintf("%s Path '%s' not found", request.Method, request.URL.Path), + Reason: fmt.Sprintf("The %s request contains a path of '%s' "+ + "however that path, or the %s method for that path does not exist in the specification", + request.Method, request.URL.Path, request.Method), + SpecLine: -1, + SpecCol: -1, + HowToFix: errors.HowToFixPath, + }} + } + // extract params for the operation + params := helpers.ExtractParamsForOperation(request, pathItem) + queryParams := make(map[string][]*helpers.QueryParam) + var validationErrors []*errors.ValidationError + + // build a set of spec parameter names for exact matching + specParamNames := make(map[string]bool) + for _, p := range params { + if p.In == helpers.Query { + specParamNames[p.Name] = true + } + } + + for qKey, qVal := range request.URL.Query() { + // check if the query key exactly matches a spec parameter name (e.g., "match[]") + // if so, store it literally without deepObject stripping + if specParamNames[qKey] { + queryParams[qKey] = append(queryParams[qKey], &helpers.QueryParam{ + Key: qKey, + Values: qVal, + }) + } else if strings.IndexRune(qKey, '[') > 0 && strings.IndexRune(qKey, ']') > 0 { + // check if the param is encoded as a property / deepObject + stripped := qKey[:strings.IndexRune(qKey, '[')] + value := qKey[strings.IndexRune(qKey, '[')+1 : strings.IndexRune(qKey, ']')] + queryParams[stripped] = append(queryParams[stripped], &helpers.QueryParam{ + Key: stripped, + Values: qVal, + Property: value, + }) + } else { + queryParams[qKey] = append(queryParams[qKey], &helpers.QueryParam{ + Key: qKey, + Values: qVal, + }) + } + } + + // look through the params for the query key +doneLooking: + for p := range params { + if params[p].In == helpers.Query { + + contentWrapped := false + var contentType string + // check if this param is found as a set of query strings + if jk, ok := queryParams[params[p].Name]; ok { + skipValues: + for _, fp := range jk { + // let's check styles first. + validationErrors = append(validationErrors, ValidateQueryParamStyle(params[p], jk)...) + + // there is a match, is the type correct + // this context is extracted from the 3.1 spec to explain what is going on here: + // For more complex scenarios, the content property can define the media type and schema of the + // parameter. A parameter MUST contain either a schema property, or a content property, but not both. + // The map MUST only contain one entry. (for content) + var sch *base.Schema + if params[p].Schema != nil { + sch = params[p].Schema.Schema() + } else { + // ok, no schema, check for a content type + for pair := orderedmap.First(params[p].Content); pair != nil; pair = pair.Next() { + sch = pair.Value().Schema.Schema() + contentWrapped = true + contentType = pair.Key() + break + } + } + pType := sch.Type + + // for each param, check each type + for _, ef := range fp.Values { + + // check allowReserved values. If this is set to true, then we can allow the + // following characters + // :/?#[]@!$&'()*+,;= + // to be present as they are, without being URLEncoded. + if !params[p].AllowReserved { + if rxRxp.MatchString(ef) && params[p].IsExploded() { + validationErrors = append(validationErrors, + errors.IncorrectReservedValues(params[p], ef, sch)) + } + } + for _, ty := range pType { + switch ty { + + case helpers.String: + validationErrors = append(validationErrors, v.validateSimpleParam(sch, ef, ef, params[p])...) + case helpers.Integer: + efF, err := strconv.ParseInt(ef, 10, 64) + if err != nil { + validationErrors = append(validationErrors, + errors.InvalidQueryParamInteger(params[p], ef, sch)) + break + } + validationErrors = append(validationErrors, v.validateSimpleParam(sch, ef, efF, params[p])...) + case helpers.Number: + efF, err := strconv.ParseFloat(ef, 64) + if err != nil { + validationErrors = append(validationErrors, + errors.InvalidQueryParamNumber(params[p], ef, sch)) + break + } + validationErrors = append(validationErrors, v.validateSimpleParam(sch, ef, efF, params[p])...) + case helpers.Boolean: + if _, err := strconv.ParseBool(ef); err != nil { + validationErrors = append(validationErrors, + errors.IncorrectQueryParamBool(params[p], ef, sch)) + } + case helpers.Object: + + // check what style of encoding was used and then construct a map[string]interface{} + // and pass that in as encoded JSON. + var encodedObj map[string]interface{} + + switch params[p].Style { + case helpers.DeepObject: + encodedObj = helpers.ConstructParamMapFromDeepObjectEncoding(jk, sch) + case helpers.PipeDelimited: + encodedObj = helpers.ConstructParamMapFromPipeEncoding(jk) + case helpers.SpaceDelimited: + encodedObj = helpers.ConstructParamMapFromSpaceEncoding(jk) + default: + // form encoding is default. + if contentWrapped { + switch contentType { + case helpers.JSONContentType: + // we need to unmarshal the JSON into a map[string]interface{} + encodedParams := make(map[string]interface{}) + encodedObj = make(map[string]interface{}) + if err := json.Unmarshal([]byte(ef), &encodedParams); err != nil { + validationErrors = append(validationErrors, + errors.IncorrectParamEncodingJSON(params[p], ef, sch)) + break skipValues + } + encodedObj[params[p].Name] = encodedParams + } + } else { + encodedObj = helpers.ConstructParamMapFromFormEncodingArray(jk) + } + } + + numErrors := len(validationErrors) + validationErrors = append(validationErrors, + ValidateParameterSchema(sch, encodedObj[params[p].Name].(map[string]interface{}), + ef, + "Query parameter", + "The query parameter", + params[p].Name, + helpers.ParameterValidation, + helpers.ParameterValidationQuery, v.options)...) + if len(validationErrors) > numErrors { + // we've already added an error for this, so we can skip the rest of the values + break skipValues + } + + case helpers.Array: + // well we're already in an array, so we need to check the items schema + // to ensure this array items matches the type + // only check if items is a schema, not a boolean + if sch.Items != nil && sch.Items.IsA() { + validationErrors = append(validationErrors, + ValidateQueryArray(sch, params[p], ef, contentWrapped, v.options)...) + } + } + } + } + } + } else { + // if the param is not in the requests, so let's check if this param is an + // object, and if we should use default encoding and explode values. + if params[p].Schema != nil { + sch := params[p].Schema.Schema() + + if len(sch.Type) > 0 && sch.Type[0] == helpers.Object && params[p].IsDefaultFormEncoding() { + // if the param is an object, and we're using default encoding, then we need to + // validate the schema. + decoded := helpers.ConstructParamMapFromQueryParamInput(queryParams) + validationErrors = append(validationErrors, + ValidateParameterSchema(sch, + decoded, + "", + "Query array parameter", + "The query parameter (which is an array)", + params[p].Name, + helpers.ParameterValidation, + helpers.ParameterValidationQuery, v.options)...) + break doneLooking + } + } + // if there is no match, check if the param is required or not. + if params[p].Required != nil && *params[p].Required { + validationErrors = append(validationErrors, errors.QueryParameterMissing(params[p])) + } + } + } + } + + errors.PopulateValidationErrors(validationErrors, request, pathValue) + + if len(validationErrors) > 0 { + return false, validationErrors + } + + // strict mode: check for undeclared query parameters + if v.options.StrictMode { + undeclaredParams := strict.ValidateQueryParams(request, params, v.options) + for _, undeclared := range undeclaredParams { + validationErrors = append(validationErrors, + errors.UndeclaredQueryParamError( + undeclared.Path, + undeclared.Name, + undeclared.Value, + undeclared.DeclaredProperties, + request.URL.Path, + request.Method, + )) + } + } + + if len(validationErrors) > 0 { + return false, validationErrors + } + return true, nil +} + +func (v *paramValidator) validateSimpleParam(sch *base.Schema, rawParam string, parsedParam any, parameter *v3.Parameter) (validationErrors []*errors.ValidationError) { + // check if the param is within an enum + if sch.Enum != nil { + matchFound := false + for _, enumVal := range sch.Enum { + if strings.TrimSpace(rawParam) == fmt.Sprint(enumVal.Value) { + matchFound = true + break + } + } + if !matchFound { + return []*errors.ValidationError{errors.IncorrectQueryParamEnum(parameter, rawParam, sch)} + } + } + + return ValidateSingleParameterSchema( + sch, + parsedParam, + "Query parameter", + "The query parameter", + parameter.Name, + helpers.ParameterValidation, + helpers.ParameterValidationQuery, + v.options, + ) +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/parameters/validate_parameter.go b/vendor/github.com/pb33f/libopenapi-validator/parameters/validate_parameter.go new file mode 100644 index 000000000..041974e7d --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/parameters/validate_parameter.go @@ -0,0 +1,302 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package parameters + +import ( + "encoding/json" + "fmt" + "net/url" + "reflect" + "strings" + + "github.com/pb33f/libopenapi/datamodel/high/base" + "github.com/pb33f/libopenapi/utils" + "github.com/santhosh-tekuri/jsonschema/v6" + "golang.org/x/text/language" + "golang.org/x/text/message" + + stdError "errors" + + "github.com/pb33f/libopenapi-validator/config" + "github.com/pb33f/libopenapi-validator/errors" + "github.com/pb33f/libopenapi-validator/helpers" +) + +func ValidateSingleParameterSchema( + schema *base.Schema, + rawObject any, + entity string, + reasonEntity string, + name string, + validationType string, + subValType string, + o *config.ValidationOptions, +) (validationErrors []*errors.ValidationError) { + // Get the JSON Schema for the parameter definition. + jsonSchema, err := buildJsonRender(schema) + if err != nil { + return validationErrors + } + + // Attempt to compile the JSON Schema + jsch, err := helpers.NewCompiledSchema(name, jsonSchema, o) + if err != nil { + return validationErrors + } + + // Validate the object and report any errors. + scErrs := jsch.Validate(rawObject) + var werras *jsonschema.ValidationError + if stdError.As(scErrs, &werras) { + validationErrors = formatJsonSchemaValidationError(schema, werras, entity, reasonEntity, name, validationType, subValType) + } + return validationErrors +} + +// buildJsonRender build a JSON render of the schema. +func buildJsonRender(schema *base.Schema) ([]byte, error) { + if schema == nil { + // Sanity Check + return nil, stdError.New("buildJSONRender nil pointer") + } + + renderedSchema, err := schema.Render() + if err != nil { + return nil, err + } + + return utils.ConvertYAMLtoJSON(renderedSchema) +} + +// ValidateParameterSchema will validate a parameter against a raw object, or a blob of json/yaml. +// It will return a list of validation errors, if any. +// +// schema: the schema to validate against +// rawObject: the object to validate (leave empty if using a blob) +// rawBlob: the blob to validate (leave empty if using an object) +// entity: the entity being validated +// reasonEntity: the entity that caused the validation to be called +// name: the name of the parameter +// validationType: the type of validation being performed +// subValType: the type of sub-validation being performed +func ValidateParameterSchema( + schema *base.Schema, + rawObject any, + rawBlob, + entity, + reasonEntity, + name, + validationType, + subValType string, + validationOptions *config.ValidationOptions, +) []*errors.ValidationError { + var validationErrors []*errors.ValidationError + + // 1. build a JSON render of the schema. + renderCtx := base.NewInlineRenderContext() + renderedSchema, _ := schema.RenderInlineWithContext(renderCtx) + jsonSchema, _ := utils.ConvertYAMLtoJSON(renderedSchema) + + // 2. decode the object into a json blob. + var decodedObj interface{} + rawIsMap := false + validEncoding := false + if rawObject != nil { + // check what type of object it is + ot := reflect.TypeOf(rawObject) + var ok bool + switch ot.Kind() { + case reflect.Map: + if decodedObj, ok = rawObject.(map[string]interface{}); ok { + rawIsMap = true + validEncoding = true + } else { + rawIsMap = true + } + } + } else { + decodedString, _ := url.QueryUnescape(rawBlob) + err := json.Unmarshal([]byte(decodedString), &decodedObj) + if err != nil { + decodedObj = rawBlob + } + validEncoding = true + } + // 3. create a new json schema compiler and add the schema to it + jsch, err := helpers.NewCompiledSchema(name, jsonSchema, validationOptions) + if err != nil { + // schema compilation failed, return validation error instead of panicking + violation := &errors.SchemaValidationFailure{ + Reason: fmt.Sprintf("failed to compile JSON schema: %s", err.Error()), + Location: "schema compilation", + ReferenceSchema: string(jsonSchema), + } + validationErrors = append(validationErrors, &errors.ValidationError{ + ValidationType: validationType, + ValidationSubType: subValType, + Message: fmt.Sprintf("%s '%s' failed schema compilation", entity, name), + Reason: fmt.Sprintf("%s '%s' schema compilation failed: %s", + reasonEntity, name, err.Error()), + SpecLine: 1, + SpecCol: 0, + ParameterName: name, + SchemaValidationErrors: []*errors.SchemaValidationFailure{violation}, + HowToFix: "check the parameter schema for invalid JSON Schema syntax, complex regex patterns, or unsupported schema constructs", + Context: string(jsonSchema), + }) + return validationErrors + } + + // 4. validate the object against the schema + var scErrs error + if validEncoding { + p := decodedObj + if rawIsMap { + if g, ko := rawObject.(map[string]interface{}); ko { + if len(g) == 0 || (g[""] != nil && g[""] == "") { + p = nil + } + } + } + if p != nil { + + // check if any of the items have an empty key + skip := false + if rawIsMap { + for k := range p.(map[string]interface{}) { + if k == "" { + validationErrors = append(validationErrors, &errors.ValidationError{ + ValidationType: validationType, + ValidationSubType: subValType, + Message: fmt.Sprintf("%s '%s' failed to validate", entity, name), + Reason: fmt.Sprintf("%s '%s' is defined as an object, "+ + "however it failed to pass a schema validation", reasonEntity, name), + SpecLine: schema.GoLow().Type.KeyNode.Line, + SpecCol: schema.GoLow().Type.KeyNode.Column, + SchemaValidationErrors: nil, + HowToFix: errors.HowToFixInvalidSchema, + }) + skip = true + break + } + } + } + if !skip { + scErrs = jsch.Validate(p) + } + } + } + var werras *jsonschema.ValidationError + if stdError.As(scErrs, &werras) { + validationErrors = formatJsonSchemaValidationError(schema, werras, entity, reasonEntity, name, validationType, subValType) + } + + // if there are no validationErrors, check that the supplied value is even JSON + if len(validationErrors) == 0 { + if rawIsMap { + if !validEncoding { + // add the error to the list + validationErrors = append(validationErrors, &errors.ValidationError{ + ValidationType: validationType, + ValidationSubType: subValType, + Message: fmt.Sprintf("%s '%s' cannot be decoded", entity, name), + Reason: fmt.Sprintf("%s '%s' is defined as an object, "+ + "however it failed to be decoded as an object", reasonEntity, name), + SpecLine: schema.GoLow().RootNode.Line, + SpecCol: schema.GoLow().RootNode.Column, + HowToFix: errors.HowToFixDecodingError, + }) + } + } + } + return validationErrors +} + +func formatJsonSchemaValidationError(schema *base.Schema, scErrs *jsonschema.ValidationError, entity string, reasonEntity string, name string, validationType string, subValType string) (validationErrors []*errors.ValidationError) { + // flatten the validationErrors + schFlatErrs := scErrs.BasicOutput().Errors + var schemaValidationErrors []*errors.SchemaValidationFailure + for q := range schFlatErrs { + er := schFlatErrs[q] + + errMsg := er.Error.Kind.LocalizedString(message.NewPrinter(language.Tag{})) + if er.KeywordLocation == "" || helpers.IgnoreRegex.MatchString(errMsg) { + continue // ignore this error, it's not useful + } + + fail := &errors.SchemaValidationFailure{ + Reason: errMsg, + Location: er.KeywordLocation, + FieldName: helpers.ExtractFieldNameFromStringLocation(er.InstanceLocation), + FieldPath: helpers.ExtractJSONPathFromStringLocation(er.InstanceLocation), + InstancePath: helpers.ConvertStringLocationToPathSegments(er.InstanceLocation), + OriginalError: scErrs, + } + if schema != nil { + renderCtx := base.NewInlineRenderContext() + rendered, err := schema.RenderInlineWithContext(renderCtx) + if err == nil && rendered != nil { + fail.ReferenceSchema = string(rendered) + } + } + schemaValidationErrors = append(schemaValidationErrors, fail) + } + schemaType := "undefined" + line := 0 + col := 0 + if len(schema.Type) > 0 { + schemaType = schema.Type[0] + line = schema.GoLow().Type.KeyNode.Line + col = schema.GoLow().Type.KeyNode.Column + } else { + var sTypes []string + seen := make(map[string]struct{}) + extractTypes := func(s *base.SchemaProxy) { + pSch := s.Schema() + if pSch != nil { + for _, typ := range pSch.Type { + if _, ok := seen[typ]; !ok { + sTypes = append(sTypes, typ) + seen[typ] = struct{}{} + } + } + } + } + processPoly := func(schemas []*base.SchemaProxy) { + for _, s := range schemas { + extractTypes(s) + } + } + + // check if there is polymorphism going on here. + if len(schema.AnyOf) > 0 || len(schema.AllOf) > 0 || len(schema.OneOf) > 0 { + processPoly(schema.AnyOf) + processPoly(schema.AllOf) + processPoly(schema.OneOf) + + sep := "or" + if len(schema.AllOf) > 0 { + sep = "and a" + } + schemaType = strings.Join(sTypes, fmt.Sprintf(" %s ", sep)) + } + + line = schema.GoLow().RootNode.Line + col = schema.GoLow().RootNode.Column + } + + validationErrors = append(validationErrors, &errors.ValidationError{ + ValidationType: validationType, + ValidationSubType: subValType, + Message: fmt.Sprintf("%s '%s' failed to validate", entity, name), + Reason: fmt.Sprintf("%s '%s' is defined as an %s, "+ + "however it failed to pass a schema validation", reasonEntity, name, schemaType), + SpecLine: line, + SpecCol: col, + ParameterName: name, + SchemaValidationErrors: schemaValidationErrors, + HowToFix: errors.HowToFixInvalidSchema, + }) + return validationErrors +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/parameters/validate_security.go b/vendor/github.com/pb33f/libopenapi-validator/parameters/validate_security.go new file mode 100644 index 000000000..e5ce09894 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/parameters/validate_security.go @@ -0,0 +1,222 @@ +// Copyright 2023-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package parameters + +import ( + "fmt" + "net/http" + "strings" + + "github.com/pb33f/libopenapi/datamodel/high/base" + v3 "github.com/pb33f/libopenapi/datamodel/high/v3" + "github.com/pb33f/libopenapi/orderedmap" + + "github.com/pb33f/libopenapi-validator/errors" + "github.com/pb33f/libopenapi-validator/helpers" + "github.com/pb33f/libopenapi-validator/paths" +) + +func (v *paramValidator) ValidateSecurity(request *http.Request) (bool, []*errors.ValidationError) { + pathItem, errs, foundPath := paths.FindPath(request, v.document, v.options.RegexCache) + if len(errs) > 0 { + return false, errs + } + return v.ValidateSecurityWithPathItem(request, pathItem, foundPath) +} + +func (v *paramValidator) ValidateSecurityWithPathItem(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) { + if pathItem == nil { + return false, []*errors.ValidationError{{ + ValidationType: helpers.PathValidation, + ValidationSubType: helpers.ValidationMissing, + Message: fmt.Sprintf("%s Path '%s' not found", request.Method, request.URL.Path), + Reason: fmt.Sprintf("The %s request contains a path of '%s' "+ + "however that path, or the %s method for that path does not exist in the specification", + request.Method, request.URL.Path, request.Method), + SpecLine: -1, + SpecCol: -1, + HowToFix: errors.HowToFixPath, + }} + } + if !v.options.SecurityValidation { + return true, nil + } + // extract security for the operation + security := helpers.ExtractSecurityForOperation(request, pathItem) + + if security == nil { + return true, nil + } + + var allErrors []*errors.ValidationError + + // each security requirement in the array is OR'd - any one passing is sufficient + for _, sec := range security { + if sec.ContainsEmptyRequirement { + return true, nil + } + + // within a requirement, all schemes are AND'd - all must pass + requirementSatisfied := true + var requirementErrors []*errors.ValidationError + + for pair := orderedmap.First(sec.Requirements); pair != nil; pair = pair.Next() { + secName := pair.Key() + + // look up security from components + if v.document.Components == nil || v.document.Components.SecuritySchemes.GetOrZero(secName) == nil { + validationErrors := []*errors.ValidationError{ + { + Message: fmt.Sprintf("Security scheme '%s' is missing", secName), + Reason: fmt.Sprintf("The security scheme '%s' is defined as being required, "+ + "however it's missing from the components", secName), + ValidationType: helpers.SecurityValidation, + SpecLine: sec.GoLow().Requirements.ValueNode.Line, + SpecCol: sec.GoLow().Requirements.ValueNode.Column, + HowToFix: "Add the missing security scheme to the components", + }, + } + errors.PopulateValidationErrors(validationErrors, request, pathValue) + requirementSatisfied = false + requirementErrors = append(requirementErrors, validationErrors...) + continue + } + + secScheme := v.document.Components.SecuritySchemes.GetOrZero(secName) + schemeValid, schemeErrors := v.validateSecurityScheme(secScheme, sec, request, pathValue) + if !schemeValid { + requirementSatisfied = false + requirementErrors = append(requirementErrors, schemeErrors...) + } + } + + // if all schemes in this requirement passed (AND), the overall security passes (OR) + if requirementSatisfied { + return true, nil + } + allErrors = append(allErrors, requirementErrors...) + } + + return false, allErrors +} + +// validateSecurityScheme checks if a single security scheme is satisfied by the request. +func (v *paramValidator) validateSecurityScheme( + secScheme *v3.SecurityScheme, + sec *base.SecurityRequirement, + request *http.Request, + pathValue string, +) (bool, []*errors.ValidationError) { + switch strings.ToLower(secScheme.Type) { + case "http": + return v.validateHTTPSecurityScheme(secScheme, sec, request, pathValue) + case "apikey": + return v.validateAPIKeySecurityScheme(secScheme, sec, request, pathValue) + } + // unknown scheme type - consider it valid to avoid false negatives + return true, nil +} + +func (v *paramValidator) validateHTTPSecurityScheme( + secScheme *v3.SecurityScheme, + sec *base.SecurityRequirement, + request *http.Request, + pathValue string, +) (bool, []*errors.ValidationError) { + switch strings.ToLower(secScheme.Scheme) { + case "basic", "bearer", "digest": + if request.Header.Get("Authorization") == "" { + validationErrors := []*errors.ValidationError{ + { + Message: fmt.Sprintf("Authorization header for '%s' scheme", secScheme.Scheme), + Reason: "Authorization header was not found", + ValidationType: helpers.SecurityValidation, + ValidationSubType: secScheme.Scheme, + SpecLine: sec.GoLow().Requirements.ValueNode.Line, + SpecCol: sec.GoLow().Requirements.ValueNode.Column, + HowToFix: "Add an 'Authorization' header to this request", + }, + } + errors.PopulateValidationErrors(validationErrors, request, pathValue) + return false, validationErrors + } + return true, nil + } + return true, nil +} + +func (v *paramValidator) validateAPIKeySecurityScheme( + secScheme *v3.SecurityScheme, + sec *base.SecurityRequirement, + request *http.Request, + pathValue string, +) (bool, []*errors.ValidationError) { + switch secScheme.In { + case "header": + if request.Header.Get(secScheme.Name) == "" { + validationErrors := []*errors.ValidationError{ + { + Message: fmt.Sprintf("API Key %s not found in header", secScheme.Name), + Reason: "API Key not found in http header for security scheme 'apiKey' with type 'header'", + ValidationType: helpers.SecurityValidation, + ValidationSubType: "apiKey", + SpecLine: sec.GoLow().Requirements.ValueNode.Line, + SpecCol: sec.GoLow().Requirements.ValueNode.Column, + HowToFix: fmt.Sprintf("Add the API Key via '%s' as a header of the request", secScheme.Name), + }, + } + errors.PopulateValidationErrors(validationErrors, request, pathValue) + return false, validationErrors + } + return true, nil + + case "query": + if request.URL.Query().Get(secScheme.Name) == "" { + copyUrl := *request.URL + fixed := ©Url + q := fixed.Query() + q.Add(secScheme.Name, "your-api-key") + fixed.RawQuery = q.Encode() + + validationErrors := []*errors.ValidationError{ + { + Message: fmt.Sprintf("API Key %s not found in query", secScheme.Name), + Reason: "API Key not found in URL query for security scheme 'apiKey' with type 'query'", + ValidationType: helpers.SecurityValidation, + ValidationSubType: "apiKey", + SpecLine: sec.GoLow().Requirements.ValueNode.Line, + SpecCol: sec.GoLow().Requirements.ValueNode.Column, + HowToFix: fmt.Sprintf("Add an API Key via '%s' to the query string "+ + "of the URL, for example '%s'", secScheme.Name, fixed.String()), + }, + } + errors.PopulateValidationErrors(validationErrors, request, pathValue) + return false, validationErrors + } + return true, nil + + case "cookie": + cookies := request.Cookies() + for _, cookie := range cookies { + if cookie.Name == secScheme.Name { + return true, nil + } + } + validationErrors := []*errors.ValidationError{ + { + Message: fmt.Sprintf("API Key %s not found in cookies", secScheme.Name), + Reason: "API Key not found in http request cookies for security scheme 'apiKey' with type 'cookie'", + ValidationType: helpers.SecurityValidation, + ValidationSubType: "apiKey", + SpecLine: sec.GoLow().Requirements.ValueNode.Line, + SpecCol: sec.GoLow().Requirements.ValueNode.Column, + HowToFix: fmt.Sprintf("Submit an API Key '%s' as a cookie with the request", secScheme.Name), + }, + } + errors.PopulateValidationErrors(validationErrors, request, pathValue) + return false, validationErrors + } + + return true, nil +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/parameters/validation_functions.go b/vendor/github.com/pb33f/libopenapi-validator/parameters/validation_functions.go new file mode 100644 index 000000000..f1080a598 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/parameters/validation_functions.go @@ -0,0 +1,288 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package parameters + +import ( + "fmt" + "slices" + "strconv" + "strings" + + "github.com/pb33f/libopenapi/datamodel/high/base" + "github.com/pb33f/libopenapi/datamodel/high/v3" + + "github.com/pb33f/libopenapi-validator/config" + "github.com/pb33f/libopenapi-validator/errors" + "github.com/pb33f/libopenapi-validator/helpers" +) + +// ValidateCookieArray will validate a cookie parameter that is an array +func ValidateCookieArray( + sch *base.Schema, param *v3.Parameter, value string, +) []*errors.ValidationError { + var validationErrors []*errors.ValidationError + itemsSchema := sch.Items.A.Schema() + + // header arrays can only be encoded as CSV + items := helpers.ExplodeQueryValue(value, helpers.DefaultDelimited) + + // now check each item in the array + for _, item := range items { + // for each type defined in the item's schema, check the item + for _, itemType := range itemsSchema.Type { + switch itemType { + case helpers.Integer, helpers.Number: + if _, err := strconv.ParseFloat(item, 64); err != nil { + validationErrors = append(validationErrors, + errors.IncorrectCookieParamArrayNumber(param, item, sch, itemsSchema)) + } + case helpers.Boolean: + if _, err := strconv.ParseBool(item); err != nil { + validationErrors = append(validationErrors, + errors.IncorrectCookieParamArrayBoolean(param, item, sch, itemsSchema)) + break + } + // check for edge-cases "0" and "1" which can also be parsed into valid booleans + if item == "0" || item == "1" { + validationErrors = append(validationErrors, + errors.IncorrectCookieParamArrayBoolean(param, item, sch, itemsSchema)) + } + case helpers.String: + // do nothing for now. + continue + } + } + } + return validationErrors +} + +// ValidateHeaderArray will validate a header parameter that is an array +func ValidateHeaderArray( + sch *base.Schema, param *v3.Parameter, value string, +) []*errors.ValidationError { + var validationErrors []*errors.ValidationError + itemsSchema := sch.Items.A.Schema() + + // header arrays can only be encoded as CSV + items := helpers.ExplodeQueryValue(value, helpers.DefaultDelimited) + + // now check each item in the array + for _, item := range items { + // for each type defined in the item's schema, check the item + for _, itemType := range itemsSchema.Type { + switch itemType { + case helpers.Integer, helpers.Number: + if _, err := strconv.ParseFloat(item, 64); err != nil { + validationErrors = append(validationErrors, + errors.IncorrectHeaderParamArrayNumber(param, item, sch, itemsSchema)) + } + case helpers.Boolean: + if _, err := strconv.ParseBool(item); err != nil { + validationErrors = append(validationErrors, + errors.IncorrectHeaderParamArrayBoolean(param, item, sch, itemsSchema)) + break + } + // check for edge-cases "0" and "1" which can also be parsed into valid booleans + if item == "0" || item == "1" { + validationErrors = append(validationErrors, + errors.IncorrectHeaderParamArrayBoolean(param, item, sch, itemsSchema)) + } + case helpers.String: + // do nothing for now. + continue + } + } + } + return validationErrors +} + +// ValidateQueryArray will validate a query parameter that is an array +func ValidateQueryArray( + sch *base.Schema, param *v3.Parameter, ef string, contentWrapped bool, validationOptions *config.ValidationOptions, +) []*errors.ValidationError { + var validationErrors []*errors.ValidationError + itemsSchema := sch.Items.A.Schema() + + // check for an exploded bit on the schema. + // if it's exploded, then we need to check each item in the array + // if it's not exploded, then we need to check the whole array as a string + var items []string + if param.IsExploded() { + items = helpers.ExplodeQueryValue(ef, param.Style) + } else { + // check for a style of form (or no style) and if so, explode the value + if param.Style == "" || param.Style == helpers.Form { + if !contentWrapped { + items = helpers.ExplodeQueryValue(ef, param.Style) + } else { + items = []string{ef} + } + } else { + switch param.Style { + case helpers.PipeDelimited, helpers.SpaceDelimited: + items = helpers.ExplodeQueryValue(ef, param.Style) + } + } + } + + // check if the param is within an enum + checkEnum := func(item string) { + // check if the array param is within an enum + if sch.Items.IsA() { + itemsSch := sch.Items.A.Schema() + if itemsSch.Enum != nil { + matchFound := false + for _, enumVal := range itemsSch.Enum { + if strings.TrimSpace(item) == fmt.Sprint(enumVal.Value) { + matchFound = true + break + } + } + if !matchFound { + validationErrors = append(validationErrors, + errors.IncorrectQueryParamEnumArray(param, item, sch)) + } + } + } + } + + // now check each item in the array + seen := make(map[string]struct{}) + uniqueItems := true + var duplicates []string + for _, item := range items { + + if _, exists := seen[item]; exists { + uniqueItems = false + duplicates = append(duplicates, item) + } + seen[item] = struct{}{} + + // for each type defined in the item's schema, check the item + for _, itemType := range itemsSchema.Type { + switch itemType { + case helpers.Integer: + if _, err := strconv.ParseInt(item, 10, 64); err != nil { + validationErrors = append(validationErrors, + errors.IncorrectQueryParamArrayInteger(param, item, sch, itemsSchema)) + break + } + // will it blend? + checkEnum(item) + case helpers.Number: + if _, err := strconv.ParseFloat(item, 64); err != nil { + validationErrors = append(validationErrors, + errors.IncorrectQueryParamArrayNumber(param, item, sch, itemsSchema)) + break + } + // will it blend? + checkEnum(item) + + case helpers.Boolean: + if _, err := strconv.ParseBool(item); err != nil { + validationErrors = append(validationErrors, + errors.IncorrectQueryParamArrayBoolean(param, item, sch, itemsSchema)) + } + case helpers.Object: + validationErrors = append(validationErrors, + ValidateParameterSchema(itemsSchema, + nil, + item, + "Query array parameter", + "The query parameter (which is an array)", + param.Name, + helpers.ParameterValidation, + helpers.ParameterValidationQuery, validationOptions)...) + + case helpers.String: + + // will it float? + checkEnum(item) + } + } + } + + // check for min and max items + if sch.MaxItems != nil { + if len(items) > int(*sch.MaxItems) { + validationErrors = append(validationErrors, + errors.IncorrectParamArrayMaxNumItems(param, sch, *sch.MaxItems, int64(len(items)))) + } + } + + if sch.MinItems != nil { + if len(items) < int(*sch.MinItems) { + validationErrors = append(validationErrors, + errors.IncorrectParamArrayMinNumItems(param, sch, *sch.MinItems, int64(len(items)))) + } + } + + // check for unique items + if sch.UniqueItems != nil { + if *sch.UniqueItems && !uniqueItems { + validationErrors = append(validationErrors, + errors.IncorrectParamArrayUniqueItems(param, sch, strings.Join(duplicates, ", "))) + } + } + return validationErrors +} + +// ValidateQueryParamStyle will validate a query parameter by style +func ValidateQueryParamStyle(param *v3.Parameter, as []*helpers.QueryParam) []*errors.ValidationError { + var validationErrors []*errors.ValidationError +stopValidation: + for _, qp := range as { + for i := range qp.Values { + switch param.Style { + case helpers.DeepObject: + // check if the object has additional properties defined that treat this as an array + if param.Schema != nil { + pSchema := param.Schema.Schema() + if slices.Contains(pSchema.Type, helpers.Array) { + continue + } + if pSchema.AdditionalProperties != nil && pSchema.AdditionalProperties.IsA() { + addPropSchema := pSchema.AdditionalProperties.A.Schema() + if len(addPropSchema.Type) > 0 { + if slices.Contains(addPropSchema.Type, helpers.Array) { + // an array can have more than one value. + continue + } + } + } + } + if len(qp.Values) > 1 { + validationErrors = append(validationErrors, errors.InvalidDeepObject(param, qp)) + break stopValidation + } + + case helpers.PipeDelimited: + // check if explode is false, but we have used an array style + if !param.IsExploded() { + if len(qp.Values) > 1 { + validationErrors = append(validationErrors, errors.IncorrectPipeDelimiting(param, qp)) + break stopValidation + } + } + case helpers.SpaceDelimited: + // check if explode is false, but we have used an array style + if !param.IsExploded() { + if len(qp.Values) > 1 { + validationErrors = append(validationErrors, errors.IncorrectSpaceDelimiting(param, qp)) + break stopValidation + } + } + default: + // check for a delimited list. + if helpers.DoesFormParamContainDelimiter(qp.Values[i], param.Style) { + if param.Explode != nil && *param.Explode { + validationErrors = append(validationErrors, errors.IncorrectFormEncoding(param, qp, i)) + break stopValidation + } + } + } + } + } + return validationErrors // defaults to true if no style is set. +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/paths/package.go b/vendor/github.com/pb33f/libopenapi-validator/paths/package.go new file mode 100644 index 000000000..c651ce5ca --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/paths/package.go @@ -0,0 +1,5 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +// Package paths contains all the logic, models and interfaces for validating OpenAPI 3+ Paths. +package paths diff --git a/vendor/github.com/pb33f/libopenapi-validator/paths/paths.go b/vendor/github.com/pb33f/libopenapi-validator/paths/paths.go new file mode 100644 index 000000000..d8e3806ab --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/paths/paths.go @@ -0,0 +1,222 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package paths + +import ( + "fmt" + "net/http" + "net/url" + "path/filepath" + "regexp" + "strings" + + "github.com/pb33f/libopenapi/orderedmap" + + v3 "github.com/pb33f/libopenapi/datamodel/high/v3" + + "github.com/pb33f/libopenapi-validator/config" + "github.com/pb33f/libopenapi-validator/errors" + "github.com/pb33f/libopenapi-validator/helpers" +) + +// FindPath will find the path in the document that matches the request path. If a successful match was found, then +// the first return value will be a pointer to the PathItem. The second return value will contain any validation errors +// that were picked up when locating the path. +// The third return value will be the path that was found in the document, as it pertains to the contract, so all path +// parameters will not have been replaced with their values from the request - allowing model lookups. +// +// Path matching follows the OpenAPI specification: literal (concrete) paths take precedence over +// parameterized paths, regardless of definition order in the specification. +func FindPath(request *http.Request, document *v3.Document, regexCache config.RegexCache) (*v3.PathItem, []*errors.ValidationError, string) { + basePaths := getBasePaths(document) + stripped := StripRequestPath(request, document) + + reqPathSegments := strings.Split(stripped, "/") + if reqPathSegments[0] == "" { + reqPathSegments = reqPathSegments[1:] + } + + candidates := make([]pathCandidate, 0, document.Paths.PathItems.Len()) + + for pair := orderedmap.First(document.Paths.PathItems); pair != nil; pair = pair.Next() { + path := pair.Key() + pathItem := pair.Value() + + pathForMatching := normalizePathForMatching(path, stripped) + + segs := strings.Split(pathForMatching, "/") + if segs[0] == "" { + segs = segs[1:] + } + + ok := comparePaths(segs, reqPathSegments, basePaths, regexCache) + if !ok { + continue + } + + // Compute specificity score and check if method exists + score := computeSpecificityScore(path) + hasMethod := pathHasMethod(pathItem, request.Method) + + candidates = append(candidates, pathCandidate{ + pathItem: pathItem, + path: path, + score: score, + hasMethod: hasMethod, + }) + } + + if len(candidates) == 0 { + validationErrors := []*errors.ValidationError{ + { + ValidationType: helpers.PathValidation, + ValidationSubType: helpers.ValidationMissing, + Message: fmt.Sprintf("%s Path '%s' not found", request.Method, request.URL.Path), + Reason: fmt.Sprintf("The %s request contains a path of '%s' "+ + "however that path, or the %s method for that path does not exist in the specification", + request.Method, request.URL.Path, request.Method), + SpecLine: -1, + SpecCol: -1, + HowToFix: errors.HowToFixPath, + }, + } + errors.PopulateValidationErrors(validationErrors, request, "") + return nil, validationErrors, "" + } + + bestWithMethod, bestOverall := selectMatches(candidates) + + if bestWithMethod != nil { + return bestWithMethod.pathItem, nil, bestWithMethod.path + } + + // path matches exist but none have the required method + validationErrors := []*errors.ValidationError{{ + ValidationType: helpers.PathValidation, + ValidationSubType: helpers.ValidationMissingOperation, + Message: fmt.Sprintf("%s Path '%s' not found", request.Method, request.URL.Path), + Reason: fmt.Sprintf("The %s method for that path does not exist in the specification", + request.Method), + SpecLine: -1, + SpecCol: -1, + HowToFix: errors.HowToFixPath, + }} + errors.PopulateValidationErrors(validationErrors, request, bestOverall.path) + return bestOverall.pathItem, validationErrors, bestOverall.path +} + +// normalizePathForMatching removes the fragment from a path template unless +// the request path itself contains a fragment. +func normalizePathForMatching(path, requestPath string) string { + if strings.Contains(requestPath, "#") { + return path + } + if idx := strings.IndexByte(path, '#'); idx >= 0 { + return path[:idx] + } + return path +} + +func getBasePaths(document *v3.Document) []string { + // extract base path from document to check against paths. + var basePaths []string + for _, s := range document.Servers { + u, err := url.Parse(s.URL) + // if the host contains special characters, we should attempt to split and parse only the relative path + if err != nil { + // split at first occurrence + _, serverPath, _ := strings.Cut(strings.Replace(s.URL, "//", "", 1), "/") + + if !strings.HasPrefix(serverPath, "/") { + serverPath = "/" + serverPath + } + + u, _ = url.Parse(serverPath) + } + + if u != nil && u.Path != "" { + basePaths = append(basePaths, u.Path) + } + } + + return basePaths +} + +// StripRequestPath strips the base path from the request path, based on the server paths provided in the specification +func StripRequestPath(request *http.Request, document *v3.Document) string { + basePaths := getBasePaths(document) + + // strip any base path + stripped := stripBaseFromPath(request.URL.EscapedPath(), basePaths) + if request.URL.Fragment != "" { + stripped = fmt.Sprintf("%s#%s", stripped, request.URL.Fragment) + } + if len(stripped) > 0 && !strings.HasPrefix(stripped, "/") { + stripped = "/" + stripped + } + return stripped +} + +func checkPathAgainstBase(docPath, urlPath string, basePaths []string) bool { + if docPath == urlPath { + return true + } + for _, basePath := range basePaths { + if basePath[len(basePath)-1] == '/' { + basePath = basePath[:len(basePath)-1] + } + merged := fmt.Sprintf("%s%s", basePath, urlPath) + if docPath == merged { + return true + } + } + return false +} + +func stripBaseFromPath(path string, basePaths []string) string { + for i := range basePaths { + if strings.HasPrefix(path, basePaths[i]) { + return path[len(basePaths[i]):] + } + } + return path +} + +func comparePaths(mapped, requested, basePaths []string, regexCache config.RegexCache) bool { + if len(mapped) != len(requested) { + return false // short circuit out + } + var imploded []string + for i, seg := range mapped { + s := seg + var rgx *regexp.Regexp + + if regexCache != nil { + if cachedRegex, found := regexCache.Load(s); found { + rgx = cachedRegex.(*regexp.Regexp) + } + } + + if rgx == nil { + r, err := helpers.GetRegexForPath(seg) + if err != nil { + return false + } + + rgx = r + + if regexCache != nil { + regexCache.Store(seg, r) + } + } + + if rgx.MatchString(requested[i]) { + s = requested[i] + } + imploded = append(imploded, s) + } + l := filepath.Join(imploded...) + r := filepath.Join(requested...) + return checkPathAgainstBase(l, r, basePaths) +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/paths/specificity.go b/vendor/github.com/pb33f/libopenapi-validator/paths/specificity.go new file mode 100644 index 000000000..ddf838802 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/paths/specificity.go @@ -0,0 +1,93 @@ +// Copyright 2023-2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package paths + +import ( + "net/http" + "strings" + + v3 "github.com/pb33f/libopenapi/datamodel/high/v3" +) + +// pathCandidate represents a potential path match with metadata for selection. +type pathCandidate struct { + pathItem *v3.PathItem + path string + score int + hasMethod bool +} + +// computeSpecificityScore calculates how specific a path template is. +// literal segments score higher than parameterized segments, ensuring +// "/pets/mine" is preferred over "/pets/{id}" per OpenAPI spec. +// +// scoring: +// - literal segment: 1000 points +// - parameter segment: 1 point +// +// this weighting ensures any path with more literal segments always wins, +// regardless of parameter positions. +func computeSpecificityScore(pathTemplate string) int { + segments := strings.Split(pathTemplate, "/") + score := 0 + + for _, seg := range segments { + if seg == "" { + continue + } + if isParameterSegment(seg) { + score += 1 + } else { + score += 1000 + } + } + return score +} + +// isParameterSegment returns true if the segment contains a path parameter. +// handles standard {param}, label {.param}, and exploded {param*} formats. +func isParameterSegment(seg string) bool { + return strings.Contains(seg, "{") && strings.Contains(seg, "}") +} + +// pathHasMethod checks if the PathItem has an operation for the given HTTP method. +func pathHasMethod(pathItem *v3.PathItem, method string) bool { + switch method { + case http.MethodGet: + return pathItem.Get != nil + case http.MethodPost: + return pathItem.Post != nil + case http.MethodPut: + return pathItem.Put != nil + case http.MethodDelete: + return pathItem.Delete != nil + case http.MethodOptions: + return pathItem.Options != nil + case http.MethodHead: + return pathItem.Head != nil + case http.MethodPatch: + return pathItem.Patch != nil + case http.MethodTrace: + return pathItem.Trace != nil + } + return false +} + +// selectMatches finds the best matching candidates in a single pass. +// returns the highest-scoring candidate with the method (or nil), and +// the highest-scoring candidate overall (for error reporting). +func selectMatches(candidates []pathCandidate) (withMethod, highest *pathCandidate) { + for i := range candidates { + c := &candidates[i] + + if c.hasMethod && (withMethod == nil || c.score > withMethod.score) { + withMethod = c + } + + if highest == nil || c.score > highest.score { + highest = c + } + } + return withMethod, highest +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/requests/package.go b/vendor/github.com/pb33f/libopenapi-validator/requests/package.go new file mode 100644 index 000000000..a45bf31a5 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/requests/package.go @@ -0,0 +1,6 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +// Package requests contains all the logic, models and interfaces for validating OpenAPI 3+ Requests. +// The package depends on *http.Request +package requests diff --git a/vendor/github.com/pb33f/libopenapi-validator/requests/request_body.go b/vendor/github.com/pb33f/libopenapi-validator/requests/request_body.go new file mode 100644 index 000000000..164689087 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/requests/request_body.go @@ -0,0 +1,41 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package requests + +import ( + "net/http" + + v3 "github.com/pb33f/libopenapi/datamodel/high/v3" + + "github.com/pb33f/libopenapi-validator/config" + "github.com/pb33f/libopenapi-validator/errors" +) + +// RequestBodyValidator is an interface that defines the methods for validating request bodies for Operations. +// +// ValidateRequestBodyWithPathItem method accepts an *http.Request and returns true if validation passed, +// false if validation failed and a slice of ValidationError pointers. +type RequestBodyValidator interface { + // ValidateRequestBody will validate the request body for an operation. The first return value will be true if the + // request body is valid, false if it is not. The second return value will be a slice of ValidationError pointers if + // the body is not valid. + ValidateRequestBody(request *http.Request) (bool, []*errors.ValidationError) + + // ValidateRequestBodyWithPathItem will validate the request body for an operation. The first return value will be true if the + // request body is valid, false if it is not. The second return value will be a slice of ValidationError pointers if + // the body is not valid. + ValidateRequestBodyWithPathItem(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) +} + +// NewRequestBodyValidator will create a new RequestBodyValidator from an OpenAPI 3+ document +func NewRequestBodyValidator(document *v3.Document, opts ...config.Option) RequestBodyValidator { + options := config.NewValidationOptions(opts...) + + return &requestBodyValidator{options: options, document: document} +} + +type requestBodyValidator struct { + options *config.ValidationOptions + document *v3.Document +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/requests/validate_body.go b/vendor/github.com/pb33f/libopenapi-validator/requests/validate_body.go new file mode 100644 index 000000000..e9aad7013 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/requests/validate_body.go @@ -0,0 +1,110 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package requests + +import ( + "fmt" + "net/http" + "strings" + + v3 "github.com/pb33f/libopenapi/datamodel/high/v3" + + "github.com/pb33f/libopenapi-validator/config" + "github.com/pb33f/libopenapi-validator/errors" + "github.com/pb33f/libopenapi-validator/helpers" + "github.com/pb33f/libopenapi-validator/paths" +) + +func (v *requestBodyValidator) ValidateRequestBody(request *http.Request) (bool, []*errors.ValidationError) { + pathItem, errs, foundPath := paths.FindPath(request, v.document, v.options.RegexCache) + if len(errs) > 0 { + return false, errs + } + return v.ValidateRequestBodyWithPathItem(request, pathItem, foundPath) +} + +func (v *requestBodyValidator) ValidateRequestBodyWithPathItem(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) { + if pathItem == nil { + return false, []*errors.ValidationError{{ + ValidationType: helpers.PathValidation, + ValidationSubType: helpers.ValidationMissing, + Message: fmt.Sprintf("%s Path '%s' not found", request.Method, request.URL.Path), + Reason: fmt.Sprintf("The %s request contains a path of '%s' "+ + "however that path, or the %s method for that path does not exist in the specification", + request.Method, request.URL.Path, request.Method), + SpecLine: -1, + SpecCol: -1, + HowToFix: errors.HowToFixPath, + }} + } + operation := helpers.ExtractOperation(request, pathItem) + if operation == nil { + return false, []*errors.ValidationError{errors.OperationNotFound(pathItem, request, request.Method, pathValue)} + } + if operation.RequestBody == nil { + return true, nil + } + + // extract the content type from the request + contentType := request.Header.Get(helpers.ContentTypeHeader) + required := false + if operation.RequestBody.Required != nil { + required = *operation.RequestBody.Required + } + if contentType == "" { + if !required { + // request body is not required, the validation stop there. + return true, nil + } + return false, []*errors.ValidationError{errors.RequestContentTypeNotFound(operation, request, pathValue)} + } + + // extract the media type from the content type header. + mediaType, ok := v.extractContentType(contentType, operation) + if !ok { + return false, []*errors.ValidationError{errors.RequestContentTypeNotFound(operation, request, pathValue)} + } + + // we currently only support JSON validation for request bodies + // this will capture *everything* that contains some form of 'json' in the content type + if !strings.Contains(strings.ToLower(contentType), helpers.JSONType) { + return true, nil + } + + // Nothing to validate + if mediaType.Schema == nil { + return true, nil + } + + // extract schema from media type + schema := mediaType.Schema.Schema() + + validationSucceeded, validationErrors := ValidateRequestSchema(&ValidateRequestSchemaInput{ + Request: request, + Schema: schema, + Version: helpers.VersionToFloat(v.document.Version), + Options: []config.Option{config.WithExistingOpts(v.options)}, + }) + + errors.PopulateValidationErrors(validationErrors, request, pathValue) + + return validationSucceeded, validationErrors +} + +func (v *requestBodyValidator) extractContentType(contentType string, operation *v3.Operation) (*v3.MediaType, bool) { + ct, _, _ := helpers.ExtractContentType(contentType) + mediaType, ok := operation.RequestBody.Content.Get(ct) + if ok { + return mediaType, true + } + ctMediaRange := strings.SplitN(ct, "/", 2) + for s, mediaTypeValue := range operation.RequestBody.Content.FromOldest() { + opMediaRange := strings.SplitN(s, "/", 2) + if (opMediaRange[0] == "*" || opMediaRange[0] == ctMediaRange[0]) && + (opMediaRange[1] == "*" || opMediaRange[1] == ctMediaRange[1]) { + return mediaTypeValue, true + } + } + return nil, false +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/requests/validate_request.go b/vendor/github.com/pb33f/libopenapi-validator/requests/validate_request.go new file mode 100644 index 000000000..f1ce93c05 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/requests/validate_request.go @@ -0,0 +1,361 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package requests + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "reflect" + "regexp" + "strconv" + + "github.com/pb33f/libopenapi/datamodel/high/base" + "github.com/pb33f/libopenapi/utils" + "github.com/santhosh-tekuri/jsonschema/v6" + "go.yaml.in/yaml/v4" + "golang.org/x/text/language" + "golang.org/x/text/message" + + "github.com/pb33f/libopenapi-validator/cache" + "github.com/pb33f/libopenapi-validator/config" + "github.com/pb33f/libopenapi-validator/errors" + "github.com/pb33f/libopenapi-validator/helpers" + "github.com/pb33f/libopenapi-validator/schema_validation" + "github.com/pb33f/libopenapi-validator/strict" +) + +var instanceLocationRegex = regexp.MustCompile(`^/(\d+)`) + +// ValidateRequestSchemaInput contains parameters for request schema validation. +type ValidateRequestSchemaInput struct { + Request *http.Request // Required: The HTTP request to validate + Schema *base.Schema // Required: The OpenAPI schema to validate against + Version float32 // Required: OpenAPI version (3.0 or 3.1) + Options []config.Option // Optional: Functional options (defaults applied if empty/nil) +} + +// ValidateRequestSchema will validate a http.Request pointer against a schema. +// If validation fails, it will return a list of validation errors as the second return value. +// The schema will be stored and reused from cache if available, otherwise it will be compiled on each call. +func ValidateRequestSchema(input *ValidateRequestSchemaInput) (bool, []*errors.ValidationError) { + validationOptions := config.NewValidationOptions(input.Options...) + var validationErrors []*errors.ValidationError + var renderedSchema, jsonSchema []byte + var referenceSchema string + var compiledSchema *jsonschema.Schema + var cachedNode *yaml.Node + + if input.Schema == nil { + return false, []*errors.ValidationError{{ + ValidationType: helpers.RequestBodyValidation, + ValidationSubType: helpers.Schema, + Message: "schema is nil", + Reason: "The schema to validate against is nil", + }} + } else if input.Schema.GoLow() == nil { + return false, []*errors.ValidationError{{ + ValidationType: helpers.RequestBodyValidation, + ValidationSubType: helpers.Schema, + Message: "schema cannot be rendered", + Reason: "The schema does not have low-level information and cannot be rendered. Please ensure the schema is loaded from a document.", + }} + } + + if validationOptions.SchemaCache != nil { + hash := input.Schema.GoLow().Hash() + if cached, ok := validationOptions.SchemaCache.Load(hash); ok && cached != nil && cached.CompiledSchema != nil { + renderedSchema = cached.RenderedInline + referenceSchema = cached.ReferenceSchema + jsonSchema = cached.RenderedJSON + compiledSchema = cached.CompiledSchema + cachedNode = cached.RenderedNode + } + } + + // Cache miss or no cache - render and compile + if compiledSchema == nil { + renderCtx := base.NewInlineRenderContext() + var renderErr error + renderedSchema, renderErr = input.Schema.RenderInlineWithContext(renderCtx) + referenceSchema = string(renderedSchema) + + // If rendering failed (e.g., circular reference), return the render error + if renderErr != nil { + violation := &errors.SchemaValidationFailure{ + Reason: renderErr.Error(), + Location: "schema rendering", + ReferenceSchema: referenceSchema, + } + validationErrors = append(validationErrors, &errors.ValidationError{ + ValidationType: helpers.RequestBodyValidation, + ValidationSubType: helpers.Schema, + Message: fmt.Sprintf("%s request body for '%s' failed schema rendering", + input.Request.Method, input.Request.URL.Path), + Reason: fmt.Sprintf("The request schema failed to render: %s", + renderErr.Error()), + SpecLine: 1, + SpecCol: 0, + SchemaValidationErrors: []*errors.SchemaValidationFailure{violation}, + HowToFix: "check the request schema for circular references or invalid structures", + Context: referenceSchema, + }) + return false, validationErrors + } + + jsonSchema, _ = utils.ConvertYAMLtoJSON(renderedSchema) + + var err error + schemaName := fmt.Sprintf("%x", input.Schema.GoLow().Hash()) + compiledSchema, err = helpers.NewCompiledSchemaWithVersion( + schemaName, + jsonSchema, + validationOptions, + input.Version, + ) + if err != nil { + violation := &errors.SchemaValidationFailure{ + Reason: fmt.Sprintf("failed to compile JSON schema: %s", err.Error()), + Location: "schema compilation", + ReferenceSchema: referenceSchema, + } + validationErrors = append(validationErrors, &errors.ValidationError{ + ValidationType: helpers.RequestBodyValidation, + ValidationSubType: helpers.Schema, + Message: fmt.Sprintf("%s request body for '%s' failed schema compilation", + input.Request.Method, input.Request.URL.Path), + Reason: fmt.Sprintf("The request schema failed to compile: %s", err.Error()), + SpecLine: 1, + SpecCol: 0, + SchemaValidationErrors: []*errors.SchemaValidationFailure{violation}, + HowToFix: "check the request schema for invalid JSON Schema syntax, complex regex patterns, or unsupported schema constructs", + Context: referenceSchema, + }) + return false, validationErrors + } + + if validationOptions.SchemaCache != nil { + hash := input.Schema.GoLow().Hash() + validationOptions.SchemaCache.Store(hash, &cache.SchemaCacheEntry{ + Schema: input.Schema, + RenderedInline: renderedSchema, + ReferenceSchema: referenceSchema, + RenderedJSON: jsonSchema, + CompiledSchema: compiledSchema, + }) + } + } + + request := input.Request + schema := input.Schema + + var requestBody []byte + if request != nil && request.Body != nil { + requestBody, _ = io.ReadAll(request.Body) + + // close the request body, so it can be re-read later by another player in the chain + _ = request.Body.Close() + request.Body = io.NopCloser(bytes.NewBuffer(requestBody)) + + } + + var decodedObj interface{} + + if len(requestBody) > 0 { + err := json.Unmarshal(requestBody, &decodedObj) + if err != nil { + // cannot decode the request body, so it's not valid + violation := &errors.SchemaValidationFailure{ + Reason: err.Error(), + Location: "unavailable", + ReferenceSchema: referenceSchema, + ReferenceObject: string(requestBody), + } + validationErrors = append(validationErrors, &errors.ValidationError{ + ValidationType: helpers.RequestBodyValidation, + ValidationSubType: helpers.Schema, + Message: fmt.Sprintf("%s request body for '%s' failed to validate schema", + request.Method, request.URL.Path), + Reason: fmt.Sprintf("The request body cannot be decoded: %s", err.Error()), + SpecLine: 1, + SpecCol: 0, + SchemaValidationErrors: []*errors.SchemaValidationFailure{violation}, + HowToFix: errors.HowToFixInvalidSchema, + Context: referenceSchema, // attach the rendered schema to the error + }) + return false, validationErrors + } + } + + // no request body? but we do have a schema? + if len(requestBody) == 0 && len(jsonSchema) > 0 { + + line := schema.ParentProxy.GetSchemaKeyNode().Line + col := schema.ParentProxy.GetSchemaKeyNode().Line + if schema.Type != nil { + line = schema.GoLow().Type.KeyNode.Line + col = schema.GoLow().Type.KeyNode.Column + } + + // cannot decode the request body, so it's not valid + violation := &errors.SchemaValidationFailure{ + Reason: "request body is empty, but there is a schema defined", + ReferenceSchema: referenceSchema, + ReferenceObject: string(requestBody), + } + validationErrors = append(validationErrors, &errors.ValidationError{ + ValidationType: helpers.RequestBodyValidation, + ValidationSubType: helpers.Schema, + Message: fmt.Sprintf("%s request body is empty for '%s'", + request.Method, request.URL.Path), + Reason: "The request body is empty but there is a schema defined", + SpecLine: line, + SpecCol: col, + SchemaValidationErrors: []*errors.SchemaValidationFailure{violation}, + HowToFix: errors.HowToFixInvalidSchema, + Context: referenceSchema, // attach the rendered schema to the error + }) + return false, validationErrors + } + + // validate the object against the schema + scErrs := compiledSchema.Validate(decodedObj) + if scErrs != nil { + + jk := scErrs.(*jsonschema.ValidationError) + + // flatten the validationErrors + schFlatErrs := jk.BasicOutput().Errors + var schemaValidationErrors []*errors.SchemaValidationFailure + + // Use cached node if available, otherwise parse + renderedNode := cachedNode + if renderedNode == nil { + renderedNode = new(yaml.Node) + _ = yaml.Unmarshal(renderedSchema, renderedNode) + } + for q := range schFlatErrs { + er := schFlatErrs[q] + + errMsg := er.Error.Kind.LocalizedString(message.NewPrinter(language.Tag{})) + + if er.KeywordLocation == "" || helpers.IgnoreRegex.MatchString(errMsg) { + continue // ignore this error, it's useless tbh, utter noise. + } + if er.Error != nil { + + // locate the violated property in the schema + located := schema_validation.LocateSchemaPropertyNodeByJSONPath(renderedNode.Content[0], er.KeywordLocation) + + // extract the element specified by the instance + val := instanceLocationRegex.FindStringSubmatch(er.InstanceLocation) + var referenceObject string + + if len(val) > 0 { + referenceIndex, _ := strconv.Atoi(val[1]) + if reflect.ValueOf(decodedObj).Type().Kind() == reflect.Slice { + found := decodedObj.([]any)[referenceIndex] + recoded, _ := json.MarshalIndent(found, "", " ") + referenceObject = string(recoded) + } + } + if referenceObject == "" { + referenceObject = string(requestBody) + } + + errMsg := er.Error.Kind.LocalizedString(message.NewPrinter(language.Tag{})) + + violation := &errors.SchemaValidationFailure{ + Reason: errMsg, + Location: er.KeywordLocation, + FieldName: helpers.ExtractFieldNameFromStringLocation(er.InstanceLocation), + FieldPath: helpers.ExtractJSONPathFromStringLocation(er.InstanceLocation), + InstancePath: helpers.ConvertStringLocationToPathSegments(er.InstanceLocation), + ReferenceSchema: referenceSchema, + ReferenceObject: referenceObject, + OriginalError: jk, + } + // if we have a location within the schema, add it to the error + if located != nil { + + line := located.Line + // if the located node is a map or an array, then the actual human interpretable + // line on which the violation occurred is the line of the key, not the value. + if located.Kind == yaml.MappingNode || located.Kind == yaml.SequenceNode { + if line > 0 { + line-- + } + } + + // location of the violation within the rendered schema. + violation.Line = line + violation.Column = located.Column + } + schemaValidationErrors = append(schemaValidationErrors, violation) + } + } + + line := 1 + col := 0 + if schema.GoLow().Type.KeyNode != nil { + line = schema.GoLow().Type.KeyNode.Line + col = schema.GoLow().Type.KeyNode.Column + } + + // add the error to the list + validationErrors = append(validationErrors, &errors.ValidationError{ + ValidationType: helpers.RequestBodyValidation, + ValidationSubType: helpers.Schema, + Message: fmt.Sprintf("%s request body for '%s' failed to validate schema", + request.Method, request.URL.Path), + Reason: "The request body is defined as an object. " + + "However, it does not meet the schema requirements of the specification", + SpecLine: line, + SpecCol: col, + SchemaValidationErrors: schemaValidationErrors, + HowToFix: errors.HowToFixInvalidSchema, + Context: referenceSchema, // attach the rendered schema to the error + }) + } + if len(validationErrors) > 0 { + return false, validationErrors + } + + // strict mode: check for undeclared properties in request body + if validationOptions.StrictMode && decodedObj != nil { + strictValidator := strict.NewValidator(validationOptions, input.Version) + strictResult := strictValidator.Validate(strict.Input{ + Schema: schema, + Data: decodedObj, + Direction: strict.DirectionRequest, + Options: validationOptions, + BasePath: "$.body", + Version: input.Version, + }) + + if !strictResult.Valid { + for _, undeclared := range strictResult.UndeclaredValues { + validationErrors = append(validationErrors, + errors.UndeclaredPropertyError( + undeclared.Path, + undeclared.Name, + undeclared.Value, + undeclared.DeclaredProperties, + undeclared.Direction.String(), + request.URL.Path, + request.Method, + undeclared.SpecLine, + undeclared.SpecCol, + )) + } + } + } + + if len(validationErrors) > 0 { + return false, validationErrors + } + return true, nil +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/responses/package.go b/vendor/github.com/pb33f/libopenapi-validator/responses/package.go new file mode 100644 index 000000000..2d6c9a593 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/responses/package.go @@ -0,0 +1,6 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +// Package responses contains all the logic, models and interfaces for validating OpenAPI 3+ Responses +// The package depends on *http.Response +package responses diff --git a/vendor/github.com/pb33f/libopenapi-validator/responses/response_body.go b/vendor/github.com/pb33f/libopenapi-validator/responses/response_body.go new file mode 100644 index 000000000..62bac3f64 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/responses/response_body.go @@ -0,0 +1,41 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package responses + +import ( + "net/http" + + v3 "github.com/pb33f/libopenapi/datamodel/high/v3" + + "github.com/pb33f/libopenapi-validator/config" + "github.com/pb33f/libopenapi-validator/errors" +) + +// ResponseBodyValidator is an interface that defines the methods for validating response bodies for Operations. +// +// ValidateResponseBody method accepts an *http.Request and returns true if validation passed, +// false if validation failed and a slice of ValidationError pointers. +type ResponseBodyValidator interface { + // ValidateResponseBody will validate the response body for a http.Response pointer. The request is used to + // locate the operation in the specification, the response is used to ensure the response code, media type and the + // schema of the response body are valid. + ValidateResponseBody(request *http.Request, response *http.Response) (bool, []*errors.ValidationError) + + // ValidateResponseBodyWithPathItem will validate the response body for a http.Response pointer. The request is used to + // locate the operation in the specification, the response is used to ensure the response code, media type and the + // schema of the response body are valid. + ValidateResponseBodyWithPathItem(request *http.Request, response *http.Response, pathItem *v3.PathItem, pathFound string) (bool, []*errors.ValidationError) +} + +// NewResponseBodyValidator will create a new ResponseBodyValidator from an OpenAPI 3+ document +func NewResponseBodyValidator(document *v3.Document, opts ...config.Option) ResponseBodyValidator { + options := config.NewValidationOptions(opts...) + + return &responseBodyValidator{options: options, document: document} +} + +type responseBodyValidator struct { + options *config.ValidationOptions + document *v3.Document +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/responses/validate_body.go b/vendor/github.com/pb33f/libopenapi-validator/responses/validate_body.go new file mode 100644 index 000000000..ae09b3073 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/responses/validate_body.go @@ -0,0 +1,156 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package responses + +import ( + "fmt" + "net/http" + "strconv" + "strings" + + "github.com/pb33f/libopenapi/orderedmap" + + v3 "github.com/pb33f/libopenapi/datamodel/high/v3" + + "github.com/pb33f/libopenapi-validator/config" + "github.com/pb33f/libopenapi-validator/errors" + "github.com/pb33f/libopenapi-validator/helpers" + "github.com/pb33f/libopenapi-validator/paths" +) + +func (v *responseBodyValidator) ValidateResponseBody( + request *http.Request, + response *http.Response, +) (bool, []*errors.ValidationError) { + pathItem, errs, foundPath := paths.FindPath(request, v.document, v.options.RegexCache) + if len(errs) > 0 { + return false, errs + } + return v.ValidateResponseBodyWithPathItem(request, response, pathItem, foundPath) +} + +func (v *responseBodyValidator) ValidateResponseBodyWithPathItem(request *http.Request, response *http.Response, pathItem *v3.PathItem, pathFound string) (bool, []*errors.ValidationError) { + if pathItem == nil { + return false, []*errors.ValidationError{{ + ValidationType: helpers.PathValidation, + ValidationSubType: helpers.ValidationMissing, + Message: fmt.Sprintf("%s Path '%s' not found", request.Method, request.URL.Path), + Reason: fmt.Sprintf("The %s request contains a path of '%s' "+ + "however that path, or the %s method for that path does not exist in the specification", + request.Method, request.URL.Path, request.Method), + SpecLine: -1, + SpecCol: -1, + HowToFix: errors.HowToFixPath, + }} + } + var validationErrors []*errors.ValidationError + operation := helpers.ExtractOperation(request, pathItem) + if operation == nil { + return false, []*errors.ValidationError{errors.OperationNotFound(pathItem, request, request.Method, pathFound)} + } + // extract the response code from the response + httpCode := response.StatusCode + contentType := response.Header.Get(helpers.ContentTypeHeader) + codeStr := strconv.Itoa(httpCode) + + // extract the media type from the content type header. + mediaTypeSting, _, _ := helpers.ExtractContentType(contentType) + + // check if the response code is in the contract + foundResponse := operation.Responses.Codes.GetOrZero(codeStr) + if foundResponse == nil { + // check range definition for response codes + foundResponse = operation.Responses.Codes.GetOrZero(fmt.Sprintf("%dXX", httpCode/100)) + if foundResponse != nil { + codeStr = fmt.Sprintf("%dXX", httpCode/100) + } + } + + if foundResponse != nil { + if foundResponse.Content != nil { // only validate if we have content types. + // check content type has been defined in the contract + if mediaType, ok := foundResponse.Content.Get(mediaTypeSting); ok { + validationErrors = append(validationErrors, + v.checkResponseSchema(request, response, mediaTypeSting, mediaType)...) + } else { + // check that the operation *actually* returns a body. (i.e. a 204 response) + if foundResponse.Content != nil && orderedmap.Len(foundResponse.Content) > 0 { + // content type not found in the contract + validationErrors = append(validationErrors, + errors.ResponseContentTypeNotFound(operation, request, response, codeStr, false)) + } + } + } + } else { + // no code match, check for default response + if operation.Responses.Default != nil && operation.Responses.Default.Content != nil { + // check content type has been defined in the contract + if mediaType, ok := operation.Responses.Default.Content.Get(mediaTypeSting); ok { + foundResponse = operation.Responses.Default + validationErrors = append(validationErrors, + v.checkResponseSchema(request, response, contentType, mediaType)...) + } else { + // check that the operation *actually* returns a body. (i.e. a 204 response) + if operation.Responses.Default.Content != nil && orderedmap.Len(operation.Responses.Default.Content) > 0 { + // content type not found in the contract + validationErrors = append(validationErrors, + errors.ResponseContentTypeNotFound(operation, request, response, codeStr, true)) + } + } + } else { + // TODO: add support for '2XX' and '3XX' responses in the contract + // no default, no code match, nothing! + validationErrors = append(validationErrors, + errors.ResponseCodeNotFound(operation, request, httpCode)) + } + } + + if foundResponse != nil { + // check for headers in the response + if foundResponse.Headers != nil { + if ok, hErrs := ValidateResponseHeaders(request, response, foundResponse.Headers, config.WithExistingOpts(v.options)); !ok { + validationErrors = append(validationErrors, hErrs...) + } + } + } + + errors.PopulateValidationErrors(validationErrors, request, pathFound) + + if len(validationErrors) > 0 { + return false, validationErrors + } + return true, nil +} + +func (v *responseBodyValidator) checkResponseSchema( + request *http.Request, + response *http.Response, + contentType string, + mediaType *v3.MediaType, +) []*errors.ValidationError { + var validationErrors []*errors.ValidationError + + // currently, we can only validate JSON based responses, so check for the presence + // of 'json' in the content type (what ever it may be) so we can perform a schema check on it. + // anything other than JSON, will be ignored. + if strings.Contains(strings.ToLower(contentType), helpers.JSONType) { + // extract schema from media type + if mediaType.Schema != nil { + schema := mediaType.Schema.Schema() + + // Validate response schema + valid, vErrs := ValidateResponseSchema(&ValidateResponseSchemaInput{ + Request: request, + Response: response, + Schema: schema, + Version: helpers.VersionToFloat(v.document.Version), + Options: []config.Option{config.WithExistingOpts(v.options)}, + }) + if !valid { + validationErrors = append(validationErrors, vErrs...) + } + } + } + return validationErrors +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/responses/validate_headers.go b/vendor/github.com/pb33f/libopenapi-validator/responses/validate_headers.go new file mode 100644 index 000000000..0381b6d56 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/responses/validate_headers.go @@ -0,0 +1,117 @@ +// Copyright 2023-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// https://pb33f.io + +package responses + +import ( + "fmt" + "net/http" + "strings" + + "github.com/pb33f/libopenapi/orderedmap" + + v3 "github.com/pb33f/libopenapi/datamodel/high/v3" + lowv3 "github.com/pb33f/libopenapi/datamodel/low/v3" + + "github.com/pb33f/libopenapi-validator/config" + "github.com/pb33f/libopenapi-validator/errors" + "github.com/pb33f/libopenapi-validator/helpers" + "github.com/pb33f/libopenapi-validator/parameters" + "github.com/pb33f/libopenapi-validator/strict" +) + +// ValidateResponseHeaders validates the response headers against the OpenAPI spec. +func ValidateResponseHeaders( + request *http.Request, + response *http.Response, + headers *orderedmap.Map[string, *v3.Header], + opts ...config.Option, +) (bool, []*errors.ValidationError) { + options := config.NewValidationOptions(opts...) + + // locate headers + type headerPair struct { + name string + value []string + model *v3.Header + } + locatedHeaders := make(map[string]headerPair) + var validationErrors []*errors.ValidationError + // iterate through the response headers + for name, v := range response.Header { + // check if the model is in the spec + for k, header := range headers.FromOldest() { + if strings.EqualFold(k, name) { + locatedHeaders[strings.ToLower(name)] = headerPair{ + name: k, + value: v, + model: header, + } + } + } + } + + // determine if any required headers are missing from the response + for name, header := range headers.FromOldest() { + if header.Required { + if _, ok := locatedHeaders[strings.ToLower(name)]; !ok { + validationErrors = append(validationErrors, &errors.ValidationError{ + ValidationType: helpers.ResponseBodyValidation, + ValidationSubType: helpers.ParameterValidationHeader, + Message: "Missing required header", + Reason: fmt.Sprintf("Required header '%s' was not found in response", name), + SpecLine: header.GoLow().KeyNode.Line, + SpecCol: header.GoLow().KeyNode.Column, + HowToFix: errors.HowToFixMissingHeader, + RequestPath: request.URL.Path, + RequestMethod: request.Method, + }) + } + } + } + + // validate the model schemas if they are set. + for h, header := range locatedHeaders { + if header.model.Schema != nil { + schema := header.model.Schema.Schema() + if schema != nil && header.model.Required { + for _, headerValue := range header.value { + validationErrors = append(validationErrors, + parameters.ValidateParameterSchema(schema, nil, headerValue, "header", + "response header", h, helpers.ResponseBodyValidation, lowv3.HeadersLabel, options)...) + } + } + } + } + + if len(validationErrors) > 0 { + return false, validationErrors + } + + // strict mode: check for undeclared response headers + if options.StrictMode { + // convert orderedmap to regular map for strict validation + declaredMap := make(map[string]*v3.Header) + for name, header := range headers.FromOldest() { + declaredMap[name] = header + } + + undeclaredHeaders := strict.ValidateResponseHeaders(response.Header, &declaredMap, options) + for _, undeclared := range undeclaredHeaders { + validationErrors = append(validationErrors, + errors.UndeclaredHeaderError( + undeclared.Name, + undeclared.Value.(string), + undeclared.DeclaredProperties, + undeclared.Direction.String(), + request.URL.Path, + request.Method, + )) + } + } + + if len(validationErrors) > 0 { + return false, validationErrors + } + return true, nil +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/responses/validate_response.go b/vendor/github.com/pb33f/libopenapi-validator/responses/validate_response.go new file mode 100644 index 000000000..f63a6a351 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/responses/validate_response.go @@ -0,0 +1,375 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package responses + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "reflect" + "regexp" + "strconv" + + "github.com/pb33f/libopenapi/datamodel/high/base" + "github.com/pb33f/libopenapi/utils" + "github.com/santhosh-tekuri/jsonschema/v6" + "go.yaml.in/yaml/v4" + "golang.org/x/text/language" + "golang.org/x/text/message" + + "github.com/pb33f/libopenapi-validator/cache" + "github.com/pb33f/libopenapi-validator/config" + "github.com/pb33f/libopenapi-validator/errors" + "github.com/pb33f/libopenapi-validator/helpers" + "github.com/pb33f/libopenapi-validator/schema_validation" + "github.com/pb33f/libopenapi-validator/strict" +) + +var instanceLocationRegex = regexp.MustCompile(`^/(\d+)`) + +// ValidateResponseSchemaInput contains parameters for response schema validation. +type ValidateResponseSchemaInput struct { + Request *http.Request // Required: The HTTP request (for context) + Response *http.Response // Required: The HTTP response to validate + Schema *base.Schema // Required: The OpenAPI schema to validate against + Version float32 // Required: OpenAPI version (3.0 or 3.1) + Options []config.Option // Optional: Functional options (defaults applied if empty/nil) +} + +// ValidateResponseSchema will validate the response body for a http.Response pointer. The request is used to +// locate the operation in the specification, the response is used to ensure the response code, media type and the +// schema of the response body are valid. +// +// This function is used by the ValidateResponseBody function, but can be used independently. +// The schema will be compiled from cache if available, otherwise it will be compiled and cached. +func ValidateResponseSchema(input *ValidateResponseSchemaInput) (bool, []*errors.ValidationError) { + validationOptions := config.NewValidationOptions(input.Options...) + var validationErrors []*errors.ValidationError + var renderedSchema, jsonSchema []byte + var referenceSchema string + var compiledSchema *jsonschema.Schema + var cachedNode *yaml.Node + + if input.Schema == nil { + return false, []*errors.ValidationError{{ + ValidationType: helpers.ResponseBodyValidation, + ValidationSubType: helpers.Schema, + Message: "schema is nil", + Reason: "The schema to validate against is nil", + }} + } else if input.Schema.GoLow() == nil { + return false, []*errors.ValidationError{{ + ValidationType: helpers.ResponseBodyValidation, + ValidationSubType: helpers.Schema, + Message: "schema cannot be rendered", + Reason: "The schema does not have low-level information and cannot be rendered. Please ensure the schema is loaded from a document.", + }} + } + + if validationOptions.SchemaCache != nil { + hash := input.Schema.GoLow().Hash() + if cached, ok := validationOptions.SchemaCache.Load(hash); ok && cached != nil && cached.CompiledSchema != nil { + renderedSchema = cached.RenderedInline + referenceSchema = cached.ReferenceSchema + compiledSchema = cached.CompiledSchema + cachedNode = cached.RenderedNode + } + } + + // Cache miss or no cache - render and compile + if compiledSchema == nil { + renderCtx := base.NewInlineRenderContext() + var renderErr error + renderedSchema, renderErr = input.Schema.RenderInlineWithContext(renderCtx) + referenceSchema = string(renderedSchema) + + // If rendering failed (e.g., circular reference), return the render error + if renderErr != nil { + violation := &errors.SchemaValidationFailure{ + Reason: renderErr.Error(), + Location: "schema rendering", + ReferenceSchema: referenceSchema, + } + validationErrors = append(validationErrors, &errors.ValidationError{ + ValidationType: helpers.ResponseBodyValidation, + ValidationSubType: helpers.Schema, + Message: fmt.Sprintf("%d response body for '%s' failed schema rendering", + input.Response.StatusCode, input.Request.URL.Path), + Reason: fmt.Sprintf("The response schema for status code '%d' failed to render: %s", + input.Response.StatusCode, renderErr.Error()), + SpecLine: 1, + SpecCol: 0, + SchemaValidationErrors: []*errors.SchemaValidationFailure{violation}, + HowToFix: "check the response schema for circular references or invalid structures", + Context: referenceSchema, + }) + return false, validationErrors + } + + jsonSchema, _ = utils.ConvertYAMLtoJSON(renderedSchema) + + var err error + schemaName := fmt.Sprintf("%x", input.Schema.GoLow().Hash()) + compiledSchema, err = helpers.NewCompiledSchemaWithVersion( + schemaName, + jsonSchema, + validationOptions, + input.Version, + ) + if err != nil { + violation := &errors.SchemaValidationFailure{ + Reason: fmt.Sprintf("failed to compile JSON schema: %s", err.Error()), + Location: "schema compilation", + ReferenceSchema: referenceSchema, + } + validationErrors = append(validationErrors, &errors.ValidationError{ + ValidationType: helpers.ResponseBodyValidation, + ValidationSubType: helpers.Schema, + Message: fmt.Sprintf("%d response body for '%s' failed schema compilation", + input.Response.StatusCode, input.Request.URL.Path), + Reason: fmt.Sprintf("The response schema for status code '%d' failed to compile: %s", + input.Response.StatusCode, err.Error()), + SpecLine: 1, + SpecCol: 0, + SchemaValidationErrors: []*errors.SchemaValidationFailure{violation}, + HowToFix: "check the response schema for invalid JSON Schema syntax, complex regex patterns, or unsupported schema constructs", + Context: referenceSchema, + }) + return false, validationErrors + } + + if validationOptions.SchemaCache != nil { + hash := input.Schema.GoLow().Hash() + validationOptions.SchemaCache.Store(hash, &cache.SchemaCacheEntry{ + Schema: input.Schema, + RenderedInline: renderedSchema, + ReferenceSchema: referenceSchema, + RenderedJSON: jsonSchema, + CompiledSchema: compiledSchema, + }) + } + } + + request := input.Request + response := input.Response + schema := input.Schema + + if response == nil || response.Body == http.NoBody { + // cannot decode the response body, so it's not valid + violation := &errors.SchemaValidationFailure{ + Reason: "response is empty", + Location: "unavailable", + ReferenceSchema: referenceSchema, + } + validationErrors = append(validationErrors, &errors.ValidationError{ + ValidationType: "response", + ValidationSubType: "object", + Message: fmt.Sprintf("%s response object is missing for '%s'", + request.Method, request.URL.Path), + Reason: "The response object is completely missing", + SpecLine: 1, + SpecCol: 0, + SchemaValidationErrors: []*errors.SchemaValidationFailure{violation}, + HowToFix: "ensure response object has been set", + Context: referenceSchema, // attach the rendered schema to the error + }) + return false, validationErrors + } + + responseBody, ioErr := io.ReadAll(response.Body) + if ioErr != nil { + // cannot decode the response body, so it's not valid + violation := &errors.SchemaValidationFailure{ + Reason: ioErr.Error(), + Location: "unavailable", + ReferenceSchema: referenceSchema, + ReferenceObject: string(responseBody), + } + validationErrors = append(validationErrors, &errors.ValidationError{ + ValidationType: helpers.ResponseBodyValidation, + ValidationSubType: helpers.Schema, + Message: fmt.Sprintf("%s response body for '%s' cannot be read, it's empty or malformed", + request.Method, request.URL.Path), + Reason: fmt.Sprintf("The response body cannot be decoded: %s", ioErr.Error()), + SpecLine: 1, + SpecCol: 0, + SchemaValidationErrors: []*errors.SchemaValidationFailure{violation}, + HowToFix: "ensure body is not empty", + Context: referenceSchema, // attach the rendered schema to the error + }) + return false, validationErrors + } + + // close the request body, so it can be re-read later by another player in the chain + _ = response.Body.Close() + response.Body = io.NopCloser(bytes.NewBuffer(responseBody)) + + var decodedObj interface{} + + if len(responseBody) > 0 { + err := json.Unmarshal(responseBody, &decodedObj) + if err != nil { + // cannot decode the response body, so it's not valid + violation := &errors.SchemaValidationFailure{ + Reason: err.Error(), + Location: "unavailable", + ReferenceSchema: referenceSchema, + ReferenceObject: string(responseBody), + } + validationErrors = append(validationErrors, &errors.ValidationError{ + ValidationType: helpers.ResponseBodyValidation, + ValidationSubType: helpers.Schema, + Message: fmt.Sprintf("%s response body for '%s' failed to validate schema", + request.Method, request.URL.Path), + Reason: fmt.Sprintf("The response body cannot be decoded: %s", err.Error()), + SpecLine: 1, + SpecCol: 0, + SchemaValidationErrors: []*errors.SchemaValidationFailure{violation}, + HowToFix: errors.HowToFixInvalidSchema, + Context: referenceSchema, // attach the rendered schema to the error + }) + return false, validationErrors + } + } + + // no response body? failed to decode anything? nothing to do here. + if responseBody == nil || decodedObj == nil { + return true, nil + } + + // validate the object against the schema + scErrs := compiledSchema.Validate(decodedObj) + if scErrs != nil { + jk := scErrs.(*jsonschema.ValidationError) + + // flatten the validationErrors + schFlatErrs := jk.BasicOutput().Errors + var schemaValidationErrors []*errors.SchemaValidationFailure + + renderedNode := cachedNode + if renderedNode == nil { + renderedNode = new(yaml.Node) + _ = yaml.Unmarshal(renderedSchema, renderedNode) + } + + for q := range schFlatErrs { + er := schFlatErrs[q] + + errMsg := er.Error.Kind.LocalizedString(message.NewPrinter(language.Tag{})) + if er.KeywordLocation == "" || helpers.IgnoreRegex.MatchString(errMsg) { + continue // ignore this error, it's useless tbh, utter noise. + } + if er.Error != nil { + // locate the violated property in the schema + located := schema_validation.LocateSchemaPropertyNodeByJSONPath(renderedNode.Content[0], er.KeywordLocation) + + // extract the element specified by the instance + val := instanceLocationRegex.FindStringSubmatch(er.InstanceLocation) + var referenceObject string + + if len(val) > 0 { + referenceIndex, _ := strconv.Atoi(val[1]) + if reflect.ValueOf(decodedObj).Type().Kind() == reflect.Slice { + found := decodedObj.([]any)[referenceIndex] + recoded, _ := json.MarshalIndent(found, "", " ") + referenceObject = string(recoded) + } + } + if referenceObject == "" { + referenceObject = string(responseBody) + } + + violation := &errors.SchemaValidationFailure{ + Reason: errMsg, + Location: er.KeywordLocation, + FieldName: helpers.ExtractFieldNameFromStringLocation(er.InstanceLocation), + FieldPath: helpers.ExtractJSONPathFromStringLocation(er.InstanceLocation), + InstancePath: helpers.ConvertStringLocationToPathSegments(er.InstanceLocation), + ReferenceSchema: referenceSchema, + ReferenceObject: referenceObject, + OriginalError: jk, + } + // if we have a location within the schema, add it to the error + if located != nil { + + line := located.Line + // if the located node is a map or an array, then the actual human interpretable + // line on which the violation occurred is the line of the key, not the value. + if located.Kind == yaml.MappingNode || located.Kind == yaml.SequenceNode { + if line > 0 { + line-- + } + } + + // location of the violation within the rendered schema. + violation.Line = line + violation.Column = located.Column + } + schemaValidationErrors = append(schemaValidationErrors, violation) + } + } + + line := 1 + col := 0 + if schema.GoLow().Type.KeyNode != nil { + line = schema.GoLow().Type.KeyNode.Line + col = schema.GoLow().Type.KeyNode.Column + } + + // add the error to the list + validationErrors = append(validationErrors, &errors.ValidationError{ + ValidationType: helpers.ResponseBodyValidation, + ValidationSubType: helpers.Schema, + Message: fmt.Sprintf("%d response body for '%s' failed to validate schema", + response.StatusCode, request.URL.Path), + Reason: fmt.Sprintf("The response body for status code '%d' is defined as an object. "+ + "However, it does not meet the schema requirements of the specification", response.StatusCode), + SpecLine: line, + SpecCol: col, + SchemaValidationErrors: schemaValidationErrors, + HowToFix: errors.HowToFixInvalidSchema, + Context: referenceSchema, // attach the rendered schema to the error + }) + } + if len(validationErrors) > 0 { + return false, validationErrors + } + + // strict mode: check for undeclared properties in response body + if validationOptions.StrictMode && decodedObj != nil { + strictValidator := strict.NewValidator(validationOptions, input.Version) + strictResult := strictValidator.Validate(strict.Input{ + Schema: schema, + Data: decodedObj, + Direction: strict.DirectionResponse, + Options: validationOptions, + BasePath: "$.body", + Version: input.Version, + }) + + if !strictResult.Valid { + for _, undeclared := range strictResult.UndeclaredValues { + validationErrors = append(validationErrors, + errors.UndeclaredPropertyError( + undeclared.Path, + undeclared.Name, + undeclared.Value, + undeclared.DeclaredProperties, + undeclared.Direction.String(), + request.URL.Path, + request.Method, + undeclared.SpecLine, + undeclared.SpecCol, + )) + } + } + } + + if len(validationErrors) > 0 { + return false, validationErrors + } + return true, nil +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/schema_validation/locate_schema_property.go b/vendor/github.com/pb33f/libopenapi-validator/schema_validation/locate_schema_property.go new file mode 100644 index 000000000..4ade2f465 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/schema_validation/locate_schema_property.go @@ -0,0 +1,42 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package schema_validation + +import ( + "github.com/pb33f/jsonpath/pkg/jsonpath" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// LocateSchemaPropertyNodeByJSONPath will locate a schema property node by a JSONPath. It converts something like +// #/components/schemas/MySchema/properties/MyProperty to something like $.components.schemas.MySchema.properties.MyProperty +func LocateSchemaPropertyNodeByJSONPath(doc *yaml.Node, JSONPath string) *yaml.Node { + var locatedNode *yaml.Node + doneChan := make(chan bool) + locatedNodeChan := make(chan *yaml.Node) + go func() { + defer func() { + if err := recover(); err != nil { + // can't search path, too crazy. + doneChan <- true + } + }() + _, path := utils.ConvertComponentIdIntoFriendlyPathSearch(JSONPath) + if path == "" { + doneChan <- true + } + jsonPath, _ := jsonpath.NewPath(path) + locatedNodes := jsonPath.Query(doc) + if len(locatedNodes) > 0 { + locatedNode = locatedNodes[0] + } + locatedNodeChan <- locatedNode + }() + select { + case locatedNode = <-locatedNodeChan: + return locatedNode + case <-doneChan: + return nil + } +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/schema_validation/package.go b/vendor/github.com/pb33f/libopenapi-validator/schema_validation/package.go new file mode 100644 index 000000000..ee951b1f0 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/schema_validation/package.go @@ -0,0 +1,6 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +// Package schema_validation contains all the logic, models and interfaces for validating OpenAPI 3+ Schemas. +// Functionality for validating individual *base.Schema instances, but as well as validating a complete OpenAPI 3+ document +package schema_validation diff --git a/vendor/github.com/pb33f/libopenapi-validator/schema_validation/property_locator.go b/vendor/github.com/pb33f/libopenapi-validator/schema_validation/property_locator.go new file mode 100644 index 000000000..14bfe56a9 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/schema_validation/property_locator.go @@ -0,0 +1,310 @@ +// Copyright 2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package schema_validation + +import ( + "regexp" + "strings" + + "github.com/santhosh-tekuri/jsonschema/v6" + "go.yaml.in/yaml/v4" + + liberrors "github.com/pb33f/libopenapi-validator/errors" + "github.com/pb33f/libopenapi-validator/helpers" +) + +// PropertyNameInfo contains extracted information about a property name validation error +type PropertyNameInfo struct { + PropertyName string // The property name that violated validation (e.g., "$defs-atmVolatility_type") + ParentLocation []string // The path to the parent containing the property (e.g., ["components", "schemas"]) + EnhancedReason string // A more detailed error message with context + Pattern string // The pattern that was violated, if applicable +} + +var ( + // invalidPropertyNameRegex matches errors like: "invalid propertyName 'X'" + invalidPropertyNameRegex = regexp.MustCompile(`invalid propertyName '([^']+)'`) + + // patternMismatchRegex matches errors like: "'X' does not match pattern 'Y'" + patternMismatchRegex = regexp.MustCompile(`'([^']+)' does not match pattern '([^']+)'`) +) + +// extractPropertyNameFromError extracts property name information from a jsonschema.ValidationError +// when BasicOutput doesn't provide useful InstanceLocation. +// This handles Priority 1 (invalid propertyName) and Priority 2 (pattern mismatch) cases. +// +// Returns PropertyNameInfo with extracted details, or nil if no relevant information found. +// Note: ValidationError.Error() includes all cause information in the formatted string, +// so we only need to check the root error message. +func extractPropertyNameFromError(ve *jsonschema.ValidationError) *PropertyNameInfo { + if ve == nil { + return nil + } + + // Check error message for patterns (Error() includes all cause information) + return checkErrorForPropertyInfo(ve) +} + +// checkErrorForPropertyInfo examines a single ValidationError for property name patterns. +// This is extracted as a separate function to avoid duplication and improve testability. +func checkErrorForPropertyInfo(ve *jsonschema.ValidationError) *PropertyNameInfo { + errMsg := ve.Error() + return checkErrorMessageForPropertyInfo(errMsg, ve.InstanceLocation, ve) +} + +// checkErrorMessageForPropertyInfo extracts property name info from an error message string. +// This is separated to improve testability while keeping validation error traversal logic intact. +func checkErrorMessageForPropertyInfo(errMsg string, instanceLocation []string, ve *jsonschema.ValidationError) *PropertyNameInfo { + // Check for "invalid propertyName 'X'" first (most specific error message) + if matches := invalidPropertyNameRegex.FindStringSubmatch(errMsg); len(matches) > 1 { + propertyName := matches[1] + info := &PropertyNameInfo{ + PropertyName: propertyName, + ParentLocation: instanceLocation, + } + + // try to extract pattern information from deeper causes if available + var pattern string + if ve != nil { + pattern = extractPatternFromCauses(ve) + } + + if pattern != "" { + info.Pattern = pattern + info.EnhancedReason = buildEnhancedReason(propertyName, pattern) + } else { + info.EnhancedReason = "invalid propertyName '" + propertyName + "'" + } + + return info + } + + // Check for "'X' does not match pattern 'Y'" as fallback (pattern violation) + if matches := patternMismatchRegex.FindStringSubmatch(errMsg); len(matches) > 2 { + return &PropertyNameInfo{ + PropertyName: matches[1], + ParentLocation: instanceLocation, + Pattern: matches[2], + EnhancedReason: buildEnhancedReason(matches[1], matches[2]), + } + } + + return nil +} + +// extractPatternFromCauses looks through error causes to find pattern violation details. +// Since ValidationError.Error() includes all cause information, we check the formatted error string. +func extractPatternFromCauses(ve *jsonschema.ValidationError) string { + if ve == nil { + return "" + } + + // Check the error message which includes all cause information + errMsg := ve.Error() + if matches := patternMismatchRegex.FindStringSubmatch(errMsg); len(matches) > 2 { + return matches[2] + } + + return "" +} + +// buildEnhancedReason constructs a detailed error message with property name and pattern +func buildEnhancedReason(propertyName, pattern string) string { + var buf strings.Builder + buf.Grow(len(propertyName) + len(pattern) + 50) // pre-allocate to avoid reallocation + buf.WriteString("invalid propertyName '") + buf.WriteString(propertyName) + buf.WriteString("': does not match pattern '") + buf.WriteString(pattern) + buf.WriteString("'") + return buf.String() +} + +// findPropertyKeyNodeInYAML searches the YAML tree for a property key node at a specific location. +// It first navigates to the parent location, then searches for the property name as a map key. +// +// Parameters: +// - rootNode: The root YAML node to search from +// - propertyName: The property key to find (e.g., "$defs-atmVolatility_type") +// - parentPath: Path segments to the parent (e.g., ["components", "schemas"]) +// +// Returns the YAML node for the property key, or nil if not found. +func findPropertyKeyNodeInYAML(rootNode *yaml.Node, propertyName string, parentPath []string) *yaml.Node { + if rootNode == nil || propertyName == "" { + return nil + } + + // Navigate to parent location first + currentNode := rootNode + for _, segment := range parentPath { + currentNode = navigateToYAMLChild(currentNode, segment) + if currentNode == nil { + return nil + } + } + + // Search for the property name as a map key + return findMapKeyNode(currentNode, propertyName) +} + +// navigateToYAMLChild navigates from a parent node to a child by name. +// Handles both document root navigation and map content navigation. +func navigateToYAMLChild(parent *yaml.Node, childName string) *yaml.Node { + if parent == nil { + return nil + } + + // If parent is a document node, navigate to its content + if parent.Kind == yaml.DocumentNode && len(parent.Content) > 0 { + parent = parent.Content[0] + } + + // Navigate through mapping node + if parent.Kind == yaml.MappingNode { + return findMapKeyValue(parent, childName) + } + + return nil +} + +// findMapKeyValue searches a mapping node for a key and returns its value node +func findMapKeyValue(mappingNode *yaml.Node, keyName string) *yaml.Node { + if mappingNode.Kind != yaml.MappingNode { + return nil + } + + // mapping nodes have key-value pairs: [key1, value1, key2, value2, ...] + for i := 0; i < len(mappingNode.Content); i += 2 { + keyNode := mappingNode.Content[i] + if keyNode.Value == keyName { + // return the value node (i+1) + if i+1 < len(mappingNode.Content) { + return mappingNode.Content[i+1] + } + } + } + + return nil +} + +// findMapKeyNode searches a mapping node for a key and returns the key node itself (not the value) +func findMapKeyNode(mappingNode *yaml.Node, keyName string) *yaml.Node { + if mappingNode == nil { + return nil + } + + // if it's a document node, unwrap to content + if mappingNode.Kind == yaml.DocumentNode && len(mappingNode.Content) > 0 { + mappingNode = mappingNode.Content[0] + } + + if mappingNode.Kind != yaml.MappingNode { + return nil + } + + // mapping nodes have key-value pairs: [key1, value1, key2, value2, ...] + for i := 0; i < len(mappingNode.Content); i += 2 { + keyNode := mappingNode.Content[i] + if keyNode.Value == keyName { + return keyNode // contains line/column metadata for error reporting + } + } + + return nil +} + +// applyPropertyNameFallback attempts to enrich a violation with property name information +// when the primary location method fails. Returns true if enrichment was applied. +func applyPropertyNameFallback( + propertyInfo *PropertyNameInfo, + rootNode *yaml.Node, + violation *liberrors.SchemaValidationFailure, +) bool { + if propertyInfo == nil { + return false + } + + return enrichSchemaValidationFailure( + propertyInfo, + rootNode, + &violation.Line, + &violation.Column, + &violation.Reason, + &violation.FieldName, + &violation.FieldPath, + &violation.Location, + &violation.InstancePath, + ) +} + +// enrichSchemaValidationFailure attempts to enhance a SchemaValidationFailure with better +// location information by searching the YAML tree when the standard location is empty. +// +// This function: +// 1. searches YAML tree for the property key in various locations +// 2. updates Line, Column, Reason, and other fields if found +// +// Returns true if enrichment was performed, false otherwise. +func enrichSchemaValidationFailure( + failure *PropertyNameInfo, + rootNode *yaml.Node, + line *int, + column *int, + reason *string, + fieldName *string, + fieldPath *string, + location *string, + instancePath *[]string, +) bool { + if failure == nil { + return false + } + + // search for the property key in the YAML tree with multiple fallback locations + // since InstanceLocation may be empty for property name errors + var foundNode *yaml.Node + + // try with the provided parent location first + if len(failure.ParentLocation) > 0 { + foundNode = findPropertyKeyNodeInYAML(rootNode, failure.PropertyName, failure.ParentLocation) + } + + // common fallback locations for OpenAPI property name errors + if foundNode == nil { + foundNode = findPropertyKeyNodeInYAML(rootNode, failure.PropertyName, []string{"components", "schemas"}) + } + if foundNode == nil { + foundNode = findPropertyKeyNodeInYAML(rootNode, failure.PropertyName, []string{"components"}) + } + if foundNode == nil { + foundNode = findPropertyKeyNodeInYAML(rootNode, failure.PropertyName, []string{}) + } + + if foundNode == nil { + return false + } + + // populate location metadata from YAML node + *line = foundNode.Line + *column = foundNode.Column + + if failure.EnhancedReason != "" { + *reason = failure.EnhancedReason + } + + *fieldName = failure.PropertyName + + // construct JSONPath from parent location segments + if len(failure.ParentLocation) > 0 { + *fieldPath = helpers.ExtractJSONPathFromStringLocation("/" + strings.Join(failure.ParentLocation, "/") + "/" + failure.PropertyName) + *location = "/" + strings.Join(failure.ParentLocation, "/") + *instancePath = failure.ParentLocation + } else { + *fieldPath = helpers.ExtractJSONPathFromStringLocation("/" + failure.PropertyName) + *location = "/" + *instancePath = []string{} + } + + return true +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/schema_validation/validate_document.go b/vendor/github.com/pb33f/libopenapi-validator/schema_validation/validate_document.go new file mode 100644 index 000000000..bc4f30f73 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/schema_validation/validate_document.go @@ -0,0 +1,159 @@ +// Copyright 2023-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package schema_validation + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/pb33f/libopenapi" + "github.com/santhosh-tekuri/jsonschema/v6" + "go.yaml.in/yaml/v4" + "golang.org/x/text/language" + "golang.org/x/text/message" + + "github.com/pb33f/libopenapi-validator/config" + liberrors "github.com/pb33f/libopenapi-validator/errors" + "github.com/pb33f/libopenapi-validator/helpers" +) + +func normalizeJSON(data any) any { + d, _ := json.Marshal(data) + var normalized any + _ = json.Unmarshal(d, &normalized) + return normalized +} + +// ValidateOpenAPIDocument will validate an OpenAPI document against the OpenAPI 2, 3.0 and 3.1 schemas (depending on version) +// It will return true if the document is valid, false if it is not and a slice of ValidationError pointers. +func ValidateOpenAPIDocument(doc libopenapi.Document, opts ...config.Option) (bool, []*liberrors.ValidationError) { + options := config.NewValidationOptions(opts...) + + info := doc.GetSpecInfo() + loadedSchema := info.APISchema + var validationErrors []*liberrors.ValidationError + + // Check if SpecJSON is nil before dereferencing + if info.SpecJSON == nil { + violation := &liberrors.SchemaValidationFailure{ + Reason: "document SpecJSON is nil - document may not be properly parsed", + Location: "document root", + ReferenceSchema: loadedSchema, + } + validationErrors = append(validationErrors, &liberrors.ValidationError{ + ValidationType: "schema", + ValidationSubType: "document", + Message: "OpenAPI document validation failed", + Reason: "The document's SpecJSON is nil, indicating the document was not properly parsed or is empty", + SpecLine: 1, + SpecCol: 0, + SchemaValidationErrors: []*liberrors.SchemaValidationFailure{violation}, + HowToFix: "ensure the OpenAPI document is valid YAML/JSON and can be properly parsed by libopenapi", + Context: "document root", + }) + return false, validationErrors + } + + decodedDocument := *info.SpecJSON + + // Compile the JSON Schema + jsch, err := helpers.NewCompiledSchema("schema", []byte(loadedSchema), options) + if err != nil { + // schema compilation failed, return validation error instead of panicking + violation := &liberrors.SchemaValidationFailure{ + Reason: fmt.Sprintf("failed to compile OpenAPI schema: %s", err.Error()), + Location: "schema compilation", + ReferenceSchema: loadedSchema, + } + validationErrors = append(validationErrors, &liberrors.ValidationError{ + ValidationType: helpers.Schema, + ValidationSubType: "compilation", + Message: "OpenAPI document schema compilation failed", + Reason: fmt.Sprintf("The OpenAPI schema failed to compile: %s", err.Error()), + SpecLine: 1, + SpecCol: 0, + SchemaValidationErrors: []*liberrors.SchemaValidationFailure{violation}, + HowToFix: "check the OpenAPI schema for invalid JSON Schema syntax, complex regex patterns, or unsupported schema constructs", + Context: loadedSchema, + }) + return false, validationErrors + } + + // Validate the document + scErrs := jsch.Validate(normalizeJSON(decodedDocument)) + + var schemaValidationErrors []*liberrors.SchemaValidationFailure + + if scErrs != nil { + + var jk *jsonschema.ValidationError + if errors.As(scErrs, &jk) { + + // flatten the validationErrors + schFlatErrs := jk.BasicOutput().Errors + + // Extract property name info once before processing errors (performance optimization) + propertyInfo := extractPropertyNameFromError(jk) + + for q := range schFlatErrs { + er := schFlatErrs[q] + + errMsg := er.Error.Kind.LocalizedString(message.NewPrinter(language.Tag{})) + if er.KeywordLocation == "" || helpers.IgnorePolyRegex.MatchString(errMsg) { + continue // ignore this error, it's useless tbh, utter noise. + } + if errMsg != "" { + + // locate the violated property in the schema + located := LocateSchemaPropertyNodeByJSONPath(info.RootNode.Content[0], er.InstanceLocation) + violation := &liberrors.SchemaValidationFailure{ + Reason: errMsg, + Location: er.InstanceLocation, + FieldName: helpers.ExtractFieldNameFromStringLocation(er.InstanceLocation), + FieldPath: helpers.ExtractJSONPathFromStringLocation(er.InstanceLocation), + InstancePath: helpers.ConvertStringLocationToPathSegments(er.InstanceLocation), + DeepLocation: er.KeywordLocation, + AbsoluteLocation: er.AbsoluteKeywordLocation, + OriginalError: jk, + } + + // if we have a location within the schema, add it to the error + if located != nil { + line := located.Line + // if the located node is a map or an array, then the actual human interpretable + // line on which the violation occurred is the line of the key, not the value. + if located.Kind == yaml.MappingNode || located.Kind == yaml.SequenceNode { + if line > 0 { + line-- + } + } + + // location of the violation within the rendered schema. + violation.Line = line + violation.Column = located.Column + } else { + // handles property name validation errors that don't provide useful InstanceLocation + applyPropertyNameFallback(propertyInfo, info.RootNode.Content[0], violation) + } + schemaValidationErrors = append(schemaValidationErrors, violation) + } + } + } + + // add the error to the list + validationErrors = append(validationErrors, &liberrors.ValidationError{ + ValidationType: helpers.Schema, + Message: "Document does not pass validation", + Reason: fmt.Sprintf("OpenAPI document is not valid according "+ + "to the %s specification", info.Version), + SchemaValidationErrors: schemaValidationErrors, + HowToFix: liberrors.HowToFixInvalidSchema, + }) + } + if len(validationErrors) > 0 { + return false, validationErrors + } + return true, nil +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/schema_validation/validate_schema.go b/vendor/github.com/pb33f/libopenapi-validator/schema_validation/validate_schema.go new file mode 100644 index 000000000..5740f1e1f --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/schema_validation/validate_schema.go @@ -0,0 +1,342 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package schema_validation + +import ( + "encoding/json" + "errors" + "fmt" + "log/slog" + "os" + "reflect" + "regexp" + "strconv" + "sync" + + "github.com/pb33f/libopenapi/datamodel/high/base" + "github.com/pb33f/libopenapi/utils" + "github.com/santhosh-tekuri/jsonschema/v6" + "go.yaml.in/yaml/v4" + "golang.org/x/text/language" + "golang.org/x/text/message" + + _ "embed" + + "github.com/pb33f/libopenapi-validator/config" + liberrors "github.com/pb33f/libopenapi-validator/errors" + "github.com/pb33f/libopenapi-validator/helpers" +) + +// SchemaValidator is an interface that defines the methods for validating a *base.Schema (V3+ Only) object. +// There are 6 methods for validating a schema: +// +// ValidateSchemaString accepts a schema object to validate against, and a JSON/YAML blob that is defined as a string. +// ValidateSchemaObject accepts a schema object to validate against, and an object, created from unmarshalled JSON/YAML. +// ValidateSchemaBytes accepts a schema object to validate against, and a JSON/YAML blob that is defined as a byte array. +// ValidateSchemaStringWithVersion - version-aware validation that allows OpenAPI 3.0 keywords when version is specified. +// ValidateSchemaObjectWithVersion - version-aware validation that allows OpenAPI 3.0 keywords when version is specified. +// ValidateSchemaBytesWithVersion - version-aware validation that allows OpenAPI 3.0 keywords when version is specified. +type SchemaValidator interface { + // ValidateSchemaString accepts a schema object to validate against, and a JSON/YAML blob that is defined as a string. + // Uses OpenAPI 3.1+ validation by default (strict JSON Schema compliance). + ValidateSchemaString(schema *base.Schema, payload string) (bool, []*liberrors.ValidationError) + + // ValidateSchemaObject accepts a schema object to validate against, and an object, created from unmarshalled JSON/YAML. + // This is a pre-decoded object that will skip the need to unmarshal a string of JSON/YAML. + // Uses OpenAPI 3.1+ validation by default (strict JSON Schema compliance). + ValidateSchemaObject(schema *base.Schema, payload interface{}) (bool, []*liberrors.ValidationError) + + // ValidateSchemaBytes accepts a schema object to validate against, and a byte slice containing a schema to + // validate against. Uses OpenAPI 3.1+ validation by default (strict JSON Schema compliance). + ValidateSchemaBytes(schema *base.Schema, payload []byte) (bool, []*liberrors.ValidationError) + + // ValidateSchemaStringWithVersion accepts a schema object to validate against, a JSON/YAML blob, and an OpenAPI version. + // When version is 3.0, OpenAPI 3.0-specific keywords like 'nullable' are allowed and processed. + // When version is 3.1+, OpenAPI 3.0-specific keywords like 'nullable' will cause validation to fail. + ValidateSchemaStringWithVersion(schema *base.Schema, payload string, version float32) (bool, []*liberrors.ValidationError) + + // ValidateSchemaObjectWithVersion accepts a schema object to validate against, an object, and an OpenAPI version. + // When version is 3.0, OpenAPI 3.0-specific keywords like 'nullable' are allowed and processed. + // When version is 3.1+, OpenAPI 3.0-specific keywords like 'nullable' will cause validation to fail. + ValidateSchemaObjectWithVersion(schema *base.Schema, payload interface{}, version float32) (bool, []*liberrors.ValidationError) + + // ValidateSchemaBytesWithVersion accepts a schema object to validate against, a byte slice, and an OpenAPI version. + // When version is 3.0, OpenAPI 3.0-specific keywords like 'nullable' are allowed and processed. + // When version is 3.1+, OpenAPI 3.0-specific keywords like 'nullable' will cause validation to fail. + ValidateSchemaBytesWithVersion(schema *base.Schema, payload []byte, version float32) (bool, []*liberrors.ValidationError) +} + +var instanceLocationRegex = regexp.MustCompile(`^/(\d+)`) + +type schemaValidator struct { + options *config.ValidationOptions + logger *slog.Logger + lock sync.Mutex +} + +// NewSchemaValidatorWithLogger will create a new SchemaValidator instance, ready to accept schemas and payloads to validate. +func NewSchemaValidatorWithLogger(logger *slog.Logger, opts ...config.Option) SchemaValidator { + options := config.NewValidationOptions(opts...) + + return &schemaValidator{options: options, logger: logger, lock: sync.Mutex{}} +} + +// NewSchemaValidator will create a new SchemaValidator instance, ready to accept schemas and payloads to validate. +func NewSchemaValidator(opts ...config.Option) SchemaValidator { + logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ + Level: slog.LevelError, + })) + return NewSchemaValidatorWithLogger(logger, opts...) +} + +func (s *schemaValidator) ValidateSchemaString(schema *base.Schema, payload string) (bool, []*liberrors.ValidationError) { + return s.validateSchemaWithVersion(schema, []byte(payload), nil, s.logger, 3.1) +} + +func (s *schemaValidator) ValidateSchemaObject(schema *base.Schema, payload interface{}) (bool, []*liberrors.ValidationError) { + return s.validateSchemaWithVersion(schema, nil, payload, s.logger, 3.1) +} + +func (s *schemaValidator) ValidateSchemaBytes(schema *base.Schema, payload []byte) (bool, []*liberrors.ValidationError) { + return s.validateSchemaWithVersion(schema, payload, nil, s.logger, 3.1) +} + +func (s *schemaValidator) ValidateSchemaStringWithVersion(schema *base.Schema, payload string, version float32) (bool, []*liberrors.ValidationError) { + return s.validateSchemaWithVersion(schema, []byte(payload), nil, s.logger, version) +} + +func (s *schemaValidator) ValidateSchemaObjectWithVersion(schema *base.Schema, payload interface{}, version float32) (bool, []*liberrors.ValidationError) { + return s.validateSchemaWithVersion(schema, nil, payload, s.logger, version) +} + +func (s *schemaValidator) ValidateSchemaBytesWithVersion(schema *base.Schema, payload []byte, version float32) (bool, []*liberrors.ValidationError) { + return s.validateSchemaWithVersion(schema, payload, nil, s.logger, version) +} + +func (s *schemaValidator) validateSchemaWithVersion(schema *base.Schema, payload []byte, decodedObject interface{}, log *slog.Logger, version float32) (bool, []*liberrors.ValidationError) { + var validationErrors []*liberrors.ValidationError + + if schema == nil { + log.Info("schema is empty and cannot be validated. This generally means the schema is missing from the spec, or could not be read.") + return false, validationErrors + } + + var renderedSchema []byte + + // render the schema, to be used for validation, stop this from running concurrently, mutations are made to state + // and, it will cause async issues. + // Create isolated render context for this validation to prevent false positive cycle detection + // when multiple validations run concurrently. + // Use validation mode to force full inlining of discriminator refs - the JSON schema compiler + // needs a self-contained schema without unresolved $refs. + renderCtx := base.NewInlineRenderContextForValidation() + s.lock.Lock() + var e error + renderedSchema, e = schema.RenderInlineWithContext(renderCtx) + if e != nil { + // schema cannot be rendered, so it's not valid! + violation := &liberrors.SchemaValidationFailure{ + Reason: e.Error(), + Location: "unavailable", + ReferenceSchema: string(renderedSchema), + ReferenceObject: string(payload), + } + validationErrors = append(validationErrors, &liberrors.ValidationError{ + ValidationType: helpers.RequestBodyValidation, + ValidationSubType: helpers.Schema, + Message: "schema does not pass validation", + Reason: fmt.Sprintf("The schema cannot be decoded: %s", e.Error()), + SpecLine: schema.GoLow().GetRootNode().Line, + SpecCol: schema.GoLow().GetRootNode().Column, + SchemaValidationErrors: []*liberrors.SchemaValidationFailure{violation}, + HowToFix: liberrors.HowToFixInvalidSchema, + Context: string(renderedSchema), + }) + s.lock.Unlock() + return false, validationErrors + + } + s.lock.Unlock() + + jsonSchema, _ := utils.ConvertYAMLtoJSON(renderedSchema) + + if decodedObject == nil && len(payload) > 0 { + err := json.Unmarshal(payload, &decodedObject) + if err != nil { + + // cannot decode the request body, so it's not valid + violation := &liberrors.SchemaValidationFailure{ + Reason: err.Error(), + Location: "unavailable", + ReferenceSchema: string(renderedSchema), + ReferenceObject: string(payload), + } + line := 1 + col := 0 + if schema.GoLow().Type.KeyNode != nil { + line = schema.GoLow().Type.KeyNode.Line + col = schema.GoLow().Type.KeyNode.Column + } + validationErrors = append(validationErrors, &liberrors.ValidationError{ + ValidationType: helpers.RequestBodyValidation, + ValidationSubType: helpers.Schema, + Message: "schema does not pass validation", + Reason: fmt.Sprintf("The schema cannot be decoded: %s", err.Error()), + SpecLine: line, + SpecCol: col, + SchemaValidationErrors: []*liberrors.SchemaValidationFailure{violation}, + HowToFix: liberrors.HowToFixInvalidSchema, + Context: string(renderedSchema), + }) + return false, validationErrors + } + + } + + path := "" + if schema.GoLow().GetIndex() != nil { + path = schema.GoLow().GetIndex().GetSpecAbsolutePath() + } + jsch, err := helpers.NewCompiledSchemaWithVersion(path, jsonSchema, s.options, version) + + var schemaValidationErrors []*liberrors.SchemaValidationFailure + if err != nil { + violation := &liberrors.SchemaValidationFailure{ + Reason: err.Error(), + Location: "schema compilation", + ReferenceSchema: string(renderedSchema), + ReferenceObject: string(payload), + } + line := 1 + col := 0 + if schema.GoLow().Type.KeyNode != nil { + line = schema.GoLow().Type.KeyNode.Line + col = schema.GoLow().Type.KeyNode.Column + } + validationErrors = append(validationErrors, &liberrors.ValidationError{ + ValidationType: helpers.Schema, + ValidationSubType: helpers.Schema, + Message: "schema compilation failed", + Reason: fmt.Sprintf("Schema compilation failed: %s", err.Error()), + SpecLine: line, + SpecCol: col, + SchemaValidationErrors: []*liberrors.SchemaValidationFailure{violation}, + HowToFix: liberrors.HowToFixInvalidSchema, + Context: string(renderedSchema), + }) + return false, validationErrors + } + + if jsch != nil && decodedObject != nil { + scErrs := jsch.Validate(decodedObject) + if scErrs != nil { + + var jk *jsonschema.ValidationError + if errors.As(scErrs, &jk) { + + // flatten the validationErrors + schFlatErr := jk.BasicOutput().Errors + schemaValidationErrors = extractBasicErrors(schFlatErr, renderedSchema, + decodedObject, payload, jk, schemaValidationErrors) + } + line := 1 + col := 0 + if schema.GoLow().Type.KeyNode != nil { + line = schema.GoLow().Type.KeyNode.Line + col = schema.GoLow().Type.KeyNode.Column + } + + validationErrors = append(validationErrors, &liberrors.ValidationError{ + ValidationType: helpers.Schema, + Message: "schema does not pass validation", + Reason: "Schema failed to validate against the contract requirements", + SpecLine: line, + SpecCol: col, + SchemaValidationErrors: schemaValidationErrors, + HowToFix: liberrors.HowToFixInvalidSchema, + Context: string(renderedSchema), + }) + } + } + if len(validationErrors) > 0 { + return false, validationErrors + } + return true, nil +} + +func extractBasicErrors(schFlatErrs []jsonschema.OutputUnit, + renderedSchema []byte, decodedObject interface{}, + payload []byte, jk *jsonschema.ValidationError, + schemaValidationErrors []*liberrors.SchemaValidationFailure, +) []*liberrors.SchemaValidationFailure { + // Extract property name info once before processing errors (performance optimization) + propertyInfo := extractPropertyNameFromError(jk) + + for q := range schFlatErrs { + er := schFlatErrs[q] + + errMsg := er.Error.Kind.LocalizedString(message.NewPrinter(language.Tag{})) + if helpers.IgnoreRegex.MatchString(errMsg) { + continue // ignore this error, it's useless tbh, utter noise. + } + if er.Error != nil { + + // re-encode the schema. + var renderedNode yaml.Node + _ = yaml.Unmarshal(renderedSchema, &renderedNode) + + // locate the violated property in the schema + located := LocateSchemaPropertyNodeByJSONPath(renderedNode.Content[0], er.KeywordLocation) + + // extract the element specified by the instance + val := instanceLocationRegex.FindStringSubmatch(er.InstanceLocation) + var referenceObject string + + if len(val) > 0 { + referenceIndex, _ := strconv.Atoi(val[1]) + if reflect.ValueOf(decodedObject).Type().Kind() == reflect.Slice { + found := decodedObject.([]any)[referenceIndex] + recoded, _ := json.MarshalIndent(found, "", " ") + referenceObject = string(recoded) + } + } + if referenceObject == "" { + referenceObject = string(payload) + } + + violation := &liberrors.SchemaValidationFailure{ + Reason: errMsg, + Location: er.InstanceLocation, + FieldName: helpers.ExtractFieldNameFromStringLocation(er.InstanceLocation), + FieldPath: helpers.ExtractJSONPathFromStringLocation(er.InstanceLocation), + InstancePath: helpers.ConvertStringLocationToPathSegments(er.InstanceLocation), + DeepLocation: er.KeywordLocation, + AbsoluteLocation: er.AbsoluteKeywordLocation, + ReferenceSchema: string(renderedSchema), + ReferenceObject: referenceObject, + OriginalError: jk, + } + // if we have a location within the schema, add it to the error + if located != nil { + line := located.Line + // if the located node is a map or an array, then the actual human interpretable + // line on which the violation occurred is the line of the key, not the value. + if located.Kind == yaml.MappingNode || located.Kind == yaml.SequenceNode { + if line > 0 { + line-- + } + } + + // location of the violation within the rendered schema. + violation.Line = line + violation.Column = located.Column + } else { + // handles property name validation errors that don't provide useful InstanceLocation + applyPropertyNameFallback(propertyInfo, renderedNode.Content[0], violation) + } + schemaValidationErrors = append(schemaValidationErrors, violation) + } + } + return schemaValidationErrors +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/schema_validation/validate_xml.go b/vendor/github.com/pb33f/libopenapi-validator/schema_validation/validate_xml.go new file mode 100644 index 000000000..ad30bd85b --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/schema_validation/validate_xml.go @@ -0,0 +1,174 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package schema_validation + +import ( + "encoding/json" + "fmt" + "log/slog" + "strings" + + "github.com/pb33f/libopenapi/datamodel/high/base" + + xj "github.com/basgys/goxml2json" + + liberrors "github.com/pb33f/libopenapi-validator/errors" + "github.com/pb33f/libopenapi-validator/helpers" +) + +func (x *xmlValidator) validateXMLWithVersion(schema *base.Schema, xmlString string, log *slog.Logger, version float32) (bool, []*liberrors.ValidationError) { + var validationErrors []*liberrors.ValidationError + + if schema == nil { + log.Info("schema is empty and cannot be validated") + return false, validationErrors + } + + // parse xml and transform to json structure matching schema + transformedJSON, err := transformXMLToSchemaJSON(xmlString, schema) + if err != nil { + violation := &liberrors.SchemaValidationFailure{ + Reason: err.Error(), + Location: "xml parsing", + ReferenceSchema: "", + ReferenceObject: xmlString, + } + validationErrors = append(validationErrors, &liberrors.ValidationError{ + ValidationType: helpers.RequestBodyValidation, + ValidationSubType: helpers.Schema, + Message: "xml example is malformed", + Reason: fmt.Sprintf("failed to parse xml: %s", err.Error()), + SchemaValidationErrors: []*liberrors.SchemaValidationFailure{violation}, + HowToFix: "ensure xml is well-formed and matches schema structure", + }) + return false, validationErrors + } + + // validate transformed json against schema using existing validator + return x.schemaValidator.validateSchemaWithVersion(schema, nil, transformedJSON, log, version) +} + +// transformXMLToSchemaJSON converts xml to json structure matching openapi schema. +// applies xml object transformations: name, attribute, wrapped. +func transformXMLToSchemaJSON(xmlString string, schema *base.Schema) (interface{}, error) { + if xmlString == "" { + return nil, fmt.Errorf("empty xml content") + } + + // parse xml using goxml2json with type conversion for numbers only + // note: we convert floats and ints, but not booleans, since xml content + // may legitimately contain "true"/"false" as string values + jsonBuf, err := xj.Convert(strings.NewReader(xmlString), xj.WithTypeConverter(xj.Float, xj.Int)) + if err != nil { + return nil, fmt.Errorf("malformed xml: %w", err) + } + + // decode to interface{} + var rawJSON interface{} + if err := json.Unmarshal(jsonBuf.Bytes(), &rawJSON); err != nil { + return nil, fmt.Errorf("failed to decode json: %w", err) + } + + // apply openapi xml object transformations + transformed := applyXMLTransformations(rawJSON, schema) + return transformed, nil +} + +// applyXMLTransformations applies openapi xml object rules to match json schema. +// handles xml.name (root unwrapping), xml.attribute (dash prefix), xml.wrapped (array unwrapping). +func applyXMLTransformations(data interface{}, schema *base.Schema) interface{} { + if schema == nil { + return data + } + + // unwrap root element if xml.name is set on schema + if schema.XML != nil && schema.XML.Name != "" { + if dataMap, ok := data.(map[string]interface{}); ok { + if wrapped, exists := dataMap[schema.XML.Name]; exists { + data = wrapped + } + } + } + + // transform properties based on their xml configurations + if dataMap, ok := data.(map[string]interface{}); ok { + if schema.Properties == nil || schema.Properties.Len() == 0 { + return data + } + + transformed := make(map[string]interface{}, schema.Properties.Len()) + + for pair := schema.Properties.First(); pair != nil; pair = pair.Next() { + propName := pair.Key() + propSchemaProxy := pair.Value() + propSchema := propSchemaProxy.Schema() + if propSchema == nil { + continue + } + + // determine xml element name (defaults to property name) + xmlName := propName + if propSchema.XML != nil && propSchema.XML.Name != "" { + xmlName = propSchema.XML.Name + } + + // handle xml.attribute: true - attributes are prefixed with dash + if propSchema.XML != nil && propSchema.XML.Attribute { + attrKey := "-" + xmlName + if val, exists := dataMap[attrKey]; exists { + transformed[propName] = val + continue + } + } + + // handle regular elements + if val, exists := dataMap[xmlName]; exists { + // handle wrapped arrays: unwrap container element + if len(propSchema.Type) > 0 && propSchema.Type[0] == "array" && + propSchema.XML != nil && propSchema.XML.Wrapped { + val = unwrapArrayElement(val, propSchema) + } + + transformed[propName] = val + } + } + + return transformed + } + + return data +} + +// unwrapArrayElement removes wrapping element from xml arrays when xml.wrapped is true. +// example: {"items": {"item": [...]}} becomes [...] +func unwrapArrayElement(val interface{}, propSchema *base.Schema) interface{} { + wrapMap, ok := val.(map[string]interface{}) + if !ok { + return val + } + + // determine item element name + itemName := "item" + if propSchema.Items != nil && propSchema.Items.A != nil { + itemSchema := propSchema.Items.A.Schema() + if itemSchema != nil && itemSchema.XML != nil && itemSchema.XML.Name != "" { + itemName = itemSchema.XML.Name + } + } + + // unwrap: look for item element inside wrapper + if unwrapped, exists := wrapMap[itemName]; exists { + return unwrapped + } + + return val +} + +// IsXMLContentType checks if a media type string represents xml content. +func IsXMLContentType(mediaType string) bool { + mt := strings.ToLower(strings.TrimSpace(mediaType)) + return strings.HasPrefix(mt, "application/xml") || + strings.HasPrefix(mt, "text/xml") || + strings.HasSuffix(mt, "+xml") +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/schema_validation/xml_validator.go b/vendor/github.com/pb33f/libopenapi-validator/schema_validation/xml_validator.go new file mode 100644 index 000000000..f9b2ef6e4 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/schema_validation/xml_validator.go @@ -0,0 +1,59 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package schema_validation + +import ( + "log/slog" + "os" + + "github.com/pb33f/libopenapi/datamodel/high/base" + + "github.com/pb33f/libopenapi-validator/config" + liberrors "github.com/pb33f/libopenapi-validator/errors" +) + +// XMLValidator is an interface that defines methods for validating XML against OpenAPI schemas. +// There are 2 methods for validating XML: +// +// ValidateXMLString validates an XML string against a schema, applying OpenAPI xml object transformations. +// ValidateXMLStringWithVersion - version-aware XML validation that allows OpenAPI 3.0 keywords when version is specified. +type XMLValidator interface { + // ValidateXMLString validates an XML string against an OpenAPI schema, applying xml object transformations. + // Uses OpenAPI 3.1+ validation by default (strict JSON Schema compliance). + ValidateXMLString(schema *base.Schema, xmlString string) (bool, []*liberrors.ValidationError) + + // ValidateXMLStringWithVersion validates an XML string with version-specific rules. + // When version is 3.0, OpenAPI 3.0-specific keywords like 'nullable' are allowed and processed. + // When version is 3.1+, OpenAPI 3.0-specific keywords like 'nullable' will cause validation to fail. + ValidateXMLStringWithVersion(schema *base.Schema, xmlString string, version float32) (bool, []*liberrors.ValidationError) +} + +type xmlValidator struct { + schemaValidator *schemaValidator + logger *slog.Logger +} + +// NewXMLValidatorWithLogger creates a new XMLValidator instance with a custom logger. +func NewXMLValidatorWithLogger(logger *slog.Logger, opts ...config.Option) XMLValidator { + options := config.NewValidationOptions(opts...) + // Create an internal schema validator for JSON validation after XML transformation + sv := &schemaValidator{options: options, logger: logger} + return &xmlValidator{schemaValidator: sv, logger: logger} +} + +// NewXMLValidator creates a new XMLValidator instance with default logging configuration. +func NewXMLValidator(opts ...config.Option) XMLValidator { + logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ + Level: slog.LevelError, + })) + return NewXMLValidatorWithLogger(logger, opts...) +} + +func (x *xmlValidator) ValidateXMLString(schema *base.Schema, xmlString string) (bool, []*liberrors.ValidationError) { + return x.validateXMLWithVersion(schema, xmlString, x.logger, 3.1) +} + +func (x *xmlValidator) ValidateXMLStringWithVersion(schema *base.Schema, xmlString string, version float32) (bool, []*liberrors.ValidationError) { + return x.validateXMLWithVersion(schema, xmlString, x.logger, version) +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/strict/array_validator.go b/vendor/github.com/pb33f/libopenapi-validator/strict/array_validator.go new file mode 100644 index 000000000..066053074 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/strict/array_validator.go @@ -0,0 +1,107 @@ +// Copyright 2023-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package strict + +import ( + "strconv" + + "github.com/pb33f/libopenapi/datamodel/high/base" +) + +// validateArray checks an array value against a schema for undeclared properties +// within array items. It handles: +// - items (schema for all items or boolean) +// - prefixItems (tuple validation with positional schemas) +// - unevaluatedItems (items not covered by items/prefixItems) +func (v *Validator) validateArray(ctx *traversalContext, schema *base.Schema, data []any) []UndeclaredValue { + if len(data) == 0 { + return nil + } + + var undeclared []UndeclaredValue + + // Check for items: false + // When items: false, no items are allowed. If base validation passed, the + // array should be empty. But we explicitly check in case it wasn't caught. + if schema.Items != nil && schema.Items.IsB() && !schema.Items.B { + for i := range data { + itemPath := buildArrayPath(ctx.path, i) + undeclared = append(undeclared, + newUndeclaredItem(itemPath, strconv.Itoa(i), data[i], ctx.direction)) + } + return undeclared + } + + prefixLen := 0 + + // handle prefixItems first (tuple validation) + if len(schema.PrefixItems) > 0 { + for i, itemProxy := range schema.PrefixItems { + if i >= len(data) { + break + } + + itemPath := buildArrayPath(ctx.path, i) + itemCtx := ctx.withPath(itemPath) + + if itemCtx.shouldIgnore() { + prefixLen++ + continue + } + + itemSchema := itemProxy.Schema() + if itemSchema != nil { + undeclared = append(undeclared, v.validateValue(itemCtx, itemSchema, data[i])...) + } + prefixLen++ + } + } + + // handle items for remaining elements (after prefixItems) + if schema.Items != nil && schema.Items.A != nil { + itemProxy := schema.Items.A + itemSchema := itemProxy.Schema() + + if itemSchema != nil { + for i := prefixLen; i < len(data); i++ { + itemPath := buildArrayPath(ctx.path, i) + itemCtx := ctx.withPath(itemPath) + + if itemCtx.shouldIgnore() { + continue + } + + undeclared = append(undeclared, v.validateValue(itemCtx, itemSchema, data[i])...) + } + } + } + + // handle unevaluatedItems with schema. + // unevaluatedItems: false is handled by base validation. + // unevaluatedItems: {schema} means items matching the schema are valid. + // note: this doesn't account for items evaluated by `contains`. for strict + // validation this is acceptable as we check conservatively. + if schema.UnevaluatedItems != nil && schema.UnevaluatedItems.Schema() != nil { + // this applies to items not covered by items or prefixItems. + // if there's no items schema, unevaluatedItems applies to: + // - items after prefixItems (if prefixItems exists) + // - all items (if neither items nor prefixItems exists) + if schema.Items == nil { + unevalSchema := schema.UnevaluatedItems.Schema() + startIndex := len(schema.PrefixItems) // 0 if no prefixItems + for i := startIndex; i < len(data); i++ { + itemPath := buildArrayPath(ctx.path, i) + itemCtx := ctx.withPath(itemPath) + + if itemCtx.shouldIgnore() { + continue + } + + undeclared = append(undeclared, v.validateValue(itemCtx, unevalSchema, data[i])...) + } + } + } + + return undeclared +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/strict/headers.go b/vendor/github.com/pb33f/libopenapi-validator/strict/headers.go new file mode 100644 index 000000000..823848ca4 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/strict/headers.go @@ -0,0 +1,35 @@ +// Copyright 2023-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package strict + +import "strings" + +// isHeaderIgnored checks if a header name should be ignored in strict validation. +// Uses the effective ignored headers list from options (defaults, replaced, or merged). +// Set-Cookie is direction-aware: ignored in responses but reported in requests. +func (v *Validator) isHeaderIgnored(name string, direction Direction) bool { + lower := strings.ToLower(name) + + // Set-Cookie is expected in responses but unexpected in requests + if lower == "set-cookie" { + return direction == DirectionResponse + } + + // Check effective ignored list + for _, h := range v.getEffectiveIgnoredHeaders() { + if strings.ToLower(h) == lower { + return true + } + } + return false +} + +// getEffectiveIgnoredHeaders returns the list of headers to ignore based on +// configuration. Uses the ValidationOptions method for consistency. +func (v *Validator) getEffectiveIgnoredHeaders() []string { + if v.options == nil { + return nil + } + return v.options.GetEffectiveStrictIgnoredHeaders() +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/strict/matcher.go b/vendor/github.com/pb33f/libopenapi-validator/strict/matcher.go new file mode 100644 index 000000000..89bb2c3b3 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/strict/matcher.go @@ -0,0 +1,120 @@ +// Copyright 2023-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package strict + +import ( + "errors" + "fmt" + + "github.com/pb33f/libopenapi/datamodel/high/base" + "github.com/pb33f/libopenapi/utils" + "github.com/santhosh-tekuri/jsonschema/v6" + + "github.com/pb33f/libopenapi-validator/helpers" +) + +// dataMatchesSchema checks if the given data matches the schema using +// JSON Schema validation. This is used for: +// - oneOf/anyOf variant selection (finding which variant the data matches) +// - if/then/else condition evaluation +// - additionalProperties schema matching +// +// The method uses version-aware schema compilation to handle OpenAPI 3.0 vs 3.1 +// differences (especially nullable handling). +// +// Returns (true, nil) if data matches the schema. +// Returns (false, nil) if data does not match the schema. +// Returns (false, error) if schema compilation failed. +func (v *Validator) dataMatchesSchema(schema *base.Schema, data any) (bool, error) { + if schema == nil { + return true, nil // No schema means anything matches + } + + compiled, err := v.getCompiledSchema(schema) + if err != nil { + return false, err + } + return compiled.Validate(data) == nil, nil +} + +// getCompiledSchema returns a compiled JSON Schema for the given high-level schema. +// It checks multiple cache levels: +// 1. Global SchemaCache (if configured in options) +// 2. Local instance cache (for reuse within this validation call) +// 3. Compiles on-the-fly if not cached +// +// Returns the compiled schema and nil error on success. +// Returns nil schema and nil error if the input schema is nil. +// Returns nil schema and error if compilation failed. +func (v *Validator) getCompiledSchema(schema *base.Schema) (*jsonschema.Schema, error) { + if schema == nil || schema.GoLow() == nil { + return nil, nil + } + + hash := schema.GoLow().Hash() + hashKey := fmt.Sprintf("%x", hash) + + // try global cache first (if available) + if v.options != nil && v.options.SchemaCache != nil { + if cached, ok := v.options.SchemaCache.Load(hash); ok && cached != nil && cached.CompiledSchema != nil { + return cached.CompiledSchema, nil + } + } + + // try local instance cache + if compiled, ok := v.localCache[hashKey]; ok { + return compiled, nil + } + + // cache miss - compile on-the-fly with context-aware rendering + compiled, err := v.compileSchema(schema) + if err != nil { + return nil, err + } + if compiled != nil { + v.localCache[hashKey] = compiled + } + + return compiled, nil +} + +// compileSchema renders and compiles a schema for validation. +// Uses RenderInlineWithContext for safe cycle handling. +// +// Returns the compiled schema and nil error on success. +// Returns nil schema and error if any step fails (render, conversion, compilation). +func (v *Validator) compileSchema(schema *base.Schema) (*jsonschema.Schema, error) { + if schema == nil { + return nil, nil + } + + schemaHash := fmt.Sprintf("%x", schema.GoLow().Hash()) + + // use RenderInlineWithContext for safe cycle handling + renderedSchema, err := schema.RenderInlineWithContext(v.renderCtx) + if err != nil { + return nil, fmt.Errorf("strict: schema render failed (hash=%s): %w", schemaHash, err) + } + + jsonSchema, convErr := utils.ConvertYAMLtoJSON(renderedSchema) + if convErr != nil { + return nil, fmt.Errorf("strict: YAML to JSON conversion failed: %w", convErr) + } + if len(jsonSchema) == 0 { + return nil, errors.New("strict: schema rendered to empty JSON") + } + + schemaName := fmt.Sprintf("strict-match-%s", schemaHash) + compiled, err := helpers.NewCompiledSchemaWithVersion( + schemaName, + jsonSchema, + v.options, + v.version, + ) + if err != nil { + return nil, fmt.Errorf("strict: schema compilation failed (name=%s): %w", schemaName, err) + } + + return compiled, nil +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/strict/polymorphic.go b/vendor/github.com/pb33f/libopenapi-validator/strict/polymorphic.go new file mode 100644 index 000000000..d229f3383 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/strict/polymorphic.go @@ -0,0 +1,474 @@ +// Copyright 2023-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package strict + +import ( + "regexp" + "strings" + + "github.com/pb33f/libopenapi/datamodel/high/base" +) + +// validatePolymorphic handles allOf, oneOf, and anyOf schemas. +// For allOf: merge all schemas and validate against all. +// For oneOf/anyOf: find the matching variant and validate against it. +func (v *Validator) validatePolymorphic(ctx *traversalContext, schema *base.Schema, data map[string]any) []UndeclaredValue { + var undeclared []UndeclaredValue + + // Handle allOf first - data must match ALL schemas + if len(schema.AllOf) > 0 { + undeclared = append(undeclared, v.validateAllOf(ctx, schema, data)...) + } + + // Handle oneOf - data must match exactly ONE schema + if len(schema.OneOf) > 0 { + undeclared = append(undeclared, v.validateOneOf(ctx, schema, data)...) + } + + // Handle anyOf - data must match at least ONE schema + if len(schema.AnyOf) > 0 { + undeclared = append(undeclared, v.validateAnyOf(ctx, schema, data)...) + } + + // Also validate any direct properties on the parent schema + if schema.Properties != nil { + declared, patterns := v.collectDeclaredProperties(schema, data) + + // Check properties that aren't handled by allOf/oneOf/anyOf + for propName := range data { + // Skip if declared directly or via patterns + if isPropertyDeclared(propName, declared, patterns) { + continue + } + + // Check if it's declared in any of the allOf schemas + if v.isPropertyDeclaredInAllOf(schema.AllOf, propName) { + continue + } + + // For oneOf/anyOf, we've already validated against the matching variant + } + } + + return undeclared +} + +// validateAllOf validates data against all schemas in allOf. +// Collects properties from all schemas as declared. +func (v *Validator) validateAllOf(ctx *traversalContext, schema *base.Schema, data map[string]any) []UndeclaredValue { + var undeclared []UndeclaredValue + + // Collect declared properties from ALL schemas in allOf + allDeclared := make(map[string]*declaredProperty) + var allPatterns []*regexp.Regexp + + for _, schemaProxy := range schema.AllOf { + if schemaProxy == nil { + continue + } + + subSchema := schemaProxy.Schema() + if subSchema == nil { + continue + } + + declared, patterns := v.collectDeclaredProperties(subSchema, data) + for name, prop := range declared { + if _, exists := allDeclared[name]; !exists { + allDeclared[name] = prop + } + } + + allPatterns = append(allPatterns, patterns...) + } + + // collect from parent schema + declared, patterns := v.collectDeclaredProperties(schema, data) + for name, prop := range declared { + if _, exists := allDeclared[name]; !exists { + allDeclared[name] = prop + } + } + + allPatterns = append(allPatterns, patterns...) + + // check if strict mode should report for this combined schema + if !v.shouldReportUndeclaredForAllOf(schema) { + // Still recurse into declared properties + return v.recurseIntoAllOfDeclaredProperties(ctx, schema.AllOf, data, allDeclared) + } + + // Check each property in data + for propName, propValue := range data { + propPath := buildPath(ctx.path, propName) + propCtx := ctx.withPath(propPath) + + if propCtx.shouldIgnore() { + continue + } + + // Check if declared in merged schema + if isPropertyDeclared(propName, allDeclared, allPatterns) { + // Recurse into the property + propSchema := v.findPropertySchemaInAllOf(schema.AllOf, propName, allDeclared) + if propSchema != nil { + if v.shouldSkipProperty(propSchema, ctx.direction) { + continue + } + undeclared = append(undeclared, v.validateValue(propCtx, propSchema, propValue)...) + } + continue + } + + // Not declared - report as undeclared + undeclared = append(undeclared, + newUndeclaredProperty(propPath, propName, propValue, getDeclaredPropertyNames(allDeclared), ctx.direction, schema)) + } + + return undeclared +} + +// validateOneOf finds the matching oneOf variant and validates against it. +// Parent schema properties are merged with the variant's properties. +func (v *Validator) validateOneOf(ctx *traversalContext, schema *base.Schema, data map[string]any) []UndeclaredValue { + var matchingVariant *base.Schema + + // discriminator is present, use it to select the variant + if schema.Discriminator != nil { + matchingVariant = v.selectByDiscriminator(schema, schema.OneOf, data) + } + + // no discriminator or no match: find matching variant by validation + if matchingVariant == nil { + matchingVariant = v.findMatchingVariant(schema.OneOf, data) + } + + if matchingVariant == nil { + // No match found - base validation would report this error + return nil + } + + // Validate against variant, but filter out properties declared in parent + return v.validateVariantWithParent(ctx, schema, matchingVariant, data) +} + +// validateAnyOf finds matching anyOf variants and validates against them. +// Parent schema properties are merged with the variant's properties. +func (v *Validator) validateAnyOf(ctx *traversalContext, schema *base.Schema, data map[string]any) []UndeclaredValue { + var matchingVariant *base.Schema + + // If discriminator is present, use it to select the variant + if schema.Discriminator != nil { + matchingVariant = v.selectByDiscriminator(schema, schema.AnyOf, data) + } + + // No discriminator or no match: find matching variant by validation + if matchingVariant == nil { + matchingVariant = v.findMatchingVariant(schema.AnyOf, data) + } + + if matchingVariant == nil { + // No match found - base validation would report this error + return nil + } + + // Validate against variant, but filter out properties declared in parent + return v.validateVariantWithParent(ctx, schema, matchingVariant, data) +} + +// validateVariantWithParent validates data against a variant schema while also +// considering properties declared in the parent schema. This ensures parent +// properties are not reported as undeclared when using oneOf/anyOf. +func (v *Validator) validateVariantWithParent(ctx *traversalContext, parent *base.Schema, variant *base.Schema, data map[string]any) []UndeclaredValue { + var undeclared []UndeclaredValue + + // Collect declared properties from parent schema + parentDeclared, parentPatterns := v.collectDeclaredProperties(parent, data) + + // Collect declared properties from variant schema + variantDeclared, variantPatterns := v.collectDeclaredProperties(variant, data) + + // Merge: parent + variant + allDeclared := make(map[string]*declaredProperty) + for name, prop := range parentDeclared { + allDeclared[name] = prop + } + for name, prop := range variantDeclared { + allDeclared[name] = prop + } + allPatterns := append(parentPatterns, variantPatterns...) + + // Check if we should report undeclared (skip if additionalProperties: false) + if !v.shouldReportUndeclared(variant) && !v.shouldReportUndeclared(parent) { + // Still recurse into declared properties + return v.recurseIntoDeclaredPropertiesWithMerged(ctx, variant, parent, data, allDeclared) + } + + // Check each property in data + for propName, propValue := range data { + propPath := buildPath(ctx.path, propName) + propCtx := ctx.withPath(propPath) + + if propCtx.shouldIgnore() { + continue + } + + // Check if declared in merged schema (parent + variant) + if isPropertyDeclared(propName, allDeclared, allPatterns) { + // Find the property schema (prefer variant, fallback to parent) + propSchema := v.findPropertySchemaInMerged(variant, parent, propName, allDeclared) + if propSchema != nil { + if v.shouldSkipProperty(propSchema, ctx.direction) { + continue + } + undeclared = append(undeclared, v.validateValue(propCtx, propSchema, propValue)...) + } + continue + } + + // Not declared - report as undeclared + // Use variant schema location if available, otherwise fall back to parent + locationSchema := variant + if locationSchema == nil || locationSchema.GoLow() == nil { + locationSchema = parent + } + undeclared = append(undeclared, + newUndeclaredProperty(propPath, propName, propValue, getDeclaredPropertyNames(allDeclared), ctx.direction, locationSchema)) + } + + return undeclared +} + +// findPropertySchemaInMerged finds the schema for a property, preferring variant over parent. +// Checks explicit properties first, then patternProperties. +func (v *Validator) findPropertySchemaInMerged(variant, parent *base.Schema, propName string, declared map[string]*declaredProperty) *base.Schema { + // Check explicit declared first + if prop, ok := declared[propName]; ok && prop.proxy != nil { + return prop.proxy.Schema() + } + + // Check variant schema explicit properties + if variant != nil && variant.Properties != nil { + if propProxy, exists := variant.Properties.Get(propName); exists && propProxy != nil { + return propProxy.Schema() + } + } + + // Check parent schema explicit properties + if parent != nil && parent.Properties != nil { + if propProxy, exists := parent.Properties.Get(propName); exists && propProxy != nil { + return propProxy.Schema() + } + } + + // Check variant patternProperties + if variant != nil { + if propProxy := v.getPatternPropertySchema(variant, propName); propProxy != nil { + return propProxy.Schema() + } + } + + // Check parent patternProperties + if parent != nil { + if propProxy := v.getPatternPropertySchema(parent, propName); propProxy != nil { + return propProxy.Schema() + } + } + + return nil +} + +// recurseIntoDeclaredPropertiesWithMerged recurses into properties from merged parent+variant. +func (v *Validator) recurseIntoDeclaredPropertiesWithMerged(ctx *traversalContext, variant, parent *base.Schema, data map[string]any, declared map[string]*declaredProperty) []UndeclaredValue { + var undeclared []UndeclaredValue + + for propName, propValue := range data { + propPath := buildPath(ctx.path, propName) + propCtx := ctx.withPath(propPath) + + if propCtx.shouldIgnore() { + continue + } + + propSchema := v.findPropertySchemaInMerged(variant, parent, propName, declared) + if propSchema != nil { + if v.shouldSkipProperty(propSchema, ctx.direction) { + continue + } + undeclared = append(undeclared, v.validateValue(propCtx, propSchema, propValue)...) + } + } + + return undeclared +} + +// selectByDiscriminator uses the discriminator to select the appropriate variant. +func (v *Validator) selectByDiscriminator(schema *base.Schema, variants []*base.SchemaProxy, data map[string]any) *base.Schema { + if schema.Discriminator == nil { + return nil + } + + propName := schema.Discriminator.PropertyName + if propName == "" { + return nil + } + + discriminatorValue, ok := data[propName] + if !ok { + return nil + } + + valueStr, ok := discriminatorValue.(string) + if !ok { + return nil + } + + // check mapping first + if schema.Discriminator.Mapping != nil { + for pair := schema.Discriminator.Mapping.First(); pair != nil; pair = pair.Next() { + if pair.Key() == valueStr { + // The mapping value is a reference like "#/components/schemas/Dog" + mappedRef := pair.Value() + for _, variantProxy := range variants { + if variantProxy.IsReference() && variantProxy.GetReference() == mappedRef { + return variantProxy.Schema() + } + } + } + } + } + + // no mapping match, try to match by schema name in reference + for _, variantProxy := range variants { + if variantProxy.IsReference() { + ref := variantProxy.GetReference() + // Extract schema name from reference like "#/components/schemas/Dog" + parts := strings.Split(ref, "/") + if len(parts) > 0 && parts[len(parts)-1] == valueStr { + return variantProxy.Schema() + } + } + } + + return nil +} + +// findMatchingVariant finds the first variant that the data validates against. +func (v *Validator) findMatchingVariant(variants []*base.SchemaProxy, data map[string]any) *base.Schema { + for _, variantProxy := range variants { + if variantProxy == nil { + continue + } + + variantSchema := variantProxy.Schema() + if variantSchema == nil { + continue + } + + matches, _ := v.dataMatchesSchema(variantSchema, data) + if matches { + return variantSchema + } + } + return nil +} + +// isPropertyDeclaredInAllOf checks if a property is declared in any allOf schema. +func (v *Validator) isPropertyDeclaredInAllOf(allOf []*base.SchemaProxy, propName string) bool { + for _, schemaProxy := range allOf { + if schemaProxy == nil { + continue + } + + subSchema := schemaProxy.Schema() + if subSchema == nil { + continue + } + + if subSchema.Properties != nil { + if _, exists := subSchema.Properties.Get(propName); exists { + return true + } + } + } + return false +} + +// shouldReportUndeclaredForAllOf checks if any schema in allOf disables additional properties. +func (v *Validator) shouldReportUndeclaredForAllOf(schema *base.Schema) bool { + // Check parent schema + if schema.AdditionalProperties != nil && schema.AdditionalProperties.IsB() && !schema.AdditionalProperties.B { + return false + } + + // Check each allOf schema + for _, schemaProxy := range schema.AllOf { + if schemaProxy == nil { + continue + } + + subSchema := schemaProxy.Schema() + if subSchema == nil { + continue + } + + if subSchema.AdditionalProperties != nil && subSchema.AdditionalProperties.IsB() && !subSchema.AdditionalProperties.B { + return false + } + } + + return true +} + +// findPropertySchemaInAllOf finds the schema for a property in allOf schemas. +func (v *Validator) findPropertySchemaInAllOf(allOf []*base.SchemaProxy, propName string, declared map[string]*declaredProperty) *base.Schema { + // Check explicit declared first + if prop, ok := declared[propName]; ok && prop.proxy != nil { + return prop.proxy.Schema() + } + + // Search in allOf schemas + for _, schemaProxy := range allOf { + if schemaProxy == nil { + continue + } + + subSchema := schemaProxy.Schema() + if subSchema == nil { + continue + } + + if subSchema.Properties != nil { + if propProxy, exists := subSchema.Properties.Get(propName); exists && propProxy != nil { + return propProxy.Schema() + } + } + } + + return nil +} + +// recurseIntoAllOfDeclaredProperties recurses into properties without checking for undeclared. +func (v *Validator) recurseIntoAllOfDeclaredProperties(ctx *traversalContext, allOf []*base.SchemaProxy, data map[string]any, declared map[string]*declaredProperty) []UndeclaredValue { + var undeclared []UndeclaredValue + + for propName, propValue := range data { + propPath := buildPath(ctx.path, propName) + propCtx := ctx.withPath(propPath) + + if propCtx.shouldIgnore() { + continue + } + + propSchema := v.findPropertySchemaInAllOf(allOf, propName, declared) + if propSchema != nil { + if v.shouldSkipProperty(propSchema, ctx.direction) { + continue + } + undeclared = append(undeclared, v.validateValue(propCtx, propSchema, propValue)...) + } + } + + return undeclared +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/strict/property_collector.go b/vendor/github.com/pb33f/libopenapi-validator/strict/property_collector.go new file mode 100644 index 000000000..24317b833 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/strict/property_collector.go @@ -0,0 +1,168 @@ +// Copyright 2023-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package strict + +import ( + "regexp" + + "github.com/pb33f/libopenapi/datamodel/high/base" +) + +// declaredProperty holds information about a declared property in a schema. +type declaredProperty struct { + // proxy is the SchemaProxy for the property. + proxy *base.SchemaProxy +} + +// collectDeclaredProperties gathers all property names that are declared in a schema. +// This includes explicit properties, patternProperties matches, and properties from +// dependentSchemas and if/then/else based on the actual data. +// +// Returns a map from property name to its declaration info, plus a slice of +// pattern regexes for patternProperties matching. +func (v *Validator) collectDeclaredProperties( + schema *base.Schema, + data map[string]any, +) (declared map[string]*declaredProperty, patterns []*regexp.Regexp) { + declared = make(map[string]*declaredProperty) + + if schema == nil { + return declared, nil + } + + // explicit properties + if schema.Properties != nil { + for pair := schema.Properties.First(); pair != nil; pair = pair.Next() { + declared[pair.Key()] = &declaredProperty{ + proxy: pair.Value(), + } + } + } + + // pattern properties - use cached compiled patterns + if schema.PatternProperties != nil { + for pair := schema.PatternProperties.First(); pair != nil; pair = pair.Next() { + pattern := v.getCompiledPattern(pair.Key()) + if pattern == nil { + continue + } + patterns = append(patterns, pattern) + } + } + + // dependent schemas - if trigger property exists in data + if schema.DependentSchemas != nil { + for pair := schema.DependentSchemas.First(); pair != nil; pair = pair.Next() { + triggerProp := pair.Key() + if _, exists := data[triggerProp]; !exists { + continue + } + // trigger property exists, include dependent schema's properties + mergePropertiesIntoDeclared(declared, pair.Value().Schema()) + } + } + + // if/then/else + if schema.If != nil { + ifProxy := schema.If + ifSchema := ifProxy.Schema() + if ifSchema != nil { + matches, err := v.dataMatchesSchema(ifSchema, data) + if err != nil { + // schema compilation failed - log and use else branch + v.logger.Debug("strict: if schema compilation failed, using else branch", "error", err) + matches = false + } + if matches { + if schema.Then != nil { + mergePropertiesIntoDeclared(declared, schema.Then.Schema()) + } + } else { + if schema.Else != nil { + mergePropertiesIntoDeclared(declared, schema.Else.Schema()) + } + } + } + } + + return declared, patterns +} + +// mergePropertiesIntoDeclared merges properties from a schema's Properties map into +// the declared map. Only adds properties that are not already declared. +// This eliminates code duplication when collecting properties from multiple sources. +func mergePropertiesIntoDeclared(declared map[string]*declaredProperty, schema *base.Schema) { + if schema == nil || schema.Properties == nil { + return + } + for p := schema.Properties.First(); p != nil; p = p.Next() { + if _, alreadyDeclared := declared[p.Key()]; !alreadyDeclared { + declared[p.Key()] = &declaredProperty{ + proxy: p.Value(), + } + } + } +} + +// getDeclaredPropertyNames returns just the property names from declared properties. +func getDeclaredPropertyNames(declared map[string]*declaredProperty) []string { + if len(declared) == 0 { + return nil + } + names := make([]string, 0, len(declared)) + for name := range declared { + names = append(names, name) + } + return names +} + +// isPropertyDeclared checks if a property name is declared in the schema. +// A property is declared if: +// - It's in the explicit properties map +// - It matches any patternProperties regex +func isPropertyDeclared(name string, declared map[string]*declaredProperty, patterns []*regexp.Regexp) bool { + // check explicit properties + if _, ok := declared[name]; ok { + return true + } + + // check pattern properties + for _, pattern := range patterns { + if pattern.MatchString(name) { + return true + } + } + + return false +} + +// getPropertySchema returns the SchemaProxy for a declared property. +// Returns nil if the property is not declared or is only matched by pattern. +func getPropertySchema(name string, declared map[string]*declaredProperty) *base.SchemaProxy { + // check explicit properties first + if dp, ok := declared[name]; ok && dp.proxy != nil { + return dp.proxy + } + return nil +} + +// shouldSkipProperty checks if a property should be skipped based on +// readOnly/writeOnly and the current validation direction. +func (v *Validator) shouldSkipProperty(schema *base.Schema, direction Direction) bool { + if schema == nil { + return false + } + + // readOnly: skip in requests (should not be sent by client) + if direction == DirectionRequest && schema.ReadOnly != nil && *schema.ReadOnly { + return true + } + + // writeOnly: skip in responses (should not be returned by server) + if direction == DirectionResponse && schema.WriteOnly != nil && *schema.WriteOnly { + return true + } + + return false +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/strict/schema_walker.go b/vendor/github.com/pb33f/libopenapi-validator/strict/schema_walker.go new file mode 100644 index 000000000..042a91ccd --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/strict/schema_walker.go @@ -0,0 +1,234 @@ +// Copyright 2023-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package strict + +import ( + "github.com/pb33f/libopenapi/datamodel/high/base" +) + +// validateValue is the main entry point for validating a value against a schema. +// It dispatches to the appropriate handler based on the value type. +func (v *Validator) validateValue(ctx *traversalContext, schema *base.Schema, data any) []UndeclaredValue { + if schema == nil || data == nil { + return nil + } + + if ctx.shouldIgnore() { + return nil + } + + if ctx.exceedsDepth() { + return nil + } + + // check for cycles using schema hash + schemaKey := v.getSchemaKey(schema) + if ctx.checkAndMarkVisited(schemaKey) { + return nil + } + + // switch on data type + switch val := data.(type) { + case map[string]any: + return v.validateObject(ctx, schema, val) + case []any: + return v.validateArray(ctx, schema, val) + default: + return nil + } +} + +// validateObject checks an object value against a schema for undeclared properties. +func (v *Validator) validateObject(ctx *traversalContext, schema *base.Schema, data map[string]any) []UndeclaredValue { + var undeclared []UndeclaredValue + + if len(schema.AllOf) > 0 || len(schema.OneOf) > 0 || len(schema.AnyOf) > 0 { + return v.validatePolymorphic(ctx, schema, data) + } + + if !v.shouldReportUndeclared(schema) { + // additionalProperties: false - base validation catches this, no strict check needed + // Still need to recurse into declared properties + return v.recurseIntoDeclaredProperties(ctx, schema, data) + } + + declared, patterns := v.collectDeclaredProperties(schema, data) + + // check each property in the data + for propName, propValue := range data { + propPath := buildPath(ctx.path, propName) + propCtx := ctx.withPath(propPath) + + if propCtx.shouldIgnore() { + continue + } + + if !isPropertyDeclared(propName, declared, patterns) { + undeclared = append(undeclared, + newUndeclaredProperty(propPath, propName, propValue, getDeclaredPropertyNames(declared), ctx.direction, schema)) + + // even if undeclared, recurse into additionalProperties schema if present + if schema.AdditionalProperties != nil && schema.AdditionalProperties.IsA() { + addlProxy := schema.AdditionalProperties.A + if addlProxy != nil { + addlSchema := addlProxy.Schema() + if addlSchema != nil { + undeclared = append(undeclared, v.validateValue(propCtx, addlSchema, propValue)...) + } + } + } + continue + } + + // property is declared, recurse into it + propProxy := getPropertySchema(propName, declared) + if propProxy == nil { + propProxy = v.getPatternPropertySchema(schema, propName) + } + + if propProxy != nil { + propSchema := propProxy.Schema() + if propSchema != nil { + // check readOnly/writeOnly + if v.shouldSkipProperty(propSchema, ctx.direction) { + continue + } + undeclared = append(undeclared, v.validateValue(propCtx, propSchema, propValue)...) + } + } + } + + return undeclared +} + +// shouldReportUndeclared determines if strict mode should report undeclared +// properties for this schema. +func (v *Validator) shouldReportUndeclared(schema *base.Schema) bool { + if schema == nil { + return false + } + + // SHORT-CIRCUIT: If additionalProperties: false, base validation already catches extras. + if schema.AdditionalProperties != nil && schema.AdditionalProperties.IsB() && !schema.AdditionalProperties.B { + return false + } + + // STRICT OVERRIDE: Even if additionalProperties: true, report undeclared. + if schema.AdditionalProperties != nil { + if schema.AdditionalProperties.IsB() && schema.AdditionalProperties.B { + return true + } + if schema.AdditionalProperties.IsA() { + // additionalProperties with schema - properties matching schema are + // technically "declared" but we still want to flag them as not in + // the explicit schema. They will be recursed into. + return true + } + } + + // STRICT OVERRIDE: unevaluatedProperties: false with implicit additionalProperties: true + // Standard JSON Schema would catch via unevaluatedProperties, but strict reports + // even when additionalProperties: true would normally allow extras. + if schema.UnevaluatedProperties != nil && schema.UnevaluatedProperties.IsB() && !schema.UnevaluatedProperties.B { + // unevaluatedProperties: false means base validation catches extras + // BUT if there's no additionalProperties: false, strict should report + return true + } + + // default: no additionalProperties means implicit true in JSON Schema + // Strict reports undeclared in this case + return true +} + +// getPatternPropertySchema finds the schema for a property that matches +// a patternProperties regex. Uses cached compiled patterns. +func (v *Validator) getPatternPropertySchema(schema *base.Schema, propName string) *base.SchemaProxy { + if schema.PatternProperties == nil { + return nil + } + + for pair := schema.PatternProperties.First(); pair != nil; pair = pair.Next() { + pattern := v.getCompiledPattern(pair.Key()) + if pattern == nil { + continue + } + if pattern.MatchString(propName) { + return pair.Value() + } + } + + return nil +} + +// recurseIntoDeclaredProperties recurses into declared properties without +// checking for undeclared (used when additionalProperties: false). +// This includes both explicit properties and patternProperties matches. +func (v *Validator) recurseIntoDeclaredProperties(ctx *traversalContext, schema *base.Schema, data map[string]any) []UndeclaredValue { + var undeclared []UndeclaredValue + + processed := make(map[string]bool) + + // process explicit properties + if schema.Properties != nil { + for pair := schema.Properties.First(); pair != nil; pair = pair.Next() { + propName := pair.Key() + propProxy := pair.Value() + + propValue, exists := data[propName] + if !exists { + continue + } + + processed[propName] = true + + propPath := buildPath(ctx.path, propName) + propCtx := ctx.withPath(propPath) + + if propCtx.shouldIgnore() { + continue + } + + propSchema := propProxy.Schema() + if propSchema != nil { + if v.shouldSkipProperty(propSchema, ctx.direction) { + continue + } + undeclared = append(undeclared, v.validateValue(propCtx, propSchema, propValue)...) + } + } + } + + // process patternProperties - recurse into any data properties that match patterns + if schema.PatternProperties != nil { + for propName, propValue := range data { + if processed[propName] { + continue + } + + propProxy := v.getPatternPropertySchema(schema, propName) + if propProxy == nil { + continue + } + + processed[propName] = true + + propPath := buildPath(ctx.path, propName) + propCtx := ctx.withPath(propPath) + + if propCtx.shouldIgnore() { + continue + } + + propSchema := propProxy.Schema() + if propSchema != nil { + if v.shouldSkipProperty(propSchema, ctx.direction) { + continue + } + undeclared = append(undeclared, v.validateValue(propCtx, propSchema, propValue)...) + } + } + } + + return undeclared +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/strict/types.go b/vendor/github.com/pb33f/libopenapi-validator/strict/types.go new file mode 100644 index 000000000..2d682cc96 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/strict/types.go @@ -0,0 +1,391 @@ +// Copyright 2023-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +// Package strict provides strict validation that detects undeclared +// properties in requests and responses, even when additionalProperties +// would normally allow them. +// +// Strict mode is designed for API governance scenarios where you want to +// ensure that clients only send properties that are explicitly documented +// in the OpenAPI specification, regardless of whether additionalProperties +// is set to true. +// +// # Key Features +// +// - Detects undeclared properties in request/response bodies (JSON only) +// - Detects undeclared query parameters, headers, and cookies +// - Supports ignore paths with glob patterns (e.g., "$.body.metadata.*") +// - Handles polymorphic schemas (oneOf/anyOf) via per-branch validation +// - Respects readOnly/writeOnly based on request vs response direction +// - Configurable header ignore list with sensible defaults +// +// # Known Limitations +// +// Property names containing single quotes (e.g., {"it's": "value"}) cannot be +// represented in bracket notation and cannot be matched by ignore patterns. +// Such properties will always be reported as undeclared if not in schema. +// This is acceptable because property names with quotes are extremely rare. +package strict + +import ( + "context" + "fmt" + "log/slog" + "regexp" + + "github.com/pb33f/libopenapi/datamodel/high/base" + "github.com/santhosh-tekuri/jsonschema/v6" + + "github.com/pb33f/libopenapi-validator/config" +) + +// Direction indicates whether validation is for a request or response. +// This affects readOnly/writeOnly handling and Set-Cookie behavior. +type Direction int + +const ( + // DirectionRequest indicates validation of an HTTP request. + // readOnly properties are not expected in request bodies. + DirectionRequest Direction = iota + + // DirectionResponse indicates validation of an HTTP response. + // writeOnly properties are not expected in response bodies. + // Set-Cookie headers are ignored (expected in responses). + DirectionResponse +) + +// String returns a human-readable direction name. +func (d Direction) String() string { + if d == DirectionResponse { + return "response" + } + return "request" +} + +// UndeclaredValue represents a value found in data that is not declared +// in the schema. This is the core output of strict validation. +type UndeclaredValue struct { + // Path is the instance JSONPath where the undeclared value was found. + // uses bracket notation for property names with special characters. + // examples: "$.body.user.extra", "$.body['a.b'].value", "$.query.debug" + Path string + + // Name is the property, parameter, header, or cookie name. + Name string + + // Value is the actual value found (it may be truncated for display). + Value any + + // Type indicates what kind of value this is. + // one of: "property", "header", "query", "cookie", "item" + Type string + + // DeclaredProperties lists property names that ARE declared at this + // location in the schema. Helps users understand what's expected. + // for headers/query/cookies, this lists declared parameter names. + DeclaredProperties []string + + // Direction indicates whether this was in a request or response. + // used for error message disambiguation when Path is "$.body". + Direction Direction + + // SpecLine is the line number in the OpenAPI spec where the parent + // schema is defined. Zero if unavailable. + SpecLine int + + // SpecCol is the column number in the OpenAPI spec where the parent + // schema is defined. Zero if unavailable. + SpecCol int +} + +// extractSchemaLocation extracts the line and column from a schema's low-level +// representation. returns (0, 0) if the schema is nil or has no low-level info. +func extractSchemaLocation(schema *base.Schema) (line, col int) { + if schema == nil { + return 0, 0 + } + low := schema.GoLow() + if low == nil || low.RootNode == nil { + return 0, 0 + } + return low.RootNode.Line, low.RootNode.Column +} + +// newUndeclaredProperty creates an UndeclaredValue for an undeclared object property. +// the schema parameter is the parent schema where the property would need to be declared. +func newUndeclaredProperty(path, name string, value any, declaredNames []string, direction Direction, schema *base.Schema) UndeclaredValue { + line, col := extractSchemaLocation(schema) + return UndeclaredValue{ + Path: path, + Name: name, + Value: TruncateValue(value), + Type: "property", + DeclaredProperties: declaredNames, + Direction: direction, + SpecLine: line, + SpecCol: col, + } +} + +// newUndeclaredParam creates an UndeclaredValue for an undeclared parameter (query/header/cookie). +// note: parameters don't have SpecLine/SpecCol because they're defined in OpenAPI parameter objects, +// not schema objects. the parameter itself is the issue, not a schema definition. +func newUndeclaredParam(path, name string, value any, paramType string, declaredNames []string, direction Direction) UndeclaredValue { + return UndeclaredValue{ + Path: path, + Name: name, + Value: value, + Type: paramType, + DeclaredProperties: declaredNames, + Direction: direction, + } +} + +// newUndeclaredItem creates an UndeclaredValue for an undeclared array item. +func newUndeclaredItem(path, name string, value any, direction Direction) UndeclaredValue { + return UndeclaredValue{ + Path: path, + Name: name, + Value: TruncateValue(value), + Type: "item", + Direction: direction, + } +} + +// Input contains the parameters for strict validation. +type Input struct { + // Schema is the OpenAPI schema to validate against. + Schema *base.Schema + + // Data is the unmarshalled data to validate (from request/response body). + // Should be the result of json.Unmarshal. + Data any + + // Direction indicates request vs response validation. + // affects readOnly/writeOnly and Set-Cookie handling. + Direction Direction + + // Options contains validation configuration including ignore paths. + Options *config.ValidationOptions + + // BasePath is the prefix for generated instance paths. + // typically "$.body" for bodies, "$.query" for query params, etc. + BasePath string + + // Version is the OpenAPI version (3.0 or 3.1). + // affects nullable handling in schema matching. + Version float32 +} + +// Result contains the output of strict validation. +type Result struct { + Valid bool + + // UndeclaredValues lists all undeclared properties, parameters, + // headers, or cookies found during validation. + UndeclaredValues []UndeclaredValue +} + +// cycleKey uniquely identifies a schema at a specific validation path. +// Using a struct key avoids string allocation in the hot path. +type cycleKey struct { + path string + schemaKey string +} + +// traversalContext tracks state during schema traversal to detect cycles +// and limit recursion depth. +type traversalContext struct { + // visited tracks schemas already being validated at specific paths. + // key combines instance path + schema key to allow same schema at different paths. + visited map[cycleKey]bool + + // depth tracks current recursion depth for safety limits. + depth int + + // maxDepth is the maximum allowed recursion depth (default: 100). + maxDepth int + + // direction indicates request vs response for readOnly/writeOnly. + direction Direction + + // ignorePaths are compiled regex patterns for paths to skip. + ignorePaths []*regexp.Regexp + + // path is the current instance path being validated. + path string +} + +// newTraversalContext creates a new context for schema traversal. +func newTraversalContext(direction Direction, ignorePaths []*regexp.Regexp, basePath string) *traversalContext { + return &traversalContext{ + visited: make(map[cycleKey]bool), + depth: 0, + maxDepth: 100, + direction: direction, + ignorePaths: ignorePaths, + path: basePath, + } +} + +// withPath returns a new context with an updated path. +func (c *traversalContext) withPath(path string) *traversalContext { + return &traversalContext{ + visited: c.visited, + depth: c.depth + 1, + maxDepth: c.maxDepth, + direction: c.direction, + ignorePaths: c.ignorePaths, + path: path, + } +} + +// shouldIgnore checks if the current path matches any ignore pattern. +func (c *traversalContext) shouldIgnore() bool { + for _, pattern := range c.ignorePaths { + if pattern.MatchString(c.path) { + return true + } + } + return false +} + +// exceedsDepth checks if we've exceeded the maximum recursion depth. +func (c *traversalContext) exceedsDepth() bool { + return c.depth > c.maxDepth +} + +// checkAndMarkVisited checks if a schema has been visited at the current path. +// Returns true if this is a cycle (already visited), false otherwise. +// If not a cycle, marks the schema as visited. +func (c *traversalContext) checkAndMarkVisited(schemaKey string) bool { + key := cycleKey{path: c.path, schemaKey: schemaKey} + if c.visited[key] { + return true // Cycle detected + } + c.visited[key] = true + return false +} + +// Validator performs strict property validation against OpenAPI schemas. +// It detects any properties present in data that are not explicitly +// declared in the schema, regardless of additionalProperties settings. +// +// A new Validator should be created for each validation call to ensure +// isolation of internal caches and render contexts. +// +// # Cycle Detection +// +// The Validator uses two distinct cycle detection mechanisms: +// +// 1. traversalContext.visited: Tracks visited (path, schemaKey) combinations +// during the main validation traversal. This prevents infinite recursion +// when the same schema is encountered at the same instance path. The key +// uses a struct for zero-allocation lookups in the hot path. +// +// 2. renderCtx (InlineRenderContext): libopenapi's built-in cycle detection +// for schema rendering. This is used when compiling schemas for oneOf/anyOf +// variant matching. It operates at the schema reference level rather than +// instance path level. +// +// These mechanisms serve complementary purposes: visited tracks data traversal +// while renderCtx tracks schema resolution during compilation. +type Validator struct { + options *config.ValidationOptions + logger *slog.Logger + + // localCache stores compiled schemas for reuse within this validation. + // ley is schema hash (as string for map compatibility), value is compiled jsonschema. + localCache map[string]*jsonschema.Schema + + // patternCache stores compiled regex patterns for patternProperties. + // key is the pattern string, value is the compiled regex. + patternCache map[string]*regexp.Regexp + + // renderCtx is used for safe schema rendering with cycle detection. + // see Validator doc comment for how this relates to traversalContext.visited. + renderCtx *base.InlineRenderContext + + // version is the OpenAPI version (3.0 or 3.1). + version float32 + + // compiledIgnorePaths are the pre-compiled regex patterns. + compiledIgnorePaths []*regexp.Regexp +} + +// NewValidator creates a fresh validator for a single validation call. +// The validator should not be reused across concurrent requests. +// Uses the logger from options if available, otherwise logging is silent. +func NewValidator(options *config.ValidationOptions, version float32) *Validator { + var logger *slog.Logger + if options != nil && options.Logger != nil { + logger = options.Logger + } else { + // create a no-op logger that discards all output + logger = slog.New(discardHandler{}) + } + + v := &Validator{ + options: options, + logger: logger, + localCache: make(map[string]*jsonschema.Schema), + patternCache: make(map[string]*regexp.Regexp), + renderCtx: base.NewInlineRenderContext(), + version: version, + } + + if options != nil { + v.compiledIgnorePaths = compileIgnorePaths(options.StrictIgnorePaths) + } + + return v +} + +// discardHandler is a slog.Handler that discards all log records. +type discardHandler struct{} + +func (discardHandler) Enabled(context.Context, slog.Level) bool { return false } +func (discardHandler) Handle(context.Context, slog.Record) error { return nil } +func (d discardHandler) WithAttrs([]slog.Attr) slog.Handler { return d } +func (d discardHandler) WithGroup(string) slog.Handler { return d } + +// matchesIgnorePath checks if a path matches any pre-compiled ignore pattern. +func (v *Validator) matchesIgnorePath(path string) bool { + for _, pattern := range v.compiledIgnorePaths { + if pattern.MatchString(path) { + return true + } + } + return false +} + +// getCompiledPattern returns a cached compiled regex for a pattern string. +// If the pattern is not in the cache, it compiles and caches it. +// Returns nil if the pattern is invalid. +func (v *Validator) getCompiledPattern(pattern string) *regexp.Regexp { + if cached, ok := v.patternCache[pattern]; ok { + return cached + } + + compiled, err := regexp.Compile(pattern) + if err != nil { + return nil + } + + v.patternCache[pattern] = compiled + return compiled +} + +// getSchemaKey returns a unique key for a schema used in cycle detection. +// Uses the schema's low-level hash if available, otherwise the pointer address. +func (v *Validator) getSchemaKey(schema *base.Schema) string { + if schema == nil { + return "" + } + if low := schema.GoLow(); low != nil { + hash := low.Hash() + return fmt.Sprintf("%x", hash) // uint64 hash as hex string + } + // fallback to pointer address for inline schemas without low-level info + return fmt.Sprintf("%p", schema) +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/strict/utils.go b/vendor/github.com/pb33f/libopenapi-validator/strict/utils.go new file mode 100644 index 000000000..5bdc4c962 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/strict/utils.go @@ -0,0 +1,161 @@ +// Copyright 2023-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package strict + +import ( + "regexp" + "strconv" + "strings" +) + +// buildPath creates an instance path by appending a property name to a base path. +// Property names containing dots or brackets use bracket notation for clarity. +// +// Examples: +// - buildPath("$.body", "name") → "$.body.name" +// - buildPath("$.body", "a.b") → "$.body['a.b']" +// - buildPath("$.body", "x[0]") → "$.body['x[0]']" +func buildPath(base, propName string) string { + if needsBracketNotation(propName) { + return base + "['" + propName + "']" + } + return base + "." + propName +} + +// needsBracketNotation returns true if a property name contains characters +// that require bracket notation (dots, brackets). +func needsBracketNotation(name string) bool { + return strings.ContainsAny(name, ".[]") +} + +// buildArrayPath creates an instance path for an array element. +func buildArrayPath(base string, index int) string { + return base + "[" + strconv.Itoa(index) + "]" +} + +// compileIgnorePaths converts glob patterns to compiled regular expressions. +// Supports: +// - * matches single path segment (no dots or brackets) +// - ** matches any depth (zero or more segments) +// - [*] matches any array index +// - \* escapes literal asterisk +// - \*\* escapes literal double-asterisk +func compileIgnorePaths(patterns []string) []*regexp.Regexp { + if len(patterns) == 0 { + return nil + } + + compiled := make([]*regexp.Regexp, 0, len(patterns)) + for _, pattern := range patterns { + re := compilePattern(pattern) + if re != nil { + compiled = append(compiled, re) + } + } + return compiled +} + +// compilePattern converts a single glob pattern to a regular expression. +func compilePattern(pattern string) *regexp.Regexp { + if pattern == "" { + return nil + } + + var b strings.Builder + b.WriteString("^") + + i := 0 + for i < len(pattern) { + c := pattern[i] + + // handle escape sequences + if c == '\\' && i+1 < len(pattern) { + next := pattern[i+1] + if next == '*' { + // check for escaped ** + if i+2 < len(pattern) && pattern[i+2] == '\\' && i+3 < len(pattern) && pattern[i+3] == '*' { + b.WriteString(`\*\*`) + i += 4 + continue + } + // escaped single * + b.WriteString(`\*`) + i += 2 + continue + } + // other escape - include literally + b.WriteString(regexp.QuoteMeta(string(next))) + i += 2 + continue + } + + // handle ** (any depth) + if c == '*' && i+1 < len(pattern) && pattern[i+1] == '*' { + // ** matches any sequence of segments including none + b.WriteString(`.*`) + i += 2 + continue + } + + // handle single * (single segment) + if c == '*' { + // * matches single path segment (no dots or brackets) + b.WriteString(`[^.\[\]]+`) + i++ + continue + } + + // handle [*] (any array index) + if c == '[' && i+2 < len(pattern) && pattern[i+1] == '*' && pattern[i+2] == ']' { + b.WriteString(`\[\d+\]`) + i += 3 + continue + } + + // handle special regex characters + switch c { + case '.', '[', ']', '(', ')', '{', '}', '+', '?', '^', '$', '|': + b.WriteString(`\`) + b.WriteByte(c) + default: + b.WriteByte(c) + } + i++ + } + + b.WriteString("$") + + re, _ := regexp.Compile(b.String()) + return re +} + +// TruncateValue creates a display-friendly version of a value. +// Long strings are truncated, complex objects show type info. +// This is exported for use in error messages. +func TruncateValue(v any) any { + switch val := v.(type) { + case string: + if len(val) > 50 { + return val[:47] + "..." + } + return val + case map[string]any: + if len(val) > 3 { + return "{...}" + } + return val + case []any: + if len(val) > 3 { + return "[...]" + } + return val + default: + return v + } +} + +// truncateValue is an internal alias for TruncateValue. +func truncateValue(v any) any { + return TruncateValue(v) +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/strict/validator.go b/vendor/github.com/pb33f/libopenapi-validator/strict/validator.go new file mode 100644 index 000000000..1917eff91 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/strict/validator.go @@ -0,0 +1,258 @@ +// Copyright 2023-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package strict + +import ( + "net/http" + "strings" + + "github.com/pb33f/libopenapi/datamodel/high/base" + v3 "github.com/pb33f/libopenapi/datamodel/high/v3" + + "github.com/pb33f/libopenapi-validator/config" +) + +// Validate performs strict validation on the input data against the schema. +// This is the main entry point for body validation. +// +// It detects undeclared properties even when additionalProperties: true +// would normally allow them. This is useful for API governance scenarios +// where you want to ensure clients only send explicitly documented properties. +func (v *Validator) Validate(input Input) *Result { + result := &Result{Valid: true} + + if input.Schema == nil || input.Data == nil { + return result + } + + ctx := newTraversalContext(input.Direction, v.compiledIgnorePaths, input.BasePath) + + undeclared := v.validateValue(ctx, input.Schema, input.Data) + + if len(undeclared) > 0 { + result.Valid = false + result.UndeclaredValues = undeclared + } + + return result +} + +// ValidateBody is a convenience method for validating request/response bodies. +func ValidateBody(schema *base.Schema, data any, direction Direction, options *config.ValidationOptions, version float32) *Result { + v := NewValidator(options, version) + return v.Validate(Input{ + Schema: schema, + Data: data, + Direction: direction, + Options: options, + BasePath: "$.body", + Version: version, + }) +} + +// ValidateQueryParams checks for undeclared query parameters in an HTTP request. +// It compares the query parameters present in the request against those +// declared in the OpenAPI operation. +func ValidateQueryParams( + request *http.Request, + declaredParams []*v3.Parameter, + options *config.ValidationOptions, +) []UndeclaredValue { + if request == nil || options == nil || !options.StrictMode { + return nil + } + + v := NewValidator(options, 3.2) + + // build set of declared query params (case-sensitive) + declared := make(map[string]bool) + for _, param := range declaredParams { + if param.In == "query" { + declared[param.Name] = true + } + } + + var undeclared []UndeclaredValue + + // check each query parameter in the request + for paramName := range request.URL.Query() { + if !declared[paramName] { + // build path using proper notation for special characters + path := buildPath("$.query", paramName) + if v.matchesIgnorePath(path) { + continue + } + + undeclared = append(undeclared, + newUndeclaredParam(path, paramName, request.URL.Query().Get(paramName), "query", getParamNames(declaredParams, "query"), DirectionRequest)) + } + } + + return undeclared +} + +// ValidateRequestHeaders checks for undeclared headers in an HTTP request. +// Header names are normalized to lowercase for path generation and pattern matching. +// +// The securityHeaders parameter contains header names that are valid due to security +// scheme definitions (e.g., "X-API-Key" for apiKey schemes, "Authorization" for +// http/oauth2/openIdConnect schemes). These headers are considered "declared" even +// though they don't appear in the operation's parameters array. +func ValidateRequestHeaders( + headers http.Header, + declaredParams []*v3.Parameter, + securityHeaders []string, + options *config.ValidationOptions, +) []UndeclaredValue { + if headers == nil || options == nil || !options.StrictMode { + return nil + } + + v := NewValidator(options, 3.2) + + // build set of declared headers (case-insensitive) + declared := make(map[string]bool) + for _, param := range declaredParams { + if param.In == "header" { + declared[strings.ToLower(param.Name)] = true + } + } + + // add security scheme headers (case-insensitive) + for _, h := range securityHeaders { + declared[strings.ToLower(h)] = true + } + + var undeclared []UndeclaredValue + + // check each header + for headerName := range headers { + lowerName := strings.ToLower(headerName) + + // skip if declared (via parameters or security schemes) + if declared[lowerName] { + continue + } + + // skip if in ignored headers list + if v.isHeaderIgnored(headerName, DirectionRequest) { + continue + } + + // build path using lowercase name for case-insensitive pattern matching + path := buildPath("$.headers", lowerName) + if v.matchesIgnorePath(path) { + continue + } + + undeclared = append(undeclared, + newUndeclaredParam(path, headerName, headers.Get(headerName), "header", getParamNames(declaredParams, "header"), DirectionRequest)) + } + + return undeclared +} + +// ValidateCookies checks for undeclared cookies in an HTTP request. +func ValidateCookies( + request *http.Request, + declaredParams []*v3.Parameter, + options *config.ValidationOptions, +) []UndeclaredValue { + if request == nil || options == nil || !options.StrictMode { + return nil + } + + v := NewValidator(options, 3.2) + + // build set of declared cookies + declared := make(map[string]bool) + for _, param := range declaredParams { + if param.In == "cookie" { + declared[param.Name] = true + } + } + + var undeclared []UndeclaredValue + + // check each cookie in the request + for _, cookie := range request.Cookies() { + if !declared[cookie.Name] { + // build path using proper notation for special characters + path := buildPath("$.cookies", cookie.Name) + if v.matchesIgnorePath(path) { + continue + } + + undeclared = append(undeclared, + newUndeclaredParam(path, cookie.Name, cookie.Value, "cookie", getParamNames(declaredParams, "cookie"), DirectionRequest)) + } + } + + return undeclared +} + +// getParamNames extracts parameter names of a specific type. +func getParamNames(params []*v3.Parameter, paramType string) []string { + var names []string + for _, param := range params { + if param.In == paramType { + names = append(names, param.Name) + } + } + return names +} + +// ValidateResponseHeaders checks for undeclared headers in an HTTP response. +// Uses the declared headers from the OpenAPI response object. +// Header names are normalized to lowercase for path generation and pattern matching. +func ValidateResponseHeaders( + headers http.Header, + declaredHeaders *map[string]*v3.Header, + options *config.ValidationOptions, +) []UndeclaredValue { + if headers == nil || options == nil || !options.StrictMode { + return nil + } + + v := NewValidator(options, 3.2) + + // build set of declared headers (case-insensitive) + declared := make(map[string]bool) + if declaredHeaders != nil { + for name := range *declaredHeaders { + declared[strings.ToLower(name)] = true + } + } + + var undeclared []UndeclaredValue + var declaredNames []string + if declaredHeaders != nil { + for name := range *declaredHeaders { + declaredNames = append(declaredNames, name) + } + } + + for headerName := range headers { + lowerName := strings.ToLower(headerName) + + if declared[lowerName] { + continue + } + + if v.isHeaderIgnored(headerName, DirectionResponse) { + continue + } + + // build path using lowercase name for case-insensitive pattern matching + path := buildPath("$.headers", lowerName) + if v.matchesIgnorePath(path) { + continue + } + + undeclared = append(undeclared, + newUndeclaredParam(path, headerName, headers.Get(headerName), "header", declaredNames, DirectionResponse)) + } + + return undeclared +} diff --git a/vendor/github.com/pb33f/libopenapi-validator/validator.go b/vendor/github.com/pb33f/libopenapi-validator/validator.go new file mode 100644 index 000000000..bc6e39cf8 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi-validator/validator.go @@ -0,0 +1,565 @@ +// Copyright 2023-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package validator + +import ( + "fmt" + "net/http" + "sort" + "sync" + + "github.com/pb33f/libopenapi" + "github.com/pb33f/libopenapi/datamodel/high/base" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" + + v3 "github.com/pb33f/libopenapi/datamodel/high/v3" + + "github.com/pb33f/libopenapi-validator/cache" + "github.com/pb33f/libopenapi-validator/config" + "github.com/pb33f/libopenapi-validator/errors" + "github.com/pb33f/libopenapi-validator/helpers" + "github.com/pb33f/libopenapi-validator/parameters" + "github.com/pb33f/libopenapi-validator/paths" + "github.com/pb33f/libopenapi-validator/requests" + "github.com/pb33f/libopenapi-validator/responses" + "github.com/pb33f/libopenapi-validator/schema_validation" +) + +// Validator provides a coarse grained interface for validating an OpenAPI 3+ documents. +// There are three primary use-cases for validation +// +// Validating *http.Request objects against and OpenAPI 3+ document +// Validating *http.Response objects against an OpenAPI 3+ document +// Validating an OpenAPI 3+ document against the OpenAPI 3+ specification +type Validator interface { + // ValidateHttpRequest will validate an *http.Request object against an OpenAPI 3+ document. + // The path, query, cookie and header parameters and request body are validated. + ValidateHttpRequest(request *http.Request) (bool, []*errors.ValidationError) + // ValidateHttpRequestSync will validate an *http.Request object against an OpenAPI 3+ document synchronously and without spawning any goroutines. + // The path, query, cookie and header parameters and request body are validated. + ValidateHttpRequestSync(request *http.Request) (bool, []*errors.ValidationError) + + // ValidateHttpRequestWithPathItem will validate an *http.Request object against an OpenAPI 3+ document. + // The path, query, cookie and header parameters and request body are validated. + ValidateHttpRequestWithPathItem(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) + + // ValidateHttpRequestSyncWithPathItem will validate an *http.Request object against an OpenAPI 3+ document synchronously and without spawning any goroutines. + // The path, query, cookie and header parameters and request body are validated. + ValidateHttpRequestSyncWithPathItem(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) + + // ValidateHttpResponse will an *http.Response object against an OpenAPI 3+ document. + // The response body is validated. The request is only used to extract the correct response from the spec. + ValidateHttpResponse(request *http.Request, response *http.Response) (bool, []*errors.ValidationError) + + // ValidateHttpRequestResponse will validate both the *http.Request and *http.Response objects against an OpenAPI 3+ document. + // The path, query, cookie and header parameters and request and response body are validated. + ValidateHttpRequestResponse(request *http.Request, response *http.Response) (bool, []*errors.ValidationError) + + // ValidateDocument will validate an OpenAPI 3+ document against the 3.0 or 3.1 OpenAPI 3+ specification + ValidateDocument() (bool, []*errors.ValidationError) + + // GetParameterValidator will return a parameters.ParameterValidator instance used to validate parameters + GetParameterValidator() parameters.ParameterValidator + + // GetRequestBodyValidator will return a parameters.RequestBodyValidator instance used to validate request bodies + GetRequestBodyValidator() requests.RequestBodyValidator + + // GetResponseBodyValidator will return a parameters.ResponseBodyValidator instance used to validate response bodies + GetResponseBodyValidator() responses.ResponseBodyValidator + + // SetDocument will set the OpenAPI 3+ document to be validated + SetDocument(document libopenapi.Document) +} + +// NewValidator will create a new Validator from an OpenAPI 3+ document +func NewValidator(document libopenapi.Document, opts ...config.Option) (Validator, []error) { + m, errs := document.BuildV3Model() + if errs != nil { + return nil, []error{errs} + } + v := NewValidatorFromV3Model(&m.Model, opts...) + v.(*validator).document = document + return v, nil +} + +// NewValidatorFromV3Model will create a new Validator from an OpenAPI Model +func NewValidatorFromV3Model(m *v3.Document, opts ...config.Option) Validator { + options := config.NewValidationOptions(opts...) + + v := &validator{options: options, v3Model: m} + + // create a new parameter validator + v.paramValidator = parameters.NewParameterValidator(m, config.WithExistingOpts(options)) + + // create aq new request body validator + v.requestValidator = requests.NewRequestBodyValidator(m, config.WithExistingOpts(options)) + + // create a response body validator + v.responseValidator = responses.NewResponseBodyValidator(m, config.WithExistingOpts(options)) + + // warm the schema caches by pre-compiling all schemas in the document + // (warmSchemaCaches checks for nil cache and skips if disabled) + warmSchemaCaches(m, options) + + return v +} + +func (v *validator) SetDocument(document libopenapi.Document) { + v.document = document +} + +func (v *validator) GetParameterValidator() parameters.ParameterValidator { + return v.paramValidator +} + +func (v *validator) GetRequestBodyValidator() requests.RequestBodyValidator { + return v.requestValidator +} + +func (v *validator) GetResponseBodyValidator() responses.ResponseBodyValidator { + return v.responseValidator +} + +func (v *validator) ValidateDocument() (bool, []*errors.ValidationError) { + if v.document == nil { + return false, []*errors.ValidationError{{ + ValidationType: helpers.DocumentValidation, + ValidationSubType: helpers.ValidationMissing, + Message: "Document is not set", + Reason: "The document cannot be validated as it is not set", + SpecLine: 1, + SpecCol: 1, + HowToFix: "Set the document via `SetDocument` before validating", + }} + } + var validationOpts []config.Option + if v.options != nil { + validationOpts = append(validationOpts, config.WithRegexEngine(v.options.RegexEngine)) + } + return schema_validation.ValidateOpenAPIDocument(v.document, validationOpts...) +} + +func (v *validator) ValidateHttpResponse( + request *http.Request, + response *http.Response, +) (bool, []*errors.ValidationError) { + var pathItem *v3.PathItem + var pathValue string + var errs []*errors.ValidationError + + pathItem, errs, pathValue = paths.FindPath(request, v.v3Model, v.options.RegexCache) + if pathItem == nil || errs != nil { + return false, errs + } + + responseBodyValidator := v.responseValidator + + // validate response + _, responseErrors := responseBodyValidator.ValidateResponseBodyWithPathItem(request, response, pathItem, pathValue) + + if len(responseErrors) > 0 { + return false, responseErrors + } + return true, nil +} + +func (v *validator) ValidateHttpRequestResponse( + request *http.Request, + response *http.Response, +) (bool, []*errors.ValidationError) { + var pathItem *v3.PathItem + var pathValue string + var errs []*errors.ValidationError + + pathItem, errs, pathValue = paths.FindPath(request, v.v3Model, v.options.RegexCache) + if pathItem == nil || errs != nil { + return false, errs + } + + responseBodyValidator := v.responseValidator + + // validate request and response + _, requestErrors := v.ValidateHttpRequestWithPathItem(request, pathItem, pathValue) + _, responseErrors := responseBodyValidator.ValidateResponseBodyWithPathItem(request, response, pathItem, pathValue) + + if len(requestErrors) > 0 || len(responseErrors) > 0 { + return false, append(requestErrors, responseErrors...) + } + return true, nil +} + +func (v *validator) ValidateHttpRequest(request *http.Request) (bool, []*errors.ValidationError) { + pathItem, errs, foundPath := paths.FindPath(request, v.v3Model, v.options.RegexCache) + if len(errs) > 0 { + return false, errs + } + return v.ValidateHttpRequestWithPathItem(request, pathItem, foundPath) +} + +func (v *validator) ValidateHttpRequestWithPathItem(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) { + // create a new parameter validator + paramValidator := v.paramValidator + + // create a new request body validator + reqBodyValidator := v.requestValidator + + // create some channels to handle async validation + doneChan := make(chan struct{}) + errChan := make(chan []*errors.ValidationError) + controlChan := make(chan struct{}) + + // async param validation function. + parameterValidationFunc := func(control chan struct{}, errorChan chan []*errors.ValidationError) { + paramErrs := make(chan []*errors.ValidationError) + paramControlChan := make(chan struct{}) + paramFunctionControlChan := make(chan struct{}) + var paramValidationErrors []*errors.ValidationError + + validations := []validationFunction{ + paramValidator.ValidatePathParamsWithPathItem, + paramValidator.ValidateCookieParamsWithPathItem, + paramValidator.ValidateHeaderParamsWithPathItem, + paramValidator.ValidateQueryParamsWithPathItem, + paramValidator.ValidateSecurityWithPathItem, + } + + // listen for validation errors on parameters. everything will run async. + paramListener := func(control chan struct{}, errorChan chan []*errors.ValidationError) { + completedValidations := 0 + for { + select { + case vErrs := <-errorChan: + paramValidationErrors = append(paramValidationErrors, vErrs...) + case <-control: + completedValidations++ + if completedValidations == len(validations) { + paramFunctionControlChan <- struct{}{} + return + } + } + } + } + + validateParamFunction := func( + control chan struct{}, + errorChan chan []*errors.ValidationError, + validatorFunc validationFunction, + ) { + valid, pErrs := validatorFunc(request, pathItem, pathValue) + if !valid { + errorChan <- pErrs + } + control <- struct{}{} + } + go paramListener(paramControlChan, paramErrs) + for i := range validations { + go validateParamFunction(paramControlChan, paramErrs, validations[i]) + } + + // wait for all the validations to complete + <-paramFunctionControlChan + if len(paramValidationErrors) > 0 { + errorChan <- paramValidationErrors + } + + // let runValidation know we are done with this part. + controlChan <- struct{}{} + } + + requestBodyValidationFunc := func(control chan struct{}, errorChan chan []*errors.ValidationError) { + valid, pErrs := reqBodyValidator.ValidateRequestBodyWithPathItem(request, pathItem, pathValue) + if !valid { + errorChan <- pErrs + } + control <- struct{}{} + } + + // build async functions + asyncFunctions := []validationFunctionAsync{ + parameterValidationFunc, + requestBodyValidationFunc, + } + + var validationErrors []*errors.ValidationError + + // sit and wait for everything to report back. + go runValidation(controlChan, doneChan, errChan, &validationErrors, len(asyncFunctions)) + + // run async functions + for i := range asyncFunctions { + go asyncFunctions[i](controlChan, errChan) + } + + // wait for all the validations to complete + <-doneChan + + // sort errors for deterministic ordering (async validation can return errors in any order) + sortValidationErrors(validationErrors) + + return len(validationErrors) == 0, validationErrors +} + +func (v *validator) ValidateHttpRequestSync(request *http.Request) (bool, []*errors.ValidationError) { + pathItem, errs, foundPath := paths.FindPath(request, v.v3Model, v.options.RegexCache) + if len(errs) > 0 { + return false, errs + } + return v.ValidateHttpRequestSyncWithPathItem(request, pathItem, foundPath) +} + +func (v *validator) ValidateHttpRequestSyncWithPathItem(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) { + // create a new parameter validator + paramValidator := v.paramValidator + + // create a new request body validator + reqBodyValidator := v.requestValidator + + validationErrors := make([]*errors.ValidationError, 0) + + paramValidationErrors := make([]*errors.ValidationError, 0) + for _, validateFunc := range []validationFunction{ + paramValidator.ValidatePathParamsWithPathItem, + paramValidator.ValidateCookieParamsWithPathItem, + paramValidator.ValidateHeaderParamsWithPathItem, + paramValidator.ValidateQueryParamsWithPathItem, + paramValidator.ValidateSecurityWithPathItem, + } { + valid, pErrs := validateFunc(request, pathItem, pathValue) + if !valid { + paramValidationErrors = append(paramValidationErrors, pErrs...) + } + } + + valid, pErrs := reqBodyValidator.ValidateRequestBodyWithPathItem(request, pathItem, pathValue) + if !valid { + paramValidationErrors = append(paramValidationErrors, pErrs...) + } + + validationErrors = append(validationErrors, paramValidationErrors...) + return len(validationErrors) == 0, validationErrors +} + +type validator struct { + options *config.ValidationOptions + v3Model *v3.Document + document libopenapi.Document + paramValidator parameters.ParameterValidator + requestValidator requests.RequestBodyValidator + responseValidator responses.ResponseBodyValidator +} + +func runValidation(control, doneChan chan struct{}, + errorChan chan []*errors.ValidationError, + validationErrors *[]*errors.ValidationError, + total int, +) { + var validationLock sync.Mutex + completedValidations := 0 + for { + select { + case vErrs := <-errorChan: + validationLock.Lock() + *validationErrors = append(*validationErrors, vErrs...) + validationLock.Unlock() + case <-control: + completedValidations++ + if completedValidations == total { + doneChan <- struct{}{} + return + } + } + } +} + +type ( + validationFunction func(request *http.Request, pathItem *v3.PathItem, pathValue string) (bool, []*errors.ValidationError) + validationFunctionAsync func(control chan struct{}, errorChan chan []*errors.ValidationError) +) + +// sortValidationErrors sorts validation errors for deterministic ordering. +// Errors are sorted by validation type first, then by message. +func sortValidationErrors(errs []*errors.ValidationError) { + sort.Slice(errs, func(i, j int) bool { + if errs[i].ValidationType != errs[j].ValidationType { + return errs[i].ValidationType < errs[j].ValidationType + } + return errs[i].Message < errs[j].Message + }) +} + +// warmSchemaCaches pre-compiles all schemas in the OpenAPI document and stores them in the validator caches. +// This frontloads the compilation cost so that runtime validation doesn't need to compile schemas. +func warmSchemaCaches( + doc *v3.Document, + options *config.ValidationOptions, +) { + // Skip warming if cache is nil (explicitly disabled via WithSchemaCache(nil)) + if doc == nil || doc.Paths == nil || doc.Paths.PathItems == nil || options.SchemaCache == nil { + return + } + + schemaCache := options.SchemaCache + + // Walk through all paths and operations + for pathPair := doc.Paths.PathItems.First(); pathPair != nil; pathPair = pathPair.Next() { + pathItem := pathPair.Value() + + // Get all operations for this path (handles all HTTP methods including OpenAPI 3.2+ extensions) + operations := pathItem.GetOperations() + if operations == nil { + continue + } + + for opPair := operations.First(); opPair != nil; opPair = opPair.Next() { + operation := opPair.Value() + if operation == nil { + continue + } + + // Warm request body schemas + if operation.RequestBody != nil && operation.RequestBody.Content != nil { + for contentPair := operation.RequestBody.Content.First(); contentPair != nil; contentPair = contentPair.Next() { + mediaType := contentPair.Value() + if mediaType.Schema != nil { + warmMediaTypeSchema(mediaType, schemaCache, options) + } + } + } + + // Warm response body schemas + if operation.Responses != nil { + // Warm status code responses + if operation.Responses.Codes != nil { + for codePair := operation.Responses.Codes.First(); codePair != nil; codePair = codePair.Next() { + response := codePair.Value() + if response != nil && response.Content != nil { + for contentPair := response.Content.First(); contentPair != nil; contentPair = contentPair.Next() { + mediaType := contentPair.Value() + if mediaType.Schema != nil { + warmMediaTypeSchema(mediaType, schemaCache, options) + } + } + } + } + } + + // Warm default response schemas + if operation.Responses.Default != nil && operation.Responses.Default.Content != nil { + for contentPair := operation.Responses.Default.Content.First(); contentPair != nil; contentPair = contentPair.Next() { + mediaType := contentPair.Value() + if mediaType.Schema != nil { + warmMediaTypeSchema(mediaType, schemaCache, options) + } + } + } + } + + // Warm parameter schemas + if operation.Parameters != nil { + for _, param := range operation.Parameters { + if param != nil { + warmParameterSchema(param, schemaCache, options) + } + } + } + } + + // Warm path-level parameters + if pathItem.Parameters != nil { + for _, param := range pathItem.Parameters { + if param != nil { + warmParameterSchema(param, schemaCache, options) + } + } + } + } +} + +// warmMediaTypeSchema warms the cache for a media type schema +func warmMediaTypeSchema(mediaType *v3.MediaType, schemaCache cache.SchemaCache, options *config.ValidationOptions) { + if mediaType != nil && mediaType.Schema != nil { + hash := mediaType.GoLow().Schema.Value.Hash() + + if _, exists := schemaCache.Load(hash); !exists { + schema := mediaType.Schema.Schema() + if schema != nil { + renderCtx := base.NewInlineRenderContext() + renderedInline, _ := schema.RenderInlineWithContext(renderCtx) + referenceSchema := string(renderedInline) + renderedJSON, _ := utils.ConvertYAMLtoJSON(renderedInline) + if len(renderedInline) > 0 { + compiledSchema, _ := helpers.NewCompiledSchema(fmt.Sprintf("%x", hash), renderedJSON, options) + + // Pre-parse YAML node for error reporting (avoids re-parsing on each error) + var renderedNode yaml.Node + _ = yaml.Unmarshal(renderedInline, &renderedNode) + + schemaCache.Store(hash, &cache.SchemaCacheEntry{ + Schema: schema, + RenderedInline: renderedInline, + ReferenceSchema: referenceSchema, + RenderedJSON: renderedJSON, + CompiledSchema: compiledSchema, + RenderedNode: &renderedNode, + }) + } + } + } + } +} + +// warmParameterSchema warms the cache for a parameter schema +func warmParameterSchema(param *v3.Parameter, schemaCache cache.SchemaCache, options *config.ValidationOptions) { + if param != nil { + var schema *base.Schema + var hash uint64 + + // Parameters can have schemas in two places: schema property or content property + if param.Schema != nil { + schema = param.Schema.Schema() + if schema != nil { + hash = param.GoLow().Schema.Value.Hash() + } + } else if param.Content != nil { + // Check content for schema + for contentPair := param.Content.First(); contentPair != nil; contentPair = contentPair.Next() { + mediaType := contentPair.Value() + if mediaType.Schema != nil { + schema = mediaType.Schema.Schema() + if schema != nil { + hash = mediaType.GoLow().Schema.Value.Hash() + } + break // Only process first content type + } + } + } + + if schema != nil { + if _, exists := schemaCache.Load(hash); !exists { + renderCtx := base.NewInlineRenderContext() + renderedInline, _ := schema.RenderInlineWithContext(renderCtx) + referenceSchema := string(renderedInline) + renderedJSON, _ := utils.ConvertYAMLtoJSON(renderedInline) + if len(renderedInline) > 0 { + compiledSchema, _ := helpers.NewCompiledSchema(fmt.Sprintf("%x", hash), renderedJSON, options) + + // Pre-parse YAML node for error reporting (avoids re-parsing on each error) + var renderedNode yaml.Node + _ = yaml.Unmarshal(renderedInline, &renderedNode) + + // Store in cache using the shared SchemaCache type + schemaCache.Store(hash, &cache.SchemaCacheEntry{ + Schema: schema, + RenderedInline: renderedInline, + ReferenceSchema: referenceSchema, + RenderedJSON: renderedJSON, + CompiledSchema: compiledSchema, + RenderedNode: &renderedNode, + }) + } + } + } + } +} diff --git a/vendor/github.com/pb33f/libopenapi/.gitignore b/vendor/github.com/pb33f/libopenapi/.gitignore new file mode 100644 index 000000000..069611e7d --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/.gitignore @@ -0,0 +1,3 @@ +test-operation.yaml +.idea/ +*.iml \ No newline at end of file diff --git a/vendor/github.com/pb33f/libopenapi/LICENSE b/vendor/github.com/pb33f/libopenapi/LICENSE new file mode 100644 index 000000000..ef140da38 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022-2025 Princess Beef Heavy Industries, LLC / Dave Shanley + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/pb33f/libopenapi/README.md b/vendor/github.com/pb33f/libopenapi/README.md new file mode 100644 index 000000000..ec9efa6a7 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/README.md @@ -0,0 +1,149 @@ +

+ libopenapi +

+ +# libopenapi - enterprise grade OpenAPI tools for golang. + + +![Pipeline](https://github.com/pb33f/libopenapi/workflows/Build/badge.svg) +[![GoReportCard](https://goreportcard.com/badge/github.com/pb33f/libopenapi)](https://goreportcard.com/report/github.com/pb33f/libopenapi) +[![codecov](https://codecov.io/gh/pb33f/libopenapi/branch/main/graph/badge.svg?)](https://codecov.io/gh/pb33f/libopenapi) +[![discord](https://img.shields.io/discord/923258363540815912)](https://discord.gg/x7VACVuEGP) +[![Docs](https://img.shields.io/badge/godoc-reference-5fafd7)](https://pkg.go.dev/github.com/pb33f/libopenapi) + +libopenapi has full support for OpenAPI 3, 3.1 and 3.2. It can handle the largest and most +complex specifications you can think of. + +--- + +## Sponsors & users +If your company is using `libopenapi`, please considering [supporting this project](https://github.com/sponsors/daveshanley), +like our _very kind_ sponsors: + + + + + speakeasy' + + + +[Speakeasy](https://speakeasy.com/editor?utm_source=libopenapi+repo&utm_medium=github+sponsorship) + + + + + scalar' + + + +[scalar](https://scalar.com) + + + + + apideck' + + + +[apideck](https://apideck.com) + +--- + +## Come chat with us + +Need help? Have a question? Want to share your work? [Join our discord](https://discord.gg/x7VACVuEGP) and +come say hi! + +## Check out the `libopenapi-validator` + +Need to validate requests, responses, parameters or schemas? Use the new +[libopenapi-validator](https://github.com/pb33f/libopenapi-validator) module. + +## Documentation + +See all the documentation at https://pb33f.io/libopenapi/ + +- [Installing libopenapi](https://pb33f.io/libopenapi/installing/) +- [Using OpenAPI](https://pb33f.io/libopenapi/openapi/) +- [Using Swagger](https://pb33f.io/libopenapi/swagger/) +- [The Data Model](https://pb33f.io/libopenapi/model/) +- [Validation](https://pb33f.io/libopenapi/validation/) +- [Modifying / Mutating the OpenAPI Model](https://pb33f.io/libopenapi/modifying/) +- [Mocking / Creating Examples](https://pb33f.io/libopenapi/mocks/) +- [Using Vendor Extensions](https://pb33f.io/libopenapi/extensions/) +- [The Index](https://pb33f.io/libopenapi/index/) +- [The Resolver](https://pb33f.io/libopenapi/resolver/) +- [The Rolodex](https://pb33f.io/libopenapi/rolodex/) +- [Circular References](https://pb33f.io/libopenapi/circular-references/) +- [Bundling Specs](https://pb33f.io/libopenapi/bundling/) +- [What Changed / Diff Engine](https://pb33f.io/libopenapi/what-changed/) +- [Overlays](https://pb33f.io/libopenapi/overlays/) +- [FAQ](https://pb33f.io/libopenapi/faq/) +- [About libopenapi](https://pb33f.io/libopenapi/about/) +--- + +### Quick-start tutorial + +👀 **Get rolling fast using `libopenapi` with the +[Parsing OpenAPI files using go](https://quobix.com/articles/parsing-openapi-using-go/)** guide 👀 + +Or, follow these steps and see something in a few seconds. + +#### Step 1: Grab the petstore + +```bash +curl https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/_archive_/schemas/v3.0/pass/petstore.yaml > petstorev3.json +``` + +#### Step 2: Grab libopenapi + +```bash +go get github.com/pb33f/libopenapi +``` + +#### Step 3: Parse the petstore using libopenapi + +Copy and paste this code into a `main.go` file. + +```go +package main + +import ( + "fmt" + "os" + "github.com/pb33f/libopenapi" +) + +func main() { + petstore, _ := os.ReadFile("petstorev3.json") + document, err := libopenapi.NewDocument(petstore) + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } + docModel, err := document.BuildV3Model() + if err != nil { + panic(fmt.Sprintf("cannot create v3 model from document: %e", err)) + } + + // The following fails after the first iteration + for schemaName, schema := range docModel.Model.Components.Schemas.FromOldest() { + if schema.Schema().Properties != nil { + fmt.Printf("Schema '%s' has %d properties\n", schemaName, schema.Schema().Properties.Len()) + } + } +} +``` + +Run it, which should print out: + +```bash +Schema 'Pet' has 3 properties +Schema 'Error' has 2 properties +``` + + +> Read the full docs at [https://pb33f.io/libopenapi/](https://pb33f.io/libopenapi/) + +--- + +Logo gopher is modified, originally from [egonelbre](https://github.com/egonelbre/gophers) diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/constants.go b/vendor/github.com/pb33f/libopenapi/datamodel/constants.go new file mode 100644 index 000000000..bff632b55 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/constants.go @@ -0,0 +1,70 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +// Package datamodel contains two sets of models, high and low. +// +// The low-level (or plumbing) models are designed to capture every single detail about specification, including +// all lines, columns, positions, tags, comments and essentially everything you would ever want to know. +// Positions of every key, value and meta-data that is lost when blindly un-marshaling JSON/YAML into a struct. +// +// The high model (porcelain) is a much simpler representation of the low model, keys are simple strings and indices +// are numbers. When developing consumers of the model, the high model is really what you want to use instead of the +// low model, it's much easier to navigate and is designed for easy consumption. +// +// The high model requires the low model to be built. Every high model has a 'GoLow' method that allows the consumer +// to 'drop down' from the porcelain API to the plumbing API, which gives instant access to everything low. +package datamodel + +import ( + _ "embed" +) + +// Constants used by utilities to determine the version of OpenAPI that we're referring to. +const ( + // OAS2 represents Swagger Documents + OAS2 = "oas2" + + // OAS3 represents OpenAPI 3.0+ Documents + OAS3 = "oas3" + + // OAS31 represents OpenAPI 3.1 Documents + OAS31 = "oas3_1" + + // OAS32 represents OpenAPI 3.2+ Documents + OAS32 = "oas3_2" +) + +// OpenAPI3SchemaData is an embedded version of the OpenAPI 3 Schema +// +//go:embed schemas/oas3-schema.json +var OpenAPI3SchemaData string // embedded OAS3 schema + +// OpenAPI31SchemaData is an embedded version of the OpenAPI 3.1 Schema +// +//go:embed schemas/oas31-schema.json +var OpenAPI31SchemaData string // embedded OAS31 schema + +//go:embed schemas/oas32-schema.json +var OpenAPI32SchemaData string // embedded OAS32 schema + +// OpenAPI2SchemaData is an embedded version of the OpenAPI 2 (Swagger) Schema +// +//go:embed schemas/swagger2-schema.json +var OpenAPI2SchemaData string // embedded OAS3 schema + +// OAS3_1Format defines documents that can only be version 3.1 +var OAS3_1Format = []string{OAS31} + +var OAS3_2Format = []string{OAS32} + +// OAS3Format defines documents that can only be version 3.0 +var OAS3Format = []string{OAS3} + +// OAS3AllFormat defines documents that compose all 3+ versions +var OAS3AllFormat = []string{OAS3, OAS31, OAS32} + +// OAS2Format defines documents that compose swagger documents (version 2.0) +var OAS2Format = []string{OAS2} + +// AllFormats defines all versions of OpenAPI +var AllFormats = []string{OAS3, OAS31, OAS32, OAS2} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/document_config.go b/vendor/github.com/pb33f/libopenapi/datamodel/document_config.go new file mode 100644 index 000000000..29b7f2e79 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/document_config.go @@ -0,0 +1,216 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package datamodel + +import ( + "io/fs" + "log/slog" + "net/url" + "os" + + "github.com/pb33f/libopenapi/utils" +) + +// PropertyMergeStrategy defines how conflicting properties are handled during reference resolution +type PropertyMergeStrategy int + +const ( + // PreserveLocal means local properties take precedence over referenced properties + PreserveLocal PropertyMergeStrategy = iota + // OverwriteWithRemote means referenced properties overwrite local properties + OverwriteWithRemote + // RejectConflicts means throw error when properties conflict + RejectConflicts +) + +// DocumentConfiguration is used to configure the document creation process. It was added in v0.6.0 to allow +// for more fine-grained control over controls and new features. +// +// The default configuration will set AllowFileReferences to false and AllowRemoteReferences to false, which means +// any non-local (local being the specification, not the file system) references, will be ignored. +type DocumentConfiguration struct { + // The BaseURL will be the root from which relative references will be resolved from if they can't be found locally. + // Make sure it does not point to a file as relative paths will be blindly added to the end of the + // BaseURL's path. + // Schema must be set to "http/https". + BaseURL *url.URL + + // RemoteURLHandler is a function that will be used to retrieve remote documents. If not set, the default + // remote document getter will be used. + // + // The remote handler is only used if the BaseURL is set. If the BaseURL is not set, then the remote handler + // will not be used, as there will be nothing to use it against. + // + // Resolves [#132]: https://github.com/pb33f/libopenapi/issues/132 + RemoteURLHandler utils.RemoteURLHandler + + // If resolving locally, the BasePath will be the root from which relative references will be resolved from. + // It's usually the location of the root specification. + // + // Be warned, setting this value will instruct the rolodex to index EVERY yaml and JSON file it finds from the + // base path. The rolodex will recurse into every directory and pick up everything form this location down. + // + // To avoid sucking in all the files, set the FileFilter to a list of specific files to be included. + BasePath string // set the Base Path for resolving relative references if the spec is exploded. + + // SpecFilePath is the name of the root specification file (usually named "openapi.yaml"). + SpecFilePath string + + // FileFilter is a list of specific files to be included by the rolodex when looking up references. If this value + // is set, then only these specific files will be included. If this value is not set, then all files will be included. + FileFilter []string + + // RemoteFS is a filesystem that will be used to retrieve remote documents. If not set, then the rolodex will + // use its own internal remote filesystem implementation. The RemoteURLHandler will be used to retrieve remote + // documents if it has been set. The default is to use the internal remote filesystem loader. + RemoteFS fs.FS + + // LocalFS is a filesystem that will be used to retrieve local documents. If not set, then the rolodex will + // use its own internal local filesystem implementation. The default is to use the internal local filesystem loader. + LocalFS fs.FS + + // AllowFileReferences will allow the index to locate relative file references. This is disabled by default. + // + // This behavior is now driven by the inclusion of a BasePath. If a BasePath is set, then the + // rolodex will look for relative file references. If no BasePath is set, then the rolodex will not look for + // relative file references. + // + // This value when set, will force the creation of a local file system even when the BasePath has not been set. + // it will suck in and index everything from the current working directory, down... so be warned + // FileFilter should be used to limit the scope of the rolodex. + AllowFileReferences bool + + // SkipExternalRefResolution will skip resolving external $ref references (those not starting with #). + // When enabled, external references will be left as-is during model building. Schema proxies will + // report IsReference()=true and GetReference() will return the ref string, but Schema() will return nil. + // This is useful for code generators that handle external refs via import mappings. + SkipExternalRefResolution bool + + // AllowRemoteReferences will allow the index to lookup remote references. This is disabled by default. + // + // This behavior is now driven by the inclusion of a BaseURL. If a BaseURL is set, then the + // rolodex will look for remote references. If no BaseURL is set, then the rolodex will not look for + // remote references. This value has no effect as of version 0.13.0 and will be removed in a future release. + // + // This value when set, will force the creation of a remote file system even when the BaseURL has not been set. + // it will suck in every http link it finds, and recurse through all references located in each document. + AllowRemoteReferences bool + + // AvoidIndexBuild will avoid building the index. This is disabled by default, only use if you are sure you don't need it. + // This is useful for developers building out models that should be indexed later on. + AvoidIndexBuild bool + + // BypassDocumentCheck will bypass the document check. This is disabled by default. This will allow any document to + // passed in and used. Only enable this when parsing non openapi documents. + BypassDocumentCheck bool + + // IgnorePolymorphicCircularReferences will skip over checking for circular references in polymorphic schemas. + // A polymorphic schema is any schema that is composed other schemas using references via `oneOf`, `anyOf` of `allOf`. + // This is disabled by default, which means polymorphic circular references will be checked. + IgnorePolymorphicCircularReferences bool + + // IgnoreArrayCircularReferences will skip over checking for circular references in arrays. Sometimes a circular + // reference is required to describe a data-shape correctly. Often those shapes are valid circles if the + // type of the schema implementing the loop is an array. An empty array would technically break the loop. + // So if libopenapi is returning circular references for this use case, then this option should be enabled. + // this is disabled by default, which means array circular references will be checked. + IgnoreArrayCircularReferences bool + + // SkipCircularReferenceCheck will skip over checking for circular references. This is disabled by default, which + // means circular references will be checked. This is useful for developers building out models that should be + // indexed later on. + SkipCircularReferenceCheck bool + + // Logger is a structured logger that will be used for logging errors and warnings. If not set, a default logger + // will be used, set to the Error level. + Logger *slog.Logger + + // ExtractRefsSequentially will extract all references sequentially, which means the index will look up references + // as it finds them, vs looking up everything asynchronously. + // This is a more thorough way of building the index, but it's slower. It's required building a document + // to be bundled. + ExtractRefsSequentially bool + + // ExcludeExtensionReferences will prevent the indexing of any $ref pointers buried under extensions. + // defaults to false (which means extensions will be included) + ExcludeExtensionRefs bool + + // BundleInlineRefs controls whether local component references are inlined during bundling. + // When false (default): Local refs like #/components/schemas/Pet are preserved + // When true: Local refs are also inlined (may break discriminator mappings) + // + // Note: This setting can be overridden per-call using BundleInlineConfig.InlineLocalRefs + // when calling bundler.BundleBytesWithConfig() + // + // Circular references are ALWAYS preserved regardless of this setting. + BundleInlineRefs bool + + // RecomposeRefs is used by the bundler module. If set to true, all references will be composed into the root document. + // The bundler will attempt to create a single document, with all references moved to the `components` section. Any names used + // will be kept, any collisions will be resolved by appending a number to the name + RecomposeRefs bool + + // UseSchemaQuickHash will use a quick hash to determine if a schema is the same as another schema if its a reference. + // This is important when a root / entry document does not have a components/schemas node, and schemas are defined in + // external documents. Enabling this will allow the what-changed module to perform deeper schema reference checks. + /// + // -- IMPORTANT -- + /// + // Enabling this (default is false) will stop changes from being detected if a schema is circular. + // As identified in https://github.com/pb33f/libopenapi/pull/441 + // + // In the edge case where you have circular references in your root / entry components/schemas and you also + // want changes in them to be picked up, then you should not enable this. + // + // If your schemas are in external documents, and you want changes in them to be picked up, then you should enable this. + // + // By default schemas as references are ignored and only the root / entry document's components/schemas are + // used to determine changes. + UseSchemaQuickHash bool + + // AllowUnknownExtensionContentDetection will enable content detection for remote URLs that don't have + // a known file extension. When enabled, libopenapi will fetch the first 1-2KB of unknown URLs to determine + // if they contain valid JSON or YAML content. This is disabled by default for security and performance. + // + // If disabled, URLs without recognized extensions (.yaml, .yml, .json) will be rejected. + // If enabled, unknown URLs will be fetched and analyzed for JSON/YAML content with retry logic. + AllowUnknownExtensionContentDetection bool + + // TransformSiblingRefs enables OpenAPI 3.1/JSON Schema Draft 2020-12 compliance for sibling refs. + // When enabled, schemas with $ref and additional properties like: + // title: MySchema + // $ref: '#/components/schemas/Base' + // Will be transformed to: + // allOf: + // - title: MySchema + // - $ref: '#/components/schemas/Base' + // This is enabled by default to ensure OpenAPI 3.1+ compliance. + TransformSiblingRefs bool + + // MergeReferencedProperties enables enhanced reference resolution that preserves local properties + // when resolving references. For example: + // $ref: '#/components/schemas/Address' + // example: + // street: '123 Main St' + // city: 'Somewhere' + // The example will be preserved during reference resolution instead of being overwritten. + MergeReferencedProperties bool + + // PropertyMergeStrategy determines how conflicting properties are handled during reference resolution. + // - PreserveLocal: Local properties take precedence over referenced properties + // - OverwriteWithRemote: Referenced properties overwrite local properties + // - RejectConflicts: Throw error when properties conflict + PropertyMergeStrategy PropertyMergeStrategy +} + +func NewDocumentConfiguration() *DocumentConfiguration { + return &DocumentConfiguration{ + Logger: slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ + Level: slog.LevelError, + })), + TransformSiblingRefs: true, // enable openapi 3.1 compliance by default + MergeReferencedProperties: true, // enable enhanced resolution by default + PropertyMergeStrategy: PreserveLocal, // local properties take precedence + } +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/base/base.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/base.go new file mode 100644 index 000000000..064015db5 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/base.go @@ -0,0 +1,11 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +// Package base contains shared high-level models that are used between both versions 2 and 3 of OpenAPI. +// These models are consistent across both specifications, except for the Schema. +// +// OpenAPI 3 contains all the same properties that an OpenAPI 2 specification does, and more. The choice +// to not duplicate the schemas is to allow a graceful degradation pattern to be used. Schemas are the most complex +// beats, particularly when polymorphism is used. By re-using the same superset Schema across versions, we can ensure +// that all the latest features are collected, without damaging backwards compatibility. +package base diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/base/contact.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/contact.go new file mode 100644 index 000000000..bf9d533d0 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/contact.go @@ -0,0 +1,53 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// Contact represents a high-level representation of the Contact definitions found at +// +// v2 - https://swagger.io/specification/v2/#contactObject +// v3 - https://spec.openapis.org/oas/v3.1.0#contact-object +type Contact struct { + Name string `json:"name,omitempty" yaml:"name,omitempty"` + URL string `json:"url,omitempty" yaml:"url,omitempty"` + Email string `json:"email,omitempty" yaml:"email,omitempty"` + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *low.Contact `json:"-" yaml:"-"` // low-level representation +} + +// NewContact will create a new Contact instance using a low-level Contact +func NewContact(contact *low.Contact) *Contact { + c := new(Contact) + c.low = contact + c.URL = contact.URL.Value + c.Name = contact.Name.Value + c.Email = contact.Email.Value + c.Extensions = high.ExtractExtensions(contact.Extensions) + return c +} + +// GoLow returns the low level Contact object used to create the high-level one. +func (c *Contact) GoLow() *low.Contact { + return c.low +} + +// GoLowUntyped will return the low-level Contact instance that was used to create the high-level one, with no type +func (c *Contact) GoLowUntyped() any { + return c.low +} + +func (c *Contact) Render() ([]byte, error) { + return yaml.Marshal(c) +} + +func (c *Contact) MarshalYAML() (interface{}, error) { + nb := high.NewNodeBuilder(c, c.low) + return nb.Render(), nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/base/discriminator.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/discriminator.go new file mode 100644 index 000000000..df2ebd656 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/discriminator.go @@ -0,0 +1,59 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/datamodel/low" + lowBase "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// Discriminator is only used by OpenAPI 3+ documents, it represents a polymorphic discriminator used for schemas +// +// When request bodies or response payloads may be one of a number of different schemas, a discriminator object can be +// used to aid in serialization, deserialization, and validation. The discriminator is a specific object in a schema +// which is used to inform the consumer of the document of an alternative schema based on the value associated with it. +// +// When using the discriminator, inline schemas will not be considered. +// +// v3 - https://spec.openapis.org/oas/v3.1.0#discriminator-object +type Discriminator struct { + PropertyName string `json:"propertyName,omitempty" yaml:"propertyName,omitempty"` + Mapping *orderedmap.Map[string, string] `json:"mapping,omitempty" yaml:"mapping,omitempty"` + DefaultMapping string `json:"defaultMapping,omitempty" yaml:"defaultMapping,omitempty"` // OpenAPI 3.2+ defaultMapping for fallback schema + low *lowBase.Discriminator +} + +// NewDiscriminator will create a new high-level Discriminator from a low-level one. +func NewDiscriminator(disc *lowBase.Discriminator) *Discriminator { + d := new(Discriminator) + d.low = disc + d.PropertyName = disc.PropertyName.Value + d.Mapping = low.FromReferenceMap(disc.Mapping.Value) + d.DefaultMapping = disc.DefaultMapping.Value + return d +} + +// GoLow returns the low-level Discriminator used to build the high-level one. +func (d *Discriminator) GoLow() *lowBase.Discriminator { + return d.low +} + +// GoLowUntyped will return the low-level Discriminator instance that was used to create the high-level one, with no type +func (d *Discriminator) GoLowUntyped() any { + return d.low +} + +// Render will return a YAML representation of the Discriminator object as a byte slice. +func (d *Discriminator) Render() ([]byte, error) { + return yaml.Marshal(d) +} + +// MarshalYAML will create a ready to render YAML representation of the Discriminator object. +func (d *Discriminator) MarshalYAML() (interface{}, error) { + nb := high.NewNodeBuilder(d, d.low) + return nb.Render(), nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/base/dynamic_value.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/dynamic_value.go new file mode 100644 index 000000000..40473b65b --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/dynamic_value.go @@ -0,0 +1,122 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "reflect" + + "github.com/pb33f/libopenapi/datamodel/high" + "go.yaml.in/yaml/v4" +) + +// DynamicValue is used to hold multiple possible types for a schema property. There are two values, a left +// value (A) and a right value (B). The A and B values represent different types that a property can have, +// not necessarily different OpenAPI versions. +// +// For example: +// - additionalProperties: A = SchemaProxy (when it's a schema), B = bool (when it's a boolean) +// - items: A = SchemaProxy (when it's a schema), B = bool (when it's a boolean in 3.1) +// - type: A = string (single type), B = []string (multiple types in 3.1) +// - exclusiveMinimum: A = bool (in 3.0), B = float64 (in 3.1) +// +// The N value indicates which value is set (0 = A, 1 == B), preventing the need to check both values. +type DynamicValue[A any, B any] struct { + N int // 0 == A, 1 == B + A A + B B + inline bool + renderCtx any // Context for inline rendering (typed as any to avoid import cycles) +} + +// IsA will return true if the 'A' or left value is set. +func (d *DynamicValue[A, B]) IsA() bool { + return d.N == 0 +} + +// IsB will return true if the 'B' or right value is set. +func (d *DynamicValue[A, B]) IsB() bool { + return d.N == 1 +} + +func (d *DynamicValue[A, B]) Render() ([]byte, error) { + d.inline = false + return yaml.Marshal(d) +} + +func (d *DynamicValue[A, B]) RenderInline() ([]byte, error) { + d.inline = true + return yaml.Marshal(d) +} + +// MarshalYAML will create a ready to render YAML representation of the DynamicValue object. +func (d *DynamicValue[A, B]) MarshalYAML() (interface{}, error) { + // this is a custom renderer, we can't use the NodeBuilder out of the gate. + var n yaml.Node + var err error + var value any + + if d.IsA() { + value = d.A + } + if d.IsB() { + value = d.B + } + to := reflect.TypeOf(value) + switch to.Kind() { + case reflect.Ptr: + if d.inline { + // prefer context-aware method when context is available + if d.renderCtx != nil { + if r, ok := value.(high.RenderableInlineWithContext); ok { + return r.MarshalYAMLInlineWithContext(d.renderCtx) + } + } + // fall back to context-less method + if r, ok := value.(high.RenderableInline); ok { + return r.MarshalYAMLInline() + } else { + _ = n.Encode(value) + } + } else { + if r, ok := value.(high.Renderable); ok { + return r.MarshalYAML() + } else { + _ = n.Encode(value) + } + } + case reflect.Bool: + _ = n.Encode(value.(bool)) + case reflect.Int: + _ = n.Encode(value.(int)) + case reflect.String: + _ = n.Encode(value.(string)) + case reflect.Int64: + _ = n.Encode(value.(int64)) + case reflect.Float64: + _ = n.Encode(value.(float64)) + case reflect.Float32: + _ = n.Encode(value.(float32)) + case reflect.Int32: + _ = n.Encode(value.(int32)) + + } + return &n, err +} + +// MarshalYAMLInline will create a ready to render YAML representation of the DynamicValue object. The +// references will be inlined instead of kept as references. +func (d *DynamicValue[A, B]) MarshalYAMLInline() (interface{}, error) { + d.inline = true + d.renderCtx = nil + return d.MarshalYAML() +} + +// MarshalYAMLInlineWithContext will create a ready to render YAML representation of the DynamicValue object. +// The references will be inlined and the provided context will be passed through to nested schemas. +// The ctx parameter should be *InlineRenderContext but is typed as any to avoid import cycles. +func (d *DynamicValue[A, B]) MarshalYAMLInlineWithContext(ctx any) (interface{}, error) { + d.inline = true + d.renderCtx = ctx + return d.MarshalYAML() +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/base/example.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/example.go new file mode 100644 index 000000000..fecae5b4f --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/example.go @@ -0,0 +1,166 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "context" + "encoding/json" + + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/datamodel/low" + lowBase "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// buildLowExample builds a low-level Example from a resolved YAML node. +func buildLowExample(node *yaml.Node, idx *index.SpecIndex) (*lowBase.Example, error) { + var ex lowBase.Example + low.BuildModel(node, &ex) + ex.Build(context.Background(), nil, node, idx) + return &ex, nil +} + +// Example represents a high-level Example object as defined by OpenAPI 3+ +// +// v3 - https://spec.openapis.org/oas/v3.1.0#example-object +type Example struct { + Reference string `json:"$ref,omitempty" yaml:"$ref,omitempty"` + Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Value *yaml.Node `json:"value,omitempty" yaml:"value,omitempty"` + ExternalValue string `json:"externalValue,omitempty" yaml:"externalValue,omitempty"` + DataValue *yaml.Node `json:"dataValue,omitempty" yaml:"dataValue,omitempty"` // OpenAPI 3.2+ dataValue field + SerializedValue string `json:"serializedValue,omitempty" yaml:"serializedValue,omitempty"` // OpenAPI 3.2+ serializedValue field + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *lowBase.Example +} + +// NewExample will create a new instance of an Example, using a low-level Example. +func NewExample(example *lowBase.Example) *Example { + e := new(Example) + e.low = example + e.Summary = example.Summary.Value + e.Description = example.Description.Value + e.Value = example.Value.Value + e.ExternalValue = example.ExternalValue.Value + e.DataValue = example.DataValue.Value + e.SerializedValue = example.SerializedValue.Value + e.Extensions = high.ExtractExtensions(example.Extensions) + return e +} + +// GoLow will return the low-level Example used to build the high level one. +func (e *Example) GoLow() *lowBase.Example { + if e == nil { + return nil + } + return e.low +} + +// GoLowUntyped will return the low-level Example instance that was used to create the high-level one, with no type +func (e *Example) GoLowUntyped() any { + if e == nil { + return nil + } + return e.low +} + +// IsReference returns true if this Example is a reference to another Example definition. +func (e *Example) IsReference() bool { + return e.Reference != "" +} + +// GetReference returns the reference string if this is a reference Example. +func (e *Example) GetReference() string { + return e.Reference +} + +// Render will return a YAML representation of the Example object as a byte slice. +func (e *Example) Render() ([]byte, error) { + return yaml.Marshal(e) +} + +// MarshalYAML will create a ready to render YAML representation of the Example object. +func (e *Example) MarshalYAML() (interface{}, error) { + // Handle reference-only example + if e.Reference != "" { + return utils.CreateRefNode(e.Reference), nil + } + nb := high.NewNodeBuilder(e, e.low) + return nb.Render(), nil +} + +// MarshalYAMLInline will create a ready to render YAML representation of the Example object, +// with all references resolved inline. +func (e *Example) MarshalYAMLInline() (interface{}, error) { + // reference-only objects render as $ref nodes + if e.Reference != "" { + return utils.CreateRefNode(e.Reference), nil + } + + // resolve external reference if present + if e.low != nil { + // buildLowExample never returns an error, so we can ignore it + rendered, err := high.RenderExternalRef(e.low, buildLowExample, NewExample) + if rendered != nil || err != nil { + return rendered, err + } + } + + return high.RenderInline(e, e.low) +} + +// MarshalYAMLInlineWithContext will create a ready to render YAML representation of the Example object, +// resolving any references inline where possible. Uses the provided context for cycle detection. +// The ctx parameter should be *InlineRenderContext but is typed as any to satisfy the +// high.RenderableInlineWithContext interface without import cycles. +func (e *Example) MarshalYAMLInlineWithContext(ctx any) (interface{}, error) { + if e.Reference != "" { + return utils.CreateRefNode(e.Reference), nil + } + + // resolve external reference if present + if e.low != nil { + // buildLowExample never returns an error, so we can ignore it + rendered, _ := high.RenderExternalRefWithContext(e.low, buildLowExample, NewExample, ctx) + if rendered != nil { + return rendered, nil + } + } + + return high.RenderInlineWithContext(e, e.low, ctx) +} + +// CreateExampleRef creates an Example that renders as a $ref to another example definition. +// This is useful when building OpenAPI specs programmatically and you want to reference +// an example defined in components/examples rather than inlining the full definition. +// +// Example: +// +// ex := base.CreateExampleRef("#/components/examples/UserExample") +// +// Renders as: +// +// $ref: '#/components/examples/UserExample' +func CreateExampleRef(ref string) *Example { + return &Example{Reference: ref} +} + +// MarshalJSON will marshal this into a JSON byte slice +func (e *Example) MarshalJSON() ([]byte, error) { + var g map[string]any + nb := high.NewNodeBuilder(e, e.low) + r := nb.Render() + r.Decode(&g) + return json.Marshal(g) +} + +// ExtractExamples will convert a low-level example map, into a high level one that is simple to navigate. +// no fidelity is lost, everything is still available via GoLow() +func ExtractExamples(elements *orderedmap.Map[low.KeyReference[string], low.ValueReference[*lowBase.Example]]) *orderedmap.Map[string, *Example] { + return low.FromReferenceMapWithFunc(elements, NewExample) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/base/external_doc.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/external_doc.go new file mode 100644 index 000000000..c5603bb88 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/external_doc.go @@ -0,0 +1,63 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// ExternalDoc represents a high-level External Documentation object as defined by OpenAPI 2 and 3 +// +// Allows referencing an external resource for extended documentation. +// +// v2 - https://swagger.io/specification/v2/#externalDocumentationObject +// v3 - https://spec.openapis.org/oas/v3.1.0#external-documentation-object +type ExternalDoc struct { + Description string `json:"description,omitempty" yaml:"description,omitempty"` + URL string `json:"url,omitempty" yaml:"url,omitempty"` + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *low.ExternalDoc +} + +// NewExternalDoc will create a new high-level External Documentation object from a low-level one. +func NewExternalDoc(extDoc *low.ExternalDoc) *ExternalDoc { + d := new(ExternalDoc) + d.low = extDoc + if !extDoc.Description.IsEmpty() { + d.Description = extDoc.Description.Value + } + if !extDoc.URL.IsEmpty() { + d.URL = extDoc.URL.Value + } + d.Extensions = high.ExtractExtensions(extDoc.Extensions) + return d +} + +// GoLow returns the low-level ExternalDoc instance used to create the high-level one. +func (e *ExternalDoc) GoLow() *low.ExternalDoc { + return e.low +} + +// GoLowUntyped will return the low-level ExternalDoc instance that was used to create the high-level one, with no type +func (e *ExternalDoc) GoLowUntyped() any { + return e.low +} + +func (e *ExternalDoc) GetExtensions() *orderedmap.Map[string, *yaml.Node] { + return e.Extensions +} + +// Render will return a YAML representation of the ExternalDoc object as a byte slice. +func (e *ExternalDoc) Render() ([]byte, error) { + return yaml.Marshal(e) +} + +// MarshalYAML will create a ready to render YAML representation of the ExternalDoc object. +func (e *ExternalDoc) MarshalYAML() (interface{}, error) { + nb := high.NewNodeBuilder(e, e.low) + return nb.Render(), nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/base/info.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/info.go new file mode 100644 index 000000000..2555af6e5 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/info.go @@ -0,0 +1,82 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// Info represents a high-level Info object as defined by both OpenAPI 2 and OpenAPI 3. +// +// The object provides metadata about the API. The metadata MAY be used by the clients if needed, and MAY be presented +// in editing or documentation generation tools for convenience. +// +// v2 - https://swagger.io/specification/v2/#infoObject +// v3 - https://spec.openapis.org/oas/v3.1.0#info-object +type Info struct { + Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + TermsOfService string `json:"termsOfService,omitempty" yaml:"termsOfService,omitempty"` + Contact *Contact `json:"contact,omitempty" yaml:"contact,omitempty"` + License *License `json:"license,omitempty" yaml:"license,omitempty"` + Version string `json:"version,omitempty" yaml:"version,omitempty"` + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *low.Info +} + +// NewInfo will create a new high-level Info instance from a low-level one. +func NewInfo(info *low.Info) *Info { + i := new(Info) + i.low = info + if !info.Title.IsEmpty() { + i.Title = info.Title.Value + } + if !info.Summary.IsEmpty() { + i.Summary = info.Summary.Value + } + if !info.Description.IsEmpty() { + i.Description = info.Description.Value + } + if !info.TermsOfService.IsEmpty() { + i.TermsOfService = info.TermsOfService.Value + } + if !info.Contact.IsEmpty() { + i.Contact = NewContact(info.Contact.Value) + } + if !info.License.IsEmpty() { + i.License = NewLicense(info.License.Value) + } + if !info.Version.IsEmpty() { + i.Version = info.Version.Value + } + if orderedmap.Len(info.Extensions) > 0 { + i.Extensions = high.ExtractExtensions(info.Extensions) + } + return i +} + +// GoLow will return the low-level Info instance that was used to create the high-level one. +func (i *Info) GoLow() *low.Info { + return i.low +} + +// GoLowUntyped will return the low-level Info instance that was used to create the high-level one, with no type +func (i *Info) GoLowUntyped() any { + return i.low +} + +// Render will return a YAML representation of the Info object as a byte slice. +func (i *Info) Render() ([]byte, error) { + return yaml.Marshal(i) +} + +// MarshalYAML will create a ready to render YAML representation of the Info object. +func (i *Info) MarshalYAML() (interface{}, error) { + nb := high.NewNodeBuilder(i, i.low) + return nb.Render(), nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/base/license.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/license.go new file mode 100644 index 000000000..a8aea88c6 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/license.go @@ -0,0 +1,61 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// License is a high-level representation of a License object as defined by OpenAPI 2 and OpenAPI 3 +// +// v2 - https://swagger.io/specification/v2/#licenseObject +// v3 - https://spec.openapis.org/oas/v3.1.0#license-object +type License struct { + Name string `json:"name,omitempty" yaml:"name,omitempty"` + URL string `json:"url,omitempty" yaml:"url,omitempty"` + Identifier string `json:"identifier,omitempty" yaml:"identifier,omitempty"` + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *low.License +} + +// NewLicense will create a new high-level License instance from a low-level one. +func NewLicense(license *low.License) *License { + l := new(License) + l.low = license + l.Extensions = high.ExtractExtensions(license.Extensions) + if !license.URL.IsEmpty() { + l.URL = license.URL.Value + } + if !license.Name.IsEmpty() { + l.Name = license.Name.Value + } + if !license.Identifier.IsEmpty() { + l.Identifier = license.Identifier.Value + } + return l +} + +// GoLow will return the low-level License used to create the high-level one. +func (l *License) GoLow() *low.License { + return l.low +} + +// GoLowUntyped will return the low-level License instance that was used to create the high-level one, with no type +func (l *License) GoLowUntyped() any { + return l.low +} + +// Render will return a YAML representation of the License object as a byte slice. +func (l *License) Render() ([]byte, error) { + return yaml.Marshal(l) +} + +// MarshalYAML will create a ready to render YAML representation of the License object. +func (l *License) MarshalYAML() (interface{}, error) { + nb := high.NewNodeBuilder(l, l.low) + return nb.Render(), nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/base/schema.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/schema.go new file mode 100644 index 000000000..5d71e7f84 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/schema.go @@ -0,0 +1,664 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "encoding/json" + + "errors" + + "github.com/pb33f/libopenapi/datamodel/high" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// Schema represents a JSON Schema that support Swagger, OpenAPI 3 and OpenAPI 3.1 +// +// Until 3.1 OpenAPI had a strange relationship with JSON Schema. It's been a super-set/sub-set +// mix, which has been confusing. So, instead of building a bunch of different models, we have compressed +// all variations into a single model that makes it easy to support multiple spec types. +// +// - v2 schema: https://swagger.io/specification/v2/#schemaObject +// - v3 schema: https://swagger.io/specification/#schema-object +// - v3.1 schema: https://spec.openapis.org/oas/v3.1.0#schema-object +type Schema struct { + // 3.1 only, used to define a dialect for this schema, label is '$schema'. + SchemaTypeRef string `json:"$schema,omitempty" yaml:"$schema,omitempty"` + + // In versions 2 and 3.0, this ExclusiveMaximum can only be a boolean. + // In version 3.1, ExclusiveMaximum is a number. + ExclusiveMaximum *DynamicValue[bool, float64] `json:"exclusiveMaximum,omitempty" yaml:"exclusiveMaximum,omitempty"` + + // In versions 2 and 3.0, this ExclusiveMinimum can only be a boolean. + // In version 3.1, ExclusiveMinimum is a number. + ExclusiveMinimum *DynamicValue[bool, float64] `json:"exclusiveMinimum,omitempty" yaml:"exclusiveMinimum,omitempty"` + + // In versions 2 and 3.0, this Type is a single value, so array will only ever have one value + // in version 3.1, Type can be multiple values + Type []string `json:"type,omitempty" yaml:"type,omitempty"` + + // Schemas are resolved on demand using a SchemaProxy + AllOf []*SchemaProxy `json:"allOf,omitempty" yaml:"allOf,omitempty"` + + // Polymorphic Schemas are only available in version 3+ + OneOf []*SchemaProxy `json:"oneOf,omitempty" yaml:"oneOf,omitempty"` + AnyOf []*SchemaProxy `json:"anyOf,omitempty" yaml:"anyOf,omitempty"` + Discriminator *Discriminator `json:"discriminator,omitempty" yaml:"discriminator,omitempty"` + + // in 3.1 examples can be an array (which is recommended) + Examples []*yaml.Node `json:"examples,omitempty" yaml:"examples,omitempty"` + + // in 3.1 prefixItems provides tuple validation support. + PrefixItems []*SchemaProxy `json:"prefixItems,omitempty" yaml:"prefixItems,omitempty"` + + // 3.1 Specific properties + Contains *SchemaProxy `json:"contains,omitempty" yaml:"contains,omitempty"` + MinContains *int64 `json:"minContains,omitempty" yaml:"minContains,omitempty"` + MaxContains *int64 `json:"maxContains,omitempty" yaml:"maxContains,omitempty"` + If *SchemaProxy `json:"if,omitempty" yaml:"if,omitempty"` + Else *SchemaProxy `json:"else,omitempty" yaml:"else,omitempty"` + Then *SchemaProxy `json:"then,omitempty" yaml:"then,omitempty"` + DependentSchemas *orderedmap.Map[string, *SchemaProxy] `json:"dependentSchemas,omitempty" yaml:"dependentSchemas,omitempty"` + DependentRequired *orderedmap.Map[string, []string] `json:"dependentRequired,omitempty" yaml:"dependentRequired,omitempty"` + PatternProperties *orderedmap.Map[string, *SchemaProxy] `json:"patternProperties,omitempty" yaml:"patternProperties,omitempty"` + PropertyNames *SchemaProxy `json:"propertyNames,omitempty" yaml:"propertyNames,omitempty"` + UnevaluatedItems *SchemaProxy `json:"unevaluatedItems,omitempty" yaml:"unevaluatedItems,omitempty"` + + // in 3.1 UnevaluatedProperties can be a Schema or a boolean + // https://github.com/pb33f/libopenapi/issues/118 + UnevaluatedProperties *DynamicValue[*SchemaProxy, bool] `json:"unevaluatedProperties,omitempty" yaml:"unevaluatedProperties,omitempty"` + + // in 3.1 Items can be a Schema or a boolean + Items *DynamicValue[*SchemaProxy, bool] `json:"items,omitempty" yaml:"items,omitempty"` + + // 3.1+ only, JSON Schema 2020-12 $id - declares this schema as a schema resource with a URI identifier + Id string `json:"$id,omitempty" yaml:"$id,omitempty"` + + // 3.1 only, part of the JSON Schema spec provides a way to identify a sub-schema + Anchor string `json:"$anchor,omitempty" yaml:"$anchor,omitempty"` + + // 3.1+ only, JSON Schema 2020-12 dynamic anchor for recursive schema resolution + DynamicAnchor string `json:"$dynamicAnchor,omitempty" yaml:"$dynamicAnchor,omitempty"` + + // 3.1+ only, JSON Schema 2020-12 dynamic reference for recursive schema resolution + DynamicRef string `json:"$dynamicRef,omitempty" yaml:"$dynamicRef,omitempty"` + + // 3.1+ only, JSON Schema 2020-12 $comment - explanatory notes without affecting validation + Comment string `json:"$comment,omitempty" yaml:"$comment,omitempty"` + + // 3.1+ only, JSON Schema 2020-12 contentSchema - describes structure of decoded content + ContentSchema *SchemaProxy `json:"contentSchema,omitempty" yaml:"contentSchema,omitempty"` + + // 3.1+ only, JSON Schema 2020-12 $vocabulary - defines available vocabularies in meta-schemas + Vocabulary *orderedmap.Map[string, bool] `json:"$vocabulary,omitempty" yaml:"$vocabulary,omitempty"` + + // Compatible with all versions + Not *SchemaProxy `json:"not,omitempty" yaml:"not,omitempty"` + Properties *orderedmap.Map[string, *SchemaProxy] `json:"properties,omitempty" yaml:"properties,omitempty"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` + MultipleOf *float64 `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"` + Maximum *float64 `json:"maximum,renderZero,omitempty" yaml:"maximum,renderZero,omitempty"` + Minimum *float64 `json:"minimum,renderZero,omitempty," yaml:"minimum,renderZero,omitempty"` + MaxLength *int64 `json:"maxLength,omitempty" yaml:"maxLength,omitempty"` + MinLength *int64 `json:"minLength,omitempty" yaml:"minLength,omitempty"` + Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"` + Format string `json:"format,omitempty" yaml:"format,omitempty"` + MaxItems *int64 `json:"maxItems,omitempty" yaml:"maxItems,omitempty"` + MinItems *int64 `json:"minItems,omitempty" yaml:"minItems,omitempty"` + UniqueItems *bool `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"` + MaxProperties *int64 `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"` + MinProperties *int64 `json:"minProperties,omitempty" yaml:"minProperties,omitempty"` + Required []string `json:"required,omitempty" yaml:"required,omitempty"` + Enum []*yaml.Node `json:"enum,omitempty" yaml:"enum,omitempty"` + AdditionalProperties *DynamicValue[*SchemaProxy, bool] `json:"additionalProperties,renderZero,omitempty" yaml:"additionalProperties,renderZero,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + ContentEncoding string `json:"contentEncoding,omitempty" yaml:"contentEncoding,omitempty"` + ContentMediaType string `json:"contentMediaType,omitempty" yaml:"contentMediaType,omitempty"` + Default *yaml.Node `json:"default,omitempty" yaml:"default,renderZero,omitempty"` + Const *yaml.Node `json:"const,omitempty" yaml:"const,renderZero,omitempty"` + Nullable *bool `json:"nullable,omitempty" yaml:"nullable,omitempty"` + ReadOnly *bool `json:"readOnly,renderZero,omitempty" yaml:"readOnly,renderZero,omitempty"` // https://github.com/pb33f/libopenapi/issues/30 + WriteOnly *bool `json:"writeOnly,renderZero,omitempty" yaml:"writeOnly,renderZero,omitempty"` // https://github.com/pb33f/libopenapi/issues/30 + XML *XML `json:"xml,omitempty" yaml:"xml,omitempty"` + ExternalDocs *ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` + Example *yaml.Node `json:"example,omitempty" yaml:"example,omitempty"` + Deprecated *bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *base.Schema + + // Parent Proxy refers back to the low level SchemaProxy that is proxying this schema. + ParentProxy *SchemaProxy `json:"-" yaml:"-"` +} + +// NewSchema will create a new high-level schema from a low-level one. +func NewSchema(schema *base.Schema) *Schema { + s := new(Schema) + s.low = schema + s.Title = schema.Title.Value + if !schema.SchemaTypeRef.IsEmpty() { + s.SchemaTypeRef = schema.SchemaTypeRef.Value + } + if !schema.MultipleOf.IsEmpty() { + s.MultipleOf = &schema.MultipleOf.Value + } + if !schema.Maximum.IsEmpty() { + s.Maximum = &schema.Maximum.Value + } + if !schema.Minimum.IsEmpty() { + s.Minimum = &schema.Minimum.Value + } + // if we're dealing with a 3.0 spec using a bool + if !schema.ExclusiveMaximum.IsEmpty() && schema.ExclusiveMaximum.Value.IsA() { + s.ExclusiveMaximum = &DynamicValue[bool, float64]{ + A: schema.ExclusiveMaximum.Value.A, + } + } + // if we're dealing with a 3.1 spec using an int + if !schema.ExclusiveMaximum.IsEmpty() && schema.ExclusiveMaximum.Value.IsB() { + s.ExclusiveMaximum = &DynamicValue[bool, float64]{ + N: 1, + B: schema.ExclusiveMaximum.Value.B, + } + } + // if we're dealing with a 3.0 spec using a bool + if !schema.ExclusiveMinimum.IsEmpty() && schema.ExclusiveMinimum.Value.IsA() { + s.ExclusiveMinimum = &DynamicValue[bool, float64]{ + A: schema.ExclusiveMinimum.Value.A, + } + } + // if we're dealing with a 3.1 spec, using an int + if !schema.ExclusiveMinimum.IsEmpty() && schema.ExclusiveMinimum.Value.IsB() { + s.ExclusiveMinimum = &DynamicValue[bool, float64]{ + N: 1, + B: schema.ExclusiveMinimum.Value.B, + } + } + if !schema.MaxLength.IsEmpty() { + s.MaxLength = &schema.MaxLength.Value + } + if !schema.MinLength.IsEmpty() { + s.MinLength = &schema.MinLength.Value + } + if !schema.MaxItems.IsEmpty() { + s.MaxItems = &schema.MaxItems.Value + } + if !schema.MinItems.IsEmpty() { + s.MinItems = &schema.MinItems.Value + } + if !schema.MaxProperties.IsEmpty() { + s.MaxProperties = &schema.MaxProperties.Value + } + if !schema.MinProperties.IsEmpty() { + s.MinProperties = &schema.MinProperties.Value + } + + if !schema.MaxContains.IsEmpty() { + s.MaxContains = &schema.MaxContains.Value + } + if !schema.MinContains.IsEmpty() { + s.MinContains = &schema.MinContains.Value + } + if !schema.UniqueItems.IsEmpty() { + s.UniqueItems = &schema.UniqueItems.Value + } + if !schema.Contains.IsEmpty() { + s.Contains = NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{ + ValueNode: schema.Contains.ValueNode, + Value: schema.Contains.Value, + }) + } + if !schema.If.IsEmpty() { + s.If = NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{ + ValueNode: schema.If.ValueNode, + Value: schema.If.Value, + }) + } + if !schema.Else.IsEmpty() { + s.Else = NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{ + ValueNode: schema.Else.ValueNode, + Value: schema.Else.Value, + }) + } + if !schema.Then.IsEmpty() { + s.Then = NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{ + ValueNode: schema.Then.ValueNode, + Value: schema.Then.Value, + }) + } + if !schema.PropertyNames.IsEmpty() { + s.PropertyNames = NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{ + ValueNode: schema.PropertyNames.ValueNode, + Value: schema.PropertyNames.Value, + }) + } + if !schema.UnevaluatedItems.IsEmpty() { + s.UnevaluatedItems = NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{ + ValueNode: schema.UnevaluatedItems.ValueNode, + Value: schema.UnevaluatedItems.Value, + }) + } + + var unevaluatedProperties *DynamicValue[*SchemaProxy, bool] + if !schema.UnevaluatedProperties.IsEmpty() { + if schema.UnevaluatedProperties.Value.IsA() { + unevaluatedProperties = &DynamicValue[*SchemaProxy, bool]{ + A: NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{ + ValueNode: schema.UnevaluatedProperties.ValueNode, + Value: schema.UnevaluatedProperties.Value.A, + KeyNode: schema.UnevaluatedProperties.KeyNode, + }), + } + } else { + unevaluatedProperties = &DynamicValue[*SchemaProxy, bool]{N: 1, B: schema.UnevaluatedProperties.Value.B} + } + } + s.UnevaluatedProperties = unevaluatedProperties + + s.Pattern = schema.Pattern.Value + s.Format = schema.Format.Value + + // 3.0 spec is a single value + if !schema.Type.IsEmpty() && schema.Type.Value.IsA() { + s.Type = []string{schema.Type.Value.A} + } + // 3.1 spec may have multiple values + if !schema.Type.IsEmpty() && schema.Type.Value.IsB() { + for i := range schema.Type.Value.B { + s.Type = append(s.Type, schema.Type.Value.B[i].Value) + } + } + + var additionalProperties *DynamicValue[*SchemaProxy, bool] + if !schema.AdditionalProperties.IsEmpty() { + if schema.AdditionalProperties.Value.IsA() { + additionalProperties = &DynamicValue[*SchemaProxy, bool]{ + A: NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{ + ValueNode: schema.AdditionalProperties.ValueNode, + Value: schema.AdditionalProperties.Value.A, + KeyNode: schema.AdditionalProperties.KeyNode, + }), + } + } else { + additionalProperties = &DynamicValue[*SchemaProxy, bool]{N: 1, B: schema.AdditionalProperties.Value.B} + } + } + s.AdditionalProperties = additionalProperties + + s.Description = schema.Description.Value + s.ContentEncoding = schema.ContentEncoding.Value + s.ContentMediaType = schema.ContentMediaType.Value + s.Default = schema.Default.Value + s.Const = schema.Const.Value + if !schema.Nullable.IsEmpty() { + s.Nullable = &schema.Nullable.Value + } + if !schema.ReadOnly.IsEmpty() { + s.ReadOnly = &schema.ReadOnly.Value + } + if !schema.WriteOnly.IsEmpty() { + s.WriteOnly = &schema.WriteOnly.Value + } + if !schema.Deprecated.IsEmpty() { + s.Deprecated = &schema.Deprecated.Value + } + s.Example = schema.Example.Value + if len(schema.Examples.Value) > 0 { + examples := make([]*yaml.Node, len(schema.Examples.Value)) + for i := 0; i < len(schema.Examples.Value); i++ { + examples[i] = schema.Examples.Value[i].Value + } + s.Examples = examples + } + s.Extensions = high.ExtractExtensions(schema.Extensions) + if !schema.Discriminator.IsEmpty() { + s.Discriminator = NewDiscriminator(schema.Discriminator.Value) + } + if !schema.XML.IsEmpty() { + s.XML = NewXML(schema.XML.Value) + } + if !schema.ExternalDocs.IsEmpty() { + s.ExternalDocs = NewExternalDoc(schema.ExternalDocs.Value) + } + var req []string + for i := range schema.Required.Value { + req = append(req, schema.Required.Value[i].Value) + } + s.Required = req + + if !schema.Id.IsEmpty() { + s.Id = schema.Id.Value + } + if !schema.Anchor.IsEmpty() { + s.Anchor = schema.Anchor.Value + } + if !schema.DynamicAnchor.IsEmpty() { + s.DynamicAnchor = schema.DynamicAnchor.Value + } + if !schema.DynamicRef.IsEmpty() { + s.DynamicRef = schema.DynamicRef.Value + } + if !schema.Comment.IsEmpty() { + s.Comment = schema.Comment.Value + } + if !schema.ContentSchema.IsEmpty() { + s.ContentSchema = NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{ + ValueNode: schema.ContentSchema.ValueNode, + Value: schema.ContentSchema.Value, + }) + } + if schema.Vocabulary.Value != nil { + vocabularyMap := orderedmap.New[string, bool]() + for k, v := range schema.Vocabulary.Value.FromOldest() { + vocabularyMap.Set(k.Value, v.Value) + } + s.Vocabulary = vocabularyMap + } + + var enum []*yaml.Node + for i := range schema.Enum.Value { + enum = append(enum, schema.Enum.Value[i].Value) + } + s.Enum = enum + + // async work. + // any polymorphic properties need to be handled in their own threads + // any properties each need to be processed in their own thread. + // we go as fast as we can. + polyCompletedChan := make(chan struct{}) + errChan := make(chan error) + + type buildResult struct { + idx int + s *SchemaProxy + } + + // for every item, build schema async + buildSchema := func(sch lowmodel.ValueReference[*base.SchemaProxy], idx int, bChan chan buildResult) { + n := &lowmodel.NodeReference[*base.SchemaProxy]{ + ValueNode: sch.ValueNode, + Value: sch.Value, + } + n.SetReference(sch.GetReference(), sch.GetReferenceNode()) + + p := NewSchemaProxy(n) + + bChan <- buildResult{idx: idx, s: p} + } + + // schema async + buildOutSchemas := func(schemas []lowmodel.ValueReference[*base.SchemaProxy], items *[]*SchemaProxy, + doneChan chan struct{}, e chan error, + ) { + bChan := make(chan buildResult) + totalSchemas := len(schemas) + for i := range schemas { + go buildSchema(schemas[i], i, bChan) + } + j := 0 + for j < totalSchemas { + r := <-bChan + j++ + (*items)[r.idx] = r.s + } + doneChan <- struct{}{} + } + + // props async + buildProps := func(k lowmodel.KeyReference[string], v lowmodel.ValueReference[*base.SchemaProxy], + props *orderedmap.Map[string, *SchemaProxy], sw int, + ) { + props.Set(k.Value, NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{ + Value: v.Value, + KeyNode: k.KeyNode, + ValueNode: v.ValueNode, + })) + + switch sw { + case 0: + s.Properties = props + case 1: + s.DependentSchemas = props + case 2: + s.PatternProperties = props + } + } + + props := orderedmap.New[string, *SchemaProxy]() + for name, schemaProxy := range schema.Properties.Value.FromOldest() { + buildProps(name, schemaProxy, props, 0) + } + + dependents := orderedmap.New[string, *SchemaProxy]() + for name, schemaProxy := range schema.DependentSchemas.Value.FromOldest() { + buildProps(name, schemaProxy, dependents, 1) + } + + // Handle DependentRequired + if schema.DependentRequired.Value != nil { + depRequired := orderedmap.New[string, []string]() + for prop, requiredProps := range schema.DependentRequired.Value.FromOldest() { + depRequired.Set(prop.Value, requiredProps.Value) + } + s.DependentRequired = depRequired + } + + patternProps := orderedmap.New[string, *SchemaProxy]() + for name, schemaProxy := range schema.PatternProperties.Value.FromOldest() { + buildProps(name, schemaProxy, patternProps, 2) + } + + var allOf []*SchemaProxy + var oneOf []*SchemaProxy + var anyOf []*SchemaProxy + var not *SchemaProxy + var items *DynamicValue[*SchemaProxy, bool] + var prefixItems []*SchemaProxy + + children := 0 + if !schema.AllOf.IsEmpty() { + children++ + allOf = make([]*SchemaProxy, len(schema.AllOf.Value)) + go buildOutSchemas(schema.AllOf.Value, &allOf, polyCompletedChan, errChan) + } + if !schema.AnyOf.IsEmpty() { + children++ + anyOf = make([]*SchemaProxy, len(schema.AnyOf.Value)) + go buildOutSchemas(schema.AnyOf.Value, &anyOf, polyCompletedChan, errChan) + } + if !schema.OneOf.IsEmpty() { + children++ + oneOf = make([]*SchemaProxy, len(schema.OneOf.Value)) + go buildOutSchemas(schema.OneOf.Value, &oneOf, polyCompletedChan, errChan) + } + if !schema.Not.IsEmpty() { + not = NewSchemaProxy(&schema.Not) + } + if !schema.Items.IsEmpty() { + if schema.Items.Value.IsA() { + items = &DynamicValue[*SchemaProxy, bool]{ + A: NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{ + ValueNode: schema.Items.ValueNode, + Value: schema.Items.Value.A, + KeyNode: schema.Items.KeyNode, + }, + ), + } + } else { + items = &DynamicValue[*SchemaProxy, bool]{N: 1, B: schema.Items.Value.B} + } + } + if !schema.PrefixItems.IsEmpty() { + children++ + prefixItems = make([]*SchemaProxy, len(schema.PrefixItems.Value)) + go buildOutSchemas(schema.PrefixItems.Value, &prefixItems, polyCompletedChan, errChan) + } + + completeChildren := 0 + if children > 0 { + allDone: + for { + <-polyCompletedChan + completeChildren++ + if children == completeChildren { + break allDone + } + } + } + s.OneOf = oneOf + s.AnyOf = anyOf + s.AllOf = allOf + s.Items = items + s.PrefixItems = prefixItems + s.Not = not + return s +} + +// GoLow will return the low-level instance of Schema that was used to create the high level one. +func (s *Schema) GoLow() *base.Schema { + return s.low +} + +// GoLowUntyped will return the low-level Schema instance that was used to create the high-level one, with no type +func (s *Schema) GoLowUntyped() any { + return s.low +} + +// Render will return a YAML representation of the Schema object as a byte slice. +func (s *Schema) Render() ([]byte, error) { + return yaml.Marshal(s) +} + +// RenderInlineWithContext will return a YAML representation of the Schema object as a byte slice +// using the provided InlineRenderContext for cycle detection. +// Use this when multiple goroutines may render the same schemas concurrently. +// The ctx parameter should be *InlineRenderContext but is typed as any to avoid import cycles. +func (s *Schema) RenderInlineWithContext(ctx any) ([]byte, error) { + d, err := s.MarshalYAMLInlineWithContext(ctx) + if err != nil { + return nil, err + } + return yaml.Marshal(d) +} + +// RenderInline will return a YAML representation of the Schema object as a byte slice. +// All the $ref values will be inlined, as in resolved in place. +// This method creates a fresh InlineRenderContext internally. +// +// Make sure you don't have any circular references! +func (s *Schema) RenderInline() ([]byte, error) { + ctx := NewInlineRenderContext() + return s.RenderInlineWithContext(ctx) +} + +// MarshalYAML will create a ready to render YAML representation of the Schema object. +func (s *Schema) MarshalYAML() (interface{}, error) { + nb := high.NewNodeBuilder(s, s.low) + + // determine index version + idx := s.GoLow().Index + if idx != nil { + if idx.GetConfig().SpecInfo != nil { + nb.Version = idx.GetConfig().SpecInfo.VersionNumeric + } + } + return nb.Render(), nil +} + +// MarshalJSON will create a ready to render JSON representation of the Schema object. +func (s *Schema) MarshalJSON() ([]byte, error) { + nb := high.NewNodeBuilder(s, s.low) + + // determine index version + idx := s.GoLow().Index + if idx != nil { + if idx.GetConfig().SpecInfo != nil { + nb.Version = idx.GetConfig().SpecInfo.VersionNumeric + } + } + // render node + node := nb.Render() + var renderedJSON map[string]interface{} + + // marshal into struct + _ = node.Decode(&renderedJSON) + + // return JSON bytes + return json.Marshal(renderedJSON) +} + +// MarshalYAMLInlineWithContext will render out the Schema pointer as YAML using the provided +// InlineRenderContext for cycle detection. All refs will be inlined fully. +// Use this when multiple goroutines may render the same schemas concurrently. +// The ctx parameter should be *InlineRenderContext but is typed as any to satisfy the +// high.RenderableInlineWithContext interface without import cycles. +func (s *Schema) MarshalYAMLInlineWithContext(ctx any) (interface{}, error) { + // ensure we have a valid render context; create default bundle mode context if nil. + // this ensures backward compatibility where nil context = bundle mode behavior. + renderCtx, ok := ctx.(*InlineRenderContext) + if !ok || renderCtx == nil { + renderCtx = NewInlineRenderContext() + ctx = renderCtx + } + + // determine if we should preserve discriminator refs based on rendering mode. + // in validation mode, we need to fully inline all refs for the JSON schema compiler. + // in bundle mode (default), we preserve discriminator refs for mapping compatibility. + if s.Discriminator != nil && renderCtx.Mode != RenderingModeValidation { + // mark oneOf/anyOf refs as preserved in the context (not on the SchemaProxy). + // this avoids mutating shared state and prevents race conditions. + for _, sp := range s.OneOf { + if sp != nil && sp.IsReference() { + renderCtx.MarkRefAsPreserved(sp.GetReference()) + } + } + for _, sp := range s.AnyOf { + if sp != nil && sp.IsReference() { + renderCtx.MarkRefAsPreserved(sp.GetReference()) + } + } + } + + nb := high.NewNodeBuilder(s, s.low) + nb.Resolve = true + nb.RenderContext = ctx + // determine index version + idx := s.GoLow().Index + if idx != nil { + if idx.GetConfig().SpecInfo != nil { + nb.Version = idx.GetConfig().SpecInfo.VersionNumeric + } + } + return nb.Render(), errors.Join(nb.Errors...) +} + +// MarshalYAMLInline will render out the Schema pointer as YAML, and all refs will be inlined fully. +// This method creates a fresh InlineRenderContext internally. +func (s *Schema) MarshalYAMLInline() (interface{}, error) { + ctx := NewInlineRenderContext() + return s.MarshalYAMLInlineWithContext(ctx) +} + +// MarshalJSONInline will render out the Schema pointer as JSON, and all refs will be inlined fully +func (s *Schema) MarshalJSONInline() ([]byte, error) { + nb := high.NewNodeBuilder(s, s.low) + nb.Resolve = true + // determine index version + idx := s.GoLow().Index + if idx != nil { + if idx.GetConfig().SpecInfo != nil { + nb.Version = idx.GetConfig().SpecInfo.VersionNumeric + } + } + // render node + node := nb.Render() + var renderedJSON map[string]interface{} + + // marshal into struct + _ = node.Decode(&renderedJSON) + + // return JSON bytes + return json.Marshal(renderedJSON) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/base/schema_proxy.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/schema_proxy.go new file mode 100644 index 000000000..00cad3f78 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/schema_proxy.go @@ -0,0 +1,580 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "errors" + "fmt" + "net/url" + "path/filepath" + "strings" + "sync" + "sync/atomic" + + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// inlineRenderingTracker tracks schemas during inline rendering to prevent infinite recursion. +// Uses sync.Map for lock-free concurrent access - each goroutine works on different keys, +// so sync.Map's internal sharding reduces contention compared to a single mutex. +var inlineRenderingTracker sync.Map + +// bundlingModeCount tracks the number of active bundling operations. +// Uses reference counting to support concurrent BundleDocument calls safely. +// +// NOTE: This is process-wide. Any RenderInline() call made while bundling is active +// (count > 0) will also preserve local component refs. This is intentional - the bundler +// uses RenderInline internally, and concurrent bundles must all see consistent behavior. +// Direct RenderInline() calls outside of bundling are unaffected when no bundles are running. +var bundlingModeCount atomic.Int32 + +// SetBundlingMode increments or decrements the bundling mode reference count. +// Bundling mode is active when count > 0, supporting concurrent bundle operations. +func SetBundlingMode(enabled bool) { + if enabled { + bundlingModeCount.Add(1) + } else { + bundlingModeCount.Add(-1) + } +} + +// IsBundlingMode returns whether any bundling operation is active. +func IsBundlingMode() bool { + return bundlingModeCount.Load() > 0 +} + +// RenderingMode controls how inline rendering handles discriminator $refs. +type RenderingMode int + +const ( + // RenderingModeBundle is the default mode - preserves $refs in discriminator + // oneOf/anyOf for compatibility with discriminator mappings during bundling. + RenderingModeBundle RenderingMode = iota + + // RenderingModeValidation forces full inlining of all $refs, ignoring + // discriminator preservation. Use this when rendering schemas for JSON + // Schema validation where the compiler needs a self-contained schema. + RenderingModeValidation +) + +// InlineRenderContext provides isolated tracking for inline rendering operations. +// Each render call-chain should use its own context to prevent false positive +// cycle detection when multiple goroutines render the same schemas concurrently. +type InlineRenderContext struct { + tracker sync.Map + Mode RenderingMode + preservedRefs sync.Map // tracks refs that should be preserved in this render +} + +// NewInlineRenderContext creates a new isolated rendering context with default bundle mode. +func NewInlineRenderContext() *InlineRenderContext { + return &InlineRenderContext{Mode: RenderingModeBundle} +} + +// NewInlineRenderContextForValidation creates a context that fully inlines +// all refs, including discriminator oneOf/anyOf refs. Use this when rendering +// schemas for JSON Schema validation. +func NewInlineRenderContextForValidation() *InlineRenderContext { + return &InlineRenderContext{Mode: RenderingModeValidation} +} + +// StartRendering marks a key as being rendered. Returns true if already rendering (cycle detected). +// The key should be stable and unique per schema instance (e.g., filePath:$ref). +func (ctx *InlineRenderContext) StartRendering(key string) bool { + if key == "" { + return false + } + _, loaded := ctx.tracker.LoadOrStore(key, true) + return loaded +} + +// StopRendering marks a key as done rendering. +func (ctx *InlineRenderContext) StopRendering(key string) { + if key != "" { + ctx.tracker.Delete(key) + } +} + +// MarkRefAsPreserved marks a reference as one that should be preserved (not inlined) in this render. +// used by discriminator handling to track which refs need preservation without mutating shared state. +func (ctx *InlineRenderContext) MarkRefAsPreserved(ref string) { + if ref != "" { + ctx.preservedRefs.Store(ref, true) + } +} + +// ShouldPreserveRef returns true if the given reference was marked for preservation. +func (ctx *InlineRenderContext) ShouldPreserveRef(ref string) bool { + if ref == "" { + return false + } + _, ok := ctx.preservedRefs.Load(ref) + return ok +} + +// SchemaProxy exists as a stub that will create a Schema once (and only once) the Schema() method is called. An +// underlying low-level SchemaProxy backs this high-level one. +// +// Why use a Proxy design? +// +// There are three reasons. +// +// 1. Circular References and Endless Loops. +// +// JSON Schema allows for references to be used. This means references can loop around and create infinite recursive +// structures, These 'Circular references' technically mean a schema can NEVER be resolved, not without breaking the +// loop somewhere along the chain. +// +// Polymorphism in the form of 'oneOf' and 'anyOf' in version 3+ only exacerbates the problem. +// +// These circular traps can be discovered using the resolver, however it's still not enough to stop endless loops and +// endless goroutine spawning. A proxy design means that resolving occurs on demand and runs down a single level only. +// preventing any run-away loops. +// +// 2. Performance +// +// Even without circular references, Polymorphism creates large additional resolving chains that take a long time +// and slow things down when building. By preventing recursion through every polymorphic item, building models is kept +// fast and snappy, which is desired for realtime processing of specs. +// +// - Q: Yeah, but, why not just use state to avoiding re-visiting seen polymorphic nodes? +// - A: It's slow, takes up memory and still has runaway potential in very, very long chains. +// +// 3. Short Circuit Errors. +// +// Schemas are where things can get messy, mainly because the Schema standard changes between versions, and +// it's not actually JSONSchema until 3.1, so lots of times a bad schema will break parsing. Errors are only found +// when a schema is needed, so the rest of the document is parsed and ready to use. +type SchemaProxy struct { + schema *low.NodeReference[*base.SchemaProxy] + buildError error + rendered *Schema + refStr string + lock *sync.Mutex +} + +// NewSchemaProxy creates a new high-level SchemaProxy from a low-level one. +func NewSchemaProxy(schema *low.NodeReference[*base.SchemaProxy]) *SchemaProxy { + return &SchemaProxy{schema: schema, lock: &sync.Mutex{}} +} + +// copySchemaWithParentProxy creates a shallow copy of a schema and sets the ParentProxy +func (sp *SchemaProxy) copySchemaWithParentProxy(schema *Schema) *Schema { + schemaCopy := *schema + schemaCopy.ParentProxy = sp + return &schemaCopy +} + +// CreateSchemaProxy will create a new high-level SchemaProxy from a high-level Schema, this acts the same +// as if the SchemaProxy is pre-rendered. +func CreateSchemaProxy(schema *Schema) *SchemaProxy { + return &SchemaProxy{rendered: schema, lock: &sync.Mutex{}} +} + +// CreateSchemaProxyRef will create a new high-level SchemaProxy from a reference string, this is used only when +// building out new models from scratch that require a reference rather than a schema implementation. +func CreateSchemaProxyRef(ref string) *SchemaProxy { + return &SchemaProxy{refStr: ref, lock: &sync.Mutex{}} +} + +// GetValueNode returns the value node of the SchemaProxy. +func (sp *SchemaProxy) GetValueNode() *yaml.Node { + if sp.schema != nil { + return sp.schema.ValueNode + } + return nil +} + +// Schema will create a new Schema instance using NewSchema from the low-level SchemaProxy backing this high-level one. +// If there is a problem building the Schema, then this method will return nil. Use GetBuildError to gain access +// to that building error. +// +// It's important to note that this method will return nil on a pointer created using NewSchemaProxy or CreateSchema* methods +// there is no low-level SchemaProxy backing it, and therefore no schema to build, so this will fail. Use BuildSchema +// instead for proxies created using NewSchemaProxy or CreateSchema* methods. +// https://github.com/pb33f/libopenapi/issues/403 +func (sp *SchemaProxy) Schema() *Schema { + if sp == nil || sp.lock == nil { + return nil + } + + sp.lock.Lock() + defer sp.lock.Unlock() + + if sp.rendered != nil { + return sp.rendered + } + + if sp.schema == nil || sp.schema.Value == nil { + return nil + } + + // check the high-level cache first. + idx := sp.schema.Value.GetIndex() + if idx != nil && sp.schema.Value != nil { + if sp.schema.Value.IsReference() && sp.schema.Value.GetReferenceNode() != nil && sp.schema.GetValueNode() != nil { + loc := fmt.Sprintf("%s:%d:%d", idx.GetSpecAbsolutePath(), sp.schema.GetValueNode().Line, sp.schema.GetValueNode().Column) + if seen, ok := idx.GetHighCache().Load(loc); ok { + idx.HighCacheHit() + // cache locally to avoid recreating on repeated access + sp.rendered = sp.copySchemaWithParentProxy(seen.(*Schema)) + return sp.rendered + } else { + idx.HighCacheMiss() + } + } + } + + s := sp.schema.Value.Schema() + if s == nil { + sp.buildError = sp.schema.Value.GetBuildError() + return nil + } + sch := NewSchema(s) + + if idx != nil { + // only store the schema in the cache if is a reference! + if sp.IsReference() && sp.GetReferenceNode() != nil && sp.schema != nil && sp.schema.GetValueNode() != nil { + // if sp.schema.GetValueNode() != nil { + loc := fmt.Sprintf("%s:%d:%d", idx.GetSpecAbsolutePath(), sp.schema.GetValueNode().Line, sp.schema.GetValueNode().Column) + + // caching is only performed on traditional $ref nodes with a reference and a value node, any 3.1 additional + // will not be cached as libopenapi does not yet support them. + if len(sp.GetReferenceNode().Content) == 2 { + idx.GetHighCache().Store(loc, sch) + } + } + } + + sp.rendered = sp.copySchemaWithParentProxy(sch) + return sp.rendered +} + +// IsReference returns true if the SchemaProxy is a reference to another Schema. +func (sp *SchemaProxy) IsReference() bool { + if sp == nil { + return false + } + + if sp.refStr != "" { + return true + } + if sp.schema != nil && sp.schema.Value != nil { + return sp.schema.Value.IsReference() + } + return false +} + +// GetReference returns the location of the $ref if this SchemaProxy is a reference to another Schema. +func (sp *SchemaProxy) GetReference() string { + if sp.refStr != "" { + return sp.refStr + } + return sp.schema.GetValue().GetReference() +} + +func (sp *SchemaProxy) GetSchemaKeyNode() *yaml.Node { + if sp.schema != nil { + return sp.GoLow().GetKeyNode() + } + return nil +} + +func (sp *SchemaProxy) GetReferenceNode() *yaml.Node { + if sp.refStr != "" { + return utils.CreateRefNode(sp.refStr) + } + return sp.schema.GetValue().GetReferenceNode() +} + +// GetReferenceOrigin returns a pointer to the index.NodeOrigin of the $ref if this SchemaProxy is a reference to another Schema. +// returns nil if the origin cannot be found (which, means there is a bug, and we need to fix it). +func (sp *SchemaProxy) GetReferenceOrigin() *index.NodeOrigin { + if sp.schema != nil { + return sp.schema.Value.GetSchemaReferenceLocation() + } + return nil +} + +// BuildSchema operates the same way as Schema, except it will return any error along with the *Schema. Unlike the Schema +// method, this will work on a proxy created by the NewSchemaProxy or CreateSchema* methods. +// +// It differs from Schema in that it does not require a low-level SchemaProxy to be present, +// and will build the schema from the high-level one. +func (sp *SchemaProxy) BuildSchema() (*Schema, error) { + if sp == nil { + return nil, nil + } + schema := sp.Schema() + if sp.lock == nil { + return schema, sp.buildError + } + sp.lock.Lock() + er := sp.buildError + sp.lock.Unlock() + return schema, er +} + +// GetBuildError returns any error that was thrown when calling Schema() +func (sp *SchemaProxy) GetBuildError() error { + if sp == nil { + return nil + } + if sp.lock == nil { + return sp.buildError + } + sp.lock.Lock() + err := sp.buildError + sp.lock.Unlock() + return err +} + +func (sp *SchemaProxy) GoLow() *base.SchemaProxy { + if sp.schema == nil { + return nil + } + return sp.schema.Value +} + +func (sp *SchemaProxy) GoLowUntyped() any { + if sp.schema == nil { + return nil + } + return sp.schema.Value +} + +// Render will return a YAML representation of the Schema object as a byte slice. +func (sp *SchemaProxy) Render() ([]byte, error) { + return yaml.Marshal(sp) +} + +// MarshalYAML will create a ready to render YAML representation of the SchemaProxy object. +func (sp *SchemaProxy) MarshalYAML() (interface{}, error) { + var s *Schema + var err error + // if this schema isn't a reference, then build it out. + if !sp.IsReference() { + s, err = sp.BuildSchema() + if err != nil { + return nil, err + } + nb := high.NewNodeBuilder(s, s.low) + return nb.Render(), nil + } else { + return sp.GetReferenceNode(), nil + } +} + +// getInlineRenderKey generates a unique key for tracking this schema during inline rendering. +// This prevents infinite recursion when schemas reference each other circularly. +func (sp *SchemaProxy) getInlineRenderKey() string { + // Check for nil schema first (sp.schema or sp.schema.Value could be nil) + if sp.schema == nil || sp.schema.Value == nil { + // Check for refStr-based reference + if sp.refStr != "" { + return sp.refStr + } + return "" + } + // Use the reference string if available + if sp.IsReference() { + ref := sp.GetReference() + // Include the index path to handle cross-file references + if sp.schema.Value != nil && sp.schema.Value.GetIndex() != nil { + idx := sp.schema.Value.GetIndex() + return fmt.Sprintf("%s:%s", idx.GetSpecAbsolutePath(), ref) + } + return ref + } + // For inline schemas, use the node position + if sp.schema.ValueNode != nil { + node := sp.schema.ValueNode + var idx *index.SpecIndex + if sp.schema.Value != nil { + idx = sp.schema.Value.GetIndex() + } + if node.Line > 0 && node.Column > 0 { + if idx != nil { + return fmt.Sprintf("%s:%d:%d", idx.GetSpecAbsolutePath(), node.Line, node.Column) + } + return fmt.Sprintf("inline:%d:%d", node.Line, node.Column) + } + // Nodes created via yaml.Node.Encode() don't include line/column info. + // Fall back to a pointer-based key to avoid false cycle detection. + if idx != nil { + return fmt.Sprintf("%s:inline:%p", idx.GetSpecAbsolutePath(), node) + } + return fmt.Sprintf("inline:%p", node) + } + return "" +} + +// MarshalYAMLInlineWithContext will create a ready to render YAML representation of the SchemaProxy object +// using the provided InlineRenderContext for cycle detection. Use this when multiple goroutines may render +// the same schemas concurrently to avoid false positive cycle detection. +// The ctx parameter should be *InlineRenderContext but is typed as any to satisfy the +// high.RenderableInlineWithContext interface without import cycles. +func (sp *SchemaProxy) MarshalYAMLInlineWithContext(ctx any) (interface{}, error) { + if renderCtx, ok := ctx.(*InlineRenderContext); ok { + return sp.marshalYAMLInlineInternal(renderCtx) + } + // Fallback to fresh context if wrong type passed + return sp.marshalYAMLInlineInternal(NewInlineRenderContext()) +} + +// MarshalYAMLInline will create a ready to render YAML representation of the SchemaProxy object. The +// $ref values will be inlined instead of kept as is. All circular references will be ignored, regardless +// of the type of circular reference, they are all bad when rendering. +// This method creates a fresh InlineRenderContext internally. For concurrent scenarios, use +// MarshalYAMLInlineWithContext instead. +func (sp *SchemaProxy) MarshalYAMLInline() (interface{}, error) { + ctx := NewInlineRenderContext() + return sp.marshalYAMLInlineInternal(ctx) +} + +func (sp *SchemaProxy) marshalYAMLInlineInternal(ctx *InlineRenderContext) (interface{}, error) { + // check if this reference should be preserved (set via context by discriminator handling). + // this avoids mutating shared SchemaProxy state and prevents race conditions. + // need to guard against nil schema.Value which can happen with bad/incomplete proxies. + if sp.IsReference() { + ref := sp.GetReference() + if ref != "" && ctx.ShouldPreserveRef(ref) { + return sp.GetReferenceNode(), nil + } + } + + // In bundling mode, preserve local component refs that point to schemas in the SAME document. + // Only inline refs that point to schemas from EXTERNAL files. + // Outside of bundling mode (direct MarshalYAMLInline calls), inline everything. + if IsBundlingMode() && sp.IsReference() { + ref := sp.GetReference() + if strings.HasPrefix(ref, "#/components/") { + // Check if this ref points to a schema in the same root document. + // If the low-level proxy has an index, compare it to the root index. + if sp.schema != nil && sp.schema.Value != nil { + lowProxy := sp.schema.Value + schemaIdx := lowProxy.GetIndex() + if schemaIdx != nil { + rolodex := schemaIdx.GetRolodex() + if rolodex != nil { + rootIdx := rolodex.GetRootIndex() + // If the schema is in the root index, preserve the ref + if rootIdx != nil && schemaIdx == rootIdx { + return sp.GetReferenceNode(), nil + } + } + } + } + } + } + + // Check for recursive rendering using the context's tracker. + // This prevents infinite recursion when circular references aren't properly detected. + // Using a scoped context instead of a global tracker prevents false positive cycle detection + // when multiple goroutines render the same schemas concurrently. + renderKey := sp.getInlineRenderKey() + if ctx.StartRendering(renderKey) { + // We're already rendering this schema in THIS call chain - return ref to break the cycle + if sp.IsReference() { + return sp.GetReferenceNode(), + fmt.Errorf("schema render failure, circular reference: `%s`", sp.GetReference()) + } + // For inline schemas, return an empty map to avoid infinite recursion + return &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map"}, + fmt.Errorf("schema render failure, circular reference detected during inline rendering") + } + defer ctx.StopRendering(renderKey) + + var s *Schema + var err error + s, err = sp.BuildSchema() + + if s != nil && s.GoLow() != nil && s.GoLow().Index != nil { + idx := s.GoLow().Index + circ := idx.GetCircularReferences() + + // Extract ignored and safe circular references from rolodex if available + if idx.GetRolodex() != nil { + ignored := idx.GetRolodex().GetIgnoredCircularReferences() + safe := idx.GetRolodex().GetSafeCircularReferences() + circ = append(circ, ignored...) + circ = append(circ, safe...) + } + + cirError := func(str string) error { + return fmt.Errorf("schema render failure, circular reference: `%s`", str) + } + + for _, c := range circ { + if sp.IsReference() { + if sp.GetReference() == c.LoopPoint.Definition { + // nope + return sp.GetReferenceNode(), + cirError((c.LoopPoint.Definition)) + } + basePath := sp.GoLow().GetIndex().GetSpecAbsolutePath() + + if !filepath.IsAbs(basePath) && !strings.HasPrefix(basePath, "http") { + basePath, _ = filepath.Abs(basePath) + } + + if basePath == c.LoopPoint.FullDefinition { + // we loop on our-self + return sp.GetReferenceNode(), + cirError((c.LoopPoint.Definition)) + } + a := utils.ReplaceWindowsDriveWithLinuxPath(strings.Replace(c.LoopPoint.FullDefinition, basePath, "", 1)) + b := sp.GetReference() + if strings.HasPrefix(b, "./") { + b = strings.Replace(b, "./", "/", 1) // strip any leading ./ from the reference + } + // if loading things in remotely and references are relative. + if strings.HasPrefix(a, "http") { + purl, _ := url.Parse(a) + if purl != nil { + specPath := filepath.Dir(purl.Path) + host := fmt.Sprintf("%s://%s", purl.Scheme, purl.Host) + a = strings.Replace(a, host, "", 1) + a = strings.Replace(a, specPath, "", 1) + } + } + + aBase, aFragment := index.SplitRefFragment(a) + bBase, bFragment := index.SplitRefFragment(b) + + if aFragment != "" && bFragment != "" && aFragment == bFragment { + return sp.GetReferenceNode(), + cirError((c.LoopPoint.Definition)) + } + + if aFragment == "" && bFragment == "" { + aNorm := strings.TrimPrefix(strings.TrimPrefix(aBase, "./"), "/") + bNorm := strings.TrimPrefix(strings.TrimPrefix(bBase, "./"), "/") + if aNorm != "" && bNorm != "" && aNorm == bNorm { + // nope + return sp.GetReferenceNode(), + cirError((c.LoopPoint.Definition)) + } + } + } + } + } + + if err != nil { + return nil, err + } + if s != nil { + // Delegate to Schema.MarshalYAMLInlineWithContext to ensure discriminator handling is applied + // and cycle detection context is propagated. + // Schema.MarshalYAMLInlineWithContext sets preserveReference on OneOf/AnyOf items when + // a discriminator is present, which is required for proper bundling. + return s.MarshalYAMLInlineWithContext(ctx) + } + return nil, errors.New("unable to render schema") +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/base/security_requirement.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/security_requirement.go new file mode 100644 index 000000000..0596dd159 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/security_requirement.go @@ -0,0 +1,137 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "sort" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// SecurityRequirement is a high-level representation of a Swagger / OpenAPI 3 SecurityRequirement object. +// +// SecurityRequirement lists the required security schemes to execute this operation. The object can have multiple +// security schemes declared in it which are all required (that is, there is a logical AND between the schemes). +// +// The name used for each property MUST correspond to a security scheme declared in the Security Definitions +// - https://swagger.io/specification/v2/#securityDefinitionsObject +type SecurityRequirement struct { + Requirements *orderedmap.Map[string, []string] `json:"-" yaml:"-"` + ContainsEmptyRequirement bool // if a requirement is empty (this means it's optional) + low *base.SecurityRequirement +} + +// NewSecurityRequirement creates a new high-level SecurityRequirement from a low-level one. +func NewSecurityRequirement(req *base.SecurityRequirement) *SecurityRequirement { + r := new(SecurityRequirement) + r.low = req + values := orderedmap.New[string, []string]() + // to keep things fast, avoiding copying anything - makes it a little hard to read. + for name, val := range req.Requirements.Value.FromOldest() { + var vals []string + for valK := range val.Value { + vals = append(vals, val.Value[valK].Value) + } + values.Set(name.Value, vals) + } + r.Requirements = values + r.ContainsEmptyRequirement = req.ContainsEmptyRequirement + return r +} + +// GoLow returns the low-level SecurityRequirement used to create the high-level one. +func (s *SecurityRequirement) GoLow() *base.SecurityRequirement { + return s.low +} + +// GoLowUntyped will return the low-level Discriminator instance that was used to create the high-level one, with no type +func (s *SecurityRequirement) GoLowUntyped() any { + return s.low +} + +// Render will return a YAML representation of the SecurityRequirement object as a byte slice. +func (s *SecurityRequirement) Render() ([]byte, error) { + return yaml.Marshal(s) +} + +// MarshalYAML will create a ready to render YAML representation of the SecurityRequirement object. +func (s *SecurityRequirement) MarshalYAML() (interface{}, error) { + type req struct { + line int + key string + val []string + lowKey *low.KeyReference[string] + lowVal *low.ValueReference[[]low.ValueReference[string]] + } + + m := utils.CreateEmptyMapNode() + keys := make([]*req, orderedmap.Len(s.Requirements)) + + i := 0 + + for name, vals := range s.Requirements.FromOldest() { + keys[i] = &req{key: name, val: vals} + i++ + } + i = 0 + if s.low != nil { + for o := range keys { + kv := keys[o].key + for pair := orderedmap.First(s.low.Requirements.Value); pair != nil; pair = pair.Next() { + if pair.Key().Value == kv { + keys[o].line = pair.Key().KeyNode.Line + keys[o].lowKey = pair.KeyPtr() + keys[o].lowVal = pair.ValuePtr() + } + i++ + } + } + } + sort.Slice(keys, func(i, j int) bool { + return keys[i].line < keys[j].line + }) + + for k := range keys { + l := utils.CreateStringNode(keys[k].key) + l.Line = keys[k].line + + // for each key, extract all the values and order them. + type req struct { + line int + val string + } + + reqs := make([]*req, len(keys[k].val)) + for t := range keys[k].val { + reqs[t] = &req{val: keys[k].val[t], line: 9999 + t} + if keys[k].lowVal != nil { + for range keys[k].lowVal.Value[t].Value { + fh := keys[k].val[t] + df := keys[k].lowVal.Value[t].Value + if fh == df { + reqs[t].line = keys[k].lowVal.Value[t].ValueNode.Line + break + } + } + } + } + sort.Slice(reqs, func(i, j int) bool { + return reqs[i].line < reqs[j].line + }) + sn := utils.CreateEmptySequenceNode() + sn.Line = keys[k].line + 1 + for z := range reqs { + n := utils.CreateStringNode(reqs[z].val) + n.Line = reqs[z].line + 1 + sn.Content = append(sn.Content, n) + } + + m.Content = append(m.Content, l, sn) + } + return m, nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/base/tag.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/tag.go new file mode 100644 index 000000000..988f613a2 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/tag.go @@ -0,0 +1,88 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// Tag represents a high-level Tag instance that is backed by a low-level one. +// +// Adds metadata to a single tag that is used by the Operation Object. It is not mandatory to have a Tag Object per +// tag defined in the Operation Object instances. +// - v2: https://swagger.io/specification/v2/#tagObject +// - v3: https://swagger.io/specification/#tag-object +// - v3.2: https://spec.openapis.org/oas/v3.2.0#tag-object +type Tag struct { + Name string `json:"name,omitempty" yaml:"name,omitempty"` + Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + ExternalDocs *ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` + Parent string `json:"parent,omitempty" yaml:"parent,omitempty"` + Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` + Extensions *orderedmap.Map[string, *yaml.Node] + low *low.Tag +} + +// NewTag creates a new high-level Tag instance that is backed by a low-level one. +func NewTag(tag *low.Tag) *Tag { + t := new(Tag) + t.low = tag + if !tag.Name.IsEmpty() { + t.Name = tag.Name.Value + } + if !tag.Summary.IsEmpty() { + t.Summary = tag.Summary.Value + } + if !tag.Description.IsEmpty() { + t.Description = tag.Description.Value + } + if !tag.ExternalDocs.IsEmpty() { + t.ExternalDocs = NewExternalDoc(tag.ExternalDocs.Value) + } + if !tag.Parent.IsEmpty() { + t.Parent = tag.Parent.Value + } + if !tag.Kind.IsEmpty() { + t.Kind = tag.Kind.Value + } + t.Extensions = high.ExtractExtensions(tag.Extensions) + return t +} + +// GoLow returns the low-level Tag instance used to create the high-level one. +func (t *Tag) GoLow() *low.Tag { + return t.low +} + +// GoLowUntyped will return the low-level Tag instance that was used to create the high-level one, with no type +func (t *Tag) GoLowUntyped() any { + return t.low +} + +// Render will return a YAML representation of the Info object as a byte slice. +func (t *Tag) Render() ([]byte, error) { + return yaml.Marshal(t) +} + +// Render will return a YAML representation of the Info object as a byte slice. +func (t *Tag) RenderInline() ([]byte, error) { + d, _ := t.MarshalYAMLInline() + return yaml.Marshal(d) +} + +// MarshalYAML will create a ready to render YAML representation of the Info object. +func (t *Tag) MarshalYAML() (interface{}, error) { + nb := high.NewNodeBuilder(t, t.low) + return nb.Render(), nil +} + +func (t *Tag) MarshalYAMLInline() (interface{}, error) { + nb := high.NewNodeBuilder(t, t.low) + nb.Resolve = true + return nb.Render(), nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/base/xml.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/xml.go new file mode 100644 index 000000000..a02f01dc0 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/base/xml.go @@ -0,0 +1,83 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "log/slog" + + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// XML represents a high-level representation of an XML object defined by all versions of OpenAPI and backed by +// low-level XML object. +// +// A metadata object that allows for more fine-tuned XML model definitions. +// +// When using arrays, XML element names are not inferred (for singular/plural forms) and the name property SHOULD be +// used to add that information. See examples for expected behavior. +// +// v2 - https://swagger.io/specification/v2/#xmlObject +// v3 - https://swagger.io/specification/#xml-object +type XML struct { + Name string `json:"name,omitempty" yaml:"name,omitempty"` + Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` + Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"` + Attribute bool `json:"attribute,omitempty" yaml:"attribute,omitempty"` + NodeType string `json:"nodeType,omitempty" yaml:"nodeType,omitempty"` // OpenAPI 3.2+ nodeType field + Wrapped bool `json:"wrapped,omitempty" yaml:"wrapped,omitempty"` + Extensions *orderedmap.Map[string, *yaml.Node] + low *low.XML +} + +// NewXML creates a new high-level XML instance from a low-level one. +func NewXML(xml *low.XML) *XML { + x := new(XML) + x.low = xml + x.Name = xml.Name.Value + x.Namespace = xml.Namespace.Value + x.Prefix = xml.Prefix.Value + x.Attribute = xml.Attribute.Value + x.NodeType = xml.NodeType.Value + x.Wrapped = xml.Wrapped.Value + x.Extensions = high.ExtractExtensions(xml.Extensions) + + // log warning if using deprecated attribute field in OpenAPI 3.2+ + if xml.GetIndex() != nil && xml.GetIndex().GetConfig() != nil && xml.GetIndex().GetConfig().SpecInfo != nil { + version := xml.GetIndex().GetConfig().SpecInfo.VersionNumeric + if version >= 3.2 && x.Attribute && x.NodeType == "" { + // log deprecation warning + logger := xml.GetIndex().GetConfig().Logger + if logger != nil { + logger.Warn("XML 'attribute' field is deprecated in OpenAPI 3.2+, use 'nodeType' instead", + slog.String("name", x.Name)) + } + } + } + + return x +} + +// GoLow returns the low level XML reference used to create the high level one. +func (x *XML) GoLow() *low.XML { + return x.low +} + +// GoLowUntyped will return the low-level XML instance that was used to create the high-level one, with no type +func (x *XML) GoLowUntyped() any { + return x.low +} + +// Render will return a YAML representation of the XML object as a byte slice. +func (x *XML) Render() ([]byte, error) { + return yaml.Marshal(x) +} + +// MarshalYAML will create a ready to render YAML representation of the XML object. +func (x *XML) MarshalYAML() (interface{}, error) { + nb := high.NewNodeBuilder(x, x.low) + return nb.Render(), nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/node_builder.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/node_builder.go new file mode 100644 index 000000000..dc1a991cb --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/node_builder.go @@ -0,0 +1,649 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package high + +import ( + "fmt" + "reflect" + "sort" + "strconv" + "strings" + "unicode" + + "github.com/pb33f/libopenapi/datamodel/high/nodes" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// NodeBuilder is a structure used by libopenapi high-level objects, to render themselves back to YAML. +// this allows high-level objects to be 'mutable' because all changes will be rendered out. +type NodeBuilder struct { + Version float32 + Nodes []*nodes.NodeEntry + High any + Low any + Resolve bool // If set to true, all references will be rendered inline + RenderContext any // Context for inline rendering cycle detection (*base.InlineRenderContext) + Errors []error +} + +// RenderableInlineWithContext is an interface that can be implemented by types that support +// context-aware inline rendering for proper cycle detection in concurrent scenarios. +// The context parameter should be *base.InlineRenderContext but is typed as any to avoid import cycles. +type RenderableInlineWithContext interface { + MarshalYAMLInlineWithContext(ctx any) (interface{}, error) +} + +const renderZero = "renderZero" + +// NewNodeBuilder will create a new NodeBuilder instance, this is the only way to create a NodeBuilder. +// The function accepts a high level object and a low level object (need to be siblings/same type). +// +// Using reflection, a map of every field in the high level object is created, ready to be rendered. +func NewNodeBuilder(high any, low any) *NodeBuilder { + // create a new node builder + nb := new(NodeBuilder) + nb.High = high + if low != nil { + nb.Low = low + } + + // extract fields from the high level object and add them into our node builder. + // this will allow us to extract the line numbers from the low level object as well. + v := reflect.ValueOf(high).Elem() + num := v.NumField() + for i := 0; i < num; i++ { + nb.add(v.Type().Field(i).Name, i) + } + return nb +} + +func (n *NodeBuilder) add(key string, i int) { + // only operate on exported fields. + if unicode.IsLower(rune(key[0])) { + return + } + + var ( + lowFieldValue reflect.Value + lowFieldValid bool + ) + + if n.Low != nil && !reflect.ValueOf(n.Low).IsZero() { + low := reflect.ValueOf(n.Low) + if low.Kind() == reflect.Ptr && !low.IsNil() { + elem := low.Elem() + if elem.IsValid() { + field := elem.FieldByName(key) + if field.IsValid() { + lowFieldValue = field + lowFieldValid = true + } + } + } else if low.IsValid() { + field := low.FieldByName(key) + if field.IsValid() { + lowFieldValue = field + lowFieldValid = true + } + } + } + + // if the key is 'Extensions' then we need to extract the keys from the map + // and add them to the node builder. + if key == "Extensions" { + ev := reflect.ValueOf(n.High).Elem().FieldByName(key).Interface() + var extensions *orderedmap.Map[string, *yaml.Node] + if ev != nil { + extensions = ev.(*orderedmap.Map[string, *yaml.Node]) + } + + var lowExtensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + if n.Low != nil && !reflect.ValueOf(n.Low).IsZero() { + if j, ok := n.Low.(low.HasExtensionsUntyped); ok { + lowExtensions = j.GetExtensions() + } + } + + j := 0 + if lowExtensions != nil { + // If we have low extensions get the original lowest line number so we end up in the same place + for ext := range lowExtensions.KeysFromOldest() { + if j == 0 || ext.KeyNode.Line < j { + j = ext.KeyNode.Line + } + } + } + + for ext, node := range extensions.FromOldest() { + nodeEntry := &nodes.NodeEntry{Tag: ext, Key: ext, Value: node, Line: j} + + if lowExtensions != nil { + lowItem := low.FindItemInOrderedMap(ext, lowExtensions) + nodeEntry.LowValue = lowItem + } + n.Nodes = append(n.Nodes, nodeEntry) + j++ + } + // done, extensions are handled separately. + return + } + + // find the field with the tag supplied. + field, _ := reflect.TypeOf(n.High).Elem().FieldByName(key) + tag := string(field.Tag.Get("yaml")) + tagName := strings.Split(tag, ",")[0] + + if tag == "-" { + return + } + + var renderZeroFlag, omitEmptyFlag bool + tagParts := strings.Split(tag, ",") + for _, part := range tagParts { + if part == renderZero { + renderZeroFlag = true + } + if part == "omitempty" { + omitEmptyFlag = true + } + } + + // extract the value of the field + fieldValue := reflect.ValueOf(n.High).Elem().FieldByName(key) + f := fieldValue.Interface() + value := reflect.ValueOf(f) + var isZero bool + if (value.Kind() == reflect.Interface || value.Kind() == reflect.Ptr) && value.IsNil() { + isZero = true + } else if zeroer, ok := f.(yaml.IsZeroer); ok && zeroer.IsZero() { + isZero = true + } else if f == nil || value.IsZero() { + if tagName != "description" { + isZero = true + } else { + if omitEmptyFlag { + isZero = true + } + } + } + + if isZero && lowFieldValid { + var lowInterface any + if lowFieldValue.Kind() == reflect.Ptr { + if !lowFieldValue.IsNil() { + lowInterface = lowFieldValue.Elem().Interface() + } + } else { + lowInterface = lowFieldValue.Interface() + } + if lowInterface != nil { + if emptier, ok := lowInterface.(interface{ IsEmpty() bool }); ok && !emptier.IsEmpty() { + isZero = false + } else if nodeGetter, ok := lowInterface.(interface{ GetValueNode() *yaml.Node }); ok { + if node := nodeGetter.GetValueNode(); node != nil { + isZero = false + } + } + } + } + + if !renderZeroFlag && isZero || omitEmptyFlag && isZero { + return + } + + // create a new node entry + nodeEntry := &nodes.NodeEntry{Tag: tagName, Key: key} + nodeEntry.RenderZero = renderZeroFlag + switch value.Kind() { + case reflect.Float64, reflect.Float32: + nodeEntry.Value = value.Float() + x := float64(int(value.Float()*100)) / 100 // trim this down + nodeEntry.StringValue = strconv.FormatFloat(x, 'f', -1, 64) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + nodeEntry.Value = value.Int() + nodeEntry.StringValue = value.String() + case reflect.String: + nodeEntry.Value = value.String() + case reflect.Bool: + nodeEntry.Value = value.Bool() + case reflect.Slice: + if tagName == "type" { + if value.Len() == 1 { + nodeEntry.Value = value.Index(0).String() + } else { + nodeEntry.Value = f + } + } else { + if renderZeroFlag || (!value.IsNil() && !isZero) { + nodeEntry.Value = f + } + } + case reflect.Ptr: + if !value.IsNil() { + nodeEntry.Value = f + } + default: + nodeEntry.Value = f + } + + // if there is no low-level object, then we cannot extract line numbers, + // so skip and default to 0, which means a new entry to the spec. + // this will place new content and the top of the rendered object. + if n.Low != nil && !reflect.ValueOf(n.Low).IsZero() { + if lowFieldValid { + fLow := lowFieldValue.Interface() + value = reflect.ValueOf(fLow) + + nodeEntry.LowValue = fLow + switch value.Kind() { + + case reflect.Slice: + l := value.Len() + lines := make([]int, l) + for g := 0; g < l; g++ { + qw := value.Index(g).Interface() + if we, wok := qw.(low.HasKeyNode); wok { + lines[g] = we.GetKeyNode().Line + } + } + sort.Slice(lines, func(i, j int) bool { + return lines[i] < lines[j] + }) + if len(lines) > 0 { + nodeEntry.Line = lines[0] + } + case reflect.Struct: + y := value.Interface() + nodeEntry.Line = 9999 + i + if nb, ok := y.(low.HasValueNodeUntyped); ok { + if nb.IsReference() { + if jk, kj := y.(low.HasKeyNode); kj { + nodeEntry.Line = jk.GetKeyNode().Line + break + } + } + if nb.GetValueNode() != nil { + nodeEntry.Line = nb.GetValueNode().Line + } + } + default: + // everything else, weight it to the bottom of the rendered object. + // this is things that we have no way of knowing where they should be placed. + nodeEntry.Line = 9999 + i + } + } + } + if nodeEntry.Value != nil { + n.Nodes = append(n.Nodes, nodeEntry) + } +} + +func (n *NodeBuilder) renderReference(fg low.IsReferenced) *yaml.Node { + origNode := fg.GetReferenceNode() + if origNode == nil { + return utils.CreateRefNode(fg.GetReference()) + } + return origNode +} + +// Render will render the NodeBuilder back to a YAML node, iterating over every NodeEntry defined +func (n *NodeBuilder) Render() *yaml.Node { + if len(n.Nodes) == 0 { + return utils.CreateEmptyMapNode() + } + + // order nodes by line number, retain original order + m := utils.CreateEmptyMapNode() + if fg, ok := n.Low.(low.IsReferenced); ok { + g := reflect.ValueOf(fg) + if !g.IsNil() { + if fg.IsReference() && !n.Resolve { + return n.renderReference(n.Low.(low.IsReferenced)) + } + } + } + + sort.Slice(n.Nodes, func(i, j int) bool { + if n.Nodes[i].Line != n.Nodes[j].Line { + return n.Nodes[i].Line < n.Nodes[j].Line + } + return false + }) + + for i := range n.Nodes { + node := n.Nodes[i] + n.AddYAMLNode(m, node) + } + return m +} + +// AddYAMLNode will add a new *yaml.Node to the parent node, using the tag, key and value provided. +// If the value is nil, then the node will not be added. This method is recursive, so it will dig down +// into any non-scalar types. +func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *nodes.NodeEntry) *yaml.Node { + if entry.Value == nil { + return parent + } + + // check the type + t := reflect.TypeOf(entry.Value) + var l *yaml.Node + if entry.Tag != "" { + l = utils.CreateStringNode(entry.Tag) + l.Style = entry.KeyStyle + } + + value := entry.Value + line := entry.Line + + var nodeErrors []error + var ne error + + var valueNode *yaml.Node + switch t.Kind() { + case reflect.String: + val := value.(string) + valueNode = utils.CreateStringNode(val) + valueNode.Line = line + + if entry.LowValue != nil { + if vnut, ok := entry.LowValue.(low.HasValueNodeUntyped); ok { + vn := vnut.GetValueNode() + if vn != nil { + valueNode.Style = vn.Style + } + } + } + case reflect.Bool: + val := value.(bool) + if !val { + valueNode = utils.CreateBoolNode("false") + } else { + valueNode = utils.CreateBoolNode("true") + } + valueNode.Line = line + case reflect.Int: + val := strconv.Itoa(value.(int)) + valueNode = utils.CreateIntNode(val) + valueNode.Line = line + case reflect.Int64: + val := strconv.FormatInt(value.(int64), 10) + valueNode = utils.CreateIntNode(val) + valueNode.Line = line + case reflect.Float32: + val := strconv.FormatFloat(float64(value.(float32)), 'f', 2, 64) + valueNode = utils.CreateFloatNode(val) + valueNode.Line = line + case reflect.Float64: + precision := -1 + if entry.StringValue != "" && strings.Contains(entry.StringValue, ".") { + precision = len(strings.Split(fmt.Sprint(entry.StringValue), ".")[1]) + } + val := strconv.FormatFloat(value.(float64), 'f', precision, 64) + // Always create float node for float64 values, even if they don't contain decimal points + // This handles cases like negative zero (-0.0) which formats as "-0" but should remain float + valueNode = utils.CreateFloatNode(val) + valueNode.Line = line + case reflect.Slice: + var rawNode yaml.Node + m := reflect.ValueOf(value) + sl := utils.CreateEmptySequenceNode() + skip := false + for i := 0; i < m.Len(); i++ { + // Reset skip at the start of each iteration to handle items without low-level models + // (e.g., newly created high-level objects appended to an existing slice) + skip = false + sqi := m.Index(i).Interface() + // check if this is a reference. + if glu, ok := sqi.(GoesLowUntyped); ok { + if glu != nil { + ut := glu.GoLowUntyped() + if ut != nil && !reflect.ValueOf(ut).IsNil() { + r := ut.(low.IsReferenced) + if ut != nil && r.GetReference() != "" && + ut.(low.IsReferenced).IsReference() { + if !n.Resolve { + sl.Content = append(sl.Content, n.renderReference(glu.GoLowUntyped().(low.IsReferenced))) + skip = true + } + } + } + } + } + if !skip { + if er, ko := sqi.(Renderable); ko { + var rend interface{} + if !n.Resolve { + rend, ne = er.MarshalYAML() + nodeErrors = append(nodeErrors, ne) + } else { + // try and render inline, if we can, otherwise treat as normal. + // Prefer a context-aware method when RenderContext is available + if n.RenderContext != nil { + if ctxRenderer, ko := er.(RenderableInlineWithContext); ko { + rend, ne = ctxRenderer.MarshalYAMLInlineWithContext(n.RenderContext) + nodeErrors = append(nodeErrors, ne) + } else if inliner, ko := er.(RenderableInline); ko { + rend, ne = inliner.MarshalYAMLInline() + nodeErrors = append(nodeErrors, ne) + } else { + rend, ne = er.MarshalYAML() + nodeErrors = append(nodeErrors, ne) + } + } else if inliner, ko := er.(RenderableInline); ko { + rend, ne = inliner.MarshalYAMLInline() + nodeErrors = append(nodeErrors, ne) + } else { + rend, ne = er.MarshalYAML() + nodeErrors = append(nodeErrors, ne) + } + } + // check if this is a pointer or not. + if _, ok := rend.(*yaml.Node); ok { + sl.Content = append(sl.Content, rend.(*yaml.Node)) + } + if _, ok := rend.(yaml.Node); ok { + k := rend.(yaml.Node) + sl.Content = append(sl.Content, &k) + } + } + } + } + + if len(sl.Content) > 0 { + valueNode = sl + break + } + if skip { + break + } + + err := rawNode.Encode(value) + if err != nil { + return parent + } else { + if entry.LowValue != nil { + if vnut, ok := entry.LowValue.(low.HasValueNodeUntyped); ok { + vn := vnut.GetValueNode() + if vn != nil && vn.Kind == yaml.SequenceNode { + for i := range vn.Content { + if len(rawNode.Content) > i { + rawNode.Content[i].Style = vn.Content[i].Style + } + } + } + } + } + + valueNode = &rawNode + } + + case reflect.Struct: + if r, ok := value.(low.ValueReference[any]); ok { + valueNode = r.GetValueNode() + break + } + if r, ok := value.(low.ValueReference[string]); ok { + valueNode = r.GetValueNode() + break + } + if r, ok := value.(low.NodeReference[string]); ok { + valueNode = r.GetValueNode() + break + } + return parent + + case reflect.Ptr: + if m, ok := value.(orderedmap.MapToYamlNoder); ok { + l := entry.LowValue + + if l == nil { + if gl, ok := value.(GoesLowUntyped); ok && gl.GoLowUntyped() != nil { + l = gl.GoLowUntyped() + } + } + + p := m.ToYamlNode(n, l) + if p.Content != nil { + valueNode = p + } + } else if r, ok := value.(Renderable); ok { + if gl, lg := value.(GoesLowUntyped); lg { + lut := gl.GoLowUntyped() + if lut != nil { + lr := lut.(low.IsReferenced) + ut := reflect.ValueOf(lr) + if !ut.IsNil() { + if lr != nil && lr.IsReference() { + if !n.Resolve { + valueNode = n.renderReference(lut.(low.IsReferenced)) + break + } + } + } + } + } + var rawRender interface{} + if !n.Resolve { + rawRender, ne = r.MarshalYAML() + nodeErrors = append(nodeErrors, ne) + } else { + // try an inline render if we can, otherwise there is no option but to default to the + // full render. Prefer a context-aware method when RenderContext is available + if n.RenderContext != nil { + if ctxRenderer, ko := r.(RenderableInlineWithContext); ko { + rawRender, ne = ctxRenderer.MarshalYAMLInlineWithContext(n.RenderContext) + nodeErrors = append(nodeErrors, ne) + } else if inliner, ko := r.(RenderableInline); ko { + rawRender, ne = inliner.MarshalYAMLInline() + nodeErrors = append(nodeErrors, ne) + } else { + rawRender, ne = r.MarshalYAML() + nodeErrors = append(nodeErrors, ne) + } + } else if inliner, ko := r.(RenderableInline); ko { + rawRender, ne = inliner.MarshalYAMLInline() + nodeErrors = append(nodeErrors, ne) + } else { + rawRender, ne = r.MarshalYAML() + nodeErrors = append(nodeErrors, ne) + } + } + if rawRender != nil { + if _, ko := rawRender.(*yaml.Node); ko { + valueNode = rawRender.(*yaml.Node) + } + if _, ko := rawRender.(yaml.Node); ko { + d := rawRender.(yaml.Node) + valueNode = &d + } + } + } else { + + encodeSkip := false + // check if the value is a bool, int or float + if b, bok := value.(*bool); bok { + encodeSkip = true + if *b { + valueNode = utils.CreateBoolNode("true") + valueNode.Line = line + } else { + if entry.RenderZero { + valueNode = utils.CreateBoolNode("false") + valueNode.Line = line + } + } + } + if b, bok := value.(*int64); bok { + encodeSkip = true + if *b != 0 || entry.RenderZero { + valueNode = utils.CreateIntNode(strconv.Itoa(int(*b))) + valueNode.Line = line + } + } + if b, bok := value.(*float64); bok { + encodeSkip = true + if *b != 0 || entry.RenderZero { + formatFloat := strconv.FormatFloat(*b, 'f', -1, 64) + + // Always create float node for float64 values, even if they're whole numbers + // This handles cases like negative zero (-0.0) and ensures type consistency + valueNode = utils.CreateFloatNode(formatFloat) + + valueNode.Line = line + } + } + if b, bok := value.(*yaml.Node); bok && b.Kind == yaml.ScalarNode && b.Tag == "!!null" { + encodeSkip = true + valueNode = utils.CreateEmptyScalarNode() + valueNode.Line = line + } + if !encodeSkip { + var rawNode yaml.Node + if value != nil { + // check if is a node and it's null + if v, ko := value.(*yaml.Node); ko { + if v.Tag == "!!null" { + return parent + } + } + + err := rawNode.Encode(value) + if err != nil { + return parent + } else { + valueNode = &rawNode + valueNode.Line = line + } + } + } + } + + } + if nodeErrors != nil && len(nodeErrors) > 0 { + n.Errors = append(n.Errors, nodeErrors...) + } + if valueNode == nil { + return parent + } + if l != nil { + parent.Content = append(parent.Content, l, valueNode) + } else { + parent.Content = valueNode.Content + } + return parent +} + +// Renderable is an interface that can be implemented by types that provide a custom MarshalYAML method. +type Renderable interface { + MarshalYAML() (interface{}, error) +} + +// RenderableInline is an interface that can be implemented by types that provide a custom MarshalYAML method. +type RenderableInline interface { + MarshalYAMLInline() (interface{}, error) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/nodes/nodeentry.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/nodes/nodeentry.go new file mode 100644 index 000000000..4b2b93aaf --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/nodes/nodeentry.go @@ -0,0 +1,16 @@ +package nodes + +import "go.yaml.in/yaml/v4" + +// NodeEntry represents a single node used by NodeBuilder. +type NodeEntry struct { + Tag string + Key string + Value any + StringValue string + Line int + KeyStyle yaml.Style + // ValueStyle yaml.Style + RenderZero bool + LowValue any +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/overlay/action.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/overlay/action.go new file mode 100644 index 000000000..86b0dd3cd --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/overlay/action.go @@ -0,0 +1,85 @@ +// Copyright 2022-2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package overlay + +import ( + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/overlay" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// Action represents a high-level Overlay Action Object. +// https://spec.openapis.org/overlay/v1.1.0#action-object +type Action struct { + Target string `json:"target,omitempty" yaml:"target,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Update *yaml.Node `json:"update,omitempty" yaml:"update,omitempty"` + Remove bool `json:"remove,omitempty" yaml:"remove,omitempty"` + Copy string `json:"copy,omitempty" yaml:"copy,omitempty"` + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *low.Action +} + +// NewAction creates a new high-level Action instance from a low-level one. +func NewAction(action *low.Action) *Action { + a := new(Action) + a.low = action + if !action.Target.IsEmpty() { + a.Target = action.Target.Value + } + if !action.Description.IsEmpty() { + a.Description = action.Description.Value + } + if !action.Update.IsEmpty() { + a.Update = action.Update.Value + } + if !action.Remove.IsEmpty() { + a.Remove = action.Remove.Value + } + if !action.Copy.IsEmpty() { + a.Copy = action.Copy.Value + } + a.Extensions = high.ExtractExtensions(action.Extensions) + return a +} + +// GoLow returns the low-level Action instance used to create the high-level one. +func (a *Action) GoLow() *low.Action { + return a.low +} + +// GoLowUntyped returns the low-level Action instance with no type. +func (a *Action) GoLowUntyped() any { + return a.low +} + +// Render returns a YAML representation of the Action object as a byte slice. +func (a *Action) Render() ([]byte, error) { + return yaml.Marshal(a) +} + +// MarshalYAML creates a ready to render YAML representation of the Action object. +func (a *Action) MarshalYAML() (any, error) { + m := orderedmap.New[string, any]() + if a.Target != "" { + m.Set("target", a.Target) + } + if a.Description != "" { + m.Set("description", a.Description) + } + if a.Copy != "" { + m.Set("copy", a.Copy) + } + if a.Update != nil { + m.Set("update", a.Update) + } + if a.Remove { + m.Set("remove", a.Remove) + } + for pair := a.Extensions.First(); pair != nil; pair = pair.Next() { + m.Set(pair.Key(), pair.Value()) + } + return m, nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/overlay/info.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/overlay/info.go new file mode 100644 index 000000000..dc3eeb5f3 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/overlay/info.go @@ -0,0 +1,71 @@ +// Copyright 2022-2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package overlay + +import ( + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/overlay" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// Info represents a high-level Overlay Info Object. +// https://spec.openapis.org/overlay/v1.1.0#info-object +type Info struct { + Title string `json:"title,omitempty" yaml:"title,omitempty"` + Version string `json:"version,omitempty" yaml:"version,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *low.Info +} + +// NewInfo creates a new high-level Info instance from a low-level one. +func NewInfo(info *low.Info) *Info { + i := new(Info) + i.low = info + if !info.Title.IsEmpty() { + i.Title = info.Title.Value + } + if !info.Version.IsEmpty() { + i.Version = info.Version.Value + } + if !info.Description.IsEmpty() { + i.Description = info.Description.Value + } + i.Extensions = high.ExtractExtensions(info.Extensions) + return i +} + +// GoLow returns the low-level Info instance used to create the high-level one. +func (i *Info) GoLow() *low.Info { + return i.low +} + +// GoLowUntyped returns the low-level Info instance with no type. +func (i *Info) GoLowUntyped() any { + return i.low +} + +// Render returns a YAML representation of the Info object as a byte slice. +func (i *Info) Render() ([]byte, error) { + return yaml.Marshal(i) +} + +// MarshalYAML creates a ready to render YAML representation of the Info object. +func (i *Info) MarshalYAML() (any, error) { + m := orderedmap.New[string, any]() + if i.Title != "" { + m.Set("title", i.Title) + } + if i.Version != "" { + m.Set("version", i.Version) + } + if i.Description != "" { + m.Set("description", i.Description) + } + for pair := i.Extensions.First(); pair != nil; pair = pair.Next() { + m.Set(pair.Key(), pair.Value()) + } + return m, nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/overlay/overlay.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/overlay/overlay.go new file mode 100644 index 000000000..7a6288a8e --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/overlay/overlay.go @@ -0,0 +1,82 @@ +// Copyright 2022-2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package overlay + +import ( + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/overlay" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// Overlay represents a high-level OpenAPI Overlay document. +// https://spec.openapis.org/overlay/v1.0.0 +type Overlay struct { + Overlay string `json:"overlay,omitempty" yaml:"overlay,omitempty"` + Info *Info `json:"info,omitempty" yaml:"info,omitempty"` + Extends string `json:"extends,omitempty" yaml:"extends,omitempty"` + Actions []*Action `json:"actions,omitempty" yaml:"actions,omitempty"` + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *low.Overlay +} + +// NewOverlay creates a new high-level Overlay instance from a low-level one. +func NewOverlay(overlay *low.Overlay) *Overlay { + o := new(Overlay) + o.low = overlay + if !overlay.Overlay.IsEmpty() { + o.Overlay = overlay.Overlay.Value + } + if !overlay.Info.IsEmpty() { + o.Info = NewInfo(overlay.Info.Value) + } + if !overlay.Extends.IsEmpty() { + o.Extends = overlay.Extends.Value + } + if !overlay.Actions.IsEmpty() { + actions := make([]*Action, 0, len(overlay.Actions.Value)) + for _, action := range overlay.Actions.Value { + actions = append(actions, NewAction(action.Value)) + } + o.Actions = actions + } + o.Extensions = high.ExtractExtensions(overlay.Extensions) + return o +} + +// GoLow returns the low-level Overlay instance used to create the high-level one. +func (o *Overlay) GoLow() *low.Overlay { + return o.low +} + +// GoLowUntyped returns the low-level Overlay instance with no type. +func (o *Overlay) GoLowUntyped() any { + return o.low +} + +// Render returns a YAML representation of the Overlay object as a byte slice. +func (o *Overlay) Render() ([]byte, error) { + return yaml.Marshal(o) +} + +// MarshalYAML creates a ready to render YAML representation of the Overlay object. +func (o *Overlay) MarshalYAML() (interface{}, error) { + m := orderedmap.New[string, any]() + if o.Overlay != "" { + m.Set("overlay", o.Overlay) + } + if o.Info != nil { + m.Set("info", o.Info) + } + if o.Extends != "" { + m.Set("extends", o.Extends) + } + if len(o.Actions) > 0 { + m.Set("actions", o.Actions) + } + for pair := o.Extensions.First(); pair != nil; pair = pair.Next() { + m.Set(pair.Key(), pair.Value()) + } + return m, nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/shared.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/shared.go new file mode 100644 index 000000000..bc72d0853 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/shared.go @@ -0,0 +1,203 @@ +// Copyright 2022 Princess Beef Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +// Package high contains a set of high-level models that represent OpenAPI 2 and 3 documents. +// These high-level models (porcelain) are used by applications directly, rather than the low-level models +// plumbing) that are used to compose high level models. +// +// High level models are simple to navigate, strongly typed, precise representations of the OpenAPI schema +// that are created from an OpenAPI specification. +// +// All high level objects contains a 'GoLow' method. This 'GoLow' method will return the low-level model that +// was used to create it, which provides an engineer as much low level detail about the raw spec used to create +// those models, things like key/value breakdown of each value, lines, column, source comments etc. +package high + +import ( + "context" + "fmt" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// GoesLow is used to represent any high-level model. All high level models meet this interface and can be used to +// extract low-level models from any high-level model. +type GoesLow[T any] interface { + // GoLow returns the low-level object that was used to create the high-level object. This allows consumers + // to dive-down into the plumbing API at any point in the model. + GoLow() T +} + +// GoesLowUntyped is used to represent any high-level model. All high level models meet this interface and can be used to +// extract low-level models from any high-level model. +type GoesLowUntyped interface { + // GoLowUntyped returns the low-level object that was used to create the high-level object. This allows consumers + // to dive-down into the plumbing API at any point in the model. + GoLowUntyped() any +} + +// ExtractExtensions is a convenience method for converting low-level extension definitions, to a high level *orderedmap.Map[string, *yaml.Node] +// definition that is easier to consume in applications. +func ExtractExtensions(extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]]) *orderedmap.Map[string, *yaml.Node] { + return low.FromReferenceMap(extensions) +} + +// RenderInline creates an inline YAML representation of a high-level object with all references resolved. +// This is a shared helper used by MarshalYAMLInline implementations across high-level types. +func RenderInline(high, low any) (interface{}, error) { + nb := NewNodeBuilder(high, low) + nb.Resolve = true + return nb.Render(), nil +} + +// RenderInlineWithContext creates an inline YAML representation of a high-level object with all references resolved. +// Uses the provided context for cycle detection during inline rendering. +// The ctx parameter should be *base.InlineRenderContext but is typed as any to avoid import cycles. +func RenderInlineWithContext(high, low, ctx any) (interface{}, error) { + nb := NewNodeBuilder(high, low) + nb.Resolve = true + nb.RenderContext = ctx + return nb.Render(), nil +} + +// UnpackExtensions is a convenience function that makes it easy and simple to unpack an objects extensions +// into a complex type, provided as a generic. This function is for high-level models that implement `GoesLow()` +// and for low-level models that support extensions via `HasExtensions`. +// +// This feature will be upgraded at some point to hold a registry of types and extension mappings to make this +// functionality available a little more automatically. +// You can read more about the discussion here: https://github.com/pb33f/libopenapi/issues/8 +// +// `T` represents the Type you want to unpack into +// `R` represents the LOW type of the object that contains the extensions (not the high) +// `low` represents the HIGH type of the object that contains the extensions. +// +// to use: +// +// schema := schemaProxy.Schema() // any high-level object that has +// extensions, err := UnpackExtensions[MyComplexType, low.Schema](schema) +func UnpackExtensions[T any, R low.HasExtensions[T]](low GoesLow[R]) (*orderedmap.Map[string, *T], error) { + m := orderedmap.New[string, *T]() + ext := low.GoLow().GetExtensions() + for ext, value := range ext.FromOldest() { + g := new(T) + valueNode := value.ValueNode + err := valueNode.Decode(g) + if err != nil { + return nil, err + } + m.Set(ext.Value, g) + } + return m, nil +} + +// ExternalRefResolver is an interface for low-level objects that can be external references. +// This is used by ResolveExternalRef to resolve external $ref values during inline rendering. +type ExternalRefResolver interface { + IsReference() bool + GetReference() string + GetIndex() *index.SpecIndex +} + +// ExternalRefBuildFunc is a function that builds a low-level object from a resolved YAML node. +// It should create a new instance of the low-level type, call BuildModel and Build on it, +// and return the constructed object along with any error encountered. +type ExternalRefBuildFunc[L any] func(node *yaml.Node, idx *index.SpecIndex) (L, error) + +// ExternalRefResult contains the result of resolving an external reference. +type ExternalRefResult[H any, L any] struct { + High H + Low L + Resolved bool +} + +// ResolveExternalRef attempts to resolve an external reference from a low-level object. +// If the low-level object is an external reference (IsReference() returns true), this function +// will use the index to find and resolve the referenced component, build new low and high level +// objects from the resolved content, and return them. +// +// Parameters: +// - lowObj: the low-level object that may be an external reference +// - buildLow: function to build a new low-level object from the resolved YAML node +// - buildHigh: function to create a high-level object from the resolved low-level object +// +// Returns: +// - ExternalRefResult containing the resolved high and low objects if resolution succeeded +// - error if resolution failed (malformed YAML, build errors, etc.) +// +// If the object is not a reference or cannot be resolved, Resolved will be false and the +// caller should fall back to rendering the original object. +func ResolveExternalRef[H any, L any]( + lowObj ExternalRefResolver, + buildLow ExternalRefBuildFunc[L], + buildHigh func(L) H, +) (ExternalRefResult[H, L], error) { + var result ExternalRefResult[H, L] + + // not a reference, nothing to resolve + if lowObj == nil || !lowObj.IsReference() { + return result, nil + } + + idx := lowObj.GetIndex() + if idx == nil { + return result, nil + } + + ref := lowObj.GetReference() + resolved := idx.FindComponent(context.Background(), ref) + if resolved == nil || resolved.Node == nil { + return result, nil + } + + // build the low-level object from the resolved node + lowResolved, err := buildLow(resolved.Node, resolved.Index) + if err != nil { + return result, fmt.Errorf("failed to build resolved external reference '%s': %w", ref, err) + } + + // build the high-level object from the resolved low-level object + highResolved := buildHigh(lowResolved) + + result.High = highResolved + result.Low = lowResolved + result.Resolved = true + return result, nil +} + +// RenderExternalRef is a convenience function that resolves an external reference and renders it inline. +// This combines ResolveExternalRef with RenderInline for the common case where you want to +// resolve and immediately render an external reference. +// +// If the low-level object is not a reference or resolution fails gracefully (not found), +// this returns (nil, nil) and the caller should fall back to normal rendering. +// If resolution succeeds, returns the rendered YAML node. +// If an error occurs during resolution or rendering, returns the error. +func RenderExternalRef[H any, L any]( + lowObj ExternalRefResolver, + buildLow ExternalRefBuildFunc[L], + buildHigh func(L) H, +) (interface{}, error) { + result, err := ResolveExternalRef(lowObj, buildLow, buildHigh) + if err != nil || !result.Resolved { + return nil, err + } + return RenderInline(result.High, result.Low) +} + +// RenderExternalRefWithContext is like RenderExternalRef but passes a context for cycle detection. +func RenderExternalRefWithContext[H any, L any]( + lowObj ExternalRefResolver, + buildLow ExternalRefBuildFunc[L], + buildHigh func(L) H, + ctx any, +) (interface{}, error) { + result, err := ResolveExternalRef(lowObj, buildLow, buildHigh) + if err != nil || !result.Resolved { + return nil, err + } + return RenderInlineWithContext(result.High, result.Low, ctx) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/asyncresult.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/asyncresult.go new file mode 100644 index 000000000..d9193d7f5 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/asyncresult.go @@ -0,0 +1,6 @@ +package v2 + +type asyncResult[T any] struct { + key string + result T +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/definitions.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/definitions.go new file mode 100644 index 000000000..081b778cc --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/definitions.go @@ -0,0 +1,50 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "github.com/pb33f/libopenapi/datamodel" + highbase "github.com/pb33f/libopenapi/datamodel/high/base" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + lowbase "github.com/pb33f/libopenapi/datamodel/low/base" + low "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/orderedmap" +) + +// Definitions is a high-level represents of a Swagger / OpenAPI 2 Definitions object, backed by a low-level one. +// +// An object to hold data types that can be consumed and produced by operations. These data types can be primitives, +// arrays or models. +// - https://swagger.io/specification/v2/#definitionsObject +type Definitions struct { + Definitions *orderedmap.Map[string, *highbase.SchemaProxy] + low *low.Definitions +} + +// NewDefinitions will create a new high-level instance of a Definition from a low-level one. +func NewDefinitions(definitions *low.Definitions) *Definitions { + rd := new(Definitions) + rd.low = definitions + defs := orderedmap.New[string, *highbase.SchemaProxy]() + translateFunc := func(pair orderedmap.Pair[lowmodel.KeyReference[string], lowmodel.ValueReference[*lowbase.SchemaProxy]]) (asyncResult[*highbase.SchemaProxy], error) { + return asyncResult[*highbase.SchemaProxy]{ + key: pair.Key().Value, + result: highbase.NewSchemaProxy(&lowmodel.NodeReference[*lowbase.SchemaProxy]{ + Value: pair.Value().Value, + }), + }, nil + } + resultFunc := func(value asyncResult[*highbase.SchemaProxy]) error { + defs.Set(value.key, value.result) + return nil + } + _ = datamodel.TranslateMapParallel(definitions.Schemas, translateFunc, resultFunc) + rd.Definitions = defs + return rd +} + +// GoLow returns the low-level Definitions object used to create the high-level one. +func (d *Definitions) GoLow() *low.Definitions { + return d.low +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/examples.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/examples.go new file mode 100644 index 000000000..131fbb6b0 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/examples.go @@ -0,0 +1,34 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "github.com/pb33f/libopenapi/datamodel/low" + lowv2 "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// Example represents a high-level Swagger / OpenAPI 2 Example object, backed by a low level one. +// Allows sharing examples for operation responses +// - https://swagger.io/specification/v2/#exampleObject +type Example struct { + Values *orderedmap.Map[string, *yaml.Node] + low *lowv2.Examples +} + +// NewExample creates a new high-level Example instance from a low-level one. +func NewExample(examples *lowv2.Examples) *Example { + e := new(Example) + e.low = examples + if orderedmap.Len(examples.Values) > 0 { + e.Values = low.FromReferenceMap(examples.Values) + } + return e +} + +// GoLow returns the low-level Example used to create the high-level one. +func (e *Example) GoLow() *lowv2.Examples { + return e.low +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/header.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/header.go new file mode 100644 index 000000000..bb356aed5 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/header.go @@ -0,0 +1,108 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// Header Represents a high-level Swagger / OpenAPI 2 Header object, backed by a low-level one. +// A Header is essentially identical to a Parameter, except it does not contain 'name' or 'in' properties. +// - https://swagger.io/specification/v2/#headerObject +type Header struct { + Type string + Format string + Description string + Items *Items + CollectionFormat string + Default any + Maximum int + ExclusiveMaximum bool + Minimum int + ExclusiveMinimum bool + MaxLength int + MinLength int + Pattern string + MaxItems int + MinItems int + UniqueItems bool + Enum []any + MultipleOf int + Extensions *orderedmap.Map[string, *yaml.Node] + low *low.Header +} + +// NewHeader will create a new high-level Swagger / OpenAPI 2 Header instance, from a low-level one. +func NewHeader(header *low.Header) *Header { + h := new(Header) + h.low = header + h.Extensions = high.ExtractExtensions(header.Extensions) + if !header.Type.IsEmpty() { + h.Type = header.Type.Value + } + if !header.Format.IsEmpty() { + h.Format = header.Type.Value + } + if !header.Description.IsEmpty() { + h.Description = header.Description.Value + } + if !header.Items.IsEmpty() { + h.Items = NewItems(header.Items.Value) + } + if !header.CollectionFormat.IsEmpty() { + h.CollectionFormat = header.CollectionFormat.Value + } + if !header.Default.IsEmpty() { + h.Default = header.Default.Value + } + if !header.Maximum.IsEmpty() { + h.Maximum = header.Maximum.Value + } + if !header.ExclusiveMaximum.IsEmpty() { + h.ExclusiveMaximum = header.ExclusiveMaximum.Value + } + if !header.Minimum.IsEmpty() { + h.Minimum = header.Minimum.Value + } + if !header.ExclusiveMinimum.Value { + h.ExclusiveMinimum = header.ExclusiveMinimum.Value + } + if !header.MaxLength.IsEmpty() { + h.MaxLength = header.MaxLength.Value + } + if !header.MinLength.IsEmpty() { + h.MinLength = header.MinLength.Value + } + if !header.Pattern.IsEmpty() { + h.Pattern = header.Pattern.Value + } + if !header.MinItems.IsEmpty() { + h.MinItems = header.MinItems.Value + } + if !header.MaxItems.IsEmpty() { + h.MaxItems = header.MaxItems.Value + } + if !header.UniqueItems.IsEmpty() { + h.UniqueItems = header.UniqueItems.IsEmpty() + } + if !header.Enum.IsEmpty() { + var enums []any + for e := range header.Enum.Value { + enums = append(enums, header.Enum.Value[e].Value) + } + h.Enum = enums + } + if !header.MultipleOf.IsEmpty() { + h.MultipleOf = header.MultipleOf.Value + } + return h +} + +// GoLow returns the low-level header used to create the high-level one. +func (h *Header) GoLow() *low.Header { + return h.low +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/items.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/items.go new file mode 100644 index 000000000..2b6e1eda2 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/items.go @@ -0,0 +1,101 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + low "github.com/pb33f/libopenapi/datamodel/low/v2" + "go.yaml.in/yaml/v4" +) + +// Items is a high-level representation of a Swagger / OpenAPI 2 Items object, backed by a low level one. +// Items is a limited subset of JSON-Schema's items object. It is used by parameter definitions that are not +// located in "body" +// - https://swagger.io/specification/v2/#itemsObject +type Items struct { + Type string + Format string + CollectionFormat string + Items *Items + Default *yaml.Node + Maximum int + ExclusiveMaximum bool + Minimum int + ExclusiveMinimum bool + MaxLength int + MinLength int + Pattern string + MaxItems int + MinItems int + UniqueItems bool + Enum []*yaml.Node + MultipleOf int + low *low.Items +} + +// NewItems creates a new high-level Items instance from a low-level one. +func NewItems(items *low.Items) *Items { + i := new(Items) + i.low = items + if !items.Type.IsEmpty() { + i.Type = items.Type.Value + } + if !items.Format.IsEmpty() { + i.Format = items.Format.Value + } + if !items.Items.IsEmpty() { + i.Items = NewItems(items.Items.Value) + } + if !items.CollectionFormat.IsEmpty() { + i.CollectionFormat = items.CollectionFormat.Value + } + if !items.Default.IsEmpty() { + i.Default = items.Default.Value + } + if !items.Maximum.IsEmpty() { + i.Maximum = items.Maximum.Value + } + if !items.ExclusiveMaximum.IsEmpty() { + i.ExclusiveMaximum = items.ExclusiveMaximum.Value + } + if !items.Minimum.IsEmpty() { + i.Minimum = items.Minimum.Value + } + if !items.ExclusiveMinimum.IsEmpty() { + i.ExclusiveMinimum = items.ExclusiveMinimum.Value + } + if !items.MaxLength.IsEmpty() { + i.MaxLength = items.MaxLength.Value + } + if !items.MinLength.IsEmpty() { + i.MinLength = items.MinLength.Value + } + if !items.Pattern.IsEmpty() { + i.Pattern = items.Pattern.Value + } + if !items.MinItems.IsEmpty() { + i.MinItems = items.MinItems.Value + } + if !items.MaxItems.IsEmpty() { + i.MaxItems = items.MaxItems.Value + } + if !items.UniqueItems.IsEmpty() { + i.UniqueItems = items.UniqueItems.Value + } + if !items.Enum.IsEmpty() { + var enums []*yaml.Node + for e := range items.Enum.Value { + enums = append(enums, items.Enum.Value[e].Value) + } + i.Enum = enums + } + if !items.MultipleOf.IsEmpty() { + i.MultipleOf = items.MultipleOf.Value + } + return i +} + +// GoLow returns the low-level Items object that was used to create the high-level one. +func (i *Items) GoLow() *low.Items { + return i.low +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/operation.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/operation.go new file mode 100644 index 000000000..dd2a157b0 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/operation.go @@ -0,0 +1,105 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/datamodel/high/base" + low "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// Operation represents a high-level Swagger / OpenAPI 2 Operation object, backed by a low-level one. +// It describes a single API operation on a path. +// - https://swagger.io/specification/v2/#operationObject +type Operation struct { + Tags []string + Summary string + Description string + ExternalDocs *base.ExternalDoc + OperationId string + Consumes []string + Produces []string + Parameters []*Parameter + Responses *Responses + Schemes []string + Deprecated bool + Security []*base.SecurityRequirement + Extensions *orderedmap.Map[string, *yaml.Node] + low *low.Operation +} + +// NewOperation creates a new high-level Operation instance from a low-level one. +func NewOperation(operation *low.Operation) *Operation { + o := new(Operation) + o.low = operation + o.Extensions = high.ExtractExtensions(operation.Extensions) + if !operation.Tags.IsEmpty() { + var tags []string + for t := range operation.Tags.Value { + tags = append(tags, operation.Tags.Value[t].Value) + } + o.Tags = tags + } + if !operation.Summary.IsEmpty() { + o.Summary = operation.Summary.Value + } + if !operation.Description.IsEmpty() { + o.Description = operation.Description.Value + } + if !operation.ExternalDocs.IsEmpty() { + o.ExternalDocs = base.NewExternalDoc(operation.ExternalDocs.Value) + } + if !operation.OperationId.IsEmpty() { + o.OperationId = operation.OperationId.Value + } + if !operation.Consumes.IsEmpty() { + var cons []string + for c := range operation.Consumes.Value { + cons = append(cons, operation.Consumes.Value[c].Value) + } + o.Consumes = cons + } + if !operation.Produces.IsEmpty() { + var prods []string + for p := range operation.Produces.Value { + prods = append(prods, operation.Produces.Value[p].Value) + } + o.Produces = prods + } + if !operation.Parameters.IsEmpty() { + var params []*Parameter + for p := range operation.Parameters.Value { + params = append(params, NewParameter(operation.Parameters.Value[p].Value)) + } + o.Parameters = params + } + if !operation.Responses.IsEmpty() { + o.Responses = NewResponses(operation.Responses.Value) + } + if !operation.Schemes.IsEmpty() { + var schemes []string + for s := range operation.Schemes.Value { + schemes = append(schemes, operation.Schemes.Value[s].Value) + } + o.Schemes = schemes + } + if !operation.Deprecated.IsEmpty() { + o.Deprecated = operation.Deprecated.Value + } + if !operation.Security.IsEmpty() { + var sec []*base.SecurityRequirement + for s := range operation.Security.Value { + sec = append(sec, base.NewSecurityRequirement(operation.Security.Value[s].Value)) + } + o.Security = sec + } + return o +} + +// GoLow returns the low-level operation used to create the high-level one. +func (o *Operation) GoLow() *low.Operation { + return o.low +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/parameter.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/parameter.go new file mode 100644 index 000000000..c064f4b0d --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/parameter.go @@ -0,0 +1,167 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/datamodel/high/base" + low "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// Parameter represents a high-level Swagger / OpenAPI 2 Parameter object, backed by a low-level one. +// +// A unique parameter is defined by a combination of a name and location. +// +// There are five possible parameter types. +// +// Path +// +// Used together with Path Templating, where the parameter value is actually part of the operation's URL. +// This does not include the host or base path of the API. For example, in /items/{itemId}, the path parameter is itemId. +// +// Query +// +// Parameters that are appended to the URL. For example, in /items?id=###, the query parameter is id. +// +// Header +// +// Custom headers that are expected as part of the request. +// +// Body +// +// The payload that's appended to the HTTP request. Since there can only be one payload, there can only be one body parameter. +// The name of the body parameter has no effect on the parameter itself and is used for documentation purposes only. +// Since Form parameters are also in the payload, body and form parameters cannot exist together for the same operation. +// +// Form +// +// Used to describe the payload of an HTTP request when either application/x-www-form-urlencoded, multipart/form-data +// or both are used as the content type of the request (in Swagger's definition, the consumes property of an operation). +// This is the only parameter type that can be used to send files, thus supporting the file type. Since form parameters +// are sent in the payload, they cannot be declared together with a body parameter for the same operation. Form +// parameters have a different format based on the content-type used (for further details, +// consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4): +// application/x-www-form-urlencoded - Similar to the format of Query parameters but as a payload. For example, +// foo=1&bar=swagger - both foo and bar are form parameters. This is normally used for simple parameters that are +// being transferred. +// multipart/form-data - each parameter takes a section in the payload with an internal header. For example, for +// the header Content-Disposition: form-data; name="submit-name" the name of the parameter is +// submit-name. This type of form parameters is more commonly used for file transfers +// +// https://swagger.io/specification/v2/#parameterObject +type Parameter struct { + Name string + In string + Type string + Format string + Description string + Required *bool + AllowEmptyValue *bool + Schema *base.SchemaProxy + Items *Items + CollectionFormat string + Default *yaml.Node + Maximum *int + ExclusiveMaximum *bool + Minimum *int + ExclusiveMinimum *bool + MaxLength *int + MinLength *int + Pattern string + MaxItems *int + MinItems *int + UniqueItems *bool + Enum []*yaml.Node + MultipleOf *int + Extensions *orderedmap.Map[string, *yaml.Node] + low *low.Parameter +} + +// NewParameter creates a new high-level instance of a Parameter from a low-level one. +func NewParameter(parameter *low.Parameter) *Parameter { + p := new(Parameter) + p.low = parameter + p.Extensions = high.ExtractExtensions(parameter.Extensions) + if !parameter.Name.IsEmpty() { + p.Name = parameter.Name.Value + } + if !parameter.In.IsEmpty() { + p.In = parameter.In.Value + } + if !parameter.Type.IsEmpty() { + p.Type = parameter.Type.Value + } + if !parameter.Format.IsEmpty() { + p.Format = parameter.Format.Value + } + if !parameter.Description.IsEmpty() { + p.Description = parameter.Description.Value + } + if !parameter.Required.IsEmpty() { + p.Required = ¶meter.Required.Value + } + if !parameter.AllowEmptyValue.IsEmpty() { + p.AllowEmptyValue = ¶meter.AllowEmptyValue.Value + } + if !parameter.Schema.IsEmpty() { + p.Schema = base.NewSchemaProxy(¶meter.Schema) + } + if !parameter.Items.IsEmpty() { + p.Items = NewItems(parameter.Items.Value) + } + if !parameter.CollectionFormat.IsEmpty() { + p.CollectionFormat = parameter.CollectionFormat.Value + } + if !parameter.Default.IsEmpty() { + p.Default = parameter.Default.Value + } + if !parameter.Maximum.IsEmpty() { + p.Maximum = ¶meter.Maximum.Value + } + if !parameter.ExclusiveMaximum.IsEmpty() { + p.ExclusiveMaximum = ¶meter.ExclusiveMaximum.Value + } + if !parameter.Minimum.IsEmpty() { + p.Minimum = ¶meter.Minimum.Value + } + if !parameter.ExclusiveMinimum.IsEmpty() { + p.ExclusiveMinimum = ¶meter.ExclusiveMinimum.Value + } + if !parameter.MaxLength.IsEmpty() { + p.MaxLength = ¶meter.MaxLength.Value + } + if !parameter.MinLength.IsEmpty() { + p.MinLength = ¶meter.MinLength.Value + } + if !parameter.Pattern.IsEmpty() { + p.Pattern = parameter.Pattern.Value + } + if !parameter.MinItems.IsEmpty() { + p.MinItems = ¶meter.MinItems.Value + } + if !parameter.MaxItems.IsEmpty() { + p.MaxItems = ¶meter.MaxItems.Value + } + if !parameter.UniqueItems.IsEmpty() { + p.UniqueItems = ¶meter.UniqueItems.Value + } + if !parameter.Enum.IsEmpty() { + var enums []*yaml.Node + for e := range parameter.Enum.Value { + enums = append(enums, parameter.Enum.Value[e].Value) + } + p.Enum = enums + } + if !parameter.MultipleOf.IsEmpty() { + p.MultipleOf = ¶meter.MultipleOf.Value + } + return p +} + +// GoLow returns the low-level Parameter used to create the high-level one. +func (p *Parameter) GoLow() *low.Parameter { + return p.low +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/parameter_definitions.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/parameter_definitions.go new file mode 100644 index 000000000..7fede3822 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/parameter_definitions.go @@ -0,0 +1,48 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "github.com/pb33f/libopenapi/datamodel" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + low "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/orderedmap" +) + +// ParameterDefinitions is a high-level representation of a Swagger / OpenAPI 2 Parameters Definitions object +// that is backed by a low-level one. +// +// ParameterDefinitions holds parameters to be reused across operations. Parameter definitions can be +// referenced to the ones defined here. It does not define global operation parameters +// - https://swagger.io/specification/v2/#parametersDefinitionsObject +type ParameterDefinitions struct { + Definitions *orderedmap.Map[string, *Parameter] + low *low.ParameterDefinitions +} + +// NewParametersDefinitions creates a new instance of a high-level ParameterDefinitions, from a low-level one. +// Every parameter is extracted asynchronously due to the potential depth +func NewParametersDefinitions(parametersDefinitions *low.ParameterDefinitions) *ParameterDefinitions { + pd := new(ParameterDefinitions) + pd.low = parametersDefinitions + params := orderedmap.New[string, *Parameter]() + translateFunc := func(pair orderedmap.Pair[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.Parameter]]) (asyncResult[*Parameter], error) { + return asyncResult[*Parameter]{ + key: pair.Key().Value, + result: NewParameter(pair.Value().Value), + }, nil + } + resultFunc := func(value asyncResult[*Parameter]) error { + params.Set(value.key, value.result) + return nil + } + _ = datamodel.TranslateMapParallel(parametersDefinitions.Definitions, translateFunc, resultFunc) + pd.Definitions = params + return pd +} + +// GoLow returns the low-level ParameterDefinitions instance that backs the low-level one. +func (p *ParameterDefinitions) GoLow() *low.ParameterDefinitions { + return p.low +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/path_item.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/path_item.go new file mode 100644 index 000000000..ee7b13b76 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/path_item.go @@ -0,0 +1,170 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "reflect" + "slices" + "sync" + + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/datamodel/low" + lowV2 "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// PathItem represents a high-level Swagger / OpenAPI 2 PathItem object backed by a low-level one. +// +// Describes the operations available on a single path. A Path Item may be empty, due to ACL constraints. +// The path itself is still exposed to the tooling, but will not know which operations and parameters +// are available. +// - https://swagger.io/specification/v2/#pathItemObject +type PathItem struct { + Ref string + Get *Operation + Put *Operation + Post *Operation + Delete *Operation + Options *Operation + Head *Operation + Patch *Operation + Parameters []*Parameter + Extensions *orderedmap.Map[string, *yaml.Node] + low *lowV2.PathItem +} + +// NewPathItem will create a new high-level PathItem from a low-level one. All paths are built out asynchronously. +func NewPathItem(pathItem *lowV2.PathItem) *PathItem { + p := new(PathItem) + p.low = pathItem + p.Extensions = high.ExtractExtensions(pathItem.Extensions) + if !pathItem.Parameters.IsEmpty() { + var params []*Parameter + for k := range pathItem.Parameters.Value { + params = append(params, NewParameter(pathItem.Parameters.Value[k].Value)) + } + p.Parameters = params + } + buildOperation := func(method string, op *lowV2.Operation) *Operation { + return NewOperation(op) + } + + var wg sync.WaitGroup + if !pathItem.Get.IsEmpty() { + wg.Add(1) + go func() { + p.Get = buildOperation(lowV2.GetLabel, pathItem.Get.Value) + wg.Done() + }() + } + if !pathItem.Put.IsEmpty() { + wg.Add(1) + go func() { + p.Put = buildOperation(lowV2.PutLabel, pathItem.Put.Value) + wg.Done() + }() + } + if !pathItem.Post.IsEmpty() { + wg.Add(1) + go func() { + p.Post = buildOperation(lowV2.PostLabel, pathItem.Post.Value) + wg.Done() + }() + } + if !pathItem.Patch.IsEmpty() { + wg.Add(1) + go func() { + p.Patch = buildOperation(lowV2.PatchLabel, pathItem.Patch.Value) + wg.Done() + }() + } + if !pathItem.Delete.IsEmpty() { + wg.Add(1) + go func() { + p.Delete = buildOperation(lowV2.DeleteLabel, pathItem.Delete.Value) + wg.Done() + }() + } + if !pathItem.Head.IsEmpty() { + wg.Add(1) + go func() { + p.Head = buildOperation(lowV2.HeadLabel, pathItem.Head.Value) + wg.Done() + }() + } + if !pathItem.Options.IsEmpty() { + wg.Add(1) + go func() { + p.Options = buildOperation(lowV2.OptionsLabel, pathItem.Options.Value) + wg.Done() + }() + } + wg.Wait() + return p +} + +// GoLow returns the low-level PathItem used to create the high-level one. +func (p *PathItem) GoLow() *lowV2.PathItem { + return p.low +} + +func (p *PathItem) GetOperations() *orderedmap.Map[string, *Operation] { + o := orderedmap.New[string, *Operation]() + + // TODO: this is a bit of a hack, but it works for now. We might just want to actually pull the data out of the document as a map and split it into the individual operations + + type op struct { + name string + op *Operation + line int + } + + getLine := func(field string, idx int) int { + if p.GoLow() == nil { + return idx + } + + l, ok := reflect.ValueOf(p.GoLow()).Elem().FieldByName(field).Interface().(low.NodeReference[*lowV2.Operation]) + if !ok || l.GetKeyNode() == nil { + return idx + } + + return l.GetKeyNode().Line + } + + ops := []op{} + + if p.Get != nil { + ops = append(ops, op{name: lowV2.GetLabel, op: p.Get, line: getLine("Get", -7)}) + } + if p.Put != nil { + ops = append(ops, op{name: lowV2.PutLabel, op: p.Put, line: getLine("Put", -6)}) + } + if p.Post != nil { + ops = append(ops, op{name: lowV2.PostLabel, op: p.Post, line: getLine("Post", -5)}) + } + if p.Delete != nil { + ops = append(ops, op{name: lowV2.DeleteLabel, op: p.Delete, line: getLine("Delete", -4)}) + } + if p.Options != nil { + ops = append(ops, op{name: lowV2.OptionsLabel, op: p.Options, line: getLine("Options", -3)}) + } + if p.Head != nil { + ops = append(ops, op{name: lowV2.HeadLabel, op: p.Head, line: getLine("Head", -2)}) + } + if p.Patch != nil { + ops = append(ops, op{name: lowV2.PatchLabel, op: p.Patch, line: getLine("Patch", -1)}) + } + + slices.SortStableFunc(ops, func(a op, b op) int { + return a.line - b.line + }) + + for _, op := range ops { + o.Set(op.name, op.op) + } + + return o +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/paths.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/paths.go new file mode 100644 index 000000000..ed271d24c --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/paths.go @@ -0,0 +1,49 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/datamodel/low" + v2low "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// Paths represents a high-level Swagger / OpenAPI Paths object, backed by a low-level one. +type Paths struct { + PathItems *orderedmap.Map[string, *PathItem] + Extensions *orderedmap.Map[string, *yaml.Node] + low *v2low.Paths +} + +// NewPaths creates a new high-level instance of Paths from a low-level one. +func NewPaths(paths *v2low.Paths) *Paths { + p := new(Paths) + p.low = paths + p.Extensions = high.ExtractExtensions(paths.Extensions) + pathItems := orderedmap.New[string, *PathItem]() + + translateFunc := func(pair orderedmap.Pair[low.KeyReference[string], low.ValueReference[*v2low.PathItem]]) (asyncResult[*PathItem], error) { + return asyncResult[*PathItem]{ + key: pair.Key().Value, + result: NewPathItem(pair.Value().Value), + }, nil + } + resultFunc := func(result asyncResult[*PathItem]) error { + pathItems.Set(result.key, result.result) + return nil + } + _ = datamodel.TranslateMapParallel[low.KeyReference[string], low.ValueReference[*v2low.PathItem], asyncResult[*PathItem]]( + paths.PathItems, translateFunc, resultFunc, + ) + p.PathItems = pathItems + return p +} + +// GoLow returns the low-level Paths instance that backs the high level one. +func (p *Paths) GoLow() *v2low.Paths { + return p.low +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/response.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/response.go new file mode 100644 index 000000000..9478c1e72 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/response.go @@ -0,0 +1,50 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/datamodel/high/base" + "github.com/pb33f/libopenapi/datamodel/low" + lowv2 "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// Response is a representation of a high-level Swagger / OpenAPI 2 Response object, backed by a low-level one. +// Response describes a single response from an API Operation +// - https://swagger.io/specification/v2/#responseObject +type Response struct { + Description string + Schema *base.SchemaProxy + Headers *orderedmap.Map[string, *Header] + Examples *Example + Extensions *orderedmap.Map[string, *yaml.Node] + low *lowv2.Response +} + +// NewResponse creates a new high-level instance of Response from a low level one. +func NewResponse(response *lowv2.Response) *Response { + r := new(Response) + r.low = response + r.Extensions = high.ExtractExtensions(response.Extensions) + if !response.Description.IsEmpty() { + r.Description = response.Description.Value + } + if !response.Schema.IsEmpty() { + r.Schema = base.NewSchemaProxy(&response.Schema) + } + if !response.Headers.IsEmpty() { + r.Headers = low.FromReferenceMapWithFunc(response.Headers.Value, NewHeader) + } + if !response.Examples.IsEmpty() { + r.Examples = NewExample(response.Examples.Value) + } + return r +} + +// GoLow will return the low-level Response instance used to create the high level one. +func (r *Response) GoLow() *lowv2.Response { + return r.low +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/responses.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/responses.go new file mode 100644 index 000000000..0da1e0635 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/responses.go @@ -0,0 +1,55 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/datamodel/high" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + low "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// Responses is a high-level representation of a Swagger / OpenAPI 2 Responses object, backed by a low level one. +type Responses struct { + Codes *orderedmap.Map[string, *Response] + Default *Response + Extensions *orderedmap.Map[string, *yaml.Node] + low *low.Responses +} + +// NewResponses will create a new high-level instance of Responses from a low-level one. +func NewResponses(responses *low.Responses) *Responses { + r := new(Responses) + r.low = responses + r.Extensions = high.ExtractExtensions(responses.Extensions) + + if !responses.Default.IsEmpty() { + r.Default = NewResponse(responses.Default.Value) + } + + if orderedmap.Len(responses.Codes) > 0 { + resp := orderedmap.New[string, *Response]() + translateFunc := func(pair orderedmap.Pair[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.Response]]) (asyncResult[*Response], error) { + return asyncResult[*Response]{ + key: pair.Key().Value, + result: NewResponse(pair.Value().Value), + }, nil + } + resultFunc := func(value asyncResult[*Response]) error { + resp.Set(value.key, value.result) + return nil + } + _ = datamodel.TranslateMapParallel(responses.Codes, translateFunc, resultFunc) + r.Codes = resp + } + + return r +} + +// GoLow will return the low-level object used to create the high-level one. +func (r *Responses) GoLow() *low.Responses { + return r.low +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/responses_definitions.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/responses_definitions.go new file mode 100644 index 000000000..141b7be56 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/responses_definitions.go @@ -0,0 +1,48 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "github.com/pb33f/libopenapi/datamodel" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + low "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/orderedmap" +) + +// ResponsesDefinitions is a high-level representation of a Swagger / OpenAPI 2 Responses Definitions object. +// that is backed by a low-level one. +// +// ResponsesDefinitions is an object to hold responses to be reused across operations. Response definitions can be +// referenced to the ones defined here. It does not define global operation responses +// - https://swagger.io/specification/v2/#responsesDefinitionsObject +type ResponsesDefinitions struct { + Definitions *orderedmap.Map[string, *Response] + low *low.ResponsesDefinitions +} + +// NewResponsesDefinitions will create a new high-level instance of ResponsesDefinitions from a low-level one. +func NewResponsesDefinitions(responsesDefinitions *low.ResponsesDefinitions) *ResponsesDefinitions { + rd := new(ResponsesDefinitions) + rd.low = responsesDefinitions + responses := orderedmap.New[string, *Response]() + translateFunc := func(pair orderedmap.Pair[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.Response]]) (asyncResult[*Response], error) { + return asyncResult[*Response]{ + key: pair.Key().Value, + result: NewResponse(pair.Value().Value), + }, nil + } + resultFunc := func(value asyncResult[*Response]) error { + responses.Set(value.key, value.result) + return nil + } + + _ = datamodel.TranslateMapParallel(responsesDefinitions.Definitions, translateFunc, resultFunc) + rd.Definitions = responses + return rd +} + +// GoLow returns the low-level ResponsesDefinitions used to create the high-level one. +func (r *ResponsesDefinitions) GoLow() *low.ResponsesDefinitions { + return r.low +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/scopes.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/scopes.go new file mode 100644 index 000000000..52ec296cb --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/scopes.go @@ -0,0 +1,32 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "github.com/pb33f/libopenapi/datamodel/low" + lowv2 "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/orderedmap" +) + +// Scopes is a high-level representation of a Swagger / OpenAPI 2 OAuth2 Scopes object, that is backed by a low-level one. +// +// Scopes lists the available scopes for an OAuth2 security scheme. +// - https://swagger.io/specification/v2/#scopesObject +type Scopes struct { + Values *orderedmap.Map[string, string] + low *lowv2.Scopes +} + +// NewScopes creates a new high-level instance of Scopes from a low-level one. +func NewScopes(scopes *lowv2.Scopes) *Scopes { + s := new(Scopes) + s.low = scopes + s.Values = low.FromReferenceMap(scopes.Values) + return s +} + +// GoLow returns the low-level instance of Scopes used to create the high-level one. +func (s *Scopes) GoLow() *lowv2.Scopes { + return s.low +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/security_definitions.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/security_definitions.go new file mode 100644 index 000000000..67ad0697c --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/security_definitions.go @@ -0,0 +1,48 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "github.com/pb33f/libopenapi/datamodel" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + low "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/orderedmap" +) + +// SecurityDefinitions is a high-level representation of a Swagger / OpenAPI 2 Security Definitions object, that +// is backed by a low-level one. +// +// A declaration of the security schemes available to be used in the specification. This does not enforce the security +// schemes on the operations and only serves to provide the relevant details for each scheme +// - https://swagger.io/specification/v2/#securityDefinitionsObject +type SecurityDefinitions struct { + Definitions *orderedmap.Map[string, *SecurityScheme] + low *low.SecurityDefinitions +} + +// NewSecurityDefinitions creates a new high-level instance of a SecurityDefinitions from a low-level one. +func NewSecurityDefinitions(definitions *low.SecurityDefinitions) *SecurityDefinitions { + sd := new(SecurityDefinitions) + sd.low = definitions + schemes := orderedmap.New[string, *SecurityScheme]() + translateFunc := func(pair orderedmap.Pair[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.SecurityScheme]]) (asyncResult[*SecurityScheme], error) { + return asyncResult[*SecurityScheme]{ + key: pair.Key().Value, + result: NewSecurityScheme(pair.Value().Value), + }, nil + } + resultFunc := func(value asyncResult[*SecurityScheme]) error { + schemes.Set(value.key, value.result) + return nil + } + _ = datamodel.TranslateMapParallel(definitions.Definitions, translateFunc, resultFunc) + + sd.Definitions = schemes + return sd +} + +// GoLow returns the low-level SecurityDefinitions instance used to create the high-level one. +func (sd *SecurityDefinitions) GoLow() *low.SecurityDefinitions { + return sd.low +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/security_scheme.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/security_scheme.go new file mode 100644 index 000000000..55606f375 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/security_scheme.go @@ -0,0 +1,68 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// SecurityScheme is a high-level representation of a Swagger / OpenAPI 2 SecurityScheme object +// backed by a low-level one. +// +// SecurityScheme allows the definition of a security scheme that can be used by the operations. Supported schemes are +// basic authentication, an API key (either as a header or as a query parameter) and OAuth2's common flows +// (implicit, password, application and access code) +// - https://swagger.io/specification/v2/#securityDefinitionsObject +type SecurityScheme struct { + Type string + Description string + Name string + In string + Flow string + AuthorizationUrl string + TokenUrl string + Scopes *Scopes + Extensions *orderedmap.Map[string, *yaml.Node] + low *low.SecurityScheme +} + +// NewSecurityScheme creates a new instance of SecurityScheme from a low-level one. +func NewSecurityScheme(securityScheme *low.SecurityScheme) *SecurityScheme { + s := new(SecurityScheme) + s.low = securityScheme + s.Extensions = high.ExtractExtensions(securityScheme.Extensions) + if !securityScheme.Type.IsEmpty() { + s.Type = securityScheme.Type.Value + } + if !securityScheme.Description.IsEmpty() { + s.Description = securityScheme.Description.Value + } + if !securityScheme.Name.IsEmpty() { + s.Name = securityScheme.Name.Value + } + if !securityScheme.In.IsEmpty() { + s.In = securityScheme.In.Value + } + if !securityScheme.Flow.IsEmpty() { + s.Flow = securityScheme.Flow.Value + } + if !securityScheme.AuthorizationUrl.IsEmpty() { + s.AuthorizationUrl = securityScheme.AuthorizationUrl.Value + } + if !securityScheme.TokenUrl.IsEmpty() { + s.TokenUrl = securityScheme.TokenUrl.Value + } + if !securityScheme.Scopes.IsEmpty() { + s.Scopes = NewScopes(securityScheme.Scopes.Value) + } + return s +} + +// GoLow returns the low-level SecurityScheme that was used to create the high-level one. +func (s *SecurityScheme) GoLow() *low.SecurityScheme { + return s.low +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/swagger.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/swagger.go new file mode 100644 index 000000000..bd06ae4f0 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v2/swagger.go @@ -0,0 +1,178 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +// Package v2 represents all Swagger / OpenAPI 2 high-level models. High-level models are easy to navigate +// and simple to extract what ever is required from a specification. +// +// High-level models are backed by low-level ones. There is a 'GoLow()' method available on every high level +// object. 'Going Low' allows engineers to transition from a high-level or 'porcelain' API, to a low-level 'plumbing' +// API, which provides fine grain detail to the underlying AST powering the data, lines, columns, raw nodes etc. +// +// IMPORTANT: As a general rule, Swagger / OpenAPI 2 should be avoided for new projects. +// VERY IMPORTANT: pb33f is no longer maintaining the v2 model. It's a commercial product (Swagger) by a company (SmartBear) and not OpenAPI. +// PLEASE DO NOT USE THIS MODEL UNLESS YOU HAVE TO. IT'S HERE FOR LEGACY SUPPORT ONLY. Upgrade to 3x! +package v2 + +import ( + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/datamodel/high/base" + low "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// Swagger represents a high-level Swagger / OpenAPI 2 document. An instance of Swagger is the root of the specification. +type Swagger struct { + // Swagger is the version of Swagger / OpenAPI being used, extracted from the 'swagger: 2.x' definition. + Swagger string + + // Info represents a specification Info definition. + // Provides metadata about the API. The metadata can be used by the clients if needed. + // - https://swagger.io/specification/v2/#infoObject + Info *base.Info + + // Host is The host (name or ip) serving the API. This MUST be the host only and does not include the scheme nor + // sub-paths. It MAY include a port. If the host is not included, the host serving the documentation is to be used + // (including the port). The host does not support path templating. + Host string + + // BasePath is The base path on which the API is served, which is relative to the host. If it is not included, the API is + // served directly under the host. The value MUST start with a leading slash (/). + // The basePath does not support path templating. + BasePath string + + // Schemes represents the transfer protocol of the API. Requirements MUST be from the list: "http", "https", "ws", "wss". + // If the schemes is not included, the default scheme to be used is the one used to access + // the Swagger definition itself. + Schemes []string + + // Consumes is a list of MIME types the APIs can consume. This is global to all APIs but can be overridden on + // specific API calls. Value MUST be as described under Mime Types. + Consumes []string + + // Produces is a list of MIME types the APIs can produce. This is global to all APIs but can be overridden on + // specific API calls. Value MUST be as described under Mime Types. + Produces []string + + // Paths are the paths and operations for the API. Perhaps the most important part of the specification. + // - https://swagger.io/specification/v2/#pathsObject + Paths *Paths + + // Definitions is an object to hold data types produced and consumed by operations. It's composed of Schema instances + // - https://swagger.io/specification/v2/#definitionsObject + Definitions *Definitions + + // Parameters is an object to hold parameters that can be used across operations. + // This property does not define global parameters for all operations. + // - https://swagger.io/specification/v2/#parametersDefinitionsObject + Parameters *ParameterDefinitions + + // Responses is an object to hold responses that can be used across operations. + // This property does not define global responses for all operations. + // - https://swagger.io/specification/v2/#responsesDefinitionsObject + Responses *ResponsesDefinitions + + // SecurityDefinitions represents security scheme definitions that can be used across the specification. + // - https://swagger.io/specification/v2/#securityDefinitionsObject + SecurityDefinitions *SecurityDefinitions + + // Security is a declaration of which security schemes are applied for the API as a whole. The list of values + // describes alternative security schemes that can be used (that is, there is a logical OR between the security + // requirements). Individual operations can override this definition. + // - https://swagger.io/specification/v2/#securityRequirementObject + Security []*base.SecurityRequirement + + // Tags are A list of tags used by the specification with additional metadata. + // The order of the tags can be used to reflect on their order by the parsing tools. Not all tags that are used + // by the Operation Object must be declared. The tags that are not declared may be organized randomly or based + // on the tools' logic. Each tag name in the list MUST be unique. + // - https://swagger.io/specification/v2/#tagObject + Tags []*base.Tag + + // ExternalDocs is an instance of base.ExternalDoc for.. well, obvious really, innit. + ExternalDocs *base.ExternalDoc + + // Extensions contains all custom extensions defined for the top-level document. + Extensions *orderedmap.Map[string, *yaml.Node] + low *low.Swagger +} + +// NewSwaggerDocument will create a new high-level Swagger document from a low-level one. +func NewSwaggerDocument(document *low.Swagger) *Swagger { + d := new(Swagger) + d.low = document + d.Extensions = high.ExtractExtensions(document.Extensions) + if !document.Info.IsEmpty() { + d.Info = base.NewInfo(document.Info.Value) + } + if !document.Swagger.IsEmpty() { + d.Swagger = document.Swagger.Value + } + if !document.Host.IsEmpty() { + d.Host = document.Host.Value + } + if !document.BasePath.IsEmpty() { + d.BasePath = document.BasePath.Value + } + + if !document.Schemes.IsEmpty() { + var schemes []string + for s := range document.Schemes.Value { + schemes = append(schemes, document.Schemes.Value[s].Value) + } + d.Schemes = schemes + } + if !document.Consumes.IsEmpty() { + var consumes []string + for c := range document.Consumes.Value { + consumes = append(consumes, document.Consumes.Value[c].Value) + } + d.Consumes = consumes + } + if !document.Produces.IsEmpty() { + var produces []string + for p := range document.Produces.Value { + produces = append(produces, document.Produces.Value[p].Value) + } + d.Produces = produces + } + if !document.Paths.IsEmpty() { + d.Paths = NewPaths(document.Paths.Value) + } + if !document.Definitions.IsEmpty() { + d.Definitions = NewDefinitions(document.Definitions.Value) + } + if !document.Parameters.IsEmpty() { + d.Parameters = NewParametersDefinitions(document.Parameters.Value) + } + + if !document.Responses.IsEmpty() { + d.Responses = NewResponsesDefinitions(document.Responses.Value) + } + if !document.SecurityDefinitions.IsEmpty() { + d.SecurityDefinitions = NewSecurityDefinitions(document.SecurityDefinitions.Value) + } + if !document.Security.IsEmpty() { + var security []*base.SecurityRequirement + for s := range document.Security.Value { + security = append(security, base.NewSecurityRequirement(document.Security.Value[s].Value)) + } + d.Security = security + } + if !document.Tags.IsEmpty() { + var tags []*base.Tag + for t := range document.Tags.Value { + tags = append(tags, base.NewTag(document.Tags.Value[t].Value)) + } + d.Tags = tags + } + if !document.ExternalDocs.IsEmpty() { + d.ExternalDocs = base.NewExternalDoc(document.ExternalDocs.Value) + } + return d +} + +// GoLow returns the low-level Swagger instance that was used to create the high-level one. +func (s *Swagger) GoLow() *low.Swagger { + return s.low +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/asyncresult.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/asyncresult.go new file mode 100644 index 000000000..caa53409f --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/asyncresult.go @@ -0,0 +1,6 @@ +package v3 + +type asyncResult[T any] struct { + key string + result T +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/callback.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/callback.go new file mode 100644 index 000000000..8b9d8f2ae --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/callback.go @@ -0,0 +1,278 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + "sort" + + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/datamodel/low" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + lowv3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// buildLowCallback builds a low-level Callback from a resolved YAML node. +func buildLowCallback(node *yaml.Node, idx *index.SpecIndex) (*lowv3.Callback, error) { + var cb lowv3.Callback + _ = lowmodel.BuildModel(node, &cb) + if err := cb.Build(context.Background(), nil, node, idx); err != nil { + return nil, err + } + return &cb, nil +} + +// Callback represents a high-level Callback object for OpenAPI 3+. +// +// A map of possible out-of band callbacks related to the parent operation. Each value in the map is a +// PathItem Object that describes a set of requests that may be initiated by the API provider and the expected +// responses. The key value used to identify the path item object is an expression, evaluated at runtime, +// that identifies a URL to use for the callback operation. +// - https://spec.openapis.org/oas/v3.1.0#callback-object +type Callback struct { + Reference string `json:"$ref,omitempty" yaml:"$ref,omitempty"` + Expression *orderedmap.Map[string, *PathItem] `json:"-" yaml:"-"` + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *lowv3.Callback +} + +// NewCallback creates a new high-level callback from a low-level one. +func NewCallback(lowCallback *lowv3.Callback) *Callback { + n := new(Callback) + n.low = lowCallback + n.Expression = low.FromReferenceMapWithFunc(lowCallback.Expression, NewPathItem) + n.Extensions = high.ExtractExtensions(lowCallback.Extensions) + return n +} + +// GoLow returns the low-level Callback instance used to create the high-level one. +func (c *Callback) GoLow() *lowv3.Callback { + return c.low +} + +// GoLowUntyped will return the low-level Callback instance that was used to create the high-level one, with no type +func (c *Callback) GoLowUntyped() any { + return c.low +} + +// IsReference returns true if this Callback is a reference to another Callback definition. +func (c *Callback) IsReference() bool { + return c.Reference != "" +} + +// GetReference returns the reference string if this is a reference Callback. +func (c *Callback) GetReference() string { + return c.Reference +} + +// Render will return a YAML representation of the Callback object as a byte slice. +func (c *Callback) Render() ([]byte, error) { + return yaml.Marshal(c) +} + +// RenderInline will return an YAML representation of the Callback object as a byte slice with references resolved. +func (c *Callback) RenderInline() ([]byte, error) { + d, _ := c.MarshalYAMLInline() + return yaml.Marshal(d) +} + +// MarshalYAML will create a ready to render YAML representation of the Paths object. +func (c *Callback) MarshalYAML() (interface{}, error) { + // Handle reference-only callback + if c.Reference != "" { + return utils.CreateRefNode(c.Reference), nil + } + // map keys correctly. + m := utils.CreateEmptyMapNode() + type pathItem struct { + pi *PathItem + path string + line int + style yaml.Style + rendered *yaml.Node + } + var mapped []*pathItem + + for k, pi := range c.Expression.FromOldest() { + ln := 9999 // default to a high value to weight new content to the bottom. + var style yaml.Style + if c.low != nil { + lpi := c.low.FindExpression(k) + if lpi != nil { + ln = lpi.ValueNode.Line + } + + for lk := range c.low.Expression.KeysFromOldest() { + if lk.Value == k { + style = lk.KeyNode.Style + break + } + } + } + mapped = append(mapped, &pathItem{pi, k, ln, style, nil}) + } + + nb := high.NewNodeBuilder(c, c.low) + extNode := nb.Render() + if extNode != nil && extNode.Content != nil { + var label string + for u := range extNode.Content { + if u%2 == 0 { + label = extNode.Content[u].Value + continue + } + mapped = append(mapped, &pathItem{ + nil, label, + extNode.Content[u].Line, 0, extNode.Content[u], + }) + } + } + + sort.Slice(mapped, func(i, j int) bool { + return mapped[i].line < mapped[j].line + }) + for _, mp := range mapped { + if mp.pi != nil { + rendered, _ := mp.pi.MarshalYAML() + + kn := utils.CreateStringNode(mp.path) + kn.Style = mp.style + + m.Content = append(m.Content, kn) + m.Content = append(m.Content, rendered.(*yaml.Node)) + } + if mp.rendered != nil { + m.Content = append(m.Content, utils.CreateStringNode(mp.path)) + m.Content = append(m.Content, mp.rendered) + } + } + + return m, nil +} + +// MarshalYAMLInline will create a ready to render YAML representation of the Callback object, +// with all references resolved inline. +func (c *Callback) MarshalYAMLInline() (interface{}, error) { + return c.marshalYAMLInlineInternal(nil) +} + +// MarshalYAMLInlineWithContext will create a ready to render YAML representation of the Callback object, +// resolving any references inline where possible. Uses the provided context for cycle detection. +// The ctx parameter should be *base.InlineRenderContext but is typed as any to satisfy the +// high.RenderableInlineWithContext interface without import cycles. +func (c *Callback) MarshalYAMLInlineWithContext(ctx any) (interface{}, error) { + return c.marshalYAMLInlineInternal(ctx) +} + +func (c *Callback) marshalYAMLInlineInternal(ctx any) (interface{}, error) { + // reference-only objects render as $ref nodes + if c.Reference != "" { + return utils.CreateRefNode(c.Reference), nil + } + + // resolve external reference if present + if c.low != nil { + result, err := high.ResolveExternalRef(c.low, buildLowCallback, NewCallback) + if err != nil { + return nil, err + } + if result.Resolved { + // recursively render the resolved callback + return result.High.marshalYAMLInlineInternal(ctx) + } + } + + // map keys correctly. + m := utils.CreateEmptyMapNode() + type pathItem struct { + pi *PathItem + path string + line int + style yaml.Style + rendered *yaml.Node + } + var mapped []*pathItem + + for k, pi := range c.Expression.FromOldest() { + ln := 9999 // default to a high value to weight new content to the bottom. + var style yaml.Style + if c.low != nil { + lpi := c.low.FindExpression(k) + if lpi != nil { + ln = lpi.ValueNode.Line + } + + for lk := range c.low.Expression.KeysFromOldest() { + if lk.Value == k { + style = lk.KeyNode.Style + break + } + } + } + mapped = append(mapped, &pathItem{pi, k, ln, style, nil}) + } + + nb := high.NewNodeBuilder(c, c.low) + nb.Resolve = true + nb.RenderContext = ctx + extNode := nb.Render() + if extNode != nil && extNode.Content != nil { + var label string + for u := range extNode.Content { + if u%2 == 0 { + label = extNode.Content[u].Value + continue + } + mapped = append(mapped, &pathItem{ + nil, label, + extNode.Content[u].Line, 0, extNode.Content[u], + }) + } + } + + sort.Slice(mapped, func(i, j int) bool { + return mapped[i].line < mapped[j].line + }) + for _, mp := range mapped { + if mp.pi != nil { + var rendered interface{} + if ctx != nil { + rendered, _ = mp.pi.MarshalYAMLInlineWithContext(ctx) + } else { + rendered, _ = mp.pi.MarshalYAMLInline() + } + + kn := utils.CreateStringNode(mp.path) + kn.Style = mp.style + + m.Content = append(m.Content, kn) + m.Content = append(m.Content, rendered.(*yaml.Node)) + } + if mp.rendered != nil { + m.Content = append(m.Content, utils.CreateStringNode(mp.path)) + m.Content = append(m.Content, mp.rendered) + } + } + + return m, nil +} + +// CreateCallbackRef creates a Callback that renders as a $ref to another callback definition. +// This is useful when building OpenAPI specs programmatically and you want to reference +// a callback defined in components/callbacks rather than inlining the full definition. +// +// Example: +// +// cb := v3.CreateCallbackRef("#/components/callbacks/WebhookCallback") +// +// Renders as: +// +// $ref: '#/components/callbacks/WebhookCallback' +func CreateCallbackRef(ref string) *Callback { + return &Callback{Reference: ref} +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/components.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/components.go new file mode 100644 index 000000000..f16c9d9c2 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/components.go @@ -0,0 +1,191 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "sync" + + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/datamodel/high" + highbase "github.com/pb33f/libopenapi/datamodel/high/base" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + low "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// Components represents a high-level OpenAPI 3+ Components Object, that is backed by a low-level one. +// +// Holds a set of reusable objects for different aspects of the OAS. All objects defined within the components object +// will have no effect on the API unless they are explicitly referenced from properties outside the components object. +// - https://spec.openapis.org/oas/v3.1.0#components-object +type Components struct { + Schemas *orderedmap.Map[string, *highbase.SchemaProxy] `json:"schemas,omitempty" yaml:"schemas,omitempty"` + Responses *orderedmap.Map[string, *Response] `json:"responses,omitempty" yaml:"responses,omitempty"` + Parameters *orderedmap.Map[string, *Parameter] `json:"parameters,omitempty" yaml:"parameters,omitempty"` + Examples *orderedmap.Map[string, *highbase.Example] `json:"examples,omitempty" yaml:"examples,omitempty"` + RequestBodies *orderedmap.Map[string, *RequestBody] `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"` + Headers *orderedmap.Map[string, *Header] `json:"headers,omitempty" yaml:"headers,omitempty"` + SecuritySchemes *orderedmap.Map[string, *SecurityScheme] `json:"securitySchemes,omitempty" yaml:"securitySchemes,omitempty"` + Links *orderedmap.Map[string, *Link] `json:"links,omitempty" yaml:"links,omitempty"` + Callbacks *orderedmap.Map[string, *Callback] `json:"callbacks,omitempty" yaml:"callbacks,omitempty"` + PathItems *orderedmap.Map[string, *PathItem] `json:"pathItems,omitempty" yaml:"pathItems,omitempty"` + MediaTypes *orderedmap.Map[string, *MediaType] `json:"mediaTypes,omitempty" yaml:"mediaTypes,omitempty"` // OpenAPI 3.2+ mediaTypes section + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *low.Components +} + +// NewComponents will create new high-level instance of Components from a low-level one. Components can be considerable +// in scope, with a lot of different properties across different categories. All components are built asynchronously +// in order to keep things fast. +func NewComponents(comp *low.Components) *Components { + c := new(Components) + c.low = comp + if orderedmap.Len(comp.Extensions) > 0 { + c.Extensions = high.ExtractExtensions(comp.Extensions) + } + cbMap := orderedmap.New[string, *Callback]() + linkMap := orderedmap.New[string, *Link]() + responseMap := orderedmap.New[string, *Response]() + parameterMap := orderedmap.New[string, *Parameter]() + exampleMap := orderedmap.New[string, *highbase.Example]() + requestBodyMap := orderedmap.New[string, *RequestBody]() + headerMap := orderedmap.New[string, *Header]() + pathItemMap := orderedmap.New[string, *PathItem]() + securitySchemeMap := orderedmap.New[string, *SecurityScheme]() + mediaTypesMap := orderedmap.New[string, *MediaType]() + schemas := orderedmap.New[string, *highbase.SchemaProxy]() + + // build all components asynchronously. + var wg sync.WaitGroup + wg.Add(11) + go func() { + buildComponent[*low.Callback, *Callback](comp.Callbacks.Value, cbMap, NewCallback) + wg.Done() + }() + go func() { + buildComponent[*low.Link, *Link](comp.Links.Value, linkMap, NewLink) + wg.Done() + }() + go func() { + buildComponent[*low.Response, *Response](comp.Responses.Value, responseMap, NewResponse) + wg.Done() + }() + go func() { + buildComponent[*low.Parameter, *Parameter](comp.Parameters.Value, parameterMap, NewParameter) + wg.Done() + }() + go func() { + buildComponent[*base.Example, *highbase.Example](comp.Examples.Value, exampleMap, highbase.NewExample) + wg.Done() + }() + go func() { + buildComponent[*low.RequestBody, *RequestBody](comp.RequestBodies.Value, requestBodyMap, NewRequestBody) + wg.Done() + }() + go func() { + buildComponent[*low.Header, *Header](comp.Headers.Value, headerMap, NewHeader) + wg.Done() + }() + go func() { + buildComponent[*low.PathItem, *PathItem](comp.PathItems.Value, pathItemMap, NewPathItem) + wg.Done() + }() + go func() { + buildComponent[*low.SecurityScheme, *SecurityScheme](comp.SecuritySchemes.Value, securitySchemeMap, NewSecurityScheme) + wg.Done() + }() + go func() { + buildSchema(comp.Schemas.Value, schemas) + wg.Done() + }() + go func() { + buildComponent[*low.MediaType, *MediaType](comp.MediaTypes.Value, mediaTypesMap, NewMediaType) + wg.Done() + }() + + wg.Wait() + c.Schemas = schemas + c.Callbacks = cbMap + c.Links = linkMap + c.Parameters = parameterMap + c.Headers = headerMap + c.Responses = responseMap + c.RequestBodies = requestBodyMap + c.Examples = exampleMap + c.SecuritySchemes = securitySchemeMap + c.PathItems = pathItemMap + c.MediaTypes = mediaTypesMap + return c +} + +// contains a component build result. +type componentResult[T any] struct { + res T + key string +} + +// buildComponent builds component structs from low level structs. +func buildComponent[IN any, OUT any](inMap *orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[IN]], outMap *orderedmap.Map[string, OUT], translateItem func(IN) OUT) { + translateFunc := func(pair orderedmap.Pair[lowmodel.KeyReference[string], lowmodel.ValueReference[IN]]) (componentResult[OUT], error) { + return componentResult[OUT]{key: pair.Key().Value, res: translateItem(pair.Value().Value)}, nil + } + resultFunc := func(value componentResult[OUT]) error { + outMap.Set(value.key, value.res) + return nil + } + _ = datamodel.TranslateMapParallel(inMap, translateFunc, resultFunc) +} + +// buildSchema builds a schema from low level structs. +func buildSchema(inMap *orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[*base.SchemaProxy]], outMap *orderedmap.Map[string, *highbase.SchemaProxy]) { + translateFunc := func(pair orderedmap.Pair[lowmodel.KeyReference[string], lowmodel.ValueReference[*base.SchemaProxy]]) (componentResult[*highbase.SchemaProxy], error) { + value := pair.Value() + sch := highbase.NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{ + Value: value.Value, + ValueNode: value.ValueNode, + }) + return componentResult[*highbase.SchemaProxy]{res: sch, key: pair.Key().Value}, nil + } + resultFunc := func(value componentResult[*highbase.SchemaProxy]) error { + outMap.Set(value.key, value.res) + return nil + } + _ = datamodel.TranslateMapParallel(inMap, translateFunc, resultFunc) +} + +// GoLow returns the low-level Components instance used to create the high-level one. +func (c *Components) GoLow() *low.Components { + return c.low +} + +// GoLowUntyped returns the low-level Components instance used to create the high-level one as an interface{}. +func (c *Components) GoLowUntyped() any { + return c.low +} + +// Render will return a YAML representation of the Components object as a byte slice. +func (c *Components) Render() ([]byte, error) { + return yaml.Marshal(c) +} + +// MarshalYAML will create a ready to render YAML representation of the Response object. +func (c *Components) MarshalYAML() (interface{}, error) { + nb := high.NewNodeBuilder(c, c.low) + return nb.Render(), nil +} + +// RenderInline will return a YAML representation of the Components object as a byte slice with references resolved. +func (c *Components) RenderInline() ([]byte, error) { + d, _ := c.MarshalYAMLInline() + return yaml.Marshal(d) +} + +// MarshalYAMLInline will create a ready to render YAML representation of the Components object with references resolved. +func (c *Components) MarshalYAMLInline() (interface{}, error) { + nb := high.NewNodeBuilder(c, c.low) + nb.Resolve = true + return nb.Render(), nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/document.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/document.go new file mode 100644 index 000000000..5bbd231ee --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/document.go @@ -0,0 +1,212 @@ +// Copyright 2022-2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +// Package v3 represents all OpenAPI 3+ high-level models. High-level models are easy to navigate +// and simple to extract what ever is required from an OpenAPI 3+ specification. +// +// High-level models are backed by low-level ones. There is a 'GoLow()' method available on every high level +// object. 'Going Low' allows engineers to transition from a high-level or 'porcelain' API, to a low-level 'plumbing' +// API, which provides fine grain detail to the underlying AST powering the data, lines, columns, raw nodes etc. +package v3 + +import ( + "bytes" + + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/datamodel/high/base" + "github.com/pb33f/libopenapi/datamodel/low" + lowv3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/json" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// Document represents a high-level OpenAPI 3 document (both 3.0 & 3.1). A Document is the root of the specification. +type Document struct { + // Version is the version of OpenAPI being used, extracted from the 'openapi: x.x.x' definition. + // This is not a standard property of the OpenAPI model, it's a convenience mechanism only. + Version string `json:"openapi,omitempty" yaml:"openapi,omitempty"` + + // Info represents a specification Info definitions + // Provides metadata about the API. The metadata MAY be used by tooling as required. + // - https://spec.openapis.org/oas/v3.1.0#info-object + Info *base.Info `json:"info,omitempty" yaml:"info,omitempty"` + + // Servers is a slice of Server instances which provide connectivity information to a target server. If the servers + // property is not provided, or is an empty array, the default value would be a Server Object with an url value of /. + // - https://spec.openapis.org/oas/v3.1.0#server-object + Servers []*Server `json:"servers,omitempty" yaml:"servers,omitempty"` + + // Paths contains all the PathItem definitions for the specification. + // The available paths and operations for the API, The most important part of ths spec. + // - https://spec.openapis.org/oas/v3.1.0#paths-object + Paths *Paths `json:"paths,omitempty" yaml:"paths,omitempty"` + + // Components is an element to hold various schemas for the document. + // - https://spec.openapis.org/oas/v3.1.0#components-object + Components *Components `json:"components,omitempty" yaml:"components,omitempty"` + + // Security contains global security requirements/roles for the specification + // A declaration of which security mechanisms can be used across the API. The list of values includes alternative + // security requirement objects that can be used. Only one of the security requirement objects need to be satisfied + // to authorize a request. Individual operations can override this definition. To make security optional, + // an empty security requirement ({}) can be included in the array. + // - https://spec.openapis.org/oas/v3.1.0#security-requirement-object + Security []*base.SecurityRequirement `json:"security,omitempty" yaml:"security,omitempty"` + // Security []*base.SecurityRequirement `json:"-" yaml:"-"` + + // Tags is a slice of base.Tag instances defined by the specification + // A list of tags used by the document with additional metadata. The order of the tags can be used to reflect on + // their order by the parsing tools. Not all tags that are used by the Operation Object must be declared. + // The tags that are not declared MAY be organized randomly or based on the tools’ logic. + // Each tag name in the list MUST be unique. + // - https://spec.openapis.org/oas/v3.1.0#tag-object + Tags []*base.Tag `json:"tags,omitempty" yaml:"tags,omitempty"` + + // ExternalDocs is an instance of base.ExternalDoc for.. well, obvious really, innit. + // - https://spec.openapis.org/oas/v3.1.0#external-documentation-object + ExternalDocs *base.ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` + + // Extensions contains all custom extensions defined for the top-level document. + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + + // JsonSchemaDialect is a 3.1+ property that sets the dialect to use for validating *base.Schema definitions + // The default value for the $schema keyword within Schema Objects contained within this OAS document. + // This MUST be in the form of a URI. + // - https://spec.openapis.org/oas/v3.1.0#schema-object + JsonSchemaDialect string `json:"jsonSchemaDialect,omitempty" yaml:"jsonSchemaDialect,omitempty"` + + // Self is a 3.2+ property that sets the base URI for the document for resolving relative references + // - https://spec.openapis.org/oas/v3.2.0#openapi-object + Self string `json:"$self,omitempty" yaml:"$self,omitempty"` + + // Webhooks is a 3.1+ property that is similar to callbacks, except, this defines incoming webhooks. + // The incoming webhooks that MAY be received as part of this API and that the API consumer MAY choose to implement. + // Closely related to the callbacks feature, this section describes requests initiated other than by an API call, + // for example by an out-of-band registration. The key name is a unique string to refer to each webhook, + // while the (optionally referenced) Path Item Object describes a request that may be initiated by the API provider + // and the expected responses. An example is available. + Webhooks *orderedmap.Map[string, *PathItem] `json:"webhooks,omitempty" yaml:"webhooks,omitempty"` + + // Index is a reference to the *index.SpecIndex that was created for the document and used + // as a guide when building out the Document. Ideal if further processing is required on the model and + // the original details are required to continue the work. + // + // This property is not a part of the OpenAPI schema, this is custom to libopenapi. + Index *index.SpecIndex `json:"-" yaml:"-"` + + // Rolodex is the low-level rolodex used when creating this document. + // This in an internal structure and not part of the OpenAPI schema. + Rolodex *index.Rolodex `json:"-" yaml:"-"` + low *lowv3.Document +} + +// NewDocument will create a new high-level Document from a low-level one. +func NewDocument(document *lowv3.Document) *Document { + d := new(Document) + d.low = document + d.Index = document.Index + if !document.Info.IsEmpty() { + d.Info = base.NewInfo(document.Info.Value) + } + if !document.Version.IsEmpty() { + d.Version = document.Version.Value + } + var servers []*Server + for _, ser := range document.Servers.Value { + servers = append(servers, NewServer(ser.Value)) + } + d.Servers = servers + var tags []*base.Tag + for _, tag := range document.Tags.Value { + tags = append(tags, base.NewTag(tag.Value)) + } + d.Tags = tags + if !document.ExternalDocs.IsEmpty() { + d.ExternalDocs = base.NewExternalDoc(document.ExternalDocs.Value) + } + if orderedmap.Len(document.Extensions) > 0 { + d.Extensions = high.ExtractExtensions(document.Extensions) + } + if !document.Components.IsEmpty() { + d.Components = NewComponents(document.Components.Value) + } + if !document.Paths.IsEmpty() { + d.Paths = NewPaths(document.Paths.Value) + } + if !document.JsonSchemaDialect.IsEmpty() { + d.JsonSchemaDialect = document.JsonSchemaDialect.Value + } + if !document.Self.IsEmpty() { + d.Self = document.Self.Value + } + if !document.Webhooks.IsEmpty() { + d.Webhooks = low.FromReferenceMapWithFunc(document.Webhooks.Value, NewPathItem) + } + if !document.Security.IsEmpty() { + var security []*base.SecurityRequirement + for s := range document.Security.Value { + security = append(security, base.NewSecurityRequirement(document.Security.Value[s].Value)) + } + d.Security = security + } + return d +} + +// GoLow returns the low-level Document that was used to create the high level one. +func (d *Document) GoLow() *lowv3.Document { + return d.low +} + +// GoLowUntyped returns the low-level Document that was used to create the high level one, however, it's untyped. +func (d *Document) GoLowUntyped() any { + return d.low +} + +// Render will return a YAML representation of the Document object as a byte slice. +func (d *Document) Render() ([]byte, error) { + return yaml.Marshal(d) +} + +// RenderWithIndention will return a YAML representation of the Document object as a byte slice. +// the rendering will use the original indention of the document. +func (d *Document) RenderWithIndention(indent int) []byte { + var buf bytes.Buffer + yamlEncoder := yaml.NewEncoder(&buf) + yamlEncoder.SetIndent(indent) + _ = yamlEncoder.Encode(d) + return buf.Bytes() +} + +// RenderJSON will return a JSON representation of the Document object as a byte slice. +func (d *Document) RenderJSON(indention string) ([]byte, error) { + nb := high.NewNodeBuilder(d, d.low) + + dat, err := json.YAMLNodeToJSON(nb.Render(), indention) + if err != nil { + return dat, err + } + return dat, nil +} + +func (d *Document) RenderInline() ([]byte, error) { + di, _ := d.MarshalYAMLInline() + return yaml.Marshal(di) +} + +// MarshalYAML will create a ready to render YAML representation of the Document object. +func (d *Document) MarshalYAML() (interface{}, error) { + nb := high.NewNodeBuilder(d, d.low) + return nb.Render(), nil +} + +func (d *Document) MarshalYAMLInline() (interface{}, error) { + nb := high.NewNodeBuilder(d, d.low) + nb.Resolve = true + return nb.Render(), nil +} + +func (d *Document) GetIndex() *index.SpecIndex { + return d.Index +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/encoding.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/encoding.go new file mode 100644 index 000000000..afe7d79f9 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/encoding.go @@ -0,0 +1,78 @@ +// Copyright 2022-2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/datamodel/low" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + lowv3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// Encoding represents an OpenAPI 3+ Encoding object +// - https://spec.openapis.org/oas/v3.1.0#encoding-object +type Encoding struct { + ContentType string `json:"contentType,omitempty" yaml:"contentType,omitempty"` + Headers *orderedmap.Map[string, *Header] `json:"headers,omitempty" yaml:"headers,omitempty"` + Style string `json:"style,omitempty" yaml:"style,omitempty"` + Explode *bool `json:"explode,omitempty" yaml:"explode,omitempty"` + AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"` + low *lowv3.Encoding +} + +// NewEncoding creates a new instance of Encoding from a low-level one. +func NewEncoding(encoding *lowv3.Encoding) *Encoding { + e := new(Encoding) + e.low = encoding + e.ContentType = encoding.ContentType.Value + e.Style = encoding.Style.Value + if !encoding.Explode.IsEmpty() { + e.Explode = &encoding.Explode.Value + } + e.AllowReserved = encoding.AllowReserved.Value + e.Headers = ExtractHeaders(encoding.Headers.Value) + return e +} + +// GoLow returns the low-level Encoding instance used to create the high-level one. +func (e *Encoding) GoLow() *lowv3.Encoding { + return e.low +} + +// GoLowUntyped will return the low-level Encoding instance that was used to create the high-level one, with no type +func (e *Encoding) GoLowUntyped() any { + return e.low +} + +// Render will return a YAML representation of the Encoding object as a byte slice. +func (e *Encoding) Render() ([]byte, error) { + return yaml.Marshal(e) +} + +// MarshalYAML will create a ready to render YAML representation of the Encoding object. +func (e *Encoding) MarshalYAML() (interface{}, error) { + nb := high.NewNodeBuilder(e, e.low) + return nb.Render(), nil +} + +// MarshalYAMLInline will create a ready to render YAML representation of the Encoding object, +// with all references resolved inline. +func (e *Encoding) MarshalYAMLInline() (interface{}, error) { + return high.RenderInline(e, e.low) +} + +// MarshalYAMLInlineWithContext will create a ready to render YAML representation of the Encoding object, +// resolving any references inline where possible. Uses the provided context for cycle detection. +// The ctx parameter should be *base.InlineRenderContext but is typed as any to satisfy the +// high.RenderableInlineWithContext interface without import cycles. +func (e *Encoding) MarshalYAMLInlineWithContext(ctx any) (interface{}, error) { + return high.RenderInlineWithContext(e, e.low, ctx) +} + +// ExtractEncoding converts hard to navigate low-level plumbing Encoding definitions, into a high-level simple map +func ExtractEncoding(elements *orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[*lowv3.Encoding]]) *orderedmap.Map[string, *Encoding] { + return low.FromReferenceMapWithFunc(elements, NewEncoding) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/header.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/header.go new file mode 100644 index 000000000..2d9cd5291 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/header.go @@ -0,0 +1,172 @@ +// Copyright 2022-2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + + "github.com/pb33f/libopenapi/datamodel/high" + highbase "github.com/pb33f/libopenapi/datamodel/high/base" + "github.com/pb33f/libopenapi/datamodel/low" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + lowv3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// buildLowHeader builds a low-level Header from a resolved YAML node. +func buildLowHeader(node *yaml.Node, idx *index.SpecIndex) (*lowv3.Header, error) { + var header lowv3.Header + lowmodel.BuildModel(node, &header) + if err := header.Build(context.Background(), nil, node, idx); err != nil { + return nil, err + } + return &header, nil +} + +// Header represents a high-level OpenAPI 3+ Header object backed by a low-level one. +// - https://spec.openapis.org/oas/v3.1.0#header-object +type Header struct { + Reference string `json:"$ref,omitempty" yaml:"$ref,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Required bool `json:"required,omitempty" yaml:"required,omitempty"` + Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` + AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"` + Style string `json:"style,omitempty" yaml:"style,omitempty"` + Explode bool `json:"explode,omitempty" yaml:"explode,omitempty"` + AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"` + Schema *highbase.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"` + Example *yaml.Node `json:"example,omitempty" yaml:"example,omitempty"` + Examples *orderedmap.Map[string, *highbase.Example] `json:"examples,omitempty" yaml:"examples,omitempty"` + Content *orderedmap.Map[string, *MediaType] `json:"content,omitempty" yaml:"content,omitempty"` + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *lowv3.Header +} + +// NewHeader creates a new high-level Header instance from a low-level one. +func NewHeader(header *lowv3.Header) *Header { + h := new(Header) + h.low = header + h.Description = header.Description.Value + h.Required = header.Required.Value + h.Deprecated = header.Deprecated.Value + h.AllowEmptyValue = header.AllowEmptyValue.Value + h.Style = header.Style.Value + h.Explode = header.Explode.Value + h.AllowReserved = header.AllowReserved.Value + if !header.Schema.IsEmpty() { + h.Schema = highbase.NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{ + Value: header.Schema.Value, + KeyNode: header.Schema.KeyNode, + ValueNode: header.Schema.ValueNode, + }) + } + h.Content = ExtractContent(header.Content.Value) + h.Example = header.Example.Value + h.Examples = highbase.ExtractExamples(header.Examples.Value) + h.Extensions = high.ExtractExtensions(header.Extensions) + return h +} + +// GoLow returns the low-level Header instance used to create the high-level one. +func (h *Header) GoLow() *lowv3.Header { + return h.low +} + +// GoLowUntyped will return the low-level Header instance that was used to create the high-level one, with no type +func (h *Header) GoLowUntyped() any { + return h.low +} + +// IsReference returns true if this Header is a reference to another Header definition. +func (h *Header) IsReference() bool { + return h.Reference != "" +} + +// GetReference returns the reference string if this is a reference Header. +func (h *Header) GetReference() string { + return h.Reference +} + +// ExtractHeaders will extract a hard to navigate low-level Header map, into simple high-level one. +func ExtractHeaders(elements *orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[*lowv3.Header]]) *orderedmap.Map[string, *Header] { + return low.FromReferenceMapWithFunc(elements, NewHeader) +} + +// Render will return a YAML representation of the Header object as a byte slice. +func (h *Header) Render() ([]byte, error) { + return yaml.Marshal(h) +} + +// RenderInline will return a YAML representation of the Header object as a byte slice with references resolved. +func (h *Header) RenderInline() ([]byte, error) { + d, _ := h.MarshalYAMLInline() + return yaml.Marshal(d) +} + +// MarshalYAML will create a ready to render YAML representation of the Header object. +func (h *Header) MarshalYAML() (interface{}, error) { + // Handle reference-only header + if h.Reference != "" { + return utils.CreateRefNode(h.Reference), nil + } + nb := high.NewNodeBuilder(h, h.low) + return nb.Render(), nil +} + +// MarshalYAMLInline will create a ready to render YAML representation of the Header object with references resolved. +func (h *Header) MarshalYAMLInline() (interface{}, error) { + // reference-only objects render as $ref nodes + if h.Reference != "" { + return utils.CreateRefNode(h.Reference), nil + } + + // resolve external reference if present + if h.low != nil { + rendered, err := high.RenderExternalRef(h.low, buildLowHeader, NewHeader) + if err != nil || rendered != nil { + return rendered, err + } + } + + return high.RenderInline(h, h.low) +} + +// MarshalYAMLInlineWithContext will create a ready to render YAML representation of the Header object, +// resolving any references inline where possible. Uses the provided context for cycle detection. +// The ctx parameter should be *base.InlineRenderContext but is typed as any to satisfy the +// high.RenderableInlineWithContext interface without import cycles. +func (h *Header) MarshalYAMLInlineWithContext(ctx any) (interface{}, error) { + if h.Reference != "" { + return utils.CreateRefNode(h.Reference), nil + } + + // resolve external reference if present + if h.low != nil { + rendered, err := high.RenderExternalRefWithContext(h.low, buildLowHeader, NewHeader, ctx) + if err != nil || rendered != nil { + return rendered, err + } + } + + return high.RenderInlineWithContext(h, h.low, ctx) +} + +// CreateHeaderRef creates a Header that renders as a $ref to another header definition. +// This is useful when building OpenAPI specs programmatically, and you want to reference +// a header defined in components/headers rather than inlining the full definition. +// +// Example: +// +// header := v3.CreateHeaderRef("#/components/headers/X-Rate-Limit") +// +// Renders as: +// +// $ref: '#/components/headers/X-Rate-Limit' +func CreateHeaderRef(ref string) *Header { + return &Header{Reference: ref} +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/link.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/link.go new file mode 100644 index 000000000..68956cbfa --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/link.go @@ -0,0 +1,156 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/datamodel/low" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + lowv3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// buildLowLink builds a low-level Link from a resolved YAML node. +func buildLowLink(node *yaml.Node, idx *index.SpecIndex) (*lowv3.Link, error) { + var link lowv3.Link + lowmodel.BuildModel(node, &link) + link.Build(context.Background(), nil, node, idx) + return &link, nil +} + +// Link represents a high-level OpenAPI 3+ Link object that is backed by a low-level one. +// +// The Link object represents a possible design-time link for a response. The presence of a link does not guarantee the +// caller's ability to successfully invoke it, rather it provides a known relationship and traversal mechanism between +// responses and other operations. +// +// Unlike dynamic links (i.e. links provided in the response payload), the OAS linking mechanism does not require +// link information in the runtime response. +// +// For computing links, and providing instructions to execute them, a runtime expression is used for accessing values +// in an operation and using them as parameters while invoking the linked operation. +// - https://spec.openapis.org/oas/v3.1.0#link-object +type Link struct { + Reference string `json:"$ref,omitempty" yaml:"$ref,omitempty"` + OperationRef string `json:"operationRef,omitempty" yaml:"operationRef,omitempty"` + OperationId string `json:"operationId,omitempty" yaml:"operationId,omitempty"` + Parameters *orderedmap.Map[string, string] `json:"parameters,omitempty" yaml:"parameters,omitempty"` + RequestBody string `json:"requestBody,omitempty" yaml:"requestBody,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Server *Server `json:"server,omitempty" yaml:"server,omitempty"` + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *lowv3.Link +} + +// NewLink will create a new high-level Link instance from a low-level one. +func NewLink(link *lowv3.Link) *Link { + l := new(Link) + l.low = link + l.OperationRef = link.OperationRef.Value + l.OperationId = link.OperationId.Value + l.Parameters = low.FromReferenceMap(link.Parameters.Value) + l.RequestBody = link.RequestBody.Value + l.Description = link.Description.Value + if link.Server.Value != nil { + l.Server = NewServer(link.Server.Value) + } + l.Extensions = high.ExtractExtensions(link.Extensions) + return l +} + +// GoLow will return the low-level Link instance used to create the high-level one. +func (l *Link) GoLow() *lowv3.Link { + return l.low +} + +// GoLowUntyped will return the low-level Link instance that was used to create the high-level one, with no type +func (l *Link) GoLowUntyped() any { + return l.low +} + +// IsReference returns true if this Link is a reference to another Link definition. +func (l *Link) IsReference() bool { + return l.Reference != "" +} + +// GetReference returns the reference string if this is a reference Link. +func (l *Link) GetReference() string { + return l.Reference +} + +// Render will return a YAML representation of the Link object as a byte slice. +func (l *Link) Render() ([]byte, error) { + return yaml.Marshal(l) +} + +// MarshalYAML will create a ready to render YAML representation of the Link object. +func (l *Link) MarshalYAML() (interface{}, error) { + // Handle reference-only link + if l.Reference != "" { + return utils.CreateRefNode(l.Reference), nil + } + nb := high.NewNodeBuilder(l, l.low) + return nb.Render(), nil +} + +// MarshalYAMLInline will create a ready to render YAML representation of the Link object, +// with all references resolved inline. +func (l *Link) MarshalYAMLInline() (interface{}, error) { + // reference-only objects render as $ref nodes + if l.Reference != "" { + return utils.CreateRefNode(l.Reference), nil + } + + // resolve external reference if present + if l.low != nil { + // buildLowLink never returns an error, so we can ignore it + rendered, _ := high.RenderExternalRef(l.low, buildLowLink, NewLink) + if rendered != nil { + return rendered, nil + } + } + + return high.RenderInline(l, l.low) +} + +// MarshalYAMLInlineWithContext will create a ready to render YAML representation of the Link object, +// resolving any references inline where possible. Uses the provided context for cycle detection. +// The ctx parameter should be *base.InlineRenderContext but is typed as any to satisfy the +// high.RenderableInlineWithContext interface without import cycles. +func (l *Link) MarshalYAMLInlineWithContext(ctx any) (interface{}, error) { + if l.Reference != "" { + return utils.CreateRefNode(l.Reference), nil + } + + // resolve external reference if present + if l.low != nil { + // buildLowLink never returns an error, so we can ignore it + rendered, _ := high.RenderExternalRefWithContext(l.low, buildLowLink, NewLink, ctx) + if rendered != nil { + return rendered, nil + } + } + + return high.RenderInlineWithContext(l, l.low, ctx) +} + +// CreateLinkRef creates a Link that renders as a $ref to another link definition. +// This is useful when building OpenAPI specs programmatically, and you want to reference +// a link defined in components/links rather than inlining the full definition. +// +// Example: +// +// link := v3.CreateLinkRef("#/components/links/GetUserByUserId") +// +// Renders as: +// +// $ref: '#/components/links/GetUserByUserId' +func CreateLinkRef(ref string) *Link { + return &Link{Reference: ref} +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/media_type.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/media_type.go new file mode 100644 index 000000000..1d90efa17 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/media_type.go @@ -0,0 +1,110 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/datamodel/high/base" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + low "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// MediaType represents a high-level OpenAPI MediaType object that is backed by a low-level one. +// +// Each Media Type Object provides schema and examples for the media type identified by its key. +// - https://spec.openapis.org/oas/v3.1.0#media-type-object +type MediaType struct { + Schema *base.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"` + ItemSchema *base.SchemaProxy `json:"itemSchema,omitempty" yaml:"itemSchema,omitempty"` + Example *yaml.Node `json:"example,omitempty" yaml:"example,omitempty"` + Examples *orderedmap.Map[string, *base.Example] `json:"examples,omitempty" yaml:"examples,omitempty"` + Encoding *orderedmap.Map[string, *Encoding] `json:"encoding,omitempty" yaml:"encoding,omitempty"` + ItemEncoding *orderedmap.Map[string, *Encoding] `json:"itemEncoding,omitempty" yaml:"itemEncoding,omitempty"` + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *low.MediaType +} + +// NewMediaType will create a new high-level MediaType instance from a low-level one. +func NewMediaType(mediaType *low.MediaType) *MediaType { + m := new(MediaType) + m.low = mediaType + if !mediaType.Schema.IsEmpty() { + m.Schema = base.NewSchemaProxy(&mediaType.Schema) + } + if !mediaType.ItemSchema.IsEmpty() { + m.ItemSchema = base.NewSchemaProxy(&mediaType.ItemSchema) + } + m.Example = mediaType.Example.Value + m.Examples = base.ExtractExamples(mediaType.Examples.Value) + m.Extensions = high.ExtractExtensions(mediaType.Extensions) + m.Encoding = ExtractEncoding(mediaType.Encoding.Value) + if !mediaType.ItemEncoding.IsEmpty() { + m.ItemEncoding = ExtractEncoding(mediaType.ItemEncoding.Value) + } + return m +} + +// GoLow will return the low-level instance of MediaType used to create the high-level one. +func (m *MediaType) GoLow() *low.MediaType { + return m.low +} + +// GoLowUntyped will return the low-level MediaType instance that was used to create the high-level one, with no type +func (m *MediaType) GoLowUntyped() any { + return m.low +} + +// Render will return a YAML representation of the MediaType object as a byte slice. +func (m *MediaType) Render() ([]byte, error) { + return yaml.Marshal(m) +} + +func (m *MediaType) RenderInline() ([]byte, error) { + d, _ := m.MarshalYAMLInline() + return yaml.Marshal(d) +} + +// MarshalYAML will create a ready to render YAML representation of the MediaType object. +func (m *MediaType) MarshalYAML() (interface{}, error) { + nb := high.NewNodeBuilder(m, m.low) + return nb.Render(), nil +} + +func (m *MediaType) MarshalYAMLInline() (interface{}, error) { + nb := high.NewNodeBuilder(m, m.low) + nb.Resolve = true + return nb.Render(), nil +} + +// MarshalYAMLInlineWithContext will create a ready to render YAML representation of the MediaType object, +// resolving any references inline where possible. Uses the provided context for cycle detection. +// The ctx parameter should be *base.InlineRenderContext but is typed as any to satisfy the +// high.RenderableInlineWithContext interface without import cycles. +func (m *MediaType) MarshalYAMLInlineWithContext(ctx any) (interface{}, error) { + nb := high.NewNodeBuilder(m, m.low) + nb.Resolve = true + nb.RenderContext = ctx + return nb.Render(), nil +} + +// ExtractContent takes in a complex and hard to navigate low-level content map, and converts it in to a much simpler +// and easier to navigate high-level one. +func ExtractContent(elements *orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.MediaType]]) *orderedmap.Map[string, *MediaType] { + extracted := orderedmap.New[string, *MediaType]() + translateFunc := func(pair orderedmap.Pair[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.MediaType]]) (asyncResult[*MediaType], error) { + return asyncResult[*MediaType]{ + key: pair.Key().Value, + result: NewMediaType(pair.Value().Value), + }, nil + } + resultFunc := func(value asyncResult[*MediaType]) error { + extracted.Set(value.key, value.result) + return nil + } + _ = datamodel.TranslateMapParallel(elements, translateFunc, resultFunc) + return extracted +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/oauth_flow.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/oauth_flow.go new file mode 100644 index 000000000..9b4c105fb --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/oauth_flow.go @@ -0,0 +1,56 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/datamodel/low" + lowv3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// OAuthFlow represents a high-level OpenAPI 3+ OAuthFlow object that is backed by a low-level one. +// - https://spec.openapis.org/oas/v3.1.0#oauth-flow-object +type OAuthFlow struct { + AuthorizationUrl string `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"` + TokenUrl string `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"` + RefreshUrl string `json:"refreshUrl,omitempty" yaml:"refreshUrl,omitempty"` + Scopes *orderedmap.Map[string, string] `json:"scopes,renderZero" yaml:"scopes,renderZero"` + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *lowv3.OAuthFlow +} + +// NewOAuthFlow creates a new high-level OAuthFlow instance from a low-level one. +func NewOAuthFlow(flow *lowv3.OAuthFlow) *OAuthFlow { + o := new(OAuthFlow) + o.low = flow + o.TokenUrl = flow.TokenUrl.Value + o.AuthorizationUrl = flow.AuthorizationUrl.Value + o.RefreshUrl = flow.RefreshUrl.Value + o.Scopes = low.FromReferenceMap(flow.Scopes.Value) + o.Extensions = high.ExtractExtensions(flow.Extensions) + return o +} + +// GoLow returns the low-level OAuthFlow instance used to create the high-level one. +func (o *OAuthFlow) GoLow() *lowv3.OAuthFlow { + return o.low +} + +// GoLowUntyped will return the low-level Discriminator instance that was used to create the high-level one, with no type +func (o *OAuthFlow) GoLowUntyped() any { + return o.low +} + +// Render will return a YAML representation of the OAuthFlow object as a byte slice. +func (o *OAuthFlow) Render() ([]byte, error) { + return yaml.Marshal(o) +} + +// MarshalYAML will create a ready to render YAML representation of the OAuthFlow object. +func (o *OAuthFlow) MarshalYAML() (interface{}, error) { + nb := high.NewNodeBuilder(o, o.low) + return nb.Render(), nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/oauth_flows.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/oauth_flows.go new file mode 100644 index 000000000..6e7510297 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/oauth_flows.go @@ -0,0 +1,67 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// OAuthFlows represents a high-level OpenAPI 3+ OAuthFlows object that is backed by a low-level one. +// - https://spec.openapis.org/oas/v3.1.0#oauth-flows-object +type OAuthFlows struct { + Implicit *OAuthFlow `json:"implicit,omitempty" yaml:"implicit,omitempty"` + Password *OAuthFlow `json:"password,omitempty" yaml:"password,omitempty"` + ClientCredentials *OAuthFlow `json:"clientCredentials,omitempty" yaml:"clientCredentials,omitempty"` + AuthorizationCode *OAuthFlow `json:"authorizationCode,omitempty" yaml:"authorizationCode,omitempty"` + Device *OAuthFlow `json:"device,omitempty" yaml:"device,omitempty"` // OpenAPI 3.2+ device flow + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *low.OAuthFlows +} + +// NewOAuthFlows creates a new high-level OAuthFlows instance from a low-level one. +func NewOAuthFlows(flows *low.OAuthFlows) *OAuthFlows { + o := new(OAuthFlows) + o.low = flows + if !flows.Implicit.IsEmpty() { + o.Implicit = NewOAuthFlow(flows.Implicit.Value) + } + if !flows.Password.IsEmpty() { + o.Password = NewOAuthFlow(flows.Password.Value) + } + if !flows.ClientCredentials.IsEmpty() { + o.ClientCredentials = NewOAuthFlow(flows.ClientCredentials.Value) + } + if !flows.AuthorizationCode.IsEmpty() { + o.AuthorizationCode = NewOAuthFlow(flows.AuthorizationCode.Value) + } + if !flows.Device.IsEmpty() { + o.Device = NewOAuthFlow(flows.Device.Value) + } + o.Extensions = high.ExtractExtensions(flows.Extensions) + return o +} + +// GoLow returns the low-level OAuthFlows instance used to create the high-level one. +func (o *OAuthFlows) GoLow() *low.OAuthFlows { + return o.low +} + +// GoLowUntyped will return the low-level OAuthFlows instance that was used to create the high-level one, with no type +func (o *OAuthFlows) GoLowUntyped() any { + return o.low +} + +// Render will return a YAML representation of the OAuthFlows object as a byte slice. +func (o *OAuthFlows) Render() ([]byte, error) { + return yaml.Marshal(o) +} + +// MarshalYAML will create a ready to render YAML representation of the OAuthFlows object. +func (o *OAuthFlows) MarshalYAML() (interface{}, error) { + nb := high.NewNodeBuilder(o, o.low) + return nb.Render(), nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/operation.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/operation.go new file mode 100644 index 000000000..5f831a4eb --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/operation.go @@ -0,0 +1,123 @@ +// Copyright 2022-2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/datamodel/high/base" + "github.com/pb33f/libopenapi/datamodel/low" + lowv3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// Operation is a high-level representation of an OpenAPI 3+ Operation object, backed by a low-level one. +// +// An Operation is perhaps the most important object of the entire specification. Everything of value +// happens here. The entire being for existence of this library and the specification, is this Operation. +// - https://spec.openapis.org/oas/v3.1.0#operation-object +type Operation struct { + Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"` + Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + ExternalDocs *base.ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` + OperationId string `json:"operationId,omitempty" yaml:"operationId,omitempty"` + Parameters []*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"` + RequestBody *RequestBody `json:"requestBody,omitempty" yaml:"requestBody,omitempty"` + Responses *Responses `json:"responses,omitempty" yaml:"responses,omitempty"` + Callbacks *orderedmap.Map[string, *Callback] `json:"callbacks,omitempty" yaml:"callbacks,omitempty"` + Deprecated *bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` + Security []*base.SecurityRequirement `json:"security,omitempty" yaml:"security,omitempty"` + Servers []*Server `json:"servers,omitempty" yaml:"servers,omitempty"` + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *lowv3.Operation +} + +// NewOperation will create a new Operation instance from a low-level one. +func NewOperation(operation *lowv3.Operation) *Operation { + o := new(Operation) + o.low = operation + var tags []string + if !operation.Tags.IsEmpty() { + for i := range operation.Tags.Value { + tags = append(tags, operation.Tags.Value[i].Value) + } + } + o.Tags = tags + o.Summary = operation.Summary.Value + if !operation.Deprecated.IsEmpty() { + o.Deprecated = &operation.Deprecated.Value + } + o.Description = operation.Description.Value + if !operation.ExternalDocs.IsEmpty() { + o.ExternalDocs = base.NewExternalDoc(operation.ExternalDocs.Value) + } + o.OperationId = operation.OperationId.Value + if !operation.Parameters.IsEmpty() { + params := make([]*Parameter, len(operation.Parameters.Value)) + for i := range operation.Parameters.Value { + params[i] = NewParameter(operation.Parameters.Value[i].Value) + } + o.Parameters = params + } + if !operation.RequestBody.IsEmpty() { + o.RequestBody = NewRequestBody(operation.RequestBody.Value) + } + if !operation.Responses.IsEmpty() { + o.Responses = NewResponses(operation.Responses.Value) + } + if !operation.Security.IsEmpty() { + var sec []*base.SecurityRequirement + for s := range operation.Security.Value { + sec = append(sec, base.NewSecurityRequirement(operation.Security.Value[s].Value)) + } + if len(sec) > 0 { + o.Security = sec + } else { + o.Security = []*base.SecurityRequirement{} // security is defined, but empty. + } + } + var servers []*Server + for i := range operation.Servers.Value { + servers = append(servers, NewServer(operation.Servers.Value[i].Value)) + } + o.Servers = servers + o.Extensions = high.ExtractExtensions(operation.Extensions) + if !operation.Callbacks.IsEmpty() { + o.Callbacks = low.FromReferenceMapWithFunc(operation.Callbacks.Value, NewCallback) + } + return o +} + +// GoLow will return the low-level Operation instance that was used to create the high-level one. +func (o *Operation) GoLow() *lowv3.Operation { + return o.low +} + +// GoLowUntyped will return the low-level Discriminator instance that was used to create the high-level one, with no type +func (o *Operation) GoLowUntyped() any { + return o.low +} + +// Render will return a YAML representation of the Operation object as a byte slice. +func (o *Operation) Render() ([]byte, error) { + return yaml.Marshal(o) +} + +func (o *Operation) RenderInline() ([]byte, error) { + d, _ := o.MarshalYAMLInline() + return yaml.Marshal(d) +} + +// MarshalYAML will create a ready to render YAML representation of the Operation object. +func (o *Operation) MarshalYAML() (interface{}, error) { + nb := high.NewNodeBuilder(o, o.low) + return nb.Render(), nil +} + +func (o *Operation) MarshalYAMLInline() (interface{}, error) { + nb := high.NewNodeBuilder(o, o.low) + nb.Resolve = true + return nb.Render(), nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/parameter.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/parameter.go new file mode 100644 index 000000000..f7ac1f2a2 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/parameter.go @@ -0,0 +1,209 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/datamodel/high/base" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + low "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// buildLowParameter builds a low-level Parameter from a resolved YAML node. +func buildLowParameter(node *yaml.Node, idx *index.SpecIndex) (*low.Parameter, error) { + var param low.Parameter + lowmodel.BuildModel(node, ¶m) + if err := param.Build(context.Background(), nil, node, idx); err != nil { + return nil, err + } + return ¶m, nil +} + +// Parameter represents a high-level OpenAPI 3+ Parameter object, that is backed by a low-level one. +// +// A unique parameter is defined by a combination of a name and location. +// - https://spec.openapis.org/oas/v3.1.0#parameter-object +type Parameter struct { + Reference string `json:"$ref,omitempty" yaml:"$ref,omitempty"` + Name string `json:"name,omitempty" yaml:"name,omitempty"` + In string `json:"in,omitempty" yaml:"in,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Required *bool `json:"required,renderZero,omitempty" yaml:"required,renderZero,omitempty"` + Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` + AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"` + Style string `json:"style,omitempty" yaml:"style,omitempty"` + Explode *bool `json:"explode,renderZero,omitempty" yaml:"explode,renderZero,omitempty"` + AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"` + Schema *base.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"` + Example *yaml.Node `json:"example,omitempty" yaml:"example,omitempty"` + Examples *orderedmap.Map[string, *base.Example] `json:"examples,omitempty" yaml:"examples,omitempty"` + Content *orderedmap.Map[string, *MediaType] `json:"content,omitempty" yaml:"content,omitempty"` + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *low.Parameter +} + +// NewParameter will create a new high-level instance of a Parameter, using a low-level one. +func NewParameter(param *low.Parameter) *Parameter { + p := new(Parameter) + p.low = param + p.Name = param.Name.Value + p.In = param.In.Value + p.Description = param.Description.Value + p.Deprecated = param.Deprecated.Value + p.AllowEmptyValue = param.AllowEmptyValue.Value + p.Style = param.Style.Value + if !param.Explode.IsEmpty() { + p.Explode = ¶m.Explode.Value + } + p.AllowReserved = param.AllowReserved.Value + if !param.Schema.IsEmpty() { + p.Schema = base.NewSchemaProxy(¶m.Schema) + } + if !param.Required.IsEmpty() { + p.Required = ¶m.Required.Value + } + p.Example = param.Example.Value + p.Examples = base.ExtractExamples(param.Examples.Value) + p.Content = ExtractContent(param.Content.Value) + p.Extensions = high.ExtractExtensions(param.Extensions) + return p +} + +// CreateParameterRef creates a Parameter that renders as a $ref to another parameter definition. +// This is useful when building OpenAPI specs programmatically and you want to reference +// a parameter defined in components/parameters rather than inlining the full definition. +// +// Example: +// +// param := v3.CreateParameterRef("#/components/parameters/limitParam") +// +// Renders as: +// +// $ref: '#/components/parameters/limitParam' +func CreateParameterRef(ref string) *Parameter { + return &Parameter{Reference: ref} +} + +// GoLow returns the low-level Parameter used to create the high-level one. +func (p *Parameter) GoLow() *low.Parameter { + return p.low +} + +// GoLowUntyped will return the low-level Discriminator instance that was used to create the high-level one, with no type +func (p *Parameter) GoLowUntyped() any { + return p.low +} + +// IsReference returns true if this Parameter is a reference to another Parameter definition. +func (p *Parameter) IsReference() bool { + return p.Reference != "" +} + +// GetReference returns the reference string if this is a reference Parameter. +func (p *Parameter) GetReference() string { + return p.Reference +} + +// Render will return a YAML representation of the Encoding object as a byte slice. +func (p *Parameter) Render() ([]byte, error) { + return yaml.Marshal(p) +} + +func (p *Parameter) RenderInline() ([]byte, error) { + d, _ := p.MarshalYAMLInline() + return yaml.Marshal(d) +} + +// MarshalYAML will create a ready to render YAML representation of the Parameter object. +func (p *Parameter) MarshalYAML() (interface{}, error) { + // Handle reference-only parameter + if p.Reference != "" { + return utils.CreateRefNode(p.Reference), nil + } + nb := high.NewNodeBuilder(p, p.low) + return nb.Render(), nil +} + +// MarshalYAMLInline will create a ready to render YAML representation of the Parameter object, +// resolving any references inline where possible. +func (p *Parameter) MarshalYAMLInline() (interface{}, error) { + // reference-only objects render as $ref nodes + if p.Reference != "" { + return utils.CreateRefNode(p.Reference), nil + } + + // resolve external reference if present + if p.low != nil { + rendered, err := high.RenderExternalRef(p.low, buildLowParameter, NewParameter) + if err != nil || rendered != nil { + return rendered, err + } + } + + return high.RenderInline(p, p.low) +} + +// MarshalYAMLInlineWithContext will create a ready to render YAML representation of the Parameter object, +// resolving any references inline where possible. Uses the provided context for cycle detection. +// The ctx parameter should be *base.InlineRenderContext but is typed as any to satisfy the +// high.RenderableInlineWithContext interface without import cycles. +func (p *Parameter) MarshalYAMLInlineWithContext(ctx any) (interface{}, error) { + if p.Reference != "" { + return utils.CreateRefNode(p.Reference), nil + } + + // resolve external reference if present + if p.low != nil { + rendered, err := high.RenderExternalRefWithContext(p.low, buildLowParameter, NewParameter, ctx) + if err != nil || rendered != nil { + return rendered, err + } + } + + return high.RenderInlineWithContext(p, p.low, ctx) +} + +// IsExploded will return true if the parameter is exploded, false otherwise. +func (p *Parameter) IsExploded() bool { + if p.Explode == nil { + return false + } + return *p.Explode +} + +// IsDefaultFormEncoding will return true if the parameter has no exploded value, or has exploded set to true, and no style +// or a style set to form. This combination is the default encoding/serialization style for parameters for OpenAPI 3+ +func (p *Parameter) IsDefaultFormEncoding() bool { + if p.Explode == nil && (p.Style == "" || p.Style == "form") { + return true + } + if p.Explode != nil && *p.Explode && (p.Style == "" || p.Style == "form") { + return true + } + return false +} + +// IsDefaultHeaderEncoding will return true if the parameter has no exploded value, or has exploded set to false, and no style +// or a style set to simple. This combination is the default encoding/serialization style for header parameters for OpenAPI 3+ +func (p *Parameter) IsDefaultHeaderEncoding() bool { + if p.Explode == nil && (p.Style == "" || p.Style == "simple") { + return true + } + if p.Explode != nil && !*p.Explode && (p.Style == "" || p.Style == "simple") { + return true + } + return false +} + +// IsDefaultPathEncoding will return true if the parameter has no exploded value, or has exploded set to false, and no style +// or a style set to simple. This combination is the default encoding/serialization style for path parameters for OpenAPI 3+ +func (p *Parameter) IsDefaultPathEncoding() bool { + return p.IsDefaultHeaderEncoding() // header default encoding and path default encoding are the same +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/path_item.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/path_item.go new file mode 100644 index 000000000..9c1c32537 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/path_item.go @@ -0,0 +1,326 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + "reflect" + "slices" + + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/datamodel/low" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + lowV3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// buildLowPathItem builds a low-level PathItem from a resolved YAML node. +func buildLowPathItem(node *yaml.Node, idx *index.SpecIndex) (*lowV3.PathItem, error) { + var pi lowV3.PathItem + lowmodel.BuildModel(node, &pi) + if err := pi.Build(context.Background(), nil, node, idx); err != nil { + return nil, err + } + return &pi, nil +} + +const ( + get = iota + put + post + del + options + head + patch + trace + query +) + +// PathItem represents a high-level OpenAPI 3+ PathItem object backed by a low-level one. +// +// Describes the operations available on a single path. A Path Item MAY be empty, due to ACL constraints. +// The path itself is still exposed to the documentation viewer but they will not know which operations and parameters +// are available. +// - https://spec.openapis.org/oas/v3.1.0#path-item-object +type PathItem struct { + Reference string `json:"$ref,omitempty" yaml:"$ref,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` + Get *Operation `json:"get,omitempty" yaml:"get,omitempty"` + Put *Operation `json:"put,omitempty" yaml:"put,omitempty"` + Post *Operation `json:"post,omitempty" yaml:"post,omitempty"` + Delete *Operation `json:"delete,omitempty" yaml:"delete,omitempty"` + Options *Operation `json:"options,omitempty" yaml:"options,omitempty"` + Head *Operation `json:"head,omitempty" yaml:"head,omitempty"` + Patch *Operation `json:"patch,omitempty" yaml:"patch,omitempty"` + Trace *Operation `json:"trace,omitempty" yaml:"trace,omitempty"` + Query *Operation `json:"query,omitempty" yaml:"query,omitempty"` + AdditionalOperations *orderedmap.Map[string, *Operation] `json:"additionalOperations,omitempty" yaml:"additionalOperations,omitempty"` // OpenAPI 3.2+ additional operations + Servers []*Server `json:"servers,omitempty" yaml:"servers,omitempty"` + Parameters []*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"` + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *lowV3.PathItem +} + +// NewPathItem creates a new high-level PathItem instance from a low-level one. +func NewPathItem(pathItem *lowV3.PathItem) *PathItem { + pi := new(PathItem) + pi.low = pathItem + pi.Description = pathItem.Description.Value + pi.Summary = pathItem.Summary.Value + pi.Extensions = high.ExtractExtensions(pathItem.Extensions) + var servers []*Server + for _, ser := range pathItem.Servers.Value { + servers = append(servers, NewServer(ser.Value)) + } + pi.Servers = servers + + // build operation async + type opResult struct { + method int + op *Operation + } + opChan := make(chan opResult) + buildOperation := func(method int, op *lowV3.Operation, c chan opResult) { + if op == nil { + c <- opResult{method: method, op: nil} + return + } + c <- opResult{method: method, op: NewOperation(op)} + } + // build out operations async. + go buildOperation(get, pathItem.Get.Value, opChan) + go buildOperation(put, pathItem.Put.Value, opChan) + go buildOperation(post, pathItem.Post.Value, opChan) + go buildOperation(del, pathItem.Delete.Value, opChan) + go buildOperation(options, pathItem.Options.Value, opChan) + go buildOperation(head, pathItem.Head.Value, opChan) + go buildOperation(patch, pathItem.Patch.Value, opChan) + go buildOperation(trace, pathItem.Trace.Value, opChan) + go buildOperation(query, pathItem.Query.Value, opChan) + + if !pathItem.Parameters.IsEmpty() { + params := make([]*Parameter, len(pathItem.Parameters.Value)) + for i := range pathItem.Parameters.Value { + params[i] = NewParameter(pathItem.Parameters.Value[i].Value) + } + pi.Parameters = params + } + + complete := false + opCount := 0 + for !complete { + opRes := <-opChan + switch opRes.method { + case get: + pi.Get = opRes.op + case put: + pi.Put = opRes.op + case post: + pi.Post = opRes.op + case del: + pi.Delete = opRes.op + case options: + pi.Options = opRes.op + case head: + pi.Head = opRes.op + case patch: + pi.Patch = opRes.op + case trace: + pi.Trace = opRes.op + case query: + pi.Query = opRes.op + } + + opCount++ + if opCount == 9 { + complete = true + } + } + + // build additional operations if present + if !pathItem.AdditionalOperations.IsEmpty() && pathItem.AdditionalOperations.Value.Len() > 0 { + pi.AdditionalOperations = orderedmap.New[string, *Operation]() + for k, v := range pathItem.AdditionalOperations.Value.FromOldest() { + pi.AdditionalOperations.Set(k.Value, NewOperation(v.Value)) + } + } + + return pi +} + +// GoLow returns the low level instance of PathItem, used to build the high-level one. +func (p *PathItem) GoLow() *lowV3.PathItem { + return p.low +} + +// GoLowUntyped will return the low-level PathItem instance that was used to create the high-level one, with no type +func (p *PathItem) GoLowUntyped() any { + return p.low +} + +// IsReference returns true if this PathItem is a reference to another PathItem definition. +func (p *PathItem) IsReference() bool { + return p.Reference != "" +} + +// GetReference returns the reference string if this is a reference PathItem. +func (p *PathItem) GetReference() string { + return p.Reference +} + +func (p *PathItem) GetOperations() *orderedmap.Map[string, *Operation] { + o := orderedmap.New[string, *Operation]() + + // TODO: this is a bit of a hack, but it works for now. We might just want to actually pull the data out of the document as a map and split it into the individual operations + + type op struct { + name string + op *Operation + line int + } + + getLine := func(field string, idx int) int { + if p.GoLow() == nil { + return idx + } + + l, ok := reflect.ValueOf(p.GoLow()).Elem().FieldByName(field).Interface().(low.NodeReference[*lowV3.Operation]) + if !ok || l.GetKeyNode() == nil { + return idx + } + + return l.GetKeyNode().Line + } + + ops := []op{} + + if p.Get != nil { + ops = append(ops, op{name: lowV3.GetLabel, op: p.Get, line: getLine("Get", -8)}) + } + if p.Put != nil { + ops = append(ops, op{name: lowV3.PutLabel, op: p.Put, line: getLine("Put", -7)}) + } + if p.Post != nil { + ops = append(ops, op{name: lowV3.PostLabel, op: p.Post, line: getLine("Post", -6)}) + } + if p.Delete != nil { + ops = append(ops, op{name: lowV3.DeleteLabel, op: p.Delete, line: getLine("Delete", -5)}) + } + if p.Options != nil { + ops = append(ops, op{name: lowV3.OptionsLabel, op: p.Options, line: getLine("Options", -4)}) + } + if p.Head != nil { + ops = append(ops, op{name: lowV3.HeadLabel, op: p.Head, line: getLine("Head", -3)}) + } + if p.Patch != nil { + ops = append(ops, op{name: lowV3.PatchLabel, op: p.Patch, line: getLine("Patch", -2)}) + } + if p.Trace != nil { + ops = append(ops, op{name: lowV3.TraceLabel, op: p.Trace, line: getLine("Trace", -1)}) + } + if p.Query != nil { + ops = append(ops, op{name: lowV3.QueryLabel, op: p.Query, line: getLine("Query", 0)}) + } + + // add additional operations if present - get line numbers from low-level KeyNodes + if p.AdditionalOperations != nil && p.AdditionalOperations.Len() > 0 { + if p.GoLow() != nil && !p.GoLow().AdditionalOperations.IsEmpty() { + for k := range p.GoLow().AdditionalOperations.Value.KeysFromOldest() { + // find the corresponding high-level operation + if highOp := p.AdditionalOperations.GetOrZero(k.Value); highOp != nil { + line := k.KeyNode.Line + ops = append(ops, op{name: k.Value, op: highOp, line: line}) + } + } + } + } + + slices.SortStableFunc(ops, func(a op, b op) int { + return a.line - b.line + }) + + for _, op := range ops { + o.Set(op.name, op.op) + } + + return o +} + +// Render will return a YAML representation of the PathItem object as a byte slice. +func (p *PathItem) Render() ([]byte, error) { + return yaml.Marshal(p) +} + +func (p *PathItem) RenderInline() ([]byte, error) { + d, _ := p.MarshalYAMLInline() + return yaml.Marshal(d) +} + +// MarshalYAML will create a ready to render YAML representation of the PathItem object. +func (p *PathItem) MarshalYAML() (interface{}, error) { + // Handle reference-only path item + if p.Reference != "" { + return utils.CreateRefNode(p.Reference), nil + } + nb := high.NewNodeBuilder(p, p.low) + return nb.Render(), nil +} + +// MarshalYAMLInline will create a ready to render YAML representation of the PathItem object, +// with all references resolved inline. +func (p *PathItem) MarshalYAMLInline() (interface{}, error) { + // reference-only objects render as $ref nodes + if p.Reference != "" { + return utils.CreateRefNode(p.Reference), nil + } + + // resolve external reference if present + if p.low != nil { + rendered, err := high.RenderExternalRef(p.low, buildLowPathItem, NewPathItem) + if err != nil || rendered != nil { + return rendered, err + } + } + + return high.RenderInline(p, p.low) +} + +// MarshalYAMLInlineWithContext will create a ready to render YAML representation of the PathItem object, +// resolving any references inline where possible. Uses the provided context for cycle detection. +// The ctx parameter should be *base.InlineRenderContext but is typed as any to satisfy the +// high.RenderableInlineWithContext interface without import cycles. +func (p *PathItem) MarshalYAMLInlineWithContext(ctx any) (interface{}, error) { + if p.Reference != "" { + return utils.CreateRefNode(p.Reference), nil + } + + // resolve external reference if present + if p.low != nil { + rendered, err := high.RenderExternalRefWithContext(p.low, buildLowPathItem, NewPathItem, ctx) + if err != nil || rendered != nil { + return rendered, err + } + } + + return high.RenderInlineWithContext(p, p.low, ctx) +} + +// CreatePathItemRef creates a PathItem that renders as a $ref to another path item definition. +// This is useful when building OpenAPI specs programmatically and you want to reference +// a path item defined in components/pathItems rather than inlining the full definition. +// +// Example: +// +// pi := v3.CreatePathItemRef("#/components/pathItems/CommonPathItem") +// +// Renders as: +// +// $ref: '#/components/pathItems/CommonPathItem' +func CreatePathItemRef(ref string) *PathItem { + return &PathItem{Reference: ref} +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/paths.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/paths.go new file mode 100644 index 000000000..c07edd5e6 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/paths.go @@ -0,0 +1,218 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "fmt" + "sort" + + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/datamodel/low" + v3low "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Paths represents a high-level OpenAPI 3+ Paths object, that is backed by a low-level one. +// +// Holds the relative paths to the individual endpoints and their operations. The path is appended to the URL from the +// Server Object in order to construct the full URL. The Paths MAY be empty, due to Access Control List (ACL) +// constraints. +// - https://spec.openapis.org/oas/v3.1.0#paths-object +type Paths struct { + PathItems *orderedmap.Map[string, *PathItem] `json:"-" yaml:"-"` + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *v3low.Paths +} + +// NewPaths creates a new high-level instance of Paths from a low-level one. +func NewPaths(paths *v3low.Paths) *Paths { + p := new(Paths) + p.low = paths + p.Extensions = high.ExtractExtensions(paths.Extensions) + items := orderedmap.New[string, *PathItem]() + + type pathItemResult struct { + key string + value *PathItem + } + + translateFunc := func(pair orderedmap.Pair[low.KeyReference[string], low.ValueReference[*v3low.PathItem]]) (pathItemResult, error) { + return pathItemResult{key: pair.Key().Value, value: NewPathItem(pair.Value().Value)}, nil + } + resultFunc := func(value pathItemResult) error { + items.Set(value.key, value.value) + return nil + } + _ = datamodel.TranslateMapParallel[low.KeyReference[string], low.ValueReference[*v3low.PathItem], pathItemResult]( + paths.PathItems, translateFunc, resultFunc, + ) + p.PathItems = items + return p +} + +// GoLow returns the low-level Paths instance used to create the high-level one. +func (p *Paths) GoLow() *v3low.Paths { + return p.low +} + +// GoLowUntyped will return the low-level Paths instance that was used to create the high-level one, with no type +func (p *Paths) GoLowUntyped() any { + return p.low +} + +// Render will return a YAML representation of the Paths object as a byte slice. +func (p *Paths) Render() ([]byte, error) { + return yaml.Marshal(p) +} + +func (p *Paths) RenderInline() ([]byte, error) { + d, _ := p.MarshalYAMLInline() + return yaml.Marshal(d) +} + +// MarshalYAML will create a ready to render YAML representation of the Paths object. +func (p *Paths) MarshalYAML() (interface{}, error) { + // map keys correctly. + m := utils.CreateEmptyMapNode() + type pathItem struct { + pi *PathItem + path string + line int + style yaml.Style + rendered *yaml.Node + } + var mapped []*pathItem + + for k, pi := range p.PathItems.FromOldest() { + ln := 9999 // default to a high value to weight new content to the bottom. + var style yaml.Style + if p.low != nil { + lpi := p.low.FindPath(k) + if lpi != nil { + ln = lpi.ValueNode.Line + } + + for lk := range p.low.PathItems.KeysFromOldest() { + if lk.Value == k { + style = lk.KeyNode.Style + break + } + } + } + mapped = append(mapped, &pathItem{pi, k, ln, style, nil}) + } + + nb := high.NewNodeBuilder(p, p.low) + extNode := nb.Render() + if extNode != nil && extNode.Content != nil { + var label string + for u := range extNode.Content { + if u%2 == 0 { + label = extNode.Content[u].Value + continue + } + mapped = append(mapped, &pathItem{ + nil, label, + extNode.Content[u].Line, 0, extNode.Content[u], + }) + } + } + + sort.Slice(mapped, func(i, j int) bool { + return mapped[i].line < mapped[j].line + }) + for _, mp := range mapped { + if mp.pi != nil { + rendered, _ := mp.pi.MarshalYAML() + + kn := utils.CreateStringNode(mp.path) + kn.Style = mp.style + + m.Content = append(m.Content, kn) + m.Content = append(m.Content, rendered.(*yaml.Node)) + } + if mp.rendered != nil { + m.Content = append(m.Content, utils.CreateStringNode(mp.path)) + m.Content = append(m.Content, mp.rendered) + } + } + + return m, nil +} + +func (p *Paths) MarshalYAMLInline() (interface{}, error) { + // map keys correctly. + m := utils.CreateEmptyMapNode() + type pathItem struct { + pi *PathItem + path string + line int + style yaml.Style + rendered *yaml.Node + } + var mapped []*pathItem + + for k, pi := range p.PathItems.FromOldest() { + ln := 9999 // default to a high value to weight new content to the bottom. + var style yaml.Style + if p.low != nil { + lpi := p.low.FindPath(k) + if lpi != nil { + ln = lpi.ValueNode.Line + } + + for lk := range p.low.PathItems.KeysFromOldest() { + if lk.Value == k { + style = lk.KeyNode.Style + break + } + } + } + mapped = append(mapped, &pathItem{pi, k, ln, style, nil}) + } + + nb := high.NewNodeBuilder(p, p.low) + nb.Resolve = true + extNode := nb.Render() + if extNode != nil && extNode.Content != nil { + var label string + for u := range extNode.Content { + if u%2 == 0 { + label = extNode.Content[u].Value + continue + } + mapped = append(mapped, &pathItem{ + nil, label, + extNode.Content[u].Line, 0, extNode.Content[u], + }) + } + } + + sort.Slice(mapped, func(i, j int) bool { + return mapped[i].line < mapped[j].line + }) + for _, mp := range mapped { + if mp.pi != nil { + rendered, err := mp.pi.MarshalYAMLInline() + if err != nil { + return nil, fmt.Errorf("failed to render path '%s' inline: %w", mp.path, err) + } + + kn := utils.CreateStringNode(mp.path) + kn.Style = mp.style + + m.Content = append(m.Content, kn) + m.Content = append(m.Content, rendered.(*yaml.Node)) + } + if mp.rendered != nil { + m.Content = append(m.Content, utils.CreateStringNode(mp.path)) + m.Content = append(m.Content, mp.rendered) + } + } + + return m, nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/request_body.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/request_body.go new file mode 100644 index 000000000..0ce7bd9a2 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/request_body.go @@ -0,0 +1,144 @@ +// Copyright 2022-2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + + "github.com/pb33f/libopenapi/datamodel/high" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + low "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// buildLowRequestBody builds a low-level RequestBody from a resolved YAML node. +func buildLowRequestBody(node *yaml.Node, idx *index.SpecIndex) (*low.RequestBody, error) { + var rb low.RequestBody + lowmodel.BuildModel(node, &rb) + rb.Build(context.Background(), nil, node, idx) + return &rb, nil +} + +// RequestBody represents a high-level OpenAPI 3+ RequestBody object, backed by a low-level one. +// - https://spec.openapis.org/oas/v3.1.0#request-body-object +type RequestBody struct { + Reference string `json:"$ref,omitempty" yaml:"$ref,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Content *orderedmap.Map[string, *MediaType] `json:"content,omitempty" yaml:"content,omitempty"` + Required *bool `json:"required,omitempty" yaml:"required,renderZero,omitempty"` + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *low.RequestBody +} + +// NewRequestBody will create a new high-level RequestBody instance, from a low-level one. +func NewRequestBody(rb *low.RequestBody) *RequestBody { + r := new(RequestBody) + r.low = rb + r.Description = rb.Description.Value + if !rb.Required.IsEmpty() { + r.Required = &rb.Required.Value + } + r.Extensions = high.ExtractExtensions(rb.Extensions) + r.Content = ExtractContent(rb.Content.Value) + return r +} + +// GoLow returns the low-level RequestBody instance used to create the high-level one. +func (r *RequestBody) GoLow() *low.RequestBody { + return r.low +} + +// GoLowUntyped will return the low-level RequestBody instance that was used to create the high-level one, with no type +func (r *RequestBody) GoLowUntyped() any { + return r.low +} + +// IsReference returns true if this RequestBody is a reference to another RequestBody definition. +func (r *RequestBody) IsReference() bool { + return r.Reference != "" +} + +// GetReference returns the reference string if this is a reference RequestBody. +func (r *RequestBody) GetReference() string { + return r.Reference +} + +// Render will return a YAML representation of the RequestBody object as a byte slice. +func (r *RequestBody) Render() ([]byte, error) { + return yaml.Marshal(r) +} + +func (r *RequestBody) RenderInline() ([]byte, error) { + d, _ := r.MarshalYAMLInline() + return yaml.Marshal(d) +} + +// MarshalYAML will create a ready to render YAML representation of the RequestBody object. +func (r *RequestBody) MarshalYAML() (interface{}, error) { + // Handle reference-only request body + if r.Reference != "" { + return utils.CreateRefNode(r.Reference), nil + } + nb := high.NewNodeBuilder(r, r.low) + return nb.Render(), nil +} + +// MarshalYAMLInline will create a ready to render YAML representation of the RequestBody object, +// resolving any references inline where possible. +func (r *RequestBody) MarshalYAMLInline() (interface{}, error) { + // reference-only objects render as $ref nodes + if r.Reference != "" { + return utils.CreateRefNode(r.Reference), nil + } + + // resolve external reference if present + if r.low != nil { + // buildLowRequestBody never returns an error, so we can ignore it + rendered, _ := high.RenderExternalRef(r.low, buildLowRequestBody, NewRequestBody) + if rendered != nil { + return rendered, nil + } + } + + return high.RenderInline(r, r.low) +} + +// MarshalYAMLInlineWithContext will create a ready to render YAML representation of the RequestBody object, +// resolving any references inline where possible. Uses the provided context for cycle detection. +// The ctx parameter should be *base.InlineRenderContext but is typed as any to satisfy the +// high.RenderableInlineWithContext interface without import cycles. +func (r *RequestBody) MarshalYAMLInlineWithContext(ctx any) (interface{}, error) { + if r.Reference != "" { + return utils.CreateRefNode(r.Reference), nil + } + + // resolve external reference if present + if r.low != nil { + // buildLowRequestBody never returns an error, so we can ignore it + rendered, _ := high.RenderExternalRefWithContext(r.low, buildLowRequestBody, NewRequestBody, ctx) + if rendered != nil { + return rendered, nil + } + } + + return high.RenderInlineWithContext(r, r.low, ctx) +} + +// CreateRequestBodyRef creates a RequestBody that renders as a $ref to another request body definition. +// This is useful when building OpenAPI specs programmatically and you want to reference +// a request body defined in components/requestBodies rather than inlining the full definition. +// +// Example: +// +// rb := v3.CreateRequestBodyRef("#/components/requestBodies/UserInput") +// +// Renders as: +// +// $ref: '#/components/requestBodies/UserInput' +func CreateRequestBodyRef(ref string) *RequestBody { + return &RequestBody{Reference: ref} +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/response.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/response.go new file mode 100644 index 000000000..418065beb --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/response.go @@ -0,0 +1,156 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/datamodel/low" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + lowv3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// buildLowResponse builds a low-level Response from a resolved YAML node. +func buildLowResponse(node *yaml.Node, idx *index.SpecIndex) (*lowv3.Response, error) { + var resp lowv3.Response + lowmodel.BuildModel(node, &resp) + if err := resp.Build(context.Background(), nil, node, idx); err != nil { + return nil, err + } + return &resp, nil +} + +// Response represents a high-level OpenAPI 3+ Response object that is backed by a low-level one. +// +// Describes a single response from an API Operation, including design-time, static links to +// operations based on the response. +// - https://spec.openapis.org/oas/v3.1.0#response-object +type Response struct { + Reference string `json:"$ref,omitempty" yaml:"$ref,omitempty"` + Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` + Description string `json:"description" yaml:"description"` + Headers *orderedmap.Map[string, *Header] `json:"headers,omitempty" yaml:"headers,omitempty"` + Content *orderedmap.Map[string, *MediaType] `json:"content,omitempty" yaml:"content,omitempty"` + Links *orderedmap.Map[string, *Link] `json:"links,omitempty" yaml:"links,omitempty"` + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *lowv3.Response +} + +// NewResponse creates a new high-level Response object that is backed by a low-level one. +func NewResponse(response *lowv3.Response) *Response { + r := new(Response) + r.low = response + r.Summary = response.Summary.Value + r.Description = response.Description.Value + if !response.Headers.IsEmpty() { + r.Headers = ExtractHeaders(response.Headers.Value) + } + r.Extensions = high.ExtractExtensions(response.Extensions) + if !response.Content.IsEmpty() { + r.Content = ExtractContent(response.Content.Value) + } + if !response.Links.IsEmpty() { + r.Links = low.FromReferenceMapWithFunc(response.Links.Value, NewLink) + } + return r +} + +// GoLow returns the low-level Response object that was used to create the high-level one. +func (r *Response) GoLow() *lowv3.Response { + return r.low +} + +// GoLowUntyped will return the low-level Response instance that was used to create the high-level one, with no type +func (r *Response) GoLowUntyped() any { + return r.low +} + +// IsReference returns true if this Response is a reference to another Response definition. +func (r *Response) IsReference() bool { + return r.Reference != "" +} + +// GetReference returns the reference string if this is a reference Response. +func (r *Response) GetReference() string { + return r.Reference +} + +// Render will return a YAML representation of the Response object as a byte slice. +func (r *Response) Render() ([]byte, error) { + return yaml.Marshal(r) +} + +func (r *Response) RenderInline() ([]byte, error) { + d, _ := r.MarshalYAMLInline() + return yaml.Marshal(d) +} + +// MarshalYAML will create a ready to render YAML representation of the Response object. +func (r *Response) MarshalYAML() (interface{}, error) { + // Handle reference-only response + if r.Reference != "" { + return utils.CreateRefNode(r.Reference), nil + } + nb := high.NewNodeBuilder(r, r.low) + return nb.Render(), nil +} + +// MarshalYAMLInline will create a ready to render YAML representation of the Response object, +// resolving any references inline where possible. +func (r *Response) MarshalYAMLInline() (interface{}, error) { + // reference-only objects render as $ref nodes + if r.Reference != "" { + return utils.CreateRefNode(r.Reference), nil + } + + // resolve external reference if present + if r.low != nil { + rendered, err := high.RenderExternalRef(r.low, buildLowResponse, NewResponse) + if err != nil || rendered != nil { + return rendered, err + } + } + + return high.RenderInline(r, r.low) +} + +// MarshalYAMLInlineWithContext will create a ready to render YAML representation of the Response object, +// resolving any references inline where possible. Uses the provided context for cycle detection. +// The ctx parameter should be *base.InlineRenderContext but is typed as any to satisfy the +// high.RenderableInlineWithContext interface without import cycles. +func (r *Response) MarshalYAMLInlineWithContext(ctx any) (interface{}, error) { + if r.Reference != "" { + return utils.CreateRefNode(r.Reference), nil + } + + // resolve external reference if present + if r.low != nil { + rendered, err := high.RenderExternalRefWithContext(r.low, buildLowResponse, NewResponse, ctx) + if err != nil || rendered != nil { + return rendered, err + } + } + + return high.RenderInlineWithContext(r, r.low, ctx) +} + +// CreateResponseRef creates a Response that renders as a $ref to another response definition. +// This is useful when building OpenAPI specs programmatically and you want to reference +// a response defined in components/responses rather than inlining the full definition. +// +// Example: +// +// resp := v3.CreateResponseRef("#/components/responses/NotFound") +// +// Renders as: +// +// $ref: '#/components/responses/NotFound' +func CreateResponseRef(ref string) *Response { + return &Response{Reference: ref} +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/responses.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/responses.go new file mode 100644 index 000000000..cdef1dcae --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/responses.go @@ -0,0 +1,222 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "fmt" + "sort" + + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/datamodel/high" + lowbase "github.com/pb33f/libopenapi/datamodel/low" + low "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Responses represents a high-level OpenAPI 3+ Responses object that is backed by a low-level one. +// +// It's a container for the expected responses of an operation. The container maps a HTTP response code to the +// expected response. +// +// The specification is not necessarily expected to cover all possible HTTP response codes because they may not be +// known in advance. However, documentation is expected to cover a successful operation response and any known errors. +// +// The default MAY be used as a default response object for all HTTP codes that are not covered individually by +// the Responses Object. +// +// The Responses Object MUST contain at least one response code, and if only one response code is provided it SHOULD +// be the response for a successful operation call. +// - https://spec.openapis.org/oas/v3.1.0#responses-object +type Responses struct { + Codes *orderedmap.Map[string, *Response] `json:"-" yaml:"-"` + Default *Response `json:"default,omitempty" yaml:"default,omitempty"` + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *low.Responses +} + +// NewResponses will create a new high-level Responses instance from a low-level one. It operates asynchronously +// internally, as each response may be considerable in complexity. +func NewResponses(responses *low.Responses) *Responses { + r := new(Responses) + r.low = responses + r.Extensions = high.ExtractExtensions(responses.Extensions) + if !responses.Default.IsEmpty() { + r.Default = NewResponse(responses.Default.Value) + } + codes := orderedmap.New[string, *Response]() + + translateFunc := func(pair orderedmap.Pair[lowbase.KeyReference[string], lowbase.ValueReference[*low.Response]]) (asyncResult[*Response], error) { + return asyncResult[*Response]{ + key: pair.Key().Value, + result: NewResponse(pair.Value().Value), + }, nil + } + resultFunc := func(value asyncResult[*Response]) error { + codes.Set(value.key, value.result) + return nil + } + _ = datamodel.TranslateMapParallel[lowbase.KeyReference[string], lowbase.ValueReference[*low.Response]](responses.Codes, translateFunc, resultFunc) + r.Codes = codes + return r +} + +// FindResponseByCode is a shortcut for looking up code by an integer vs. a string +func (r *Responses) FindResponseByCode(code int) *Response { + return r.Codes.GetOrZero(fmt.Sprintf("%d", code)) +} + +// GoLow returns the low-level Response object used to create the high-level one. +func (r *Responses) GoLow() *low.Responses { + return r.low +} + +// GoLowUntyped will return the low-level Responses instance that was used to create the high-level one, with no type +func (r *Responses) GoLowUntyped() any { + return r.low +} + +// Render will return a YAML representation of the Responses object as a byte slice. +func (r *Responses) Render() ([]byte, error) { + return yaml.Marshal(r) +} + +func (r *Responses) RenderInline() ([]byte, error) { + d, _ := r.MarshalYAMLInline() + return yaml.Marshal(d) +} + +// MarshalYAML will create a ready to render YAML representation of the Responses object. +func (r *Responses) MarshalYAML() (interface{}, error) { + // map keys correctly. + m := utils.CreateEmptyMapNode() + type responseItem struct { + resp *Response + code string + line int + ext *yaml.Node + style yaml.Style + } + var mapped []*responseItem + + for code, resp := range r.Codes.FromOldest() { + ln := 9999 // default to a high value to weight new content to the bottom. + var style yaml.Style + if r.low != nil { + for lk := range r.low.Codes.KeysFromOldest() { + if lk.Value == code { + ln = lk.KeyNode.Line + style = lk.KeyNode.Style + } + } + } + mapped = append(mapped, &responseItem{resp, code, ln, nil, style}) + } + + // extract extensions + nb := high.NewNodeBuilder(r, r.low) + extNode := nb.Render() + if extNode != nil && extNode.Content != nil { + var label string + for u := range extNode.Content { + if u%2 == 0 { + label = extNode.Content[u].Value + continue + } + mapped = append(mapped, &responseItem{ + nil, label, + extNode.Content[u].Line, extNode.Content[u], 0, + }) + } + } + + sort.Slice(mapped, func(i, j int) bool { + return mapped[i].line < mapped[j].line + }) + for _, mp := range mapped { + if mp.resp != nil { + rendered, _ := mp.resp.MarshalYAML() + + kn := utils.CreateStringNode(mp.code) + kn.Style = mp.style + + m.Content = append(m.Content, kn) + m.Content = append(m.Content, rendered.(*yaml.Node)) + } + if mp.ext != nil { + m.Content = append(m.Content, utils.CreateStringNode(mp.code)) + m.Content = append(m.Content, mp.ext) + } + + } + return m, nil +} + +func (r *Responses) MarshalYAMLInline() (interface{}, error) { + // map keys correctly. + m := utils.CreateEmptyMapNode() + type responseItem struct { + resp *Response + code string + line int + ext *yaml.Node + style yaml.Style + } + var mapped []*responseItem + + for code, resp := range r.Codes.FromOldest() { + ln := 9999 // default to a high value to weight new content to the bottom. + var style yaml.Style + if r.low != nil { + for lk := range r.low.Codes.KeysFromOldest() { + if lk.Value == code { + ln = lk.KeyNode.Line + style = lk.KeyNode.Style + } + } + } + mapped = append(mapped, &responseItem{resp, code, ln, nil, style}) + } + + // extract extensions + nb := high.NewNodeBuilder(r, r.low) + nb.Resolve = true + extNode := nb.Render() + if extNode != nil && extNode.Content != nil { + var label string + for u := range extNode.Content { + if u%2 == 0 { + label = extNode.Content[u].Value + continue + } + mapped = append(mapped, &responseItem{ + nil, label, + extNode.Content[u].Line, extNode.Content[u], 0, + }) + } + } + + sort.Slice(mapped, func(i, j int) bool { + return mapped[i].line < mapped[j].line + }) + for _, mp := range mapped { + if mp.resp != nil { + rendered, _ := mp.resp.MarshalYAMLInline() + + kn := utils.CreateStringNode(mp.code) + kn.Style = mp.style + + m.Content = append(m.Content, kn) + m.Content = append(m.Content, rendered.(*yaml.Node)) + + } + if mp.ext != nil { + m.Content = append(m.Content, utils.CreateStringNode(mp.code)) + m.Content = append(m.Content, mp.ext) + } + + } + return m, nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/security_scheme.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/security_scheme.go new file mode 100644 index 000000000..512117f76 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/security_scheme.go @@ -0,0 +1,161 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + + "github.com/pb33f/libopenapi/datamodel/high" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + low "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// buildLowSecurityScheme builds a low-level SecurityScheme from a resolved YAML node. +func buildLowSecurityScheme(node *yaml.Node, idx *index.SpecIndex) (*low.SecurityScheme, error) { + var ss low.SecurityScheme + lowmodel.BuildModel(node, &ss) + ss.Build(context.Background(), nil, node, idx) + return &ss, nil +} + +// SecurityScheme represents a high-level OpenAPI 3+ SecurityScheme object that is backed by a low-level one. +// +// Defines a security scheme that can be used by the operations. +// +// Supported schemes are HTTP authentication, an API key (either as a header, a cookie parameter or as a query parameter), +// mutual TLS (use of a client certificate), OAuth2's common flows (implicit, password, client credentials and +// authorization code) as defined in RFC6749 (https://www.rfc-editor.org/rfc/rfc6749), and OpenID Connect Discovery. +// Please note that as of 2020, the implicit flow is about to be deprecated by OAuth 2.0 Security Best Current Practice. +// Recommended for most use case is Authorization Code Grant flow with PKCE. +// - https://spec.openapis.org/oas/v3.1.0#security-scheme-object +type SecurityScheme struct { + Reference string `json:"$ref,omitempty" yaml:"$ref,omitempty"` + Type string `json:"type,omitempty" yaml:"type,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Name string `json:"name,omitempty" yaml:"name,omitempty"` + In string `json:"in,omitempty" yaml:"in,omitempty"` + Scheme string `json:"scheme,omitempty" yaml:"scheme,omitempty"` + BearerFormat string `json:"bearerFormat,omitempty" yaml:"bearerFormat,omitempty"` + Flows *OAuthFlows `json:"flows,omitempty" yaml:"flows,omitempty"` + OpenIdConnectUrl string `json:"openIdConnectUrl,omitempty" yaml:"openIdConnectUrl,omitempty"` + OAuth2MetadataUrl string `json:"oauth2MetadataUrl,omitempty" yaml:"oauth2MetadataUrl,omitempty"` // OpenAPI 3.2+ OAuth2 metadata URL + Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` // OpenAPI 3.2+ deprecated flag + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *low.SecurityScheme +} + +// NewSecurityScheme creates a new high-level SecurityScheme from a low-level one. +func NewSecurityScheme(ss *low.SecurityScheme) *SecurityScheme { + s := new(SecurityScheme) + s.low = ss + s.Type = ss.Type.Value + s.Description = ss.Description.Value + s.Name = ss.Name.Value + s.Scheme = ss.Scheme.Value + s.In = ss.In.Value + s.BearerFormat = ss.BearerFormat.Value + s.OpenIdConnectUrl = ss.OpenIdConnectUrl.Value + s.OAuth2MetadataUrl = ss.OAuth2MetadataUrl.Value + s.Deprecated = ss.Deprecated.Value + s.Extensions = high.ExtractExtensions(ss.Extensions) + if !ss.Flows.IsEmpty() { + s.Flows = NewOAuthFlows(ss.Flows.Value) + } + return s +} + +// GoLow returns the low-level SecurityScheme that was used to create the high-level one. +func (s *SecurityScheme) GoLow() *low.SecurityScheme { + return s.low +} + +// GoLowUntyped will return the low-level SecurityScheme instance that was used to create the high-level one, with no type +func (s *SecurityScheme) GoLowUntyped() any { + return s.low +} + +// IsReference returns true if this SecurityScheme is a reference to another SecurityScheme definition. +func (s *SecurityScheme) IsReference() bool { + return s.Reference != "" +} + +// GetReference returns the reference string if this is a reference SecurityScheme. +func (s *SecurityScheme) GetReference() string { + return s.Reference +} + +// Render will return a YAML representation of the SecurityScheme object as a byte slice. +func (s *SecurityScheme) Render() ([]byte, error) { + return yaml.Marshal(s) +} + +// MarshalYAML will create a ready to render YAML representation of the SecurityScheme object. +func (s *SecurityScheme) MarshalYAML() (interface{}, error) { + // Handle reference-only security scheme + if s.Reference != "" { + return utils.CreateRefNode(s.Reference), nil + } + nb := high.NewNodeBuilder(s, s.low) + return nb.Render(), nil +} + +// MarshalYAMLInline will create a ready to render YAML representation of the SecurityScheme object, +// with all references resolved inline. +func (s *SecurityScheme) MarshalYAMLInline() (interface{}, error) { + // reference-only objects render as $ref nodes + if s.Reference != "" { + return utils.CreateRefNode(s.Reference), nil + } + + // resolve external reference if present + if s.low != nil { + // buildLowSecurityScheme never returns an error, so we can ignore it + rendered, _ := high.RenderExternalRef(s.low, buildLowSecurityScheme, NewSecurityScheme) + if rendered != nil { + return rendered, nil + } + } + + return high.RenderInline(s, s.low) +} + +// MarshalYAMLInlineWithContext will create a ready to render YAML representation of the SecurityScheme object, +// resolving any references inline where possible. Uses the provided context for cycle detection. +// The ctx parameter should be *base.InlineRenderContext but is typed as any to satisfy the +// high.RenderableInlineWithContext interface without import cycles. +func (s *SecurityScheme) MarshalYAMLInlineWithContext(ctx any) (interface{}, error) { + if s.Reference != "" { + return utils.CreateRefNode(s.Reference), nil + } + + // resolve external reference if present + if s.low != nil { + // buildLowSecurityScheme never returns an error, so we can ignore it + rendered, _ := high.RenderExternalRefWithContext(s.low, buildLowSecurityScheme, NewSecurityScheme, ctx) + if rendered != nil { + return rendered, nil + } + } + + return high.RenderInlineWithContext(s, s.low, ctx) +} + +// CreateSecuritySchemeRef creates a SecurityScheme that renders as a $ref to another security scheme definition. +// This is useful when building OpenAPI specs programmatically and you want to reference +// a security scheme defined in components/securitySchemes rather than inlining the full definition. +// +// Example: +// +// ss := v3.CreateSecuritySchemeRef("#/components/securitySchemes/BearerAuth") +// +// Renders as: +// +// $ref: '#/components/securitySchemes/BearerAuth' +func CreateSecuritySchemeRef(ref string) *SecurityScheme { + return &SecurityScheme{Reference: ref} +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/server.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/server.go new file mode 100644 index 000000000..4f6a3cb07 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/server.go @@ -0,0 +1,56 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/datamodel/low" + lowv3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// Server represents a high-level OpenAPI 3+ Server object, that is backed by a low level one. +// - https://spec.openapis.org/oas/v3.1.0#server-object +type Server struct { + Name string `json:"name,omitempty" yaml:"name,omitempty"` // OpenAPI 3.2+ name field for documentation + URL string `json:"url,omitempty" yaml:"url,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Variables *orderedmap.Map[string, *ServerVariable] `json:"variables,omitempty" yaml:"variables,omitempty"` + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *lowv3.Server +} + +// NewServer will create a new high-level Server instance from a low-level one. +func NewServer(server *lowv3.Server) *Server { + s := new(Server) + s.low = server + s.Name = server.Name.Value + s.Description = server.Description.Value + s.URL = server.URL.Value + s.Variables = low.FromReferenceMapWithFunc(server.Variables.Value, NewServerVariable) + s.Extensions = high.ExtractExtensions(server.Extensions) + return s +} + +// GoLow returns the low-level Server instance that was used to create the high-level one +func (s *Server) GoLow() *lowv3.Server { + return s.low +} + +// GoLowUntyped will return the low-level Server instance that was used to create the high-level one, with no type +func (s *Server) GoLowUntyped() any { + return s.low +} + +// Render will return a YAML representation of the Server object as a byte slice. +func (s *Server) Render() ([]byte, error) { + return yaml.Marshal(s) +} + +// MarshalYAML will create a ready to render YAML representation of the Server object. +func (s *Server) MarshalYAML() (interface{}, error) { + nb := high.NewNodeBuilder(s, s.low) + return nb.Render(), nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/server_variable.go b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/server_variable.go new file mode 100644 index 000000000..f798ff81f --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/high/v3/server_variable.go @@ -0,0 +1,61 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// ServerVariable represents a high-level OpenAPI 3+ ServerVariable object, that is backed by a low-level one. +// +// ServerVariable is an object representing a Server Variable for server URL template substitution. +// - https://spec.openapis.org/oas/v3.1.0#server-variable-object +type ServerVariable struct { + Enum []string `json:"enum,omitempty" yaml:"enum,omitempty"` + Default string `json:"default,omitempty" yaml:"default,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` + low *low.ServerVariable +} + +// NewServerVariable will return a new high-level instance of a ServerVariable from a low-level one. +func NewServerVariable(variable *low.ServerVariable) *ServerVariable { + v := new(ServerVariable) + v.low = variable + var enums []string + for _, enum := range variable.Enum { + if enum.Value != "" { + enums = append(enums, enum.Value) + } + } + v.Default = variable.Default.Value + v.Description = variable.Description.Value + v.Enum = enums + v.Extensions = high.ExtractExtensions(variable.Extensions) + return v +} + +// GoLow returns the low-level ServerVariable used to create the high\-level one. +func (s *ServerVariable) GoLow() *low.ServerVariable { + return s.low +} + +// GoLowUntyped will return the low-level ServerVariable instance that was used to create the high-level one, with no type +func (s *ServerVariable) GoLowUntyped() any { + return s.low +} + +// Render will return a YAML representation of the ServerVariable object as a byte slice. +func (s *ServerVariable) Render() ([]byte, error) { + return yaml.Marshal(s) +} + +// MarshalYAML will create a ready to render YAML representation of the ServerVariable object. +func (s *ServerVariable) MarshalYAML() (interface{}, error) { + nb := high.NewNodeBuilder(s, s.low) + return nb.Render(), nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/base/base.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/base.go new file mode 100644 index 000000000..0bfacf20a --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/base.go @@ -0,0 +1,11 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +// Package base contains shared low-level models that are used between both versions 2 and 3 of OpenAPI. +// These models are consistent across both specifications, except for the Schema. +// +// OpenAPI 3 contains all the same properties that an OpenAPI 2 specification does, and more. The choice +// to not duplicate the schemas is to allow a graceful degradation pattern to be used. Schemas are the most complex +// beats, particularly when polymorphism is used. By re-using the same superset Schema across versions, we can ensure +// that all the latest features are collected, without damaging backwards compatibility. +package base diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/base/circ_check.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/circ_check.go new file mode 100644 index 000000000..c72af1e5b --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/circ_check.go @@ -0,0 +1,33 @@ +// Copyright 2023-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// https://pb33f.io + +package base + +// CheckSchemaProxyForCircularRefs checks if the provided SchemaProxy has any circular references, extracted from +// The rolodex attached to the index. +func CheckSchemaProxyForCircularRefs(s *SchemaProxy) bool { + if s.GetIndex() == nil || s.GetIndex().GetRolodex() == nil { + return false // no index or rolodex, so no circular references + } + rolo := s.GetIndex().GetRolodex() + allCircs := rolo.GetRootIndex().GetCircularReferences() + safeCircularRefs := rolo.GetSafeCircularReferences() + ignoredCircularRefs := rolo.GetIgnoredCircularReferences() + combinedCircularRefs := append(safeCircularRefs, ignoredCircularRefs...) + combinedCircularRefs = append(combinedCircularRefs, allCircs...) + dup := make(map[string]struct{}) + for _, ref := range combinedCircularRefs { + // hash the root node of the schema reference + if ref.LoopPoint.FullDefinition == s.GetReference() || ref.LoopPoint.Definition == s.GetReference() { + return true + } + // check journey, if we have any duplicated + for _, ji := range ref.Journey { + if _, exists := dup[ji.FullDefinition]; exists { + return true // this has already been checked, it's a loop. + } + dup[ji.FullDefinition] = struct{}{} + } + } + return false +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/base/constants.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/constants.go new file mode 100644 index 000000000..078ac9a99 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/constants.go @@ -0,0 +1,72 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +// Constants for labels used to look up values within OpenAPI specifications. +const ( + VersionLabel = "version" + TermsOfServiceLabel = "termsOfService" + DescriptionLabel = "description" + TitleLabel = "title" + EmailLabel = "email" + NameLabel = "name" + SummaryLabel = "summary" + URLLabel = "url" + ServersLabel = "servers" + ServerLabel = "server" + TagsLabel = "tags" + ParentLabel = "parent" + KindLabel = "kind" + ExternalDocsLabel = "externalDocs" + ExamplesLabel = "examples" + ExampleLabel = "example" + ValueLabel = "value" + DataValueLabel = "dataValue" // OpenAPI 3.2+ dataValue field + SerializedValueLabel = "serializedValue" // OpenAPI 3.2+ serializedValue field + InfoLabel = "info" + ContactLabel = "contact" + LicenseLabel = "license" + PropertiesLabel = "properties" + DependentSchemasLabel = "dependentSchemas" + DependentRequiredLabel = "dependentRequired" + PatternPropertiesLabel = "patternProperties" + IfLabel = "if" + ElseLabel = "else" + ThenLabel = "then" + PropertyNamesLabel = "propertyNames" + UnevaluatedItemsLabel = "unevaluatedItems" + UnevaluatedPropertiesLabel = "unevaluatedProperties" + AdditionalPropertiesLabel = "additionalProperties" + XMLLabel = "xml" + NodeTypeLabel = "nodeType" + ItemsLabel = "items" + PrefixItemsLabel = "prefixItems" + ContainsLabel = "contains" + AllOfLabel = "allOf" + AnyOfLabel = "anyOf" + OneOfLabel = "oneOf" + NotLabel = "not" + TypeLabel = "type" + DiscriminatorLabel = "discriminator" + DefaultMappingLabel = "defaultMapping" + MappingLabel = "mapping" + PropertyNameLabel = "propertyName" + ExclusiveMinimumLabel = "exclusiveMinimum" + ExclusiveMaximumLabel = "exclusiveMaximum" + SchemaLabel = "schema" + SchemaTypeLabel = "$schema" + IdLabel = "$id" + AnchorLabel = "$anchor" + DynamicAnchorLabel = "$dynamicAnchor" + DynamicRefLabel = "$dynamicRef" + CommentLabel = "$comment" + ContentSchemaLabel = "contentSchema" + VocabularyLabel = "$vocabulary" +) + +/* +PropertyNames low.NodeReference[*SchemaProxy] + UnevaluatedItems low.NodeReference[*SchemaProxy] + UnevaluatedProperties low.NodeReference[*SchemaProxy] +*/ diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/base/contact.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/contact.go new file mode 100644 index 000000000..cc1d43a23 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/contact.go @@ -0,0 +1,87 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "context" + "hash/maphash" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// Contact represents a low-level representation of the Contact definitions found at +// +// v2 - https://swagger.io/specification/v2/#contactObject +// v3 - https://spec.openapis.org/oas/v3.1.0#contact-object +type Contact struct { + Name low.NodeReference[string] + URL low.NodeReference[string] + Email low.NodeReference[string] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +func (c *Contact) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + c.KeyNode = keyNode + c.RootNode = root + c.Reference = new(low.Reference) + c.Nodes = low.ExtractNodes(ctx, root) + c.Extensions = low.ExtractExtensions(root) + c.context = ctx + c.index = idx + return nil +} + +// GetIndex will return the index.SpecIndex instance attached to the Contact object +func (c *Contact) GetIndex() *index.SpecIndex { + return c.index +} + +// GetContext will return the context.Context instance used when building the Contact object +func (c *Contact) GetContext() context.Context { + return c.context +} + +// GetRootNode will return the root yaml node of the Contact object +func (c *Contact) GetRootNode() *yaml.Node { + return c.RootNode +} + +// GetKeyNode will return the key yaml node of the Contact object +func (c *Contact) GetKeyNode() *yaml.Node { + return c.KeyNode +} + +// Hash will return a consistent hash of the Contact object +func (c *Contact) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if !c.Name.IsEmpty() { + h.WriteString(c.Name.Value) + h.WriteByte(low.HASH_PIPE) + } + if !c.URL.IsEmpty() { + h.WriteString(c.URL.Value) + h.WriteByte(low.HASH_PIPE) + } + if !c.Email.IsEmpty() { + h.WriteString(c.Email.Value) + h.WriteByte(low.HASH_PIPE) + } + // Note: Extensions are not included in the hash for Contact + return h.Sum64() + }) +} + +// GetExtensions returns all extensions for Contact +func (c *Contact) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return c.Extensions +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/base/context.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/context.go new file mode 100644 index 000000000..904fb9c0f --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/context.go @@ -0,0 +1,30 @@ +// Copyright 2023-2024 Princess Beef Heavy Industries, LLC / Dave Shanley +// https://pb33f.io + +package base + +import ( + "context" + "sync" +) + +// ModelContext is a struct that holds various persistent data structures for the model +// that passes through the entire model building process. +type ModelContext struct { + SchemaCache *sync.Map +} + +// GetModelContext will return the ModelContext from a context.Context object +// if it is available, otherwise it will return nil. +func GetModelContext(ctx context.Context) *ModelContext { + if ctx == nil { + return nil + } + if ctx.Value("modelCtx") == nil { + return nil + } + if c, ok := ctx.Value("modelCtx").(*ModelContext); ok { + return c + } + return nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/base/discriminator.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/discriminator.go new file mode 100644 index 000000000..77157492b --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/discriminator.go @@ -0,0 +1,71 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "hash/maphash" + + "go.yaml.in/yaml/v4" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/orderedmap" +) + +// Discriminator is only used by OpenAPI 3+ documents, it represents a polymorphic discriminator used for schemas +// +// When request bodies or response payloads may be one of a number of different schemas, a discriminator object can be +// used to aid in serialization, deserialization, and validation. The discriminator is a specific object in a schema +// which is used to inform the consumer of the document of an alternative schema based on the value associated with it. +// +// When using the discriminator, inline schemas will not be considered. +// +// v3 - https://spec.openapis.org/oas/v3.1.0#discriminator-object +type Discriminator struct { + PropertyName low.NodeReference[string] + Mapping low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[string]]] + DefaultMapping low.NodeReference[string] // OpenAPI 3.2+ defaultMapping for fallback schema + KeyNode *yaml.Node + RootNode *yaml.Node + low.Reference + low.NodeMap +} + +// GetRootNode will return the root yaml node of the Discriminator object +func (d *Discriminator) GetRootNode() *yaml.Node { + return d.RootNode +} + +// GetKeyNode will return the key yaml node of the Discriminator object +func (d *Discriminator) GetKeyNode() *yaml.Node { + return d.KeyNode +} + +// FindMappingValue will return a ValueReference containing the string mapping value +func (d *Discriminator) FindMappingValue(key string) *low.ValueReference[string] { + for k, v := range d.Mapping.Value.FromOldest() { + if k.Value == key { + return &v + } + } + return nil +} + +// Hash will return a consistent hash of the Discriminator object +func (d *Discriminator) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if d.PropertyName.Value != "" { + h.WriteString(d.PropertyName.Value) + h.WriteByte(low.HASH_PIPE) + } + for v := range orderedmap.SortAlpha(d.Mapping.Value).ValuesFromOldest() { + h.WriteString(v.Value) + h.WriteByte(low.HASH_PIPE) + } + if d.DefaultMapping.Value != "" { + h.WriteString(d.DefaultMapping.Value) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/base/example.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/example.go new file mode 100644 index 000000000..5b5efa143 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/example.go @@ -0,0 +1,165 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "context" + "hash/maphash" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Example represents a low-level Example object as defined by OpenAPI 3+ +// +// v3 - https://spec.openapis.org/oas/v3.1.0#example-object +type Example struct { + Summary low.NodeReference[string] + Description low.NodeReference[string] + Value low.NodeReference[*yaml.Node] + ExternalValue low.NodeReference[string] + DataValue low.NodeReference[*yaml.Node] // OpenAPI 3.2+ dataValue field (mutually exclusive with value/externalValue) + SerializedValue low.NodeReference[string] // OpenAPI 3.2+ serializedValue field (mutually exclusive with value/externalValue) + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// FindExtension returns a ValueReference containing the extension value, if found. +func (ex *Example) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap[*yaml.Node](ext, ex.Extensions) +} + +// GetRootNode will return the root yaml node of the Example object +func (ex *Example) GetRootNode() *yaml.Node { + return ex.RootNode +} + +// GetKeyNode will return the key yaml node of the Example object +func (ex *Example) GetKeyNode() *yaml.Node { + return ex.KeyNode +} + +// Hash will return a consistent hash of the Example object +func (ex *Example) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if ex.Summary.Value != "" { + h.WriteString(ex.Summary.Value) + h.WriteByte(low.HASH_PIPE) + } + if ex.Description.Value != "" { + h.WriteString(ex.Description.Value) + h.WriteByte(low.HASH_PIPE) + } + if ex.Value.Value != nil && !ex.Value.Value.IsZero() { + h.WriteString(low.GenerateHashString(ex.Value.Value)) + h.WriteByte(low.HASH_PIPE) + } + if ex.ExternalValue.Value != "" { + h.WriteString(ex.ExternalValue.Value) + h.WriteByte(low.HASH_PIPE) + } + if ex.DataValue.Value != nil && !ex.DataValue.Value.IsZero() { + h.WriteString(low.GenerateHashString(ex.DataValue.Value)) + h.WriteByte(low.HASH_PIPE) + } + if ex.SerializedValue.Value != "" { + h.WriteString(ex.SerializedValue.Value) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(ex.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} + +// Build extracts extensions and example value +func (ex *Example) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + ex.KeyNode = keyNode + ex.Reference = new(low.Reference) + if ok, _, ref := utils.IsNodeRefValue(root); ok { + ex.SetReference(ref, root) + } + root = utils.NodeAlias(root) + ex.RootNode = root + utils.CheckForMergeNodes(root) + ex.Nodes = low.ExtractNodes(ctx, root) + ex.Extensions = low.ExtractExtensions(root) + ex.context = ctx + ex.index = idx + + _, ln, vn := utils.FindKeyNodeFull(ValueLabel, root.Content) + _, dataLn, dataVn := utils.FindKeyNodeFull(DataValueLabel, root.Content) + _, serializedLn, serializedVn := utils.FindKeyNodeFull(SerializedValueLabel, root.Content) + + if vn != nil { + ex.Value = low.NodeReference[*yaml.Node]{ + Value: vn, + KeyNode: ln, + ValueNode: vn, + } + + // extract nodes for all value nodes down the tree. + expChildNodes := low.ExtractNodesRecursive(ctx, vn) + expChildNodes.Range(func(k, v interface{}) bool { + if arr, ko := v.([]*yaml.Node); ko { + ex.Nodes.Store(k, arr) + } + return true + }) + } + + // OpenAPI 3.2+ dataValue field + if dataVn != nil { + ex.DataValue = low.NodeReference[*yaml.Node]{ + Value: dataVn, + KeyNode: dataLn, + ValueNode: dataVn, + } + + // extract nodes for all dataValue nodes down the tree. + expChildNodes := low.ExtractNodesRecursive(ctx, dataVn) + expChildNodes.Range(func(k, v interface{}) bool { + if arr, ko := v.([]*yaml.Node); ko { + ex.Nodes.Store(k, arr) + } + return true + }) + } + + // OpenAPI 3.2+ serializedValue field + if serializedVn != nil { + ex.SerializedValue = low.NodeReference[string]{ + Value: serializedVn.Value, + KeyNode: serializedLn, + ValueNode: serializedVn, + } + } + + return nil +} + +// GetExtensions will return Example extensions to satisfy the HasExtensions interface. +func (ex *Example) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return ex.Extensions +} + +// GetIndex will return the index.SpecIndex instance attached to the Example object +func (ex *Example) GetIndex() *index.SpecIndex { + return ex.index +} + +// GetContext will return the context.Context instance used when building the Example object +func (ex *Example) GetContext() context.Context { + return ex.context +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/base/external_doc.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/external_doc.go new file mode 100644 index 000000000..534cc3712 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/external_doc.go @@ -0,0 +1,95 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "context" + "hash/maphash" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// ExternalDoc represents a low-level External Documentation object as defined by OpenAPI 2 and 3 +// +// Allows referencing an external resource for extended documentation. +// +// v2 - https://swagger.io/specification/v2/#externalDocumentationObject +// v3 - https://spec.openapis.org/oas/v3.1.0#external-documentation-object +type ExternalDoc struct { + Description low.NodeReference[string] + URL low.NodeReference[string] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// FindExtension returns a ValueReference containing the extension value, if found. +func (ex *ExternalDoc) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap[*yaml.Node](ext, ex.Extensions) +} + +// GetRootNode will return the root yaml node of the ExternalDoc object +func (ex *ExternalDoc) GetRootNode() *yaml.Node { + return ex.RootNode +} + +// GetKeyNode will return the key yaml node of the ExternalDoc object +func (ex *ExternalDoc) GetKeyNode() *yaml.Node { + return ex.KeyNode +} + +// Build will extract extensions from the ExternalDoc instance. +func (ex *ExternalDoc) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + ex.KeyNode = keyNode + root = utils.NodeAlias(root) + ex.RootNode = root + utils.CheckForMergeNodes(root) + ex.Reference = new(low.Reference) + ex.Nodes = low.ExtractNodes(ctx, root) + ex.Extensions = low.ExtractExtensions(root) + ex.context = ctx + ex.index = idx + return nil +} + +// GetExtensions returns all ExternalDoc extensions and satisfies the low.HasExtensions interface. +func (ex *ExternalDoc) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return ex.Extensions +} + +func (ex *ExternalDoc) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if ex.Description.Value != "" { + h.WriteString(ex.Description.Value) + h.WriteByte(low.HASH_PIPE) + } + if ex.URL.Value != "" { + h.WriteString(ex.URL.Value) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(ex.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} + +// GetIndex returns the index.SpecIndex instance attached to the ExternalDoc object +func (ex *ExternalDoc) GetIndex() *index.SpecIndex { + return ex.index +} + +// GetContext returns the context.Context instance used when building the ExternalDoc object +func (ex *ExternalDoc) GetContext() context.Context { + return ex.context +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/base/info.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/info.go new file mode 100644 index 000000000..4de8cd4e2 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/info.go @@ -0,0 +1,131 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "context" + "hash/maphash" + + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "go.yaml.in/yaml/v4" +) + +// Info represents a low-level Info object as defined by both OpenAPI 2 and OpenAPI 3. +// +// The object provides metadata about the API. The metadata MAY be used by the clients if needed, and MAY be presented +// in editing or documentation generation tools for convenience. +// +// v2 - https://swagger.io/specification/v2/#infoObject +// v3 - https://spec.openapis.org/oas/v3.1.0#info-object +type Info struct { + Title low.NodeReference[string] + Summary low.NodeReference[string] + Description low.NodeReference[string] + TermsOfService low.NodeReference[string] + Contact low.NodeReference[*Contact] + License low.NodeReference[*License] + Version low.NodeReference[string] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// FindExtension attempts to locate an extension with the supplied key +func (i *Info) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, i.Extensions) +} + +// GetRootNode will return the root yaml node of the Info object +func (i *Info) GetRootNode() *yaml.Node { + return i.RootNode +} + +// GetKeyNode will return the key yaml node of the Info object +func (i *Info) GetKeyNode() *yaml.Node { + return i.KeyNode +} + +// GetExtensions returns all extensions for Info +func (i *Info) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return i.Extensions +} + +// Build will extract out the Contact and Info objects from the supplied root node. +func (i *Info) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + i.KeyNode = keyNode + root = utils.NodeAlias(root) + i.RootNode = root + utils.CheckForMergeNodes(root) + i.Reference = new(low.Reference) + i.Nodes = low.ExtractNodes(ctx, root) + i.Extensions = low.ExtractExtensions(root) + i.index = idx + i.context = ctx + + // extract contact + contact, _ := low.ExtractObject[*Contact](ctx, ContactLabel, root, idx) + i.Contact = contact + + // extract license + lic, _ := low.ExtractObject[*License](ctx, LicenseLabel, root, idx) + i.License = lic + return nil +} + +// GetIndex will return the index.SpecIndex instance attached to the Info object +func (i *Info) GetIndex() *index.SpecIndex { + return i.index +} + +// GetContext will return the context.Context instance used when building the Info object +func (i *Info) GetContext() context.Context { + return i.context +} + +// Hash will return a consistent hash of the Info object +func (i *Info) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if !i.Title.IsEmpty() { + h.WriteString(i.Title.Value) + h.WriteByte(low.HASH_PIPE) + } + if !i.Summary.IsEmpty() { + h.WriteString(i.Summary.Value) + h.WriteByte(low.HASH_PIPE) + } + if !i.Description.IsEmpty() { + h.WriteString(i.Description.Value) + h.WriteByte(low.HASH_PIPE) + } + if !i.TermsOfService.IsEmpty() { + h.WriteString(i.TermsOfService.Value) + h.WriteByte(low.HASH_PIPE) + } + if !i.Contact.IsEmpty() { + h.WriteString(low.GenerateHashString(i.Contact.Value)) + h.WriteByte(low.HASH_PIPE) + } + if !i.License.IsEmpty() { + h.WriteString(low.GenerateHashString(i.License.Value)) + h.WriteByte(low.HASH_PIPE) + } + if !i.Version.IsEmpty() { + h.WriteString(i.Version.Value) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(i.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/base/license.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/license.go new file mode 100644 index 000000000..887dea59d --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/license.go @@ -0,0 +1,92 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "context" + "hash/maphash" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// License is a low-level representation of a License object as defined by OpenAPI 2 and OpenAPI 3 +// +// v2 - https://swagger.io/specification/v2/#licenseObject +// v3 - https://spec.openapis.org/oas/v3.1.0#license-object +type License struct { + Name low.NodeReference[string] + URL low.NodeReference[string] + Identifier low.NodeReference[string] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// Build out a license, complain if both a URL and identifier are present as they are mutually exclusive +func (l *License) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + l.KeyNode = keyNode + root = utils.NodeAlias(root) + l.RootNode = root + utils.CheckForMergeNodes(root) + l.Reference = new(low.Reference) + no := low.ExtractNodes(ctx, root) + l.Extensions = low.ExtractExtensions(root) + l.Nodes = no + l.context = ctx + l.index = idx + return nil +} + +// GetIndex will return the index.SpecIndex instance attached to the License object +func (l *License) GetIndex() *index.SpecIndex { + return l.index +} + +// GetContext will return the context.Context instance used when building the License object +func (l *License) GetContext() context.Context { + return l.context +} + +// GetRootNode will return the root yaml node of the License object +func (l *License) GetRootNode() *yaml.Node { + return l.RootNode +} + +// GetKeyNode will return the key yaml node of the License object +func (l *License) GetKeyNode() *yaml.Node { + return l.KeyNode +} + +// Hash will return a consistent hash of the License object +func (l *License) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if !l.Name.IsEmpty() { + h.WriteString(l.Name.Value) + h.WriteByte(low.HASH_PIPE) + } + if !l.URL.IsEmpty() { + h.WriteString(l.URL.Value) + h.WriteByte(low.HASH_PIPE) + } + if !l.Identifier.IsEmpty() { + h.WriteString(l.Identifier.Value) + h.WriteByte(low.HASH_PIPE) + } + // Note: Extensions are not included in the hash for License + return h.Sum64() + }) +} + +// GetExtensions returns all extensions for License +func (l *License) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return l.Extensions +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/base/property_merger.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/property_merger.go new file mode 100644 index 000000000..1ec44a708 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/property_merger.go @@ -0,0 +1,151 @@ +// Copyright 2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "fmt" + + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// PropertyMerger handles merging of local properties with referenced schema properties +type PropertyMerger struct { + strategy datamodel.PropertyMergeStrategy +} + +// NewPropertyMerger creates a new property merger with the specified strategy +func NewPropertyMerger(strategy datamodel.PropertyMergeStrategy) *PropertyMerger { + return &PropertyMerger{ + strategy: strategy, + } +} + +// MergeProperties merges local properties with referenced schema properties based on strategy +// localNode contains properties that should be preserved (e.g., examples, descriptions) +// referencedNode contains the resolved reference content +func (pm *PropertyMerger) MergeProperties(localNode, referencedNode *yaml.Node) (*yaml.Node, error) { + if localNode == nil && referencedNode == nil { + return nil, nil + } + if localNode == nil { + return pm.copyNode(referencedNode), nil + } + if referencedNode == nil { + return pm.copyNode(localNode), nil + } + + // extract properties from both nodes + localProps := pm.extractProperties(localNode) + referencedProps := pm.extractProperties(referencedNode) + + // create merged node starting with referenced content + merged := pm.copyNode(referencedNode) + mergedProps := pm.extractProperties(merged) + + // apply merge strategy for each local property + for key, localValue := range localProps { + if _, exists := referencedProps[key]; exists { + // property exists in both - apply strategy + switch pm.strategy { + case datamodel.PreserveLocal: + mergedProps[key] = localValue + case datamodel.OverwriteWithRemote: + // keep referenced value (already in merged) + continue + case datamodel.RejectConflicts: + return nil, fmt.Errorf("property conflict: '%s' exists in both local and referenced schema", key) + } + } else { + // property only exists locally - always preserve + mergedProps[key] = localValue + } + } + + // rebuild the merged node content + return pm.rebuildNodeFromProperties(merged, mergedProps), nil +} + +// extractProperties extracts key-value pairs from a yaml mapping node +func (pm *PropertyMerger) extractProperties(node *yaml.Node) map[string]*yaml.Node { + props := make(map[string]*yaml.Node) + if !utils.IsNodeMap(node) { + return props + } + + for i := 0; i < len(node.Content); i += 2 { + if i+1 < len(node.Content) { + key := node.Content[i].Value + value := node.Content[i+1] + props[key] = value + } + } + return props +} + +// rebuildNodeFromProperties reconstructs a yaml mapping node from property map +func (pm *PropertyMerger) rebuildNodeFromProperties(baseNode *yaml.Node, props map[string]*yaml.Node) *yaml.Node { + result := &yaml.Node{ + Kind: yaml.MappingNode, + Style: baseNode.Style, + Tag: baseNode.Tag, + Line: baseNode.Line, + Column: baseNode.Column, + HeadComment: baseNode.HeadComment, + LineComment: baseNode.LineComment, + FootComment: baseNode.FootComment, + } + + // rebuild content from properties + for key, value := range props { + keyNode := &yaml.Node{Kind: yaml.ScalarNode, Value: key} + result.Content = append(result.Content, keyNode, pm.copyNode(value)) + } + + return result +} + +// copyNode creates a deep copy of a yaml node +func (pm *PropertyMerger) copyNode(node *yaml.Node) *yaml.Node { + if node == nil { + return nil + } + + copied := &yaml.Node{ + Kind: node.Kind, + Style: node.Style, + Tag: node.Tag, + Value: node.Value, + Anchor: node.Anchor, + Alias: node.Alias, + Line: node.Line, + Column: node.Column, + HeadComment: node.HeadComment, + LineComment: node.LineComment, + FootComment: node.FootComment, + } + + if node.Content != nil { + copied.Content = make([]*yaml.Node, len(node.Content)) + for i, child := range node.Content { + copied.Content[i] = pm.copyNode(child) + } + } + + return copied +} + +// ShouldMergeProperties determines if property merging should be applied based on configuration +func (pm *PropertyMerger) ShouldMergeProperties(localNode, referencedNode *yaml.Node, config *datamodel.DocumentConfiguration) bool { + if config == nil || !config.MergeReferencedProperties { + return false + } + + // only merge if both nodes have properties to merge + localProps := pm.extractProperties(localNode) + referencedProps := pm.extractProperties(referencedNode) + + return len(localProps) > 0 && len(referencedProps) > 0 +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/base/schema.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/schema.go new file mode 100644 index 000000000..aac0204c2 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/schema.go @@ -0,0 +1,1711 @@ +package base + +import ( + "context" + "errors" + "fmt" + "hash/maphash" + "sort" + "strconv" + "sync" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// SchemaDynamicValue is used to hold multiple possible types for a schema property. There are two values, a left +// value (A) and a right value (B). The A and B values represent different types that a property can have, +// not necessarily different OpenAPI versions. +// +// For example: +// - additionalProperties: A = *SchemaProxy (when it's a schema), B = bool (when it's a boolean) +// - items: A = *SchemaProxy (when it's a schema), B = bool (when it's a boolean in 3.1) +// - type: A = string (single type), B = []ValueReference[string] (multiple types in 3.1) +// - exclusiveMinimum: A = bool (in 3.0), B = float64 (in 3.1) +// +// The N value indicates which value is set (0 = A, 1 = B), preventing the need to check both values. +type SchemaDynamicValue[A any, B any] struct { + N int // 0 == A, 1 == B + A A + B B +} + +// IsA will return true if the 'A' or left value is set. +func (s *SchemaDynamicValue[A, B]) IsA() bool { + return s.N == 0 +} + +// IsB will return true if the 'B' or right value is set. +func (s *SchemaDynamicValue[A, B]) IsB() bool { + return s.N == 1 +} + +// Hash will generate a stable hash of the SchemaDynamicValue +func (s *SchemaDynamicValue[A, B]) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if s.IsA() { + h.WriteString(low.GenerateHashString(s.A)) + } else { + h.WriteString(low.GenerateHashString(s.B)) + } + return h.Sum64() + }) +} + +// Schema represents a JSON Schema that support Swagger, OpenAPI 3 and OpenAPI 3.1 +// +// Until 3.1 OpenAPI had a strange relationship with JSON Schema. It's been a super-set/sub-set +// mix, which has been confusing. So, instead of building a bunch of different models, we have compressed +// all variations into a single model that makes it easy to support multiple spec types. +// +// - v2 schema: https://swagger.io/specification/v2/#schemaObject +// - v3 schema: https://swagger.io/specification/#schema-object +// - v3.1 schema: https://spec.openapis.org/oas/v3.1.0#schema-object +type Schema struct { + // Reference to the '$schema' dialect setting (3.1 only) + SchemaTypeRef low.NodeReference[string] + + // In versions 2 and 3.0, this ExclusiveMaximum can only be a boolean. + ExclusiveMaximum low.NodeReference[*SchemaDynamicValue[bool, float64]] + + // In versions 2 and 3.0, this ExclusiveMinimum can only be a boolean. + ExclusiveMinimum low.NodeReference[*SchemaDynamicValue[bool, float64]] + + // In versions 2 and 3.0, this Type is a single value, so array will only ever have one value + // in version 3.1, Type can be multiple values + Type low.NodeReference[SchemaDynamicValue[string, []low.ValueReference[string]]] + + // Schemas are resolved on demand using a SchemaProxy + AllOf low.NodeReference[[]low.ValueReference[*SchemaProxy]] + + // Polymorphic Schemas are only available in version 3+ + OneOf low.NodeReference[[]low.ValueReference[*SchemaProxy]] + AnyOf low.NodeReference[[]low.ValueReference[*SchemaProxy]] + Discriminator low.NodeReference[*Discriminator] + + // in 3.1 examples can be an array (which is recommended) + Examples low.NodeReference[[]low.ValueReference[*yaml.Node]] + // in 3.1 PrefixItems provides tuple validation using prefixItems. + PrefixItems low.NodeReference[[]low.ValueReference[*SchemaProxy]] + // in 3.1 Contains is used by arrays and points to a Schema. + Contains low.NodeReference[*SchemaProxy] + MinContains low.NodeReference[int64] + MaxContains low.NodeReference[int64] + + // items can be a schema in 2.0, 3.0 and 3.1 or a bool in 3.1 + Items low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]] + + // 3.1 only + If low.NodeReference[*SchemaProxy] + Else low.NodeReference[*SchemaProxy] + Then low.NodeReference[*SchemaProxy] + DependentSchemas low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*SchemaProxy]]] + DependentRequired low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[[]string]]] + + PatternProperties low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*SchemaProxy]]] + PropertyNames low.NodeReference[*SchemaProxy] + UnevaluatedItems low.NodeReference[*SchemaProxy] + UnevaluatedProperties low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]] + Id low.NodeReference[string] // JSON Schema 2020-12 $id - schema resource identifier + Anchor low.NodeReference[string] + DynamicAnchor low.NodeReference[string] + DynamicRef low.NodeReference[string] + + // Compatible with all versions + Title low.NodeReference[string] + MultipleOf low.NodeReference[float64] + Maximum low.NodeReference[float64] + Minimum low.NodeReference[float64] + MaxLength low.NodeReference[int64] + MinLength low.NodeReference[int64] + Pattern low.NodeReference[string] + Format low.NodeReference[string] + MaxItems low.NodeReference[int64] + MinItems low.NodeReference[int64] + UniqueItems low.NodeReference[bool] + MaxProperties low.NodeReference[int64] + MinProperties low.NodeReference[int64] + Required low.NodeReference[[]low.ValueReference[string]] + Enum low.NodeReference[[]low.ValueReference[*yaml.Node]] + Not low.NodeReference[*SchemaProxy] + Properties low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*SchemaProxy]]] + AdditionalProperties low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]] + Description low.NodeReference[string] + ContentEncoding low.NodeReference[string] + ContentMediaType low.NodeReference[string] + ContentSchema low.NodeReference[*SchemaProxy] // JSON Schema 2020-12 contentSchema + Comment low.NodeReference[string] // JSON Schema 2020-12 $comment + Vocabulary low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[bool]]] // JSON Schema 2020-12 $vocabulary + Default low.NodeReference[*yaml.Node] + Const low.NodeReference[*yaml.Node] + Nullable low.NodeReference[bool] + ReadOnly low.NodeReference[bool] + WriteOnly low.NodeReference[bool] + XML low.NodeReference[*XML] + ExternalDocs low.NodeReference[*ExternalDoc] + Example low.NodeReference[*yaml.Node] + Deprecated low.NodeReference[bool] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + + // Parent Proxy refers back to the low level SchemaProxy that is proxying this schema. + ParentProxy *SchemaProxy + + // Index is a reference to the SpecIndex that was used to build this schema. + Index *index.SpecIndex + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + hashed uint64 // quick hash of the schema, used for quick equality checking + hashLock sync.Mutex // lock to prevent concurrent hashing of the same schema + *low.Reference + low.NodeMap +} + +// GetIndex will return the index.SpecIndex instance attached to the Schema object +func (s *Schema) GetIndex() *index.SpecIndex { + return s.index +} + +// GetContext will return the context.Context instance used when building the Schema object +func (s *Schema) GetContext() context.Context { + return s.context +} + +// QuickHash will calculate a hash from the values of the schema, however the hash is not very deep +// and is used for quick equality checking, This method exists because a full hash could end up churning through +// thousands of polymorphic references. With a quick hash, polymorphic properties are not included. +func (s *Schema) QuickHash() uint64 { + return s.hash(true) +} + +// Hash will calculate a hash from the values of the schema, This allows equality checking against +// Schemas defined inside an OpenAPI document. The only way to know if a schema has changed, is to hash it. +func (s *Schema) Hash() uint64 { + return s.hash(false) +} + +// SchemaQuickHashMap is a sync.Map used to store quick hashes of schemas, used by quick hashing to prevent +// over rotation on the same schema. This map is automatically reset each time `CompareDocuments` is called by the +// `what-changed` package and each time a model is built via `BuildV3Model()` etc. +// +// This exists because to ensure deep equality checking when composing schemas using references. However this +// can cause an exhaustive deep hash calculation that chews up compute like crazy, particularly with polymorphic refs. +// The hash map means each schema is hashed once, and then the hash is reused for quick equality checking. +var SchemaQuickHashMap sync.Map + +func (s *Schema) hash(quick bool) uint64 { + if s == nil { + return 0 + } + + // create a key for the schema, this is used to quickly check if the schema has been hashed before, and prevent re-hashing. + idx := s.GetIndex() + path := "" + if idx != nil { + path = idx.GetSpecAbsolutePath() + } + cfId := "root" + if s.Index != nil { + if s.Index.GetRolodex() != nil { + if s.Index.GetRolodex().GetId() != "" { + cfId = s.Index.GetRolodex().GetId() + } + } else { + cfId = s.Index.GetConfig().GetId() + } + } + key := fmt.Sprintf("%s:%d:%d:%s", path, s.RootNode.Line, s.RootNode.Column, cfId) + if quick { + if v, ok := SchemaQuickHashMap.Load(key); ok { + if r, k := v.(uint64); k { + return r + } + } + } + + // Use string builder pool for efficient string concatenation + sb := low.GetStringBuilder() + defer low.PutStringBuilder(sb) + + // calculate a hash from every property in the schema. + if !s.SchemaTypeRef.IsEmpty() { + sb.WriteString(s.SchemaTypeRef.Value) + sb.WriteByte('|') + } + if !s.Title.IsEmpty() { + sb.WriteString(s.Title.Value) + sb.WriteByte('|') + } + if !s.MultipleOf.IsEmpty() { + sb.WriteString(fmt.Sprint(s.MultipleOf.Value)) + sb.WriteByte('|') + } + if !s.Maximum.IsEmpty() { + sb.WriteString(fmt.Sprint(s.Maximum.Value)) + sb.WriteByte('|') + } + if !s.Minimum.IsEmpty() { + sb.WriteString(fmt.Sprint(s.Minimum.Value)) + sb.WriteByte('|') + } + if !s.MaxLength.IsEmpty() { + sb.WriteString(fmt.Sprint(s.MaxLength.Value)) + sb.WriteByte('|') + } + if !s.MinLength.IsEmpty() { + sb.WriteString(fmt.Sprint(s.MinLength.Value)) + sb.WriteByte('|') + } + if !s.Pattern.IsEmpty() { + sb.WriteString(s.Pattern.Value) + sb.WriteByte('|') + } + if !s.Format.IsEmpty() { + sb.WriteString(s.Format.Value) + sb.WriteByte('|') + } + if !s.MaxItems.IsEmpty() { + sb.WriteString(fmt.Sprint(s.MaxItems.Value)) + sb.WriteByte('|') + } + if !s.MinItems.IsEmpty() { + sb.WriteString(fmt.Sprint(s.MinItems.Value)) + sb.WriteByte('|') + } + if !s.UniqueItems.IsEmpty() { + sb.WriteString(fmt.Sprint(s.UniqueItems.Value)) + sb.WriteByte('|') + } + if !s.MaxProperties.IsEmpty() { + sb.WriteString(fmt.Sprint(s.MaxProperties.Value)) + sb.WriteByte('|') + } + if !s.MinProperties.IsEmpty() { + sb.WriteString(fmt.Sprint(s.MinProperties.Value)) + sb.WriteByte('|') + } + if !s.AdditionalProperties.IsEmpty() { + sb.WriteString(low.GenerateHashString(s.AdditionalProperties.Value)) + sb.WriteByte('|') + } + if !s.Description.IsEmpty() { + sb.WriteString(s.Description.Value) + sb.WriteByte('|') + } + if !s.ContentEncoding.IsEmpty() { + sb.WriteString(s.ContentEncoding.Value) + sb.WriteByte('|') + } + if !s.ContentMediaType.IsEmpty() { + sb.WriteString(s.ContentMediaType.Value) + sb.WriteByte('|') + } + if !s.Default.IsEmpty() { + sb.WriteString(low.GenerateHashString(s.Default.Value)) + sb.WriteByte('|') + } + if !s.Const.IsEmpty() { + sb.WriteString(low.GenerateHashString(s.Const.Value)) + sb.WriteByte('|') + } + if !s.Nullable.IsEmpty() { + sb.WriteString(fmt.Sprint(s.Nullable.Value)) + sb.WriteByte('|') + } + if !s.ReadOnly.IsEmpty() { + sb.WriteString(fmt.Sprint(s.ReadOnly.Value)) + sb.WriteByte('|') + } + if !s.WriteOnly.IsEmpty() { + sb.WriteString(fmt.Sprint(s.WriteOnly.Value)) + sb.WriteByte('|') + } + if !s.Deprecated.IsEmpty() { + sb.WriteString(fmt.Sprint(s.Deprecated.Value)) + sb.WriteByte('|') + } + if !s.ExclusiveMaximum.IsEmpty() && s.ExclusiveMaximum.Value.IsA() { + sb.WriteString(fmt.Sprint(s.ExclusiveMaximum.Value.A)) + sb.WriteByte('|') + } + if !s.ExclusiveMaximum.IsEmpty() && s.ExclusiveMaximum.Value.IsB() { + sb.WriteString(fmt.Sprint(s.ExclusiveMaximum.Value.B)) + sb.WriteByte('|') + } + if !s.ExclusiveMinimum.IsEmpty() && s.ExclusiveMinimum.Value.IsA() { + sb.WriteString(fmt.Sprint(s.ExclusiveMinimum.Value.A)) + sb.WriteByte('|') + } + if !s.ExclusiveMinimum.IsEmpty() && s.ExclusiveMinimum.Value.IsB() { + sb.WriteString(fmt.Sprint(s.ExclusiveMinimum.Value.B)) + sb.WriteByte('|') + } + if !s.Type.IsEmpty() && s.Type.Value.IsA() { + sb.WriteString(s.Type.Value.A) + sb.WriteByte('|') + } + if !s.Type.IsEmpty() && s.Type.Value.IsB() { + // Pre-allocate slice for Type.B values + j := make([]string, len(s.Type.Value.B)) + for h := range s.Type.Value.B { + j[h] = s.Type.Value.B[h].Value + } + sort.Strings(j) + for _, val := range j { + sb.WriteString(val) + } + sb.WriteByte('|') + } + + // Process Required values + if len(s.Required.Value) > 0 { + keys := make([]string, len(s.Required.Value)) + for i := range s.Required.Value { + keys[i] = s.Required.Value[i].Value + } + sort.Strings(keys) + for _, key := range keys { + sb.WriteString(key) + sb.WriteByte('|') + } + } + + // Process Enum values + if len(s.Enum.Value) > 0 { + keys := make([]string, len(s.Enum.Value)) + for i := range s.Enum.Value { + keys[i] = low.ValueToString(s.Enum.Value[i].Value) + } + sort.Strings(keys) + for _, key := range keys { + sb.WriteString(key) + sb.WriteByte('|') + } + } + + // Append map hashes using helper function + for _, hash := range low.AppendMapHashes(nil, s.Properties.Value) { + sb.WriteString(hash) + sb.WriteByte('|') + } + + if s.XML.Value != nil { + sb.WriteString(low.GenerateHashString(s.XML.Value)) + sb.WriteByte('|') + } + if s.ExternalDocs.Value != nil { + sb.WriteString(low.GenerateHashString(s.ExternalDocs.Value)) + sb.WriteByte('|') + } + if s.Discriminator.Value != nil { + sb.WriteString(low.GenerateHashString(s.Discriminator.Value)) + sb.WriteByte('|') + } + + // hash polymorphic data - OneOf + if len(s.OneOf.Value) > 0 { + oneOfKeys := make([]string, len(s.OneOf.Value)) + oneOfEntities := make(map[string]*SchemaProxy, len(s.OneOf.Value)) + for i := range s.OneOf.Value { + g := s.OneOf.Value[i].Value + r := low.GenerateHashString(g) + oneOfEntities[r] = g + oneOfKeys[i] = r + } + sort.Strings(oneOfKeys) + for _, key := range oneOfKeys { + sb.WriteString(low.GenerateHashString(oneOfEntities[key])) + sb.WriteByte('|') + } + } + + // hash polymorphic data - AllOf + if len(s.AllOf.Value) > 0 { + allOfKeys := make([]string, len(s.AllOf.Value)) + allOfEntities := make(map[string]*SchemaProxy, len(s.AllOf.Value)) + for i := range s.AllOf.Value { + g := s.AllOf.Value[i].Value + r := low.GenerateHashString(g) + allOfEntities[r] = g + allOfKeys[i] = r + } + sort.Strings(allOfKeys) + for _, key := range allOfKeys { + sb.WriteString(low.GenerateHashString(allOfEntities[key])) + sb.WriteByte('|') + } + } + + // hash polymorphic data - AnyOf + if len(s.AnyOf.Value) > 0 { + anyOfKeys := make([]string, len(s.AnyOf.Value)) + anyOfEntities := make(map[string]*SchemaProxy, len(s.AnyOf.Value)) + for i := range s.AnyOf.Value { + g := s.AnyOf.Value[i].Value + r := low.GenerateHashString(g) + anyOfEntities[r] = g + anyOfKeys[i] = r + } + sort.Strings(anyOfKeys) + for _, key := range anyOfKeys { + sb.WriteString(low.GenerateHashString(anyOfEntities[key])) + sb.WriteByte('|') + } + } + + if !s.Not.IsEmpty() { + sb.WriteString(low.GenerateHashString(s.Not.Value)) + sb.WriteByte('|') + } + + // check if items is a schema or a bool. + if !s.Items.IsEmpty() && s.Items.Value.IsA() { + sb.WriteString(low.GenerateHashString(s.Items.Value.A)) + sb.WriteByte('|') + } + if !s.Items.IsEmpty() && s.Items.Value.IsB() { + sb.WriteString(fmt.Sprint(s.Items.Value.B)) + sb.WriteByte('|') + } + // 3.1 only props + if !s.If.IsEmpty() { + sb.WriteString(low.GenerateHashString(s.If.Value)) + sb.WriteByte('|') + } + if !s.Else.IsEmpty() { + sb.WriteString(low.GenerateHashString(s.Else.Value)) + sb.WriteByte('|') + } + if !s.Then.IsEmpty() { + sb.WriteString(low.GenerateHashString(s.Then.Value)) + sb.WriteByte('|') + } + if !s.PropertyNames.IsEmpty() { + sb.WriteString(low.GenerateHashString(s.PropertyNames.Value)) + sb.WriteByte('|') + } + if !s.UnevaluatedProperties.IsEmpty() { + sb.WriteString(low.GenerateHashString(s.UnevaluatedProperties.Value)) + sb.WriteByte('|') + } + if !s.UnevaluatedItems.IsEmpty() { + sb.WriteString(low.GenerateHashString(s.UnevaluatedItems.Value)) + sb.WriteByte('|') + } + if !s.Id.IsEmpty() { + sb.WriteString(s.Id.Value) + sb.WriteByte('|') + } + if !s.Anchor.IsEmpty() { + sb.WriteString(s.Anchor.Value) + sb.WriteByte('|') + } + if !s.DynamicAnchor.IsEmpty() { + sb.WriteString(s.DynamicAnchor.Value) + sb.WriteByte('|') + } + if !s.DynamicRef.IsEmpty() { + sb.WriteString(s.DynamicRef.Value) + sb.WriteByte('|') + } + if !s.Comment.IsEmpty() { + sb.WriteString(s.Comment.Value) + sb.WriteByte('|') + } + if !s.ContentSchema.IsEmpty() { + sb.WriteString(low.GenerateHashString(s.ContentSchema.Value)) + sb.WriteByte('|') + } + if s.Vocabulary.Value != nil { + // sort vocabulary keys for deterministic hashing + // pre-allocate with known size for better memory efficiency + vocabSize := orderedmap.Len(s.Vocabulary.Value) + vocabKeys := make([]string, 0, vocabSize) + vocabMap := make(map[string]bool, vocabSize) + for k, v := range s.Vocabulary.Value.FromOldest() { + vocabKeys = append(vocabKeys, k.Value) + vocabMap[k.Value] = v.Value + } + sort.Strings(vocabKeys) + for _, k := range vocabKeys { + sb.WriteString(k) + sb.WriteByte(':') + sb.WriteString(fmt.Sprint(vocabMap[k])) + sb.WriteByte('|') + } + } + + // Process dependent schemas and pattern properties + for _, hash := range low.AppendMapHashes(nil, orderedmap.SortAlpha(s.DependentSchemas.Value)) { + sb.WriteString(hash) + sb.WriteByte('|') + } + + // Process dependent required + if s.DependentRequired.Value != nil { + // Sort keys for deterministic hashing + var depReqKeys []string + depReqMap := make(map[string][]string) + for prop, requiredProps := range s.DependentRequired.Value.FromOldest() { + depReqKeys = append(depReqKeys, prop.Value) + depReqMap[prop.Value] = requiredProps.Value + } + sort.Strings(depReqKeys) + + for _, prop := range depReqKeys { + sb.WriteString(prop) + sb.WriteByte(':') + requiredProps := depReqMap[prop] + for i, reqProp := range requiredProps { + sb.WriteString(reqProp) + if i < len(requiredProps)-1 { + sb.WriteByte(',') + } + } + sb.WriteByte('|') + } + } + + for _, hash := range low.AppendMapHashes(nil, orderedmap.SortAlpha(s.PatternProperties.Value)) { + sb.WriteString(hash) + sb.WriteByte('|') + } + + // Process PrefixItems + if len(s.PrefixItems.Value) > 0 { + itemsKeys := make([]string, len(s.PrefixItems.Value)) + itemsEntities := make(map[string]*SchemaProxy, len(s.PrefixItems.Value)) + for i := range s.PrefixItems.Value { + g := s.PrefixItems.Value[i].Value + r := low.GenerateHashString(g) + itemsEntities[r] = g + itemsKeys[i] = r + } + sort.Strings(itemsKeys) + for _, key := range itemsKeys { + sb.WriteString(low.GenerateHashString(itemsEntities[key])) + sb.WriteByte('|') + } + } + + // Process extensions + for _, ext := range low.HashExtensions(s.Extensions) { + sb.WriteString(ext) + sb.WriteByte('|') + } + + if s.Example.Value != nil { + sb.WriteString(low.GenerateHashString(s.Example.Value)) + sb.WriteByte('|') + } + + // contains + if !s.Contains.IsEmpty() { + sb.WriteString(low.GenerateHashString(s.Contains.Value)) + sb.WriteByte('|') + } + if !s.MinContains.IsEmpty() { + sb.WriteString(fmt.Sprint(s.MinContains.Value)) + sb.WriteByte('|') + } + if !s.MaxContains.IsEmpty() { + sb.WriteString(fmt.Sprint(s.MaxContains.Value)) + sb.WriteByte('|') + } + if !s.Examples.IsEmpty() { + for _, ex := range s.Examples.Value { + sb.WriteString(low.GenerateHashString(ex.Value)) + sb.WriteByte('|') + } + } + + h := low.WithHasher(func(hasher *maphash.Hash) uint64 { + hasher.WriteString(sb.String()) + return hasher.Sum64() + }) + SchemaQuickHashMap.Store(key, h) + return h +} + +// FindProperty will return a ValueReference pointer containing a SchemaProxy pointer +// from a property key name. if found +func (s *Schema) FindProperty(name string) *low.ValueReference[*SchemaProxy] { + return low.FindItemInOrderedMap[*SchemaProxy](name, s.Properties.Value) +} + +// FindDependentSchema will return a ValueReference pointer containing a SchemaProxy pointer +// from a dependent schema key name. if found (3.1+ only) +func (s *Schema) FindDependentSchema(name string) *low.ValueReference[*SchemaProxy] { + return low.FindItemInOrderedMap[*SchemaProxy](name, s.DependentSchemas.Value) +} + +// FindPatternProperty will return a ValueReference pointer containing a SchemaProxy pointer +// from a pattern property key name. if found (3.1+ only) +func (s *Schema) FindPatternProperty(name string) *low.ValueReference[*SchemaProxy] { + return low.FindItemInOrderedMap[*SchemaProxy](name, s.PatternProperties.Value) +} + +// GetExtensions returns all extensions for Schema +func (s *Schema) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return s.Extensions +} + +// GetRootNode will return the root yaml node of the Schema object +func (s *Schema) GetRootNode() *yaml.Node { + return s.RootNode +} + +// Build will perform a number of operations. +// Extraction of the following happens in this method: +// - Extensions +// - Type +// - ExclusiveMinimum and ExclusiveMaximum +// - Examples +// - AdditionalProperties +// - Discriminator +// - ExternalDocs +// - XML +// - Properties +// - AllOf, OneOf, AnyOf +// - Not +// - Items +// - PrefixItems +// - If +// - Else +// - Then +// - DependentSchemas +// - PatternProperties +// - PropertyNames +// - UnevaluatedItems +// - UnevaluatedProperties +// - Anchor +func (s *Schema) Build(ctx context.Context, root *yaml.Node, idx *index.SpecIndex) error { + if root == nil { + return fmt.Errorf("cannot build schema from a nil node") + } + root = utils.NodeAlias(root) + utils.CheckForMergeNodes(root) + + // Note: sibling ref transformation now happens in SchemaProxy.Build() + // so the root node should already be pre-transformed if needed + + s.Reference = new(low.Reference) + no := low.ExtractNodes(ctx, root) + s.Nodes = no + s.Index = idx + s.RootNode = root + s.context = ctx + s.index = idx + + // check if this schema was transformed from a sibling ref + // if so, skip reference dereferencing to preserve the allOf structure + isTransformed := false + if s.ParentProxy != nil && s.ParentProxy.TransformedRef != nil { + isTransformed = true + } + + if !isTransformed { + if h, _, _ := utils.IsNodeRefValue(root); h { + ref, _, err, fctx := low.LocateRefNodeWithContext(ctx, root, idx) + if ref != nil { + root = ref + if fctx != nil { + ctx = fctx + } + if err != nil { + if !idx.AllowCircularReferenceResolving() { + return fmt.Errorf("build schema failed: %s", err.Error()) + } + } + } else { + return fmt.Errorf("build schema failed: reference cannot be found: '%s', line %d, col %d", + root.Content[1].Value, root.Content[1].Line, root.Content[1].Column) + } + } + } + + // Build model using possibly dereferenced root + if err := low.BuildModel(root, s); err != nil { + return err + } + + s.extractExtensions(root) + + // if the schema has required values, extract the nodes for them. + if s.Required.Value != nil { + for _, r := range s.Required.Value { + s.AddNode(r.ValueNode.Line, r.ValueNode) + } + } + + // same thing with enums + if s.Enum.Value != nil { + for _, e := range s.Enum.Value { + s.AddNode(e.ValueNode.Line, e.ValueNode) + } + } + + // determine schema type, singular (3.0) or multiple (3.1), use a variable value + _, typeLabel, typeValue := utils.FindKeyNodeFullTop(TypeLabel, root.Content) + if typeValue != nil { + if utils.IsNodeStringValue(typeValue) { + s.Type = low.NodeReference[SchemaDynamicValue[string, []low.ValueReference[string]]]{ + KeyNode: typeLabel, + ValueNode: typeValue, + Value: SchemaDynamicValue[string, []low.ValueReference[string]]{N: 0, A: typeValue.Value}, + } + } + if utils.IsNodeArray(typeValue) { + + var refs []low.ValueReference[string] + for r := range typeValue.Content { + refs = append(refs, low.ValueReference[string]{ + Value: typeValue.Content[r].Value, + ValueNode: typeValue.Content[r], + }) + } + s.Type = low.NodeReference[SchemaDynamicValue[string, []low.ValueReference[string]]]{ + KeyNode: typeLabel, + ValueNode: typeValue, + Value: SchemaDynamicValue[string, []low.ValueReference[string]]{N: 1, B: refs}, + } + } + } + + // determine exclusive minimum type, bool (3.0) or int (3.1) + _, exMinLabel, exMinValue := utils.FindKeyNodeFullTop(ExclusiveMinimumLabel, root.Content) + if exMinValue != nil { + // if there is an index, determine if this a 3.0 or 3.1+ schema + if idx != nil { + if idx.GetConfig().SpecInfo.VersionNumeric >= 3.1 { + val, _ := strconv.ParseFloat(exMinValue.Value, 64) + s.ExclusiveMinimum = low.NodeReference[*SchemaDynamicValue[bool, float64]]{ + KeyNode: exMinLabel, + ValueNode: exMinValue, + Value: &SchemaDynamicValue[bool, float64]{N: 1, B: val}, + } + } + if idx.GetConfig().SpecInfo.VersionNumeric <= 3.0 { + val, _ := strconv.ParseBool(exMinValue.Value) + s.ExclusiveMinimum = low.NodeReference[*SchemaDynamicValue[bool, float64]]{ + KeyNode: exMinLabel, + ValueNode: exMinValue, + Value: &SchemaDynamicValue[bool, float64]{N: 0, A: val}, + } + } + } else { + + // there is no index, so we have to determine the type based on the value + if utils.IsNodeBoolValue(exMinValue) { + val, _ := strconv.ParseBool(exMinValue.Value) + s.ExclusiveMinimum = low.NodeReference[*SchemaDynamicValue[bool, float64]]{ + KeyNode: exMinLabel, + ValueNode: exMinValue, + Value: &SchemaDynamicValue[bool, float64]{N: 0, A: val}, + } + } + if utils.IsNodeIntValue(exMinValue) { + val, _ := strconv.ParseFloat(exMinValue.Value, 64) + s.ExclusiveMinimum = low.NodeReference[*SchemaDynamicValue[bool, float64]]{ + KeyNode: exMinLabel, + ValueNode: exMinValue, + Value: &SchemaDynamicValue[bool, float64]{N: 1, B: val}, + } + } + } + } + + // determine exclusive maximum type, bool (3.0) or int (3.1+) + _, exMaxLabel, exMaxValue := utils.FindKeyNodeFullTop(ExclusiveMaximumLabel, root.Content) + if exMaxValue != nil { + // if there is an index, determine if this a 3.0 or 3.1+ schema + if idx != nil { + if idx.GetConfig().SpecInfo.VersionNumeric >= 3.1 { + val, _ := strconv.ParseFloat(exMaxValue.Value, 64) + s.ExclusiveMaximum = low.NodeReference[*SchemaDynamicValue[bool, float64]]{ + KeyNode: exMaxLabel, + ValueNode: exMaxValue, + Value: &SchemaDynamicValue[bool, float64]{N: 1, B: val}, + } + } + if idx.GetConfig().SpecInfo.VersionNumeric <= 3.0 { + val, _ := strconv.ParseBool(exMaxValue.Value) + s.ExclusiveMaximum = low.NodeReference[*SchemaDynamicValue[bool, float64]]{ + KeyNode: exMaxLabel, + ValueNode: exMaxValue, + Value: &SchemaDynamicValue[bool, float64]{N: 0, A: val}, + } + } + } else { + + // there is no index, so we have to determine the type based on the value + if utils.IsNodeBoolValue(exMaxValue) { + val, _ := strconv.ParseBool(exMaxValue.Value) + s.ExclusiveMaximum = low.NodeReference[*SchemaDynamicValue[bool, float64]]{ + KeyNode: exMaxLabel, + ValueNode: exMaxValue, + Value: &SchemaDynamicValue[bool, float64]{N: 0, A: val}, + } + } + if utils.IsNodeIntValue(exMaxValue) { + val, _ := strconv.ParseFloat(exMaxValue.Value, 64) + s.ExclusiveMaximum = low.NodeReference[*SchemaDynamicValue[bool, float64]]{ + KeyNode: exMaxLabel, + ValueNode: exMaxValue, + Value: &SchemaDynamicValue[bool, float64]{N: 1, B: val}, + } + } + } + } + + // handle schema reference type if set. (3.1) + _, schemaRefLabel, schemaRefNode := utils.FindKeyNodeFullTop(SchemaTypeLabel, root.Content) + if schemaRefNode != nil { + s.SchemaTypeRef = low.NodeReference[string]{ + Value: schemaRefNode.Value, KeyNode: schemaRefLabel, ValueNode: schemaRefNode, + } + } + + // handle $id if set. (3.1+, JSON Schema 2020-12) + _, idLabel, idNode := utils.FindKeyNodeFullTop(IdLabel, root.Content) + if idNode != nil { + s.Id = low.NodeReference[string]{ + Value: idNode.Value, KeyNode: idLabel, ValueNode: idNode, + } + } + + // handle anchor if set. (3.1) + _, anchorLabel, anchorNode := utils.FindKeyNodeFullTop(AnchorLabel, root.Content) + if anchorNode != nil { + s.Anchor = low.NodeReference[string]{ + Value: anchorNode.Value, KeyNode: anchorLabel, ValueNode: anchorNode, + } + } + + // handle $dynamicAnchor if set. (3.1+, JSON Schema 2020-12) + _, dynamicAnchorLabel, dynamicAnchorNode := utils.FindKeyNodeFullTop(DynamicAnchorLabel, root.Content) + if dynamicAnchorNode != nil { + s.DynamicAnchor = low.NodeReference[string]{ + Value: dynamicAnchorNode.Value, KeyNode: dynamicAnchorLabel, ValueNode: dynamicAnchorNode, + } + } + + // handle $dynamicRef if set. (3.1+, JSON Schema 2020-12) + _, dynamicRefLabel, dynamicRefNode := utils.FindKeyNodeFullTop(DynamicRefLabel, root.Content) + if dynamicRefNode != nil { + s.DynamicRef = low.NodeReference[string]{ + Value: dynamicRefNode.Value, KeyNode: dynamicRefLabel, ValueNode: dynamicRefNode, + } + } + + // handle $comment if set. (JSON Schema 2020-12) + _, commentLabel, commentNode := utils.FindKeyNodeFullTop(CommentLabel, root.Content) + if commentNode != nil { + s.Comment = low.NodeReference[string]{ + Value: commentNode.Value, KeyNode: commentLabel, ValueNode: commentNode, + } + } + + // handle $vocabulary if set. (JSON Schema 2020-12 - typically in meta-schemas) + _, vocabLabel, vocabNode := utils.FindKeyNodeFullTop(VocabularyLabel, root.Content) + if vocabNode != nil && utils.IsNodeMap(vocabNode) { + vocabularyMap := orderedmap.New[low.KeyReference[string], low.ValueReference[bool]]() + var currentKey *yaml.Node + for i, node := range vocabNode.Content { + if i%2 == 0 { + currentKey = node + continue + } + // use strconv.ParseBool for robust boolean parsing (handles "true", "false", "1", "0", etc.) + boolVal, _ := strconv.ParseBool(node.Value) + vocabularyMap.Set(low.KeyReference[string]{ + KeyNode: currentKey, + Value: currentKey.Value, + }, low.ValueReference[bool]{ + Value: boolVal, + ValueNode: node, + }) + } + s.Vocabulary = low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[bool]]]{ + Value: vocabularyMap, + KeyNode: vocabLabel, + ValueNode: vocabNode, + } + } + + // handle example if set. (3.0) + _, expLabel, expNode := utils.FindKeyNodeFullTop(ExampleLabel, root.Content) + if expNode != nil { + s.Example = low.NodeReference[*yaml.Node]{Value: expNode, KeyNode: expLabel, ValueNode: expNode} + + // extract nodes for all value nodes down the tree. + expChildNodes := low.ExtractNodesRecursive(ctx, expNode) + // map to the local schema + expChildNodes.Range(func(k, v interface{}) bool { + if arr, ko := v.([]*yaml.Node); ko { + if _, ok := s.Nodes.Load(k); !ok { + s.Nodes.Store(k, arr) + } + } + return true + }) + } + + // handle examples if set.(3.1) + _, expArrLabel, expArrNode := utils.FindKeyNodeFullTop(ExamplesLabel, root.Content) + if expArrNode != nil { + if utils.IsNodeArray(expArrNode) { + var examples []low.ValueReference[*yaml.Node] + for i := range expArrNode.Content { + examples = append(examples, low.ValueReference[*yaml.Node]{Value: expArrNode.Content[i], ValueNode: expArrNode.Content[i]}) + } + s.Examples = low.NodeReference[[]low.ValueReference[*yaml.Node]]{ + Value: examples, + ValueNode: expArrNode, + KeyNode: expArrLabel, + } + // extract nodes for all value nodes down the tree. + expChildNodes := low.ExtractNodesRecursive(ctx, expArrNode) + // map to the local schema + expChildNodes.Range(func(k, v interface{}) bool { + if arr, ko := v.([]*yaml.Node); ko { + if _, ok := s.Nodes.Load(k); !ok { + s.Nodes.Store(k, arr) + } + } + return true + }) + } + } + + // check additionalProperties type for schema or bool + addPropsIsBool := false + addPropsBoolValue := true + _, addPLabel, addPValue := utils.FindKeyNodeFullTop(AdditionalPropertiesLabel, root.Content) + if addPValue != nil { + if utils.IsNodeBoolValue(addPValue) { + addPropsIsBool = true + addPropsBoolValue, _ = strconv.ParseBool(addPValue.Value) + } + } + if addPropsIsBool { + s.AdditionalProperties = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]{ + Value: &SchemaDynamicValue[*SchemaProxy, bool]{ + B: addPropsBoolValue, + N: 1, + }, + KeyNode: addPLabel, + ValueNode: addPValue, + } + } + + // handle discriminator if set. + _, discLabel, discNode := utils.FindKeyNodeFullTop(DiscriminatorLabel, root.Content) + if discNode != nil { + var discriminator Discriminator + _ = low.BuildModel(discNode, &discriminator) + discriminator.KeyNode = discLabel + discriminator.RootNode = discNode + discriminator.Nodes = low.ExtractNodes(ctx, discNode) + s.Discriminator = low.NodeReference[*Discriminator]{Value: &discriminator, KeyNode: discLabel, ValueNode: discNode} + // add discriminator nodes, because there is no build method. + dn := low.ExtractNodesRecursive(ctx, discNode) + dn.Range(func(key, val any) bool { + if n, ok := val.([]*yaml.Node); ok { + for _, g := range n { + discriminator.AddNode(key.(int), g) + } + } + return true + }) + } + + // handle externalDocs if set. + _, extDocLabel, extDocNode := utils.FindKeyNodeFullTop(ExternalDocsLabel, root.Content) + if extDocNode != nil { + var exDoc ExternalDoc + _ = low.BuildModel(extDocNode, &exDoc) + _ = exDoc.Build(ctx, extDocLabel, extDocNode, idx) // throws no errors, can't check for one. + exDoc.Nodes = low.ExtractNodes(ctx, extDocNode) + s.ExternalDocs = low.NodeReference[*ExternalDoc]{Value: &exDoc, KeyNode: extDocLabel, ValueNode: extDocNode} + } + + // handle xml if set. + _, xmlLabel, xmlNode := utils.FindKeyNodeFullTop(XMLLabel, root.Content) + if xmlNode != nil { + var xml XML + _ = low.BuildModel(xmlNode, &xml) + // extract extensions if set. + _ = xml.Build(xmlNode, idx) // returns no errors, can't check for one. + xml.Nodes = low.ExtractNodes(ctx, xmlNode) + s.XML = low.NodeReference[*XML]{Value: &xml, KeyNode: xmlLabel, ValueNode: xmlNode} + } + + // handle properties + props, err := buildPropertyMap(ctx, s, root, idx, PropertiesLabel) + if err != nil { + return err + } + if props != nil { + s.Properties = *props + } + + // handle dependent schemas + props, err = buildPropertyMap(ctx, s, root, idx, DependentSchemasLabel) + if err != nil { + return err + } + if props != nil { + s.DependentSchemas = *props + } + + // handle dependent required + depReq, err := buildDependentRequiredMap(root, DependentRequiredLabel) + if err != nil { + return err + } + if depReq != nil { + s.DependentRequired = *depReq + } + + // handle pattern properties + props, err = buildPropertyMap(ctx, s, root, idx, PatternPropertiesLabel) + if err != nil { + return err + } + if props != nil { + s.PatternProperties = *props + } + + // check items type for schema or bool (3.1 only) + itemsIsBool := false + itemsBoolValue := false + _, itemsLabel, itemsValue := utils.FindKeyNodeFullTop(ItemsLabel, root.Content) + if itemsValue != nil { + if utils.IsNodeBoolValue(itemsValue) { + itemsIsBool = true + itemsBoolValue, _ = strconv.ParseBool(itemsValue.Value) + } + } + if itemsIsBool { + s.Items = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]{ + Value: &SchemaDynamicValue[*SchemaProxy, bool]{ + B: itemsBoolValue, + N: 1, + }, + KeyNode: itemsLabel, + ValueNode: itemsValue, + } + } + + // check unevaluatedProperties type for schema or bool (3.1 only) + unevalIsBool := false + unevalBoolValue := true + _, unevalLabel, unevalValue := utils.FindKeyNodeFullTop(UnevaluatedPropertiesLabel, root.Content) + if unevalValue != nil { + if utils.IsNodeBoolValue(unevalValue) { + unevalIsBool = true + unevalBoolValue, _ = strconv.ParseBool(unevalValue.Value) + } + } + if unevalIsBool { + s.UnevaluatedProperties = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]{ + Value: &SchemaDynamicValue[*SchemaProxy, bool]{ + B: unevalBoolValue, + N: 1, + }, + KeyNode: unevalLabel, + ValueNode: unevalValue, + } + } + + var allOf, anyOf, oneOf, prefixItems []low.ValueReference[*SchemaProxy] + var items, not, contains, sif, selse, sthen, propertyNames, unevalItems, unevalProperties, addProperties, contentSch low.ValueReference[*SchemaProxy] + + _, allOfLabel, allOfValue := utils.FindKeyNodeFullTop(AllOfLabel, root.Content) + _, anyOfLabel, anyOfValue := utils.FindKeyNodeFullTop(AnyOfLabel, root.Content) + _, oneOfLabel, oneOfValue := utils.FindKeyNodeFullTop(OneOfLabel, root.Content) + _, notLabel, notValue := utils.FindKeyNodeFullTop(NotLabel, root.Content) + _, prefixItemsLabel, prefixItemsValue := utils.FindKeyNodeFullTop(PrefixItemsLabel, root.Content) + _, containsLabel, containsValue := utils.FindKeyNodeFullTop(ContainsLabel, root.Content) + _, sifLabel, sifValue := utils.FindKeyNodeFullTop(IfLabel, root.Content) + _, selseLabel, selseValue := utils.FindKeyNodeFullTop(ElseLabel, root.Content) + _, sthenLabel, sthenValue := utils.FindKeyNodeFullTop(ThenLabel, root.Content) + _, propNamesLabel, propNamesValue := utils.FindKeyNodeFullTop(PropertyNamesLabel, root.Content) + _, unevalItemsLabel, unevalItemsValue := utils.FindKeyNodeFullTop(UnevaluatedItemsLabel, root.Content) + _, unevalPropsLabel, unevalPropsValue := utils.FindKeyNodeFullTop(UnevaluatedPropertiesLabel, root.Content) + _, addPropsLabel, addPropsValue := utils.FindKeyNodeFullTop(AdditionalPropertiesLabel, root.Content) + _, contentSchLabel, contentSchValue := utils.FindKeyNodeFullTop(ContentSchemaLabel, root.Content) + + errorChan := make(chan error) + allOfChan := make(chan schemaProxyBuildResult) + anyOfChan := make(chan schemaProxyBuildResult) + oneOfChan := make(chan schemaProxyBuildResult) + itemsChan := make(chan schemaProxyBuildResult) + prefixItemsChan := make(chan schemaProxyBuildResult) + notChan := make(chan schemaProxyBuildResult) + containsChan := make(chan schemaProxyBuildResult) + ifChan := make(chan schemaProxyBuildResult) + elseChan := make(chan schemaProxyBuildResult) + thenChan := make(chan schemaProxyBuildResult) + propNamesChan := make(chan schemaProxyBuildResult) + unevalItemsChan := make(chan schemaProxyBuildResult) + unevalPropsChan := make(chan schemaProxyBuildResult) + addPropsChan := make(chan schemaProxyBuildResult) + contentSchChan := make(chan schemaProxyBuildResult) + + totalBuilds := countSubSchemaItems(allOfValue) + + countSubSchemaItems(anyOfValue) + + countSubSchemaItems(oneOfValue) + + countSubSchemaItems(prefixItemsValue) + + if allOfValue != nil { + go buildSchema(ctx, allOfChan, allOfLabel, allOfValue, errorChan, idx) + } + if anyOfValue != nil { + go buildSchema(ctx, anyOfChan, anyOfLabel, anyOfValue, errorChan, idx) + } + if oneOfValue != nil { + go buildSchema(ctx, oneOfChan, oneOfLabel, oneOfValue, errorChan, idx) + } + if prefixItemsValue != nil { + go buildSchema(ctx, prefixItemsChan, prefixItemsLabel, prefixItemsValue, errorChan, idx) + } + if notValue != nil { + totalBuilds++ + go buildSchema(ctx, notChan, notLabel, notValue, errorChan, idx) + } + if containsValue != nil { + totalBuilds++ + go buildSchema(ctx, containsChan, containsLabel, containsValue, errorChan, idx) + } + if !itemsIsBool && itemsValue != nil { + totalBuilds++ + go buildSchema(ctx, itemsChan, itemsLabel, itemsValue, errorChan, idx) + } + if sifValue != nil { + totalBuilds++ + go buildSchema(ctx, ifChan, sifLabel, sifValue, errorChan, idx) + } + if selseValue != nil { + totalBuilds++ + go buildSchema(ctx, elseChan, selseLabel, selseValue, errorChan, idx) + } + if sthenValue != nil { + totalBuilds++ + go buildSchema(ctx, thenChan, sthenLabel, sthenValue, errorChan, idx) + } + if propNamesValue != nil { + totalBuilds++ + go buildSchema(ctx, propNamesChan, propNamesLabel, propNamesValue, errorChan, idx) + } + if unevalItemsValue != nil { + totalBuilds++ + go buildSchema(ctx, unevalItemsChan, unevalItemsLabel, unevalItemsValue, errorChan, idx) + } + if !unevalIsBool && unevalPropsValue != nil { + totalBuilds++ + go buildSchema(ctx, unevalPropsChan, unevalPropsLabel, unevalPropsValue, errorChan, idx) + } + if !addPropsIsBool && addPropsValue != nil { + totalBuilds++ + go buildSchema(ctx, addPropsChan, addPropsLabel, addPropsValue, errorChan, idx) + } + if contentSchValue != nil { + totalBuilds++ + go buildSchema(ctx, contentSchChan, contentSchLabel, contentSchValue, errorChan, idx) + } + + completeCount := 0 + for completeCount < totalBuilds { + select { + case e := <-errorChan: + return e + case r := <-allOfChan: + completeCount++ + allOf = append(allOf, r.v) + case r := <-anyOfChan: + completeCount++ + anyOf = append(anyOf, r.v) + case r := <-oneOfChan: + completeCount++ + oneOf = append(oneOf, r.v) + case r := <-itemsChan: + completeCount++ + items = r.v + case r := <-prefixItemsChan: + completeCount++ + prefixItems = append(prefixItems, r.v) + case r := <-notChan: + completeCount++ + not = r.v + case r := <-containsChan: + completeCount++ + contains = r.v + case r := <-ifChan: + completeCount++ + sif = r.v + case r := <-elseChan: + completeCount++ + selse = r.v + case r := <-thenChan: + completeCount++ + sthen = r.v + case r := <-propNamesChan: + completeCount++ + propertyNames = r.v + case r := <-unevalItemsChan: + completeCount++ + unevalItems = r.v + case r := <-unevalPropsChan: + completeCount++ + unevalProperties = r.v + case r := <-addPropsChan: + completeCount++ + addProperties = r.v + case r := <-contentSchChan: + completeCount++ + contentSch = r.v + } + } + + if len(anyOf) > 0 { + s.AnyOf = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{ + Value: anyOf, + KeyNode: anyOfLabel, + ValueNode: anyOfValue, + } + } + if len(oneOf) > 0 { + s.OneOf = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{ + Value: oneOf, + KeyNode: oneOfLabel, + ValueNode: oneOfValue, + } + } + if len(allOf) > 0 { + s.AllOf = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{ + Value: allOf, + KeyNode: allOfLabel, + ValueNode: allOfValue, + } + } + if !not.IsEmpty() { + s.Not = low.NodeReference[*SchemaProxy]{ + Value: not.Value, + KeyNode: notLabel, + ValueNode: notValue, + } + } + if !itemsIsBool && !items.IsEmpty() { + s.Items = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]{ + Value: &SchemaDynamicValue[*SchemaProxy, bool]{ + A: items.Value, + }, + KeyNode: itemsLabel, + ValueNode: itemsValue, + } + } + if len(prefixItems) > 0 { + s.PrefixItems = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{ + Value: prefixItems, + KeyNode: prefixItemsLabel, + ValueNode: prefixItemsValue, + } + } + if !contains.IsEmpty() { + s.Contains = low.NodeReference[*SchemaProxy]{ + Value: contains.Value, + KeyNode: containsLabel, + ValueNode: containsValue, + } + } + if !sif.IsEmpty() { + s.If = low.NodeReference[*SchemaProxy]{ + Value: sif.Value, + KeyNode: sifLabel, + ValueNode: sifValue, + } + } + if !selse.IsEmpty() { + s.Else = low.NodeReference[*SchemaProxy]{ + Value: selse.Value, + KeyNode: selseLabel, + ValueNode: selseValue, + } + } + if !sthen.IsEmpty() { + s.Then = low.NodeReference[*SchemaProxy]{ + Value: sthen.Value, + KeyNode: sthenLabel, + ValueNode: sthenValue, + } + } + if !propertyNames.IsEmpty() { + s.PropertyNames = low.NodeReference[*SchemaProxy]{ + Value: propertyNames.Value, + KeyNode: propNamesLabel, + ValueNode: propNamesValue, + } + } + if !unevalItems.IsEmpty() { + s.UnevaluatedItems = low.NodeReference[*SchemaProxy]{ + Value: unevalItems.Value, + KeyNode: unevalItemsLabel, + ValueNode: unevalItemsValue, + } + } + if !unevalIsBool && !unevalProperties.IsEmpty() { + s.UnevaluatedProperties = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]{ + Value: &SchemaDynamicValue[*SchemaProxy, bool]{ + A: unevalProperties.Value, + }, + KeyNode: unevalPropsLabel, + ValueNode: unevalPropsValue, + } + } + if !addPropsIsBool && !addProperties.IsEmpty() { + s.AdditionalProperties = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]{ + Value: &SchemaDynamicValue[*SchemaProxy, bool]{ + A: addProperties.Value, + }, + KeyNode: addPropsLabel, + ValueNode: addPropsValue, + } + } + if !contentSch.IsEmpty() { + s.ContentSchema = low.NodeReference[*SchemaProxy]{ + Value: contentSch.Value, + KeyNode: contentSchLabel, + ValueNode: contentSchValue, + } + } + return nil +} + +func buildPropertyMap(ctx context.Context, parent *Schema, root *yaml.Node, idx *index.SpecIndex, label string) (*low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*SchemaProxy]]], error) { + _, propLabel, propsNode := utils.FindKeyNodeFullTop(label, root.Content) + if propsNode != nil { + propertyMap := orderedmap.New[low.KeyReference[string], low.ValueReference[*SchemaProxy]]() + var currentProp *yaml.Node + for i, prop := range propsNode.Content { + if i%2 == 0 { + currentProp = prop + parent.Nodes.Store(prop.Line, prop) + continue + } + + foundCtx := ctx + foundIdx := idx + // check our prop isn't reference + refString := "" + var refNode *yaml.Node + if h, _, l := utils.IsNodeRefValue(prop); h { + ref, fIdx, err, fctx := low.LocateRefNodeWithContext(foundCtx, prop, foundIdx) + if ref != nil { + refNode = prop + prop = ref + refString = l + foundCtx = fctx + foundIdx = fIdx + } else if errors.Is(err, low.ErrExternalRefSkipped) { + refString = l + refNode = prop + } else { + return nil, fmt.Errorf("schema properties build failed: cannot find reference %s, line %d, col %d", + prop.Content[1].Value, prop.Content[1].Line, prop.Content[1].Column) + } + } + + sp := &SchemaProxy{ctx: foundCtx, kn: currentProp, vn: prop, idx: foundIdx} + sp.SetReference(refString, refNode) + + _ = sp.Build(foundCtx, currentProp, prop, foundIdx) + + propertyMap.Set(low.KeyReference[string]{ + KeyNode: currentProp, + Value: currentProp.Value, + }, low.ValueReference[*SchemaProxy]{ + Value: sp, + ValueNode: sp.vn, // use transformed node + }) + } + + return &low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*SchemaProxy]]]{ + Value: propertyMap, + KeyNode: propLabel, + ValueNode: propsNode, + }, nil + } + return nil, nil +} + +// buildDependentRequiredMap builds an ordered map of string arrays for the dependentRequired property +func buildDependentRequiredMap(root *yaml.Node, label string) (*low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[[]string]]], error) { + _, propLabel, propsNode := utils.FindKeyNodeFullTop(label, root.Content) + if propsNode != nil { + dependentRequiredMap := orderedmap.New[low.KeyReference[string], low.ValueReference[[]string]]() + var currentKey *yaml.Node + for i, node := range propsNode.Content { + if i%2 == 0 { + currentKey = node + continue + } + + // node should be an array of strings + if !utils.IsNodeArray(node) { + return nil, fmt.Errorf("dependentRequired value must be an array, found %v at line %d, col %d", + node.Kind, node.Line, node.Column) + } + + var requiredProps []string + for _, propNode := range node.Content { + if propNode.Kind != yaml.ScalarNode { + return nil, fmt.Errorf("dependentRequired array items must be strings, found %v at line %d, col %d", + propNode.Kind, propNode.Line, propNode.Column) + } + requiredProps = append(requiredProps, propNode.Value) + } + + dependentRequiredMap.Set(low.KeyReference[string]{ + KeyNode: currentKey, + Value: currentKey.Value, + }, low.ValueReference[[]string]{ + Value: requiredProps, + ValueNode: node, + }) + } + + return &low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[[]string]]]{ + Value: dependentRequiredMap, + KeyNode: propLabel, + ValueNode: propsNode, + }, nil + } + return nil, nil +} + +// count the number of sub-schemas in a node. +func countSubSchemaItems(node *yaml.Node) int { + if utils.IsNodeMap(node) { + return 1 + } + if utils.IsNodeArray(node) { + return len(node.Content) + } + return 0 +} + +// schema build result container used for async building. +type schemaProxyBuildResult struct { + k low.KeyReference[string] + v low.ValueReference[*SchemaProxy] +} + +// extract extensions from schema +func (s *Schema) extractExtensions(root *yaml.Node) { + s.Extensions = low.ExtractExtensions(root) +} + +// build out a child schema for parent schema. +func buildSchema(ctx context.Context, schemas chan schemaProxyBuildResult, labelNode, valueNode *yaml.Node, errors chan error, idx *index.SpecIndex) { + if valueNode != nil { + type buildResult struct { + res *low.ValueReference[*SchemaProxy] + idx int + } + + syncChan := make(chan buildResult) + + // build out a SchemaProxy for every sub-schema. + build := func(pctx context.Context, fIdx *index.SpecIndex, kn, vn *yaml.Node, rf *yaml.Node, schemaIdx int, c chan buildResult, + isRef bool, refLocation string, + ) buildResult { + // a proxy design works best here. polymorphism, pretty much guarantees that a sub-schema can + // take on circular references through polymorphism. Like the resolver, if we try and follow these + // journey's through hyperspace, we will end up creating endless amounts of threads, spinning off + // chasing down circles, that in turn spin up endless threads. + // In order to combat this, we need a schema proxy that will only resolve the schema when asked, and then + // it will only do it one level at a time. + sp := new(SchemaProxy) + + // call Build to ensure transformation happens + _ = sp.Build(pctx, kn, vn, fIdx) + + if isRef { + sp.SetReference(refLocation, rf) + } + res := &low.ValueReference[*SchemaProxy]{ + Value: sp, + ValueNode: sp.vn, // use transformed node + } + return buildResult{ + res: res, + idx: schemaIdx, + } + } + + isRef := false + refLocation := "" + var refNode *yaml.Node + foundCtx := ctx + foundIdx := idx + if utils.IsNodeMap(valueNode) { + h := false + if h, _, refLocation = utils.IsNodeRefValue(valueNode); h { + isRef = true + ref, fIdx, err, fctx := low.LocateRefNodeWithContext(foundCtx, valueNode, foundIdx) + if ref != nil { + refNode = valueNode + valueNode = ref + foundCtx = fctx + foundIdx = fIdx + } else if err == low.ErrExternalRefSkipped { + refNode = valueNode + } else { + errors <- fmt.Errorf("build schema failed: reference cannot be found: %s, line %d, col %d", + valueNode.Content[1].Value, valueNode.Content[1].Line, valueNode.Content[1].Column) + } + } + + // this only runs once, however to keep things consistent, it makes sense to use the same async method + // that arrays will use. + r := build(foundCtx, foundIdx, labelNode, valueNode, refNode, -1, syncChan, isRef, refLocation) + schemas <- schemaProxyBuildResult{ + k: low.KeyReference[string]{ + KeyNode: labelNode, + Value: labelNode.Value, + }, + v: *r.res, + } + } else if utils.IsNodeArray(valueNode) { + refBuilds := 0 + results := make([]*low.ValueReference[*SchemaProxy], len(valueNode.Content)) + + for i, vn := range valueNode.Content { + isRef = false + h := false + foundIdx = idx + foundCtx = ctx + if h, _, refLocation = utils.IsNodeRefValue(vn); h { + isRef = true + ref, fIdx, err, fctx := low.LocateRefNodeWithContext(foundCtx, vn, foundIdx) + if ref != nil { + refNode = vn + vn = ref + foundCtx = fctx + foundIdx = fIdx + } else if err == low.ErrExternalRefSkipped { + refNode = vn + } else { + errors <- fmt.Errorf("build schema failed: reference cannot be found: %s, line %d, col %d", + vn.Content[1].Value, vn.Content[1].Line, vn.Content[1].Column) + return + } + } + refBuilds++ + r := build(foundCtx, foundIdx, vn, vn, refNode, i, syncChan, isRef, refLocation) + results[r.idx] = r.res + } + + for _, r := range results { + schemas <- schemaProxyBuildResult{ + k: low.KeyReference[string]{ + KeyNode: labelNode, + Value: labelNode.Value, + }, + v: *r, + } + } + } else { + errors <- fmt.Errorf("build schema failed: unexpected data type: '%s', line %d, col %d", + utils.MakeTagReadable(valueNode), valueNode.Line, valueNode.Column) + } + } +} + +// ExtractSchema will return a pointer to a NodeReference that contains a *SchemaProxy if successful. The function +// will specifically look for a key node named 'schema' and extract the value mapped to that key. If the operation +// fails then no NodeReference is returned and an error is returned instead. +func ExtractSchema(ctx context.Context, root *yaml.Node, idx *index.SpecIndex) (*low.NodeReference[*SchemaProxy], error) { + var schLabel, schNode *yaml.Node + errStr := "schema build failed: reference '%s' cannot be found at line %d, col %d" + + refLocation := "" + var refNode *yaml.Node + + foundIndex := idx + foundCtx := ctx + if rf, rl, rv := utils.IsNodeRefValue(root); rf { + // locate reference in index. + ref, fIdx, err, nCtx := low.LocateRefNodeWithContext(ctx, root, idx) + if ref != nil { + schNode = ref + schLabel = rl + foundCtx = nCtx + foundIndex = fIdx + } else if errors.Is(err, low.ErrExternalRefSkipped) { + refLocation = rv + schema := &SchemaProxy{kn: root, vn: root, idx: idx, ctx: ctx} + _ = schema.Build(ctx, root, root, idx) + n := &low.NodeReference[*SchemaProxy]{Value: schema, KeyNode: root, ValueNode: root} + n.SetReference(refLocation, root) + schema.SetReference(refLocation, root) + return n, nil + } else { + v := root.Content[1].Value + if root.Content[1].Value == "" { + v = "[empty]" + } + return nil, fmt.Errorf(errStr, + v, root.Content[1].Line, root.Content[1].Column) + } + } else { + _, schLabel, schNode = utils.FindKeyNodeFull(SchemaLabel, root.Content) + if schNode != nil { + h := false + if h, _, refLocation = utils.IsNodeRefValue(schNode); h { + ref, fIdx, lerr, nCtx := low.LocateRefNodeWithContext(foundCtx, schNode, foundIndex) + if ref != nil { + refNode = schNode + schNode = ref + if fIdx != nil { + foundIndex = fIdx + } + foundCtx = nCtx + } else if errors.Is(lerr, low.ErrExternalRefSkipped) { + refNode = schNode + } else { + v := schNode.Content[1].Value + if schNode.Content[1].Value == "" { + v = "[empty]" + } + return nil, fmt.Errorf(errStr, + v, schNode.Content[1].Line, schNode.Content[1].Column) + } + } + } + } + + if schNode != nil { + // check if schema has already been built. + schema := &SchemaProxy{kn: schLabel, vn: schNode, idx: foundIndex, ctx: foundCtx} + + // call Build to ensure transformation happens + _ = schema.Build(foundCtx, schLabel, schNode, foundIndex) + + schema.SetReference(refLocation, refNode) + + n := &low.NodeReference[*SchemaProxy]{ + Value: schema, + KeyNode: schLabel, + ValueNode: schema.vn, // use transformed node + } + n.SetReference(refLocation, refNode) + return n, nil + } + return nil, nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/base/schema_proxy.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/schema_proxy.go new file mode 100644 index 000000000..ae037e67b --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/schema_proxy.go @@ -0,0 +1,373 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "context" + "errors" + "fmt" + "hash/maphash" + "log/slog" + "sync" + + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// SchemaProxy exists as a stub that will create a Schema once (and only once) the Schema() method is called. +// +// Why use a Proxy design? +// +// There are three reasons. +// +// 1. Circular References and Endless Loops. +// +// JSON Schema allows for references to be used. This means references can loop around and create infinite recursive +// structures, These 'Circular references' technically mean a schema can NEVER be resolved, not without breaking the +// loop somewhere along the chain. +// +// Polymorphism in the form of 'oneOf' and 'anyOf' in version 3+ only exacerbates the problem. +// +// These circular traps can be discovered using the resolver, however it's still not enough to stop endless loops and +// endless goroutine spawning. A proxy design means that resolving occurs on demand and runs down a single level only. +// preventing any run-away loops. +// +// 2. Performance +// +// Even without circular references, Polymorphism creates large additional resolving chains that take a long time +// and slow things down when building. By preventing recursion through every polymorphic item, building models is kept +// fast and snappy, which is desired for realtime processing of specs. +// +// - Q: Yeah, but, why not just use state to avoiding re-visiting seen polymorphic nodes? +// - A: It's slow, takes up memory and still has runaway potential in very, very long chains. +// +// 3. Short Circuit Errors. +// +// Schemas are where things can get messy, mainly because the Schema standard changes between versions, and +// it's not actually JSONSchema until 3.1, so lots of times a bad schema will break parsing. Errors are only found +// when a schema is needed, so the rest of the document is parsed and ready to use. +type SchemaProxy struct { + low.Reference + kn *yaml.Node + vn *yaml.Node + idx *index.SpecIndex + rendered *Schema + buildError error + ctx context.Context + cachedHash *uint64 // Cache computed hash to avoid recalculation + TransformedRef *yaml.Node // Original node that contained the ref before transformation + *low.NodeMap +} + +// Build will prepare the SchemaProxy for rendering, it does not build the Schema, only sets up internal state. +// Key maybe nil if absent. +func (sp *SchemaProxy) Build(ctx context.Context, key, value *yaml.Node, idx *index.SpecIndex) error { + sp.kn = key + sp.idx = idx + + // transform sibling refs to allOf structure if enabled and applicable + // this ensures sp.vn contains the pre-transformed YAML as the source of truth + transformedValue := value + wasTransformed := false + if idx != nil && idx.GetConfig() != nil && idx.GetConfig().TransformSiblingRefs { + transformer := NewSiblingRefTransformer(idx) + if transformer.ShouldTransform(value) { + transformed, _ := transformer.TransformSiblingRef(value) + if transformed != nil { + transformedValue = transformed + wasTransformed = true + sp.TransformedRef = value // store original node that had the ref + } + } + } + + sp.vn = transformedValue + sp.ctx = applySchemaIdScope(ctx, value, idx) + + // handle reference detection + if !wasTransformed { + // for non-transformed schemas, handle reference normally + if rf, _, r := utils.IsNodeRefValue(transformedValue); rf { + sp.SetReference(r, transformedValue) + } + } + // for transformed schemas, don't set reference since it's now an allOf structure + // the reference is embedded within the allOf, but the schema itself is not a pure reference + var m sync.Map + sp.NodeMap = &low.NodeMap{Nodes: &m} + return nil +} + +func applySchemaIdScope(ctx context.Context, node *yaml.Node, idx *index.SpecIndex) context.Context { + if node == nil { + return ctx + } + scope := index.GetSchemaIdScope(ctx) + idValue := index.FindSchemaIdInNode(node) + if idValue == "" { + return ctx + } + if scope == nil { + base := "" + if idx != nil { + base = idx.GetSpecAbsolutePath() + } + scope = index.NewSchemaIdScope(base) + ctx = index.WithSchemaIdScope(ctx, scope) + } + parentBase := scope.BaseUri + resolved, err := index.ResolveSchemaId(idValue, parentBase) + if err != nil || resolved == "" { + resolved = idValue + } + updated := scope.Copy() + updated.PushId(resolved) + return index.WithSchemaIdScope(ctx, updated) +} + +// Schema will first check if this SchemaProxy has already rendered the schema, and return the pre-rendered version +// first. +// +// If this is the first run of Schema(), then the SchemaProxy will create a new Schema from the underlying +// yaml.Node. Once built out, the SchemaProxy will record that Schema as rendered and store it for later use, +// (this is what is we mean when we say 'pre-rendered'). +// +// Schema() then returns the newly created Schema. +// +// If anything goes wrong during the build, then nothing is returned and the error that occurred can +// be retrieved by using GetBuildError() +func (sp *SchemaProxy) Schema() *Schema { + if sp.rendered != nil { + return sp.rendered + } + + // If this proxy represents an unresolved external ref, return nil without error. + if sp.IsReference() && sp.idx != nil && sp.idx.GetConfig() != nil && + sp.idx.GetConfig().SkipExternalRefResolution && utils.IsExternalRef(sp.GetReference()) { + return nil + } + + // handle property merging for references with sibling properties + buildNode := sp.vn + if sp.idx != nil && sp.idx.GetConfig() != nil { + if docConfig := sp.getDocumentConfig(); docConfig != nil && docConfig.MergeReferencedProperties { + if mergedNode := sp.attemptPropertyMerging(buildNode, docConfig); mergedNode != nil { + buildNode = mergedNode + } + } + } + + schema := new(Schema) + utils.CheckForMergeNodes(buildNode) + err := schema.Build(sp.ctx, buildNode, sp.idx) + if err != nil { + sp.buildError = err + return nil + } + schema.ParentProxy = sp // https://github.com/pb33f/libopenapi/issues/29 + sp.rendered = schema + + // for all the nodes added, copy them over to the schema + if sp.NodeMap != nil { + sp.NodeMap.Nodes.Range(func(key, value any) bool { + schema.AddNode(key.(int), value.(*yaml.Node)) + return true + }) + } + return schema +} + +// GetBuildError returns the build error that was set when Schema() was called. If Schema() has not been run, or +// there were no errors during build, then nil will be returned. +func (sp *SchemaProxy) GetBuildError() error { + return sp.buildError +} + +func (sp *SchemaProxy) GetSchemaReferenceLocation() *index.NodeOrigin { + if sp.idx != nil { + origin := sp.idx.FindNodeOrigin(sp.vn) + if origin != nil { + return origin + } + if sp.idx.GetRolodex() != nil { + origin = sp.idx.GetRolodex().FindNodeOrigin(sp.vn) + return origin + } + } + return nil +} + +// GetKeyNode will return the yaml.Node pointer that is a key for value node. +func (sp *SchemaProxy) GetKeyNode() *yaml.Node { + return sp.kn +} + +// GetContext will return the context.Context object that was passed to the SchemaProxy during build. +func (sp *SchemaProxy) GetContext() context.Context { + return sp.ctx +} + +// GetValueNode will return the yaml.Node pointer used by the proxy to generate the Schema. +func (sp *SchemaProxy) GetValueNode() *yaml.Node { + return sp.vn +} + +// Hash will return a consistent Hash of the SchemaProxy object (it will resolve it) +func (sp *SchemaProxy) Hash() uint64 { + if sp.cachedHash != nil { + return *sp.cachedHash + } + + var hash uint64 + + if sp.rendered != nil { + if !sp.IsReference() { + hash = sp.rendered.Hash() + } else { + // For references, hash the reference value + hash = low.WithHasher(func(h *maphash.Hash) uint64 { + h.WriteString(sp.GetReference()) + return h.Sum64() + }) + } + } else { + if !sp.IsReference() { + // Only resolve this proxy if it's not a ref. + sch := sp.Schema() + sp.rendered = sch + hashError := fmt.Errorf("circular reference detected: %s", sp.GetReference()) + if sch != nil { + if sp.idx != nil && sp.idx.GetConfig() != nil && sp.idx.GetConfig().UseSchemaQuickHash { + if !CheckSchemaProxyForCircularRefs(sp) { + hash = sch.Hash() + } + } else { + hash = sch.Hash() + } + } else { + var logger *slog.Logger + if sp.idx != nil && sp.idx.GetLogger() != nil { + logger = sp.idx.GetLogger() + } + if logger != nil { + bErr := errors.Join(sp.GetBuildError(), hashError) + if bErr != nil { + logger.Warn("SchemaProxy.Hash() unable to complete hash: ", "error", bErr.Error()) + } + } + hash = 0 + } + } else { + // Handle UseSchemaQuickHash case for references + if sp.idx != nil && sp.idx.GetConfig() != nil && sp.idx.GetConfig().UseSchemaQuickHash { + if sp.idx != nil && !CheckSchemaProxyForCircularRefs(sp) { + if sp.rendered == nil { + sp.rendered = sp.Schema() + } + hash = sp.rendered.QuickHash() // quick hash uses a cache to keep things fast. + } else { + hash = low.WithHasher(func(h *maphash.Hash) uint64 { + h.WriteString(sp.GetReference()) + return h.Sum64() + }) + } + } else { + // Hash reference value only, do not resolve! + hash = low.WithHasher(func(h *maphash.Hash) uint64 { + h.WriteString(sp.GetReference()) + return h.Sum64() + }) + } + } + } + + // Cache the computed hash for future calls + sp.cachedHash = &hash + return hash +} + +// AddNode stores nodes in the underlying schema if rendered, otherwise holds in the proxy until build. +func (sp *SchemaProxy) AddNode(key int, node *yaml.Node) { + // Clear cached hash since content is being modified + sp.cachedHash = nil + + if sp.rendered != nil { + sp.rendered.AddNode(key, node) + } else { + sp.Nodes.Store(key, node) + } +} + +// GetIndex will return the index.SpecIndex pointer that was passed to the SchemaProxy during build. +func (sp *SchemaProxy) GetIndex() *index.SpecIndex { + return sp.idx +} + +type HasIndex interface { + GetIndex() *index.SpecIndex +} + +// getDocumentConfig retrieves the document configuration from the index +func (sp *SchemaProxy) getDocumentConfig() *datamodel.DocumentConfiguration { + if sp.idx == nil || sp.idx.GetRolodex() == nil { + return nil + } + rolodex := sp.idx.GetRolodex() + if config := rolodex.GetConfig(); config != nil { + return config.ToDocumentConfiguration() + } + return nil +} + +// attemptPropertyMerging attempts to merge properties for references with siblings +func (sp *SchemaProxy) attemptPropertyMerging(node *yaml.Node, config *datamodel.DocumentConfiguration) *yaml.Node { + if !config.MergeReferencedProperties || !utils.IsNodeMap(node) { + return nil + } + + // extract ref value and sibling properties + var refValue string + siblings := make(map[string]*yaml.Node) + + for i := 0; i < len(node.Content); i += 2 { + if i+1 < len(node.Content) { + if node.Content[i].Value == "$ref" { + refValue = node.Content[i+1].Value + } else { + siblings[node.Content[i].Value] = node.Content[i+1] + } + } + } + + if refValue == "" || len(siblings) == 0 { + return nil // no merging needed + } + + referencedComponent := sp.idx.FindComponentInRoot(sp.ctx, refValue) + if referencedComponent == nil || referencedComponent.Node == nil { + return nil // cannot resolve reference + } + + // create property merger and merge + merger := NewPropertyMerger(config.PropertyMergeStrategy) + + // create a local node with just the sibling properties + localNode := &yaml.Node{Kind: yaml.MappingNode} + for key, value := range siblings { + keyNode := &yaml.Node{Kind: yaml.ScalarNode, Value: key} + localNode.Content = append(localNode.Content, keyNode, value) + } + + // merge local properties with referenced schema + merged, err := merger.MergeProperties(localNode, referencedComponent.Node) + if err != nil { + // if merging fails, return original node to preserve existing behavior + return nil + } + + return merged +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/base/security_requirement.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/security_requirement.go new file mode 100644 index 000000000..c5413631c --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/security_requirement.go @@ -0,0 +1,153 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "context" + "hash/maphash" + "sort" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// SecurityRequirement is a low-level representation of a Swagger / OpenAPI 3 SecurityRequirement object. +// +// SecurityRequirement lists the required security schemes to execute this operation. The object can have multiple +// security schemes declared in it which are all required (that is, there is a logical AND between the schemes). +// +// The name used for each property MUST correspond to a security scheme declared in the Security Definitions +// - https://swagger.io/specification/v2/#securityDefinitionsObject +// - https://swagger.io/specification/#security-requirement-object +type SecurityRequirement struct { + Requirements low.ValueReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[[]low.ValueReference[string]]]] + KeyNode *yaml.Node + RootNode *yaml.Node + ContainsEmptyRequirement bool // if a requirement is empty (this means it's optional) + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// GetContext will return the context.Context instance used when building the SecurityRequirement object +func (s *SecurityRequirement) GetContext() context.Context { + return s.context +} + +// GetIndex will return the index.SpecIndex instance attached to the SecurityRequirement object +func (s *SecurityRequirement) GetIndex() *index.SpecIndex { + return s.index +} + +// Build will extract security requirements from the node (the structure is odd, to be honest) +func (s *SecurityRequirement) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + s.KeyNode = keyNode + root = utils.NodeAlias(root) + s.RootNode = root + utils.CheckForMergeNodes(root) + s.Reference = new(low.Reference) + s.Nodes = low.ExtractNodes(ctx, root) + s.context = ctx + s.index = idx + + var labelNode *yaml.Node + valueMap := orderedmap.New[low.KeyReference[string], low.ValueReference[[]low.ValueReference[string]]]() + var arr []low.ValueReference[string] + for i := range root.Content { + if i%2 == 0 { + labelNode = root.Content[i] + arr = []low.ValueReference[string]{} // reset roles. + continue + } + for j := range root.Content[i].Content { + if root.Content[i].Content[j].Value == "" { + s.ContainsEmptyRequirement = true + } + arr = append(arr, low.ValueReference[string]{ + Value: root.Content[i].Content[j].Value, + ValueNode: root.Content[i].Content[j], + }) + s.Nodes.Store(root.Content[i].Content[j].Line, root.Content[i].Content[j]) + } + valueMap.Set( + low.KeyReference[string]{ + Value: labelNode.Value, + KeyNode: labelNode, + }, + low.ValueReference[[]low.ValueReference[string]]{ + Value: arr, + ValueNode: root.Content[i], + }, + ) + } + if len(root.Content) == 0 { + s.ContainsEmptyRequirement = true + } + s.Requirements = low.ValueReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[[]low.ValueReference[string]]]]{ + Value: valueMap, + ValueNode: root, + } + + return nil +} + +// GetRootNode will return the root yaml node of the SecurityRequirement object +func (s *SecurityRequirement) GetRootNode() *yaml.Node { + return s.RootNode +} + +// GetKeyNode will return the key yaml node of the SecurityRequirement object +func (s *SecurityRequirement) GetKeyNode() *yaml.Node { + return s.KeyNode +} + +// FindRequirement will attempt to locate a security requirement string from a supplied name. +func (s *SecurityRequirement) FindRequirement(name string) []low.ValueReference[string] { + for k, v := range s.Requirements.Value.FromOldest() { + if k.Value == name { + return v.Value + } + } + return nil +} + +// GetKeys returns a string slice of all the keys used in the requirement. +func (s *SecurityRequirement) GetKeys() []string { + keys := make([]string, orderedmap.Len(s.Requirements.Value)) + z := 0 + for k := range s.Requirements.Value.KeysFromOldest() { + keys[z] = k.Value + z++ + } + return keys +} + +// Hash will return a consistent hash of the SecurityRequirement object +func (s *SecurityRequirement) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + for k, v := range orderedmap.SortAlpha(s.Requirements.Value).FromOldest() { + // Pre-allocate vals slice + vals := make([]string, len(v.Value)) + for y := range v.Value { + vals[y] = v.Value[y].Value + } + sort.Strings(vals) + + h.WriteString(k.Value) + h.WriteByte('-') + for i, val := range vals { + if i > 0 { + h.WriteByte(low.HASH_PIPE) + } + h.WriteString(val) + } + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/base/sibling_ref_transformer.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/sibling_ref_transformer.go new file mode 100644 index 000000000..5173becd8 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/sibling_ref_transformer.go @@ -0,0 +1,153 @@ +// Copyright 2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// SiblingRefTransformer handles transformation of schemas with sibling properties alongside $ref +// into OpenAPI 3.1 compliant allOf structures +type SiblingRefTransformer struct { + index *index.SpecIndex +} + +// NewSiblingRefTransformer creates a new transformer instance +func NewSiblingRefTransformer(idx *index.SpecIndex) *SiblingRefTransformer { + return &SiblingRefTransformer{ + index: idx, + } +} + +// TransformSiblingRef transforms a node with $ref and sibling properties into an allOf structure +// Example transformation: +// +// Input: {title: "MySchema", $ref: "#/components/schemas/Base"} +// Output: {allOf: [{title: "MySchema"}, {$ref: "#/components/schemas/Base"}]} +func (srt *SiblingRefTransformer) TransformSiblingRef(node *yaml.Node) (*yaml.Node, error) { + if !srt.ShouldTransform(node) { + return node, nil // no transformation needed + } + + siblings, refValue := srt.ExtractSiblingProperties(node) + return srt.CreateAllOfStructure(refValue, siblings), nil +} + +// CreateAllOfStructure creates an allOf node structure from ref value and sibling properties +func (srt *SiblingRefTransformer) CreateAllOfStructure(refValue string, siblings map[string]*yaml.Node) *yaml.Node { + + allOfNode := &yaml.Node{ + Kind: yaml.MappingNode, + Tag: "!!map", + Content: []*yaml.Node{ + {Kind: yaml.ScalarNode, Tag: "!!str", Value: "allOf"}, + {Kind: yaml.SequenceNode, Tag: "!!seq", Content: []*yaml.Node{}}, + }, + } + + allOfArrayNode := allOfNode.Content[1] + + // first element: schema with sibling properties (excluding $ref) + if len(siblings) > 0 { + siblingSchemaNode := &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map"} + for key, valueNode := range siblings { + keyNode := &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: key} + // create a copy of the value node to avoid modifying original + copiedValueNode := srt.copyNode(valueNode) + siblingSchemaNode.Content = append(siblingSchemaNode.Content, keyNode, copiedValueNode) + } + allOfArrayNode.Content = append(allOfArrayNode.Content, siblingSchemaNode) + } + + // second element: the reference schema + refSchemaNode := &yaml.Node{ + Kind: yaml.MappingNode, + Tag: "!!map", + Content: []*yaml.Node{ + {Kind: yaml.ScalarNode, Tag: "!!str", Value: "$ref"}, + {Kind: yaml.ScalarNode, Tag: "!!str", Value: refValue}, + }, + } + allOfArrayNode.Content = append(allOfArrayNode.Content, refSchemaNode) + + return allOfNode +} + +// ExtractSiblingProperties extracts sibling properties from a node containing $ref +// returns a map of sibling properties and the $ref value +func (srt *SiblingRefTransformer) ExtractSiblingProperties(node *yaml.Node) (map[string]*yaml.Node, string) { + if !utils.IsNodeMap(node) || len(node.Content) < 4 { // need at least $ref + one sibling + return nil, "" + } + + siblings := make(map[string]*yaml.Node) + var refValue string + + for i := 0; i < len(node.Content); i += 2 { + if i+1 >= len(node.Content) { + break + } + + keyNode := node.Content[i] + valueNode := node.Content[i+1] + + if keyNode.Value == "$ref" { + refValue = valueNode.Value + } else { + siblings[keyNode.Value] = valueNode + } + } + + if refValue == "" || len(siblings) == 0 { + return nil, "" + } + + return siblings, refValue +} + +// ShouldTransform determines if a node should be transformed based on configuration and content +func (srt *SiblingRefTransformer) ShouldTransform(node *yaml.Node) bool { + if srt.index == nil || srt.index.GetConfig() == nil { + return false + } + + if !srt.index.GetConfig().TransformSiblingRefs { + return false + } + + siblings, refValue := srt.ExtractSiblingProperties(node) + return len(siblings) > 0 && refValue != "" +} + +// copyNode creates a deep copy of a yaml node to avoid modifying the original +func (srt *SiblingRefTransformer) copyNode(node *yaml.Node) *yaml.Node { + if node == nil { + return nil + } + + copied := &yaml.Node{ + Kind: node.Kind, + Style: node.Style, + Tag: node.Tag, + Value: node.Value, + Anchor: node.Anchor, + Alias: node.Alias, + Line: node.Line, + Column: node.Column, + HeadComment: node.HeadComment, + LineComment: node.LineComment, + FootComment: node.FootComment, + } + + if node.Content != nil { + copied.Content = make([]*yaml.Node, len(node.Content)) + for i, child := range node.Content { + copied.Content[i] = srt.copyNode(child) + } + } + + return copied +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/base/tag.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/tag.go new file mode 100644 index 000000000..3438476f1 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/tag.go @@ -0,0 +1,123 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "context" + "hash/maphash" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Tag represents a low-level Tag instance that is backed by a low-level one. +// +// Adds metadata to a single tag that is used by the Operation Object. It is not mandatory to have a Tag Object per +// tag defined in the Operation Object instances. +// - v2: https://swagger.io/specification/v2/#tagObject +// - v3: https://swagger.io/specification/#tag-object +// - v3.2: https://spec.openapis.org/oas/v3.2.0#tag-object +type Tag struct { + Name low.NodeReference[string] + Summary low.NodeReference[string] + Description low.NodeReference[string] + ExternalDocs low.NodeReference[*ExternalDoc] + Parent low.NodeReference[string] + Kind low.NodeReference[string] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// GetIndex returns the index.SpecIndex instance attached to the Tag object +func (t *Tag) GetIndex() *index.SpecIndex { + return t.index +} + +// GetContext returns the context.Context instance used when building the Tag object +func (t *Tag) GetContext() context.Context { + return t.context +} + +// FindExtension returns a ValueReference containing the extension value, if found. +func (t *Tag) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, t.Extensions) +} + +// GetRootNode returns the root yaml node of the Tag object +func (t *Tag) GetRootNode() *yaml.Node { + return t.RootNode +} + +// GetKeyNode returns the key yaml node of the Tag object +func (t *Tag) GetKeyNode() *yaml.Node { + return t.KeyNode +} + +// Build will extract extensions and external docs for the Tag. +func (t *Tag) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + t.KeyNode = keyNode + root = utils.NodeAlias(root) + t.RootNode = root + utils.CheckForMergeNodes(root) + t.Reference = new(low.Reference) + t.Nodes = low.ExtractNodes(ctx, root) + t.Extensions = low.ExtractExtensions(root) + t.index = idx + t.context = ctx + + low.ExtractExtensionNodes(ctx, t.Extensions, t.Nodes) + + // extract externalDocs + extDocs, err := low.ExtractObject[*ExternalDoc](ctx, ExternalDocsLabel, root, idx) + t.ExternalDocs = extDocs + return err +} + +// GetExtensions returns all Tag extensions and satisfies the low.HasExtensions interface. +func (t *Tag) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return t.Extensions +} + +// Hash will return a consistent hash of the Tag object +func (t *Tag) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if !t.Name.IsEmpty() { + h.WriteString(t.Name.Value) + h.WriteByte(low.HASH_PIPE) + } + if !t.Summary.IsEmpty() { + h.WriteString(t.Summary.Value) + h.WriteByte(low.HASH_PIPE) + } + if !t.Description.IsEmpty() { + h.WriteString(t.Description.Value) + h.WriteByte(low.HASH_PIPE) + } + if !t.ExternalDocs.IsEmpty() { + h.WriteString(low.GenerateHashString(t.ExternalDocs.Value)) + h.WriteByte(low.HASH_PIPE) + } + if !t.Parent.IsEmpty() { + h.WriteString(t.Parent.Value) + h.WriteByte(low.HASH_PIPE) + } + if !t.Kind.IsEmpty() { + h.WriteString(t.Kind.Value) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(t.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/base/xml.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/xml.go new file mode 100644 index 000000000..e46884310 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/base/xml.go @@ -0,0 +1,98 @@ +package base + +import ( + "context" + "hash/maphash" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// XML represents a low-level representation of an XML object defined by all versions of OpenAPI. +// +// A metadata object that allows for more fine-tuned XML model definitions. +// +// When using arrays, XML element names are not inferred (for singular/plural forms) and the name property SHOULD be +// used to add that information. See examples for expected behavior. +// +// v2 - https://swagger.io/specification/v2/#xmlObject +// v3 - https://swagger.io/specification/#xml-object +type XML struct { + Name low.NodeReference[string] + Namespace low.NodeReference[string] + Prefix low.NodeReference[string] + Attribute low.NodeReference[bool] + NodeType low.NodeReference[string] // OpenAPI 3.2+ nodeType field (replaces deprecated attribute field) + Wrapped low.NodeReference[bool] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// Build will extract extensions from the XML instance. +func (x *XML) Build(root *yaml.Node, idx *index.SpecIndex) error { + root = utils.NodeAlias(root) + utils.CheckForMergeNodes(root) + x.RootNode = root + x.Reference = new(low.Reference) + x.Nodes = low.ExtractNodes(nil, root) + x.Extensions = low.ExtractExtensions(root) + x.index = idx + return nil +} + +// GetExtensions returns all Tag extensions and satisfies the low.HasExtensions interface. +func (x *XML) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return x.Extensions +} + +// GetRootNode returns the root yaml node of the Tag object +func (x *XML) GetRootNode() *yaml.Node { + return x.RootNode +} + +// GetIndex returns the index of the XML object +func (x *XML) GetIndex() *index.SpecIndex { + return x.index +} + +// Hash generates a hash of the XML object using properties +func (x *XML) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if !x.Name.IsEmpty() { + h.WriteString(x.Name.Value) + h.WriteByte(low.HASH_PIPE) + } + if !x.Namespace.IsEmpty() { + h.WriteString(x.Namespace.Value) + h.WriteByte(low.HASH_PIPE) + } + if !x.Prefix.IsEmpty() { + h.WriteString(x.Prefix.Value) + h.WriteByte(low.HASH_PIPE) + } + if !x.Attribute.IsEmpty() { + low.HashBool(h, x.Attribute.Value) + h.WriteByte(low.HASH_PIPE) + } + if !x.NodeType.IsEmpty() { + h.WriteString(x.NodeType.Value) + h.WriteByte(low.HASH_PIPE) + } + if !x.Wrapped.IsEmpty() { + low.HashBool(h, x.Wrapped.Value) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(x.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/extraction_functions.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/extraction_functions.go new file mode 100644 index 000000000..2fcc8e72d --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/extraction_functions.go @@ -0,0 +1,1493 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package low + +import ( + "context" + "errors" + "fmt" + "hash/maphash" + "net/url" + "os" + "path/filepath" + "reflect" + "sort" + "strconv" + "strings" + "sync" + + jsonpathconfig "github.com/pb33f/jsonpath/pkg/jsonpath/config" + + "github.com/pb33f/jsonpath/pkg/jsonpath" + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// stringBuilderPool is a sync.Pool that reuses strings.Builder instances to reduce memory allocations +// when generating hashes across the codebase. +var stringBuilderPool = sync.Pool{ + New: func() interface{} { + return new(strings.Builder) + }, +} + +// hashCache is a global cache for computed hash values to avoid redundant calculations. +// Uses sync.Map for thread-safe concurrent access. +var hashCache sync.Map + +// ErrExternalRefSkipped is returned by LocateRefNodeWithContext when +// SkipExternalRefResolution is enabled and the reference is external. +var ErrExternalRefSkipped = errors.New("external reference resolution skipped") + +// ClearHashCache clears the global hash cache. This should be called before +// starting a new document comparison to ensure clean state. +func ClearHashCache() { + hashCache = sync.Map{} +} + +// GetStringBuilder retrieves a strings.Builder from the pool, resets it, and returns it. +// The caller must call PutStringBuilder when done to return it to the pool. +func GetStringBuilder() *strings.Builder { + sb := stringBuilderPool.Get().(*strings.Builder) + sb.Reset() + return sb +} + +// PutStringBuilder returns a strings.Builder to the pool for reuse. +func PutStringBuilder(sb *strings.Builder) { + stringBuilderPool.Put(sb) +} + +// FindItemInOrderedMap accepts a string key and a collection of KeyReference[string] and ValueReference[T]. +// Every KeyReference will have its value checked against the string key and if there is a match, it will be +// returned. +func FindItemInOrderedMap[T any](item string, collection *orderedmap.Map[KeyReference[string], ValueReference[T]]) *ValueReference[T] { + _, v := FindItemInOrderedMapWithKey(item, collection) + return v +} + +// FindItemInOrderedMapWithKey is the same as FindItemInOrderedMap, except this code returns the key as well as the value. +func FindItemInOrderedMapWithKey[T any](item string, collection *orderedmap.Map[KeyReference[string], ValueReference[T]]) (*KeyReference[string], *ValueReference[T]) { + for pair := orderedmap.First(collection); pair != nil; pair = pair.Next() { + n := pair.Key() + if n.Value == item { + return &n, pair.ValuePtr() + } + if strings.EqualFold(item, n.Value) { + return &n, pair.ValuePtr() + } + } + return nil, nil +} + +// HashExtensions will generate a hash from the low representation of extensions. +func HashExtensions(ext *orderedmap.Map[KeyReference[string], ValueReference[*yaml.Node]]) []string { + f := []string{} + + for e, node := range orderedmap.SortAlpha(ext).FromOldest() { + // Use content-only hash (not index.HashNode which includes line/column) + f = append(f, fmt.Sprintf("%s-%s", e.Value, hashYamlNodeFast(node.GetValue()))) + } + + return f +} + +// helper function to generate a list of all the things an index should be searched for. +func generateIndexCollection(idx *index.SpecIndex) []func() map[string]*index.Reference { + return []func() map[string]*index.Reference{ + idx.GetAllComponentSchemas, + idx.GetMappedReferences, + idx.GetAllExternalDocuments, + idx.GetAllParameters, + idx.GetAllHeaders, + idx.GetAllCallbacks, + idx.GetAllLinks, + idx.GetAllExamples, + idx.GetAllRequestBodies, + idx.GetAllResponses, + idx.GetAllSecuritySchemes, + } +} + +func LocateRefNodeWithContext(ctx context.Context, root *yaml.Node, idx *index.SpecIndex) (*yaml.Node, *index.SpecIndex, error, context.Context) { + if rf, _, rv := utils.IsNodeRefValue(root); rf { + + if rv == "" { + return nil, nil, fmt.Errorf("reference at line %d, column %d is empty, it cannot be resolved", + root.Line, root.Column), ctx + } + + if idx != nil && idx.GetConfig() != nil && idx.GetConfig().SkipExternalRefResolution && utils.IsExternalRef(rv) { + return nil, idx, ErrExternalRefSkipped, ctx + } + + origRef := rv + resolvedRef := rv + if scope := index.GetSchemaIdScope(ctx); scope != nil && scope.BaseUri != "" { + if resolved, err := index.ResolveRefAgainstSchemaId(rv, scope); err == nil && resolved != "" { + resolvedRef = resolved + } + } + searchRefs := []string{origRef} + if resolvedRef != origRef { + searchRefs = append(searchRefs, resolvedRef) + } + + // run through everything and return as soon as we find a match. + // this operates as fast as possible as ever + collections := generateIndexCollection(idx) + var found map[string]*index.Reference + for _, collection := range collections { + found = collection() + if found != nil { + for _, candidate := range searchRefs { + if found[candidate] == nil { + continue + } + foundRef := found[candidate] + foundIndex := idx + if foundRef.Index != nil { + foundIndex = foundRef.Index + } + if foundIndex != nil && foundRef.RemoteLocation != "" && + foundIndex.GetSpecAbsolutePath() != foundRef.RemoteLocation { + if rolo := foundIndex.GetRolodex(); rolo != nil { + for _, candidateIdx := range append(rolo.GetIndexes(), rolo.GetRootIndex()) { + if candidateIdx == nil { + continue + } + if candidateIdx.GetSpecAbsolutePath() == foundRef.RemoteLocation { + foundIndex = candidateIdx + break + } + } + } + } + foundCtx := ctx + if foundRef.RemoteLocation != "" { + foundCtx = context.WithValue(foundCtx, index.CurrentPathKey, foundRef.RemoteLocation) + } + foundCtx = applyResolvedSchemaIdScope(foundCtx, foundRef, foundIndex) + // if this is a ref node, we need to keep diving + // until we hit something that isn't a ref. + if jh, _, _ := utils.IsNodeRefValue(foundRef.Node); jh { + // if this node is circular, stop drop and roll. + if !IsCircular(foundRef.Node, foundIndex) && foundRef.Node != root { + return LocateRefNodeWithContext(foundCtx, foundRef.Node, foundIndex) + } + + crr := GetCircularReferenceResult(foundRef.Node, foundIndex) + jp := "" + if crr != nil { + jp = crr.GenerateJourneyPath() + } + return foundRef.Node, foundIndex, fmt.Errorf("circular reference '%s' found during lookup at line "+ + "%d, column %d, It cannot be resolved", + jp, + foundRef.Node.Line, + foundRef.Node.Column), foundCtx + } + return utils.NodeAlias(foundRef.Node), foundIndex, nil, foundCtx + } + } + } + + rv = resolvedRef + + // Obtain the absolute filepath/URL of the spec in which we are trying to + // resolve the reference value [rv] from. It's either available from the + // index or passed down through context. + specPath := idx.GetSpecAbsolutePath() + if ctx.Value(index.CurrentPathKey) != nil { + specPath = ctx.Value(index.CurrentPathKey).(string) + } + + // explodedRefValue contains both the path to the file containing the + // reference value at index 0 and the path within that file to a specific + // sub-schema, should it exist, at index 1. + explodedRefValue := strings.Split(rv, "#") + if len(explodedRefValue) == 2 { + // The ref points to a component within either this file or another file. + if !strings.HasPrefix(explodedRefValue[0], "http") { + // The ref is not an absolute URL. + if !filepath.IsAbs(explodedRefValue[0]) { + // The ref is not an absolute local file path. + if strings.HasPrefix(specPath, "http") { + // The schema containing the ref is itself a remote file. + u, _ := url.Parse(specPath) + // p is the directory the referenced file is expected to be in. + p := "" + if u.Path != "" && explodedRefValue[0] != "" { + // We are using the path of the resolved URL from the rolodex to + // obtain the "folder" or base of the file URL. + p = filepath.Dir(u.Path) + } + if p != "" && explodedRefValue[0] != "" { + // We are resolving the relative URL against the absolute URL of + // the spec containing the reference. + u.Path = utils.ReplaceWindowsDriveWithLinuxPath(utils.CheckPathOverlap(p, explodedRefValue[0], string(os.PathSeparator))) + } + u.Fragment = "" + // Turn the reference value [rv] into the absolute filepath/URL we + // resolved. + rv = fmt.Sprintf("%s#%s", u.String(), explodedRefValue[1]) + } else { + // The schema containing the ref is a local file or doesn't have an + // absolute URL. + if specPath != "" { + // We have _some_ path for the schema containing the reference. + var abs string + if explodedRefValue[0] == "" { + // Reference is made within the schema file, so we are using the + // same absolute local filepath. + abs = specPath + } else { + // break off any fragments from the spec path + sp := strings.Split(specPath, "#") + // Create a clean (absolute?) path to the file containing the + // referenced value. + abs = idx.ResolveRelativeFilePath(filepath.Dir(sp[0]), explodedRefValue[0]) + } + rv = fmt.Sprintf("%s#%s", abs, explodedRefValue[1]) + } else { + // We don't have a path for the schema we are trying to resolve + // relative references from. This likely happens when the schema + // is the root schema, i.e., the file given to libopenapi as an entry. + // + + // check for a config BaseURL and use that if it exists. + if idx.GetConfig() != nil && idx.GetConfig().BaseURL != nil { + u := *idx.GetConfig().BaseURL + p := "" + if u.Path != "" { + p = u.Path + } + + u.Path = utils.ReplaceWindowsDriveWithLinuxPath(utils.CheckPathOverlap(p, explodedRefValue[0], string(os.PathSeparator))) + rv = fmt.Sprintf("%s#%s", u.String(), explodedRefValue[1]) + } + } + } + } + } + } else { + if !strings.HasPrefix(explodedRefValue[0], "http") { + if !filepath.IsAbs(explodedRefValue[0]) { + if strings.HasPrefix(specPath, "http") { + u, _ := url.Parse(specPath) + p := filepath.Dir(u.Path) + abs, _ := filepath.Abs(utils.CheckPathOverlap(p, rv, string(os.PathSeparator))) + u.Path = utils.ReplaceWindowsDriveWithLinuxPath(abs) + rv = u.String() + + } else { + if specPath != "" { + + abs := idx.ResolveRelativeFilePath(filepath.Dir(specPath), rv) + rv = abs + + } else { + // check for a config baseURL and use that if it exists. + if idx.GetConfig().BaseURL != nil { + u := *idx.GetConfig().BaseURL + abs, _ := filepath.Abs(utils.CheckPathOverlap(u.Path, rv, string(os.PathSeparator))) + u.Path = utils.ReplaceWindowsDriveWithLinuxPath(abs) + rv = u.String() + } + } + } + } + } + } + + foundRef, fIdx, newCtx := idx.SearchIndexForReferenceWithContext(ctx, rv) + if foundRef != nil { + newCtx = applyResolvedSchemaIdScope(newCtx, foundRef, fIdx) + return utils.NodeAlias(foundRef.Node), fIdx, nil, newCtx + } + + // let's try something else to find our references. + + // cant be found? last resort is to try a path lookup + _, friendly := utils.ConvertComponentIdIntoFriendlyPathSearch(rv) + if friendly != "" { + path, err := jsonpath.NewPath(friendly, jsonpathconfig.WithPropertyNameExtension()) + if err == nil { + nodes := path.Query(idx.GetRootNode()) + if len(nodes) > 0 { + return utils.NodeAlias(nodes[0]), idx, nil, ctx + } + } + } + return nil, idx, fmt.Errorf("reference '%s' at line %d, column %d was not found", + rv, root.Line, root.Column), ctx + } + return nil, idx, nil, ctx +} + +func applyResolvedSchemaIdScope(ctx context.Context, ref *index.Reference, idx *index.SpecIndex) context.Context { + if ref == nil || ref.Node == nil { + return ctx + } + idValue := index.FindSchemaIdInNode(ref.Node) + if idValue == "" { + return ctx + } + + scope := index.GetSchemaIdScope(ctx) + base := "" + if ref.RemoteLocation != "" { + base = ref.RemoteLocation + } else if idx != nil { + base = idx.GetSpecAbsolutePath() + } + if scope == nil { + scope = index.NewSchemaIdScope(base) + ctx = index.WithSchemaIdScope(ctx, scope) + } + + parentBase := scope.BaseUri + if parentBase == "" { + parentBase = base + } + resolved, err := index.ResolveSchemaId(idValue, parentBase) + if err != nil || resolved == "" { + resolved = idValue + } + updated := scope.Copy() + updated.PushId(resolved) + return index.WithSchemaIdScope(ctx, updated) +} + +// LocateRefNode will perform a complete lookup for a $ref node. This function searches the entire index for +// the reference being supplied. If there is a match found, the reference *yaml.Node is returned. +func LocateRefNode(root *yaml.Node, idx *index.SpecIndex) (*yaml.Node, *index.SpecIndex, error) { + r, i, e, _ := LocateRefNodeWithContext(context.Background(), root, idx) + return r, i, e +} + +// ExtractObjectRaw will extract a typed Buildable[N] object from a root yaml.Node. The 'raw' aspect is +// that there is no NodeReference wrapper around the result returned, just the raw object. +func ExtractObjectRaw[T Buildable[N], N any](ctx context.Context, key, root *yaml.Node, idx *index.SpecIndex) (T, error, bool, string) { + var circError error + var isReference bool + var referenceValue string + var refNode *yaml.Node + root = utils.NodeAlias(root) + if h, _, rv := utils.IsNodeRefValue(root); h { + ref, fIdx, err, nCtx := LocateRefNodeWithContext(ctx, root, idx) + if ref != nil { + refNode = root + root = ref + isReference = true + referenceValue = rv + idx = fIdx + ctx = nCtx + if err != nil { + circError = err + } + } else if errors.Is(err, ErrExternalRefSkipped) { + var n T = new(N) + SetReference(n, rv, root) + return n, nil, true, rv + } else { + if err != nil { + return nil, fmt.Errorf("object extraction failed: %s", err.Error()), isReference, referenceValue + } + } + } + var n T = new(N) + err := BuildModel(root, n) + if err != nil { + return n, err, isReference, referenceValue + } + err = n.Build(ctx, key, root, idx) + if err != nil { + return n, err, isReference, referenceValue + } + + // if this is a reference, keep track of the reference in the value + if isReference { + SetReference(n, referenceValue, refNode) + } + + // do we want to throw an error as well if circular error reporting is on? + if circError != nil && !idx.AllowCircularReferenceResolving() { + return n, circError, isReference, referenceValue + } + return n, nil, isReference, referenceValue +} + +// ExtractObject will extract a typed Buildable[N] object from a root yaml.Node. The result is wrapped in a +// NodeReference[T] that contains the key node found and value node found when looking up the reference. +func ExtractObject[T Buildable[N], N any](ctx context.Context, label string, root *yaml.Node, idx *index.SpecIndex) (NodeReference[T], error) { + var ln, vn *yaml.Node + var circError error + var isReference bool + var referenceValue string + var refNode *yaml.Node + root = utils.NodeAlias(root) + if rf, rl, refVal := utils.IsNodeRefValue(root); rf { + ref, fIdx, err, nCtx := LocateRefNodeWithContext(ctx, root, idx) + if ref != nil { + refNode = root + vn = ref + ln = rl + isReference = true + referenceValue = refVal + idx = fIdx + ctx = nCtx + if err != nil { + circError = err + } + } else if errors.Is(err, ErrExternalRefSkipped) { + var n T = new(N) + SetReference(n, refVal, root) + res := NodeReference[T]{Value: n, KeyNode: rl, ValueNode: root} + res.SetReference(refVal, root) + return res, nil + } else { + if err != nil { + return NodeReference[T]{}, fmt.Errorf("object extraction failed: %s", err.Error()) + } + } + } else { + _, ln, vn = utils.FindKeyNodeFull(label, root.Content) + if vn != nil { + if h, _, rVal := utils.IsNodeRefValue(vn); h { + ref, fIdx, lerr, nCtx := LocateRefNodeWithContext(ctx, vn, idx) + if ref != nil { + refNode = vn + vn = ref + if fIdx != nil { + idx = fIdx + } + ctx = nCtx + isReference = true + referenceValue = rVal + if lerr != nil { + circError = lerr + } + } else if errors.Is(lerr, ErrExternalRefSkipped) { + var n T = new(N) + SetReference(n, rVal, vn) + res := NodeReference[T]{Value: n, KeyNode: ln, ValueNode: vn} + res.SetReference(rVal, vn) + return res, nil + } else { + if lerr != nil { + return NodeReference[T]{}, fmt.Errorf("object extraction failed: %s", lerr.Error()) + } + } + } + } + } + var n T = new(N) + err := BuildModel(vn, n) + if err != nil { + return NodeReference[T]{}, err + } + if ln == nil { + return NodeReference[T]{}, nil + } + err = n.Build(ctx, ln, vn, idx) + if err != nil { + return NodeReference[T]{}, err + } + + // if this is a reference, keep track of the reference in the value + if isReference { + SetReference(n, referenceValue, refNode) + } + + res := NodeReference[T]{ + Value: n, + KeyNode: ln, + ValueNode: vn, + } + res.SetReference(referenceValue, refNode) + + // do we want to throw an error as well if circular error reporting is on? + if circError != nil && !idx.AllowCircularReferenceResolving() { + return res, circError + } + return res, nil +} + +func SetReference(obj any, ref string, refNode *yaml.Node) { + if obj == nil { + return + } + + // Ensure the embedded *Reference is initialized before calling SetReference. + // Buildable types embed *Reference (a pointer) which is nil after new(T). + // Calling SetReference on a nil *Reference would panic. + initEmbeddedReference(obj) + + if r, ok := obj.(SetReferencer); ok { + r.SetReference(ref, refNode) + } +} + +// initEmbeddedReference uses reflection to find and initialize a nil *Reference +// field embedded in obj. This is needed when objects are created via new(T) without +// calling Build(), which normally initializes the embedded *Reference. +func initEmbeddedReference(obj any) { + v := reflect.ValueOf(obj) + if v.Kind() != reflect.Ptr || v.IsNil() { + return + } + v = v.Elem() + if v.Kind() != reflect.Struct { + return + } + f := v.FieldByName("Reference") + if !f.IsValid() || f.Kind() != reflect.Ptr || !f.IsNil() { + return + } + if f.Type() == reflect.TypeOf((*Reference)(nil)) { + f.Set(reflect.ValueOf(new(Reference))) + } +} + +// ExtractArray will extract a slice of []ValueReference[T] from a root yaml.Node that is defined as a sequence. +// Used when the value being extracted is an array. +func ExtractArray[T Buildable[N], N any](ctx context.Context, label string, root *yaml.Node, idx *index.SpecIndex) ([]ValueReference[T], + *yaml.Node, *yaml.Node, error, +) { + var ln, vn *yaml.Node + var circError error + root = utils.NodeAlias(root) + isRef := false + if rf, rl, _ := utils.IsNodeRefValue(root); rf { + ref, fIdx, err, nCtx := LocateRefEnd(ctx, root, idx, 0) + if ref != nil { + isRef = true + vn = ref + ln = rl + idx = fIdx + ctx = nCtx + if err != nil { + circError = err + } + } else if errors.Is(err, ErrExternalRefSkipped) { + return []ValueReference[T]{}, rl, root, nil + } else { + return []ValueReference[T]{}, nil, nil, fmt.Errorf("array build failed: reference cannot be found: %s", + root.Content[1].Value) + } + } else { + _, ln, vn = utils.FindKeyNodeFullTop(label, root.Content) + if vn != nil { + if h, _, _ := utils.IsNodeRefValue(vn); h { + ref, fIdx, err, nCtx := LocateRefEnd(ctx, vn, idx, 0) + if ref != nil { + isRef = true + vn = ref + idx = fIdx + ctx = nCtx + if err != nil { + circError = err + } + } else if errors.Is(err, ErrExternalRefSkipped) { + return []ValueReference[T]{}, ln, vn, nil + } else { + if err != nil { + return []ValueReference[T]{}, nil, nil, + fmt.Errorf("array build failed: reference cannot be found: %s", + err.Error()) + } + } + } + } + } + + var items []ValueReference[T] + if vn != nil && ln != nil { + if !utils.IsNodeArray(vn) { + + if !isRef { + return []ValueReference[T]{}, nil, nil, + fmt.Errorf("array build failed, input is not an array, line %d, column %d", vn.Line, vn.Column) + } + // if this was pulled from a ref, but it's not a sequence, check the label and see if anything comes out, + // and then check that is a sequence, if not, fail it. + _, _, fvn := utils.FindKeyNodeFullTop(label, vn.Content) + if fvn != nil { + if !utils.IsNodeArray(vn) { + return []ValueReference[T]{}, nil, nil, + fmt.Errorf("array build failed, input is not an array, line %d, column %d", vn.Line, vn.Column) + } + } + } + for _, node := range vn.Content { + localReferenceValue := "" + foundCtx := ctx + foundIndex := idx + + var refNode *yaml.Node + + if rf, _, rv := utils.IsNodeRefValue(node); rf { + refg, fIdx, err, nCtx := LocateRefEnd(ctx, node, idx, 0) + if refg != nil { + refNode = node + node = refg + localReferenceValue = rv + foundIndex = fIdx + foundCtx = nCtx + if err != nil { + circError = err + } + } else if errors.Is(err, ErrExternalRefSkipped) { + var n T = new(N) + SetReference(n, rv, node) + v := ValueReference[T]{Value: n, ValueNode: node} + v.SetReference(rv, node) + items = append(items, v) + continue + } else { + if err != nil { + return []ValueReference[T]{}, nil, nil, fmt.Errorf("array build failed: reference cannot be found: %s", + err.Error()) + } + } + } + var n T = new(N) + err := BuildModel(node, n) + if err != nil { + return []ValueReference[T]{}, ln, vn, err + } + berr := n.Build(foundCtx, ln, node, foundIndex) + if berr != nil { + return nil, ln, vn, berr + } + + if localReferenceValue != "" { + SetReference(n, localReferenceValue, refNode) + } + + v := ValueReference[T]{ + Value: n, + ValueNode: node, + } + v.SetReference(localReferenceValue, refNode) + + items = append(items, v) + } + } + // include circular errors? + if circError != nil && !idx.AllowCircularReferenceResolving() { + return items, ln, vn, circError + } + return items, ln, vn, nil +} + +// ExtractMapNoLookupExtensions will extract a map of KeyReference and ValueReference from a root yaml.Node. The 'NoLookup' part +// refers to the fact that there is no key supplied as part of the extraction, there is no lookup performed and the +// root yaml.Node pointer is used directly. Pass a true bit to includeExtensions to include extension keys in the map. +// +// This is useful when the node to be extracted, is already known and does not require a search. +func ExtractMapNoLookupExtensions[PT Buildable[N], N any]( + ctx context.Context, + root *yaml.Node, + idx *index.SpecIndex, + includeExtensions bool, +) (*orderedmap.Map[KeyReference[string], ValueReference[PT]], error) { + valueMap := orderedmap.New[KeyReference[string], ValueReference[PT]]() + var circError error + if utils.IsNodeMap(root) { + var currentKey *yaml.Node + skip := false + rlen := len(root.Content) + + for i := 0; i < rlen; i++ { + node := root.Content[i] + if !includeExtensions { + if strings.HasPrefix(strings.ToLower(node.Value), "x-") { + skip = true + continue + } + } + if skip { + skip = false + continue + } + if i%2 == 0 { + currentKey = node + continue + } + + if currentKey.Tag == "!!merge" && currentKey.Value == "<<" { + root.Content = append(root.Content, utils.NodeAlias(node).Content...) + rlen = len(root.Content) + currentKey = nil + continue + } + node = utils.NodeAlias(node) + + foundIndex := idx + foundContext := ctx + + var isReference bool + var referenceValue string + var refNode *yaml.Node + // if value is a reference, we have to look it up in the index! + if h, _, rv := utils.IsNodeRefValue(node); h { + ref, fIdx, err, nCtx := LocateRefNodeWithContext(foundContext, node, foundIndex) + if ref != nil { + refNode = node + node = ref + isReference = true + referenceValue = rv + if fIdx != nil { + foundIndex = fIdx + } + foundContext = nCtx + if err != nil { + circError = err + } + } else if errors.Is(err, ErrExternalRefSkipped) { + var n PT = new(N) + SetReference(n, rv, node) + v := ValueReference[PT]{Value: n, ValueNode: node} + v.SetReference(rv, node) + valueMap.Set(KeyReference[string]{Value: currentKey.Value, KeyNode: currentKey}, v) + continue + } else { + if err != nil { + return nil, fmt.Errorf("map build failed: reference cannot be found: %s", err.Error()) + } + } + } + var n PT = new(N) + err := BuildModel(node, n) + if err != nil { + return nil, err + } + berr := n.Build(foundContext, currentKey, node, foundIndex) + if berr != nil { + return nil, berr + } + if isReference { + SetReference(n, referenceValue, refNode) + } + if currentKey != nil { + v := ValueReference[PT]{ + Value: n, + ValueNode: node, + } + v.SetReference(referenceValue, refNode) + + valueMap.Set( + KeyReference[string]{ + Value: currentKey.Value, + KeyNode: currentKey, + }, + v, + ) + } + } + } + if circError != nil && !idx.AllowCircularReferenceResolving() { + return valueMap, circError + } + return valueMap, nil +} + +// ExtractMapNoLookup will extract a map of KeyReference and ValueReference from a root yaml.Node. The 'NoLookup' part +// refers to the fact that there is no key supplied as part of the extraction, there is no lookup performed and the +// root yaml.Node pointer is used directly. +// +// This is useful when the node to be extracted, is already known and does not require a search. +func ExtractMapNoLookup[PT Buildable[N], N any]( + ctx context.Context, + root *yaml.Node, + idx *index.SpecIndex, +) (*orderedmap.Map[KeyReference[string], ValueReference[PT]], error) { + return ExtractMapNoLookupExtensions[PT, N](ctx, root, idx, false) +} + +type mappingResult[T any] struct { + k KeyReference[string] + v ValueReference[T] +} + +type buildInput struct { + label *yaml.Node + value *yaml.Node +} + +// ExtractMapExtensions will extract a map of KeyReference and ValueReference from a root yaml.Node. The 'label' is +// used to locate the node to be extracted from the root node supplied. Supply a bit to decide if extensions should +// be included or not. required in some use cases. +// +// The second return value is the yaml.Node found for the 'label' and the third return value is the yaml.Node +// found for the value extracted from the label node. +func ExtractMapExtensions[PT Buildable[N], N any]( + ctx context.Context, + label string, + root *yaml.Node, + idx *index.SpecIndex, + extensions bool, +) (*orderedmap.Map[KeyReference[string], ValueReference[PT]], *yaml.Node, *yaml.Node, error) { + var labelNode, valueNode *yaml.Node + var circError error + root = utils.NodeAlias(root) + foundIndex := idx + foundContext := ctx + if rf, rl, _ := utils.IsNodeRefValue(root); rf { + // locate reference in index. + ref, fIdx, err, fCtx := LocateRefNodeWithContext(ctx, root, idx) + if ref != nil { + valueNode = ref + labelNode = rl + foundContext = fCtx + foundIndex = fIdx + if err != nil { + circError = err + } + } else if errors.Is(err, ErrExternalRefSkipped) { + return nil, rl, root, nil + } else { + return nil, labelNode, valueNode, fmt.Errorf("map build failed: reference cannot be found: %s", + root.Content[1].Value) + } + } else { + _, labelNode, valueNode = utils.FindKeyNodeFull(label, root.Content) + valueNode = utils.NodeAlias(valueNode) + if valueNode != nil { + if h, _, _ := utils.IsNodeRefValue(valueNode); h { + ref, fIdx, err, nCtx := LocateRefNodeWithContext(ctx, valueNode, idx) + if ref != nil { + valueNode = ref + foundIndex = fIdx + foundContext = nCtx + if err != nil { + circError = err + } + } else if errors.Is(err, ErrExternalRefSkipped) { + return nil, labelNode, valueNode, nil + } else { + if err != nil { + return nil, labelNode, valueNode, fmt.Errorf("map build failed: reference cannot be found: %s", + err.Error()) + } + } + } + } + } + if valueNode != nil { + valueMap := orderedmap.New[KeyReference[string], ValueReference[PT]]() + + in := make(chan buildInput) + out := make(chan mappingResult[PT]) + done := make(chan struct{}) + var wg sync.WaitGroup + wg.Add(2) // input and output goroutines. + + // TranslatePipeline input. + go func() { + defer func() { + close(in) + wg.Done() + }() + var currentLabelNode *yaml.Node + for i, en := range valueNode.Content { + if !extensions { + if strings.HasPrefix(en.Value, "x-") { + continue // yo, don't pay any attention to extensions, not here anyway. + } + } + if currentLabelNode == nil && i%2 != 0 { + continue // we need a label node first, and we don't have one because of extensions. + } + + en = utils.NodeAlias(en) + if i%2 == 0 { + currentLabelNode = en + continue + } + + select { + case in <- buildInput{ + label: currentLabelNode, + value: en, + }: + case <-done: + return + } + } + }() + + // TranslatePipeline output. + go func() { + for { + result, ok := <-out + if !ok { + break + } + valueMap.Set(result.k, result.v) + } + close(done) + wg.Done() + }() + + startIdx := foundIndex + startCtx := foundContext + + translateFunc := func(input buildInput) (mappingResult[PT], error) { + en := input.value + + sCtx := startCtx + sIdx := startIdx + + var refNode *yaml.Node + var referenceValue string + // check our valueNode isn't a reference still. + if h, _, refVal := utils.IsNodeRefValue(en); h { + ref, fIdx, err, nCtx := LocateRefNodeWithContext(sCtx, en, sIdx) + if ref != nil { + refNode = en + en = ref + referenceValue = refVal + if fIdx != nil { + sIdx = fIdx + } + sCtx = nCtx + if err != nil { + circError = err + } + } else if errors.Is(err, ErrExternalRefSkipped) { + var n PT = new(N) + SetReference(n, refVal, en) + v := ValueReference[PT]{Value: n, ValueNode: en} + v.SetReference(refVal, en) + return mappingResult[PT]{ + k: KeyReference[string]{KeyNode: input.label, Value: input.label.Value}, + v: v, + }, nil + } else { + if err != nil { + return mappingResult[PT]{}, fmt.Errorf("flat map build failed: reference cannot be found: %s", + err.Error()) + } + } + } + + var n PT = new(N) + en = utils.NodeAlias(en) + _ = BuildModel(en, n) + err := n.Build(sCtx, input.label, en, sIdx) + if err != nil { + return mappingResult[PT]{}, err + } + + if referenceValue != "" { + SetReference(n, referenceValue, refNode) + } + + v := ValueReference[PT]{ + Value: n, + ValueNode: en, + } + v.SetReference(referenceValue, refNode) + + return mappingResult[PT]{ + k: KeyReference[string]{ + KeyNode: input.label, + Value: input.label.Value, + }, + v: v, + }, nil + } + + err := datamodel.TranslatePipeline[buildInput, mappingResult[PT]](in, out, translateFunc) + wg.Wait() + if err != nil { + return nil, labelNode, valueNode, err + } + if circError != nil && !idx.AllowCircularReferenceResolving() { + return valueMap, labelNode, valueNode, circError + } + return valueMap, labelNode, valueNode, nil + } + return nil, labelNode, valueNode, nil +} + +// ExtractMap will extract a map of KeyReference and ValueReference from a root yaml.Node. The 'label' is +// used to locate the node to be extracted from the root node supplied. +// +// The second return value is the yaml.Node found for the 'label' and the third return value is the yaml.Node +// found for the value extracted from the label node. +func ExtractMap[PT Buildable[N], N any]( + ctx context.Context, + label string, + root *yaml.Node, + idx *index.SpecIndex, +) (*orderedmap.Map[KeyReference[string], ValueReference[PT]], *yaml.Node, *yaml.Node, error) { + return ExtractMapExtensions[PT, N](ctx, label, root, idx, false) +} + +// ExtractExtensions will extract any 'x-' prefixed key nodes from a root node into a map. Requirements have been pre-cast: +// +// Maps +// +// *orderedmap.Map[string, *yaml.Node] for maps +// +// Slices +// +// []interface{} +// +// int, float, bool, string +// +// int64, float64, bool, string +func ExtractExtensions(root *yaml.Node) *orderedmap.Map[KeyReference[string], ValueReference[*yaml.Node]] { + root = utils.NodeAlias(root) + if root == nil { + return nil + } + extensions := utils.FindExtensionNodes(root.Content) + extensionMap := orderedmap.New[KeyReference[string], ValueReference[*yaml.Node]]() + for _, ext := range extensions { + extensionMap.Set(KeyReference[string]{ + Value: ext.Key.Value, + KeyNode: ext.Key, + }, ValueReference[*yaml.Node]{Value: ext.Value, ValueNode: ext.Value}) + } + return extensionMap +} + +// AreEqual returns true if two Hashable objects are equal or not. +func AreEqual(l, r Hashable) bool { + if l == nil || r == nil { + return false + } + vol := reflect.ValueOf(l) + vor := reflect.ValueOf(r) + + if vol.Kind() != reflect.Struct && vor.Kind() != reflect.Struct { + if vol.IsNil() || vor.IsNil() { + return false + } + } + return l.Hash() == r.Hash() +} + +// GenerateHashString will generate a SHA256 hash of any object passed in. If the object is Hashable +// then the underlying Hash() method will be called. Optimized to avoid excessive allocations and +// uses caching to eliminate redundant calculations. +func GenerateHashString(v any) string { + if v == nil { + return "" + } + + // Try cache first using the pointer as key for non-primitives + // However, skip caching for types with mutable hash state like SchemaProxy + val := reflect.ValueOf(v) + shouldCache := true + if val.Kind() == reflect.Ptr && !val.IsNil() { + // Check if this is a type that has mutable hash state or complex comparison logic + typeName := val.Type().String() + if typeName == "*base.SchemaProxy" || typeName == "*base.Schema" { + shouldCache = false + } + + if shouldCache { + cacheKey := val.Pointer() + if cached, ok := hashCache.Load(cacheKey); ok { + return cached.(string) + } + } + } + + var hashStr string + + if h, ok := v.(Hashable); ok { + if h != nil { + // Format uint64 hash as hex string + hash := h.Hash() + hashStr = strconv.FormatUint(hash, 16) + } + } else if n, ok := v.(*yaml.Node); ok { + // Fast path for common YAML node types to avoid marshaling + hashStr = hashYamlNodeFast(n) + } else { + // Primitive types + // if we get here, we're a primitive, check if we're a pointer and de-point + if val.Kind() == reflect.Ptr { + v = val.Elem().Interface() + } + + // Convert to string efficiently using strconv instead of fmt.Sprintf + var str string + switch val := v.(type) { + case string: + str = val + case int: + str = strconv.Itoa(val) + case int8: + str = strconv.FormatInt(int64(val), 10) + case int16: + str = strconv.FormatInt(int64(val), 10) + case int32: + str = strconv.FormatInt(int64(val), 10) + case int64: + str = strconv.FormatInt(val, 10) + case uint: + str = strconv.FormatUint(uint64(val), 10) + case uint8: + str = strconv.FormatUint(uint64(val), 10) + case uint16: + str = strconv.FormatUint(uint64(val), 10) + case uint32: + str = strconv.FormatUint(uint64(val), 10) + case uint64: + str = strconv.FormatUint(val, 10) + case float32: + str = strconv.FormatFloat(float64(val), 'g', -1, 32) + case float64: + str = strconv.FormatFloat(val, 'g', -1, 64) + case bool: + if val { + str = "true" + } else { + str = "false" + } + default: + str = fmt.Sprint(v) + } + + hashStr = strconv.FormatUint(maphash.String(globalHashSeed, str), 16) + } + + // Store in cache if we have a valid pointer and caching is enabled for this type + if shouldCache && val.Kind() == reflect.Ptr && !val.IsNil() && hashStr != "" { + cacheKey := val.Pointer() + hashCache.Store(cacheKey, hashStr) + } + + return hashStr +} + +// hashYamlNodeFast provides fast hashing for YAML nodes without ANY marshaling +func hashYamlNodeFast(n *yaml.Node) string { + if n == nil { + return "" + } + + // Try cache first for complex nodes + // Use pointer directly as key - *yaml.Node pointers are stable and comparable + if n.Kind != yaml.ScalarNode { + if cached, ok := hashCache.Load(n); ok { + return cached.(string) + } + } + + h := hasherPool.Get().(*maphash.Hash) + h.Reset() + visited := make(map[*yaml.Node]bool) + hashNodeTree(h, n, visited) + result := strconv.FormatUint(h.Sum64(), 16) + hasherPool.Put(h) + + // Cache complex nodes + if n.Kind != yaml.ScalarNode { + hashCache.Store(n, result) + } + + return result +} + +// hashNodeTree walks the YAML tree and hashes it without marshaling +func hashNodeTree(h *maphash.Hash, n *yaml.Node, visited map[*yaml.Node]bool) { + if n == nil { + return + } + + // Prevent circular reference infinite loops + if visited[n] { + h.Write([]byte("<>")) + return + } + visited[n] = true + + // Hash node metadata + h.Write([]byte{byte(n.Kind)}) + h.Write([]byte(n.Tag)) + h.Write([]byte(n.Value)) + if n.Anchor != "" { + h.Write([]byte(n.Anchor)) + } + + // CRITICAL: Snapshot Content to prevent TOCTOU races + // This captures the slice header (pointer, len, cap) atomically. + // Even if another goroutine reassigns n.Content later, our local + // 'content' variable still refers to the original backing array. + content := n.Content + + // Hash based on node type + switch n.Kind { + case yaml.ScalarNode: + // Already hashed value above + + case yaml.SequenceNode: + h.Write([]byte("[")) + for _, child := range content { + hashNodeTree(h, child, visited) + h.Write([]byte(",")) + } + h.Write([]byte("]")) + + case yaml.MappingNode: + h.Write([]byte("{")) + + // Guard against empty mapping nodes + if len(content) == 0 { + h.Write([]byte("}")) + return + } + + // For maps, we need consistent ordering + // Collect key-value pairs and sort by key hash + type kvPair struct { + keyHash uint64 + keyNode *yaml.Node + valueNode *yaml.Node + } + pairs := make([]kvPair, 0, len(content)/2) + + for i := 0; i < len(content); i += 2 { + if i+1 < len(content) { + keyH := hasherPool.Get().(*maphash.Hash) + keyH.Reset() + hashNodeTree(keyH, content[i], visited) + pairs = append(pairs, kvPair{ + keyHash: keyH.Sum64(), + keyNode: content[i], + valueNode: content[i+1], + }) + hasherPool.Put(keyH) + } + } + + // Sort for consistent hashing + sort.Slice(pairs, func(i, j int) bool { + return pairs[i].keyHash < pairs[j].keyHash + }) + + // Hash in sorted order + for _, pair := range pairs { + hashNodeTree(h, pair.keyNode, visited) + h.Write([]byte(":")) + hashNodeTree(h, pair.valueNode, visited) + h.Write([]byte(",")) + } + h.Write([]byte("}")) + + case yaml.DocumentNode: + h.Write([]byte("DOC[")) + for _, child := range content { + hashNodeTree(h, child, visited) + } + h.Write([]byte("]")) + + case yaml.AliasNode: + h.Write([]byte("ALIAS[")) + if n.Alias != nil { + hashNodeTree(h, n.Alias, visited) + } + h.Write([]byte("]")) + } +} + +// CompareYAMLNodes compares two YAML nodes for equality without marshaling to YAML. +// This reuses the hashNodeTree logic to generate consistent hashes for comparison, +// avoiding the expensive yaml.Marshal operations that cause massive allocations. +func CompareYAMLNodes(left, right *yaml.Node) bool { + if left == nil && right == nil { + return true + } + if left == nil || right == nil { + return false + } + + leftH := hasherPool.Get().(*maphash.Hash) + leftH.Reset() + rightH := hasherPool.Get().(*maphash.Hash) + rightH.Reset() + + leftVisited := make(map[*yaml.Node]bool) + rightVisited := make(map[*yaml.Node]bool) + + hashNodeTree(leftH, left, leftVisited) + hashNodeTree(rightH, right, rightVisited) + + result := leftH.Sum64() == rightH.Sum64() + + hasherPool.Put(leftH) + hasherPool.Put(rightH) + return result +} + +// YAMLNodeToBytes converts a YAML node to bytes in a more efficient way than yaml.Marshal +// This function should be used when you actually need the marshaled bytes (like for JSON conversion) +// rather than just comparing nodes (use CompareYAMLNodes for that) +func YAMLNodeToBytes(n *yaml.Node) ([]byte, error) { + if n == nil { + return nil, nil + } + // For now, we still use yaml.Marshal for cases that actually need the bytes + // This can be optimized further in the future with a custom serializer + return yaml.Marshal(n) +} + +// HashYAMLNodeSlice creates a hash for a slice of YAML nodes efficiently +// This replaces the pattern of yaml.Marshal + sha256 that's used in example comparisons +func HashYAMLNodeSlice(nodes []*yaml.Node) string { + if len(nodes) == 0 { + return "" + } + + h := hasherPool.Get().(*maphash.Hash) + h.Reset() + visited := make(map[*yaml.Node]bool) + + for _, node := range nodes { + hashNodeTree(h, node, visited) + } + + result := strconv.FormatUint(h.Sum64(), 16) + hasherPool.Put(h) + return result +} + +// AppendMapHashes will append all the hashes of a map to a slice of strings. +// Optimized to avoid creating sorted copies on every call. +func AppendMapHashes[v any](a []string, m *orderedmap.Map[KeyReference[string], ValueReference[v]]) []string { + if m == nil { + return a + } + + // Pre-allocate slice for better performance when we know the size + if cap(a)-len(a) < m.Len() { + newA := make([]string, len(a), len(a)+m.Len()) + copy(newA, a) + a = newA + } + + // Collect entries and sort them by key for consistent hashing + // This is more efficient than orderedmap.SortAlpha() which creates a full copy + type entry struct { + key string + value v + } + entries := make([]entry, 0, m.Len()) + + for k, v := range m.FromOldest() { + entries = append(entries, entry{ + key: k.Value, + value: v.Value, + }) + } + + // Sort entries by key for consistent hash ordering + // Use a simple insertion sort for small maps, quicksort for larger ones + if len(entries) <= 10 { + // Insertion sort for small maps + for i := 1; i < len(entries); i++ { + key := entries[i] + j := i - 1 + for j >= 0 && entries[j].key > key.key { + entries[j+1] = entries[j] + j-- + } + entries[j+1] = key + } + } else { + // Use Go's built-in sort for larger maps + sort.Slice(entries, func(i, j int) bool { + return entries[i].key < entries[j].key + }) + } + + // For small maps, avoid string builder overhead and use direct string concatenation + if len(entries) <= 5 { + for _, entry := range entries { + hashStr := entry.key + "-" + GenerateHashString(entry.value) + a = append(a, hashStr) + } + } else { + // Use string builder for larger maps with pre-allocated capacity + sb := GetStringBuilder() + defer PutStringBuilder(sb) + + for _, entry := range entries { + sb.Reset() + // Pre-size for this specific entry to avoid growth + expectedLen := len(entry.key) + 64 + 1 // key + hash + separator + sb.Grow(expectedLen) + sb.WriteString(entry.key) + sb.WriteByte('-') + sb.WriteString(GenerateHashString(entry.value)) + a = append(a, sb.String()) + } + } + + return a +} + +func ValueToString(v any) string { + if n, ok := v.(*yaml.Node); ok { + // For simple scalar nodes, return the value directly + if n.Kind == yaml.ScalarNode { + return n.Value + } + // For complex nodes, still need to marshal for string representation + b, _ := yaml.Marshal(n) + return string(b) + } + + return fmt.Sprint(v) +} + +// LocateRefEnd will perform a complete lookup for a $ref node. This function searches the entire index for +// the reference being supplied. If there is a match found, the reference *yaml.Node is returned. +// the function operates recursively and will keep iterating through references until it finds a non-reference +// node. +func LocateRefEnd(ctx context.Context, root *yaml.Node, idx *index.SpecIndex, depth int) (*yaml.Node, *index.SpecIndex, error, context.Context) { + depth++ + if depth > 100 { + return nil, nil, fmt.Errorf("reference resolution depth exceeded, possible circular reference"), ctx + } + ref, fIdx, err, nCtx := LocateRefNodeWithContext(ctx, root, idx) + if err != nil { + return ref, fIdx, err, nCtx + } + if rf, _, _ := utils.IsNodeRefValue(ref); rf { + return LocateRefEnd(nCtx, ref, fIdx, depth) + } else { + return ref, fIdx, err, nCtx + } +} + +// FromReferenceMap will convert a *orderedmap.Map[KeyReference[K], ValueReference[V]] to a *orderedmap.Map[K, V] +func FromReferenceMap[K comparable, V any](refMap *orderedmap.Map[KeyReference[K], ValueReference[V]]) *orderedmap.Map[K, V] { + om := orderedmap.New[K, V]() + for k, v := range refMap.FromOldest() { + om.Set(k.Value, v.Value) + } + return om +} + +// FromReferenceMapWithFunc will convert a *orderedmap.Map[KeyReference[K], ValueReference[V]] to a *orderedmap.Map[K, VOut] using a transform function +func FromReferenceMapWithFunc[K comparable, V any, VOut any](refMap *orderedmap.Map[KeyReference[K], ValueReference[V]], transform func(v V) VOut) *orderedmap.Map[K, VOut] { + om := orderedmap.New[K, VOut]() + for k, v := range refMap.FromOldest() { + om.Set(k.Value, transform(v.Value)) + } + return om +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/hash.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/hash.go new file mode 100644 index 000000000..37f2a5170 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/hash.go @@ -0,0 +1,64 @@ +// Copyright 2022-2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package low + +import ( + "encoding/binary" + "hash/maphash" + "sync" +) + +// globalHashSeed ensures consistent hashes across all pooled instances. +// Set once at init, deterministic within a process run. +var globalHashSeed maphash.Seed + +func init() { + globalHashSeed = maphash.MakeSeed() +} + +// hasherPool pools maphash.Hash instances for reuse +var hasherPool = sync.Pool{ + New: func() any { + h := &maphash.Hash{} + h.SetSeed(globalHashSeed) + return h + }, +} + +// WithHasher provides a pooled hasher for the duration of fn. +// The hasher is automatically returned to the pool after fn completes. +// This pattern eliminates forgotten PutHasher() bugs. +func WithHasher(fn func(h *maphash.Hash) uint64) uint64 { + hasher := hasherPool.Get().(*maphash.Hash) + hasher.Reset() + result := fn(hasher) + hasherPool.Put(hasher) + return result +} + +// HashBool writes a boolean as a single byte. +func HashBool(h *maphash.Hash, b bool) { + if b { + h.WriteByte(1) + } else { + h.WriteByte(0) + } +} + +// HashInt64 writes an int64 without allocation using binary encoding. +func HashInt64(h *maphash.Hash, n int64) { + var buf [8]byte + binary.LittleEndian.PutUint64(buf[:], uint64(n)) + h.Write(buf[:]) +} + +// HashUint64 writes another hash value (for composition of nested Hashable objects). +func HashUint64(h *maphash.Hash, v uint64) { + var buf [8]byte + binary.LittleEndian.PutUint64(buf[:], v) + h.Write(buf[:]) +} + +// HASH_PIPE is the separator byte used between hash fields. :) +const HASH_PIPE = '|' diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/low.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/low.go new file mode 100644 index 000000000..b6dff8a84 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/low.go @@ -0,0 +1,22 @@ +// Copyright 2022-2004 Princess B33f Heavy Industries / Dave Shanley / Quobix +// SPDX-License-Identifier: MIT + +// Package low contains a set of low-level models that represent OpenAPI 2 and 3 documents. +// These low-level models (plumbing) are used to create high-level models, and used when deep knowledge +// about the original data, positions, comments and the original node structures. +// +// Low-level models are not designed to be easily navigated, every single property is either a NodeReference +// an KeyReference or a ValueReference. These references hold the raw value and key or value nodes that contain +// the original yaml.Node trees that make up the object. +// +// Navigating maps that use a KeyReference as a key is tricky, because there is no easy way to provide a lookup. +// Convenience methods for lookup up properties in a low-level model have therefore been provided. +package low + +import "go.yaml.in/yaml/v4" + +// HasRootNode is an interface that is used to extract the root yaml.Node from a low-level model. The root node is +// the top-level node that represents the entire object as represented in the original source file. +type HasRootNode interface { + GetRootNode() *yaml.Node +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/model_builder.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/model_builder.go new file mode 100644 index 000000000..491b43e8c --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/model_builder.go @@ -0,0 +1,461 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package low + +import ( + "fmt" + "reflect" + "strconv" + "sync" + + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// BuildModel accepts a yaml.Node pointer and a model, which can be any struct. Using reflection, the model is +// analyzed and the names of all the properties are extracted from the model and subsequently looked up from within +// the yaml.Node.Content value. +// +// BuildModel is non-recursive and will only build out a single layer of the node tree. +func BuildModel(node *yaml.Node, model interface{}) error { + if node == nil { + return nil + } + node = utils.NodeAlias(node) + utils.CheckForMergeNodes(node) + + if reflect.ValueOf(model).Type().Kind() != reflect.Pointer { + return fmt.Errorf("cannot build model on non-pointer: %v", reflect.ValueOf(model).Type().Kind()) + } + v := reflect.ValueOf(model).Elem() + num := v.NumField() + for i := 0; i < num; i++ { + + fName := v.Type().Field(i).Name + + if fName == "Extensions" { + continue // internal construct + } + + if fName == "PathItems" { + continue // internal construct + } + + kn, vn := utils.FindKeyNodeTop(fName, node.Content) + if vn == nil { + // no point in going on. + continue + } + + field := v.FieldByName(fName) + kind := field.Kind() + switch kind { + case reflect.Struct, reflect.Slice, reflect.Map, reflect.Pointer: + vn = utils.NodeAlias(vn) + SetField(&field, vn, kn) + default: + return fmt.Errorf("unable to parse unsupported type: %v", kind) + } + + } + + return nil +} + +// SetField accepts a field reflection value, a yaml.Node valueNode and a yaml.Node keyNode. Using reflection, the +// function will attempt to set the value of the field based on the key and value nodes. This method is only useful +// for low-level models, it has no value to high-level ones. +func SetField(field *reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) { + if valueNode == nil { + return + } + + switch field.Type() { + + case reflect.TypeOf(orderedmap.New[string, NodeReference[*yaml.Node]]()): + + if utils.IsNodeMap(valueNode) { + if field.CanSet() { + items := orderedmap.New[string, NodeReference[*yaml.Node]]() + var currentLabel string + for i, sliceItem := range valueNode.Content { + if i%2 == 0 { + currentLabel = sliceItem.Value + continue + } + items.Set(currentLabel, NodeReference[*yaml.Node]{ + Value: sliceItem, + ValueNode: sliceItem, + KeyNode: valueNode, + }) + } + field.Set(reflect.ValueOf(items)) + } + } + + case reflect.TypeOf(orderedmap.New[string, NodeReference[string]]()): + + if utils.IsNodeMap(valueNode) { + if field.CanSet() { + items := orderedmap.New[string, NodeReference[string]]() + var currentLabel string + for i, sliceItem := range valueNode.Content { + if i%2 == 0 { + currentLabel = sliceItem.Value + continue + } + items.Set(currentLabel, NodeReference[string]{ + Value: fmt.Sprintf("%v", sliceItem.Value), + ValueNode: sliceItem, + KeyNode: valueNode, + }) + } + field.Set(reflect.ValueOf(items)) + } + } + + case reflect.TypeOf(NodeReference[*yaml.Node]{}): + + if field.CanSet() { + or := NodeReference[*yaml.Node]{Value: valueNode, ValueNode: valueNode, KeyNode: keyNode} + field.Set(reflect.ValueOf(or)) + } + + case reflect.TypeOf([]NodeReference[*yaml.Node]{}): + + if utils.IsNodeArray(valueNode) { + if field.CanSet() { + var items []NodeReference[*yaml.Node] + for _, sliceItem := range valueNode.Content { + items = append(items, NodeReference[*yaml.Node]{ + Value: sliceItem, + ValueNode: sliceItem, + KeyNode: valueNode, + }) + } + field.Set(reflect.ValueOf(items)) + } + } + + case reflect.TypeOf(NodeReference[string]{}): + + if field.CanSet() { + nr := NodeReference[string]{ + Value: fmt.Sprintf("%v", valueNode.Value), + ValueNode: valueNode, + KeyNode: keyNode, + } + field.Set(reflect.ValueOf(nr)) + } + + case reflect.TypeOf(ValueReference[string]{}): + + if field.CanSet() { + nr := ValueReference[string]{ + Value: fmt.Sprintf("%v", valueNode.Value), + ValueNode: valueNode, + } + field.Set(reflect.ValueOf(nr)) + } + + case reflect.TypeOf(NodeReference[bool]{}): + + if utils.IsNodeBoolValue(valueNode) { + if field.CanSet() { + bv, _ := strconv.ParseBool(valueNode.Value) + nr := NodeReference[bool]{ + Value: bv, + ValueNode: valueNode, + KeyNode: keyNode, + } + field.Set(reflect.ValueOf(nr)) + } + } + + case reflect.TypeOf(NodeReference[int]{}): + + if utils.IsNodeIntValue(valueNode) { + if field.CanSet() { + fv, _ := strconv.Atoi(valueNode.Value) + nr := NodeReference[int]{ + Value: fv, + ValueNode: valueNode, + KeyNode: keyNode, + } + field.Set(reflect.ValueOf(nr)) + } + } + + case reflect.TypeOf(NodeReference[int64]{}): + + if utils.IsNodeIntValue(valueNode) || utils.IsNodeFloatValue(valueNode) { + if field.CanSet() { + fv, _ := strconv.ParseInt(valueNode.Value, 10, 64) + nr := NodeReference[int64]{ + Value: fv, + ValueNode: valueNode, + KeyNode: keyNode, + } + field.Set(reflect.ValueOf(nr)) + } + } + + case reflect.TypeOf(NodeReference[float32]{}): + + if utils.IsNodeNumberValue(valueNode) { + if field.CanSet() { + fv, _ := strconv.ParseFloat(valueNode.Value, 32) + nr := NodeReference[float32]{ + Value: float32(fv), + ValueNode: valueNode, + KeyNode: keyNode, + } + field.Set(reflect.ValueOf(nr)) + } + } + + case reflect.TypeOf(NodeReference[float64]{}): + + if utils.IsNodeNumberValue(valueNode) { + if field.CanSet() { + fv, _ := strconv.ParseFloat(valueNode.Value, 64) + nr := NodeReference[float64]{ + Value: fv, + ValueNode: valueNode, + KeyNode: keyNode, + } + field.Set(reflect.ValueOf(nr)) + } + } + + case reflect.TypeOf([]NodeReference[string]{}): + + if utils.IsNodeArray(valueNode) { + if field.CanSet() { + var items []NodeReference[string] + for _, sliceItem := range valueNode.Content { + items = append(items, NodeReference[string]{ + Value: sliceItem.Value, + ValueNode: sliceItem, + KeyNode: valueNode, + }) + } + field.Set(reflect.ValueOf(items)) + } + } + + case reflect.TypeOf([]NodeReference[float32]{}): + + if utils.IsNodeArray(valueNode) { + if field.CanSet() { + var items []NodeReference[float32] + for _, sliceItem := range valueNode.Content { + fv, _ := strconv.ParseFloat(sliceItem.Value, 32) + items = append(items, NodeReference[float32]{ + Value: float32(fv), + ValueNode: sliceItem, + KeyNode: valueNode, + }) + } + field.Set(reflect.ValueOf(items)) + } + } + + case reflect.TypeOf([]NodeReference[float64]{}): + + if utils.IsNodeArray(valueNode) { + if field.CanSet() { + var items []NodeReference[float64] + for _, sliceItem := range valueNode.Content { + fv, _ := strconv.ParseFloat(sliceItem.Value, 64) + items = append(items, NodeReference[float64]{Value: fv, ValueNode: sliceItem}) + } + field.Set(reflect.ValueOf(items)) + } + } + + case reflect.TypeOf([]NodeReference[int]{}): + + if utils.IsNodeArray(valueNode) { + if field.CanSet() { + var items []NodeReference[int] + for _, sliceItem := range valueNode.Content { + iv, _ := strconv.Atoi(sliceItem.Value) + items = append(items, NodeReference[int]{ + Value: iv, + ValueNode: sliceItem, + KeyNode: valueNode, + }) + } + field.Set(reflect.ValueOf(items)) + } + } + + case reflect.TypeOf([]NodeReference[int64]{}): + + if utils.IsNodeArray(valueNode) { + if field.CanSet() { + var items []NodeReference[int64] + for _, sliceItem := range valueNode.Content { + iv, _ := strconv.ParseInt(sliceItem.Value, 10, 64) + items = append(items, NodeReference[int64]{ + Value: iv, + ValueNode: sliceItem, + KeyNode: valueNode, + }) + } + field.Set(reflect.ValueOf(items)) + } + } + + case reflect.TypeOf([]NodeReference[bool]{}): + + if utils.IsNodeArray(valueNode) { + if field.CanSet() { + var items []NodeReference[bool] + for _, sliceItem := range valueNode.Content { + bv, _ := strconv.ParseBool(sliceItem.Value) + items = append(items, NodeReference[bool]{ + Value: bv, + ValueNode: sliceItem, + KeyNode: valueNode, + }) + } + field.Set(reflect.ValueOf(items)) + } + } + + // helper for unpacking string maps. + case reflect.TypeOf(orderedmap.New[KeyReference[string], ValueReference[string]]()): + + if utils.IsNodeMap(valueNode) { + if field.CanSet() { + items := orderedmap.New[KeyReference[string], ValueReference[string]]() + var cf *yaml.Node + for i, sliceItem := range valueNode.Content { + if i%2 == 0 { + cf = sliceItem + continue + } + items.Set(KeyReference[string]{ + Value: cf.Value, + KeyNode: cf, + }, ValueReference[string]{ + Value: sliceItem.Value, + ValueNode: sliceItem, + }) + } + field.Set(reflect.ValueOf(items)) + } + } + + case reflect.TypeOf(KeyReference[*orderedmap.Map[KeyReference[string], ValueReference[string]]]{}): + + if utils.IsNodeMap(valueNode) { + if field.CanSet() { + items := orderedmap.New[KeyReference[string], ValueReference[string]]() + var cf *yaml.Node + for i, sliceItem := range valueNode.Content { + if i%2 == 0 { + cf = sliceItem + continue + } + items.Set(KeyReference[string]{ + Value: cf.Value, + KeyNode: cf, + }, ValueReference[string]{ + Value: sliceItem.Value, + ValueNode: sliceItem, + }) + } + ref := KeyReference[*orderedmap.Map[KeyReference[string], ValueReference[string]]]{ + Value: items, + KeyNode: keyNode, + } + field.Set(reflect.ValueOf(ref)) + } + } + case reflect.TypeOf(NodeReference[*orderedmap.Map[KeyReference[string], ValueReference[string]]]{}): + if utils.IsNodeMap(valueNode) { + if field.CanSet() { + items := orderedmap.New[KeyReference[string], ValueReference[string]]() + var cf *yaml.Node + for i, sliceItem := range valueNode.Content { + if i%2 == 0 { + cf = sliceItem + continue + } + items.Set(KeyReference[string]{ + Value: cf.Value, + KeyNode: cf, + }, ValueReference[string]{ + Value: sliceItem.Value, + ValueNode: sliceItem, + }) + } + ref := NodeReference[*orderedmap.Map[KeyReference[string], ValueReference[string]]]{ + Value: items, + KeyNode: keyNode, + ValueNode: valueNode, + } + field.Set(reflect.ValueOf(ref)) + } + } + case reflect.TypeOf(NodeReference[[]ValueReference[string]]{}): + + if utils.IsNodeArray(valueNode) { + if field.CanSet() { + var items []ValueReference[string] + for _, sliceItem := range valueNode.Content { + items = append(items, ValueReference[string]{ + Value: sliceItem.Value, + ValueNode: sliceItem, + }) + } + n := NodeReference[[]ValueReference[string]]{ + Value: items, + KeyNode: keyNode, + ValueNode: valueNode, + } + field.Set(reflect.ValueOf(n)) + } + } + + case reflect.TypeOf(NodeReference[[]ValueReference[*yaml.Node]]{}): + + if utils.IsNodeArray(valueNode) { + if field.CanSet() { + var items []ValueReference[*yaml.Node] + for _, sliceItem := range valueNode.Content { + items = append(items, ValueReference[*yaml.Node]{ + Value: sliceItem, + ValueNode: sliceItem, + }) + } + n := NodeReference[[]ValueReference[*yaml.Node]]{ + Value: items, + KeyNode: keyNode, + ValueNode: valueNode, + } + field.Set(reflect.ValueOf(n)) + } + } + + default: + // we want to ignore everything else, each model handles its own complex types. + break + } +} + +// BuildModelAsync is a convenience function for calling BuildModel from a goroutine, requires a sync.WaitGroup +func BuildModelAsync(n *yaml.Node, model interface{}, lwg *sync.WaitGroup, errors *[]error) { + if n != nil { + err := BuildModel(n, model) + if err != nil { + *errors = append(*errors, err) + } + } + lwg.Done() +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/model_interfaces.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/model_interfaces.go new file mode 100644 index 000000000..e6248712e --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/model_interfaces.go @@ -0,0 +1,126 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package low + +import ( + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +type SharedParameters interface { + HasDescription + Hash() uint64 + GetName() *NodeReference[string] + GetIn() *NodeReference[string] + GetAllowEmptyValue() *NodeReference[bool] + GetRequired() *NodeReference[bool] + GetSchema() *NodeReference[any] // requires cast. +} + +type HasExternalDocs interface { + GetExternalDocs() *NodeReference[any] +} + +type HasDescription interface { + GetDescription() *NodeReference[string] +} + +type HasInfo interface { + GetInfo() *NodeReference[any] +} + +type SwaggerParameter interface { + SharedParameters + GetType() *NodeReference[string] + GetFormat() *NodeReference[string] + GetCollectionFormat() *NodeReference[string] + GetDefault() *NodeReference[*yaml.Node] + GetMaximum() *NodeReference[int] + GetExclusiveMaximum() *NodeReference[bool] + GetMinimum() *NodeReference[int] + GetExclusiveMinimum() *NodeReference[bool] + GetMaxLength() *NodeReference[int] + GetMinLength() *NodeReference[int] + GetPattern() *NodeReference[string] + GetMaxItems() *NodeReference[int] + GetMinItems() *NodeReference[int] + GetUniqueItems() *NodeReference[bool] + GetEnum() *NodeReference[[]ValueReference[*yaml.Node]] + GetMultipleOf() *NodeReference[int] +} + +type SwaggerHeader interface { + HasDescription + Hash() uint64 + GetType() *NodeReference[string] + GetFormat() *NodeReference[string] + GetCollectionFormat() *NodeReference[string] + GetDefault() *NodeReference[*yaml.Node] + GetMaximum() *NodeReference[int] + GetExclusiveMaximum() *NodeReference[bool] + GetMinimum() *NodeReference[int] + GetExclusiveMinimum() *NodeReference[bool] + GetMaxLength() *NodeReference[int] + GetMinLength() *NodeReference[int] + GetPattern() *NodeReference[string] + GetMaxItems() *NodeReference[int] + GetMinItems() *NodeReference[int] + GetUniqueItems() *NodeReference[bool] + GetEnum() *NodeReference[[]ValueReference[*yaml.Node]] + GetMultipleOf() *NodeReference[int] + GetItems() *NodeReference[any] // requires cast. +} + +type OpenAPIHeader interface { + HasDescription + Hash() uint64 + GetDeprecated() *NodeReference[bool] + GetStyle() *NodeReference[string] + GetAllowReserved() *NodeReference[bool] + GetExplode() *NodeReference[bool] + GetExample() *NodeReference[*yaml.Node] + GetRequired() *NodeReference[bool] + GetAllowEmptyValue() *NodeReference[bool] + GetSchema() *NodeReference[any] // requires cast. + GetExamples() *NodeReference[any] // requires cast. + GetContent() *NodeReference[any] // requires cast. +} + +type OpenAPIParameter interface { + SharedParameters + GetDeprecated() *NodeReference[bool] + GetStyle() *NodeReference[string] + GetAllowReserved() *NodeReference[bool] + GetExplode() *NodeReference[bool] + GetExample() *NodeReference[*yaml.Node] + GetExamples() *NodeReference[any] // requires cast. + GetContent() *NodeReference[any] // requires cast. +} + +// TODO: this needs to be fixed, move returns to pointers. +type SharedOperations interface { + GetOperationId() NodeReference[string] + GetExternalDocs() NodeReference[any] + GetDescription() NodeReference[string] + GetTags() NodeReference[[]ValueReference[string]] + GetSummary() NodeReference[string] + GetDeprecated() NodeReference[bool] + GetExtensions() *orderedmap.Map[KeyReference[string], ValueReference[*yaml.Node]] + GetResponses() NodeReference[any] // requires cast. + GetParameters() NodeReference[any] // requires cast. + GetSecurity() NodeReference[any] // requires cast. +} + +type SwaggerOperations interface { + SharedOperations + GetConsumes() NodeReference[[]ValueReference[string]] + GetProduces() NodeReference[[]ValueReference[string]] + GetSchemes() NodeReference[[]ValueReference[string]] +} + +type OpenAPIOperations interface { + SharedOperations + GetCallbacks() NodeReference[*orderedmap.Map[KeyReference[string], ValueReference[any]]] // requires cast + GetServers() NodeReference[any] // requires cast. +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/node_map.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/node_map.go new file mode 100644 index 000000000..b2b1d8bb0 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/node_map.go @@ -0,0 +1,153 @@ +// Copyright 2023-2024 Princess Beef Heavy Industries, LLC / Dave Shanley +// https://pb33f.io +// MIT License + +package low + +import ( + "context" + "sync" + + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// HasNodes is an interface that defines a method to get a map of nodes +type HasNodes interface { + GetNodes() map[int][]*yaml.Node +} + +// AddNodes is an interface that defined a method to add nodes. +type AddNodes interface { + AddNode(key int, node *yaml.Node) +} + +// NodeMap represents a map of yaml nodes +type NodeMap struct { + // Nodes is a sync map of nodes for this object, and the key is the line number of the node + // a line can contain many nodes (in JSON), so the value is a slice of *yaml.Node + Nodes *sync.Map `yaml:"-" json:"-"` +} + +// AddNode will add a node to the NodeMap +func (nm *NodeMap) AddNode(key int, node *yaml.Node) { + if existing, ok := nm.Nodes.Load(key); ok { + if ext, ko := existing.(*yaml.Node); ko { + nm.Nodes.Store(key, []*yaml.Node{ext, node}) + } + if ext, ko := existing.([]*yaml.Node); ko { + ext = append(ext, node) + nm.Nodes.Store(key, ext) + } + } else { + nm.Nodes.Store(key, []*yaml.Node{node}) + } +} + +// GetNodes will return the map of nodes +func (nm *NodeMap) GetNodes() map[int][]*yaml.Node { + composed := make(map[int][]*yaml.Node) + if nm.Nodes != nil { + nm.Nodes.Range(func(key, value interface{}) bool { + if v, ok := value.([]*yaml.Node); ok { + composed[key.(int)] = v + } + if v, ok := value.(*yaml.Node); ok { + composed[key.(int)] = []*yaml.Node{v} + } + + return true + }) + } + if len(composed) <= 0 { + composed[0] = []*yaml.Node{} // return an empty slice if there are no nodes + } + return composed +} + +// ExtractNodes will iterate over a *yaml.Node and extract all nodes with a line number into a map +func (nm *NodeMap) ExtractNodes(node *yaml.Node, recurse bool) { + if node == nil { + return + } + // if the node has content, iterate over it and extract every top level line number + if node.Content != nil { + for i := 0; i < len(node.Content); i++ { + if node.Content[i].Line != 0 && len(node.Content[i].Content) <= 0 { + nm.AddNode(node.Content[i].Line, node.Content[i]) + } + if node.Content[i].Line != 0 && len(node.Content[i].Content) > 0 { + if recurse { + nm.AddNode(node.Content[i].Line, node.Content[i]) + nm.ExtractNodes(node.Content[i], recurse) + } + } + } + } +} + +// ContainsLine will return true if the NodeMap contains a node with the supplied line number +func (nm *NodeMap) ContainsLine(line int) bool { + if _, ok := nm.Nodes.Load(line); ok { + return true + } + return false +} + +// ExtractNodes will extract all nodes from a yaml.Node and return them in a map +func ExtractNodes(_ context.Context, root *yaml.Node) *sync.Map { + var syncMap sync.Map + nm := &NodeMap{Nodes: &syncMap} + if root != nil && len(root.Content) > 0 { + nm.ExtractNodes(root, false) + } else { + if root != nil { + nm.AddNode(root.Line, root) + } + } + return nm.Nodes +} + +// ExtractNodesRecursive will extract all nodes from a yaml.Node and return them in a map, just like ExtractNodes +// however, this version will dive-down the tree and extract all nodes from all child nodes as well until the tree +// is done. +func ExtractNodesRecursive(_ context.Context, root *yaml.Node) *sync.Map { + var syncMap sync.Map + nm := &NodeMap{Nodes: &syncMap} + nm.ExtractNodes(root, true) + return nm.Nodes +} + +// ExtractExtensionNodes will extract all extension nodes from a map of extensions, recursively. +func ExtractExtensionNodes(_ context.Context, + extensionMap *orderedmap.Map[KeyReference[string], + ValueReference[*yaml.Node]], nodeMap *sync.Map, +) { + // range over the extension map and extract all nodes + for k, v := range extensionMap.FromOldest() { + results := []*yaml.Node{k.KeyNode} + var newNodeMap sync.Map + nm := &NodeMap{Nodes: &newNodeMap} + if len(v.ValueNode.Content) > 0 { + nm.ExtractNodes(v.ValueNode, true) + nm.Nodes.Range(func(key, value interface{}) bool { + for _, n := range value.([]*yaml.Node) { + results = append(results, n) + } + return true + }) + } else { + results = append(results, v.ValueNode) + } + if nodeMap != nil { + if k.KeyNode.Line == v.ValueNode.Line { + nodeMap.Store(k.KeyNode.Line, results) + } else { + nodeMap.Store(k.KeyNode.Line, results[0]) + for _, y := range results[1:] { + nodeMap.Store(y.Line, []*yaml.Node{y}) + } + } + } + } +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/overlay/action.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/overlay/action.go new file mode 100644 index 000000000..7b9df0364 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/overlay/action.go @@ -0,0 +1,120 @@ +// Copyright 2022-2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package overlay + +import ( + "context" + "hash/maphash" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Action represents a low-level Overlay Action Object. +// https://spec.openapis.org/overlay/v1.1.0#action-object +type Action struct { + Target low.NodeReference[string] + Description low.NodeReference[string] + Update low.NodeReference[*yaml.Node] + Remove low.NodeReference[bool] + Copy low.NodeReference[string] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// GetIndex returns the index.SpecIndex instance attached to the Action object +func (a *Action) GetIndex() *index.SpecIndex { + return a.index +} + +// GetContext returns the context.Context instance used when building the Action object +func (a *Action) GetContext() context.Context { + return a.context +} + +// FindExtension returns a ValueReference containing the extension value, if found. +func (a *Action) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, a.Extensions) +} + +// GetRootNode returns the root yaml node of the Action object +func (a *Action) GetRootNode() *yaml.Node { + return a.RootNode +} + +// GetKeyNode returns the key yaml node of the Action object +func (a *Action) GetKeyNode() *yaml.Node { + return a.KeyNode +} + +// Build will extract extensions for the Action object. +func (a *Action) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + a.KeyNode = keyNode + root = utils.NodeAlias(root) + a.RootNode = root + utils.CheckForMergeNodes(root) + a.Reference = new(low.Reference) + a.Nodes = low.ExtractNodes(ctx, root) + a.Extensions = low.ExtractExtensions(root) + a.index = idx + a.context = ctx + low.ExtractExtensionNodes(ctx, a.Extensions, a.Nodes) + + // Extract the update node directly if present + for i := 0; i < len(root.Content); i += 2 { + if i+1 < len(root.Content) && root.Content[i].Value == UpdateLabel { + a.Update = low.NodeReference[*yaml.Node]{ + Value: root.Content[i+1], + KeyNode: root.Content[i], + ValueNode: root.Content[i+1], + } + break + } + } + return nil +} + +// GetExtensions returns all Action extensions and satisfies the low.HasExtensions interface. +func (a *Action) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return a.Extensions +} + +// Hash will return a consistent Hash of the Action object +func (a *Action) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if !a.Target.IsEmpty() { + h.WriteString(a.Target.Value) + h.WriteByte(low.HASH_PIPE) + } + if !a.Description.IsEmpty() { + h.WriteString(a.Description.Value) + h.WriteByte(low.HASH_PIPE) + } + if !a.Update.IsEmpty() { + h.WriteString(low.GenerateHashString(a.Update.Value)) + h.WriteByte(low.HASH_PIPE) + } + if !a.Remove.IsEmpty() { + low.HashBool(h, a.Remove.Value) + h.WriteByte(low.HASH_PIPE) + } + if !a.Copy.IsEmpty() { + h.WriteString(a.Copy.Value) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(a.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/overlay/constants.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/overlay/constants.go new file mode 100644 index 000000000..0bbcaf019 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/overlay/constants.go @@ -0,0 +1,20 @@ +// Copyright 2022-2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package overlay + +// Constants for labels used to look up values within OpenAPI Overlay specifications. +// https://spec.openapis.org/overlay/v1.1.0 +const ( + OverlayLabel = "overlay" + InfoLabel = "info" + ExtendsLabel = "extends" + ActionsLabel = "actions" + TitleLabel = "title" + VersionLabel = "version" + TargetLabel = "target" + DescriptionLabel = "description" + UpdateLabel = "update" + RemoveLabel = "remove" + CopyLabel = "copy" +) diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/overlay/info.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/overlay/info.go new file mode 100644 index 000000000..a27810291 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/overlay/info.go @@ -0,0 +1,98 @@ +// Copyright 2022-2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package overlay + +import ( + "context" + "hash/maphash" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Info represents a low-level Overlay Info Object. +// https://spec.openapis.org/overlay/v1.1.0#info-object +type Info struct { + Title low.NodeReference[string] + Version low.NodeReference[string] + Description low.NodeReference[string] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// GetIndex returns the index.SpecIndex instance attached to the Info object +func (i *Info) GetIndex() *index.SpecIndex { + return i.index +} + +// GetContext returns the context.Context instance used when building the Info object +func (i *Info) GetContext() context.Context { + return i.context +} + +// FindExtension returns a ValueReference containing the extension value, if found. +func (i *Info) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, i.Extensions) +} + +// GetRootNode returns the root yaml node of the Info object +func (i *Info) GetRootNode() *yaml.Node { + return i.RootNode +} + +// GetKeyNode returns the key yaml node of the Info object +func (i *Info) GetKeyNode() *yaml.Node { + return i.KeyNode +} + +// Build will extract extensions for the Info object. +func (i *Info) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + i.KeyNode = keyNode + root = utils.NodeAlias(root) + i.RootNode = root + utils.CheckForMergeNodes(root) + i.Reference = new(low.Reference) + i.Nodes = low.ExtractNodes(ctx, root) + i.Extensions = low.ExtractExtensions(root) + i.index = idx + i.context = ctx + low.ExtractExtensionNodes(ctx, i.Extensions, i.Nodes) + return nil +} + +// GetExtensions returns all Info extensions and satisfies the low.HasExtensions interface. +func (i *Info) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return i.Extensions +} + +// Hash will return a consistent Hash of the Info object +func (inf *Info) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if !inf.Title.IsEmpty() { + h.WriteString(inf.Title.Value) + h.WriteByte(low.HASH_PIPE) + } + if !inf.Version.IsEmpty() { + h.WriteString(inf.Version.Value) + h.WriteByte(low.HASH_PIPE) + } + if !inf.Description.IsEmpty() { + h.WriteString(inf.Description.Value) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(inf.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/overlay/overlay.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/overlay/overlay.go new file mode 100644 index 000000000..d0e9e3f0e --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/overlay/overlay.go @@ -0,0 +1,151 @@ +// Copyright 2022-2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package overlay + +import ( + "context" + "hash/maphash" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Overlay represents a low-level OpenAPI Overlay document. +// https://spec.openapis.org/overlay/v1.0.0 +type Overlay struct { + Overlay low.NodeReference[string] + Info low.NodeReference[*Info] + Extends low.NodeReference[string] + Actions low.NodeReference[[]low.ValueReference[*Action]] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// GetIndex returns the index.SpecIndex instance attached to the Overlay object +func (o *Overlay) GetIndex() *index.SpecIndex { + return o.index +} + +// GetContext returns the context.Context instance used when building the Overlay object +func (o *Overlay) GetContext() context.Context { + return o.context +} + +// FindExtension returns a ValueReference containing the extension value, if found. +func (o *Overlay) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, o.Extensions) +} + +// GetRootNode returns the root yaml node of the Overlay object +func (o *Overlay) GetRootNode() *yaml.Node { + return o.RootNode +} + +// GetKeyNode returns the key yaml node of the Overlay object +func (o *Overlay) GetKeyNode() *yaml.Node { + return o.KeyNode +} + +// Build will extract all properties of the Overlay document. +func (o *Overlay) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + o.KeyNode = keyNode + root = utils.NodeAlias(root) + o.RootNode = root + utils.CheckForMergeNodes(root) + o.Reference = new(low.Reference) + o.Nodes = low.ExtractNodes(ctx, root) + o.Extensions = low.ExtractExtensions(root) + o.index = idx + o.context = ctx + low.ExtractExtensionNodes(ctx, o.Extensions, o.Nodes) + + // Extract info object + info, err := low.ExtractObject[*Info](ctx, InfoLabel, root, idx) + if err != nil { + return err + } + o.Info = info + + // Extract actions array + o.Actions = o.extractActions(ctx, root, idx) + + return nil +} + +func (o *Overlay) extractActions(ctx context.Context, root *yaml.Node, idx *index.SpecIndex) low.NodeReference[[]low.ValueReference[*Action]] { + var result low.NodeReference[[]low.ValueReference[*Action]] + + for i := 0; i < len(root.Content); i += 2 { + if i+1 >= len(root.Content) { + break + } + key := root.Content[i] + value := root.Content[i+1] + + if key.Value == ActionsLabel { + result.KeyNode = key + result.ValueNode = value + + if value.Kind != yaml.SequenceNode { + continue + } + + actions := make([]low.ValueReference[*Action], 0, len(value.Content)) + for _, actionNode := range value.Content { + action := &Action{} + _ = low.BuildModel(actionNode, action) + _ = action.Build(ctx, nil, actionNode, idx) + actions = append(actions, low.ValueReference[*Action]{ + Value: action, + ValueNode: actionNode, + }) + } + result.Value = actions + break + } + } + return result +} + +// GetExtensions returns all Overlay extensions and satisfies the low.HasExtensions interface. +func (o *Overlay) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return o.Extensions +} + +// Hash will return a consistent Hash of the Overlay object +func (o *Overlay) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if !o.Overlay.IsEmpty() { + h.WriteString(o.Overlay.Value) + h.WriteByte(low.HASH_PIPE) + } + if !o.Info.IsEmpty() { + h.WriteString(low.GenerateHashString(o.Info.Value)) + h.WriteByte(low.HASH_PIPE) + } + if !o.Extends.IsEmpty() { + h.WriteString(o.Extends.Value) + h.WriteByte(low.HASH_PIPE) + } + if !o.Actions.IsEmpty() { + for _, action := range o.Actions.Value { + h.WriteString(low.GenerateHashString(action.Value)) + h.WriteByte(low.HASH_PIPE) + } + } + for _, ext := range low.HashExtensions(o.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/race_disabled.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/race_disabled.go new file mode 100644 index 000000000..bcf16a191 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/race_disabled.go @@ -0,0 +1,5 @@ +//go:build !race + +package low + +const raceEnabled = false diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/race_enabled.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/race_enabled.go new file mode 100644 index 000000000..8121be38e --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/race_enabled.go @@ -0,0 +1,5 @@ +//go:build race + +package low + +const raceEnabled = true diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/reference.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/reference.go new file mode 100644 index 000000000..c7d1cc19d --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/reference.go @@ -0,0 +1,349 @@ +package low + +import ( + "context" + "fmt" + + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +const ( + HASH = "%x" +) + +type Reference struct { + refNode *yaml.Node + reference string +} + +func (r Reference) GetReference() string { + return r.reference +} + +func (r Reference) IsReference() bool { + return r.reference != "" +} + +func (r Reference) GetReferenceNode() *yaml.Node { + if r.IsReference() && r.refNode == nil { + return utils.CreateRefNode(r.reference) + } + + return r.refNode +} + +func (r *Reference) SetReference(ref string, node *yaml.Node) { + r.reference = ref + r.refNode = node +} + +type IsReferenced interface { + IsReference() bool + GetReference() string + GetReferenceNode() *yaml.Node +} + +type SetReferencer interface { + SetReference(ref string, node *yaml.Node) +} + +// Buildable is an interface for any struct that can be 'built out'. This means that a struct can accept +// a root node and a reference to the index that carries data about any references used. +// +// Used by generic functions when automatically building out structs based on yaml.Node inputs. +type Buildable[T any] interface { + Build(ctx context.Context, key, value *yaml.Node, idx *index.SpecIndex) error + *T +} + +// HasValueNode is implemented by NodeReference and ValueReference to return the yaml.Node backing the value. +type HasValueNode[T any] interface { + GetValueNode() *yaml.Node + *T +} + +// HasValueNodeUntyped is implemented by NodeReference and ValueReference to return the yaml.Node backing the value. +type HasValueNodeUntyped interface { + GetValueNode() *yaml.Node + IsReferenced +} + +// Hashable defines any struct that implements a Hash function that returns a 64-bit hash of the state of the +// representative object. Great for equality checking! +type Hashable interface { + Hash() uint64 +} + +// HasExtensions is implemented by any object that exposes extensions +type HasExtensions[T any] interface { + // GetExtensions returns generic low level extensions + GetExtensions() *orderedmap.Map[KeyReference[string], ValueReference[*yaml.Node]] +} + +// HasExtensionsUntyped is implemented by any object that exposes extensions +type HasExtensionsUntyped interface { + // GetExtensions returns generic low level extensions + GetExtensions() *orderedmap.Map[KeyReference[string], ValueReference[*yaml.Node]] +} + +// HasValue is implemented by NodeReference and ValueReference to return the yaml.Node backing the value. +type HasValue[T any] interface { + GetValue() T + GetValueNode() *yaml.Node + IsEmpty() bool + *T +} + +// HasValueUnTyped is implemented by NodeReference and ValueReference to return the yaml.Node backing the value. +type HasValueUnTyped interface { + GetValueUntyped() any + GetValueNode() *yaml.Node +} + +// HasKeyNode is implemented by KeyReference to return the yaml.Node backing the key. +type HasKeyNode interface { + GetKeyNode() *yaml.Node +} + +// NodeReference is a low-level container for holding a Value of type T, as well as references to +// a key yaml.Node that points to the key node that contains the value node, and the value node that contains +// the actual value. +type NodeReference[T any] struct { + Reference + + // The value being referenced + Value T + + // The yaml.Node that holds the value + ValueNode *yaml.Node + + // The yaml.Node that is the key, that contains the value. + KeyNode *yaml.Node + + Context context.Context +} + +var _ HasValueNodeUntyped = &NodeReference[any]{} + +// KeyReference is a low-level container for key nodes holding a Value of type T. A KeyNode is a pointer to the +// yaml.Node that holds a key to a value. +type KeyReference[T any] struct { + // The value being referenced. + Value T + + // The yaml.Node that holds this referenced key + KeyNode *yaml.Node +} + +// ValueReference is a low-level container for value nodes that hold a Value of type T. A ValueNode is a pointer +// to the yaml.Node that holds the value. +type ValueReference[T any] struct { + Reference + + // The value being referenced. + Value T + + // The yaml.Node that holds the referenced value + ValueNode *yaml.Node +} + +// IsEmpty will return true if this reference has no key or value nodes assigned (it's been ignored) +func (n NodeReference[T]) IsEmpty() bool { + return n.KeyNode == nil && n.ValueNode == nil +} + +func (n NodeReference[T]) NodeLineNumber() int { + if !n.IsEmpty() { + return n.ValueNode.Line + } else { + return 0 + } +} + +// GenerateMapKey will return a string based on the line and column number of the node, e.g. 33:56 for line 33, col 56. +func (n NodeReference[T]) GenerateMapKey() string { + return fmt.Sprintf("%d:%d", n.ValueNode.Line, n.ValueNode.Column) +} + +// Mutate will set the reference value to what is supplied. This happens to both the Value and ValueNode, which means +// the root document is permanently mutated and changes will be reflected in any serialization of the root document. +func (n NodeReference[T]) Mutate(value T) NodeReference[T] { + n.ValueNode.Value = fmt.Sprintf("%v", value) + n.Value = value + return n +} + +// GetValueNode will return the yaml.Node containing the reference value node +func (n NodeReference[T]) GetValueNode() *yaml.Node { + return n.ValueNode +} + +// GetKeyNode will return the yaml.Node containing the reference key node +func (n NodeReference[T]) GetKeyNode() *yaml.Node { + return n.KeyNode +} + +// GetValue will return the raw value of the node +func (n NodeReference[T]) GetValue() T { + return n.Value +} + +// GetValueUntyped will return the raw value of the node with no type +func (n NodeReference[T]) GetValueUntyped() any { + return n.Value +} + +// IsEmpty will return true if this reference has no key or value nodes assigned (it's been ignored) +func (n ValueReference[T]) IsEmpty() bool { + return n.ValueNode == nil +} + +// NodeLineNumber will return the line number of the value node (or 0 if the value node is empty) +func (n ValueReference[T]) NodeLineNumber() int { + if !n.IsEmpty() { + return n.ValueNode.Line + } else { + return 0 + } +} + +// GenerateMapKey will return a string based on the line and column number of the node, e.g. 33:56 for line 33, col 56. +func (n ValueReference[T]) GenerateMapKey() string { + return fmt.Sprintf("%d:%d", n.ValueNode.Line, n.ValueNode.Column) +} + +// GetValueNode will return the yaml.Node containing the reference value node +func (n ValueReference[T]) GetValueNode() *yaml.Node { + return n.ValueNode +} + +// GetValue will return the raw value of the node +func (n ValueReference[T]) GetValue() T { + return n.Value +} + +// GetValueUntyped will return the raw value of the node with no type +func (n ValueReference[T]) GetValueUntyped() any { + return n.Value +} + +func (n ValueReference[T]) MarshalYAML() (interface{}, error) { + if n.IsReference() { + return n.GetReferenceNode(), nil + } + var h yaml.Node + e := n.ValueNode.Decode(&h) + return h, e +} + +func (n KeyReference[T]) MarshalYAML() (interface{}, error) { + return n.KeyNode, nil +} + +// IsEmpty will return true if this reference has no key or value nodes assigned (it's been ignored) +func (n KeyReference[T]) IsEmpty() bool { + return n.KeyNode == nil +} + +// GetValueUntyped will return the raw value of the node with no type +func (n KeyReference[T]) GetValueUntyped() any { + return n.Value +} + +// GetKeyNode will return the yaml.Node containing the reference key node. +func (n KeyReference[T]) GetKeyNode() *yaml.Node { + return n.KeyNode +} + +// GenerateMapKey will return a string based on the line and column number of the node, e.g. 33:56 for line 33, col 56. +func (n KeyReference[T]) GenerateMapKey() string { + return fmt.Sprintf("%d:%d", n.KeyNode.Line, n.KeyNode.Column) +} + +// Mutate will set the reference value to what is supplied. This happens to both the Value and ValueNode, which means +// the root document is permanently mutated and changes will be reflected in any serialization of the root document. +func (n ValueReference[T]) Mutate(value T) ValueReference[T] { + n.ValueNode.Value = fmt.Sprintf("%v", value) + n.Value = value + return n +} + +// IsCircular will determine if the node in question, is part of a circular reference chain discovered by the index. +func IsCircular(node *yaml.Node, idx *index.SpecIndex) bool { + if idx == nil { + return false // no index! nothing we can do. + } + refs := idx.GetCircularReferences() + for i := range idx.GetCircularReferences() { + if refs[i].LoopPoint.Node == node { + return true + } + for k := range refs[i].Journey { + if refs[i].Journey[k].Node == node { + return true + } + isRef, _, refValue := utils.IsNodeRefValue(node) + if isRef && refs[i].Journey[k].Definition == refValue { + return true + } + } + } + // check mapped references in case we didn't find it. + _, nv := utils.FindKeyNode("$ref", node.Content) + if nv != nil { + ref := idx.GetMappedReferences()[nv.Value] + if ref != nil { + return ref.Circular + } + } + return false +} + +// GetCircularReferenceResult will check if a node is part of a circular reference chain and then return that +// index.CircularReferenceResult it was located in. Returns nil if not found. +func GetCircularReferenceResult(node *yaml.Node, idx *index.SpecIndex) *index.CircularReferenceResult { + if idx == nil { + return nil // no index! nothing we can do. + } + var refs []*index.CircularReferenceResult + if idx.GetResolver() != nil { + refs = append(refs, idx.GetResolver().GetCircularReferences()...) + refs = append(refs, idx.GetResolver().GetInfiniteCircularReferences()...) + refs = append(refs, idx.GetResolver().GetIgnoredCircularArrayReferences()...) + refs = append(refs, idx.GetResolver().GetIgnoredCircularPolyReferences()...) + refs = append(refs, idx.GetResolver().GetSafeCircularReferences()...) + } else { + refs = idx.GetCircularReferences() + } + for i := range refs { + if refs[i].LoopPoint.Node == node { + return refs[i] + } + for k := range refs[i].Journey { + if refs[i].Journey[k].Node == node { + return refs[i] + } + isRef, _, refValue := utils.IsNodeRefValue(node) + if isRef && refs[i].Journey[k].Definition == refValue { + return refs[i] + } + } + } + // check mapped references in case we didn't find it. + _, nv := utils.FindKeyNode("$ref", node.Content) + if nv != nil { + for i := range refs { + if refs[i].LoopPoint.Definition == nv.Value { + return refs[i] + } + } + } + return nil +} + +func HashToString(hash uint64) string { + return fmt.Sprintf("%x", hash) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/constants.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/constants.go new file mode 100644 index 000000000..45e373775 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/constants.go @@ -0,0 +1,25 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +const ( + DefinitionsLabel = "definitions" + SecurityDefinitionsLabel = "securityDefinitions" + ExamplesLabel = "examples" + HeadersLabel = "headers" + DefaultLabel = "default" + ItemsLabel = "items" + ParametersLabel = "parameters" + PathsLabel = "paths" + GetLabel = "get" + PostLabel = "post" + PatchLabel = "patch" + PutLabel = "put" + DeleteLabel = "delete" + OptionsLabel = "options" + HeadLabel = "head" + SecurityLabel = "security" + ScopesLabel = "scopes" + ResponsesLabel = "responses" +) diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/definitions.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/definitions.go new file mode 100644 index 000000000..cbc6156f8 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/definitions.go @@ -0,0 +1,323 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "context" + "hash/maphash" + "sync" + + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// ParameterDefinitions is a low-level representation of a Swagger / OpenAPI 2 Parameters Definitions object. +// +// ParameterDefinitions holds parameters to be reused across operations. Parameter definitions can be +// referenced to the ones defined here. It does not define global operation parameters +// - https://swagger.io/specification/v2/#parametersDefinitionsObject +type ParameterDefinitions struct { + Definitions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*Parameter]] +} + +// ResponsesDefinitions is a low-level representation of a Swagger / OpenAPI 2 Responses Definitions object. +// +// ResponsesDefinitions is an object to hold responses to be reused across operations. Response definitions can be +// referenced to the ones defined here. It does not define global operation responses +// - https://swagger.io/specification/v2/#responsesDefinitionsObject +type ResponsesDefinitions struct { + Definitions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*Response]] +} + +// SecurityDefinitions is a low-level representation of a Swagger / OpenAPI 2 Security Definitions object. +// +// A declaration of the security schemes available to be used in the specification. This does not enforce the security +// schemes on the operations and only serves to provide the relevant details for each scheme +// - https://swagger.io/specification/v2/#securityDefinitionsObject +type SecurityDefinitions struct { + Definitions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*SecurityScheme]] +} + +// Definitions is a low-level representation of a Swagger / OpenAPI 2 Definitions object +// +// An object to hold data types that can be consumed and produced by operations. These data types can be primitives, +// arrays or models. +// - https://swagger.io/specification/v2/#definitionsObject +type Definitions struct { + Schemas *orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.SchemaProxy]] +} + +// FindSchema will attempt to locate a base.SchemaProxy instance using a name. +func (d *Definitions) FindSchema(schema string) *low.ValueReference[*base.SchemaProxy] { + return low.FindItemInOrderedMap[*base.SchemaProxy](schema, d.Schemas) +} + +// FindParameter will attempt to locate a Parameter instance using a name. +func (pd *ParameterDefinitions) FindParameter(parameter string) *low.ValueReference[*Parameter] { + return low.FindItemInOrderedMap[*Parameter](parameter, pd.Definitions) +} + +// FindResponse will attempt to locate a Response instance using a name. +func (r *ResponsesDefinitions) FindResponse(response string) *low.ValueReference[*Response] { + return low.FindItemInOrderedMap[*Response](response, r.Definitions) +} + +// FindSecurityDefinition will attempt to locate a SecurityScheme using a name. +func (s *SecurityDefinitions) FindSecurityDefinition(securityDef string) *low.ValueReference[*SecurityScheme] { + return low.FindItemInOrderedMap[*SecurityScheme](securityDef, s.Definitions) +} + +// Build will extract all definitions into SchemaProxy instances. +func (d *Definitions) Build(ctx context.Context, _, root *yaml.Node, idx *index.SpecIndex) error { + root = utils.NodeAlias(root) + utils.CheckForMergeNodes(root) + type buildInput struct { + label *yaml.Node + value *yaml.Node + } + results := orderedmap.New[low.KeyReference[string], low.ValueReference[*base.SchemaProxy]]() + in := make(chan buildInput) + out := make(chan definitionResult[*base.SchemaProxy]) + done := make(chan struct{}) + var wg sync.WaitGroup + wg.Add(2) // input and output goroutines. + + // TranslatePipeline input. + go func() { + defer func() { + close(in) + wg.Done() + }() + var label *yaml.Node + for i, value := range root.Content { + if i%2 == 0 { + label = value + continue + } + + select { + case in <- buildInput{ + label: label, + value: value, + }: + case <-done: + return + } + } + }() + + // TranslatePipeline output. + go func() { + for { + result, ok := <-out + if !ok { + break + } + + key := low.KeyReference[string]{ + Value: result.k.Value, + KeyNode: result.k, + } + results.Set(key, result.v) + } + close(done) + wg.Done() + }() + + translateFunc := func(value buildInput) (definitionResult[*base.SchemaProxy], error) { + obj, err, _, rv := low.ExtractObjectRaw[*base.SchemaProxy](ctx, value.label, value.value, idx) + if err != nil { + return definitionResult[*base.SchemaProxy]{}, err + } + + v := low.ValueReference[*base.SchemaProxy]{ + Value: obj, ValueNode: value.value, + } + v.SetReference(rv, value.value) + + return definitionResult[*base.SchemaProxy]{k: value.label, v: v}, nil + } + + err := datamodel.TranslatePipeline[buildInput, definitionResult[*base.SchemaProxy]](in, out, translateFunc) + wg.Wait() + if err != nil { + return err + } + + d.Schemas = results + return nil +} + +// Hash will return a consistent Hash of the Definitions object +func (d *Definitions) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + for k := range orderedmap.SortAlpha(d.Schemas).KeysFromOldest() { + h.WriteString(low.GenerateHashString(d.FindSchema(k.Value).Value)) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} + +// Build will extract all ParameterDefinitions into Parameter instances. +func (pd *ParameterDefinitions) Build(ctx context.Context, _, root *yaml.Node, idx *index.SpecIndex) error { + errorChan := make(chan error) + resultChan := make(chan definitionResult[*Parameter]) + var defLabel *yaml.Node + totalDefinitions := 0 + buildFunc := func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex, + r chan definitionResult[*Parameter], e chan error, + ) { + obj, err, _, rv := low.ExtractObjectRaw[*Parameter](ctx, label, value, idx) + if err != nil { + e <- err + } + + v := low.ValueReference[*Parameter]{ + Value: obj, + ValueNode: value, + } + v.SetReference(rv, value) + + r <- definitionResult[*Parameter]{k: label, v: v} + } + for i := range root.Content { + if i%2 == 0 { + defLabel = root.Content[i] + continue + } + totalDefinitions++ + go buildFunc(defLabel, root.Content[i], idx, resultChan, errorChan) + } + + completedDefs := 0 + results := orderedmap.New[low.KeyReference[string], low.ValueReference[*Parameter]]() + for completedDefs < totalDefinitions { + select { + case err := <-errorChan: + return err + case sch := <-resultChan: + completedDefs++ + key := low.KeyReference[string]{ + Value: sch.k.Value, + KeyNode: sch.k, + } + results.Set(key, sch.v) + } + } + pd.Definitions = results + return nil +} + +// re-usable struct for holding results as k/v pairs. +type definitionResult[T any] struct { + k *yaml.Node + v low.ValueReference[T] +} + +// Build will extract all ResponsesDefinitions into Response instances. +func (r *ResponsesDefinitions) Build(ctx context.Context, _, root *yaml.Node, idx *index.SpecIndex) error { + errorChan := make(chan error) + resultChan := make(chan definitionResult[*Response]) + var defLabel *yaml.Node + totalDefinitions := 0 + buildFunc := func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex, + r chan definitionResult[*Response], e chan error, + ) { + obj, err, _, rv := low.ExtractObjectRaw[*Response](ctx, label, value, idx) + if err != nil { + e <- err + } + + v := low.ValueReference[*Response]{ + Value: obj, + ValueNode: value, + } + v.SetReference(rv, value) + + r <- definitionResult[*Response]{k: label, v: v} + } + for i := range root.Content { + if i%2 == 0 { + defLabel = root.Content[i] + continue + } + totalDefinitions++ + go buildFunc(defLabel, root.Content[i], idx, resultChan, errorChan) + } + + completedDefs := 0 + results := orderedmap.New[low.KeyReference[string], low.ValueReference[*Response]]() + for completedDefs < totalDefinitions { + select { + case err := <-errorChan: + return err + case sch := <-resultChan: + completedDefs++ + key := low.KeyReference[string]{ + Value: sch.k.Value, + KeyNode: sch.k, + } + results.Set(key, sch.v) + } + } + r.Definitions = results + return nil +} + +// Build will extract all SecurityDefinitions into SecurityScheme instances. +func (s *SecurityDefinitions) Build(ctx context.Context, _, root *yaml.Node, idx *index.SpecIndex) error { + errorChan := make(chan error) + resultChan := make(chan definitionResult[*SecurityScheme]) + var defLabel *yaml.Node + totalDefinitions := 0 + + buildFunc := func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex, + r chan definitionResult[*SecurityScheme], e chan error, + ) { + obj, err, _, rv := low.ExtractObjectRaw[*SecurityScheme](ctx, label, value, idx) + if err != nil { + e <- err + } + + v := low.ValueReference[*SecurityScheme]{ + Value: obj, ValueNode: value, + } + v.SetReference(rv, value) + + r <- definitionResult[*SecurityScheme]{k: label, v: v} + } + + for i := range root.Content { + if i%2 == 0 { + defLabel = root.Content[i] + continue + } + totalDefinitions++ + go buildFunc(defLabel, root.Content[i], idx, resultChan, errorChan) + } + + completedDefs := 0 + results := orderedmap.New[low.KeyReference[string], low.ValueReference[*SecurityScheme]]() + for completedDefs < totalDefinitions { + select { + case err := <-errorChan: + return err + case sch := <-resultChan: + completedDefs++ + key := low.KeyReference[string]{ + Value: sch.k.Value, + KeyNode: sch.k, + } + results.Set(key, sch.v) + } + } + s.Definitions = results + return nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/examples.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/examples.go new file mode 100644 index 000000000..6e18c58ad --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/examples.go @@ -0,0 +1,65 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "context" + "hash/maphash" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Examples represents a low-level Swagger / OpenAPI 2 Example object. +// Allows sharing examples for operation responses +// - https://swagger.io/specification/v2/#exampleObject +type Examples struct { + Values *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] +} + +// FindExample attempts to locate an example value, using a key label. +func (e *Examples) FindExample(name string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(name, e.Values) +} + +// Build will extract all examples and will attempt to unmarshal content into a map or slice based on type. +func (e *Examples) Build(_ context.Context, _, root *yaml.Node, _ *index.SpecIndex) error { + root = utils.NodeAlias(root) + utils.CheckForMergeNodes(root) + var keyNode, currNode *yaml.Node + e.Values = orderedmap.New[low.KeyReference[string], low.ValueReference[*yaml.Node]]() + for i := range root.Content { + if i%2 == 0 { + keyNode = root.Content[i] + continue + } + currNode = root.Content[i] + + e.Values.Set( + low.KeyReference[string]{ + Value: keyNode.Value, + KeyNode: keyNode, + }, + low.ValueReference[*yaml.Node]{ + Value: currNode, + ValueNode: currNode, + }, + ) + } + return nil +} + +// Hash will return a consistent Hash of the Examples object +func (e *Examples) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + for v := range orderedmap.SortAlpha(e.Values).ValuesFromOldest() { + h.WriteString(low.GenerateHashString(v.Value)) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/header.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/header.go new file mode 100644 index 000000000..890d6dd6d --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/header.go @@ -0,0 +1,225 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "context" + "hash/maphash" + "sort" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Header Represents a low-level Swagger / OpenAPI 2 Header object. +// +// A Header is essentially identical to a Parameter, except it does not contain 'name' or 'in' properties. +// - https://swagger.io/specification/v2/#headerObject +type Header struct { + Type low.NodeReference[string] + Format low.NodeReference[string] + Description low.NodeReference[string] + Items low.NodeReference[*Items] + CollectionFormat low.NodeReference[string] + Default low.NodeReference[*yaml.Node] + Maximum low.NodeReference[int] + ExclusiveMaximum low.NodeReference[bool] + Minimum low.NodeReference[int] + ExclusiveMinimum low.NodeReference[bool] + MaxLength low.NodeReference[int] + MinLength low.NodeReference[int] + Pattern low.NodeReference[string] + MaxItems low.NodeReference[int] + MinItems low.NodeReference[int] + UniqueItems low.NodeReference[bool] + Enum low.NodeReference[[]low.ValueReference[*yaml.Node]] + MultipleOf low.NodeReference[int] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] +} + +// FindExtension will attempt to locate an extension value using a name lookup. +func (h *Header) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, h.Extensions) +} + +// GetExtensions returns all Header extensions and satisfies the low.HasExtensions interface. +func (h *Header) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return h.Extensions +} + +// Build will build out items, extensions and default value from the supplied node. +func (h *Header) Build(ctx context.Context, _, root *yaml.Node, idx *index.SpecIndex) error { + root = utils.NodeAlias(root) + utils.CheckForMergeNodes(root) + h.Extensions = low.ExtractExtensions(root) + items, err := low.ExtractObject[*Items](ctx, ItemsLabel, root, idx) + if err != nil { + return err + } + h.Items = items + + _, ln, vn := utils.FindKeyNodeFull(DefaultLabel, root.Content) + if vn != nil { + h.Default = low.NodeReference[*yaml.Node]{ + Value: vn, + KeyNode: ln, + ValueNode: vn, + } + return nil + } + + return nil +} + +// Hash will return a consistent Hash of the Header object +func (hdr *Header) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if hdr.Description.Value != "" { + h.WriteString(hdr.Description.Value) + h.WriteByte(low.HASH_PIPE) + } + if hdr.Type.Value != "" { + h.WriteString(hdr.Type.Value) + h.WriteByte(low.HASH_PIPE) + } + if hdr.Format.Value != "" { + h.WriteString(hdr.Format.Value) + h.WriteByte(low.HASH_PIPE) + } + if hdr.CollectionFormat.Value != "" { + h.WriteString(hdr.CollectionFormat.Value) + h.WriteByte(low.HASH_PIPE) + } + if hdr.Default.Value != nil && !hdr.Default.Value.IsZero() { + h.WriteString(low.GenerateHashString(hdr.Default.Value)) + h.WriteByte(low.HASH_PIPE) + } + low.HashInt64(h, int64(hdr.Maximum.Value)) + h.WriteByte(low.HASH_PIPE) + low.HashInt64(h, int64(hdr.Minimum.Value)) + h.WriteByte(low.HASH_PIPE) + low.HashBool(h, hdr.ExclusiveMinimum.Value) + h.WriteByte(low.HASH_PIPE) + low.HashBool(h, hdr.ExclusiveMaximum.Value) + h.WriteByte(low.HASH_PIPE) + low.HashInt64(h, int64(hdr.MinLength.Value)) + h.WriteByte(low.HASH_PIPE) + low.HashInt64(h, int64(hdr.MaxLength.Value)) + h.WriteByte(low.HASH_PIPE) + low.HashInt64(h, int64(hdr.MinItems.Value)) + h.WriteByte(low.HASH_PIPE) + low.HashInt64(h, int64(hdr.MaxItems.Value)) + h.WriteByte(low.HASH_PIPE) + low.HashInt64(h, int64(hdr.MultipleOf.Value)) + h.WriteByte(low.HASH_PIPE) + low.HashBool(h, hdr.UniqueItems.Value) + h.WriteByte(low.HASH_PIPE) + if hdr.Pattern.Value != "" { + h.WriteString(hdr.Pattern.Value) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(hdr.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + + keys := make([]string, len(hdr.Enum.Value)) + for k := range hdr.Enum.Value { + keys[k] = low.ValueToString(hdr.Enum.Value[k].Value) + } + sort.Strings(keys) + for _, key := range keys { + h.WriteString(key) + h.WriteByte(low.HASH_PIPE) + } + + if hdr.Items.Value != nil { + h.WriteString(low.GenerateHashString(hdr.Items.Value)) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} + +// Getter methods to satisfy SwaggerHeader interface. + +func (h *Header) GetType() *low.NodeReference[string] { + return &h.Type +} + +func (h *Header) GetDescription() *low.NodeReference[string] { + return &h.Description +} + +func (h *Header) GetFormat() *low.NodeReference[string] { + return &h.Format +} + +func (h *Header) GetItems() *low.NodeReference[any] { + i := low.NodeReference[any]{ + KeyNode: h.Items.KeyNode, + ValueNode: h.Items.ValueNode, + Value: h.Items.Value, + } + return &i +} + +func (h *Header) GetCollectionFormat() *low.NodeReference[string] { + return &h.CollectionFormat +} + +func (h *Header) GetDefault() *low.NodeReference[*yaml.Node] { + return &h.Default +} + +func (h *Header) GetMaximum() *low.NodeReference[int] { + return &h.Maximum +} + +func (h *Header) GetExclusiveMaximum() *low.NodeReference[bool] { + return &h.ExclusiveMaximum +} + +func (h *Header) GetMinimum() *low.NodeReference[int] { + return &h.Minimum +} + +func (h *Header) GetExclusiveMinimum() *low.NodeReference[bool] { + return &h.ExclusiveMinimum +} + +func (h *Header) GetMaxLength() *low.NodeReference[int] { + return &h.MaxLength +} + +func (h *Header) GetMinLength() *low.NodeReference[int] { + return &h.MinLength +} + +func (h *Header) GetPattern() *low.NodeReference[string] { + return &h.Pattern +} + +func (h *Header) GetMaxItems() *low.NodeReference[int] { + return &h.MaxItems +} + +func (h *Header) GetMinItems() *low.NodeReference[int] { + return &h.MinItems +} + +func (h *Header) GetUniqueItems() *low.NodeReference[bool] { + return &h.UniqueItems +} + +func (h *Header) GetEnum() *low.NodeReference[[]low.ValueReference[*yaml.Node]] { + return &h.Enum +} + +func (h *Header) GetMultipleOf() *low.NodeReference[int] { + return &h.MultipleOf +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/items.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/items.go new file mode 100644 index 000000000..279fc8567 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/items.go @@ -0,0 +1,219 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "context" + "hash/maphash" + "sort" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Items is a low-level representation of a Swagger / OpenAPI 2 Items object. +// +// Items is a limited subset of JSON-Schema's items object. It is used by parameter definitions that are not +// located in "body". Items, is actually identical to a Header, except it does not have description. +// - https://swagger.io/specification/v2/#itemsObject +type Items struct { + Type low.NodeReference[string] + Format low.NodeReference[string] + CollectionFormat low.NodeReference[string] + Items low.NodeReference[*Items] + Default low.NodeReference[*yaml.Node] + Maximum low.NodeReference[int] + ExclusiveMaximum low.NodeReference[bool] + Minimum low.NodeReference[int] + ExclusiveMinimum low.NodeReference[bool] + MaxLength low.NodeReference[int] + MinLength low.NodeReference[int] + Pattern low.NodeReference[string] + MaxItems low.NodeReference[int] + MinItems low.NodeReference[int] + UniqueItems low.NodeReference[bool] + Enum low.NodeReference[[]low.ValueReference[*yaml.Node]] + MultipleOf low.NodeReference[int] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] +} + +// FindExtension will attempt to locate an extension value using a name lookup. +func (i *Items) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, i.Extensions) +} + +// GetExtensions returns all Items extensions and satisfies the low.HasExtensions interface. +func (i *Items) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return i.Extensions +} + +// Hash will return a consistent Hash of the Items object +func (itm *Items) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if itm.Type.Value != "" { + h.WriteString(itm.Type.Value) + h.WriteByte(low.HASH_PIPE) + } + if itm.Format.Value != "" { + h.WriteString(itm.Format.Value) + h.WriteByte(low.HASH_PIPE) + } + if itm.CollectionFormat.Value != "" { + h.WriteString(itm.CollectionFormat.Value) + h.WriteByte(low.HASH_PIPE) + } + if itm.Default.Value != nil && !itm.Default.Value.IsZero() { + h.WriteString(low.GenerateHashString(itm.Default.Value)) + h.WriteByte(low.HASH_PIPE) + } + low.HashInt64(h, int64(itm.Maximum.Value)) + h.WriteByte(low.HASH_PIPE) + low.HashInt64(h, int64(itm.Minimum.Value)) + h.WriteByte(low.HASH_PIPE) + low.HashBool(h, itm.ExclusiveMinimum.Value) + h.WriteByte(low.HASH_PIPE) + low.HashBool(h, itm.ExclusiveMaximum.Value) + h.WriteByte(low.HASH_PIPE) + low.HashInt64(h, int64(itm.MinLength.Value)) + h.WriteByte(low.HASH_PIPE) + low.HashInt64(h, int64(itm.MaxLength.Value)) + h.WriteByte(low.HASH_PIPE) + low.HashInt64(h, int64(itm.MinItems.Value)) + h.WriteByte(low.HASH_PIPE) + low.HashInt64(h, int64(itm.MaxItems.Value)) + h.WriteByte(low.HASH_PIPE) + low.HashInt64(h, int64(itm.MultipleOf.Value)) + h.WriteByte(low.HASH_PIPE) + low.HashBool(h, itm.UniqueItems.Value) + h.WriteByte(low.HASH_PIPE) + if itm.Pattern.Value != "" { + h.WriteString(itm.Pattern.Value) + h.WriteByte(low.HASH_PIPE) + } + keys := make([]string, len(itm.Enum.Value)) + for k := range itm.Enum.Value { + keys[k] = low.ValueToString(itm.Enum.Value[k].Value) + } + sort.Strings(keys) + for _, key := range keys { + h.WriteString(key) + h.WriteByte(low.HASH_PIPE) + } + + if itm.Items.Value != nil { + h.WriteString(low.GenerateHashString(itm.Items.Value)) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(itm.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} + +// Build will build out items and default value. +func (i *Items) Build(ctx context.Context, _, root *yaml.Node, idx *index.SpecIndex) error { + root = utils.NodeAlias(root) + utils.CheckForMergeNodes(root) + i.Extensions = low.ExtractExtensions(root) + items, iErr := low.ExtractObject[*Items](ctx, ItemsLabel, root, idx) + if iErr != nil { + return iErr + } + i.Items = items + + _, ln, vn := utils.FindKeyNodeFull(DefaultLabel, root.Content) + if vn != nil { + i.Default = low.NodeReference[*yaml.Node]{ + Value: vn, + KeyNode: ln, + ValueNode: vn, + } + return nil + } + return nil +} + +// IsHeader compliance methods + +func (i *Items) GetType() *low.NodeReference[string] { + return &i.Type +} + +func (i *Items) GetFormat() *low.NodeReference[string] { + return &i.Format +} + +func (i *Items) GetItems() *low.NodeReference[any] { + k := low.NodeReference[any]{ + KeyNode: i.Items.KeyNode, + ValueNode: i.Items.ValueNode, + Value: i.Items.Value, + } + return &k +} + +func (i *Items) GetCollectionFormat() *low.NodeReference[string] { + return &i.CollectionFormat +} + +func (i *Items) GetDescription() *low.NodeReference[string] { + return nil // not implemented, but required to align with header contract +} + +func (i *Items) GetDefault() *low.NodeReference[*yaml.Node] { + return &i.Default +} + +func (i *Items) GetMaximum() *low.NodeReference[int] { + return &i.Maximum +} + +func (i *Items) GetExclusiveMaximum() *low.NodeReference[bool] { + return &i.ExclusiveMaximum +} + +func (i *Items) GetMinimum() *low.NodeReference[int] { + return &i.Minimum +} + +func (i *Items) GetExclusiveMinimum() *low.NodeReference[bool] { + return &i.ExclusiveMinimum +} + +func (i *Items) GetMaxLength() *low.NodeReference[int] { + return &i.MaxLength +} + +func (i *Items) GetMinLength() *low.NodeReference[int] { + return &i.MinLength +} + +func (i *Items) GetPattern() *low.NodeReference[string] { + return &i.Pattern +} + +func (i *Items) GetMaxItems() *low.NodeReference[int] { + return &i.MaxItems +} + +func (i *Items) GetMinItems() *low.NodeReference[int] { + return &i.MinItems +} + +func (i *Items) GetUniqueItems() *low.NodeReference[bool] { + return &i.UniqueItems +} + +func (i *Items) GetEnum() *low.NodeReference[[]low.ValueReference[*yaml.Node]] { + return &i.Enum +} + +func (i *Items) GetMultipleOf() *low.NodeReference[int] { + return &i.MultipleOf +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/operation.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/operation.go new file mode 100644 index 000000000..1dcfa4817 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/operation.go @@ -0,0 +1,250 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "context" + "hash/maphash" + "sort" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Operation represents a low-level Swagger / OpenAPI 2 Operation object. +// +// It describes a single API operation on a path. +// - https://swagger.io/specification/v2/#operationObject +type Operation struct { + Tags low.NodeReference[[]low.ValueReference[string]] + Summary low.NodeReference[string] + Description low.NodeReference[string] + ExternalDocs low.NodeReference[*base.ExternalDoc] + OperationId low.NodeReference[string] + Consumes low.NodeReference[[]low.ValueReference[string]] + Produces low.NodeReference[[]low.ValueReference[string]] + Parameters low.NodeReference[[]low.ValueReference[*Parameter]] + Responses low.NodeReference[*Responses] + Schemes low.NodeReference[[]low.ValueReference[string]] + Deprecated low.NodeReference[bool] + Security low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] +} + +// Build will extract external docs, extensions, parameters, responses and security requirements. +func (o *Operation) Build(ctx context.Context, _, root *yaml.Node, idx *index.SpecIndex) error { + root = utils.NodeAlias(root) + utils.CheckForMergeNodes(root) + o.Extensions = low.ExtractExtensions(root) + + // extract externalDocs + extDocs, dErr := low.ExtractObject[*base.ExternalDoc](ctx, base.ExternalDocsLabel, root, idx) + if dErr != nil { + return dErr + } + o.ExternalDocs = extDocs + + // extract parameters + params, ln, vn, pErr := low.ExtractArray[*Parameter](ctx, ParametersLabel, root, idx) + if pErr != nil { + return pErr + } + if params != nil { + o.Parameters = low.NodeReference[[]low.ValueReference[*Parameter]]{ + Value: params, + KeyNode: ln, + ValueNode: vn, + } + } + + // extract responses + respBody, respErr := low.ExtractObject[*Responses](ctx, ResponsesLabel, root, idx) + if respErr != nil { + return respErr + } + o.Responses = respBody + + // extract security + sec, sln, svn, sErr := low.ExtractArray[*base.SecurityRequirement](ctx, SecurityLabel, root, idx) + if sErr != nil { + return sErr + } + if sec != nil { + o.Security = low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]{ + Value: sec, + KeyNode: sln, + ValueNode: svn, + } + } + return nil +} + +// Hash will return a consistent Hash of the Operation object +func (o *Operation) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if !o.Summary.IsEmpty() { + h.WriteString(o.Summary.Value) + h.WriteByte(low.HASH_PIPE) + } + if !o.Description.IsEmpty() { + h.WriteString(o.Description.Value) + h.WriteByte(low.HASH_PIPE) + } + if !o.OperationId.IsEmpty() { + h.WriteString(o.OperationId.Value) + h.WriteByte(low.HASH_PIPE) + } + if !o.ExternalDocs.IsEmpty() { + h.WriteString(low.GenerateHashString(o.ExternalDocs.Value)) + h.WriteByte(low.HASH_PIPE) + } + if !o.Responses.IsEmpty() { + h.WriteString(low.GenerateHashString(o.Responses.Value)) + h.WriteByte(low.HASH_PIPE) + } + if !o.Deprecated.IsEmpty() { + low.HashBool(h, o.Deprecated.Value) + h.WriteByte(low.HASH_PIPE) + } + var keys []string + keys = make([]string, len(o.Tags.Value)) + for k := range o.Tags.Value { + keys[k] = o.Tags.Value[k].Value + } + sort.Strings(keys) + for _, key := range keys { + h.WriteString(key) + h.WriteByte(low.HASH_PIPE) + } + + keys = make([]string, len(o.Consumes.Value)) + for k := range o.Consumes.Value { + keys[k] = o.Consumes.Value[k].Value + } + sort.Strings(keys) + for _, key := range keys { + h.WriteString(key) + h.WriteByte(low.HASH_PIPE) + } + + keys = make([]string, len(o.Produces.Value)) + for k := range o.Produces.Value { + keys[k] = o.Produces.Value[k].Value + } + sort.Strings(keys) + for _, key := range keys { + h.WriteString(key) + h.WriteByte(low.HASH_PIPE) + } + + keys = make([]string, len(o.Schemes.Value)) + for k := range o.Schemes.Value { + keys[k] = o.Schemes.Value[k].Value + } + sort.Strings(keys) + for _, key := range keys { + h.WriteString(key) + h.WriteByte(low.HASH_PIPE) + } + + keys = make([]string, len(o.Parameters.Value)) + for k := range o.Parameters.Value { + keys[k] = low.GenerateHashString(o.Parameters.Value[k].Value) + } + sort.Strings(keys) + for _, key := range keys { + h.WriteString(key) + h.WriteByte(low.HASH_PIPE) + } + + keys = make([]string, len(o.Security.Value)) + for k := range o.Security.Value { + keys[k] = low.GenerateHashString(o.Security.Value[k].Value) + } + sort.Strings(keys) + for _, key := range keys { + h.WriteString(key) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(o.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} + +// methods to satisfy swagger operations interface + +func (o *Operation) GetTags() low.NodeReference[[]low.ValueReference[string]] { + return o.Tags +} + +func (o *Operation) GetSummary() low.NodeReference[string] { + return o.Summary +} + +func (o *Operation) GetDescription() low.NodeReference[string] { + return o.Description +} + +func (o *Operation) GetExternalDocs() low.NodeReference[any] { + return low.NodeReference[any]{ + ValueNode: o.ExternalDocs.ValueNode, + KeyNode: o.ExternalDocs.KeyNode, + Value: o.ExternalDocs.Value, + } +} + +func (o *Operation) GetOperationId() low.NodeReference[string] { + return o.OperationId +} + +func (o *Operation) GetDeprecated() low.NodeReference[bool] { + return o.Deprecated +} + +func (o *Operation) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return o.Extensions +} + +func (o *Operation) GetResponses() low.NodeReference[any] { + return low.NodeReference[any]{ + ValueNode: o.Responses.ValueNode, + KeyNode: o.Responses.KeyNode, + Value: o.Responses.Value, + } +} + +func (o *Operation) GetParameters() low.NodeReference[any] { + return low.NodeReference[any]{ + ValueNode: o.Parameters.ValueNode, + KeyNode: o.Parameters.KeyNode, + Value: o.Parameters.Value, + } +} + +func (o *Operation) GetSecurity() low.NodeReference[any] { + return low.NodeReference[any]{ + ValueNode: o.Security.ValueNode, + KeyNode: o.Security.KeyNode, + Value: o.Security.Value, + } +} + +func (o *Operation) GetSchemes() low.NodeReference[[]low.ValueReference[string]] { + return o.Schemes +} + +func (o *Operation) GetProduces() low.NodeReference[[]low.ValueReference[string]] { + return o.Produces +} + +func (o *Operation) GetConsumes() low.NodeReference[[]low.ValueReference[string]] { + return o.Consumes +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/parameter.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/parameter.go new file mode 100644 index 000000000..e38aaf6c4 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/parameter.go @@ -0,0 +1,315 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "context" + "hash/maphash" + "sort" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Parameter represents a low-level Swagger / OpenAPI 2 Parameter object. +// +// A unique parameter is defined by a combination of a name and location. +// +// There are five possible parameter types. +// +// Path +// +// Used together with Path Templating, where the parameter value is actually part of the operation's URL. +// This does not include the host or base path of the API. For example, in /items/{itemId}, the path parameter is itemId. +// +// Query +// +// Parameters that are appended to the URL. For example, in /items?id=###, the query parameter is id. +// +// Header +// +// Custom headers that are expected as part of the request. +// +// Body +// +// The payload that's appended to the HTTP request. Since there can only be one payload, there can only be one body parameter. +// The name of the body parameter has no effect on the parameter itself and is used for documentation purposes only. +// Since Form parameters are also in the payload, body and form parameters cannot exist together for the same operation. +// +// Form +// +// Used to describe the payload of an HTTP request when either application/x-www-form-urlencoded, multipart/form-data +// or both are used as the content type of the request (in Swagger's definition, the consumes property of an operation). +// This is the only parameter type that can be used to send files, thus supporting the file type. Since form parameters +// are sent in the payload, they cannot be declared together with a body parameter for the same operation. Form +// parameters have a different format based on the content-type used (for further details, +// consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4): +// application/x-www-form-urlencoded - Similar to the format of Query parameters but as a payload. For example, +// foo=1&bar=swagger - both foo and bar are form parameters. This is normally used for simple parameters that are +// being transferred. +// multipart/form-data - each parameter takes a section in the payload with an internal header. For example, for +// the header Content-Disposition: form-data; name="submit-name" the name of the parameter is +// submit-name. This type of form parameters is more commonly used for file transfers +// +// https://swagger.io/specification/v2/#parameterObject +type Parameter struct { + Name low.NodeReference[string] + In low.NodeReference[string] + Type low.NodeReference[string] + Format low.NodeReference[string] + Description low.NodeReference[string] + Required low.NodeReference[bool] + AllowEmptyValue low.NodeReference[bool] + Schema low.NodeReference[*base.SchemaProxy] + Items low.NodeReference[*Items] + CollectionFormat low.NodeReference[string] + Default low.NodeReference[*yaml.Node] + Maximum low.NodeReference[int] + ExclusiveMaximum low.NodeReference[bool] + Minimum low.NodeReference[int] + ExclusiveMinimum low.NodeReference[bool] + MaxLength low.NodeReference[int] + MinLength low.NodeReference[int] + Pattern low.NodeReference[string] + MaxItems low.NodeReference[int] + MinItems low.NodeReference[int] + UniqueItems low.NodeReference[bool] + Enum low.NodeReference[[]low.ValueReference[*yaml.Node]] + MultipleOf low.NodeReference[int] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] +} + +// FindExtension attempts to locate a extension value given a name. +func (p *Parameter) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, p.Extensions) +} + +// GetExtensions returns all Parameter extensions and satisfies the low.HasExtensions interface. +func (p *Parameter) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return p.Extensions +} + +// Build will extract out extensions, schema, items and default value +func (p *Parameter) Build(ctx context.Context, _, root *yaml.Node, idx *index.SpecIndex) error { + root = utils.NodeAlias(root) + utils.CheckForMergeNodes(root) + p.Extensions = low.ExtractExtensions(root) + sch, sErr := base.ExtractSchema(ctx, root, idx) + if sErr != nil { + return sErr + } + if sch != nil { + p.Schema = *sch + } + items, iErr := low.ExtractObject[*Items](ctx, ItemsLabel, root, idx) + if iErr != nil { + return iErr + } + p.Items = items + + _, ln, vn := utils.FindKeyNodeFull(DefaultLabel, root.Content) + if vn != nil { + p.Default = low.NodeReference[*yaml.Node]{ + Value: vn, + KeyNode: ln, + ValueNode: vn, + } + return nil + } + return nil +} + +// Hash will return a consistent Hash of the Parameter object +func (p *Parameter) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if p.Name.Value != "" { + h.WriteString(p.Name.Value) + h.WriteByte(low.HASH_PIPE) + } + if p.In.Value != "" { + h.WriteString(p.In.Value) + h.WriteByte(low.HASH_PIPE) + } + if p.Type.Value != "" { + h.WriteString(p.Type.Value) + h.WriteByte(low.HASH_PIPE) + } + if p.Format.Value != "" { + h.WriteString(p.Format.Value) + h.WriteByte(low.HASH_PIPE) + } + if p.Description.Value != "" { + h.WriteString(p.Description.Value) + h.WriteByte(low.HASH_PIPE) + } + low.HashBool(h, p.Required.Value) + h.WriteByte(low.HASH_PIPE) + low.HashBool(h, p.AllowEmptyValue.Value) + h.WriteByte(low.HASH_PIPE) + if p.Schema.Value != nil { + h.WriteString(low.GenerateHashString(p.Schema.Value.Schema())) + h.WriteByte(low.HASH_PIPE) + } + if p.CollectionFormat.Value != "" { + h.WriteString(p.CollectionFormat.Value) + h.WriteByte(low.HASH_PIPE) + } + if p.Default.Value != nil && !p.Default.Value.IsZero() { + h.WriteString(low.GenerateHashString(p.Default.Value)) + h.WriteByte(low.HASH_PIPE) + } + low.HashInt64(h, int64(p.Maximum.Value)) + h.WriteByte(low.HASH_PIPE) + low.HashInt64(h, int64(p.Minimum.Value)) + h.WriteByte(low.HASH_PIPE) + low.HashBool(h, p.ExclusiveMinimum.Value) + h.WriteByte(low.HASH_PIPE) + low.HashBool(h, p.ExclusiveMaximum.Value) + h.WriteByte(low.HASH_PIPE) + low.HashInt64(h, int64(p.MinLength.Value)) + h.WriteByte(low.HASH_PIPE) + low.HashInt64(h, int64(p.MaxLength.Value)) + h.WriteByte(low.HASH_PIPE) + low.HashInt64(h, int64(p.MinItems.Value)) + h.WriteByte(low.HASH_PIPE) + low.HashInt64(h, int64(p.MaxItems.Value)) + h.WriteByte(low.HASH_PIPE) + low.HashInt64(h, int64(p.MultipleOf.Value)) + h.WriteByte(low.HASH_PIPE) + low.HashBool(h, p.UniqueItems.Value) + h.WriteByte(low.HASH_PIPE) + if p.Pattern.Value != "" { + h.WriteString(p.Pattern.Value) + h.WriteByte(low.HASH_PIPE) + } + + keys := make([]string, len(p.Enum.Value)) + for k := range p.Enum.Value { + keys[k] = low.ValueToString(p.Enum.Value[k].Value) + } + sort.Strings(keys) + for _, key := range keys { + h.WriteString(key) + h.WriteByte(low.HASH_PIPE) + } + + for _, ext := range low.HashExtensions(p.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + if p.Items.Value != nil { + low.HashUint64(h, p.Items.Value.Hash()) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} + +// Getters used by what-changed feature to satisfy the SwaggerParameter interface. + +func (p *Parameter) GetName() *low.NodeReference[string] { + return &p.Name +} + +func (p *Parameter) GetIn() *low.NodeReference[string] { + return &p.In +} + +func (p *Parameter) GetType() *low.NodeReference[string] { + return &p.Type +} + +func (p *Parameter) GetDescription() *low.NodeReference[string] { + return &p.Description +} + +func (p *Parameter) GetRequired() *low.NodeReference[bool] { + return &p.Required +} + +func (p *Parameter) GetAllowEmptyValue() *low.NodeReference[bool] { + return &p.AllowEmptyValue +} + +func (p *Parameter) GetSchema() *low.NodeReference[any] { + i := low.NodeReference[any]{ + KeyNode: p.Schema.KeyNode, + ValueNode: p.Schema.ValueNode, + Value: p.Schema.Value, + } + return &i +} + +func (p *Parameter) GetFormat() *low.NodeReference[string] { + return &p.Format +} + +func (p *Parameter) GetItems() *low.NodeReference[any] { + i := low.NodeReference[any]{ + KeyNode: p.Items.KeyNode, + ValueNode: p.Items.ValueNode, + Value: p.Items.Value, + } + return &i +} + +func (p *Parameter) GetCollectionFormat() *low.NodeReference[string] { + return &p.CollectionFormat +} + +func (p *Parameter) GetDefault() *low.NodeReference[*yaml.Node] { + return &p.Default +} + +func (p *Parameter) GetMaximum() *low.NodeReference[int] { + return &p.Maximum +} + +func (p *Parameter) GetExclusiveMaximum() *low.NodeReference[bool] { + return &p.ExclusiveMaximum +} + +func (p *Parameter) GetMinimum() *low.NodeReference[int] { + return &p.Minimum +} + +func (p *Parameter) GetExclusiveMinimum() *low.NodeReference[bool] { + return &p.ExclusiveMinimum +} + +func (p *Parameter) GetMaxLength() *low.NodeReference[int] { + return &p.MaxLength +} + +func (p *Parameter) GetMinLength() *low.NodeReference[int] { + return &p.MinLength +} + +func (p *Parameter) GetPattern() *low.NodeReference[string] { + return &p.Pattern +} + +func (p *Parameter) GetMaxItems() *low.NodeReference[int] { + return &p.MaxItems +} + +func (p *Parameter) GetMinItems() *low.NodeReference[int] { + return &p.MinItems +} + +func (p *Parameter) GetUniqueItems() *low.NodeReference[bool] { + return &p.UniqueItems +} + +func (p *Parameter) GetEnum() *low.NodeReference[[]low.ValueReference[*yaml.Node]] { + return &p.Enum +} + +func (p *Parameter) GetMultipleOf() *low.NodeReference[int] { + return &p.MultipleOf +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/path_item.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/path_item.go new file mode 100644 index 000000000..85a24a4c1 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/path_item.go @@ -0,0 +1,249 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "context" + "hash/maphash" + "sort" + "strings" + "sync" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// PathItem represents a low-level Swagger / OpenAPI 2 PathItem object. +// +// Describes the operations available on a single path. A Path Item may be empty, due to ACL constraints. +// The path itself is still exposed to the tooling, but will not know which operations and parameters +// are available. +// +// - https://swagger.io/specification/v2/#pathItemObject +type PathItem struct { + Ref low.NodeReference[string] + Get low.NodeReference[*Operation] + Put low.NodeReference[*Operation] + Post low.NodeReference[*Operation] + Delete low.NodeReference[*Operation] + Options low.NodeReference[*Operation] + Head low.NodeReference[*Operation] + Patch low.NodeReference[*Operation] + Parameters low.NodeReference[[]low.ValueReference[*Parameter]] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] +} + +// FindExtension will attempt to locate an extension given a name. +func (p *PathItem) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, p.Extensions) +} + +// GetExtensions returns all PathItem extensions and satisfies the low.HasExtensions interface. +func (p *PathItem) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return p.Extensions +} + +// Build will extract extensions, parameters and operations for all methods. Every method is handled +// asynchronously, in order to keep things moving quickly for complex operations. +func (p *PathItem) Build(ctx context.Context, _, root *yaml.Node, idx *index.SpecIndex) error { + root = utils.NodeAlias(root) + utils.CheckForMergeNodes(root) + p.Extensions = low.ExtractExtensions(root) + skip := false + var currentNode *yaml.Node + + var wg sync.WaitGroup + var errors []error + + var ops []low.NodeReference[*Operation] + + // extract parameters + params, ln, vn, pErr := low.ExtractArray[*Parameter](ctx, ParametersLabel, root, idx) + if pErr != nil { + return pErr + } + if params != nil { + p.Parameters = low.NodeReference[[]low.ValueReference[*Parameter]]{ + Value: params, + KeyNode: ln, + ValueNode: vn, + } + } + + for i, pathNode := range root.Content { + if strings.HasPrefix(strings.ToLower(pathNode.Value), "x-") { + skip = true + continue + } + // because (for some reason) the spec for swagger docs allows for a '$ref' property for path items. + // this is kinda nuts, because '$ref' is a reserved keyword for JSON references, which is ALSO used + // in swagger. Why this choice was made, I do not know. + if strings.Contains(strings.ToLower(pathNode.Value), "$ref") { + rn := root.Content[i+1] + p.Ref = low.NodeReference[string]{ + Value: rn.Value, + ValueNode: rn, + KeyNode: pathNode, + } + skip = true + continue + } + if skip { + skip = false + continue + } + if i%2 == 0 { + currentNode = pathNode + continue + } + + // the only thing we now care about is handling operations, filter out anything that's not a verb. + switch currentNode.Value { + case GetLabel: + case PostLabel: + case PutLabel: + case PatchLabel: + case DeleteLabel: + case HeadLabel: + case OptionsLabel: + default: + continue // ignore everything else. + } + + var op Operation + + wg.Add(1) + + low.BuildModelAsync(pathNode, &op, &wg, &errors) + + opRef := low.NodeReference[*Operation]{ + Value: &op, + KeyNode: currentNode, + ValueNode: pathNode, + } + + ops = append(ops, opRef) + + switch currentNode.Value { + case GetLabel: + p.Get = opRef + case PostLabel: + p.Post = opRef + case PutLabel: + p.Put = opRef + case PatchLabel: + p.Patch = opRef + case DeleteLabel: + p.Delete = opRef + case HeadLabel: + p.Head = opRef + case OptionsLabel: + p.Options = opRef + } + } + + // all operations have been superficially built, + // now we need to build out the operation, we will do this asynchronously for speed. + opBuildChan := make(chan struct{}) + opErrorChan := make(chan error) + + buildOpFunc := func(op low.NodeReference[*Operation], ch chan<- struct{}, errCh chan<- error) { + er := op.Value.Build(ctx, op.KeyNode, op.ValueNode, idx) + if er != nil { + errCh <- er + } + ch <- struct{}{} + } + + if len(ops) <= 0 { + return nil // nothing to do. + } + + for _, op := range ops { + go buildOpFunc(op, opBuildChan, opErrorChan) + } + + n := 0 + total := len(ops) + for n < total { + select { + case buildError := <-opErrorChan: + return buildError + case <-opBuildChan: + n++ + } + } + + // make sure we don't exit before the path is finished building. + if len(ops) > 0 { + wg.Wait() + } + + return nil +} + +// Hash will return a consistent Hash of the PathItem object +func (p *PathItem) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if !p.Get.IsEmpty() { + h.WriteString(GetLabel) + h.WriteByte('-') + h.WriteString(low.GenerateHashString(p.Get.Value)) + h.WriteByte(low.HASH_PIPE) + } + if !p.Put.IsEmpty() { + h.WriteString(PutLabel) + h.WriteByte('-') + h.WriteString(low.GenerateHashString(p.Put.Value)) + h.WriteByte(low.HASH_PIPE) + } + if !p.Post.IsEmpty() { + h.WriteString(PostLabel) + h.WriteByte('-') + h.WriteString(low.GenerateHashString(p.Post.Value)) + h.WriteByte(low.HASH_PIPE) + } + if !p.Delete.IsEmpty() { + h.WriteString(DeleteLabel) + h.WriteByte('-') + h.WriteString(low.GenerateHashString(p.Delete.Value)) + h.WriteByte(low.HASH_PIPE) + } + if !p.Options.IsEmpty() { + h.WriteString(OptionsLabel) + h.WriteByte('-') + h.WriteString(low.GenerateHashString(p.Options.Value)) + h.WriteByte(low.HASH_PIPE) + } + if !p.Head.IsEmpty() { + h.WriteString(HeadLabel) + h.WriteByte('-') + h.WriteString(low.GenerateHashString(p.Head.Value)) + h.WriteByte(low.HASH_PIPE) + } + if !p.Patch.IsEmpty() { + h.WriteString(PatchLabel) + h.WriteByte('-') + h.WriteString(low.GenerateHashString(p.Patch.Value)) + h.WriteByte(low.HASH_PIPE) + } + keys := make([]string, len(p.Parameters.Value)) + for k := range p.Parameters.Value { + keys[k] = low.GenerateHashString(p.Parameters.Value[k].Value) + } + sort.Strings(keys) + for _, key := range keys { + h.WriteString(key) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(p.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/paths.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/paths.go new file mode 100644 index 000000000..dc4b7fb96 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/paths.go @@ -0,0 +1,170 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "context" + "hash/maphash" + "strings" + "sync" + + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Paths represents a low-level Swagger / OpenAPI Paths object. +type Paths struct { + PathItems *orderedmap.Map[low.KeyReference[string], low.ValueReference[*PathItem]] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] +} + +// GetExtensions returns all Paths extensions and satisfies the low.HasExtensions interface. +func (p *Paths) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return p.Extensions +} + +// FindPath attempts to locate a PathItem instance, given a path key. +func (p *Paths) FindPath(path string) (result *low.ValueReference[*PathItem]) { + for pair := orderedmap.First(p.PathItems); pair != nil; pair = pair.Next() { + if pair.Key().Value == path { + result = pair.ValuePtr() + break + } + } + return result +} + +// FindPathAndKey attempts to locate a PathItem instance, given a path key. +func (p *Paths) FindPathAndKey(path string) (key *low.KeyReference[string], value *low.ValueReference[*PathItem]) { + for pair := orderedmap.First(p.PathItems); pair != nil; pair = pair.Next() { + if pair.Key().Value == path { + key = pair.KeyPtr() + value = pair.ValuePtr() + break + } + } + return key, value +} + +// FindExtension will attempt to locate an extension value given a name. +func (p *Paths) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, p.Extensions) +} + +// Build will extract extensions and paths from node. +func (p *Paths) Build(ctx context.Context, _, root *yaml.Node, idx *index.SpecIndex) error { + root = utils.NodeAlias(root) + utils.CheckForMergeNodes(root) + p.Extensions = low.ExtractExtensions(root) + + // Translate YAML nodes to pathsMap using `TranslatePipeline`. + type pathBuildResult struct { + key low.KeyReference[string] + value low.ValueReference[*PathItem] + } + type buildInput struct { + currentNode *yaml.Node + pathNode *yaml.Node + } + pathsMap := orderedmap.New[low.KeyReference[string], low.ValueReference[*PathItem]]() + in := make(chan buildInput) + out := make(chan pathBuildResult) + done := make(chan struct{}) + var wg sync.WaitGroup + wg.Add(2) // input and output goroutines. + + // TranslatePipeline input. + go func() { + defer func() { + close(in) + wg.Done() + }() + skip := false + var currentNode *yaml.Node + for i, pathNode := range root.Content { + if strings.HasPrefix(strings.ToLower(pathNode.Value), "x-") { + skip = true + continue + } + if skip { + skip = false + continue + } + if i%2 == 0 { + currentNode = pathNode + continue + } + + select { + case in <- buildInput{ + currentNode: currentNode, + pathNode: pathNode, + }: + case <-done: + return + } + } + }() + + // TranslatePipeline output. + go func() { + for { + result, ok := <-out + if !ok { + break + } + pathsMap.Set(result.key, result.value) + } + close(done) + wg.Done() + }() + + translateFunc := func(value buildInput) (pathBuildResult, error) { + pNode := value.pathNode + cNode := value.currentNode + path := new(PathItem) + _ = low.BuildModel(pNode, path) + err := path.Build(ctx, cNode, pNode, idx) + if err != nil { + return pathBuildResult{}, err + } + return pathBuildResult{ + key: low.KeyReference[string]{ + Value: cNode.Value, + KeyNode: cNode, + }, + value: low.ValueReference[*PathItem]{ + Value: path, + ValueNode: pNode, + }, + }, nil + } + err := datamodel.TranslatePipeline[buildInput, pathBuildResult](in, out, translateFunc) + wg.Wait() + if err != nil { + return err + } + + p.PathItems = pathsMap + return nil +} + +// Hash will return a consistent Hash of the Paths object +func (p *Paths) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + for v := range orderedmap.SortAlpha(p.PathItems).ValuesFromOldest() { + h.WriteString(low.GenerateHashString(v.Value)) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(p.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/response.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/response.go new file mode 100644 index 000000000..a5bf46303 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/response.go @@ -0,0 +1,103 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "context" + "hash/maphash" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Response is a representation of a high-level Swagger / OpenAPI 2 Response object, backed by a low-level one. +// +// Response describes a single response from an API Operation +// - https://swagger.io/specification/v2/#responseObject +type Response struct { + Description low.NodeReference[string] + Schema low.NodeReference[*base.SchemaProxy] + Headers low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*Header]]] + Examples low.NodeReference[*Examples] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] +} + +// FindExtension will attempt to locate an extension value given a key to lookup. +func (r *Response) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, r.Extensions) +} + +// GetExtensions returns all Response extensions and satisfies the low.HasExtensions interface. +func (r *Response) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return r.Extensions +} + +// FindHeader will attempt to locate a Header value, given a key +func (r *Response) FindHeader(hType string) *low.ValueReference[*Header] { + return low.FindItemInOrderedMap[*Header](hType, r.Headers.Value) +} + +// Build will extract schema, extensions, examples and headers from node +func (r *Response) Build(ctx context.Context, _, root *yaml.Node, idx *index.SpecIndex) error { + root = utils.NodeAlias(root) + utils.CheckForMergeNodes(root) + r.Extensions = low.ExtractExtensions(root) + s, err := base.ExtractSchema(ctx, root, idx) + if err != nil { + return err + } + if s != nil { + r.Schema = *s + } + + // extract examples + examples, expErr := low.ExtractObject[*Examples](ctx, ExamplesLabel, root, idx) + if expErr != nil { + return expErr + } + r.Examples = examples + + // extract headers + headers, lN, kN, err := low.ExtractMap[*Header](ctx, HeadersLabel, root, idx) + if err != nil { + return err + } + if headers != nil { + r.Headers = low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*Header]]]{ + Value: headers, + KeyNode: lN, + ValueNode: kN, + } + } + return nil +} + +// Hash will return a consistent Hash of the Response object +func (r *Response) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if r.Description.Value != "" { + h.WriteString(r.Description.Value) + h.WriteByte(low.HASH_PIPE) + } + if !r.Schema.IsEmpty() { + h.WriteString(low.GenerateHashString(r.Schema.Value)) + h.WriteByte(low.HASH_PIPE) + } + if !r.Examples.IsEmpty() { + for v := range orderedmap.SortAlpha(r.Examples.Value.Values).ValuesFromOldest() { + h.WriteString(low.GenerateHashString(v.Value)) + h.WriteByte(low.HASH_PIPE) + } + } + for _, ext := range low.HashExtensions(r.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/responses.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/responses.go new file mode 100644 index 000000000..f389e284a --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/responses.go @@ -0,0 +1,111 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "context" + "fmt" + "hash/maphash" + "strings" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Responses is a low-level representation of a Swagger / OpenAPI 2 Responses object. +type Responses struct { + Codes *orderedmap.Map[low.KeyReference[string], low.ValueReference[*Response]] + Default low.NodeReference[*Response] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] +} + +// GetExtensions returns all Responses extensions and satisfies the low.HasExtensions interface. +func (r *Responses) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return r.Extensions +} + +// Build will extract default value and extensions from node. +func (r *Responses) Build(ctx context.Context, _, root *yaml.Node, idx *index.SpecIndex) error { + root = utils.NodeAlias(root) + utils.CheckForMergeNodes(root) + r.Extensions = low.ExtractExtensions(root) + + if utils.IsNodeMap(root) { + codes, err := low.ExtractMapNoLookup[*Response](ctx, root, idx) + if err != nil { + return err + } + if codes != nil { + r.Codes = codes + } + def := r.getDefault() + if def != nil { + // default is bundled into codes, pull it out + r.Default = *def + // remove default from codes + r.deleteCode(DefaultLabel) + } + } else { + return fmt.Errorf("responses build failed: vn node is not a map! line %d, col %d", + root.Line, root.Column) + } + return nil +} + +func (r *Responses) getDefault() *low.NodeReference[*Response] { + for code, resp := range r.Codes.FromOldest() { + if strings.ToLower(code.Value) == DefaultLabel { + return &low.NodeReference[*Response]{ + ValueNode: resp.ValueNode, + KeyNode: code.KeyNode, + Value: resp.Value, + } + } + } + return nil +} + +// used to remove default from codes extracted by Build() +func (r *Responses) deleteCode(code string) { + var key *low.KeyReference[string] + if r.Codes != nil { + for pair := orderedmap.First(r.Codes); pair != nil; pair = pair.Next() { + if pair.Key().Value == code { + key = pair.KeyPtr() + break + } + } + } + // should never be nil, but, you never know... science and all that! + if key != nil { + r.Codes.Delete(*key) + } +} + +// FindResponseByCode will attempt to locate a Response instance using an HTTP response code string. +func (r *Responses) FindResponseByCode(code string) *low.ValueReference[*Response] { + return low.FindItemInOrderedMap[*Response](code, r.Codes) +} + +// Hash will return a consistent Hash of the Responses object +func (r *Responses) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + for _, hash := range low.AppendMapHashes(nil, orderedmap.SortAlpha(r.Codes)) { + h.WriteString(hash) + h.WriteByte(low.HASH_PIPE) + } + if !r.Default.IsEmpty() { + h.WriteString(low.GenerateHashString(r.Default.Value)) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(r.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/scopes.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/scopes.go new file mode 100644 index 000000000..0b22ddea6 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/scopes.go @@ -0,0 +1,80 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "context" + "fmt" + "hash/maphash" + "strings" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Scopes is a low-level representation of a Swagger / OpenAPI 2 OAuth2 Scopes object. +// +// Scopes lists the available scopes for an OAuth2 security scheme. +// - https://swagger.io/specification/v2/#scopesObject +type Scopes struct { + Values *orderedmap.Map[low.KeyReference[string], low.ValueReference[string]] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] +} + +// GetExtensions returns all Scopes extensions and satisfies the low.HasExtensions interface. +func (s *Scopes) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return s.Extensions +} + +// FindScope will attempt to locate a scope string using a key. +func (s *Scopes) FindScope(scope string) *low.ValueReference[string] { + return low.FindItemInOrderedMap[string](scope, s.Values) +} + +// Build will extract scope values and extensions from node. +func (s *Scopes) Build(_ context.Context, _, root *yaml.Node, _ *index.SpecIndex) error { + root = utils.NodeAlias(root) + utils.CheckForMergeNodes(root) + s.Extensions = low.ExtractExtensions(root) + valueMap := orderedmap.New[low.KeyReference[string], low.ValueReference[string]]() + if utils.IsNodeMap(root) { + for k := range root.Content { + if k%2 == 0 { + if strings.Contains(root.Content[k].Value, "x-") { + continue + } + valueMap.Set( + low.KeyReference[string]{ + Value: root.Content[k].Value, + KeyNode: root.Content[k], + }, + low.ValueReference[string]{ + Value: root.Content[k+1].Value, + ValueNode: root.Content[k+1], + }, + ) + } + } + s.Values = valueMap + } + return nil +} + +// Hash will return a consistent Hash of the Scopes object +func (s *Scopes) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + for k, v := range orderedmap.SortAlpha(s.Values).FromOldest() { + h.WriteString(fmt.Sprintf("%s-%s", k.Value, v.Value)) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(s.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/security_scheme.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/security_scheme.go new file mode 100644 index 000000000..6e93e7d49 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/security_scheme.go @@ -0,0 +1,95 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v2 + +import ( + "context" + "hash/maphash" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// SecurityScheme is a low-level representation of a Swagger / OpenAPI 2 SecurityScheme object. +// +// SecurityScheme allows the definition of a security scheme that can be used by the operations. Supported schemes are +// basic authentication, an API key (either as a header or as a query parameter) and OAuth2's common flows +// (implicit, password, application and access code) +// - https://swagger.io/specification/v2/#securityDefinitionsObject +type SecurityScheme struct { + Type low.NodeReference[string] + Description low.NodeReference[string] + Name low.NodeReference[string] + In low.NodeReference[string] + Flow low.NodeReference[string] + AuthorizationUrl low.NodeReference[string] + TokenUrl low.NodeReference[string] + Scopes low.NodeReference[*Scopes] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] +} + +// GetExtensions returns all SecurityScheme extensions and satisfies the low.HasExtensions interface. +func (ss *SecurityScheme) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return ss.Extensions +} + +// Build will extract extensions and scopes from the node. +func (ss *SecurityScheme) Build(ctx context.Context, _, root *yaml.Node, idx *index.SpecIndex) error { + root = utils.NodeAlias(root) + utils.CheckForMergeNodes(root) + ss.Extensions = low.ExtractExtensions(root) + + scopes, sErr := low.ExtractObject[*Scopes](ctx, ScopesLabel, root, idx) + if sErr != nil { + return sErr + } + ss.Scopes = scopes + return nil +} + +// Hash will return a consistent Hash of the SecurityScheme object +func (ss *SecurityScheme) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if !ss.Type.IsEmpty() { + h.WriteString(ss.Type.Value) + h.WriteByte(low.HASH_PIPE) + } + if !ss.Description.IsEmpty() { + h.WriteString(ss.Description.Value) + h.WriteByte(low.HASH_PIPE) + } + if !ss.Name.IsEmpty() { + h.WriteString(ss.Name.Value) + h.WriteByte(low.HASH_PIPE) + } + if !ss.In.IsEmpty() { + h.WriteString(ss.In.Value) + h.WriteByte(low.HASH_PIPE) + } + if !ss.Flow.IsEmpty() { + h.WriteString(ss.Flow.Value) + h.WriteByte(low.HASH_PIPE) + } + if !ss.AuthorizationUrl.IsEmpty() { + h.WriteString(ss.AuthorizationUrl.Value) + h.WriteByte(low.HASH_PIPE) + } + if !ss.TokenUrl.IsEmpty() { + h.WriteString(ss.TokenUrl.Value) + h.WriteByte(low.HASH_PIPE) + } + if !ss.Scopes.IsEmpty() { + h.WriteString(low.GenerateHashString(ss.Scopes.Value)) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(ss.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/swagger.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/swagger.go new file mode 100644 index 000000000..835836ad2 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v2/swagger.go @@ -0,0 +1,370 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +// Package v2 represents all Swagger / OpenAPI 2 low-level models. +// +// Low-level models are more difficult to navigate than higher-level models, however they are packed with all the +// raw AST and node data required to perform any kind of analysis on the underlying data. +// +// Every property is wrapped in a NodeReference or a KeyReference or a ValueReference. +// +// IMPORTANT: As a general rule, Swagger / OpenAPI 2 should be avoided for new projects. +package v2 + +import ( + "context" + "errors" + "path/filepath" + + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// processes a property of a Swagger document asynchronously using bool and error channels for signals. +type documentFunction func(ctx context.Context, root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- struct{}, e chan<- error) + +// Swagger represents a high-level Swagger / OpenAPI 2 document. An instance of Swagger is the root of the specification. +type Swagger struct { + // Swagger is the version of Swagger / OpenAPI being used, extracted from the 'swagger: 2.x' definition. + Swagger low.ValueReference[string] + + // Info represents a specification Info definition. + // Provides metadata about the API. The metadata can be used by the clients if needed. + // - https://swagger.io/specification/v2/#infoObject + Info low.NodeReference[*base.Info] + + // Host is The host (name or ip) serving the API. This MUST be the host only and does not include the scheme nor + // sub-paths. It MAY include a port. If the host is not included, the host serving the documentation is to be used + // (including the port). The host does not support path templating. + Host low.NodeReference[string] + + // BasePath is The base path on which the API is served, which is relative to the host. If it is not included, + // the API is served directly under the host. The value MUST start with a leading slash (/). + // The basePath does not support path templating. + BasePath low.NodeReference[string] + + // Schemes represents the transfer protocol of the API. Requirements MUST be from the list: "http", "https", "ws", "wss". + // If the schemes is not included, the default scheme to be used is the one used to access + // the Swagger definition itself. + Schemes low.NodeReference[[]low.ValueReference[string]] + + // Consumes is a list of MIME types the APIs can consume. This is global to all APIs but can be overridden on + // specific API calls. Value MUST be as described under Mime Types. + Consumes low.NodeReference[[]low.ValueReference[string]] + + // Produces is a list of MIME types the APIs can produce. This is global to all APIs but can be overridden on + // specific API calls. Value MUST be as described under Mime Types. + Produces low.NodeReference[[]low.ValueReference[string]] + + // Paths are the paths and operations for the API. Perhaps the most important part of the specification. + // - https://swagger.io/specification/v2/#pathsObject + Paths low.NodeReference[*Paths] + + // Definitions is an object to hold data types produced and consumed by operations. It's composed of Schema instances + // - https://swagger.io/specification/v2/#definitionsObject + Definitions low.NodeReference[*Definitions] + + // SecurityDefinitions represents security scheme definitions that can be used across the specification. + // - https://swagger.io/specification/v2/#securityDefinitionsObject + SecurityDefinitions low.NodeReference[*SecurityDefinitions] + + // Parameters is an object to hold parameters that can be used across operations. + // This property does not define global parameters for all operations. + // - https://swagger.io/specification/v2/#parametersDefinitionsObject + Parameters low.NodeReference[*ParameterDefinitions] + + // Responses is an object to hold responses that can be used across operations. + // This property does not define global responses for all operations. + // - https://swagger.io/specification/v2/#responsesDefinitionsObject + Responses low.NodeReference[*ResponsesDefinitions] + + // Security is a declaration of which security schemes are applied for the API as a whole. The list of values + // describes alternative security schemes that can be used (that is, there is a logical OR between the security + // requirements). Individual operations can override this definition. + // - https://swagger.io/specification/v2/#securityRequirementObject + Security low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]] + + // Tags are A list of tags used by the specification with additional metadata. + // The order of the tags can be used to reflect on their order by the parsing tools. Not all tags that are used + // by the Operation Object must be declared. The tags that are not declared may be organized randomly or based + // on the tools' logic. Each tag name in the list MUST be unique. + // - https://swagger.io/specification/v2/#tagObject + Tags low.NodeReference[[]low.ValueReference[*base.Tag]] + + // ExternalDocs is an instance of base.ExternalDoc for.. well, obvious really, innit mate? + ExternalDocs low.NodeReference[*base.ExternalDoc] + + // Extensions contains all custom extensions defined for the top-level document. + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + + // Index is a reference to the index.SpecIndex that was created for the document and used + // as a guide when building out the Document. Ideal if further processing is required on the model and + // the original details are required to continue the work. + // + // This property is not a part of the OpenAPI schema, this is custom to libopenapi. + Index *index.SpecIndex + + // SpecInfo is a reference to the datamodel.SpecInfo instance created when the specification was read. + // + // This property is not a part of the OpenAPI schema, this is custom to libopenapi. + SpecInfo *datamodel.SpecInfo + + // Rolodex is a reference to the index.Rolodex instance created when the specification was read. + // The rolodex is used to look up references from file systems (local or remote) + Rolodex *index.Rolodex +} + +// FindExtension locates an extension from the root of the Swagger document. +func (s *Swagger) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, s.Extensions) +} + +// GetExtensions returns all Swagger/Top level extensions and satisfies the low.HasExtensions interface. +func (s *Swagger) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return s.Extensions +} + +// CreateDocumentFromConfig will create a new Swagger document from the provided SpecInfo and DocumentConfiguration. +func CreateDocumentFromConfig(info *datamodel.SpecInfo, + configuration *datamodel.DocumentConfiguration, +) (*Swagger, error) { + return createDocument(info, configuration) +} + +func createDocument(info *datamodel.SpecInfo, config *datamodel.DocumentConfiguration) (*Swagger, error) { + doc := Swagger{Swagger: low.ValueReference[string]{Value: info.Version, ValueNode: info.RootNode}} + doc.Extensions = low.ExtractExtensions(info.RootNode.Content[0]) + + // create an index config and shadow the document configuration. + idxConfig := index.CreateClosedAPIIndexConfig() + idxConfig.SpecInfo = info + idxConfig.IgnoreArrayCircularReferences = config.IgnoreArrayCircularReferences + idxConfig.IgnorePolymorphicCircularReferences = config.IgnorePolymorphicCircularReferences + idxConfig.AllowUnknownExtensionContentDetection = config.AllowUnknownExtensionContentDetection + idxConfig.SkipExternalRefResolution = config.SkipExternalRefResolution + idxConfig.AvoidCircularReferenceCheck = true + idxConfig.BaseURL = config.BaseURL + idxConfig.BasePath = config.BasePath + idxConfig.Logger = config.Logger + idxConfig.ExcludeExtensionRefs = config.ExcludeExtensionRefs + rolodex := index.NewRolodex(idxConfig) + rolodex.SetRootNode(info.RootNode) + doc.Rolodex = rolodex + + // If basePath is provided, add a local filesystem to the rolodex. + if idxConfig.BasePath != "" { + var cwd string + cwd, _ = filepath.Abs(config.BasePath) + // if a supplied local filesystem is provided, add it to the rolodex. + if config.LocalFS != nil { + var localFS index.RolodexFS + if fs, ok := config.LocalFS.(index.RolodexFS); ok { + localFS = fs + } else { + // wrap a plain fs.FS so it can be indexed. + localFSConf := index.LocalFSConfig{ + BaseDirectory: cwd, + IndexConfig: idxConfig, + FileFilters: config.FileFilter, + DirFS: config.LocalFS, + } + + localFS, _ = index.NewLocalFSWithConfig(&localFSConf) + idxConfig.AllowFileLookup = true + } + + rolodex.AddLocalFS(cwd, localFS) + } else { + + // create a local filesystem + localFSConf := index.LocalFSConfig{ + BaseDirectory: cwd, + IndexConfig: idxConfig, + FileFilters: config.FileFilter, + } + fileFS, _ := index.NewLocalFSWithConfig(&localFSConf) + idxConfig.AllowFileLookup = true + + // add the filesystem to the rolodex + rolodex.AddLocalFS(cwd, fileFS) + } + } + + // if base url is provided, add a remote filesystem to the rolodex. + if idxConfig.BaseURL != nil { + + // create a remote filesystem + remoteFS, _ := index.NewRemoteFSWithConfig(idxConfig) + if config.RemoteURLHandler != nil { + remoteFS.RemoteHandlerFunc = config.RemoteURLHandler + } + idxConfig.AllowRemoteLookup = true + + // add to the rolodex + rolodex.AddRemoteFS(config.BaseURL.String(), remoteFS) + + } + + doc.Rolodex = rolodex + + var errs []error + + // index all the things! + _ = rolodex.IndexTheRolodex(context.Background()) + + // check for circular references + if !config.SkipCircularReferenceCheck { + rolodex.CheckForCircularReferences() + } + + // extract errors + roloErrs := rolodex.GetCaughtErrors() + if roloErrs != nil { + errs = append(errs, roloErrs...) + } + + // set the index on the document. + doc.Index = rolodex.GetRootIndex() + doc.SpecInfo = info + + // build out swagger scalar variables. + _ = low.BuildModel(info.RootNode.Content[0], &doc) + + ctx := context.Background() + + // extract externalDocs + extDocs, err := low.ExtractObject[*base.ExternalDoc](ctx, base.ExternalDocsLabel, info.RootNode, rolodex.GetRootIndex()) + if err != nil { + errs = append(errs, err) + } + + doc.ExternalDocs = extDocs + + extractionFuncs := []documentFunction{ + extractInfo, + extractPaths, + extractDefinitions, + extractParamDefinitions, + extractResponsesDefinitions, + extractSecurityDefinitions, + extractTags, + extractSecurity, + } + doneChan := make(chan struct{}) + errChan := make(chan error) + for i := range extractionFuncs { + go extractionFuncs[i](ctx, info.RootNode.Content[0], &doc, rolodex.GetRootIndex(), doneChan, errChan) + } + completedExtractions := 0 + for completedExtractions < len(extractionFuncs) { + select { + case <-doneChan: + completedExtractions++ + case e := <-errChan: + completedExtractions++ + errs = append(errs, e) + } + } + + return &doc, errors.Join(errs...) +} + +func (s *Swagger) GetExternalDocs() *low.NodeReference[any] { + return &low.NodeReference[any]{ + KeyNode: s.ExternalDocs.KeyNode, + ValueNode: s.ExternalDocs.ValueNode, + Value: s.ExternalDocs.Value, + } +} + +func extractInfo(ctx context.Context, root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- struct{}, e chan<- error) { + info, err := low.ExtractObject[*base.Info](ctx, base.InfoLabel, root, idx) + if err != nil { + e <- err + return + } + doc.Info = info + c <- struct{}{} +} + +func extractPaths(ctx context.Context, root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- struct{}, e chan<- error) { + paths, err := low.ExtractObject[*Paths](ctx, PathsLabel, root, idx) + if err != nil { + e <- err + return + } + doc.Paths = paths + c <- struct{}{} +} + +func extractDefinitions(ctx context.Context, root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- struct{}, e chan<- error) { + def, err := low.ExtractObject[*Definitions](ctx, DefinitionsLabel, root, idx) + if err != nil { + e <- err + return + } + doc.Definitions = def + c <- struct{}{} +} + +func extractParamDefinitions(ctx context.Context, root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- struct{}, e chan<- error) { + param, err := low.ExtractObject[*ParameterDefinitions](ctx, ParametersLabel, root, idx) + if err != nil { + e <- err + return + } + doc.Parameters = param + c <- struct{}{} +} + +func extractResponsesDefinitions(ctx context.Context, root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- struct{}, e chan<- error) { + resp, err := low.ExtractObject[*ResponsesDefinitions](ctx, ResponsesLabel, root, idx) + if err != nil { + e <- err + return + } + doc.Responses = resp + c <- struct{}{} +} + +func extractSecurityDefinitions(ctx context.Context, root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- struct{}, e chan<- error) { + sec, err := low.ExtractObject[*SecurityDefinitions](ctx, SecurityDefinitionsLabel, root, idx) + if err != nil { + e <- err + return + } + doc.SecurityDefinitions = sec + c <- struct{}{} +} + +func extractTags(ctx context.Context, root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- struct{}, e chan<- error) { + tags, ln, vn, err := low.ExtractArray[*base.Tag](ctx, base.TagsLabel, root, idx) + if err != nil { + e <- err + return + } + doc.Tags = low.NodeReference[[]low.ValueReference[*base.Tag]]{ + Value: tags, + KeyNode: ln, + ValueNode: vn, + } + c <- struct{}{} +} + +func extractSecurity(ctx context.Context, root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- struct{}, e chan<- error) { + sec, ln, vn, err := low.ExtractArray[*base.SecurityRequirement](ctx, SecurityLabel, root, idx) + if err != nil { + e <- err + return + } + doc.Security = low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]{ + Value: sec, + KeyNode: ln, + ValueNode: vn, + } + c <- struct{}{} +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/callback.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/callback.go new file mode 100644 index 000000000..b1014745e --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/callback.go @@ -0,0 +1,108 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + "hash/maphash" + + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "go.yaml.in/yaml/v4" +) + +// Callback represents a low-level Callback object for OpenAPI 3+. +// +// A map of possible out-of band callbacks related to the parent operation. Each value in the map is a +// PathItem Object that describes a set of requests that may be initiated by the API provider and the expected +// responses. The key value used to identify the path item object is an expression, evaluated at runtime, +// that identifies a URL to use for the callback operation. +// - https://spec.openapis.org/oas/v3.1.0#callback-object +type Callback struct { + Expression *orderedmap.Map[low.KeyReference[string], low.ValueReference[*PathItem]] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// GetIndex returns the index.SpecIndex instance attached to the Callback object +func (cb *Callback) GetIndex() *index.SpecIndex { + return cb.index +} + +// GetContext returns the context.Context instance used when building the Callback object +func (cb *Callback) GetContext() context.Context { + return cb.context +} + +// GetExtensions returns all Callback extensions and satisfies the low.HasExtensions interface. +func (cb *Callback) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return cb.Extensions +} + +// GetRootNode returns the root yaml node of the Callback object +func (cb *Callback) GetRootNode() *yaml.Node { + return cb.RootNode +} + +// GetKeyNode returns the key yaml node of the Callback object +func (cb *Callback) GetKeyNode() *yaml.Node { + return cb.KeyNode +} + +// FindExpression will locate a string expression and return a ValueReference containing the located PathItem +func (cb *Callback) FindExpression(exp string) *low.ValueReference[*PathItem] { + return low.FindItemInOrderedMap(exp, cb.Expression) +} + +// Build will extract extensions, expressions and PathItem objects for Callback +func (cb *Callback) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + cb.KeyNode = keyNode + cb.Reference = new(low.Reference) + if ok, _, ref := utils.IsNodeRefValue(root); ok { + cb.SetReference(ref, root) + } + root = utils.NodeAlias(root) + cb.RootNode = root + utils.CheckForMergeNodes(root) + cb.Nodes = low.ExtractNodes(ctx, root) + cb.Extensions = low.ExtractExtensions(root) + cb.context = ctx + cb.index = idx + + low.ExtractExtensionNodes(ctx, cb.Extensions, cb.Nodes) + + expressions, err := extractPathItemsMap(ctx, root, idx) + if err != nil { + return err + } + cb.Expression = expressions + for k := range expressions.KeysFromOldest() { + cb.Nodes.Store(k.KeyNode.Line, k.KeyNode) + } + return nil +} + +// Hash will return a consistent Hash of the Callback object +func (cb *Callback) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + for v := range orderedmap.SortAlpha(cb.Expression).ValuesFromOldest() { + h.WriteString(low.GenerateHashString(v.Value)) + h.WriteByte(low.HASH_PIPE) + } + + for _, ext := range low.HashExtensions(cb.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/components.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/components.go new file mode 100644 index 000000000..7edde619f --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/components.go @@ -0,0 +1,378 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + "fmt" + "hash/maphash" + "reflect" + "sync" + + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Components represents a low-level OpenAPI 3+ Components Object, that is backed by a low-level one. +// +// Holds a set of reusable objects for different aspects of the OAS. All objects defined within the components object +// will have no effect on the API unless they are explicitly referenced from properties outside the components object. +// - https://spec.openapis.org/oas/v3.1.0#components-object +type Components struct { + Schemas low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.SchemaProxy]]] + Responses low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*Response]]] + Parameters low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*Parameter]]] + Examples low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.Example]]] + RequestBodies low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*RequestBody]]] + Headers low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*Header]]] + SecuritySchemes low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*SecurityScheme]]] + Links low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*Link]]] + Callbacks low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*Callback]]] + PathItems low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*PathItem]]] + MediaTypes low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*MediaType]]] // OpenAPI 3.2+ mediaTypes section + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +type componentBuildResult[T any] struct { + key low.KeyReference[string] + value low.ValueReference[T] +} + +type componentInput struct { + node *yaml.Node + currentLabel *yaml.Node +} + +// GetIndex returns the index.SpecIndex instance attached to the Components object +func (co *Components) GetIndex() *index.SpecIndex { + return co.index +} + +// GetContext returns the context.Context instance used when building the Components object +func (co *Components) GetContext() context.Context { + return co.context +} + +// GetExtensions returns all Components extensions and satisfies the low.HasExtensions interface. +func (co *Components) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return co.Extensions +} + +// GetRootNode returns the root yaml node of the Components object +func (co *Components) GetRootNode() *yaml.Node { + return co.RootNode +} + +// GetKeyNode returns the key yaml node of the Components object +func (co *Components) GetKeyNode() *yaml.Node { + return co.KeyNode +} + +// Hash will return a consistent Hash of the Components object +func (co *Components) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + generateHashForObjectMap(co.Schemas.Value, h) + generateHashForObjectMap(co.Responses.Value, h) + generateHashForObjectMap(co.Parameters.Value, h) + generateHashForObjectMap(co.Examples.Value, h) + generateHashForObjectMap(co.RequestBodies.Value, h) + generateHashForObjectMap(co.Headers.Value, h) + generateHashForObjectMap(co.SecuritySchemes.Value, h) + generateHashForObjectMap(co.Links.Value, h) + generateHashForObjectMap(co.Callbacks.Value, h) + generateHashForObjectMap(co.PathItems.Value, h) + generateHashForObjectMap(co.MediaTypes.Value, h) + for _, ext := range low.HashExtensions(co.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} + +func generateHashForObjectMap[T any](collection *orderedmap.Map[low.KeyReference[string], low.ValueReference[T]], h *maphash.Hash) { + for v := range orderedmap.SortAlpha(collection).ValuesFromOldest() { + h.WriteString(low.GenerateHashString(v.Value)) + h.WriteByte(low.HASH_PIPE) + } +} + +// FindExtension attempts to locate an extension with the supplied key +func (co *Components) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, co.Extensions) +} + +// FindSchema attempts to locate a SchemaProxy from 'schemas' with a specific name +func (co *Components) FindSchema(schema string) *low.ValueReference[*base.SchemaProxy] { + return low.FindItemInOrderedMap[*base.SchemaProxy](schema, co.Schemas.Value) +} + +// FindResponse attempts to locate a Response from 'responses' with a specific name +func (co *Components) FindResponse(response string) *low.ValueReference[*Response] { + return low.FindItemInOrderedMap[*Response](response, co.Responses.Value) +} + +// FindParameter attempts to locate a Parameter from 'parameters' with a specific name +func (co *Components) FindParameter(response string) *low.ValueReference[*Parameter] { + return low.FindItemInOrderedMap[*Parameter](response, co.Parameters.Value) +} + +// FindSecurityScheme attempts to locate a SecurityScheme from 'securitySchemes' with a specific name +func (co *Components) FindSecurityScheme(sScheme string) *low.ValueReference[*SecurityScheme] { + return low.FindItemInOrderedMap[*SecurityScheme](sScheme, co.SecuritySchemes.Value) +} + +// FindExample attempts tp +func (co *Components) FindExample(example string) *low.ValueReference[*base.Example] { + return low.FindItemInOrderedMap[*base.Example](example, co.Examples.Value) +} + +func (co *Components) FindRequestBody(requestBody string) *low.ValueReference[*RequestBody] { + return low.FindItemInOrderedMap[*RequestBody](requestBody, co.RequestBodies.Value) +} + +func (co *Components) FindHeader(header string) *low.ValueReference[*Header] { + return low.FindItemInOrderedMap[*Header](header, co.Headers.Value) +} + +func (co *Components) FindLink(link string) *low.ValueReference[*Link] { + return low.FindItemInOrderedMap[*Link](link, co.Links.Value) +} + +func (co *Components) FindPathItem(path string) *low.ValueReference[*PathItem] { + return low.FindItemInOrderedMap[*PathItem](path, co.PathItems.Value) +} + +func (co *Components) FindCallback(callback string) *low.ValueReference[*Callback] { + return low.FindItemInOrderedMap[*Callback](callback, co.Callbacks.Value) +} + +// FindMediaType attempts to locate a MediaType from 'mediaTypes' with a specific name +func (co *Components) FindMediaType(mediaType string) *low.ValueReference[*MediaType] { + return low.FindItemInOrderedMap[*MediaType](mediaType, co.MediaTypes.Value) +} + +// Build converts root YAML node containing components to low level model. +// Process each component in parallel. +func (co *Components) Build(ctx context.Context, root *yaml.Node, idx *index.SpecIndex) error { + root = utils.NodeAlias(root) + utils.CheckForMergeNodes(root) + co.Reference = new(low.Reference) + co.Nodes = low.ExtractNodes(ctx, root) + co.Extensions = low.ExtractExtensions(root) + low.ExtractExtensionNodes(ctx, co.Extensions, co.Nodes) + co.RootNode = root + co.KeyNode = root + co.index = idx + co.context = ctx + + var reterr error + var ceMutex sync.Mutex + var wg sync.WaitGroup + wg.Add(11) + + captureError := func(err error) { + ceMutex.Lock() + defer ceMutex.Unlock() + if err != nil { + reterr = err + } + } + + go func() { + schemas, err := extractComponentValues[*base.SchemaProxy](ctx, SchemasLabel, root, idx, co) + captureError(err) + co.Schemas = schemas + wg.Done() + }() + go func() { + parameters, err := extractComponentValues[*Parameter](ctx, ParametersLabel, root, idx, co) + captureError(err) + co.Parameters = parameters + wg.Done() + }() + go func() { + responses, err := extractComponentValues[*Response](ctx, ResponsesLabel, root, idx, co) + captureError(err) + co.Responses = responses + wg.Done() + }() + go func() { + examples, err := extractComponentValues[*base.Example](ctx, base.ExamplesLabel, root, idx, co) + captureError(err) + co.Examples = examples + wg.Done() + }() + go func() { + requestBodies, err := extractComponentValues[*RequestBody](ctx, RequestBodiesLabel, root, idx, co) + captureError(err) + co.RequestBodies = requestBodies + wg.Done() + }() + go func() { + headers, err := extractComponentValues[*Header](ctx, HeadersLabel, root, idx, co) + captureError(err) + co.Headers = headers + wg.Done() + }() + go func() { + securitySchemes, err := extractComponentValues[*SecurityScheme](ctx, SecuritySchemesLabel, root, idx, co) + captureError(err) + co.SecuritySchemes = securitySchemes + wg.Done() + }() + go func() { + links, err := extractComponentValues[*Link](ctx, LinksLabel, root, idx, co) + captureError(err) + co.Links = links + wg.Done() + }() + go func() { + callbacks, err := extractComponentValues[*Callback](ctx, CallbacksLabel, root, idx, co) + captureError(err) + co.Callbacks = callbacks + wg.Done() + }() + go func() { + pathItems, err := extractComponentValues[*PathItem](ctx, PathItemsLabel, root, idx, co) + captureError(err) + co.PathItems = pathItems + wg.Done() + }() + go func() { + mediaTypes, err := extractComponentValues[*MediaType](ctx, MediaTypesLabel, root, idx, co) + captureError(err) + co.MediaTypes = mediaTypes + wg.Done() + }() + + wg.Wait() + return reterr +} + +// extractComponentValues converts all the YAML nodes of a component type to +// low level model. +// Process each node in parallel. +func extractComponentValues[T low.Buildable[N], N any](ctx context.Context, label string, root *yaml.Node, idx *index.SpecIndex, co *Components) (low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[T]]], error) { + var emptyResult low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[T]]] + _, nodeLabel, nodeValue := utils.FindKeyNodeFullTop(label, root.Content) + if nodeValue == nil { + return emptyResult, nil + } + co.Nodes.Store(nodeLabel.Line, nodeLabel) + componentValues := orderedmap.New[low.KeyReference[string], low.ValueReference[T]]() + if utils.IsNodeArray(nodeValue) { + return emptyResult, fmt.Errorf("node is array, cannot be used in components: line %d, column %d", nodeValue.Line, nodeValue.Column) + } + + in := make(chan componentInput) + out := make(chan componentBuildResult[T]) + done := make(chan struct{}) + var wg sync.WaitGroup + wg.Add(2) // input and output goroutines. + + // Send input. + go func() { + defer func() { + close(in) + wg.Done() + }() + var currentLabel *yaml.Node + for i, node := range nodeValue.Content { + // always ignore extensions + if i%2 == 0 { + currentLabel = node + continue + } + + select { + case in <- componentInput{ + node: node, + currentLabel: currentLabel, + }: + case <-done: + return + } + } + }() + + // Collect output. + go func() { + for result := range out { + componentValues.Set(result.key, result.value) + } + close(done) + wg.Done() + }() + + // Translate. + translateFunc := func(value componentInput) (componentBuildResult[T], error) { + var n T = new(N) + currentLabel := value.currentLabel + node := value.node + + // build. + _ = low.BuildModel(node, n) + err := n.Build(ctx, currentLabel, node, idx) + if err != nil { + return componentBuildResult[T]{}, err + } + + nType := reflect.TypeOf(n) + nValue := reflect.ValueOf(n) + + // for SchemaProxy, use the transformed node from sp.vn instead of original node + finalValueNode := node + if valueNodeGetter, ok := nValue.Interface().(low.HasValueNodeUntyped); ok { + if transformedNode := valueNodeGetter.GetValueNode(); transformedNode != nil { + finalValueNode = transformedNode + } + } + + // Check if the type implements low.HasKeyNode + hasKeyNodeType := reflect.TypeOf((*low.HasKeyNode)(nil)).Elem() + if nType.Implements(hasKeyNodeType) { + r := nValue.Interface() + if h, ok := r.(low.HasKeyNode); ok { + if k, ko := r.(low.AddNodes); ko { + k.AddNode(h.GetKeyNode().Line, h.GetKeyNode()) + } + } + + } + return componentBuildResult[T]{ + key: low.KeyReference[string]{ + KeyNode: currentLabel, + Value: currentLabel.Value, + }, + value: low.ValueReference[T]{ + Value: n, + ValueNode: finalValueNode, // use transformed node if available + }, + }, nil + } + err := datamodel.TranslatePipeline[componentInput, componentBuildResult[T]](in, out, translateFunc) + wg.Wait() + if err != nil { + return emptyResult, err + } + + results := low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[T]]]{ + KeyNode: nodeLabel, + ValueNode: nodeValue, + Value: componentValues, + } + return results, nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/constants.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/constants.go new file mode 100644 index 000000000..473c26765 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/constants.go @@ -0,0 +1,158 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +// Label definitions used to look up vales in yaml.Node tree. +const ( + ComponentsLabel = "components" + SchemasLabel = "schemas" + MediaTypesLabel = "mediaTypes" // OpenAPI 3.2+ mediaTypes component section + EncodingLabel = "encoding" + ItemSchemaLabel = "itemSchema" + ItemEncodingLabel = "itemEncoding" + HeadersLabel = "headers" + ExpressionLabel = "expression" + InfoLabel = "info" + SwaggerLabel = "swagger" + ParametersLabel = "parameters" + ParameterLabel = "parameter" + RequestBodyLabel = "requestBody" + RequestBodiesLabel = "requestBodies" + ResponsesLabel = "responses" + ResponseLabel = "response" + CallbacksLabel = "callbacks" + ContentLabel = "content" + PathsLabel = "paths" + PathItemsLabel = "pathItems" + PathLabel = "path" + WebhooksLabel = "webhooks" + JSONSchemaDialectLabel = "jsonSchemaDialect" + JSONSchemaLabel = "$schema" + SelfLabel = "$self" + GetLabel = "get" + PostLabel = "post" + PatchLabel = "patch" + PutLabel = "put" + DeleteLabel = "delete" + OptionsLabel = "options" + HeadLabel = "head" + TraceLabel = "trace" + QueryLabel = "query" + AdditionalOperationsLabel = "additionalOperations" // OpenAPI 3.2+ additional operations + LinksLabel = "links" + DefaultLabel = "default" + ConstLabel = "const" + SecurityLabel = "security" + SecuritySchemesLabel = "securitySchemes" + OAuthFlowsLabel = "flows" + VariablesLabel = "variables" + ServersLabel = "servers" + ServerLabel = "server" + ImplicitLabel = "implicit" + PasswordLabel = "password" + ClientCredentialsLabel = "clientCredentials" + AuthorizationCodeLabel = "authorizationCode" + DeviceLabel = "device" // OpenAPI 3.2+ device flow + DescriptionLabel = "description" + URLLabel = "url" + NameLabel = "name" + EmailLabel = "email" + TitleLabel = "title" + TermsOfServiceLabel = "termsOfService" + VersionLabel = "version" + OpenAPILabel = "openapi" + HostLabel = "host" + BasePathLabel = "basePath" + LicenseLabel = "license" + Identifier = "identifier" + ContactLabel = "contact" + NamespaceLabel = "namespace" + PrefixLabel = "prefix" + AttributeLabel = "attribute" + WrappedLabel = "wrapped" + PropertyNameLabel = "propertyName" + SummaryLabel = "summary" + ParentLabel = "parent" + KindLabel = "kind" + ValueLabel = "value" + ExternalValue = "externalValue" + SchemaDialectLabel = "$schema" + ExclusiveMaximumLabel = "exclusiveMaximum" + ExclusiveMinimumLabel = "exclusiveMinimum" + TypeLabel = "type" + TagsLabel = "tags" + MultipleOfLabel = "multipleOf" + MaximumLabel = "maximum" + MinimumLabel = "minimum" + MaxLengthLabel = "maxLength" + MinLengthLabel = "minLength" + PatternLabel = "pattern" + FormatLabel = "format" + MaxItemsLabel = "maxItems" + ExamplesLabel = "examples" + MinItemsLabel = "minItems" + UniqueItemsLabel = "uniqueItems" + MaxPropertiesLabel = "maxProperties" + MinPropertiesLabel = "minProperties" + RequiredLabel = "required" + EnumLabel = "enum" + SchemaLabel = "schema" + NotLabel = "not" + ItemsLabel = "items" + PropertiesLabel = "properties" + AllOfLabel = "allOf" + AnyOfLabel = "anyOf" + PrefixItemsLabel = "prefixItems" + OneOfLabel = "oneOf" + AdditionalPropertiesLabel = "additionalProperties" + ContentEncodingLabel = "contentEncoding" + ContentMediaType = "contentMediaType" + NullableLabel = "nullable" + ReadOnlyLabel = "readOnly" + WriteOnlyLabel = "writeOnly" + XMLLabel = "xml" + DeprecatedLabel = "deprecated" + ExampleLabel = "example" + RefLabel = "$ref" + DiscriminatorLabel = "discriminator" + ExternalDocsLabel = "externalDocs" + InLabel = "in" + AllowEmptyValueLabel = "allowEmptyValue" + StyleLabel = "style" + CollectionFormatLabel = "collectionFormat" + AllowReservedLabel = "allowReserved" + ExplodeLabel = "explode" + ContentTypeLabel = "contentType" + SecurityDefinitionLabel = "securityDefinition" + Scopes = "scopes" + AuthorizationUrlLabel = "authorizationUrl" + TokenUrlLabel = "tokenUrl" + RefreshUrlLabel = "refreshUrl" + FlowLabel = "flow" + FlowsLabel = "flows" + SchemeLabel = "scheme" + OpenIdConnectUrlLabel = "openIdConnectUrl" + OAuth2MetadataUrlLabel = "oauth2MetadataUrl" // OpenAPI 3.2+ OAuth2 metadata URL + ScopesLabel = "scopes" + OperationRefLabel = "operationRef" + OperationIdLabel = "operationId" + CodesLabel = "codes" + ProducesLabel = "produces" + ConsumesLabel = "consumes" + SchemesLabel = "schemes" + IfLabel = "if" + ElseLabel = "else" + ThenLabel = "then" + PropertyNamesLabel = "propertyNames" + ContainsLabel = "contains" + MinContainsLabel = "minContains" + MaxContainsLabel = "maxContains" + UnevaluatedItemsLabel = "unevaluatedItems" + UnevaluatedPropertiesLabel = "unevaluatedProperties" + DependentSchemasLabel = "dependentSchemas" + PatternPropertiesLabel = "patternProperties" + AnchorLabel = "$anchor" + DynamicAnchorLabel = "$dynamicAnchor" + DynamicRefLabel = "$dynamicRef" +) diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/create_document.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/create_document.go new file mode 100644 index 000000000..953252c9f --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/create_document.go @@ -0,0 +1,403 @@ +package v3 + +import ( + "context" + "errors" + "fmt" + "net/url" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" +) + +// CreateDocument will create a new Document instance from the provided SpecInfo. +// +// Deprecated: Use CreateDocumentFromConfig instead. This function will be removed in a later version, it +// defaults to allowing file and remote references, and does not support relative file references. +func CreateDocument(info *datamodel.SpecInfo) (*Document, error) { + return createDocument(info, datamodel.NewDocumentConfiguration()) +} + +// CreateDocumentFromConfig Create a new document from the provided SpecInfo and DocumentConfiguration pointer. +func CreateDocumentFromConfig(info *datamodel.SpecInfo, config *datamodel.DocumentConfiguration) (*Document, error) { + return createDocument(info, config) +} + +func createDocument(info *datamodel.SpecInfo, config *datamodel.DocumentConfiguration) (*Document, error) { + _, labelNode, versionNode := utils.FindKeyNodeFull(OpenAPILabel, info.RootNode.Content) + var version low.NodeReference[string] + if versionNode == nil { + return nil, errors.New("no openapi version/tag found, cannot create document") + } + version = low.NodeReference[string]{Value: versionNode.Value, KeyNode: labelNode, ValueNode: versionNode} + doc := Document{Version: version} + doc.Nodes = low.ExtractNodes(nil, info.RootNode.Content[0]) + // create an index config and shadow the document configuration. + idxConfig := index.CreateClosedAPIIndexConfig() + idxConfig.SpecInfo = info + idxConfig.UseSchemaQuickHash = config.UseSchemaQuickHash + idxConfig.ExcludeExtensionRefs = config.ExcludeExtensionRefs + idxConfig.IgnoreArrayCircularReferences = config.IgnoreArrayCircularReferences + idxConfig.IgnorePolymorphicCircularReferences = config.IgnorePolymorphicCircularReferences + idxConfig.AllowUnknownExtensionContentDetection = config.AllowUnknownExtensionContentDetection + idxConfig.TransformSiblingRefs = config.TransformSiblingRefs + idxConfig.SkipExternalRefResolution = config.SkipExternalRefResolution + idxConfig.AvoidCircularReferenceCheck = true + + // handle $self field for OpenAPI 3.2+ documents + baseURL := config.BaseURL + if info.Self != "" { + selfURL, err := url.Parse(info.Self) + if err != nil { + // log error but continue with original config + if config.Logger != nil { + config.Logger.Error("$self field contains invalid URL", "self", info.Self, "error", err) + } + // store error in spec info for later retrieval + if info.Error == nil { + info.Error = fmt.Errorf("$self field contains invalid URL: %w", err) + } + } else if strings.HasPrefix(info.Self, "http") { + // validate http/https URLs + if config.BaseURL != nil { + // conflict detected + if config.Logger != nil { + config.Logger.Error("BaseURL and $self have been set and conflict, defaulting to BaseURL", + "baseURL", config.BaseURL.String(), "self", info.Self) + } + // use config BaseURL (programmatic control trumps document) + } else { + // use $self as BaseURL + baseURL = selfURL + } + } else { + // for non-http URLs (like file:// or custom schemes), use as-is if no conflict + if config.BaseURL != nil { + if config.Logger != nil { + config.Logger.Error("BaseURL and $self have been set and conflict, defaulting to BaseURL", + "baseURL", config.BaseURL.String(), "self", info.Self) + } + } else { + baseURL = selfURL + } + } + } + + idxConfig.BaseURL = urlWithoutTrailingSlash(baseURL) + idxConfig.BasePath = config.BasePath + idxConfig.SpecFilePath = config.SpecFilePath + idxConfig.Logger = config.Logger + extract := config.ExtractRefsSequentially + idxConfig.ExtractRefsSequentially = extract + rolodex := index.NewRolodex(idxConfig) + rolodex.SetRootNode(info.RootNode) + doc.Rolodex = rolodex + + // If basePath is provided, add a local filesystem to the rolodex. + if idxConfig.BasePath != "" || config.AllowFileReferences { + var cwd string + cwd, _ = filepath.Abs(config.BasePath) + // if a supplied local filesystem is provided, add it to the rolodex. + if config.LocalFS != nil { + var localFS index.RolodexFS + if fs, ok := config.LocalFS.(index.RolodexFS); ok { + localFS = fs + } else { + // wrap a plain fs.FS so it can be indexed. + localFSConf := index.LocalFSConfig{ + BaseDirectory: cwd, + IndexConfig: idxConfig, + FileFilters: config.FileFilter, + DirFS: config.LocalFS, + } + + localFS, _ = index.NewLocalFSWithConfig(&localFSConf) + idxConfig.AllowFileLookup = true + } + + rolodex.AddLocalFS(cwd, localFS) + } else { + + // create a local filesystem + localFSConf := index.LocalFSConfig{ + BaseDirectory: cwd, + IndexConfig: idxConfig, + FileFilters: config.FileFilter, + } + + fileFS, _ := index.NewLocalFSWithConfig(&localFSConf) + idxConfig.AllowFileLookup = true + + // add the filesystem to the rolodex + rolodex.AddLocalFS(cwd, fileFS) + } + } + // if base url is provided, add a remote filesystem to the rolodex. + if idxConfig.BaseURL != nil || config.AllowRemoteReferences { + + // create a remote filesystem + remoteFS, _ := index.NewRemoteFSWithConfig(idxConfig) + if config.RemoteURLHandler != nil { + remoteFS.RemoteHandlerFunc = config.RemoteURLHandler + } + idxConfig.AllowRemoteLookup = true + + // add to the rolodex + u := "default" + if config.BaseURL != nil { + u = config.BaseURL.String() + } + rolodex.AddRemoteFS(u, remoteFS) + } + + // index the rolodex + var errs []error + + // index all the things. + if config.Logger != nil { + config.Logger.Debug("indexing rolodex") + } + now := time.Now() + _ = rolodex.IndexTheRolodex(context.Background()) + done := time.Duration(time.Since(now).Milliseconds()) + if config.Logger != nil { + config.Logger.Debug("rolodex indexed", "ms", done) + } + // check for circular references + if config.Logger != nil { + config.Logger.Debug("checking for circular references") + } + now = time.Now() + if !config.SkipCircularReferenceCheck { + rolodex.CheckForCircularReferences() + } + done = time.Duration(time.Since(now).Milliseconds()) + if config.Logger != nil { + if !config.SkipCircularReferenceCheck { + config.Logger.Debug("circular check completed", "ms", done) + } + } + // extract errors + roloErrs := rolodex.GetCaughtErrors() + if roloErrs != nil { + errs = append(errs, roloErrs...) + } + + // set root index. + doc.Index = rolodex.GetRootIndex() + var wg sync.WaitGroup + + var cacheMap sync.Map + modelContext := base.ModelContext{SchemaCache: &cacheMap} + ctx := context.WithValue(context.Background(), "modelCtx", &modelContext) + + doc.Extensions = low.ExtractExtensions(info.RootNode.Content[0]) + low.ExtractExtensionNodes(ctx, doc.Extensions, doc.Nodes) + + // if set, extract jsonSchemaDialect (3.1) + _, dialectLabel, dialectNode := utils.FindKeyNodeFull(JSONSchemaDialectLabel, info.RootNode.Content) + if dialectNode != nil { + doc.JsonSchemaDialect = low.NodeReference[string]{ + Value: dialectNode.Value, KeyNode: dialectLabel, ValueNode: dialectNode, + } + } + + // if set, extract $self (3.2) + _, selfLabel, selfNode := utils.FindKeyNodeFull(SelfLabel, info.RootNode.Content) + if selfNode != nil { + doc.Self = low.NodeReference[string]{ + Value: selfNode.Value, KeyNode: selfLabel, ValueNode: selfNode, + } + } + + runExtraction := func(ctx context.Context, info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex, + runFunc func(ctx context.Context, i *datamodel.SpecInfo, d *Document, idx *index.SpecIndex) error, + ers *[]error, + wg *sync.WaitGroup, + ) { + if er := runFunc(ctx, info, doc, idx); er != nil { + *ers = append(*ers, er) + } + wg.Done() + } + extractionFuncs := []func(ctx context.Context, i *datamodel.SpecInfo, d *Document, idx *index.SpecIndex) error{ + extractInfo, + extractServers, + extractTags, + extractComponents, + extractSecurity, + extractExternalDocs, + extractPaths, + extractWebhooks, + } + + wg.Add(len(extractionFuncs)) + if config.Logger != nil { + config.Logger.Debug("running extractions") + } + now = time.Now() + for _, f := range extractionFuncs { + runExtraction(ctx, info, &doc, rolodex.GetRootIndex(), f, &errs, &wg) + } + wg.Wait() + done = time.Duration(time.Since(now).Milliseconds()) + if config.Logger != nil { + config.Logger.Debug("extractions complete", "time", done) + } + return &doc, errors.Join(errs...) +} + +func extractInfo(ctx context.Context, info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex) error { + _, ln, vn := utils.FindKeyNodeFullTop(base.InfoLabel, info.RootNode.Content[0].Content) + if vn != nil { + ir := base.Info{} + _ = low.BuildModel(vn, &ir) + _ = ir.Build(ctx, ln, vn, idx) + nr := low.NodeReference[*base.Info]{Value: &ir, ValueNode: vn, KeyNode: ln} + doc.Info = nr + } + return nil +} + +func extractSecurity(ctx context.Context, info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex) error { + sec, ln, vn, err := low.ExtractArray[*base.SecurityRequirement](ctx, SecurityLabel, info.RootNode.Content[0], idx) + if err != nil { + return err + } + if vn != nil && ln != nil { + doc.Security = low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]{ + Value: sec, + KeyNode: ln, + ValueNode: vn, + } + } + return nil +} + +func extractExternalDocs(ctx context.Context, info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex) error { + extDocs, dErr := low.ExtractObject[*base.ExternalDoc](ctx, base.ExternalDocsLabel, info.RootNode.Content[0], idx) + if dErr != nil { + return dErr + } + doc.ExternalDocs = extDocs + return nil +} + +func extractComponents(ctx context.Context, info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex) error { + _, ln, vn := utils.FindKeyNodeFullTop(ComponentsLabel, info.RootNode.Content[0].Content) + if vn != nil { + ir := Components{} + _ = low.BuildModel(vn, &ir) + err := ir.Build(ctx, vn, idx) + if err != nil { + return err + } + nr := low.NodeReference[*Components]{Value: &ir, ValueNode: vn, KeyNode: ln} + doc.Components = nr + } + return nil +} + +func extractServers(ctx context.Context, info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex) error { + _, ln, vn := utils.FindKeyNodeFull(ServersLabel, info.RootNode.Content[0].Content) + if vn != nil { + if utils.IsNodeArray(vn) { + var servers []low.ValueReference[*Server] + for _, srvN := range vn.Content { + if utils.IsNodeMap(srvN) { + srvr := Server{} + _ = low.BuildModel(srvN, &srvr) + _ = srvr.Build(ctx, ln, srvN, idx) + servers = append(servers, low.ValueReference[*Server]{ + Value: &srvr, + ValueNode: srvN, + }) + } + } + doc.Servers = low.NodeReference[[]low.ValueReference[*Server]]{ + Value: servers, + KeyNode: ln, + ValueNode: vn, + } + } + } + return nil +} + +func extractTags(ctx context.Context, info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex) error { + _, ln, vn := utils.FindKeyNodeFull(base.TagsLabel, info.RootNode.Content[0].Content) + if vn != nil { + if utils.IsNodeArray(vn) { + var tags []low.ValueReference[*base.Tag] + for _, tagN := range vn.Content { + if utils.IsNodeMap(tagN) { + tag := base.Tag{} + _ = low.BuildModel(tagN, &tag) + if err := tag.Build(ctx, ln, tagN, idx); err != nil { + return err + } + tags = append(tags, low.ValueReference[*base.Tag]{ + Value: &tag, + ValueNode: tagN, + }) + } + } + doc.Tags = low.NodeReference[[]low.ValueReference[*base.Tag]]{ + Value: tags, + KeyNode: ln, + ValueNode: vn, + } + } + } + return nil +} + +func extractPaths(ctx context.Context, info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex) error { + _, ln, vn := utils.FindKeyNodeFull(PathsLabel, info.RootNode.Content[0].Content) + if vn != nil { + ir := Paths{} + err := ir.Build(ctx, ln, vn, idx) + if err != nil { + return err + } + nr := low.NodeReference[*Paths]{Value: &ir, ValueNode: vn, KeyNode: ln} + doc.Paths = nr + } + return nil +} + +func extractWebhooks(ctx context.Context, info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex) error { + hooks, hooksL, hooksN, eErr := low.ExtractMap[*PathItem](ctx, WebhooksLabel, info.RootNode, idx) + if eErr != nil { + return eErr + } + if hooks != nil { + doc.Webhooks = low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*PathItem]]]{ + Value: hooks, + KeyNode: hooksL, + ValueNode: hooksN, + } + for k, v := range hooks.FromOldest() { + v.Value.Nodes.Store(k.KeyNode.Line, k.KeyNode) + } + } + return nil +} + +func urlWithoutTrailingSlash(u *url.URL) *url.URL { + if u == nil { + return nil + } + + u.Path, _ = strings.CutSuffix(u.Path, "/") + + return u +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/document.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/document.go new file mode 100644 index 000000000..1b7ddd490 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/document.go @@ -0,0 +1,233 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +// Package v3 represents all OpenAPI 3+ low-level models. Low-level models are more difficult to navigate +// than higher-level models, however they are packed with all the raw AST and node data required to perform +// any kind of analysis on the underlying data. +// +// Every property is wrapped in a NodeReference or a KeyReference or a ValueReference. +package v3 + +import ( + "hash/maphash" + "sort" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +type Document struct { + // Version is the version of OpenAPI being used, extracted from the 'openapi: x.x.x' definition. + // This is not a standard property of the OpenAPI model, it's a convenience mechanism only. + Version low.NodeReference[string] + + // Info represents a specification Info definitions + // Provides metadata about the API. The metadata MAY be used by tooling as required. + // - https://spec.openapis.org/oas/v3.1.0#info-object + Info low.NodeReference[*base.Info] + + // JsonSchemaDialect is a 3.1+ property that sets the dialect to use for validating *base.Schema definitions + // The default value for the $schema keyword within Schema Objects contained within this OAS document. + // This MUST be in the form of a URI. + // - https://spec.openapis.org/oas/v3.1.0#schema-object + JsonSchemaDialect low.NodeReference[string] // 3.1 + + // Self is a 3.2+ property that sets the base URI for the document for resolving relative references + // - https://spec.openapis.org/oas/v3.2.0#openapi-object + Self low.NodeReference[string] // 3.2 + + // Webhooks is a 3.1+ property that is similar to callbacks, except, this defines incoming webhooks. + // The incoming webhooks that MAY be received as part of this API and that the API consumer MAY choose to implement. + // Closely related to the callbacks feature, this section describes requests initiated other than by an API call, + // for example by an out-of-band registration. The key name is a unique string to refer to each webhook, + // while the (optionally referenced) Path Item Object describes a request that may be initiated by the API provider + // and the expected responses. An example is available. + Webhooks low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*PathItem]]] // 3.1 + + // Servers is a slice of Server instances which provide connectivity information to a target server. If the servers + // property is not provided, or is an empty array, the default value would be a Server Object with an url value of /. + // - https://spec.openapis.org/oas/v3.1.0#server-object + Servers low.NodeReference[[]low.ValueReference[*Server]] + + // Paths contains all the PathItem definitions for the specification. + // The available paths and operations for the API, The most important part of ths spec. + // - https://spec.openapis.org/oas/v3.1.0#paths-object + Paths low.NodeReference[*Paths] + + // Components is an element to hold various schemas for the document. + // - https://spec.openapis.org/oas/v3.1.0#components-object + Components low.NodeReference[*Components] + + // Security contains global security requirements/roles for the specification + // A declaration of which security mechanisms can be used across the API. The list of values includes alternative + // security requirement objects that can be used. Only one of the security requirement objects need to be satisfied + // to authorize a request. Individual operations can override this definition. To make security optional, + // an empty security requirement ({}) can be included in the array. + // - https://spec.openapis.org/oas/v3.1.0#security-requirement-object + Security low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]] + + // Tags is a slice of base.Tag instances defined by the specification + // A list of tags used by the document with additional metadata. The order of the tags can be used to reflect on + // their order by the parsing tools. Not all tags that are used by the Operation Object must be declared. + // The tags that are not declared MAY be organized randomly or based on the tools’ logic. + // Each tag name in the list MUST be unique. + // - https://spec.openapis.org/oas/v3.1.0#tag-object + Tags low.NodeReference[[]low.ValueReference[*base.Tag]] + + // ExternalDocs is an instance of base.ExternalDoc for.. well, obvious really, innit. + // - https://spec.openapis.org/oas/v3.1.0#external-documentation-object + ExternalDocs low.NodeReference[*base.ExternalDoc] + + // Extensions contains all custom extensions defined for the top-level document. + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + + // Index is a reference to the *index.SpecIndex that was created for the document and used + // as a guide when building out the Document. Ideal if further processing is required on the model and + // the original details are required to continue the work. + // + // This property is not a part of the OpenAPI schema, this is custom to libopenapi. + Index *index.SpecIndex + + // Rolodex is a reference to the rolodex used when creating this document. + Rolodex *index.Rolodex + + // StorageRoot is the root path to the storage location of the document. This has no effect on resolving references. + // but it's used by the doctor to determine where to store the document. This is not part of the OpenAPI schema. + StorageRoot string `json:"-" yaml:"-"` + + low.NodeMap +} + +// FindSecurityRequirement will attempt to locate a security requirement string from a supplied name. +func (d *Document) FindSecurityRequirement(name string) []low.ValueReference[string] { + for k := range d.Security.Value { + requirements := d.Security.Value[k].Value.Requirements + for k, v := range requirements.Value.FromOldest() { + if k.Value == name { + return v.Value + } + } + } + return nil +} + +// GetExtensions returns all Document extensions and satisfies the low.HasExtensions interface. +func (d *Document) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return d.Extensions +} + +func (d *Document) GetExternalDocs() *low.NodeReference[any] { + return &low.NodeReference[any]{ + KeyNode: d.ExternalDocs.KeyNode, + ValueNode: d.ExternalDocs.ValueNode, + Value: d.ExternalDocs.Value, + } +} + +func (d *Document) GetIndex() *index.SpecIndex { + return d.Index +} + +// Hash will return a consistent Hash of the Document object +func (d *Document) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if d.Version.Value != "" { + h.WriteString(d.Version.Value) + h.WriteByte(low.HASH_PIPE) + } + if d.Info.Value != nil { + h.WriteString(low.GenerateHashString(d.Info.Value)) + h.WriteByte(low.HASH_PIPE) + } + if d.JsonSchemaDialect.Value != "" { + h.WriteString(d.JsonSchemaDialect.Value) + h.WriteByte(low.HASH_PIPE) + } + if d.Self.Value != "" { + h.WriteString(d.Self.Value) + h.WriteByte(low.HASH_PIPE) + } + + // Webhooks - pre-allocate slice + if d.Webhooks.GetValue() != nil { + webhookLen := d.Webhooks.GetValue().Len() + if webhookLen > 0 { + keys := make([]string, 0, webhookLen) + for k, v := range d.Webhooks.GetValue().FromOldest() { + keys = append(keys, k.Value+"-"+low.GenerateHashString(v.Value)) + } + sort.Strings(keys) + for _, key := range keys { + h.WriteString(key) + h.WriteByte(low.HASH_PIPE) + } + } + } + + // Servers - pre-allocate slice + serverLen := len(d.Servers.Value) + if serverLen > 0 { + keys := make([]string, 0, serverLen) + for i := range d.Servers.Value { + keys = append(keys, low.GenerateHashString(d.Servers.Value[i].Value)) + } + sort.Strings(keys) + for _, key := range keys { + h.WriteString(key) + h.WriteByte(low.HASH_PIPE) + } + } + + if d.Paths.Value != nil { + h.WriteString(low.GenerateHashString(d.Paths.Value)) + h.WriteByte(low.HASH_PIPE) + } + if d.Components.Value != nil { + h.WriteString(low.GenerateHashString(d.Components.Value)) + h.WriteByte(low.HASH_PIPE) + } + + // Security - pre-allocate slice + securityLen := len(d.Security.Value) + if securityLen > 0 { + keys := make([]string, 0, securityLen) + for i := range d.Security.Value { + keys = append(keys, low.GenerateHashString(d.Security.Value[i].Value)) + } + sort.Strings(keys) + for _, key := range keys { + h.WriteString(key) + h.WriteByte(low.HASH_PIPE) + } + } + + // Tags - pre-allocate slice + tagLen := len(d.Tags.Value) + if tagLen > 0 { + keys := make([]string, 0, tagLen) + for i := range d.Tags.Value { + keys = append(keys, low.GenerateHashString(d.Tags.Value[i].Value)) + } + sort.Strings(keys) + for _, key := range keys { + h.WriteString(key) + h.WriteByte(low.HASH_PIPE) + } + } + + if d.ExternalDocs.Value != nil { + h.WriteString(low.GenerateHashString(d.ExternalDocs.Value)) + h.WriteByte(low.HASH_PIPE) + } + + // Extensions + for _, ext := range low.HashExtensions(d.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/encoding.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/encoding.go new file mode 100644 index 000000000..5c3b46a25 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/encoding.go @@ -0,0 +1,109 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + "fmt" + "hash/maphash" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Encoding represents a low-level OpenAPI 3+ Encoding object +// - https://spec.openapis.org/oas/v3.1.0#encoding-object +type Encoding struct { + ContentType low.NodeReference[string] + Headers low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*Header]]] + Style low.NodeReference[string] + Explode low.NodeReference[bool] + AllowReserved low.NodeReference[bool] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// GetIndex returns the index.SpecIndex instance attached to the Encoding object +func (en *Encoding) GetIndex() *index.SpecIndex { + return en.index +} + +// GetContext returns the context.Context instance used when building the Encoding object +func (en *Encoding) GetContext() context.Context { + return en.context +} + +// FindHeader attempts to locate a Header with the supplied name +func (en *Encoding) FindHeader(hType string) *low.ValueReference[*Header] { + return low.FindItemInOrderedMap[*Header](hType, en.Headers.Value) +} + +// GetRootNode returns the root yaml node of the Encoding object +func (en *Encoding) GetRootNode() *yaml.Node { + return en.RootNode +} + +// GetKeyNode returns the key yaml node of the Encoding object +func (en *Encoding) GetKeyNode() *yaml.Node { + return en.KeyNode +} + +// Hash will return a consistent Hash of the Encoding object +func (en *Encoding) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if en.ContentType.Value != "" { + h.WriteString(en.ContentType.Value) + h.WriteByte(low.HASH_PIPE) + } + for k, v := range orderedmap.SortAlpha(en.Headers.Value).FromOldest() { + h.WriteString(fmt.Sprintf("%s-%x", k.Value, v.Value.Hash())) + h.WriteByte(low.HASH_PIPE) + } + if en.Style.Value != "" { + h.WriteString(en.Style.Value) + h.WriteByte(low.HASH_PIPE) + } + low.HashBool(h, en.Explode.Value) + h.WriteByte(low.HASH_PIPE) + low.HashBool(h, en.AllowReserved.Value) + h.WriteByte(low.HASH_PIPE) + return h.Sum64() + }) +} + +// Build will extract all Header objects from supplied node. +func (en *Encoding) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + en.KeyNode = keyNode + root = utils.NodeAlias(root) + en.RootNode = root + utils.CheckForMergeNodes(root) + en.Nodes = low.ExtractNodes(ctx, root) + en.Reference = new(low.Reference) + en.index = idx + en.context = ctx + + headers, hL, hN, err := low.ExtractMap[*Header](ctx, HeadersLabel, root, idx) + if err != nil { + return err + } + if headers != nil { + en.Headers = low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*Header]]]{ + Value: headers, + KeyNode: hL, + ValueNode: hN, + } + en.Nodes.Store(hL.Line, hL) + for k, v := range headers.FromOldest() { + v.Value.Nodes.Store(k.KeyNode.Line, k.KeyNode) + } + } + return nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/header.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/header.go new file mode 100644 index 000000000..2acbc1b3d --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/header.go @@ -0,0 +1,257 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + "fmt" + "hash/maphash" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Header represents a low-level OpenAPI 3+ Header object. +// - https://spec.openapis.org/oas/v3.1.0#header-object +type Header struct { + Description low.NodeReference[string] + Required low.NodeReference[bool] + Deprecated low.NodeReference[bool] + AllowEmptyValue low.NodeReference[bool] + Style low.NodeReference[string] + Explode low.NodeReference[bool] + AllowReserved low.NodeReference[bool] + Schema low.NodeReference[*base.SchemaProxy] + Example low.NodeReference[*yaml.Node] + Examples low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.Example]]] + Content low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*MediaType]]] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// GetIndex returns the index.SpecIndex instance attached to the Header object +func (h *Header) GetIndex() *index.SpecIndex { + return h.index +} + +// GetContext returns the context.Context instance used when building the Header object +func (h *Header) GetContext() context.Context { + return h.context +} + +// FindExtension will attempt to locate an extension with the supplied name +func (h *Header) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, h.Extensions) +} + +// FindExample will attempt to locate an Example with a specified name +func (h *Header) FindExample(eType string) *low.ValueReference[*base.Example] { + return low.FindItemInOrderedMap[*base.Example](eType, h.Examples.Value) +} + +// FindContent will attempt to locate a MediaType definition, with a specified name +func (h *Header) FindContent(ext string) *low.ValueReference[*MediaType] { + return low.FindItemInOrderedMap[*MediaType](ext, h.Content.Value) +} + +// GetRootNode returns the root yaml node of the Header object +func (h *Header) GetRootNode() *yaml.Node { + return h.RootNode +} + +// GetKeyNode returns the key yaml node of the Header object +func (h *Header) GetKeyNode() *yaml.Node { + return h.KeyNode +} + +// GetExtensions returns all Header extensions and satisfies the low.HasExtensions interface. +func (h *Header) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return h.Extensions +} + +// Hash will return a consistent Hash of the Header object +func (h *Header) Hash() uint64 { + return low.WithHasher(func(hsh *maphash.Hash) uint64 { + if h.Description.Value != "" { + hsh.WriteString(h.Description.Value) + hsh.WriteByte(low.HASH_PIPE) + } + low.HashBool(hsh, h.Required.Value) + hsh.WriteByte(low.HASH_PIPE) + low.HashBool(hsh, h.Deprecated.Value) + hsh.WriteByte(low.HASH_PIPE) + low.HashBool(hsh, h.AllowEmptyValue.Value) + hsh.WriteByte(low.HASH_PIPE) + if h.Style.Value != "" { + hsh.WriteString(h.Style.Value) + hsh.WriteByte(low.HASH_PIPE) + } + low.HashBool(hsh, h.Explode.Value) + hsh.WriteByte(low.HASH_PIPE) + low.HashBool(hsh, h.AllowReserved.Value) + hsh.WriteByte(low.HASH_PIPE) + if h.Schema.Value != nil { + hsh.WriteString(low.GenerateHashString(h.Schema.Value)) + hsh.WriteByte(low.HASH_PIPE) + } + if h.Example.Value != nil && !h.Example.Value.IsZero() { + hsh.WriteString(low.GenerateHashString(h.Example.Value)) + hsh.WriteByte(low.HASH_PIPE) + } + for k, v := range orderedmap.SortAlpha(h.Examples.Value).FromOldest() { + hsh.WriteString(fmt.Sprintf("%s-%x", k.Value, v.Value.Hash())) + hsh.WriteByte(low.HASH_PIPE) + } + for k, v := range orderedmap.SortAlpha(h.Content.Value).FromOldest() { + hsh.WriteString(fmt.Sprintf("%s-%x", k.Value, v.Value.Hash())) + hsh.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(h.Extensions) { + hsh.WriteString(ext) + hsh.WriteByte(low.HASH_PIPE) + } + return hsh.Sum64() + }) +} + +// Build will extract extensions, examples, schema and content/media types from node. +func (h *Header) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + h.KeyNode = keyNode + h.Reference = new(low.Reference) + if ok, _, ref := utils.IsNodeRefValue(root); ok { + h.SetReference(ref, root) + } + root = utils.NodeAlias(root) + h.RootNode = root + utils.CheckForMergeNodes(root) + h.Nodes = low.ExtractNodes(ctx, root) + h.Extensions = low.ExtractExtensions(root) + h.context = ctx + h.index = idx + + low.ExtractExtensionNodes(ctx, h.Extensions, h.Nodes) + // handle example if set. + _, expLabel, expNode := utils.FindKeyNodeFull(base.ExampleLabel, root.Content) + if expNode != nil { + h.Example = low.NodeReference[*yaml.Node]{ + Value: expNode, + ValueNode: expNode, + KeyNode: expLabel, + } + h.Nodes.Store(expLabel.Line, expLabel) + m := low.ExtractNodes(ctx, expNode) + m.Range(func(key, value any) bool { + h.Nodes.Store(key, value) + return true + }) + } + + // handle examples if set. + exps, expsL, expsN, eErr := low.ExtractMap[*base.Example](ctx, base.ExamplesLabel, root, idx) + if eErr != nil { + return eErr + } + if exps != nil { + h.Examples = low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.Example]]]{ + Value: exps, + KeyNode: expsL, + ValueNode: expsN, + } + h.Nodes.Store(expsL.Line, expsL) + } + + // handle schema + sch, sErr := base.ExtractSchema(ctx, root, idx) + if sErr != nil { + return sErr + } + if sch != nil { + h.Schema = *sch + } + + // handle content, if set. + con, cL, cN, cErr := low.ExtractMap[*MediaType](ctx, ContentLabel, root, idx) + if cErr != nil { + return cErr + } + h.Content = low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*MediaType]]]{ + Value: con, + KeyNode: cL, + ValueNode: cN, + } + if cL != nil { + h.Nodes.Store(cL.Line, cL) + } + return nil +} + +// Getter methods to satisfy OpenAPIHeader interface. + +func (h *Header) GetDescription() *low.NodeReference[string] { + return &h.Description +} + +func (h *Header) GetRequired() *low.NodeReference[bool] { + return &h.Required +} + +func (h *Header) GetDeprecated() *low.NodeReference[bool] { + return &h.Deprecated +} + +func (h *Header) GetAllowEmptyValue() *low.NodeReference[bool] { + return &h.AllowEmptyValue +} + +func (h *Header) GetSchema() *low.NodeReference[any] { + i := low.NodeReference[any]{ + KeyNode: h.Schema.KeyNode, + ValueNode: h.Schema.ValueNode, + Value: h.Schema.Value, + } + return &i +} + +func (h *Header) GetStyle() *low.NodeReference[string] { + return &h.Style +} + +func (h *Header) GetAllowReserved() *low.NodeReference[bool] { + return &h.AllowReserved +} + +func (h *Header) GetExplode() *low.NodeReference[bool] { + return &h.Explode +} + +func (h *Header) GetExample() *low.NodeReference[*yaml.Node] { + return &h.Example +} + +func (h *Header) GetExamples() *low.NodeReference[any] { + i := low.NodeReference[any]{ + KeyNode: h.Examples.KeyNode, + ValueNode: h.Examples.ValueNode, + Value: h.Examples.Value, + } + return &i +} + +func (h *Header) GetContent() *low.NodeReference[any] { + c := low.NodeReference[any]{ + KeyNode: h.Content.KeyNode, + ValueNode: h.Content.ValueNode, + Value: h.Content.Value, + } + return &c +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/link.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/link.go new file mode 100644 index 000000000..f7e3cd45c --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/link.go @@ -0,0 +1,145 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + "hash/maphash" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Link represents a low-level OpenAPI 3+ Link object. +// +// The Link object represents a possible design-time link for a response. The presence of a link does not guarantee the +// caller’s ability to successfully invoke it, rather it provides a known relationship and traversal mechanism between +// responses and other operations. +// +// Unlike dynamic links (i.e. links provided in the response payload), the OAS linking mechanism does not require +// link information in the runtime response. +// +// For computing links, and providing instructions to execute them, a runtime expression is used for accessing values +// in an operation and using them as parameters while invoking the linked operation. +// - https://spec.openapis.org/oas/v3.1.0#link-object +type Link struct { + OperationRef low.NodeReference[string] + OperationId low.NodeReference[string] + Parameters low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[string]]] + RequestBody low.NodeReference[string] + Description low.NodeReference[string] + Server low.NodeReference[*Server] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// GetIndex returns the index.SpecIndex instance attached to the Link object +func (l *Link) GetIndex() *index.SpecIndex { + return l.index +} + +// GetContext returns the context.Context instance used when building the Link object +func (l *Link) GetContext() context.Context { + return l.context +} + +// GetExtensions returns all Link extensions and satisfies the low.HasExtensions interface. +func (l *Link) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return l.Extensions +} + +// FindParameter will attempt to locate a parameter string value, using a parameter name input. +func (l *Link) FindParameter(pName string) *low.ValueReference[string] { + return low.FindItemInOrderedMap[string](pName, l.Parameters.Value) +} + +// FindExtension will attempt to locate an extension with a specific key +func (l *Link) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, l.Extensions) +} + +// GetRootNode returns the root yaml node of the Link object +func (l *Link) GetRootNode() *yaml.Node { + return l.RootNode +} + +// GetKeyNode returns the key yaml node of the Link object +func (l *Link) GetKeyNode() *yaml.Node { + return l.KeyNode +} + +// Build will extract extensions and servers from the node. +func (l *Link) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + l.KeyNode = keyNode + l.Reference = new(low.Reference) + if ok, _, ref := utils.IsNodeRefValue(root); ok { + l.SetReference(ref, root) + } + root = utils.NodeAlias(root) + l.RootNode = root + utils.CheckForMergeNodes(root) + l.Nodes = low.ExtractNodes(ctx, root) + l.Extensions = low.ExtractExtensions(root) + l.index = idx + l.context = ctx + low.ExtractExtensionNodes(ctx, l.Extensions, l.Nodes) + + // extract parameter nodes. + if l.Parameters.Value != nil && l.Parameters.Value.Len() > 0 { + for k := range l.Parameters.Value.KeysFromOldest() { + l.Nodes.Store(k.KeyNode.Line, k.KeyNode) + } + } + + // extract server. + ser, sErr := low.ExtractObject[*Server](ctx, ServerLabel, root, idx) + if sErr != nil { + return sErr + } + l.Server = ser + return nil +} + +// Hash will return a consistent Hash of the Link object +func (l *Link) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if l.Description.Value != "" { + h.WriteString(l.Description.Value) + h.WriteByte(low.HASH_PIPE) + } + if l.OperationRef.Value != "" { + h.WriteString(l.OperationRef.Value) + h.WriteByte(low.HASH_PIPE) + } + if l.OperationId.Value != "" { + h.WriteString(l.OperationId.Value) + h.WriteByte(low.HASH_PIPE) + } + if l.RequestBody.Value != "" { + h.WriteString(l.RequestBody.Value) + h.WriteByte(low.HASH_PIPE) + } + if l.Server.Value != nil { + h.WriteString(low.GenerateHashString(l.Server.Value)) + h.WriteByte(low.HASH_PIPE) + } + for v := range orderedmap.SortAlpha(l.Parameters.Value).ValuesFromOldest() { + h.WriteString(v.Value) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(l.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/media_type.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/media_type.go new file mode 100644 index 000000000..65dd4ef00 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/media_type.go @@ -0,0 +1,220 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + "hash/maphash" + "slices" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// MediaType represents a low-level OpenAPI MediaType object. +// +// Each Media Type Object provides schema and examples for the media type identified by its key. +// - https://spec.openapis.org/oas/v3.1.0#media-type-object +type MediaType struct { + Schema low.NodeReference[*base.SchemaProxy] + ItemSchema low.NodeReference[*base.SchemaProxy] + Example low.NodeReference[*yaml.Node] + Examples low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.Example]]] + Encoding low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*Encoding]]] + ItemEncoding low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*Encoding]]] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// GetIndex returns the index.SpecIndex instance attached to the MediaType object. +func (mt *MediaType) GetIndex() *index.SpecIndex { + return mt.index +} + +// GetContext returns the context.Context instance used when building the MediaType object. +func (mt *MediaType) GetContext() context.Context { + return mt.context +} + +// GetExtensions returns all MediaType extensions and satisfies the low.HasExtensions interface. +func (mt *MediaType) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return mt.Extensions +} + +// FindExtension will attempt to locate an extension with the supplied name. +func (mt *MediaType) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, mt.Extensions) +} + +// FindPropertyEncoding will attempt to locate an Encoding value with a specific name. +func (mt *MediaType) FindPropertyEncoding(eType string) *low.ValueReference[*Encoding] { + return low.FindItemInOrderedMap[*Encoding](eType, mt.Encoding.Value) +} + +// FindExample will attempt to locate an Example with a specific name. +func (mt *MediaType) FindExample(eType string) *low.ValueReference[*base.Example] { + return low.FindItemInOrderedMap[*base.Example](eType, mt.Examples.Value) +} + +// GetAllExamples will extract all examples from the MediaType instance. +func (mt *MediaType) GetAllExamples() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.Example]] { + return mt.Examples.Value +} + +// GetRootNode returns the root yaml node of the MediaType object. +func (mt *MediaType) GetRootNode() *yaml.Node { + return mt.RootNode +} + +// GetKeyNode returns the key yaml node of the MediaType object. +func (mt *MediaType) GetKeyNode() *yaml.Node { + return mt.KeyNode +} + +// Build will extract examples, extensions, schema and encoding from node. +func (mt *MediaType) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + mt.KeyNode = keyNode + root = utils.NodeAlias(root) + mt.RootNode = root + utils.CheckForMergeNodes(root) + mt.Reference = new(low.Reference) + mt.Nodes = low.ExtractNodes(ctx, root) + mt.Extensions = low.ExtractExtensions(root) + mt.index = idx + mt.context = ctx + + low.ExtractExtensionNodes(ctx, mt.Extensions, mt.Nodes) + + // handle example if set. + _, expLabel, expNode := utils.FindKeyNodeFullTop(base.ExampleLabel, root.Content) + if expNode != nil { + mt.Example = low.NodeReference[*yaml.Node]{Value: expNode, KeyNode: expLabel, ValueNode: expNode} + mt.Nodes.Store(expLabel.Line, expLabel) + m := low.ExtractNodesRecursive(ctx, expNode) + m.Range(func(key, value any) bool { + mt.Nodes.Store(key, value) + return true + }) + } + + // handle schema + sch, sErr := base.ExtractSchema(ctx, root, idx) + if sErr != nil { + return sErr + } + if sch != nil { + mt.Schema = *sch + } + + // handle examples if set. + exps, expsL, expsN, eErr := low.ExtractMap[*base.Example](ctx, base.ExamplesLabel, root, idx) + if eErr != nil { + return eErr + } + if exps != nil && slices.Contains(root.Content, expsL) { + mt.Nodes.Store(expsL.Line, expsL) + for k, v := range exps.FromOldest() { + v.Value.Nodes.Store(k.KeyNode.Line, k.KeyNode) + } + mt.Examples = low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.Example]]]{ + Value: exps, + KeyNode: expsL, + ValueNode: expsN, + } + + } + + // handle encoding + encs, encsL, encsN, encErr := low.ExtractMap[*Encoding](ctx, EncodingLabel, root, idx) + if encErr != nil { + return encErr + } + if encs != nil { + mt.Encoding = low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*Encoding]]]{ + Value: encs, + KeyNode: encsL, + ValueNode: encsN, + } + mt.Nodes.Store(encsL.Line, encsL) + for k, v := range encs.FromOldest() { + v.Value.Nodes.Store(k.KeyNode.Line, k.KeyNode) + } + } + + // handle itemSchema + _, itemSchLabel, itemSchNode := utils.FindKeyNodeFullTop(ItemSchemaLabel, root.Content) + if itemSchNode != nil { + itemSchProxy := &base.SchemaProxy{} + _ = itemSchProxy.Build(ctx, itemSchLabel, itemSchNode, idx) + mt.ItemSchema = low.NodeReference[*base.SchemaProxy]{ + Value: itemSchProxy, + KeyNode: itemSchLabel, + ValueNode: itemSchNode, + } + mt.Nodes.Store(itemSchLabel.Line, itemSchLabel) + } + + // handle itemEncoding + itemEncs, itemEncsL, itemEncsN, itemEncErr := low.ExtractMap[*Encoding](ctx, ItemEncodingLabel, root, idx) + if itemEncErr != nil { + return itemEncErr + } + if itemEncs != nil { + mt.ItemEncoding = low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*Encoding]]]{ + Value: itemEncs, + KeyNode: itemEncsL, + ValueNode: itemEncsN, + } + mt.Nodes.Store(itemEncsL.Line, itemEncsL) + for k, v := range itemEncs.FromOldest() { + v.Value.Nodes.Store(k.KeyNode.Line, k.KeyNode) + } + } + + return nil +} + +// Hash will return a consistent Hash of the MediaType object +func (mt *MediaType) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if mt.Schema.Value != nil { + h.WriteString(low.GenerateHashString(mt.Schema.Value)) + h.WriteByte(low.HASH_PIPE) + } + if mt.ItemSchema.Value != nil { + h.WriteString(low.GenerateHashString(mt.ItemSchema.Value)) + h.WriteByte(low.HASH_PIPE) + } + if mt.Example.Value != nil && !mt.Example.Value.IsZero() { + h.WriteString(low.GenerateHashString(mt.Example.Value)) + h.WriteByte(low.HASH_PIPE) + } + for v := range orderedmap.SortAlpha(mt.Examples.Value).ValuesFromOldest() { + h.WriteString(low.GenerateHashString(v.Value)) + h.WriteByte(low.HASH_PIPE) + } + for v := range orderedmap.SortAlpha(mt.Encoding.Value).ValuesFromOldest() { + h.WriteString(low.GenerateHashString(v.Value)) + h.WriteByte(low.HASH_PIPE) + } + for v := range orderedmap.SortAlpha(mt.ItemEncoding.Value).ValuesFromOldest() { + h.WriteString(low.GenerateHashString(v.Value)) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(mt.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/oauth_flows.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/oauth_flows.go new file mode 100644 index 000000000..67b46e3af --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/oauth_flows.go @@ -0,0 +1,230 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + "fmt" + "hash/maphash" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// OAuthFlows represents a low-level OpenAPI 3+ OAuthFlows object. +// - https://spec.openapis.org/oas/v3.1.0#oauth-flows-object +type OAuthFlows struct { + Implicit low.NodeReference[*OAuthFlow] + Password low.NodeReference[*OAuthFlow] + ClientCredentials low.NodeReference[*OAuthFlow] + AuthorizationCode low.NodeReference[*OAuthFlow] + Device low.NodeReference[*OAuthFlow] // OpenAPI 3.2+ device flow + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// GetIndex returns the index.SpecIndex instance attached to the OAuthFlows object. +func (o *OAuthFlows) GetIndex() *index.SpecIndex { + return o.index +} + +// GetContext returns the context.Context instance used when building the OAuthFlows object. +func (o *OAuthFlows) GetContext() context.Context { + return o.context +} + +// GetExtensions returns all OAuthFlows extensions and satisfies the low.HasExtensions interface. +func (o *OAuthFlows) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return o.Extensions +} + +// FindExtension will attempt to locate an extension with the supplied name. +func (o *OAuthFlows) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, o.Extensions) +} + +// GetRootNode returns the root yaml node of the OAuthFlows object. +func (o *OAuthFlows) GetRootNode() *yaml.Node { + return o.RootNode +} + +// GetKeyNode returns the key yaml node of the OAuthFlows object. +func (o *OAuthFlows) GetKeyNode() *yaml.Node { + return o.KeyNode +} + +// Build will extract extensions and all OAuthFlow types from the supplied node. +func (o *OAuthFlows) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + o.KeyNode = keyNode + root = utils.NodeAlias(root) + o.RootNode = root + utils.CheckForMergeNodes(root) + o.Reference = new(low.Reference) + o.Nodes = low.ExtractNodes(ctx, root) + o.Extensions = low.ExtractExtensions(root) + o.index = idx + o.context = ctx + + v, vErr := low.ExtractObject[*OAuthFlow](ctx, ImplicitLabel, root, idx) + if vErr != nil { + return vErr + } + o.Implicit = v + + v, vErr = low.ExtractObject[*OAuthFlow](ctx, PasswordLabel, root, idx) + if vErr != nil { + return vErr + } + o.Password = v + + v, vErr = low.ExtractObject[*OAuthFlow](ctx, ClientCredentialsLabel, root, idx) + if vErr != nil { + return vErr + } + o.ClientCredentials = v + + v, vErr = low.ExtractObject[*OAuthFlow](ctx, AuthorizationCodeLabel, root, idx) + if vErr != nil { + return vErr + } + o.AuthorizationCode = v + + v, vErr = low.ExtractObject[*OAuthFlow](ctx, DeviceLabel, root, idx) + if vErr != nil { + return vErr + } + o.Device = v + + return nil +} + +// Hash will return a consistent Hash of the OAuthFlows object +func (o *OAuthFlows) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if !o.Implicit.IsEmpty() { + h.WriteString(low.GenerateHashString(o.Implicit.Value)) + h.WriteByte(low.HASH_PIPE) + } + if !o.Password.IsEmpty() { + h.WriteString(low.GenerateHashString(o.Password.Value)) + h.WriteByte(low.HASH_PIPE) + } + if !o.ClientCredentials.IsEmpty() { + h.WriteString(low.GenerateHashString(o.ClientCredentials.Value)) + h.WriteByte(low.HASH_PIPE) + } + if !o.AuthorizationCode.IsEmpty() { + h.WriteString(low.GenerateHashString(o.AuthorizationCode.Value)) + h.WriteByte(low.HASH_PIPE) + } + if !o.Device.IsEmpty() { + h.WriteString(low.GenerateHashString(o.Device.Value)) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(o.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} + +// OAuthFlow represents a low-level OpenAPI 3+ OAuthFlow object. +// - https://spec.openapis.org/oas/v3.1.0#oauth-flow-object +type OAuthFlow struct { + AuthorizationUrl low.NodeReference[string] + TokenUrl low.NodeReference[string] + RefreshUrl low.NodeReference[string] + Scopes low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[string]]] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// GetIndex returns the index.SpecIndex instance attached to the OAuthFlow object. +func (o *OAuthFlow) GetIndex() *index.SpecIndex { + return o.index +} + +// GetContext returns the context.Context instance used when building the OAuthFlow object. +func (o *OAuthFlow) GetContext() context.Context { + return o.context +} + +// GetExtensions returns all OAuthFlow extensions and satisfies the low.HasExtensions interface. +func (o *OAuthFlow) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return o.Extensions +} + +// FindScope attempts to locate a scope using a specified name. +func (o *OAuthFlow) FindScope(scope string) *low.ValueReference[string] { + return low.FindItemInOrderedMap[string](scope, o.Scopes.Value) +} + +// FindExtension attempts to locate an extension with a specified key +func (o *OAuthFlow) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, o.Extensions) +} + +// GetRootNode returns the root yaml node of the OAuthFlow object. +func (o *OAuthFlow) GetRootNode() *yaml.Node { + return o.RootNode +} + +// Build will extract extensions from the node. +func (o *OAuthFlow) Build(ctx context.Context, _, root *yaml.Node, idx *index.SpecIndex) error { + o.Reference = new(low.Reference) + o.Nodes = low.ExtractNodes(ctx, root) + o.Extensions = low.ExtractExtensions(root) + o.index = idx + o.context = ctx + low.ExtractExtensionNodes(ctx, o.Extensions, o.Nodes) + + if o.Scopes.Value != nil && o.Scopes.Value.Len() > 0 { + for k := range o.Scopes.Value.KeysFromOldest() { + o.Nodes.Store(k.KeyNode.Line, k.KeyNode) + } + } + + o.RootNode = root + return nil +} + +// Hash will return a consistent Hash of the OAuthFlow object +func (o *OAuthFlow) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if !o.AuthorizationUrl.IsEmpty() { + h.WriteString(o.AuthorizationUrl.Value) + h.WriteByte(low.HASH_PIPE) + } + if !o.TokenUrl.IsEmpty() { + h.WriteString(o.TokenUrl.Value) + h.WriteByte(low.HASH_PIPE) + } + if !o.RefreshUrl.IsEmpty() { + h.WriteString(o.RefreshUrl.Value) + h.WriteByte(low.HASH_PIPE) + } + for k, v := range orderedmap.SortAlpha(o.Scopes.Value).FromOldest() { + h.WriteString(fmt.Sprintf("%s-%s", k.Value, v.Value)) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(o.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/operation.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/operation.go new file mode 100644 index 000000000..ba5dbf5d7 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/operation.go @@ -0,0 +1,378 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + "hash/maphash" + "sort" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Operation is a low-level representation of an OpenAPI 3+ Operation object. +// +// An Operation is perhaps the most important object of the entire specification. Everything of value +// happens here. The entire being for existence of this library and the specification, is this Operation. +// - https://spec.openapis.org/oas/v3.1.0#operation-object +type Operation struct { + Tags low.NodeReference[[]low.ValueReference[string]] + Summary low.NodeReference[string] + Description low.NodeReference[string] + ExternalDocs low.NodeReference[*base.ExternalDoc] + OperationId low.NodeReference[string] + Parameters low.NodeReference[[]low.ValueReference[*Parameter]] + RequestBody low.NodeReference[*RequestBody] + Responses low.NodeReference[*Responses] + Callbacks low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*Callback]]] + Deprecated low.NodeReference[bool] + Security low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]] + Servers low.NodeReference[[]low.ValueReference[*Server]] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// GetIndex returns the index.SpecIndex instance attached to the Operation object. +func (o *Operation) GetIndex() *index.SpecIndex { + return o.index +} + +// GetContext returns the context.Context instance used when building the Operation object. +func (o *Operation) GetContext() context.Context { + return o.context +} + +// FindCallback will attempt to locate a Callback instance by the supplied name. +func (o *Operation) FindCallback(callback string) *low.ValueReference[*Callback] { + return low.FindItemInOrderedMap(callback, o.Callbacks.GetValue()) +} + +// FindSecurityRequirement will attempt to locate a security requirement string from a supplied name. +func (o *Operation) FindSecurityRequirement(name string) []low.ValueReference[string] { + for k := range o.Security.Value { + requirements := o.Security.Value[k].Value.Requirements + for k, v := range requirements.Value.FromOldest() { + if k.Value == name { + return v.Value + } + } + } + return nil +} + +// GetRootNode returns the root yaml node of the Operation object +func (o *Operation) GetRootNode() *yaml.Node { + return o.RootNode +} + +// GetKeyNode returns the key yaml node of the Operation object +func (o *Operation) GetKeyNode() *yaml.Node { + return o.KeyNode +} + +// Build will extract external docs, parameters, request body, responses, callbacks, security and servers. +func (o *Operation) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + o.KeyNode = keyNode + o.RootNode = root + root = utils.NodeAlias(root) + utils.CheckForMergeNodes(root) + o.Reference = new(low.Reference) + o.Nodes = low.ExtractNodes(ctx, root) + o.Extensions = low.ExtractExtensions(root) + o.index = idx + o.context = ctx + low.ExtractExtensionNodes(ctx, o.Extensions, o.Nodes) + + // extract externalDocs + extDocs, dErr := low.ExtractObject[*base.ExternalDoc](ctx, base.ExternalDocsLabel, root, idx) + if dErr != nil { + return dErr + } + o.ExternalDocs = extDocs + + // extract parameters + params, ln, vn, pErr := low.ExtractArray[*Parameter](ctx, ParametersLabel, root, idx) + if pErr != nil { + return pErr + } + if params != nil { + o.Parameters = low.NodeReference[[]low.ValueReference[*Parameter]]{ + Value: params, + KeyNode: ln, + ValueNode: vn, + } + o.Nodes.Store(ln.Line, ln) + } + + // extract request body + rBody, rErr := low.ExtractObject[*RequestBody](ctx, RequestBodyLabel, root, idx) + if rErr != nil { + return rErr + } + o.RequestBody = rBody + + // extract tags, but only extract nodes, the model has already been built + k, v := utils.FindKeyNode(TagsLabel, root.Content) + if k != nil && v != nil { + o.Nodes.Store(k.Line, k) + nm := low.ExtractNodesRecursive(ctx, v) + nm.Range(func(key, value interface{}) bool { + o.Nodes.Store(key, value) + return true + }) + } + + // extract responses + respBody, respErr := low.ExtractObject[*Responses](ctx, ResponsesLabel, root, idx) + if respErr != nil { + return respErr + } + o.Responses = respBody + + // extract callbacks + callbacks, cbL, cbN, cbErr := low.ExtractMap[*Callback](ctx, CallbacksLabel, root, idx) + if cbErr != nil { + return cbErr + } + if callbacks != nil { + o.Callbacks = low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*Callback]]]{ + Value: callbacks, + KeyNode: cbL, + ValueNode: cbN, + } + o.Nodes.Store(cbL.Line, cbL) + for k, v := range callbacks.FromOldest() { + v.Value.Nodes.Store(k.KeyNode.Line, k.KeyNode) + } + } + + // extract security + sec, sln, svn, sErr := low.ExtractArray[*base.SecurityRequirement](ctx, SecurityLabel, root, idx) + if sErr != nil { + return sErr + } + + // if security is defined and requirements are provided. + if sln != nil && len(svn.Content) > 0 && sec != nil { + o.Security = low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]{ + Value: sec, + KeyNode: sln, + ValueNode: svn, + } + o.Nodes.Store(sln.Line, sln) + } + + // if security is set, but no requirements are defined. + // https://github.com/pb33f/libopenapi/issues/111 + if sln != nil && len(svn.Content) == 0 && sec == nil { + o.Security = low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]{ + Value: []low.ValueReference[*base.SecurityRequirement]{}, // empty + KeyNode: sln, + ValueNode: svn, + } + o.Nodes.Store(sln.Line, svn) + } + + // extract servers + servers, sl, sn, serErr := low.ExtractArray[*Server](ctx, ServersLabel, root, idx) + if serErr != nil { + return serErr + } + if servers != nil { + o.Servers = low.NodeReference[[]low.ValueReference[*Server]]{ + Value: servers, + KeyNode: sl, + ValueNode: sn, + } + o.Nodes.Store(sl.Line, sl) + } + return nil +} + +// Hash will return a consistent Hash of the Operation object +func (o *Operation) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if !o.Summary.IsEmpty() { + h.WriteString(o.Summary.Value) + h.WriteByte(low.HASH_PIPE) + } + if !o.Description.IsEmpty() { + h.WriteString(o.Description.Value) + h.WriteByte(low.HASH_PIPE) + } + if !o.OperationId.IsEmpty() { + h.WriteString(o.OperationId.Value) + h.WriteByte(low.HASH_PIPE) + } + if !o.RequestBody.IsEmpty() { + h.WriteString(low.GenerateHashString(o.RequestBody.Value)) + h.WriteByte(low.HASH_PIPE) + } + if !o.ExternalDocs.IsEmpty() { + h.WriteString(low.GenerateHashString(o.ExternalDocs.Value)) + h.WriteByte(low.HASH_PIPE) + } + if !o.Responses.IsEmpty() { + h.WriteString(low.GenerateHashString(o.Responses.Value)) + h.WriteByte(low.HASH_PIPE) + } + if !o.Security.IsEmpty() { + // Pre-allocate keys for sorting + secKeys := make([]string, len(o.Security.Value)) + for k := range o.Security.Value { + secKeys[k] = low.GenerateHashString(o.Security.Value[k].Value) + } + sort.Strings(secKeys) + for _, key := range secKeys { + h.WriteString(key) + h.WriteByte(low.HASH_PIPE) + } + } + if !o.Deprecated.IsEmpty() { + low.HashBool(h, o.Deprecated.Value) + h.WriteByte(low.HASH_PIPE) + } + + // Tags array - pre-allocate and sort + if len(o.Tags.Value) > 0 { + tags := make([]string, len(o.Tags.Value)) + for k := range o.Tags.Value { + tags[k] = o.Tags.Value[k].Value + } + sort.Strings(tags) + for _, tag := range tags { + h.WriteString(tag) + h.WriteByte(low.HASH_PIPE) + } + } + + // Servers array - pre-allocate and sort + if len(o.Servers.Value) > 0 { + servers := make([]string, len(o.Servers.Value)) + for k := range o.Servers.Value { + servers[k] = low.GenerateHashString(o.Servers.Value[k].Value) + } + sort.Strings(servers) + for _, server := range servers { + h.WriteString(server) + h.WriteByte(low.HASH_PIPE) + } + } + + // Parameters array - pre-allocate and sort + if len(o.Parameters.Value) > 0 { + params := make([]string, len(o.Parameters.Value)) + for k := range o.Parameters.Value { + params[k] = low.GenerateHashString(o.Parameters.Value[k].Value) + } + sort.Strings(params) + for _, param := range params { + h.WriteString(param) + h.WriteByte(low.HASH_PIPE) + } + } + + // Callbacks + for v := range orderedmap.SortAlpha(o.Callbacks.Value).ValuesFromOldest() { + h.WriteString(low.GenerateHashString(v.Value)) + h.WriteByte(low.HASH_PIPE) + } + + // Extensions + for _, ext := range low.HashExtensions(o.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + + return h.Sum64() + }) +} + +// methods to satisfy swagger operations interface + +func (o *Operation) GetTags() low.NodeReference[[]low.ValueReference[string]] { + return o.Tags +} + +func (o *Operation) GetSummary() low.NodeReference[string] { + return o.Summary +} + +func (o *Operation) GetDescription() low.NodeReference[string] { + return o.Description +} + +func (o *Operation) GetExternalDocs() low.NodeReference[any] { + return low.NodeReference[any]{ + ValueNode: o.ExternalDocs.ValueNode, + KeyNode: o.ExternalDocs.KeyNode, + Value: o.ExternalDocs.Value, + } +} + +func (o *Operation) GetOperationId() low.NodeReference[string] { + return o.OperationId +} + +func (o *Operation) GetDeprecated() low.NodeReference[bool] { + return o.Deprecated +} + +func (o *Operation) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return o.Extensions +} + +func (o *Operation) GetResponses() low.NodeReference[any] { + return low.NodeReference[any]{ + ValueNode: o.Responses.ValueNode, + KeyNode: o.Responses.KeyNode, + Value: o.Responses.Value, + } +} + +func (o *Operation) GetRequestBody() low.NodeReference[any] { + return low.NodeReference[any]{ + ValueNode: o.RequestBody.ValueNode, + KeyNode: o.RequestBody.KeyNode, + Value: o.RequestBody.Value, + } +} + +func (o *Operation) GetParameters() low.NodeReference[any] { + return low.NodeReference[any]{ + ValueNode: o.Parameters.ValueNode, + KeyNode: o.Parameters.KeyNode, + Value: o.Parameters.Value, + } +} + +func (o *Operation) GetSecurity() low.NodeReference[any] { + return low.NodeReference[any]{ + ValueNode: o.Security.ValueNode, + KeyNode: o.Security.KeyNode, + Value: o.Security.Value, + } +} + +func (o *Operation) GetServers() low.NodeReference[any] { + return low.NodeReference[any]{ + ValueNode: o.Servers.ValueNode, + KeyNode: o.Servers.KeyNode, + Value: o.Servers.Value, + } +} + +func (o *Operation) GetCallbacks() low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*Callback]]] { + return o.Callbacks +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/parameter.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/parameter.go new file mode 100644 index 000000000..05a925b2c --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/parameter.go @@ -0,0 +1,277 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + "fmt" + "hash/maphash" + "slices" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Parameter represents a high-level OpenAPI 3+ Parameter object, that is backed by a low-level one. +// +// A unique parameter is defined by a combination of a name and location. +// - https://spec.openapis.org/oas/v3.1.0#parameter-object +type Parameter struct { + KeyNode *yaml.Node + RootNode *yaml.Node + Name low.NodeReference[string] + In low.NodeReference[string] + Description low.NodeReference[string] + Required low.NodeReference[bool] + Deprecated low.NodeReference[bool] + AllowEmptyValue low.NodeReference[bool] + Style low.NodeReference[string] + Explode low.NodeReference[bool] + AllowReserved low.NodeReference[bool] + Schema low.NodeReference[*base.SchemaProxy] + Example low.NodeReference[*yaml.Node] + Examples low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.Example]]] + Content low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*MediaType]]] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// GetIndex returns the index.SpecIndex instance attached to the Parameter object. +func (p *Parameter) GetIndex() *index.SpecIndex { + return p.index +} + +// GetContext returns the context.Context instance used when building the Parameter +func (p *Parameter) GetContext() context.Context { + return p.context +} + +// GetRootNode returns the root yaml node of the Parameter object. +func (p *Parameter) GetRootNode() *yaml.Node { + return p.RootNode +} + +// GetKeyNode returns the key yaml node of the Parameter object. +func (p *Parameter) GetKeyNode() *yaml.Node { + return p.KeyNode +} + +// FindContent will attempt to locate a MediaType instance using the specified name. +func (p *Parameter) FindContent(cType string) *low.ValueReference[*MediaType] { + return low.FindItemInOrderedMap[*MediaType](cType, p.Content.Value) +} + +// FindExample will attempt to locate a base.Example instance using the specified name. +func (p *Parameter) FindExample(eType string) *low.ValueReference[*base.Example] { + return low.FindItemInOrderedMap[*base.Example](eType, p.Examples.Value) +} + +// FindExtension attempts to locate an extension using the specified name. +func (p *Parameter) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, p.Extensions) +} + +// GetExtensions returns all extensions for Parameter. +func (p *Parameter) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return p.Extensions +} + +// Build will extract examples, extensions and content/media types. +func (p *Parameter) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + p.Reference = new(low.Reference) + if ok, _, ref := utils.IsNodeRefValue(root); ok { + p.SetReference(ref, root) + } + root = utils.NodeAlias(root) + p.KeyNode = keyNode + p.RootNode = root + utils.CheckForMergeNodes(root) + p.Nodes = low.ExtractNodes(ctx, root) + p.Extensions = low.ExtractExtensions(root) + p.index = idx + p.context = ctx + low.ExtractExtensionNodes(ctx, p.Extensions, p.Nodes) + + // handle example if set. + _, expLabel, expNode := utils.FindKeyNodeFullTop(base.ExampleLabel, root.Content) + if expNode != nil { + p.Example = low.NodeReference[*yaml.Node]{Value: expNode, KeyNode: expLabel, ValueNode: expNode} + p.Nodes.Store(expLabel.Line, expLabel) + } + + // handle schema + sch, sErr := base.ExtractSchema(ctx, root, idx) + if sErr != nil { + return sErr + } + if sch != nil { + p.Schema = *sch + } + + // handle examples if set. + exps, expsL, expsN, eErr := low.ExtractMap[*base.Example](ctx, base.ExamplesLabel, root, idx) + if eErr != nil { + return eErr + } + // Only consider examples if they are defined in the root node. + if exps != nil && slices.Contains(root.Content, expsL) { + p.Examples = low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.Example]]]{ + Value: exps, + KeyNode: expsL, + ValueNode: expsN, + } + p.Nodes.Store(expsL.Line, expsL) + for k, v := range exps.FromOldest() { + v.Value.Nodes.Store(k.KeyNode.Line, k.KeyNode) + } + } + + // handle content, if set. + con, cL, cN, cErr := low.ExtractMap[*MediaType](ctx, ContentLabel, root, idx) + if cErr != nil { + return cErr + } + p.Content = low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*MediaType]]]{ + Value: con, + KeyNode: cL, + ValueNode: cN, + } + if cL != nil { + p.Nodes.Store(cL.Line, cL) + for k, v := range con.FromOldest() { + v.Value.Nodes.Store(k.KeyNode.Line, k.KeyNode) + } + } + + return nil +} + +// Hash will return a consistent Hash of the Parameter object +func (p *Parameter) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if p.Name.Value != "" { + h.WriteString(p.Name.Value) + h.WriteByte(low.HASH_PIPE) + } + if p.In.Value != "" { + h.WriteString(p.In.Value) + h.WriteByte(low.HASH_PIPE) + } + if p.Description.Value != "" { + h.WriteString(p.Description.Value) + h.WriteByte(low.HASH_PIPE) + } + low.HashBool(h, p.Required.Value) + h.WriteByte(low.HASH_PIPE) + low.HashBool(h, p.Deprecated.Value) + h.WriteByte(low.HASH_PIPE) + low.HashBool(h, p.AllowEmptyValue.Value) + h.WriteByte(low.HASH_PIPE) + if p.Style.Value != "" { + h.WriteString(p.Style.Value) + h.WriteByte(low.HASH_PIPE) + } + low.HashBool(h, p.Explode.Value) + h.WriteByte(low.HASH_PIPE) + low.HashBool(h, p.AllowReserved.Value) + h.WriteByte(low.HASH_PIPE) + if p.Schema.Value != nil && p.Schema.Value.Schema() != nil { + h.WriteString(fmt.Sprintf("%x", p.Schema.Value.Schema().Hash())) + h.WriteByte(low.HASH_PIPE) + } + if p.Example.Value != nil && !p.Example.Value.IsZero() { + h.WriteString(low.GenerateHashString(p.Example.Value)) + h.WriteByte(low.HASH_PIPE) + } + for v := range orderedmap.SortAlpha(p.Examples.Value).ValuesFromOldest() { + h.WriteString(low.GenerateHashString(v.Value)) + h.WriteByte(low.HASH_PIPE) + } + for v := range orderedmap.SortAlpha(p.Content.Value).ValuesFromOldest() { + h.WriteString(low.GenerateHashString(v.Value)) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(p.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} + +// IsParameter compliance methods. + +func (p *Parameter) GetName() *low.NodeReference[string] { + return &p.Name +} + +func (p *Parameter) GetIn() *low.NodeReference[string] { + return &p.In +} + +func (p *Parameter) GetDescription() *low.NodeReference[string] { + return &p.Description +} + +func (p *Parameter) GetRequired() *low.NodeReference[bool] { + return &p.Required +} + +func (p *Parameter) GetDeprecated() *low.NodeReference[bool] { + return &p.Deprecated +} + +func (p *Parameter) GetAllowEmptyValue() *low.NodeReference[bool] { + return &p.AllowEmptyValue +} + +func (p *Parameter) GetSchema() *low.NodeReference[any] { + i := low.NodeReference[any]{ + KeyNode: p.Schema.KeyNode, + ValueNode: p.Schema.ValueNode, + Value: p.Schema.Value, + } + return &i +} + +func (p *Parameter) GetStyle() *low.NodeReference[string] { + return &p.Style +} + +func (p *Parameter) GetAllowReserved() *low.NodeReference[bool] { + return &p.AllowReserved +} + +func (p *Parameter) GetExplode() *low.NodeReference[bool] { + return &p.Explode +} + +func (p *Parameter) GetExample() *low.NodeReference[*yaml.Node] { + return &p.Example +} + +func (p *Parameter) GetExamples() *low.NodeReference[any] { + i := low.NodeReference[any]{ + KeyNode: p.Examples.KeyNode, + ValueNode: p.Examples.ValueNode, + Value: p.Examples.Value, + } + return &i +} + +func (p *Parameter) GetContent() *low.NodeReference[any] { + c := low.NodeReference[any]{ + KeyNode: p.Content.KeyNode, + ValueNode: p.Content.ValueNode, + Value: p.Content.Value, + } + return &c +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/path_item.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/path_item.go new file mode 100644 index 000000000..51660b5ef --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/path_item.go @@ -0,0 +1,474 @@ +// Copyright 2022-2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + "fmt" + "hash/maphash" + "sort" + "strings" + "sync" + + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// PathItem represents a low-level OpenAPI 3+ PathItem object. +// +// Describes the operations available on a single path. A Path Item MAY be empty, due to ACL constraints. +// The path itself is still exposed to the documentation viewer, but they will not know which operations and parameters +// are available. +// - https://spec.openapis.org/oas/v3.1.0#path-item-object +type PathItem struct { + Description low.NodeReference[string] + Summary low.NodeReference[string] + Get low.NodeReference[*Operation] + Put low.NodeReference[*Operation] + Post low.NodeReference[*Operation] + Delete low.NodeReference[*Operation] + Options low.NodeReference[*Operation] + Head low.NodeReference[*Operation] + Patch low.NodeReference[*Operation] + Trace low.NodeReference[*Operation] + Query low.NodeReference[*Operation] + AdditionalOperations low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.NodeReference[*Operation]]] // OpenAPI 3.2+ additional operations + Servers low.NodeReference[[]low.ValueReference[*Server]] + Parameters low.NodeReference[[]low.ValueReference[*Parameter]] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// GetIndex returns the index.SpecIndex instance attached to the PathItem object. +func (p *PathItem) GetIndex() *index.SpecIndex { + return p.index +} + +// GetContext returns the context.Context instance used when building the PathItem object. +func (p *PathItem) GetContext() context.Context { + return p.context +} + +// Hash will return a consistent Hash of the PathItem object +func (p *PathItem) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if !p.Description.IsEmpty() { + h.WriteString(p.Description.Value) + h.WriteByte(low.HASH_PIPE) + } + if !p.Summary.IsEmpty() { + h.WriteString(p.Summary.Value) + h.WriteByte(low.HASH_PIPE) + } + if !p.Get.IsEmpty() { + h.WriteString(fmt.Sprintf("%s-%s", GetLabel, low.GenerateHashString(p.Get.Value))) + h.WriteByte(low.HASH_PIPE) + } + if !p.Put.IsEmpty() { + h.WriteString(fmt.Sprintf("%s-%s", PutLabel, low.GenerateHashString(p.Put.Value))) + h.WriteByte(low.HASH_PIPE) + } + if !p.Post.IsEmpty() { + h.WriteString(fmt.Sprintf("%s-%s", PostLabel, low.GenerateHashString(p.Post.Value))) + h.WriteByte(low.HASH_PIPE) + } + if !p.Delete.IsEmpty() { + h.WriteString(fmt.Sprintf("%s-%s", DeleteLabel, low.GenerateHashString(p.Delete.Value))) + h.WriteByte(low.HASH_PIPE) + } + if !p.Options.IsEmpty() { + h.WriteString(fmt.Sprintf("%s-%s", OptionsLabel, low.GenerateHashString(p.Options.Value))) + h.WriteByte(low.HASH_PIPE) + } + if !p.Head.IsEmpty() { + h.WriteString(fmt.Sprintf("%s-%s", HeadLabel, low.GenerateHashString(p.Head.Value))) + h.WriteByte(low.HASH_PIPE) + } + if !p.Patch.IsEmpty() { + h.WriteString(fmt.Sprintf("%s-%s", PatchLabel, low.GenerateHashString(p.Patch.Value))) + h.WriteByte(low.HASH_PIPE) + } + if !p.Trace.IsEmpty() { + h.WriteString(fmt.Sprintf("%s-%s", TraceLabel, low.GenerateHashString(p.Trace.Value))) + h.WriteByte(low.HASH_PIPE) + } + if !p.Query.IsEmpty() { + h.WriteString(fmt.Sprintf("%s-%s", QueryLabel, low.GenerateHashString(p.Query.Value))) + h.WriteByte(low.HASH_PIPE) + } + + // Process AdditionalOperations with pre-allocation and sorting + if p.AdditionalOperations.Value != nil && p.AdditionalOperations.Value.Len() > 0 { + keys := make([]string, 0, p.AdditionalOperations.Value.Len()) + for k, v := range p.AdditionalOperations.Value.FromOldest() { + keys = append(keys, fmt.Sprintf("%s-%s", k.Value, low.GenerateHashString(v.Value))) + } + sort.Strings(keys) + for _, key := range keys { + h.WriteString(key) + h.WriteByte(low.HASH_PIPE) + } + } + + // Process Parameters with pre-allocation and sorting + if len(p.Parameters.Value) > 0 { + keys := make([]string, len(p.Parameters.Value)) + for k := range p.Parameters.Value { + keys[k] = low.GenerateHashString(p.Parameters.Value[k].Value) + } + sort.Strings(keys) + for _, key := range keys { + h.WriteString(key) + h.WriteByte(low.HASH_PIPE) + } + } + + // Process Servers with pre-allocation and sorting + if len(p.Servers.Value) > 0 { + keys := make([]string, len(p.Servers.Value)) + for k := range p.Servers.Value { + keys[k] = low.GenerateHashString(p.Servers.Value[k].Value) + } + sort.Strings(keys) + for _, key := range keys { + h.WriteString(key) + h.WriteByte(low.HASH_PIPE) + } + } + + for _, ext := range low.HashExtensions(p.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} + +// GetRootNode returns the root yaml node of the PathItem object +func (p *PathItem) GetRootNode() *yaml.Node { + return p.RootNode +} + +// GetKeyNode returns the key yaml node of the PathItem object +func (p *PathItem) GetKeyNode() *yaml.Node { + return p.KeyNode +} + +// FindExtension attempts to find an extension +func (p *PathItem) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, p.Extensions) +} + +// GetExtensions returns all PathItem extensions and satisfies the low.HasExtensions interface. +func (p *PathItem) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return p.Extensions +} + +// Build extracts extensions, parameters, servers and each http method defined. +// everything is extracted asynchronously for speed. +func (p *PathItem) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + p.Reference = new(low.Reference) + if ok, _, ref := utils.IsNodeRefValue(root); ok { + p.SetReference(ref, root) + } + root = utils.NodeAlias(root) + p.KeyNode = keyNode + p.RootNode = root + utils.CheckForMergeNodes(root) + p.Nodes = low.ExtractNodes(ctx, root) + p.Extensions = low.ExtractExtensions(root) + p.index = idx + p.context = ctx + + low.ExtractExtensionNodes(ctx, p.Extensions, p.Nodes) + skip := false + var currentNode *yaml.Node + + var wg sync.WaitGroup + var errors []error + var ops []low.NodeReference[*Operation] + var additionalOps *orderedmap.Map[low.KeyReference[string], low.NodeReference[*Operation]] + + // extract parameters + params, ln, vn, pErr := low.ExtractArray[*Parameter](ctx, ParametersLabel, root, idx) + if pErr != nil { + return pErr + } + if params != nil { + p.Parameters = low.NodeReference[[]low.ValueReference[*Parameter]]{ + Value: params, + KeyNode: ln, + ValueNode: vn, + } + p.Nodes.Store(ln.Line, ln) + } + + _, ln, vn = utils.FindKeyNodeFullTop(ServersLabel, root.Content) + if vn != nil { + if utils.IsNodeArray(vn) { + var servers []low.ValueReference[*Server] + for _, srvN := range vn.Content { + if utils.IsNodeMap(srvN) { + srvr := new(Server) + _ = low.BuildModel(srvN, srvr) + srvr.Build(ctx, ln, srvN, idx) + servers = append(servers, low.ValueReference[*Server]{ + Value: srvr, + ValueNode: srvN, + }) + } + } + p.Servers = low.NodeReference[[]low.ValueReference[*Server]]{ + Value: servers, + KeyNode: ln, + ValueNode: vn, + } + p.Nodes.Store(ln.Line, ln) + } + } + prevExt := false + for i, pathNode := range root.Content { + if strings.HasPrefix(strings.ToLower(pathNode.Value), "x-") { + skip = true + prevExt = true + continue + } + // https://github.com/pb33f/libopenapi/issues/388 + // in the case where a user has an extension with the value 'parameters', make sure we handle + // it correctly, by not skipping. + if strings.HasPrefix(strings.ToLower(pathNode.Value), "parameters") { + if !prevExt { // this + skip = true + continue + } else { + prevExt = false + } + } + if skip { + skip = false + continue + } + if i%2 == 0 { + currentNode = pathNode + continue + } + + // check if this is an operation (either standard or additional) + isStandardOp := false + isAdditionalOp := false + + switch currentNode.Value { + case GetLabel, PostLabel, PutLabel, PatchLabel, DeleteLabel, HeadLabel, OptionsLabel, TraceLabel, QueryLabel: + isStandardOp = true + default: + // check if this looks like an HTTP method (and isn't a known non-operation field) + switch currentNode.Value { + case ParametersLabel, ServersLabel, SummaryLabel, DescriptionLabel: + continue // ignore known non-operation fields + default: + // assume it's an additional operation if it contains a mapping to an operation object + if utils.IsNodeMap(pathNode) { + isAdditionalOp = true + } else { + continue // ignore if not a map + } + } + } + + foundContext, pathNode, opIsRef, opRefVal, opRefNode, err := resolveOperationReference(ctx, pathNode, idx) + if err != nil { + return err + } + var op Operation + wg.Add(1) + low.BuildModelAsync(pathNode, &op, &wg, &errors) + + opRef := low.NodeReference[*Operation]{ + Value: &op, + KeyNode: currentNode, + ValueNode: pathNode, + Context: foundContext, + } + if opIsRef { + opRef.SetReference(opRefVal, opRefNode) + } + + ops = append(ops, opRef) + + if isStandardOp { + switch currentNode.Value { + case GetLabel: + p.Get = opRef + case PostLabel: + p.Post = opRef + case PutLabel: + p.Put = opRef + case PatchLabel: + p.Patch = opRef + case DeleteLabel: + p.Delete = opRef + case HeadLabel: + p.Head = opRef + case OptionsLabel: + p.Options = opRef + case TraceLabel: + p.Trace = opRef + case QueryLabel: + p.Query = opRef + } + } else if isAdditionalOp { + // initialize additionalOps map if this is the first additional operation + if additionalOps == nil { + additionalOps = orderedmap.New[low.KeyReference[string], low.NodeReference[*Operation]]() + } + + // now we need to determine if these are inline additional operations, or just plonked into the root. + if currentNode.Value == AdditionalOperationsLabel { + + for j := 0; j < len(pathNode.Content); j += 2 { + opKeyNode := pathNode.Content[j] + opValueNode := pathNode.Content[j+1] + + // resolve operation reference for each additional operation + foundContext, opValueNode, opIsRef, opRefVal, opRefNode, err = resolveOperationReference(ctx, opValueNode, idx) + if err != nil { + return err + } + var addOp Operation + wg.Add(1) + low.BuildModelAsync(opValueNode, &addOp, &wg, &errors) + + addOpRef := low.NodeReference[*Operation]{ + Value: &addOp, + KeyNode: opKeyNode, + ValueNode: opValueNode, + Context: foundContext, + } + if opIsRef { + addOpRef.SetReference(opRefVal, opRefNode) + } + + additionalOps.Set(low.KeyReference[string]{ + KeyNode: opKeyNode, + Value: opKeyNode.Value, + }, addOpRef) + } + } else { + + kv := pathNode.Value + if kv == "" { + kv = currentNode.Value + } + + additionalOps.Set(low.KeyReference[string]{ + KeyNode: currentNode, + Value: kv, + }, opRef) + + } + } + } + + // all operations have been superficially built, + // now we need to build out the operation, we will do this asynchronously for speed. + translateFunc := func(_ int, op low.NodeReference[*Operation]) (any, error) { + ref := "" + var refNode *yaml.Node + if op.IsReference() { + ref = op.GetReference() + refNode = op.GetReferenceNode() + } + + err := op.Value.Build(op.Context, op.KeyNode, op.ValueNode, op.Context.Value(index.FoundIndexKey).(*index.SpecIndex)) + if ref != "" { + op.Value.Reference.SetReference(ref, refNode) + } + if err != nil { + return nil, err + } + return nil, nil + } + err := datamodel.TranslateSliceParallel[low.NodeReference[*Operation], any](ops, translateFunc, nil) + if err != nil { + return err + } + + // assign additionalOperations if any were found + if additionalOps != nil && additionalOps.Len() > 0 { + var extrOps []low.NodeReference[*Operation] + // build out each additional operation + for _, appVal := range additionalOps.FromOldest() { + extrOps = append(extrOps, appVal) + } + + err = datamodel.TranslateSliceParallel[low.NodeReference[*Operation], any](extrOps, translateFunc, nil) + + p.AdditionalOperations = low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.NodeReference[*Operation]]]{ + Value: additionalOps, + } + } + return nil +} + +// resolveOperationReference handles the resolution of operation references ($ref) +// Returns: foundContext, resolvedPathNode, isRef, refValue, refNode, error +func resolveOperationReference(ctx context.Context, pathNode *yaml.Node, idx *index.SpecIndex) ( + context.Context, *yaml.Node, bool, string, *yaml.Node, error) { + + foundContext := ctx + opIsRef := false + var opRefVal string + var opRefNode *yaml.Node + + if ok, _, ref := utils.IsNodeRefValue(pathNode); ok { + // According to OpenAPI spec the only valid $ref for paths is + // reference for the whole pathItem. Unfortunately, the internet is full of invalid specs + // even from trusted companies like DigitalOcean where they tend to + // use file $ref for each respective operation: + // /endpoint/call/name: + // post: + // $ref: 'file.yaml' + // Check if that is the case and resolve such thing properly too. + + opIsRef = true + opRefVal = ref + opRefNode = pathNode + r, newIdx, err, nCtx := low.LocateRefNodeWithContext(ctx, pathNode, idx) + if r != nil { + if r.Kind == yaml.DocumentNode { + r = r.Content[0] + } + pathNode = r + foundContext = nCtx + foundContext = context.WithValue(foundContext, index.FoundIndexKey, newIdx) + + if r.Tag == "" { + // If it's a node from file, tag is empty + pathNode = r.Content[0] + } + + if err != nil { + if !idx.AllowCircularReferenceResolving() { + return nil, nil, false, "", nil, fmt.Errorf("build schema failed: %s", err.Error()) + } + } + } else { + return nil, nil, false, "", nil, fmt.Errorf("path item build failed: cannot find reference: %s at line %d, col %d", + pathNode.Content[1].Value, pathNode.Content[1].Line, pathNode.Content[1].Column) + } + } else { + foundContext = context.WithValue(foundContext, index.FoundIndexKey, idx) + } + + return foundContext, pathNode, opIsRef, opRefVal, opRefNode, nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/paths.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/paths.go new file mode 100644 index 000000000..1306a5e13 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/paths.go @@ -0,0 +1,258 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + "fmt" + "hash/maphash" + "strings" + "sync" + + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Paths represents a high-level OpenAPI 3+ Paths object, that is backed by a low-level one. +// +// Holds the relative paths to the individual endpoints and their operations. The path is appended to the URL from the +// Server Object in order to construct the full URL. The Paths MAY be empty, due to Access Control List (ACL) +// constraints. +// - https://spec.openapis.org/oas/v3.1.0#paths-object +type Paths struct { + PathItems *orderedmap.Map[low.KeyReference[string], low.ValueReference[*PathItem]] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// GetIndex returns the index.SpecIndex instance attached to the Paths object. +func (p *Paths) GetIndex() *index.SpecIndex { + return p.index +} + +// GetContext returns the context.Context instance used when building the Paths object. +func (p *Paths) GetContext() context.Context { + return p.context +} + +// GetRootNode returns the root yaml node of the Paths object. +func (p *Paths) GetRootNode() *yaml.Node { + return p.RootNode +} + +// GetKeyNode returns the key yaml node of the Paths object. +func (p *Paths) GetKeyNode() *yaml.Node { + return p.KeyNode +} + +// FindPath will attempt to locate a PathItem using the provided path string. +func (p *Paths) FindPath(path string) (result *low.ValueReference[*PathItem]) { + for pair := orderedmap.First(p.PathItems); pair != nil; pair = pair.Next() { + if pair.Key().Value == path { + result = pair.ValuePtr() + break + } + } + return result +} + +// FindPathAndKey attempts to locate a PathItem instance, given a path key. +func (p *Paths) FindPathAndKey(path string) (key *low.KeyReference[string], value *low.ValueReference[*PathItem]) { + for pair := orderedmap.First(p.PathItems); pair != nil; pair = pair.Next() { + if pair.Key().Value == path { + key = pair.KeyPtr() + value = pair.ValuePtr() + break + } + } + return key, value +} + +// FindExtension will attempt to locate an extension using the specified string. +func (p *Paths) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, p.Extensions) +} + +// GetExtensions returns all Paths extensions and satisfies the low.HasExtensions interface. +func (p *Paths) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return p.Extensions +} + +// Build will extract extensions and all PathItems. This happens asynchronously for speed. +func (p *Paths) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + root = utils.NodeAlias(root) + p.KeyNode = keyNode + p.RootNode = root + utils.CheckForMergeNodes(root) + p.Reference = new(low.Reference) + p.Nodes = low.ExtractNodes(ctx, keyNode) + p.Extensions = low.ExtractExtensions(root) + p.index = idx + p.context = ctx + + low.ExtractExtensionNodes(ctx, p.Extensions, p.Nodes) + + pathsMap, err := extractPathItemsMap(ctx, root, idx) + if err != nil { + return err + } + + p.PathItems = pathsMap + + for k, v := range pathsMap.FromOldest() { + // add path as node to path item, not this path object. + v.Value.Nodes.Store(k.KeyNode.Line, k.KeyNode) + } + + return nil +} + +// Hash will return a consistent Hash of the Paths object +func (p *Paths) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + for _, hash := range low.AppendMapHashes(nil, p.PathItems) { + h.WriteString(hash) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(p.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} + +func extractPathItemsMap(ctx context.Context, root *yaml.Node, idx *index.SpecIndex) (*orderedmap.Map[low.KeyReference[string], low.ValueReference[*PathItem]], error) { + // Translate YAML nodes to pathsMap using `TranslatePipeline`. + type buildResult struct { + key low.KeyReference[string] + value low.ValueReference[*PathItem] + } + type buildInput struct { + currentNode *yaml.Node + pathNode *yaml.Node + } + pathsMap := orderedmap.New[low.KeyReference[string], low.ValueReference[*PathItem]]() + in := make(chan buildInput) + out := make(chan buildResult) + done := make(chan struct{}) + var wg sync.WaitGroup + wg.Add(2) // input and output goroutines. + + // TranslatePipeline input. + go func() { + defer func() { + close(in) + wg.Done() + }() + skip := false + var currentNode *yaml.Node + if root != nil { + for i, pathNode := range root.Content { + if strings.HasPrefix(strings.ToLower(pathNode.Value), "x-") { + skip = true + continue + } + if skip { + skip = false + continue + } + if i%2 == 0 { + currentNode = pathNode + continue + } + + select { + case in <- buildInput{ + currentNode: currentNode, + pathNode: pathNode, + }: + case <-done: + return + } + } + } + }() + + // TranslatePipeline output. + go func() { + for { + result, ok := <-out + if !ok { + break + } + pathsMap.Set(result.key, result.value) + } + close(done) + wg.Done() + }() + + err := datamodel.TranslatePipeline[buildInput, buildResult](in, out, + func(value buildInput) (buildResult, error) { + pNode := value.pathNode + cNode := value.currentNode + + foundContext := ctx + var isRef bool + var refNode *yaml.Node + if ok, _, _ := utils.IsNodeRefValue(pNode); ok { + isRef = true + refNode = pNode + r, _, err, fCtx := low.LocateRefNodeWithContext(ctx, pNode, idx) + if r != nil { + pNode = r + foundContext = fCtx + if err != nil { + if !idx.AllowCircularReferenceResolving() { + return buildResult{}, fmt.Errorf("path item build failed: %s", err.Error()) + } + } + } else { + return buildResult{}, fmt.Errorf("path item build failed: cannot find reference: '%s' at line %d, col %d", + pNode.Content[1].Value, pNode.Content[1].Line, pNode.Content[1].Column) + } + } + + path := new(PathItem) + _ = low.BuildModel(pNode, path) + err := path.Build(foundContext, cNode, pNode, idx) + + if isRef { + path.SetReference(refNode.Content[1].Value, refNode) + } + + if err != nil { + if idx != nil && idx.GetLogger() != nil { + idx.GetLogger().Error(fmt.Sprintf("error building path item: %s", err.Error())) + } + // return buildResult{}, err + } + + return buildResult{ + key: low.KeyReference[string]{ + Value: cNode.Value, + KeyNode: cNode, + }, + value: low.ValueReference[*PathItem]{ + Value: path, + ValueNode: pNode, + }, + }, nil + }, + ) + wg.Wait() + if err != nil { + return nil, err + } + return pathsMap, nil +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/request_body.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/request_body.go new file mode 100644 index 000000000..06b054602 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/request_body.go @@ -0,0 +1,124 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + "hash/maphash" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// RequestBody represents a low-level OpenAPI 3+ RequestBody object. +// - https://spec.openapis.org/oas/v3.1.0#request-body-object +type RequestBody struct { + Description low.NodeReference[string] + Content low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*MediaType]]] + Required low.NodeReference[bool] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// GetIndex returns the index.SpecIndex instance attached to the RequestBody object. +func (rb *RequestBody) GetIndex() *index.SpecIndex { + return rb.index +} + +// GetContext returns the context.Context instance used when building the RequestBody object. +func (rb *RequestBody) GetContext() context.Context { + return rb.context +} + +// GetRootNode returns the root yaml node of the RequestBody object. +func (rb *RequestBody) GetRootNode() *yaml.Node { + return rb.RootNode +} + +// GetKeyNode returns the key yaml node of the RequestBody object. +func (rb *RequestBody) GetKeyNode() *yaml.Node { + return rb.KeyNode +} + +// FindExtension attempts to locate an extension using the provided name. +func (rb *RequestBody) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, rb.Extensions) +} + +// GetExtensions returns all RequestBody extensions and satisfies the low.HasExtensions interface. +func (rb *RequestBody) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return rb.Extensions +} + +// FindContent attempts to find content/MediaType defined using a specified name. +func (rb *RequestBody) FindContent(cType string) *low.ValueReference[*MediaType] { + return low.FindItemInOrderedMap[*MediaType](cType, rb.Content.Value) +} + +// Build will extract extensions and MediaType objects from the node. +func (rb *RequestBody) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + rb.KeyNode = keyNode + rb.Reference = new(low.Reference) + if ok, _, ref := utils.IsNodeRefValue(root); ok { + rb.SetReference(ref, root) + } + root = utils.NodeAlias(root) + rb.RootNode = root + utils.CheckForMergeNodes(root) + rb.Nodes = low.ExtractNodes(ctx, root) + rb.Extensions = low.ExtractExtensions(root) + rb.index = idx + rb.context = ctx + + low.ExtractExtensionNodes(ctx, rb.Extensions, rb.Nodes) + + // handle content, if set. + con, cL, cN, cErr := low.ExtractMap[*MediaType](ctx, ContentLabel, root, idx) + if cErr != nil { + return cErr + } + if con != nil { + rb.Content = low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*MediaType]]]{ + Value: con, + KeyNode: cL, + ValueNode: cN, + } + rb.Nodes.Store(cL.Line, cL) + for k, v := range con.FromOldest() { + v.Value.Nodes.Store(k.KeyNode.Line, k.KeyNode) + } + } + return nil +} + +// Hash will return a consistent Hash of the RequestBody object +func (rb *RequestBody) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if rb.Description.Value != "" { + h.WriteString(rb.Description.Value) + h.WriteByte(low.HASH_PIPE) + } + if !rb.Required.IsEmpty() { + low.HashBool(h, rb.Required.Value) + h.WriteByte(low.HASH_PIPE) + } + for v := range orderedmap.SortAlpha(rb.Content.Value).ValuesFromOldest() { + h.WriteString(low.GenerateHashString(v.Value)) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(rb.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/response.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/response.go new file mode 100644 index 000000000..72eca09b0 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/response.go @@ -0,0 +1,181 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + "hash/maphash" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Response represents a high-level OpenAPI 3+ Response object that is backed by a low-level one. +// +// Describes a single response from an API Operation, including design-time, static links to +// operations based on the response. +// - https://spec.openapis.org/oas/v3.1.0#response-object +type Response struct { + Summary low.NodeReference[string] + Description low.NodeReference[string] + Headers low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*Header]]] + Content low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*MediaType]]] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + Links low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*Link]]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// GetIndex returns the index.SpecIndex instance attached to the Response object. +func (r *Response) GetIndex() *index.SpecIndex { + return r.index +} + +// GetContext returns the context.Context instance used when building the Response object. +func (r *Response) GetContext() context.Context { + return r.context +} + +// GetRootNode returns the root yaml node of the Response object. +func (r *Response) GetRootNode() *yaml.Node { + return r.RootNode +} + +// GetKeyNode returns the key yaml node of the Response object. +func (r *Response) GetKeyNode() *yaml.Node { + return r.KeyNode +} + +// FindExtension will attempt to locate an extension using the supplied key +func (r *Response) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, r.Extensions) +} + +// GetExtensions returns all OAuthFlow extensions and satisfies the low.HasExtensions interface. +func (r *Response) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return r.Extensions +} + +// FindContent will attempt to locate a MediaType instance using the supplied key. +func (r *Response) FindContent(cType string) *low.ValueReference[*MediaType] { + return low.FindItemInOrderedMap[*MediaType](cType, r.Content.Value) +} + +// FindHeader will attempt to locate a Header instance using the supplied key. +func (r *Response) FindHeader(hType string) *low.ValueReference[*Header] { + return low.FindItemInOrderedMap[*Header](hType, r.Headers.Value) +} + +// FindLink will attempt to locate a Link instance using the supplied key. +func (r *Response) FindLink(hType string) *low.ValueReference[*Link] { + return low.FindItemInOrderedMap[*Link](hType, r.Links.Value) +} + +// Build will extract headers, extensions, content and links from node. +func (r *Response) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + r.KeyNode = keyNode + r.Reference = new(low.Reference) + if ok, _, ref := utils.IsNodeRefValue(root); ok { + r.SetReference(ref, root) + } + root = utils.NodeAlias(root) + r.RootNode = root + utils.CheckForMergeNodes(root) + r.Nodes = low.ExtractNodes(ctx, root) + r.Extensions = low.ExtractExtensions(root) + r.index = idx + r.context = ctx + + low.ExtractExtensionNodes(ctx, r.Extensions, r.Nodes) + + // extract headers + headers, lN, kN, err := low.ExtractMapExtensions[*Header](ctx, HeadersLabel, root, idx, true) + if err != nil { + return err + } + if headers != nil { + r.Headers = low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*Header]]]{ + Value: headers, + KeyNode: lN, + ValueNode: kN, + } + r.Nodes.Store(lN.Line, lN) + for k, v := range headers.FromOldest() { + v.Value.Nodes.Store(k.KeyNode.Line, k.KeyNode) + } + } + + con, clN, cN, cErr := low.ExtractMap[*MediaType](ctx, ContentLabel, root, idx) + if cErr != nil { + return cErr + } + if con != nil { + r.Content = low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*MediaType]]]{ + Value: con, + KeyNode: clN, + ValueNode: cN, + } + r.Nodes.Store(clN.Line, clN) + for k, v := range con.FromOldest() { + v.Value.Nodes.Store(k.KeyNode.Line, k.KeyNode) + } + } + + // handle links if set + links, linkLabel, linkValue, lErr := low.ExtractMap[*Link](ctx, LinksLabel, root, idx) + if lErr != nil { + return lErr + } + if links != nil { + r.Links = low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*Link]]]{ + Value: links, + KeyNode: linkLabel, + ValueNode: linkValue, + } + r.Nodes.Store(linkLabel.Line, linkLabel) + for k, v := range links.FromOldest() { + v.Value.Nodes.Store(k.KeyNode.Line, k.KeyNode) + } + } + return nil +} + +// Hash will return a consistent Hash of the Response object +func (r *Response) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if r.Summary.Value != "" { + h.WriteString(r.Summary.Value) + h.WriteByte(low.HASH_PIPE) + } + if r.Description.Value != "" { + h.WriteString(r.Description.Value) + h.WriteByte(low.HASH_PIPE) + } + + for _, hash := range low.AppendMapHashes(nil, r.Headers.Value) { + h.WriteString(hash) + h.WriteByte(low.HASH_PIPE) + } + for _, hash := range low.AppendMapHashes(nil, r.Content.Value) { + h.WriteString(hash) + h.WriteByte(low.HASH_PIPE) + } + for _, hash := range low.AppendMapHashes(nil, r.Links.Value) { + h.WriteString(hash) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(r.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/responses.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/responses.go new file mode 100644 index 000000000..5a43db462 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/responses.go @@ -0,0 +1,164 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + "fmt" + "hash/maphash" + "strings" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Responses represents a low-level OpenAPI 3+ Responses object. +// +// It's a container for the expected responses of an operation. The container maps an HTTP response code to the +// expected response. +// +// The specification is not necessarily expected to cover all possible HTTP response codes because they may not be +// known in advance. However, documentation is expected to cover a successful operation response and any known errors. +// +// The default MAY be used as a default response object for all HTTP codes that are not covered individually by +// the Responses Object. +// +// The Responses Object MUST contain at least one response code, and if only one response code is provided it SHOULD +// be the response for a successful operation call. +// - https://spec.openapis.org/oas/v3.1.0#responses-object +// +// This structure is identical to the v2 version, however they use different response types, hence +// the duplication. Perhaps in the future we could use generics here, but for now to keep things +// simple, they are broken out into individual versions. +type Responses struct { + Codes *orderedmap.Map[low.KeyReference[string], low.ValueReference[*Response]] + Default low.NodeReference[*Response] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// GetIndex returns the index.SpecIndex instance attached to the Responses object. +func (r *Responses) GetIndex() *index.SpecIndex { + return r.index +} + +// GetContext returns the context.Context instance used when building the Responses object. +func (r *Responses) GetContext() context.Context { + return r.context +} + +// GetRootNode returns the root yaml node of the Responses object. +func (r *Responses) GetRootNode() *yaml.Node { + return r.RootNode +} + +// GetKeyNode returns the key yaml node of the Responses object. +func (r *Responses) GetKeyNode() *yaml.Node { + return r.KeyNode +} + +// GetExtensions returns all Responses extensions and satisfies the low.HasExtensions interface. +func (r *Responses) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return r.Extensions +} + +// Build will extract default response and all Response objects for each code +func (r *Responses) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + r.KeyNode = keyNode + root = utils.NodeAlias(root) + r.RootNode = root + r.Reference = new(low.Reference) + r.Nodes = low.ExtractNodes(ctx, root) + r.Extensions = low.ExtractExtensions(root) + r.index = idx + r.context = ctx + + low.ExtractExtensionNodes(ctx, r.Extensions, r.Nodes) + utils.CheckForMergeNodes(root) + if utils.IsNodeMap(root) { + codes, err := low.ExtractMapNoLookup[*Response](ctx, root, idx) + if err != nil { + return err + } + if codes != nil { + r.Codes = codes + for code := range codes.KeysFromOldest() { + r.Nodes.Store(code.KeyNode.Line, code.KeyNode) + } + } + + def := r.getDefault() + if def != nil { + // default is bundled into codes, pull it out + r.Default = *def + r.Nodes.Store(def.KeyNode.Line, def.KeyNode) + // remove default from codes + r.deleteCode(DefaultLabel) + } + } else { + return fmt.Errorf("responses build failed: vn node is not a map! line %d, col %d", + root.Line, root.Column) + } + return nil +} + +func (r *Responses) getDefault() *low.NodeReference[*Response] { + for code, resp := range r.Codes.FromOldest() { + if strings.ToLower(code.Value) == DefaultLabel { + return &low.NodeReference[*Response]{ + ValueNode: resp.ValueNode, + KeyNode: code.KeyNode, + Value: resp.Value, + } + } + } + return nil +} + +// used to remove default from codes extracted by Build() +func (r *Responses) deleteCode(code string) { + var key *low.KeyReference[string] + for pair := orderedmap.First(r.Codes); pair != nil; pair = pair.Next() { + if pair.Key().Value == code { + key = pair.KeyPtr() + break + } + } + // should never be nil, but, you never know... science and all that! + if key != nil { + r.Codes.Delete(*key) + } +} + +// FindResponseByCode will attempt to locate a Response using an HTTP response code. +func (r *Responses) FindResponseByCode(code string) *low.ValueReference[*Response] { + return low.FindItemInOrderedMap[*Response](code, r.Codes) +} + +// Hash will return a consistent Hash of the Responses object +func (r *Responses) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + for _, hash := range low.AppendMapHashes(nil, r.Codes) { + h.WriteString(hash) + h.WriteByte(low.HASH_PIPE) + } + if !r.Default.IsEmpty() { + h.WriteString(low.GenerateHashString(r.Default.Value)) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(r.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/security_scheme.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/security_scheme.go new file mode 100644 index 000000000..42788eed0 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/security_scheme.go @@ -0,0 +1,153 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + "hash/maphash" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// SecurityScheme represents a low-level OpenAPI 3+ SecurityScheme object. +// +// Defines a security scheme that can be used by the operations. +// +// Supported schemes are HTTP authentication, an API key (either as a header, a cookie parameter or as a query parameter), +// mutual TLS (use of a client certificate), OAuth2’s common flows (implicit, password, client credentials and +// authorization code) as defined in RFC6749 (https://www.rfc-editor.org/rfc/rfc6749), and OpenID Connect Discovery. +// Please note that as of 2020, the implicit flow is about to be deprecated by OAuth 2.0 Security Best Current Practice. +// Recommended for most use case is Authorization Code Grant flow with PKCE. +// - https://spec.openapis.org/oas/v3.1.0#security-scheme-object +type SecurityScheme struct { + Type low.NodeReference[string] + Description low.NodeReference[string] + Name low.NodeReference[string] + In low.NodeReference[string] + Scheme low.NodeReference[string] + BearerFormat low.NodeReference[string] + Flows low.NodeReference[*OAuthFlows] + OpenIdConnectUrl low.NodeReference[string] + OAuth2MetadataUrl low.NodeReference[string] // OpenAPI 3.2+ OAuth2 metadata URL + Deprecated low.NodeReference[bool] // OpenAPI 3.2+ deprecated flag + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// GetIndex returns the index.SpecIndex instance attached to the SecurityScheme object. +func (ss *SecurityScheme) GetIndex() *index.SpecIndex { + return ss.index +} + +// GetContext returns the context.Context instance used when building the SecurityScheme object. +func (ss *SecurityScheme) GetContext() context.Context { + return ss.context +} + +// GetRootNode returns the root yaml node of the SecurityScheme object. +func (ss *SecurityScheme) GetRootNode() *yaml.Node { + return ss.RootNode +} + +// GetKeyNode returns the key yaml node of the SecurityScheme object. +func (ss *SecurityScheme) GetKeyNode() *yaml.Node { + return ss.KeyNode +} + +// FindExtension attempts to locate an extension using the supplied key. +func (ss *SecurityScheme) FindExtension(ext string) *low.ValueReference[*yaml.Node] { + return low.FindItemInOrderedMap(ext, ss.Extensions) +} + +// GetExtensions returns all SecurityScheme extensions and satisfies the low.HasExtensions interface. +func (ss *SecurityScheme) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return ss.Extensions +} + +// Build will extract OAuthFlows and extensions from the node. +func (ss *SecurityScheme) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + ss.KeyNode = keyNode + ss.Reference = new(low.Reference) + if ok, _, ref := utils.IsNodeRefValue(root); ok { + ss.SetReference(ref, root) + } + root = utils.NodeAlias(root) + ss.RootNode = root + utils.CheckForMergeNodes(root) + ss.Nodes = low.ExtractNodes(ctx, root) + ss.Extensions = low.ExtractExtensions(root) + ss.index = idx + ss.context = ctx + + low.ExtractExtensionNodes(ctx, ss.Extensions, ss.Nodes) + + oa, oaErr := low.ExtractObject[*OAuthFlows](ctx, OAuthFlowsLabel, root, idx) + if oaErr != nil { + return oaErr + } + if oa.Value != nil { + ss.Flows = oa + } + return nil +} + +// Hash will return a consistent Hash of the SecurityScheme object +func (ss *SecurityScheme) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if !ss.Type.IsEmpty() { + h.WriteString(ss.Type.Value) + h.WriteByte(low.HASH_PIPE) + } + if !ss.Description.IsEmpty() { + h.WriteString(ss.Description.Value) + h.WriteByte(low.HASH_PIPE) + } + if !ss.Name.IsEmpty() { + h.WriteString(ss.Name.Value) + h.WriteByte(low.HASH_PIPE) + } + if !ss.In.IsEmpty() { + h.WriteString(ss.In.Value) + h.WriteByte(low.HASH_PIPE) + } + if !ss.Scheme.IsEmpty() { + h.WriteString(ss.Scheme.Value) + h.WriteByte(low.HASH_PIPE) + } + if !ss.BearerFormat.IsEmpty() { + h.WriteString(ss.BearerFormat.Value) + h.WriteByte(low.HASH_PIPE) + } + if !ss.Flows.IsEmpty() { + h.WriteString(low.GenerateHashString(ss.Flows.Value)) + h.WriteByte(low.HASH_PIPE) + } + if !ss.OpenIdConnectUrl.IsEmpty() { + h.WriteString(ss.OpenIdConnectUrl.Value) + h.WriteByte(low.HASH_PIPE) + } + if !ss.OAuth2MetadataUrl.IsEmpty() { + h.WriteString(ss.OAuth2MetadataUrl.Value) + h.WriteByte(low.HASH_PIPE) + } + if !ss.Deprecated.IsEmpty() { + low.HashBool(h, ss.Deprecated.Value) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(ss.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/server.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/server.go new file mode 100644 index 000000000..5b45b1769 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/server.go @@ -0,0 +1,143 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "context" + "hash/maphash" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// Server represents a low-level OpenAPI 3+ Server object. +// - https://spec.openapis.org/oas/v3.1.0#server-object +type Server struct { + Name low.NodeReference[string] // OpenAPI 3.2+ name field for documentation + URL low.NodeReference[string] + Description low.NodeReference[string] + Variables low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*ServerVariable]]] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context + *low.Reference + low.NodeMap +} + +// GetIndex returns the index.SpecIndex instance attached to the Server object. +func (s *Server) GetIndex() *index.SpecIndex { + return s.index +} + +// GetContext returns the context.Context instance used when building the Server object. +func (s *Server) GetContext() context.Context { + return s.context +} + +// GetRootNode returns the root yaml node of the Server object. +func (s *Server) GetRootNode() *yaml.Node { + return s.RootNode +} + +// GetExtensions returns all Paths extensions and satisfies the low.HasExtensions interface. +func (s *Server) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return s.Extensions +} + +// FindVariable attempts to locate a ServerVariable instance using the supplied key. +func (s *Server) FindVariable(serverVar string) *low.ValueReference[*ServerVariable] { + return low.FindItemInOrderedMap[*ServerVariable](serverVar, s.Variables.Value) +} + +// Build will extract server variables from the supplied node. +func (s *Server) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { + s.KeyNode = keyNode + root = utils.NodeAlias(root) + s.RootNode = root + utils.CheckForMergeNodes(root) + s.Reference = new(low.Reference) + s.Nodes = low.ExtractNodes(ctx, root) + s.Extensions = low.ExtractExtensions(root) + s.context = ctx + s.index = idx + + low.ExtractExtensionNodes(ctx, s.Extensions, s.Nodes) + + kn, vars := utils.FindKeyNode(VariablesLabel, root.Content) + if vars == nil { + return nil + } + variablesMap := orderedmap.New[low.KeyReference[string], low.ValueReference[*ServerVariable]]() + if utils.IsNodeMap(vars) { + var currentNode string + var localKeyNode *yaml.Node + for i, varNode := range vars.Content { + if i%2 == 0 { + currentNode = varNode.Value + localKeyNode = varNode + continue + } + variable := ServerVariable{} + variable.Reference = new(low.Reference) + _ = low.BuildModel(varNode, &variable) + variable.Nodes = low.ExtractNodesRecursive(ctx, varNode) + variable.Extensions = low.ExtractExtensions(varNode) + if localKeyNode != nil { + variable.Nodes.Store(localKeyNode.Line, localKeyNode) + } + variable.RootNode = varNode + variable.KeyNode = localKeyNode + variablesMap.Set( + low.KeyReference[string]{ + Value: currentNode, + KeyNode: localKeyNode, + }, + low.ValueReference[*ServerVariable]{ + ValueNode: varNode, + Value: &variable, + }, + ) + } + s.Variables = low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*ServerVariable]]]{ + KeyNode: kn, + ValueNode: vars, + Value: variablesMap, + } + } + return nil +} + +// Hash will return a consistent Hash of the Server object +func (s *Server) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + if !s.Name.IsEmpty() { + h.WriteString(s.Name.Value) + h.WriteByte(low.HASH_PIPE) + } + if s.Variables.Value != nil { + for v := range orderedmap.SortAlpha(s.Variables.Value).ValuesFromOldest() { + h.WriteString(low.GenerateHashString(v.Value)) + h.WriteByte(low.HASH_PIPE) + } + } + if !s.URL.IsEmpty() { + h.WriteString(s.URL.Value) + h.WriteByte(low.HASH_PIPE) + } + if !s.Description.IsEmpty() { + h.WriteString(s.Description.Value) + h.WriteByte(low.HASH_PIPE) + } + for _, ext := range low.HashExtensions(s.Extensions) { + h.WriteString(ext) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/server_variable.go b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/server_variable.go new file mode 100644 index 000000000..b9e6636fa --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/low/v3/server_variable.go @@ -0,0 +1,71 @@ +package v3 + +import ( + "hash/maphash" + "sort" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// ServerVariable represents a low-level OpenAPI 3+ ServerVariable object. +// +// ServerVariable is an object representing a Server Variable for server URL template substitution. +// - https://spec.openapis.org/oas/v3.1.0#server-variable-object +// +// This is the only struct that is not Buildable, it's not used by anything other than a Server instance, +// and it has nothing to build that requires it to be buildable. +type ServerVariable struct { + Enum []low.NodeReference[string] + Default low.NodeReference[string] + Description low.NodeReference[string] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + *low.Reference + low.NodeMap +} + +// GetRootNode returns the root yaml node of the ServerVariable object. +func (s *ServerVariable) GetRootNode() *yaml.Node { + return s.RootNode +} + +// GetKeyNode returns the key yaml node of the ServerVariable object. +func (s *ServerVariable) GetKeyNode() *yaml.Node { + return s.RootNode +} + +// GetExtensions returns all extensions and satisfies the low.HasExtensions interface. +func (s *ServerVariable) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { + return s.Extensions +} + +// Hash will return a consistent Hash of the ServerVariable object +func (s *ServerVariable) Hash() uint64 { + return low.WithHasher(func(h *maphash.Hash) uint64 { + // Pre-allocate and sort enum values + if len(s.Enum) > 0 { + keys := make([]string, len(s.Enum)) + for i := range s.Enum { + keys[i] = s.Enum[i].Value + } + sort.Strings(keys) + for _, key := range keys { + h.WriteString(key) + h.WriteByte(low.HASH_PIPE) + } + } + + if !s.Default.IsEmpty() { + h.WriteString(s.Default.Value) + h.WriteByte(low.HASH_PIPE) + } + if !s.Description.IsEmpty() { + h.WriteString(s.Description.Value) + h.WriteByte(low.HASH_PIPE) + } + return h.Sum64() + }) +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/schemas/oas3-schema.json b/vendor/github.com/pb33f/libopenapi/datamodel/schemas/oas3-schema.json new file mode 100644 index 000000000..4360553fe --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/schemas/oas3-schema.json @@ -0,0 +1,1662 @@ +{ + "id": "https://spec.openapis.org/oas/3.0/schema/2021-09-28", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "The description of OpenAPI v3.0.x documents, as defined by https://spec.openapis.org/oas/v3.0.3", + "type": "object", + "required": [ + "openapi", + "info", + "paths" + ], + "properties": { + "openapi": { + "type": "string", + "pattern": "^3\\.0\\.\\d(-.+)?$" + }, + "info": { + "$ref": "#/definitions/Info" + }, + "externalDocs": { + "$ref": "#/definitions/ExternalDocumentation" + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/definitions/Server" + } + }, + "security": { + "type": "array", + "items": { + "$ref": "#/definitions/SecurityRequirement" + } + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/definitions/Tag" + }, + "uniqueItems": true + }, + "paths": { + "$ref": "#/definitions/Paths" + }, + "components": { + "$ref": "#/definitions/Components" + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false, + "definitions": { + "Reference": { + "type": "object", + "required": [ + "$ref" + ], + "patternProperties": { + "^\\$ref$": { + "type": "string", + "format": "uri-reference" + } + } + }, + "Info": { + "type": "object", + "required": [ + "title", + "version" + ], + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "termsOfService": { + "type": "string", + "format": "uri-reference" + }, + "contact": { + "$ref": "#/definitions/Contact" + }, + "license": { + "$ref": "#/definitions/License" + }, + "version": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false + }, + "Contact": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri-reference" + }, + "email": { + "type": "string", + "format": "email" + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false + }, + "License": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri-reference" + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false + }, + "Server": { + "type": "object", + "required": [ + "url" + ], + "properties": { + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "variables": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/ServerVariable" + } + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false + }, + "ServerVariable": { + "type": "object", + "required": [ + "default" + ], + "properties": { + "enum": { + "type": "array", + "items": { + "type": "string" + } + }, + "default": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false + }, + "Components": { + "type": "object", + "properties": { + "schemas": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9\\.\\-_]+$": { + "oneOf": [ + { + "$ref": "#/definitions/Schema" + }, + { + "$ref": "#/definitions/Reference" + } + ] + } + } + }, + "responses": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9\\.\\-_]+$": { + "oneOf": [ + { + "$ref": "#/definitions/Reference" + }, + { + "$ref": "#/definitions/Response" + } + ] + } + } + }, + "parameters": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9\\.\\-_]+$": { + "oneOf": [ + { + "$ref": "#/definitions/Reference" + }, + { + "$ref": "#/definitions/Parameter" + } + ] + } + } + }, + "examples": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9\\.\\-_]+$": { + "oneOf": [ + { + "$ref": "#/definitions/Reference" + }, + { + "$ref": "#/definitions/Example" + } + ] + } + } + }, + "requestBodies": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9\\.\\-_]+$": { + "oneOf": [ + { + "$ref": "#/definitions/Reference" + }, + { + "$ref": "#/definitions/RequestBody" + } + ] + } + } + }, + "headers": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9\\.\\-_]+$": { + "oneOf": [ + { + "$ref": "#/definitions/Reference" + }, + { + "$ref": "#/definitions/Header" + } + ] + } + } + }, + "securitySchemes": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9\\.\\-_]+$": { + "oneOf": [ + { + "$ref": "#/definitions/Reference" + }, + { + "$ref": "#/definitions/SecurityScheme" + } + ] + } + } + }, + "links": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9\\.\\-_]+$": { + "oneOf": [ + { + "$ref": "#/definitions/Reference" + }, + { + "$ref": "#/definitions/Link" + } + ] + } + } + }, + "callbacks": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9\\.\\-_]+$": { + "oneOf": [ + { + "$ref": "#/definitions/Reference" + }, + { + "$ref": "#/definitions/Callback" + } + ] + } + } + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false + }, + "Schema": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "multipleOf": { + "type": "number", + "minimum": 0, + "exclusiveMinimum": true + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "boolean", + "default": false + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "boolean", + "default": false + }, + "maxLength": { + "type": "integer", + "minimum": 0 + }, + "minLength": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "pattern": { + "type": "string", + "format": "regex" + }, + "maxItems": { + "type": "integer", + "minimum": 0 + }, + "minItems": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "maxProperties": { + "type": "integer", + "minimum": 0 + }, + "minProperties": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "required": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "uniqueItems": true + }, + "enum": { + "type": "array", + "items": { + }, + "minItems": 1, + "uniqueItems": false + }, + "type": { + "type": "string", + "enum": [ + "array", + "boolean", + "integer", + "number", + "object", + "string" + ] + }, + "not": { + "oneOf": [ + { + "$ref": "#/definitions/Schema" + }, + { + "$ref": "#/definitions/Reference" + } + ] + }, + "allOf": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/definitions/Schema" + }, + { + "$ref": "#/definitions/Reference" + } + ] + } + }, + "oneOf": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/definitions/Schema" + }, + { + "$ref": "#/definitions/Reference" + } + ] + } + }, + "anyOf": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/definitions/Schema" + }, + { + "$ref": "#/definitions/Reference" + } + ] + } + }, + "items": { + "oneOf": [ + { + "$ref": "#/definitions/Schema" + }, + { + "$ref": "#/definitions/Reference" + } + ] + }, + "properties": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "$ref": "#/definitions/Schema" + }, + { + "$ref": "#/definitions/Reference" + } + ] + } + }, + "additionalProperties": { + "oneOf": [ + { + "$ref": "#/definitions/Schema" + }, + { + "$ref": "#/definitions/Reference" + }, + { + "type": "boolean" + } + ], + "default": true + }, + "description": { + "type": "string" + }, + "format": { + "type": "string" + }, + "default": { + }, + "nullable": { + "type": "boolean", + "default": false + }, + "discriminator": { + "$ref": "#/definitions/Discriminator" + }, + "readOnly": { + "type": "boolean", + "default": false + }, + "writeOnly": { + "type": "boolean", + "default": false + }, + "example": { + }, + "externalDocs": { + "$ref": "#/definitions/ExternalDocumentation" + }, + "deprecated": { + "type": "boolean", + "default": false + }, + "xml": { + "$ref": "#/definitions/XML" + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false + }, + "Discriminator": { + "type": "object", + "required": [ + "propertyName" + ], + "properties": { + "propertyName": { + "type": "string" + }, + "mapping": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "XML": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "namespace": { + "type": "string", + "format": "uri" + }, + "prefix": { + "type": "string" + }, + "attribute": { + "type": "boolean", + "default": false + }, + "wrapped": { + "type": "boolean", + "default": false + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false + }, + "Response": { + "type": "object", + "required": [ + "description" + ], + "properties": { + "description": { + "type": "string" + }, + "headers": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "$ref": "#/definitions/Header" + }, + { + "$ref": "#/definitions/Reference" + } + ] + } + }, + "content": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/MediaType" + } + }, + "links": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "$ref": "#/definitions/Link" + }, + { + "$ref": "#/definitions/Reference" + } + ] + } + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false + }, + "MediaType": { + "type": "object", + "properties": { + "schema": { + "oneOf": [ + { + "$ref": "#/definitions/Schema" + }, + { + "$ref": "#/definitions/Reference" + } + ] + }, + "example": { + }, + "examples": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "$ref": "#/definitions/Example" + }, + { + "$ref": "#/definitions/Reference" + } + ] + } + }, + "encoding": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/Encoding" + } + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false, + "allOf": [ + { + "$ref": "#/definitions/ExampleXORExamples" + } + ] + }, + "Example": { + "type": "object", + "properties": { + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "value": { + }, + "externalValue": { + "type": "string", + "format": "uri-reference" + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false + }, + "Header": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "required": { + "type": "boolean", + "default": false + }, + "deprecated": { + "type": "boolean", + "default": false + }, + "allowEmptyValue": { + "type": "boolean", + "default": false + }, + "style": { + "type": "string", + "enum": [ + "simple" + ], + "default": "simple" + }, + "explode": { + "type": "boolean" + }, + "allowReserved": { + "type": "boolean", + "default": false + }, + "schema": { + "oneOf": [ + { + "$ref": "#/definitions/Schema" + }, + { + "$ref": "#/definitions/Reference" + } + ] + }, + "content": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/MediaType" + }, + "minProperties": 1, + "maxProperties": 1 + }, + "example": { + }, + "examples": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "$ref": "#/definitions/Example" + }, + { + "$ref": "#/definitions/Reference" + } + ] + } + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false, + "allOf": [ + { + "$ref": "#/definitions/ExampleXORExamples" + }, + { + "$ref": "#/definitions/SchemaXORContent" + } + ] + }, + "Paths": { + "type": "object", + "patternProperties": { + "^\\/": { + "$ref": "#/definitions/PathItem" + }, + "^x-": { + } + }, + "additionalProperties": false + }, + "PathItem": { + "type": "object", + "properties": { + "$ref": { + "type": "string" + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/definitions/Server" + } + }, + "parameters": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/definitions/Parameter" + }, + { + "$ref": "#/definitions/Reference" + } + ] + }, + "uniqueItems": true + } + }, + "patternProperties": { + "^(get|put|post|delete|options|head|patch|trace)$": { + "$ref": "#/definitions/Operation" + }, + "^x-": { + } + }, + "additionalProperties": false + }, + "Operation": { + "type": "object", + "required": [ + "responses" + ], + "properties": { + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/definitions/ExternalDocumentation" + }, + "operationId": { + "type": "string" + }, + "parameters": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/definitions/Parameter" + }, + { + "$ref": "#/definitions/Reference" + } + ] + }, + "uniqueItems": true + }, + "requestBody": { + "oneOf": [ + { + "$ref": "#/definitions/RequestBody" + }, + { + "$ref": "#/definitions/Reference" + } + ] + }, + "responses": { + "$ref": "#/definitions/Responses" + }, + "callbacks": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "$ref": "#/definitions/Callback" + }, + { + "$ref": "#/definitions/Reference" + } + ] + } + }, + "deprecated": { + "type": "boolean", + "default": false + }, + "security": { + "type": "array", + "items": { + "$ref": "#/definitions/SecurityRequirement" + } + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/definitions/Server" + } + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false + }, + "Responses": { + "type": "object", + "properties": { + "default": { + "oneOf": [ + { + "$ref": "#/definitions/Response" + }, + { + "$ref": "#/definitions/Reference" + } + ] + } + }, + "patternProperties": { + "^[1-5](?:\\d{2}|XX)$": { + "oneOf": [ + { + "$ref": "#/definitions/Response" + }, + { + "$ref": "#/definitions/Reference" + } + ] + }, + "^x-": { + } + }, + "minProperties": 1, + "additionalProperties": false + }, + "SecurityRequirement": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "Tag": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/definitions/ExternalDocumentation" + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false + }, + "ExternalDocumentation": { + "type": "object", + "required": [ + "url" + ], + "properties": { + "description": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri-reference" + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false + }, + "ExampleXORExamples": { + "description": "Example and examples are mutually exclusive", + "not": { + "required": [ + "example", + "examples" + ] + } + }, + "SchemaXORContent": { + "description": "Schema and content are mutually exclusive, at least one is required", + "not": { + "required": [ + "schema", + "content" + ] + }, + "oneOf": [ + { + "required": [ + "schema" + ] + }, + { + "required": [ + "content" + ], + "description": "Some properties are not allowed if content is present", + "allOf": [ + { + "not": { + "required": [ + "style" + ] + } + }, + { + "not": { + "required": [ + "explode" + ] + } + }, + { + "not": { + "required": [ + "allowReserved" + ] + } + }, + { + "not": { + "required": [ + "example" + ] + } + }, + { + "not": { + "required": [ + "examples" + ] + } + } + ] + } + ] + }, + "Parameter": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "in": { + "type": "string" + }, + "description": { + "type": "string" + }, + "required": { + "type": "boolean", + "default": false + }, + "deprecated": { + "type": "boolean", + "default": false + }, + "allowEmptyValue": { + "type": "boolean", + "default": false + }, + "style": { + "type": "string" + }, + "explode": { + "type": "boolean" + }, + "allowReserved": { + "type": "boolean", + "default": false + }, + "schema": { + "oneOf": [ + { + "$ref": "#/definitions/Schema" + }, + { + "$ref": "#/definitions/Reference" + } + ] + }, + "content": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/MediaType" + }, + "minProperties": 1, + "maxProperties": 1 + }, + "example": { + }, + "examples": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "$ref": "#/definitions/Example" + }, + { + "$ref": "#/definitions/Reference" + } + ] + } + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false, + "required": [ + "name", + "in" + ], + "allOf": [ + { + "$ref": "#/definitions/ExampleXORExamples" + }, + { + "$ref": "#/definitions/SchemaXORContent" + }, + { + "$ref": "#/definitions/ParameterLocation" + } + ] + }, + "ParameterLocation": { + "description": "Parameter location", + "oneOf": [ + { + "description": "Parameter in path", + "required": [ + "required" + ], + "properties": { + "in": { + "enum": [ + "path" + ] + }, + "style": { + "enum": [ + "matrix", + "label", + "simple" + ], + "default": "simple" + }, + "required": { + "enum": [ + true + ] + } + } + }, + { + "description": "Parameter in query", + "properties": { + "in": { + "enum": [ + "query" + ] + }, + "style": { + "enum": [ + "form", + "spaceDelimited", + "pipeDelimited", + "deepObject" + ], + "default": "form" + } + } + }, + { + "description": "Parameter in header", + "properties": { + "in": { + "enum": [ + "header" + ] + }, + "style": { + "enum": [ + "simple" + ], + "default": "simple" + } + } + }, + { + "description": "Parameter in cookie", + "properties": { + "in": { + "enum": [ + "cookie" + ] + }, + "style": { + "enum": [ + "form" + ], + "default": "form" + } + } + } + ] + }, + "RequestBody": { + "type": "object", + "required": [ + "content" + ], + "properties": { + "description": { + "type": "string" + }, + "content": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/MediaType" + } + }, + "required": { + "type": "boolean", + "default": false + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false + }, + "SecurityScheme": { + "oneOf": [ + { + "$ref": "#/definitions/APIKeySecurityScheme" + }, + { + "$ref": "#/definitions/HTTPSecurityScheme" + }, + { + "$ref": "#/definitions/OAuth2SecurityScheme" + }, + { + "$ref": "#/definitions/OpenIdConnectSecurityScheme" + } + ] + }, + "APIKeySecurityScheme": { + "type": "object", + "required": [ + "type", + "name", + "in" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "apiKey" + ] + }, + "name": { + "type": "string" + }, + "in": { + "type": "string", + "enum": [ + "header", + "query", + "cookie" + ] + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false + }, + "HTTPSecurityScheme": { + "type": "object", + "required": [ + "scheme", + "type" + ], + "properties": { + "scheme": { + "type": "string" + }, + "bearerFormat": { + "type": "string" + }, + "description": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "http" + ] + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false, + "oneOf": [ + { + "description": "Bearer", + "properties": { + "scheme": { + "type": "string", + "pattern": "^[Bb][Ee][Aa][Rr][Ee][Rr]$" + } + } + }, + { + "description": "Non Bearer", + "not": { + "required": [ + "bearerFormat" + ] + }, + "properties": { + "scheme": { + "not": { + "type": "string", + "pattern": "^[Bb][Ee][Aa][Rr][Ee][Rr]$" + } + } + } + } + ] + }, + "OAuth2SecurityScheme": { + "type": "object", + "required": [ + "type", + "flows" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "oauth2" + ] + }, + "flows": { + "$ref": "#/definitions/OAuthFlows" + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false + }, + "OpenIdConnectSecurityScheme": { + "type": "object", + "required": [ + "type", + "openIdConnectUrl" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "openIdConnect" + ] + }, + "openIdConnectUrl": { + "type": "string", + "format": "uri-reference" + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false + }, + "OAuthFlows": { + "type": "object", + "properties": { + "implicit": { + "$ref": "#/definitions/ImplicitOAuthFlow" + }, + "password": { + "$ref": "#/definitions/PasswordOAuthFlow" + }, + "clientCredentials": { + "$ref": "#/definitions/ClientCredentialsFlow" + }, + "authorizationCode": { + "$ref": "#/definitions/AuthorizationCodeOAuthFlow" + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false + }, + "ImplicitOAuthFlow": { + "type": "object", + "required": [ + "authorizationUrl", + "scopes" + ], + "properties": { + "authorizationUrl": { + "type": "string", + "format": "uri-reference" + }, + "refreshUrl": { + "type": "string", + "format": "uri-reference" + }, + "scopes": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false + }, + "PasswordOAuthFlow": { + "type": "object", + "required": [ + "tokenUrl", + "scopes" + ], + "properties": { + "tokenUrl": { + "type": "string", + "format": "uri-reference" + }, + "refreshUrl": { + "type": "string", + "format": "uri-reference" + }, + "scopes": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false + }, + "ClientCredentialsFlow": { + "type": "object", + "required": [ + "tokenUrl", + "scopes" + ], + "properties": { + "tokenUrl": { + "type": "string", + "format": "uri-reference" + }, + "refreshUrl": { + "type": "string", + "format": "uri-reference" + }, + "scopes": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false + }, + "AuthorizationCodeOAuthFlow": { + "type": "object", + "required": [ + "authorizationUrl", + "tokenUrl", + "scopes" + ], + "properties": { + "authorizationUrl": { + "type": "string", + "format": "uri-reference" + }, + "tokenUrl": { + "type": "string", + "format": "uri-reference" + }, + "refreshUrl": { + "type": "string", + "format": "uri-reference" + }, + "scopes": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false + }, + "Link": { + "type": "object", + "properties": { + "operationId": { + "type": "string" + }, + "operationRef": { + "type": "string", + "format": "uri-reference" + }, + "parameters": { + "type": "object", + "additionalProperties": { + } + }, + "requestBody": { + }, + "description": { + "type": "string" + }, + "server": { + "$ref": "#/definitions/Server" + } + }, + "patternProperties": { + "^x-": { + } + }, + "additionalProperties": false, + "not": { + "description": "Operation Id and Operation Ref are mutually exclusive", + "required": [ + "operationId", + "operationRef" + ] + } + }, + "Callback": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/PathItem" + }, + "patternProperties": { + "^x-": { + } + } + }, + "Encoding": { + "type": "object", + "properties": { + "contentType": { + "type": "string" + }, + "headers": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "$ref": "#/definitions/Header" + }, + { + "$ref": "#/definitions/Reference" + } + ] + } + }, + "style": { + "type": "string", + "enum": [ + "form", + "spaceDelimited", + "pipeDelimited", + "deepObject" + ] + }, + "explode": { + "type": "boolean" + }, + "allowReserved": { + "type": "boolean", + "default": false + } + }, + "additionalProperties": false + } + } +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/schemas/oas31-schema.json b/vendor/github.com/pb33f/libopenapi/datamodel/schemas/oas31-schema.json new file mode 100644 index 000000000..fe9749c2e --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/schemas/oas31-schema.json @@ -0,0 +1,1407 @@ +{ + "$id": "https://spec.openapis.org/oas/3.1/schema/2022-10-07", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "The description of OpenAPI v3.1.x documents without schema validation, as defined by https://spec.openapis.org/oas/v3.1.0", + "type": "object", + "properties": { + "openapi": { + "type": "string", + "pattern": "^3\\.1\\.\\d+(-.+)?$" + }, + "info": { + "$ref": "#/$defs/info" + }, + "jsonSchemaDialect": { + "type": "string", + "format": "uri", + "default": "https://spec.openapis.org/oas/3.1/dialect/base" + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + }, + "default": [ + { + "url": "/" + } + ] + }, + "paths": { + "$ref": "#/$defs/paths" + }, + "webhooks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/path-item" + } + }, + "components": { + "$ref": "#/$defs/components" + }, + "security": { + "type": "array", + "items": { + "$ref": "#/$defs/security-requirement" + } + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/$defs/tag" + } + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + } + }, + "required": [ + "openapi", + "info" + ], + "anyOf": [ + { + "required": [ + "paths" + ] + }, + { + "required": [ + "components" + ] + }, + { + "required": [ + "webhooks" + ] + } + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false, + "$defs": { + "info": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#info-object", + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "termsOfService": { + "type": "string", + "format": "uri" + }, + "contact": { + "$ref": "#/$defs/contact" + }, + "license": { + "$ref": "#/$defs/license" + }, + "version": { + "type": "string" + } + }, + "required": [ + "title", + "version" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "contact": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#contact-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + }, + "email": { + "type": "string", + "format": "email" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "license": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#license-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "identifier": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "name" + ], + "dependentSchemas": { + "identifier": { + "not": { + "required": [ + "url" + ] + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "server": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#server-object", + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "variables": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/server-variable" + } + } + }, + "required": [ + "url" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "server-variable": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#server-variable-object", + "type": "object", + "properties": { + "enum": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "default": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "default" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "components": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#components-object", + "type": "object", + "properties": { + "schemas": { + "type": "object", + "additionalProperties": { + "$dynamicRef": "#meta" + } + }, + "responses": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/response-or-reference" + } + }, + "parameters": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/parameter-or-reference" + } + }, + "examples": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/example-or-reference" + } + }, + "requestBodies": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/request-body-or-reference" + } + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "securitySchemes": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/security-scheme-or-reference" + } + }, + "links": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/link-or-reference" + } + }, + "callbacks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/callbacks-or-reference" + } + }, + "pathItems": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/path-item" + } + } + }, + "patternProperties": { + "^(schemas|responses|parameters|examples|requestBodies|headers|securitySchemes|links|callbacks|pathItems)$": { + "$comment": "Enumerating all of the property names in the regex above is necessary for unevaluatedProperties to work as expected", + "propertyNames": { + "pattern": "^[a-zA-Z0-9._-]+$" + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "paths": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#paths-object", + "type": "object", + "patternProperties": { + "^/": { + "$ref": "#/$defs/path-item" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "path-item": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#path-item-object", + "type": "object", + "properties": { + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + } + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/$defs/parameter-or-reference" + } + }, + "get": { + "$ref": "#/$defs/operation" + }, + "put": { + "$ref": "#/$defs/operation" + }, + "post": { + "$ref": "#/$defs/operation" + }, + "delete": { + "$ref": "#/$defs/operation" + }, + "options": { + "$ref": "#/$defs/operation" + }, + "head": { + "$ref": "#/$defs/operation" + }, + "patch": { + "$ref": "#/$defs/operation" + }, + "trace": { + "$ref": "#/$defs/operation" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "operation": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#operation-object", + "type": "object", + "properties": { + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + }, + "operationId": { + "type": "string" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/$defs/parameter-or-reference" + } + }, + "requestBody": { + "$ref": "#/$defs/request-body-or-reference" + }, + "responses": { + "$ref": "#/$defs/responses" + }, + "callbacks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/callbacks-or-reference" + } + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "security": { + "type": "array", + "items": { + "$ref": "#/$defs/security-requirement" + } + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "external-documentation": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#external-documentation-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "url" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "parameter": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#parameter-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "in": { + "enum": [ + "query", + "header", + "path", + "cookie" + ] + }, + "description": { + "type": "string" + }, + "required": { + "default": false, + "type": "boolean" + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "schema": { + "$dynamicRef": "#meta" + }, + "content": { + "$ref": "#/$defs/content", + "minProperties": 1, + "maxProperties": 1 + } + }, + "required": [ + "name", + "in" + ], + "oneOf": [ + { + "required": [ + "schema" + ] + }, + { + "required": [ + "content" + ] + } + ], + "if": { + "properties": { + "in": { + "const": "query" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "allowEmptyValue": { + "default": false, + "type": "boolean" + } + } + }, + "dependentSchemas": { + "schema": { + "properties": { + "style": { + "type": "string" + }, + "explode": { + "type": "boolean" + } + }, + "allOf": [ + { + "$ref": "#/$defs/examples" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-path" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-header" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-query" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-cookie" + }, + { + "$ref": "#/$defs/styles-for-form" + } + ], + "$defs": { + "styles-for-path": { + "if": { + "properties": { + "in": { + "const": "path" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "simple", + "enum": [ + "matrix", + "label", + "simple" + ] + }, + "required": { + "const": true + } + }, + "required": [ + "required" + ] + } + }, + "styles-for-header": { + "if": { + "properties": { + "in": { + "const": "header" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "simple", + "const": "simple" + } + } + } + }, + "styles-for-query": { + "if": { + "properties": { + "in": { + "const": "query" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "form", + "enum": [ + "form", + "spaceDelimited", + "pipeDelimited", + "deepObject" + ] + }, + "allowReserved": { + "default": false, + "type": "boolean" + } + } + } + }, + "styles-for-cookie": { + "if": { + "properties": { + "in": { + "const": "cookie" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "form", + "const": "form" + } + } + } + } + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "parameter-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/parameter" + } + }, + "request-body": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#request-body-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "content": { + "$ref": "#/$defs/content" + }, + "required": { + "default": false, + "type": "boolean" + } + }, + "required": [ + "content" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "request-body-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/request-body" + } + }, + "content": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#fixed-fields-10", + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/media-type" + }, + "propertyNames": { + "format": "media-range" + } + }, + "media-type": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#media-type-object", + "type": "object", + "properties": { + "schema": { + "$dynamicRef": "#meta" + }, + "encoding": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/encoding" + } + } + }, + "allOf": [ + { + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/examples" + } + ], + "unevaluatedProperties": false + }, + "encoding": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#encoding-object", + "type": "object", + "properties": { + "contentType": { + "type": "string", + "format": "media-range" + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "style": { + "default": "form", + "enum": [ + "form", + "spaceDelimited", + "pipeDelimited", + "deepObject" + ] + }, + "explode": { + "type": "boolean" + }, + "allowReserved": { + "default": false, + "type": "boolean" + } + }, + "allOf": [ + { + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/styles-for-form" + } + ], + "unevaluatedProperties": false + }, + "responses": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#responses-object", + "type": "object", + "properties": { + "default": { + "$ref": "#/$defs/response-or-reference" + } + }, + "patternProperties": { + "^[1-5](?:[0-9]{2}|XX)$": { + "$ref": "#/$defs/response-or-reference" + } + }, + "minProperties": 1, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false, + "if": { + "$comment": "either default, or at least one response code property must exist", + "patternProperties": { + "^[1-5](?:[0-9]{2}|XX)$": false + } + }, + "then": { + "required": [ + "default" + ] + } + }, + "response": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#response-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "content": { + "$ref": "#/$defs/content" + }, + "links": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/link-or-reference" + } + } + }, + "required": [ + "description" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "response-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/response" + } + }, + "callbacks": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#callback-object", + "type": "object", + "$ref": "#/$defs/specification-extensions", + "additionalProperties": { + "$ref": "#/$defs/path-item" + } + }, + "callbacks-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/callbacks" + } + }, + "example": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#example-object", + "type": "object", + "properties": { + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "value": true, + "externalValue": { + "type": "string", + "format": "uri" + } + }, + "not": { + "required": [ + "value", + "externalValue" + ] + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "example-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/example" + } + }, + "link": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#link-object", + "type": "object", + "properties": { + "operationRef": { + "type": "string" + }, + "operationId": { + "type": "string" + }, + "parameters": { + "$ref": "#/$defs/map-of-strings" + }, + "requestBody": true, + "description": { + "type": "string" + }, + "body": { + "$ref": "#/$defs/server" + } + }, + "oneOf": [ + { + "required": [ + "operationRef" + ] + }, + { + "required": [ + "operationId" + ] + } + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "link-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/link" + } + }, + "header": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#header-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "required": { + "default": false, + "type": "boolean" + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "schema": { + "$dynamicRef": "#meta" + }, + "content": { + "$ref": "#/$defs/content", + "minProperties": 1, + "maxProperties": 1 + } + }, + "oneOf": [ + { + "required": [ + "schema" + ] + }, + { + "required": [ + "content" + ] + } + ], + "dependentSchemas": { + "schema": { + "properties": { + "style": { + "default": "simple", + "const": "simple" + }, + "explode": { + "default": false, + "type": "boolean" + } + }, + "$ref": "#/$defs/examples" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "header-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/header" + } + }, + "tag": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#tag-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + } + }, + "required": [ + "name" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "reference": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#reference-object", + "type": "object", + "properties": { + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + } + } + }, + "schema": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#schema-object", + "$dynamicAnchor": "meta", + "type": [ + "object", + "boolean" + ] + }, + "security-scheme": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#security-scheme-object", + "type": "object", + "properties": { + "type": { + "enum": [ + "apiKey", + "http", + "mutualTLS", + "oauth2", + "openIdConnect" + ] + }, + "description": { + "type": "string" + } + }, + "required": [ + "type" + ], + "allOf": [ + { + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-apikey" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-http" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-http-bearer" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-oauth2" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-oidc" + } + ], + "unevaluatedProperties": false, + "$defs": { + "type-apikey": { + "if": { + "properties": { + "type": { + "const": "apiKey" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "name": { + "type": "string" + }, + "in": { + "enum": [ + "query", + "header", + "cookie" + ] + } + }, + "required": [ + "name", + "in" + ] + } + }, + "type-http": { + "if": { + "properties": { + "type": { + "const": "http" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "scheme": { + "type": "string" + } + }, + "required": [ + "scheme" + ] + } + }, + "type-http-bearer": { + "if": { + "properties": { + "type": { + "const": "http" + }, + "scheme": { + "type": "string", + "pattern": "^[Bb][Ee][Aa][Rr][Ee][Rr]$" + } + }, + "required": [ + "type", + "scheme" + ] + }, + "then": { + "properties": { + "bearerFormat": { + "type": "string" + } + } + } + }, + "type-oauth2": { + "if": { + "properties": { + "type": { + "const": "oauth2" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "flows": { + "$ref": "#/$defs/oauth-flows" + } + }, + "required": [ + "flows" + ] + } + }, + "type-oidc": { + "if": { + "properties": { + "type": { + "const": "openIdConnect" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "openIdConnectUrl": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "openIdConnectUrl" + ] + } + } + } + }, + "security-scheme-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/security-scheme" + } + }, + "oauth-flows": { + "type": "object", + "properties": { + "implicit": { + "$ref": "#/$defs/oauth-flows/$defs/implicit" + }, + "password": { + "$ref": "#/$defs/oauth-flows/$defs/password" + }, + "clientCredentials": { + "$ref": "#/$defs/oauth-flows/$defs/client-credentials" + }, + "authorizationCode": { + "$ref": "#/$defs/oauth-flows/$defs/authorization-code" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false, + "$defs": { + "implicit": { + "type": "object", + "properties": { + "authorizationUrl": { + "type": "string", + "format": "uri" + }, + "refreshUrl": { + "type": "string", + "format": "uri" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "authorizationUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "password": { + "type": "object", + "properties": { + "tokenUrl": { + "type": "string", + "format": "uri" + }, + "refreshUrl": { + "type": "string", + "format": "uri" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "tokenUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "client-credentials": { + "type": "object", + "properties": { + "tokenUrl": { + "type": "string", + "format": "uri" + }, + "refreshUrl": { + "type": "string", + "format": "uri" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "tokenUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "authorization-code": { + "type": "object", + "properties": { + "authorizationUrl": { + "type": "string", + "format": "uri" + }, + "tokenUrl": { + "type": "string", + "format": "uri" + }, + "refreshUrl": { + "type": "string", + "format": "uri" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "authorizationUrl", + "tokenUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + } + } + }, + "security-requirement": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#security-requirement-object", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "specification-extensions": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#specification-extensions", + "patternProperties": { + "^x-": true + } + }, + "examples": { + "properties": { + "example": true, + "examples": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/example-or-reference" + } + } + } + }, + "map-of-strings": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "styles-for-form": { + "if": { + "properties": { + "style": { + "const": "form" + } + }, + "required": [ + "style" + ] + }, + "then": { + "properties": { + "explode": { + "default": true + } + } + }, + "else": { + "properties": { + "explode": { + "default": false + } + } + } + } + } +} \ No newline at end of file diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/schemas/oas32-schema.json b/vendor/github.com/pb33f/libopenapi/datamodel/schemas/oas32-schema.json new file mode 100644 index 000000000..9453eeb20 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/schemas/oas32-schema.json @@ -0,0 +1,1666 @@ +{ + "$id": "https://spec.openapis.org/oas/3.2/schema/2025-09-17", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "The description of OpenAPI v3.2.x Documents without Schema Object validation", + "type": "object", + "properties": { + "openapi": { + "type": "string", + "pattern": "^3\\.2\\.\\d+(-.+)?$" + }, + "$self": { + "type": "string", + "format": "uri-reference", + "$comment": "MUST NOT contain a fragment", + "pattern": "^[^#]*$" + }, + "info": { + "$ref": "#/$defs/info" + }, + "jsonSchemaDialect": { + "type": "string", + "format": "uri-reference", + "default": "https://spec.openapis.org/oas/3.2/dialect/2025-09-17" + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + }, + "default": [ + { + "url": "/" + } + ] + }, + "paths": { + "$ref": "#/$defs/paths" + }, + "webhooks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/path-item" + } + }, + "components": { + "$ref": "#/$defs/components" + }, + "security": { + "type": "array", + "items": { + "$ref": "#/$defs/security-requirement" + } + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/$defs/tag" + } + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + } + }, + "required": [ + "openapi", + "info" + ], + "anyOf": [ + { + "required": [ + "paths" + ] + }, + { + "required": [ + "components" + ] + }, + { + "required": [ + "webhooks" + ] + } + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false, + "$defs": { + "info": { + "$comment": "https://spec.openapis.org/oas/v3.2#info-object", + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "termsOfService": { + "type": "string", + "format": "uri-reference" + }, + "contact": { + "$ref": "#/$defs/contact" + }, + "license": { + "$ref": "#/$defs/license" + }, + "version": { + "type": "string" + } + }, + "required": [ + "title", + "version" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "contact": { + "$comment": "https://spec.openapis.org/oas/v3.2#contact-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri-reference" + }, + "email": { + "type": "string", + "format": "email" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "license": { + "$comment": "https://spec.openapis.org/oas/v3.2#license-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "identifier": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri-reference" + } + }, + "required": [ + "name" + ], + "dependentSchemas": { + "identifier": { + "not": { + "required": [ + "url" + ] + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "server": { + "$comment": "https://spec.openapis.org/oas/v3.2#server-object", + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "variables": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/server-variable" + } + } + }, + "required": [ + "url" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "server-variable": { + "$comment": "https://spec.openapis.org/oas/v3.2#server-variable-object", + "type": "object", + "properties": { + "enum": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "default": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "default" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "components": { + "$comment": "https://spec.openapis.org/oas/v3.2#components-object", + "type": "object", + "properties": { + "schemas": { + "type": "object", + "additionalProperties": { + "$dynamicRef": "#meta" + } + }, + "responses": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/response-or-reference" + } + }, + "parameters": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/parameter-or-reference" + } + }, + "examples": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/example-or-reference" + } + }, + "requestBodies": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/request-body-or-reference" + } + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "securitySchemes": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/security-scheme-or-reference" + } + }, + "links": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/link-or-reference" + } + }, + "callbacks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/callbacks-or-reference" + } + }, + "pathItems": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/path-item" + } + }, + "mediaTypes": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/media-type-or-reference" + } + } + }, + "patternProperties": { + "^(?:schemas|responses|parameters|examples|requestBodies|headers|securitySchemes|links|callbacks|pathItems|mediaTypes)$": { + "$comment": "Enumerating all of the property names in the regex above is necessary for unevaluatedProperties to work as expected", + "propertyNames": { + "pattern": "^[a-zA-Z0-9._-]+$" + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "paths": { + "$comment": "https://spec.openapis.org/oas/v3.2#paths-object", + "type": "object", + "patternProperties": { + "^/": { + "$ref": "#/$defs/path-item" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "path-item": { + "$comment": "https://spec.openapis.org/oas/v3.2#path-item-object", + "type": "object", + "properties": { + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + } + }, + "parameters": { + "$ref": "#/$defs/parameters" + }, + "additionalOperations": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/operation" + }, + "propertyNames": { + "$comment": "RFC9110 restricts methods to \"1*tchar\" in ABNF", + "pattern": "^[a-zA-Z0-9!#$%&'*+.^_`|~-]+$", + "not": { + "enum": [ + "GET", + "PUT", + "POST", + "DELETE", + "OPTIONS", + "HEAD", + "PATCH", + "TRACE", + "QUERY" + ] + } + } + }, + "get": { + "$ref": "#/$defs/operation" + }, + "put": { + "$ref": "#/$defs/operation" + }, + "post": { + "$ref": "#/$defs/operation" + }, + "delete": { + "$ref": "#/$defs/operation" + }, + "options": { + "$ref": "#/$defs/operation" + }, + "head": { + "$ref": "#/$defs/operation" + }, + "patch": { + "$ref": "#/$defs/operation" + }, + "trace": { + "$ref": "#/$defs/operation" + }, + "query": { + "$ref": "#/$defs/operation" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "operation": { + "$comment": "https://spec.openapis.org/oas/v3.2#operation-object", + "type": "object", + "properties": { + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + }, + "operationId": { + "type": "string" + }, + "parameters": { + "$ref": "#/$defs/parameters" + }, + "requestBody": { + "$ref": "#/$defs/request-body-or-reference" + }, + "responses": { + "$ref": "#/$defs/responses" + }, + "callbacks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/callbacks-or-reference" + } + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "security": { + "type": "array", + "items": { + "$ref": "#/$defs/security-requirement" + } + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "external-documentation": { + "$comment": "https://spec.openapis.org/oas/v3.2#external-documentation-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri-reference" + } + }, + "required": [ + "url" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/$defs/parameter-or-reference" + }, + "not": { + "allOf": [ + { + "contains": { + "type": "object", + "properties": { + "in": { + "const": "query" + } + }, + "required": [ + "in" + ] + } + }, + { + "contains": { + "type": "object", + "properties": { + "in": { + "const": "querystring" + } + }, + "required": [ + "in" + ] + } + } + ] + }, + "contains": { + "type": "object", + "properties": { + "in": { + "const": "querystring" + } + }, + "required": [ + "in" + ] + }, + "minContains": 0, + "maxContains": 1 + }, + "parameter": { + "$comment": "https://spec.openapis.org/oas/v3.2#parameter-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "in": { + "enum": [ + "query", + "querystring", + "header", + "path", + "cookie" + ] + }, + "description": { + "type": "string" + }, + "required": { + "default": false, + "type": "boolean" + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "schema": { + "$dynamicRef": "#meta" + }, + "content": { + "$ref": "#/$defs/content", + "minProperties": 1, + "maxProperties": 1 + } + }, + "required": [ + "name", + "in" + ], + "oneOf": [ + { + "required": [ + "schema" + ] + }, + { + "required": [ + "content" + ] + } + ], + "allOf": [ + { + "$ref": "#/$defs/examples" + }, + { + "$ref": "#/$defs/specification-extensions" + }, + { + "if": { + "properties": { + "in": { + "const": "query" + } + } + }, + "then": { + "properties": { + "allowEmptyValue": { + "default": false, + "type": "boolean" + } + } + } + }, + { + "if": { + "properties": { + "in": { + "const": "querystring" + } + } + }, + "then": { + "required": [ + "content" + ] + } + } + ], + "dependentSchemas": { + "schema": { + "properties": { + "style": { + "type": "string" + }, + "explode": { + "type": "boolean" + }, + "allowReserved": { + "default": false, + "type": "boolean" + } + }, + "allOf": [ + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-path" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-header" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-query" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-cookie" + }, + { + "$ref": "#/$defs/styles-for-form" + } + ], + "$defs": { + "styles-for-path": { + "if": { + "properties": { + "in": { + "const": "path" + } + } + }, + "then": { + "properties": { + "style": { + "default": "simple", + "enum": [ + "matrix", + "label", + "simple" + ] + }, + "required": { + "const": true + } + }, + "required": [ + "required" + ] + } + }, + "styles-for-header": { + "if": { + "properties": { + "in": { + "const": "header" + } + } + }, + "then": { + "properties": { + "style": { + "default": "simple", + "const": "simple" + } + } + } + }, + "styles-for-query": { + "if": { + "properties": { + "in": { + "const": "query" + } + } + }, + "then": { + "properties": { + "style": { + "default": "form", + "enum": [ + "form", + "spaceDelimited", + "pipeDelimited", + "deepObject" + ] + } + } + } + }, + "styles-for-cookie": { + "if": { + "properties": { + "in": { + "const": "cookie" + } + } + }, + "then": { + "properties": { + "style": { + "default": "form", + "enum": [ + "form", + "cookie" + ] + } + } + } + } + } + } + }, + "unevaluatedProperties": false + }, + "parameter-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/parameter" + } + }, + "request-body": { + "$comment": "https://spec.openapis.org/oas/v3.2#request-body-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "content": { + "$ref": "#/$defs/content" + }, + "required": { + "default": false, + "type": "boolean" + } + }, + "required": [ + "content" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "request-body-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/request-body" + } + }, + "content": { + "$comment": "https://spec.openapis.org/oas/v3.2#fixed-fields-10", + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/media-type-or-reference" + }, + "propertyNames": { + "format": "media-range" + } + }, + "media-type": { + "$comment": "https://spec.openapis.org/oas/v3.2#media-type-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "schema": { + "$dynamicRef": "#meta" + }, + "itemSchema": { + "$dynamicRef": "#meta" + }, + "encoding": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/encoding" + } + }, + "prefixEncoding": { + "type": "array", + "items": { + "$ref": "#/$defs/encoding" + } + }, + "itemEncoding": { + "$ref": "#/$defs/encoding" + } + }, + "dependentSchemas": { + "encoding": { + "properties": { + "prefixEncoding": false, + "itemEncoding": false + } + } + }, + "allOf": [ + { + "$ref": "#/$defs/examples" + }, + { + "$ref": "#/$defs/specification-extensions" + } + ], + "unevaluatedProperties": false + }, + "media-type-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/media-type" + } + }, + "encoding": { + "$comment": "https://spec.openapis.org/oas/v3.2#encoding-object", + "type": "object", + "properties": { + "contentType": { + "type": "string", + "format": "media-range" + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "style": { + "enum": [ + "form", + "spaceDelimited", + "pipeDelimited", + "deepObject" + ] + }, + "explode": { + "type": "boolean" + }, + "allowReserved": { + "type": "boolean" + }, + "encoding": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/encoding" + } + }, + "prefixEncoding": { + "type": "array", + "items": { + "$ref": "#/$defs/encoding" + } + }, + "itemEncoding": { + "$ref": "#/$defs/encoding" + } + }, + "dependentSchemas": { + "encoding": { + "properties": { + "prefixEncoding": false, + "itemEncoding": false + } + }, + "style": { + "properties": { + "allowReserved": { + "default": false + } + } + }, + "explode": { + "properties": { + "style": { + "default": "form" + }, + "allowReserved": { + "default": false + } + } + }, + "allowReserved": { + "properties": { + "style": { + "default": "form" + } + } + } + }, + "allOf": [ + { + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/styles-for-form" + } + ], + "unevaluatedProperties": false + }, + "responses": { + "$comment": "https://spec.openapis.org/oas/v3.2#responses-object", + "type": "object", + "properties": { + "default": { + "$ref": "#/$defs/response-or-reference" + } + }, + "patternProperties": { + "^[1-5](?:[0-9]{2}|XX)$": { + "$ref": "#/$defs/response-or-reference" + } + }, + "minProperties": 1, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false, + "if": { + "$comment": "either default, or at least one response code property must exist", + "patternProperties": { + "^[1-5](?:[0-9]{2}|XX)$": false + } + }, + "then": { + "required": [ + "default" + ] + } + }, + "response": { + "$comment": "https://spec.openapis.org/oas/v3.2#response-object", + "type": "object", + "properties": { + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "content": { + "$ref": "#/$defs/content" + }, + "links": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/link-or-reference" + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "response-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/response" + } + }, + "callbacks": { + "$comment": "https://spec.openapis.org/oas/v3.2#callback-object", + "type": "object", + "$ref": "#/$defs/specification-extensions", + "additionalProperties": { + "$ref": "#/$defs/path-item" + } + }, + "callbacks-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/callbacks" + } + }, + "example": { + "$comment": "https://spec.openapis.org/oas/v3.2#example-object", + "type": "object", + "properties": { + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "dataValue": true, + "serializedValue": { + "type": "string" + }, + "value": true, + "externalValue": { + "type": "string", + "format": "uri-reference" + } + }, + "allOf": [ + { + "not": { + "required": [ + "value", + "externalValue" + ] + } + }, + { + "not": { + "required": [ + "value", + "dataValue" + ] + } + }, + { + "not": { + "required": [ + "value", + "serializedValue" + ] + } + }, + { + "not": { + "required": [ + "serializedValue", + "externalValue" + ] + } + } + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "example-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/example" + } + }, + "link": { + "$comment": "https://spec.openapis.org/oas/v3.2#link-object", + "type": "object", + "properties": { + "operationRef": { + "type": "string", + "format": "uri-reference" + }, + "operationId": { + "type": "string" + }, + "parameters": { + "$ref": "#/$defs/map-of-strings" + }, + "requestBody": true, + "description": { + "type": "string" + }, + "server": { + "$ref": "#/$defs/server" + } + }, + "oneOf": [ + { + "required": [ + "operationRef" + ] + }, + { + "required": [ + "operationId" + ] + } + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "link-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/link" + } + }, + "header": { + "$comment": "https://spec.openapis.org/oas/v3.2#header-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "required": { + "default": false, + "type": "boolean" + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "schema": { + "$dynamicRef": "#meta" + }, + "content": { + "$ref": "#/$defs/content", + "minProperties": 1, + "maxProperties": 1 + } + }, + "oneOf": [ + { + "required": [ + "schema" + ] + }, + { + "required": [ + "content" + ] + } + ], + "dependentSchemas": { + "schema": { + "properties": { + "style": { + "default": "simple", + "const": "simple" + }, + "explode": { + "default": false, + "type": "boolean" + }, + "allowReserved": { + "default": false, + "type": "boolean" + } + } + } + }, + "allOf": [ + { + "$ref": "#/$defs/examples" + }, + { + "$ref": "#/$defs/specification-extensions" + } + ], + "unevaluatedProperties": false + }, + "header-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/header" + } + }, + "tag": { + "$comment": "https://spec.openapis.org/oas/v3.2#tag-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + }, + "parent": { + "type": "string" + }, + "kind": { + "type": "string" + } + }, + "required": [ + "name" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "reference": { + "$comment": "https://spec.openapis.org/oas/v3.2#reference-object", + "type": "object", + "properties": { + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + } + } + }, + "schema": { + "$comment": "https://spec.openapis.org/oas/v3.2#schema-object", + "$dynamicAnchor": "meta", + "type": [ + "object", + "boolean" + ] + }, + "security-scheme": { + "$comment": "https://spec.openapis.org/oas/v3.2#security-scheme-object", + "type": "object", + "properties": { + "type": { + "enum": [ + "apiKey", + "http", + "mutualTLS", + "oauth2", + "openIdConnect" + ] + }, + "description": { + "type": "string" + }, + "deprecated": { + "default": false, + "type": "boolean" + } + }, + "required": [ + "type" + ], + "allOf": [ + { + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-apikey" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-http" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-http-bearer" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-oauth2" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-oidc" + } + ], + "unevaluatedProperties": false, + "$defs": { + "type-apikey": { + "if": { + "properties": { + "type": { + "const": "apiKey" + } + } + }, + "then": { + "properties": { + "name": { + "type": "string" + }, + "in": { + "enum": [ + "query", + "header", + "cookie" + ] + } + }, + "required": [ + "name", + "in" + ] + } + }, + "type-http": { + "if": { + "properties": { + "type": { + "const": "http" + } + } + }, + "then": { + "properties": { + "scheme": { + "type": "string" + } + }, + "required": [ + "scheme" + ] + } + }, + "type-http-bearer": { + "if": { + "properties": { + "type": { + "const": "http" + }, + "scheme": { + "type": "string", + "pattern": "^[Bb][Ee][Aa][Rr][Ee][Rr]$" + } + }, + "required": [ + "type", + "scheme" + ] + }, + "then": { + "properties": { + "bearerFormat": { + "type": "string" + } + } + } + }, + "type-oauth2": { + "if": { + "properties": { + "type": { + "const": "oauth2" + } + } + }, + "then": { + "properties": { + "flows": { + "$ref": "#/$defs/oauth-flows" + }, + "oauth2MetadataUrl": { + "type": "string", + "format": "uri-reference" + } + }, + "required": [ + "flows" + ] + } + }, + "type-oidc": { + "if": { + "properties": { + "type": { + "const": "openIdConnect" + } + } + }, + "then": { + "properties": { + "openIdConnectUrl": { + "type": "string", + "format": "uri-reference" + } + }, + "required": [ + "openIdConnectUrl" + ] + } + } + } + }, + "security-scheme-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/security-scheme" + } + }, + "oauth-flows": { + "type": "object", + "properties": { + "implicit": { + "$ref": "#/$defs/oauth-flows/$defs/implicit" + }, + "password": { + "$ref": "#/$defs/oauth-flows/$defs/password" + }, + "clientCredentials": { + "$ref": "#/$defs/oauth-flows/$defs/client-credentials" + }, + "authorizationCode": { + "$ref": "#/$defs/oauth-flows/$defs/authorization-code" + }, + "deviceAuthorization": { + "$ref": "#/$defs/oauth-flows/$defs/device-authorization" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false, + "$defs": { + "implicit": { + "type": "object", + "properties": { + "authorizationUrl": { + "type": "string", + "format": "uri-reference" + }, + "refreshUrl": { + "type": "string", + "format": "uri-reference" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "authorizationUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "password": { + "type": "object", + "properties": { + "tokenUrl": { + "type": "string", + "format": "uri-reference" + }, + "refreshUrl": { + "type": "string", + "format": "uri-reference" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "tokenUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "client-credentials": { + "type": "object", + "properties": { + "tokenUrl": { + "type": "string", + "format": "uri-reference" + }, + "refreshUrl": { + "type": "string", + "format": "uri-reference" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "tokenUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "authorization-code": { + "type": "object", + "properties": { + "authorizationUrl": { + "type": "string", + "format": "uri-reference" + }, + "tokenUrl": { + "type": "string", + "format": "uri-reference" + }, + "refreshUrl": { + "type": "string", + "format": "uri-reference" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "authorizationUrl", + "tokenUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "device-authorization": { + "type": "object", + "properties": { + "deviceAuthorizationUrl": { + "type": "string", + "format": "uri-reference" + }, + "tokenUrl": { + "type": "string", + "format": "uri-reference" + }, + "refreshUrl": { + "type": "string", + "format": "uri-reference" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "deviceAuthorizationUrl", + "tokenUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + } + } + }, + "security-requirement": { + "$comment": "https://spec.openapis.org/oas/v3.2#security-requirement-object", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "specification-extensions": { + "$comment": "https://spec.openapis.org/oas/v3.2#specification-extensions", + "patternProperties": { + "^x-": true + } + }, + "examples": { + "properties": { + "example": true, + "examples": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/example-or-reference" + } + } + }, + "not": { + "required": [ + "example", + "examples" + ] + } + }, + "map-of-strings": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "styles-for-form": { + "if": { + "properties": { + "style": { + "const": "form" + } + }, + "required": [ + "style" + ] + }, + "then": { + "properties": { + "explode": { + "default": true + } + } + }, + "else": { + "properties": { + "explode": { + "default": false + } + } + } + } + } +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/schemas/swagger2-schema.json b/vendor/github.com/pb33f/libopenapi/datamodel/schemas/swagger2-schema.json new file mode 100644 index 000000000..a92e18f2a --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/schemas/swagger2-schema.json @@ -0,0 +1,1607 @@ +{ + "title": "A JSON Schema for Swagger 2.0 API.", + "id": "http://swagger.io/v2/schema.json#", + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "required": [ + "swagger", + "info", + "paths" + ], + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "swagger": { + "type": "string", + "enum": [ + "2.0" + ], + "description": "The Swagger version of this document." + }, + "info": { + "$ref": "#/definitions/info" + }, + "host": { + "type": "string", + "pattern": "^[^{}/ :\\\\]+(?::\\d+)?$", + "description": "The host (name or ip) of the API. Example: 'swagger.io'" + }, + "basePath": { + "type": "string", + "pattern": "^/", + "description": "The base path to the API. Example: '/api'." + }, + "schemes": { + "$ref": "#/definitions/schemesList" + }, + "consumes": { + "description": "A list of MIME types accepted by the API.", + "allOf": [ + { + "$ref": "#/definitions/mediaTypeList" + } + ] + }, + "produces": { + "description": "A list of MIME types the API can produce.", + "allOf": [ + { + "$ref": "#/definitions/mediaTypeList" + } + ] + }, + "paths": { + "$ref": "#/definitions/paths" + }, + "definitions": { + "$ref": "#/definitions/definitions" + }, + "parameters": { + "$ref": "#/definitions/parameterDefinitions" + }, + "responses": { + "$ref": "#/definitions/responseDefinitions" + }, + "security": { + "$ref": "#/definitions/security" + }, + "securityDefinitions": { + "$ref": "#/definitions/securityDefinitions" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/definitions/tag" + }, + "uniqueItems": true + }, + "externalDocs": { + "$ref": "#/definitions/externalDocs" + } + }, + "definitions": { + "info": { + "type": "object", + "description": "General information about the API.", + "required": [ + "version", + "title" + ], + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "title": { + "type": "string", + "description": "A unique and precise title of the API." + }, + "version": { + "type": "string", + "description": "A semantic version number of the API." + }, + "description": { + "type": "string", + "description": "A longer description of the API. Should be different from the title. GitHub Flavored Markdown is allowed." + }, + "termsOfService": { + "type": "string", + "description": "The terms of service for the API." + }, + "contact": { + "$ref": "#/definitions/contact" + }, + "license": { + "$ref": "#/definitions/license" + } + } + }, + "contact": { + "type": "object", + "description": "Contact information for the owners of the API.", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "description": "The identifying name of the contact person/organization." + }, + "url": { + "type": "string", + "description": "The URL pointing to the contact information.", + "format": "uri" + }, + "email": { + "type": "string", + "description": "The email address of the contact person/organization.", + "format": "email" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "license": { + "type": "object", + "required": [ + "name" + ], + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "description": "The name of the license type. It's encouraged to use an OSI compatible license." + }, + "url": { + "type": "string", + "description": "The URL pointing to the license.", + "format": "uri" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "paths": { + "type": "object", + "description": "Relative paths to the individual endpoints. They must be relative to the 'basePath'.", + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + }, + "^/": { + "$ref": "#/definitions/pathItem" + } + }, + "additionalProperties": false + }, + "definitions": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/schema" + }, + "description": "One or more JSON objects describing the schemas being consumed and produced by the API." + }, + "parameterDefinitions": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/parameter" + }, + "description": "One or more JSON representations for parameters" + }, + "responseDefinitions": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/response" + }, + "description": "One or more JSON representations for responses" + }, + "externalDocs": { + "type": "object", + "additionalProperties": false, + "description": "information about external documentation", + "required": [ + "url" + ], + "properties": { + "description": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "examples": { + "type": "object", + "additionalProperties": true + }, + "mimeType": { + "type": "string", + "description": "The MIME type of the HTTP message." + }, + "operation": { + "type": "object", + "required": [ + "responses" + ], + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "summary": { + "type": "string", + "description": "A brief summary of the operation." + }, + "description": { + "type": "string", + "description": "A longer description of the operation, GitHub Flavored Markdown is allowed." + }, + "externalDocs": { + "$ref": "#/definitions/externalDocs" + }, + "operationId": { + "type": "string", + "description": "A unique identifier of the operation." + }, + "produces": { + "description": "A list of MIME types the API can produce.", + "allOf": [ + { + "$ref": "#/definitions/mediaTypeList" + } + ] + }, + "consumes": { + "description": "A list of MIME types the API can consume.", + "allOf": [ + { + "$ref": "#/definitions/mediaTypeList" + } + ] + }, + "parameters": { + "$ref": "#/definitions/parametersList" + }, + "responses": { + "$ref": "#/definitions/responses" + }, + "schemes": { + "$ref": "#/definitions/schemesList" + }, + "deprecated": { + "type": "boolean", + "default": false + }, + "security": { + "$ref": "#/definitions/security" + } + } + }, + "pathItem": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "$ref": { + "type": "string" + }, + "get": { + "$ref": "#/definitions/operation" + }, + "put": { + "$ref": "#/definitions/operation" + }, + "post": { + "$ref": "#/definitions/operation" + }, + "delete": { + "$ref": "#/definitions/operation" + }, + "options": { + "$ref": "#/definitions/operation" + }, + "head": { + "$ref": "#/definitions/operation" + }, + "patch": { + "$ref": "#/definitions/operation" + }, + "parameters": { + "$ref": "#/definitions/parametersList" + } + } + }, + "responses": { + "type": "object", + "description": "Response objects names can either be any valid HTTP status code or 'default'.", + "minProperties": 1, + "additionalProperties": false, + "patternProperties": { + "^([0-9]{3})$|^(default)$": { + "$ref": "#/definitions/responseValue" + }, + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "not": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + } + }, + "responseValue": { + "oneOf": [ + { + "$ref": "#/definitions/response" + }, + { + "$ref": "#/definitions/jsonReference" + } + ] + }, + "response": { + "type": "object", + "required": [ + "description" + ], + "properties": { + "description": { + "type": "string" + }, + "schema": { + "oneOf": [ + { + "$ref": "#/definitions/schema" + }, + { + "$ref": "#/definitions/fileSchema" + } + ] + }, + "headers": { + "$ref": "#/definitions/headers" + }, + "examples": { + "$ref": "#/definitions/examples" + } + }, + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/header" + } + }, + "header": { + "type": "object", + "additionalProperties": false, + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "string", + "number", + "integer", + "boolean", + "array" + ] + }, + "format": { + "type": "string" + }, + "items": { + "$ref": "#/definitions/primitivesItems" + }, + "collectionFormat": { + "$ref": "#/definitions/collectionFormat" + }, + "default": { + "$ref": "#/definitions/default" + }, + "maximum": { + "$ref": "#/definitions/maximum" + }, + "exclusiveMaximum": { + "$ref": "#/definitions/exclusiveMaximum" + }, + "minimum": { + "$ref": "#/definitions/minimum" + }, + "exclusiveMinimum": { + "$ref": "#/definitions/exclusiveMinimum" + }, + "maxLength": { + "$ref": "#/definitions/maxLength" + }, + "minLength": { + "$ref": "#/definitions/minLength" + }, + "pattern": { + "$ref": "#/definitions/pattern" + }, + "maxItems": { + "$ref": "#/definitions/maxItems" + }, + "minItems": { + "$ref": "#/definitions/minItems" + }, + "uniqueItems": { + "$ref": "#/definitions/uniqueItems" + }, + "enum": { + "$ref": "#/definitions/enum" + }, + "multipleOf": { + "$ref": "#/definitions/multipleOf" + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "vendorExtension": { + "description": "Any property starting with x- is valid.", + "additionalProperties": true, + "additionalItems": true + }, + "bodyParameter": { + "type": "object", + "required": [ + "name", + "in", + "schema" + ], + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "description": { + "type": "string", + "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." + }, + "name": { + "type": "string", + "description": "The name of the parameter." + }, + "in": { + "type": "string", + "description": "Determines the location of the parameter.", + "enum": [ + "body" + ] + }, + "required": { + "type": "boolean", + "description": "Determines whether or not this parameter is required or optional.", + "default": false + }, + "schema": { + "$ref": "#/definitions/schema" + } + }, + "additionalProperties": false + }, + "headerParameterSubSchema": { + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "required": { + "type": "boolean", + "description": "Determines whether or not this parameter is required or optional.", + "default": false + }, + "in": { + "type": "string", + "description": "Determines the location of the parameter.", + "enum": [ + "header" + ] + }, + "description": { + "type": "string", + "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." + }, + "name": { + "type": "string", + "description": "The name of the parameter." + }, + "type": { + "type": "string", + "enum": [ + "string", + "number", + "boolean", + "integer", + "array" + ] + }, + "format": { + "type": "string" + }, + "items": { + "$ref": "#/definitions/primitivesItems" + }, + "collectionFormat": { + "$ref": "#/definitions/collectionFormat" + }, + "default": { + "$ref": "#/definitions/default" + }, + "maximum": { + "$ref": "#/definitions/maximum" + }, + "exclusiveMaximum": { + "$ref": "#/definitions/exclusiveMaximum" + }, + "minimum": { + "$ref": "#/definitions/minimum" + }, + "exclusiveMinimum": { + "$ref": "#/definitions/exclusiveMinimum" + }, + "maxLength": { + "$ref": "#/definitions/maxLength" + }, + "minLength": { + "$ref": "#/definitions/minLength" + }, + "pattern": { + "$ref": "#/definitions/pattern" + }, + "maxItems": { + "$ref": "#/definitions/maxItems" + }, + "minItems": { + "$ref": "#/definitions/minItems" + }, + "uniqueItems": { + "$ref": "#/definitions/uniqueItems" + }, + "enum": { + "$ref": "#/definitions/enum" + }, + "multipleOf": { + "$ref": "#/definitions/multipleOf" + } + } + }, + "queryParameterSubSchema": { + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "required": { + "type": "boolean", + "description": "Determines whether or not this parameter is required or optional.", + "default": false + }, + "in": { + "type": "string", + "description": "Determines the location of the parameter.", + "enum": [ + "query" + ] + }, + "description": { + "type": "string", + "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." + }, + "name": { + "type": "string", + "description": "The name of the parameter." + }, + "allowEmptyValue": { + "type": "boolean", + "default": false, + "description": "allows sending a parameter by name only or with an empty value." + }, + "type": { + "type": "string", + "enum": [ + "string", + "number", + "boolean", + "integer", + "array" + ] + }, + "format": { + "type": "string" + }, + "items": { + "$ref": "#/definitions/primitivesItems" + }, + "collectionFormat": { + "$ref": "#/definitions/collectionFormatWithMulti" + }, + "default": { + "$ref": "#/definitions/default" + }, + "maximum": { + "$ref": "#/definitions/maximum" + }, + "exclusiveMaximum": { + "$ref": "#/definitions/exclusiveMaximum" + }, + "minimum": { + "$ref": "#/definitions/minimum" + }, + "exclusiveMinimum": { + "$ref": "#/definitions/exclusiveMinimum" + }, + "maxLength": { + "$ref": "#/definitions/maxLength" + }, + "minLength": { + "$ref": "#/definitions/minLength" + }, + "pattern": { + "$ref": "#/definitions/pattern" + }, + "maxItems": { + "$ref": "#/definitions/maxItems" + }, + "minItems": { + "$ref": "#/definitions/minItems" + }, + "uniqueItems": { + "$ref": "#/definitions/uniqueItems" + }, + "enum": { + "$ref": "#/definitions/enum" + }, + "multipleOf": { + "$ref": "#/definitions/multipleOf" + } + } + }, + "formDataParameterSubSchema": { + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "required": { + "type": "boolean", + "description": "Determines whether or not this parameter is required or optional.", + "default": false + }, + "in": { + "type": "string", + "description": "Determines the location of the parameter.", + "enum": [ + "formData" + ] + }, + "description": { + "type": "string", + "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." + }, + "name": { + "type": "string", + "description": "The name of the parameter." + }, + "allowEmptyValue": { + "type": "boolean", + "default": false, + "description": "allows sending a parameter by name only or with an empty value." + }, + "type": { + "type": "string", + "enum": [ + "string", + "number", + "boolean", + "integer", + "array", + "file" + ] + }, + "format": { + "type": "string" + }, + "items": { + "$ref": "#/definitions/primitivesItems" + }, + "collectionFormat": { + "$ref": "#/definitions/collectionFormatWithMulti" + }, + "default": { + "$ref": "#/definitions/default" + }, + "maximum": { + "$ref": "#/definitions/maximum" + }, + "exclusiveMaximum": { + "$ref": "#/definitions/exclusiveMaximum" + }, + "minimum": { + "$ref": "#/definitions/minimum" + }, + "exclusiveMinimum": { + "$ref": "#/definitions/exclusiveMinimum" + }, + "maxLength": { + "$ref": "#/definitions/maxLength" + }, + "minLength": { + "$ref": "#/definitions/minLength" + }, + "pattern": { + "$ref": "#/definitions/pattern" + }, + "maxItems": { + "$ref": "#/definitions/maxItems" + }, + "minItems": { + "$ref": "#/definitions/minItems" + }, + "uniqueItems": { + "$ref": "#/definitions/uniqueItems" + }, + "enum": { + "$ref": "#/definitions/enum" + }, + "multipleOf": { + "$ref": "#/definitions/multipleOf" + } + } + }, + "pathParameterSubSchema": { + "additionalProperties": false, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "required": [ + "required" + ], + "properties": { + "required": { + "type": "boolean", + "enum": [ + true + ], + "description": "Determines whether or not this parameter is required or optional." + }, + "in": { + "type": "string", + "description": "Determines the location of the parameter.", + "enum": [ + "path" + ] + }, + "description": { + "type": "string", + "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." + }, + "name": { + "type": "string", + "description": "The name of the parameter." + }, + "type": { + "type": "string", + "enum": [ + "string", + "number", + "boolean", + "integer", + "array" + ] + }, + "format": { + "type": "string" + }, + "items": { + "$ref": "#/definitions/primitivesItems" + }, + "collectionFormat": { + "$ref": "#/definitions/collectionFormat" + }, + "default": { + "$ref": "#/definitions/default" + }, + "maximum": { + "$ref": "#/definitions/maximum" + }, + "exclusiveMaximum": { + "$ref": "#/definitions/exclusiveMaximum" + }, + "minimum": { + "$ref": "#/definitions/minimum" + }, + "exclusiveMinimum": { + "$ref": "#/definitions/exclusiveMinimum" + }, + "maxLength": { + "$ref": "#/definitions/maxLength" + }, + "minLength": { + "$ref": "#/definitions/minLength" + }, + "pattern": { + "$ref": "#/definitions/pattern" + }, + "maxItems": { + "$ref": "#/definitions/maxItems" + }, + "minItems": { + "$ref": "#/definitions/minItems" + }, + "uniqueItems": { + "$ref": "#/definitions/uniqueItems" + }, + "enum": { + "$ref": "#/definitions/enum" + }, + "multipleOf": { + "$ref": "#/definitions/multipleOf" + } + } + }, + "nonBodyParameter": { + "type": "object", + "required": [ + "name", + "in", + "type" + ], + "oneOf": [ + { + "$ref": "#/definitions/headerParameterSubSchema" + }, + { + "$ref": "#/definitions/formDataParameterSubSchema" + }, + { + "$ref": "#/definitions/queryParameterSubSchema" + }, + { + "$ref": "#/definitions/pathParameterSubSchema" + } + ] + }, + "parameter": { + "oneOf": [ + { + "$ref": "#/definitions/bodyParameter" + }, + { + "$ref": "#/definitions/nonBodyParameter" + } + ] + }, + "schema": { + "type": "object", + "description": "A deterministic version of a JSON Schema object.", + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "properties": { + "$ref": { + "type": "string" + }, + "format": { + "type": "string" + }, + "title": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/title" + }, + "description": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/description" + }, + "default": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/default" + }, + "multipleOf": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/multipleOf" + }, + "maximum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/maximum" + }, + "exclusiveMaximum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum" + }, + "minimum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/minimum" + }, + "exclusiveMinimum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum" + }, + "maxLength": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + }, + "minLength": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + }, + "pattern": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/pattern" + }, + "maxItems": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + }, + "minItems": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + }, + "uniqueItems": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/uniqueItems" + }, + "maxProperties": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + }, + "minProperties": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + }, + "required": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/stringArray" + }, + "enum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/enum" + }, + "additionalProperties": { + "anyOf": [ + { + "$ref": "#/definitions/schema" + }, + { + "type": "boolean" + } + ], + "default": {} + }, + "type": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/type" + }, + "items": { + "anyOf": [ + { + "$ref": "#/definitions/schema" + }, + { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/schema" + } + } + ], + "default": {} + }, + "allOf": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/schema" + } + }, + "properties": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/schema" + }, + "default": {} + }, + "discriminator": { + "type": "string" + }, + "readOnly": { + "type": "boolean", + "default": false + }, + "xml": { + "$ref": "#/definitions/xml" + }, + "externalDocs": { + "$ref": "#/definitions/externalDocs" + }, + "example": {} + }, + "additionalProperties": false + }, + "fileSchema": { + "type": "object", + "description": "A deterministic version of a JSON Schema object.", + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + }, + "required": [ + "type" + ], + "properties": { + "format": { + "type": "string" + }, + "title": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/title" + }, + "description": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/description" + }, + "default": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/default" + }, + "required": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/stringArray" + }, + "type": { + "type": "string", + "enum": [ + "file" + ] + }, + "readOnly": { + "type": "boolean", + "default": false + }, + "externalDocs": { + "$ref": "#/definitions/externalDocs" + }, + "example": {} + }, + "additionalProperties": false + }, + "primitivesItems": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "string", + "number", + "integer", + "boolean", + "array" + ] + }, + "format": { + "type": "string" + }, + "items": { + "$ref": "#/definitions/primitivesItems" + }, + "collectionFormat": { + "$ref": "#/definitions/collectionFormat" + }, + "default": { + "$ref": "#/definitions/default" + }, + "maximum": { + "$ref": "#/definitions/maximum" + }, + "exclusiveMaximum": { + "$ref": "#/definitions/exclusiveMaximum" + }, + "minimum": { + "$ref": "#/definitions/minimum" + }, + "exclusiveMinimum": { + "$ref": "#/definitions/exclusiveMinimum" + }, + "maxLength": { + "$ref": "#/definitions/maxLength" + }, + "minLength": { + "$ref": "#/definitions/minLength" + }, + "pattern": { + "$ref": "#/definitions/pattern" + }, + "maxItems": { + "$ref": "#/definitions/maxItems" + }, + "minItems": { + "$ref": "#/definitions/minItems" + }, + "uniqueItems": { + "$ref": "#/definitions/uniqueItems" + }, + "enum": { + "$ref": "#/definitions/enum" + }, + "multipleOf": { + "$ref": "#/definitions/multipleOf" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "security": { + "type": "array", + "items": { + "$ref": "#/definitions/securityRequirement" + }, + "uniqueItems": true + }, + "securityRequirement": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } + }, + "xml": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "prefix": { + "type": "string" + }, + "attribute": { + "type": "boolean", + "default": false + }, + "wrapped": { + "type": "boolean", + "default": false + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "tag": { + "type": "object", + "additionalProperties": false, + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/definitions/externalDocs" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "securityDefinitions": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "$ref": "#/definitions/basicAuthenticationSecurity" + }, + { + "$ref": "#/definitions/apiKeySecurity" + }, + { + "$ref": "#/definitions/oauth2ImplicitSecurity" + }, + { + "$ref": "#/definitions/oauth2PasswordSecurity" + }, + { + "$ref": "#/definitions/oauth2ApplicationSecurity" + }, + { + "$ref": "#/definitions/oauth2AccessCodeSecurity" + } + ] + } + }, + "basicAuthenticationSecurity": { + "type": "object", + "additionalProperties": false, + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "basic" + ] + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "apiKeySecurity": { + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "name", + "in" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "apiKey" + ] + }, + "name": { + "type": "string" + }, + "in": { + "type": "string", + "enum": [ + "header", + "query" + ] + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "oauth2ImplicitSecurity": { + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "flow", + "authorizationUrl" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "oauth2" + ] + }, + "flow": { + "type": "string", + "enum": [ + "implicit" + ] + }, + "scopes": { + "$ref": "#/definitions/oauth2Scopes" + }, + "authorizationUrl": { + "type": "string", + "format": "uri" + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "oauth2PasswordSecurity": { + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "flow", + "tokenUrl" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "oauth2" + ] + }, + "flow": { + "type": "string", + "enum": [ + "password" + ] + }, + "scopes": { + "$ref": "#/definitions/oauth2Scopes" + }, + "tokenUrl": { + "type": "string", + "format": "uri" + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "oauth2ApplicationSecurity": { + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "flow", + "tokenUrl" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "oauth2" + ] + }, + "flow": { + "type": "string", + "enum": [ + "application" + ] + }, + "scopes": { + "$ref": "#/definitions/oauth2Scopes" + }, + "tokenUrl": { + "type": "string", + "format": "uri" + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "oauth2AccessCodeSecurity": { + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "flow", + "authorizationUrl", + "tokenUrl" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "oauth2" + ] + }, + "flow": { + "type": "string", + "enum": [ + "accessCode" + ] + }, + "scopes": { + "$ref": "#/definitions/oauth2Scopes" + }, + "authorizationUrl": { + "type": "string", + "format": "uri" + }, + "tokenUrl": { + "type": "string", + "format": "uri" + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-": { + "$ref": "#/definitions/vendorExtension" + } + } + }, + "oauth2Scopes": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "mediaTypeList": { + "type": "array", + "items": { + "$ref": "#/definitions/mimeType" + }, + "uniqueItems": true + }, + "parametersList": { + "type": "array", + "description": "The parameters needed to send a valid API call.", + "additionalItems": false, + "items": { + "oneOf": [ + { + "$ref": "#/definitions/parameter" + }, + { + "$ref": "#/definitions/jsonReference" + } + ] + }, + "uniqueItems": true + }, + "schemesList": { + "type": "array", + "description": "The transfer protocol of the API.", + "items": { + "type": "string", + "enum": [ + "http", + "https", + "ws", + "wss" + ] + }, + "uniqueItems": true + }, + "collectionFormat": { + "type": "string", + "enum": [ + "csv", + "ssv", + "tsv", + "pipes" + ], + "default": "csv" + }, + "collectionFormatWithMulti": { + "type": "string", + "enum": [ + "csv", + "ssv", + "tsv", + "pipes", + "multi" + ], + "default": "csv" + }, + "title": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/title" + }, + "description": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/description" + }, + "default": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/default" + }, + "multipleOf": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/multipleOf" + }, + "maximum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/maximum" + }, + "exclusiveMaximum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum" + }, + "minimum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/minimum" + }, + "exclusiveMinimum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum" + }, + "maxLength": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + }, + "minLength": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + }, + "pattern": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/pattern" + }, + "maxItems": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + }, + "minItems": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + }, + "uniqueItems": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/uniqueItems" + }, + "enum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/enum" + }, + "jsonReference": { + "type": "object", + "required": [ + "$ref" + ], + "additionalProperties": false, + "properties": { + "$ref": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/spec_info.go b/vendor/github.com/pb33f/libopenapi/datamodel/spec_info.go new file mode 100644 index 000000000..caec7284c --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/spec_info.go @@ -0,0 +1,335 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package datamodel + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "strings" + "time" + + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +const ( + JSONFileType = "json" + YAMLFileType = "yaml" +) + +// SpecInfo represents a 'ready-to-process' OpenAPI Document. The RootNode is the most important property +// used by the library, this contains the top of the document tree that every single low model is based off. +type SpecInfo struct { + SpecType string `json:"type"` + NumLines int `json:"numLines"` + Version string `json:"version"` + VersionNumeric float32 `json:"versionNumeric"` + SpecFormat string `json:"format"` + SpecFileType string `json:"fileType"` + SpecBytes *[]byte `json:"bytes"` // the original byte array + RootNode *yaml.Node `json:"-"` // reference to the root node of the spec. + SpecJSONBytes *[]byte `json:"-"` // original bytes converted to JSON + SpecJSON *map[string]interface{} `json:"-"` // standard JSON map of original bytes + Error error `json:"-"` // something go wrong? + APISchema string `json:"-"` // API Schema for supplied spec type (2 or 3) + Generated time.Time `json:"-"` + OriginalIndentation int `json:"-"` // the original whitespace + Self string `json:"-"` // the $self field for OpenAPI 3.2+ documents (base URI) +} + +func ExtractSpecInfoWithConfig(spec []byte, config *DocumentConfiguration) (*SpecInfo, error) { + return ExtractSpecInfoWithDocumentCheck(spec, config.BypassDocumentCheck) +} + +// ExtractSpecInfoWithDocumentCheckSync accepts an OpenAPI/Swagger specification that has been read into a byte array +// and will return a SpecInfo pointer, which contains details on the version and an un-marshaled +// deprecated: use ExtractSpecInfoWithDocumentCheck instead, this function will be removed in a later version. +func ExtractSpecInfoWithDocumentCheckSync(spec []byte, bypass bool) (*SpecInfo, error) { + i, err := ExtractSpecInfoWithDocumentCheck(spec, bypass) + if err != nil { + return nil, err + } + return i, nil +} + +// ExtractSpecInfoWithDocumentCheck accepts an OpenAPI/Swagger specification that has been read into a byte array +// and will return a SpecInfo pointer, which contains details on the version and an un-marshaled +// ensures the document is an OpenAPI document. +func ExtractSpecInfoWithDocumentCheck(spec []byte, bypass bool) (*SpecInfo, error) { + var parsedSpec yaml.Node + + specInfo := &SpecInfo{} + + // set original bytes + specInfo.SpecBytes = &spec + + stringSpec := string(spec) + runes := []rune(strings.TrimSpace(stringSpec)) + if len(runes) <= 0 { + return specInfo, errors.New("there is nothing in the spec, it's empty - so there is nothing to be done") + } + + if runes[0] == '{' && runes[len(runes)-1] == '}' { + specInfo.SpecFileType = JSONFileType + } else { + specInfo.SpecFileType = YAMLFileType + } + + specInfo.NumLines = strings.Count(stringSpec, "\n") + 1 + + // Pre-process JSON to handle \/ escape sequences that YAML parser doesn't recognize. + // JSON (RFC 8259) allows \/ as an optional escape for forward slash, but YAML does not. + // See: https://github.com/pb33f/libopenapi/issues/479 + parseBytes := spec + if specInfo.SpecFileType == JSONFileType { + parseBytes = unescapeJSONSlashes(spec) + } + + err := yaml.Unmarshal(parseBytes, &parsedSpec) + if err != nil { + if !bypass { + return nil, fmt.Errorf("unable to parse specification: %s", err.Error()) + } + + // read the file into a simulated document node. + // we can't parse it, so create a fake document node with a single string content + parsedSpec = yaml.Node{ + Kind: yaml.DocumentNode, + Content: []*yaml.Node{ + { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: string(spec), + }, + }, + } + } + + specInfo.RootNode = &parsedSpec + + _, openAPI3 := utils.FindKeyNode(utils.OpenApi3, parsedSpec.Content) + _, openAPI2 := utils.FindKeyNode(utils.OpenApi2, parsedSpec.Content) + _, asyncAPI := utils.FindKeyNode(utils.AsyncApi, parsedSpec.Content) + + parseJSON := func(bytes []byte, spec *SpecInfo, parsedNode *yaml.Node) error { + var jsonSpec map[string]interface{} + if utils.IsYAML(string(bytes)) { + // Decode YAML to map - this is critical to catch structural errors like duplicate keys + if err := parsedNode.Decode(&jsonSpec); err != nil { + return fmt.Errorf("failed to decode YAML to JSON: %w", err) + } + // Marshal to JSON - if this fails due to unsupported types (e.g. map[interface{}]interface{}), + // we tolerate it as it doesn't indicate spec invalidity, just YAML/JSON incompatibility + b, err := json.Marshal(&jsonSpec) + if err == nil { + spec.SpecJSONBytes = &b + } + spec.SpecJSON = &jsonSpec + } else { + if err := json.Unmarshal(bytes, &jsonSpec); err != nil { + return fmt.Errorf("failed to unmarshal JSON: %w", err) + } + spec.SpecJSONBytes = &bytes + spec.SpecJSON = &jsonSpec + } + return nil + } + + // if !bypass { + // check for specific keys + parsed := false + if openAPI3 != nil { + version, majorVersion, versionError := parseVersionTypeData(openAPI3.Value) + if versionError != nil { + if !bypass { + return nil, versionError + } + } + + specInfo.SpecType = utils.OpenApi3 + specInfo.Version = version + specInfo.SpecFormat = OAS3 + + // Extract the prefix version + prefixVersion := specInfo.Version + if len(specInfo.Version) >= 3 { + prefixVersion = specInfo.Version[:3] + } + switch prefixVersion { + case "3.1": + specInfo.VersionNumeric = 3.1 + specInfo.APISchema = OpenAPI31SchemaData + specInfo.SpecFormat = OAS31 + // extract $self field for OpenAPI 3.1+ (might be used as forward-compatible feature) + _, selfNode := utils.FindKeyNode("$self", parsedSpec.Content) + if selfNode != nil && selfNode.Value != "" { + specInfo.Self = selfNode.Value + } + case "3.2": + specInfo.VersionNumeric = 3.2 + specInfo.APISchema = OpenAPI32SchemaData + specInfo.SpecFormat = OAS32 + // extract $self field for OpenAPI 3.2+ + _, selfNode := utils.FindKeyNode("$self", parsedSpec.Content) + if selfNode != nil && selfNode.Value != "" { + specInfo.Self = selfNode.Value + } + default: + specInfo.VersionNumeric = 3.0 + specInfo.APISchema = OpenAPI3SchemaData + } + + // parse JSON + if err := parseJSON(spec, specInfo, &parsedSpec); err != nil && !bypass { + return nil, err + } + parsed = true + + // double check for the right version, people mix this up. + if majorVersion < 3 { + if !bypass { + specInfo.Error = errors.New("spec is defined as an openapi spec, but is using a swagger (2.0), or unknown version") + return specInfo, specInfo.Error + } + } + } + + if openAPI2 != nil { + version, majorVersion, versionError := parseVersionTypeData(openAPI2.Value) + if versionError != nil { + if !bypass { + return nil, versionError + } + } + + specInfo.SpecType = utils.OpenApi2 + specInfo.Version = version + specInfo.SpecFormat = OAS2 + specInfo.VersionNumeric = 2.0 + specInfo.APISchema = OpenAPI2SchemaData + + // parse JSON + if err := parseJSON(spec, specInfo, &parsedSpec); err != nil && !bypass { + return nil, err + } + parsed = true + + // I am not certain this edge-case is very frequent, but let's make sure we handle it anyway. + if majorVersion > 2 { + if !bypass { + specInfo.Error = errors.New("spec is defined as a swagger (openapi 2.0) spec, but is an openapi 3 or unknown version") + return specInfo, specInfo.Error + } + } + } + if asyncAPI != nil { + version, majorVersion, versionErr := parseVersionTypeData(asyncAPI.Value) + if versionErr != nil { + if !bypass { + return nil, versionErr + } + } + + specInfo.SpecType = utils.AsyncApi + specInfo.Version = version + // TODO: format for AsyncAPI. + + // parse JSON + if err := parseJSON(spec, specInfo, &parsedSpec); err != nil && !bypass { + return nil, err + } + parsed = true + + // so far there is only 2 as a major release of AsyncAPI + if majorVersion > 2 { + if !bypass { + specInfo.Error = errors.New("spec is defined as asyncapi, but has a major version that is invalid") + return specInfo, specInfo.Error + } + } + } + + if specInfo.SpecType == "" { + // parse JSON + if !bypass { + if err := parseJSON(spec, specInfo, &parsedSpec); err != nil { + return nil, err + } + specInfo.Error = errors.New("spec type not supported by libopenapi, sorry") + return specInfo, specInfo.Error + } + } + //} else { + // // parse JSON + // parseJSON(spec, specInfo, &parsedSpec) + //} + + if !parsed { + if err := parseJSON(spec, specInfo, &parsedSpec); err != nil && !bypass { + return nil, err + } + } + + // detect the original whitespace indentation + specInfo.OriginalIndentation = utils.DetermineWhitespaceLength(string(spec)) + + return specInfo, nil +} + +// ExtractSpecInfo accepts an OpenAPI/Swagger specification that has been read into a byte array +// and will return a SpecInfo pointer, which contains details on the version and an un-marshaled +// *yaml.Node root node tree. The root node tree is what's used by the library when building out models. +// +// If the spec cannot be parsed correctly then an error will be returned, otherwise the error is nil. +func ExtractSpecInfo(spec []byte) (*SpecInfo, error) { + return ExtractSpecInfoWithDocumentCheck(spec, false) +} + +// extract version number from specification +func parseVersionTypeData(d interface{}) (string, int, error) { + r := []rune(strings.TrimSpace(fmt.Sprintf("%v", d))) + if len(r) <= 0 { + return "", 0, fmt.Errorf("unable to extract version from: %v", d) + } + return string(r), int(r[0]) - '0', nil +} + +// unescapeJSONSlashes replaces the optional \/ escape sequence in JSON with / +// JSON (RFC 8259) allows \/ as an optional escape for forward slash, but YAML +// parsers (including go.yaml.in/yaml/v4) do not recognize it. +// This handles escaped backslashes correctly: \\/ becomes \/ not // +// Returns the original slice if no transformation is needed (zero allocation). +func unescapeJSONSlashes(jsonBytes []byte) []byte { + // fast path: check if transformation is needed + if !bytes.Contains(jsonBytes, []byte(`\/`)) { + return jsonBytes + } + + result := make([]byte, 0, len(jsonBytes)) + i := 0 + for i < len(jsonBytes) { + if jsonBytes[i] == '\\' && i+1 < len(jsonBytes) { + switch jsonBytes[i+1] { + case '/': + // \/ -> / (json optional escape for solidus) + result = append(result, '/') + i += 2 + case '\\': + // preserve escaped backslash to prevent \\/ becoming // + result = append(result, '\\', '\\') + i += 2 + default: + // preserve other escape sequences (\n, \t, \", etc.) + result = append(result, jsonBytes[i]) + i++ + } + } else { + result = append(result, jsonBytes[i]) + i++ + } + } + return result +} diff --git a/vendor/github.com/pb33f/libopenapi/datamodel/translate.go b/vendor/github.com/pb33f/libopenapi/datamodel/translate.go new file mode 100644 index 000000000..ceb55cae8 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/datamodel/translate.go @@ -0,0 +1,309 @@ +package datamodel + +import ( + "context" + "errors" + "io" + "runtime" + "sync" + + "github.com/pb33f/libopenapi/orderedmap" +) + +type ( + ActionFunc[T any] func(T) error + TranslateFunc[IN any, OUT any] func(IN) (OUT, error) + TranslateSliceFunc[IN any, OUT any] func(int, IN) (OUT, error) + TranslateMapFunc[IN any, OUT any] func(IN) (OUT, error) + ResultFunc[V any] func(V) error +) + +type continueError struct { + error +} + +var Continue = &continueError{error: errors.New("Continue")} + +type jobStatus[OUT any] struct { + done chan struct{} + cont bool + result OUT +} + +type pipelineJobStatus[IN any, OUT any] struct { + done chan struct{} + cont bool + input IN + result OUT +} + +// TranslateSliceParallel iterates a slice in parallel and calls translate() +// asynchronously. +// translate() may return `datamodel.Continue` to continue iteration. +// translate() or result() may return `io.EOF` to break iteration. +// Results are provided sequentially to result() in stable order from slice. +func TranslateSliceParallel[IN any, OUT any](in []IN, translate TranslateSliceFunc[IN, OUT], result ActionFunc[OUT]) error { + if in == nil { + return nil + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + concurrency := runtime.NumCPU() + jobChan := make(chan *jobStatus[OUT], concurrency) + var reterr error + var mu sync.Mutex + var wg sync.WaitGroup + wg.Add(1) // input goroutine. + + // Fan out translate jobs. + go func() { + defer func() { + close(jobChan) + wg.Done() + }() + for idx, valueIn := range in { + j := &jobStatus[OUT]{ + done: make(chan struct{}), + } + select { + case jobChan <- j: + case <-ctx.Done(): + return + } + + wg.Add(1) + go func(idx int, valueIn IN) { + valueOut, err := translate(idx, valueIn) + if err == Continue { + j.cont = true + } else if err != nil { + mu.Lock() + if reterr == nil { + reterr = err + } + mu.Unlock() + cancel() + wg.Done() + return + } + j.result = valueOut + close(j.done) + wg.Done() + }(idx, valueIn) + } + }() + + // Iterate jobChan as jobs complete. +JOBLOOP: + for j := range jobChan { + select { + case <-j.done: + if j.cont || result == nil { + break + } + err := result(j.result) + if err != nil { + cancel() + wg.Wait() + if err == io.EOF { + return nil + } + return err + } + case <-ctx.Done(): + break JOBLOOP + } + } + + wg.Wait() + if reterr == io.EOF { + return nil + } + return reterr +} + +// TranslateMapParallel iterates a `*orderedmap.Map` in parallel and calls translate() +// asynchronously. +// translate() or result() may return `io.EOF` to break iteration. +// Safely handles nil pointer. +// Results are provided sequentially to result() in stable order from `*orderedmap.Map`. +func TranslateMapParallel[K comparable, V any, RV any](m *orderedmap.Map[K, V], translate TranslateFunc[orderedmap.Pair[K, V], RV], result ResultFunc[RV]) error { + if m == nil { + return nil + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + concurrency := runtime.NumCPU() + c := orderedmap.Iterate(ctx, m) + jobChan := make(chan *jobStatus[RV], concurrency) + var reterr error + var wg sync.WaitGroup + var mu sync.Mutex + + // Fan out translate jobs. + wg.Add(1) + go func() { + defer func() { + close(jobChan) + wg.Done() + }() + for pair := range c { + j := &jobStatus[RV]{ + done: make(chan struct{}), + } + select { + case jobChan <- j: + case <-ctx.Done(): + return + } + + wg.Add(1) + go func(pair orderedmap.Pair[K, V]) { + value, err := translate(pair) + if err != nil { + mu.Lock() + defer func() { + mu.Unlock() + wg.Done() + cancel() + }() + if reterr == nil { + reterr = err + } + return + } + j.result = value + close(j.done) + wg.Done() + }(pair) + } + }() + + // Iterate jobChan as jobs complete. + defer wg.Wait() +JOBLOOP: + for j := range jobChan { + select { + case <-j.done: + err := result(j.result) + if err != nil { + cancel() + if err == io.EOF { + return nil + } + return err + } + case <-ctx.Done(): + break JOBLOOP + } + } + + if reterr == io.EOF { + return nil + } + return reterr +} + +// TranslatePipeline processes input sequentially through predicate(), sends to +// translate() in parallel, then outputs in stable order. +// translate() may return `datamodel.Continue` to continue iteration. +// Caller must close `in` channel to indicate EOF. +// TranslatePipeline closes `out` channel to indicate EOF. +func TranslatePipeline[IN any, OUT any](in <-chan IN, out chan<- OUT, translate TranslateFunc[IN, OUT]) error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + concurrency := runtime.NumCPU() + workChan := make(chan *pipelineJobStatus[IN, OUT]) + resultChan := make(chan *pipelineJobStatus[IN, OUT]) + var reterr error + var mu sync.Mutex + var wg sync.WaitGroup + defer wg.Wait() + wg.Add(1) // input goroutine. + + // Launch worker pool. + for i := 0; i < concurrency; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for { + select { + case j, ok := <-workChan: + if !ok { + return + } + result, err := translate(j.input) + if err == Continue { + j.cont = true + close(j.done) + continue + } + if err != nil { + mu.Lock() + defer mu.Unlock() + if reterr == nil { + reterr = err + } + cancel() + return + } + j.result = result + close(j.done) + case <-ctx.Done(): + return + } + } + }() + } + + // Iterate input, send to workers. + go func() { + defer func() { + close(workChan) + close(resultChan) + wg.Done() + }() + for { + select { + case value, ok := <-in: + if !ok { + return + } + j := &pipelineJobStatus[IN, OUT]{ + done: make(chan struct{}), + input: value, + } + select { + case workChan <- j: + case <-ctx.Done(): + return + } + select { + case resultChan <- j: + case <-ctx.Done(): + return + } + case <-ctx.Done(): + return + } + } + }() + + // Collect results in stable order, send to output channel. + defer close(out) + for j := range resultChan { + select { + case <-j.done: + if j.cont { + continue + } + out <- j.result + case <-ctx.Done(): + return reterr + } + } + + return reterr +} diff --git a/vendor/github.com/pb33f/libopenapi/document.go b/vendor/github.com/pb33f/libopenapi/document.go new file mode 100644 index 000000000..7d0b537ef --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/document.go @@ -0,0 +1,387 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +// Package libopenapi is a library containing tools for reading and in and manipulating Swagger (OpenAPI 2) and OpenAPI 3+ +// specifications into strongly typed documents. These documents have two APIs, a high level (porcelain) and a +// low level (plumbing). +// +// Every single type has a 'GoLow()' method that drops down from the high API to the low API. Once in the low API, +// the entire original document data is available, including all comments, line and column numbers for keys and values. +// +// There are two steps to creating a using Document. First, create a new Document using the NewDocument() method +// and pass in a specification []byte array that contains the OpenAPI Specification. It doesn't matter if YAML or JSON +// are used. +package libopenapi + +import ( + "errors" + "fmt" + + lowbase "github.com/pb33f/libopenapi/datamodel/low/base" + + "github.com/pb33f/libopenapi/index" + + "github.com/pb33f/libopenapi/datamodel" + v2high "github.com/pb33f/libopenapi/datamodel/high/v2" + v3high "github.com/pb33f/libopenapi/datamodel/high/v3" + v2low "github.com/pb33f/libopenapi/datamodel/low/v2" + v3low "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/utils" + what_changed "github.com/pb33f/libopenapi/what-changed" + "github.com/pb33f/libopenapi/what-changed/model" + "go.yaml.in/yaml/v4" +) + +// Document Represents an OpenAPI specification that can then be rendered into a model or serialized back into +// a string document after being manipulated. +type Document interface { + // GetVersion will return the exact version of the OpenAPI specification set for the document. + GetVersion() string + + // GetRolodex will return the Rolodex instance that was used to load the document. + GetRolodex() *index.Rolodex + + // GetSpecInfo will return the *datamodel.SpecInfo instance that contains all specification information. + GetSpecInfo() *datamodel.SpecInfo + + // SetConfiguration will set the configuration for the document. This allows for finer grained control over + // allowing remote or local references, as well as a BaseURL to allow for relative file references. + SetConfiguration(configuration *datamodel.DocumentConfiguration) + + // GetConfiguration will return the configuration for the document. This allows for finer grained control over + // allowing remote or local references, as well as a BaseURL to allow for relative file references. + GetConfiguration() *datamodel.DocumentConfiguration + + // BuildV2Model will build out a Swagger (version 2) model from the specification used to create the document + // If there are any issues, then no model will be returned, instead a slice of errors will explain all the + // problems that occurred. This method will only support version 2 specifications and will throw an error for + // any other types. + BuildV2Model() (*DocumentModel[v2high.Swagger], error) + + // BuildV3Model will build out an OpenAPI (version 3+) model from the specification used to create the document + // If there are any issues, then no model will be returned, instead a slice of errors will explain all the + // problems that occurred. This method will only support version 3 specifications and will throw an error for + // any other types. + BuildV3Model() (*DocumentModel[v3high.Document], error) + + // RenderAndReload will render the high level model as it currently exists (including any mutations, additions + // and removals to and from any object in the tree). It will then reload the low level model with the new bytes + // extracted from the model that was re-rendered. This is useful if you want to make changes to the high level model + // and then 'reload' the model into memory, so that line numbers and column numbers are correct and all update + // according to the changes made. + // + // The method returns the raw YAML bytes that were rendered, and any errors that occurred during rebuilding of the model. + // This is a destructive operation, and will re-build the entire model from scratch using the new bytes, so any + // references to the old model will be lost. The second return is the new Document that was created, and the third + // return is any errors hit trying to re-render. + // + // **IMPORTANT** This method only supports OpenAPI Documents. The Swagger model will not support mutations correctly + // and will not update when called. This choice has been made because we don't want to continue supporting Swagger, + // it's too old, so it should be motivation to upgrade to OpenAPI 3. + RenderAndReload() ([]byte, Document, *DocumentModel[v3high.Document], error) + + // Render will render the high level model as it currently exists (including any mutations, additions + // and removals to and from any object in the tree). Unlike RenderAndReload, Render will simply print the state + // of the model as it currently exists, and will not re-load the model into memory. It means that the low-level and + // the high-level models will be out of sync, and the index will only be useful for the original document. + // + // Why use this instead of RenderAndReload? + // + // The simple answer is that RenderAndReload is a destructive operation, and will re-build the entire model from + // scratch using the new bytes, which is desirable if you want to make changes to the high level model and then + // 'reload' the model into memory, so that line numbers and column numbers are correct and the index is accurate. + // However, if you don't care about the low-level model, and you're not using the index, and you just want to + // print the state of the model as it currently exists, then Render() is the method to use. + // **IMPORTANT** This method only supports OpenAPI Documents. + Render() ([]byte, error) + + // Serialize will re-render a Document back into a []byte slice. If any modifications have been made to the + // underlying data model using low level APIs, then those changes will be reflected in the serialized output. + // + // It's important to know that this should not be used if the resolver has been used on a specification to + // for anything other than checking for circular references. If the resolver is used to resolve the spec, then this + // method may spin out forever if the specification backing the model has circular references. + // Deprecated: This method is deprecated and will be removed in a future release. Use RenderAndReload() instead. + // This method does not support mutations correctly. + Serialize() ([]byte, error) +} + +type document struct { + rolodex *index.Rolodex + version string + info *datamodel.SpecInfo + config *datamodel.DocumentConfiguration + highOpenAPI3Model *DocumentModel[v3high.Document] + highSwaggerModel *DocumentModel[v2high.Swagger] +} + +// DocumentModel represents either a Swagger document (version 2) or an OpenAPI document (version 3) that is +// built from a parent Document. +type DocumentModel[T v2high.Swagger | v3high.Document] struct { + Model T + Index *index.SpecIndex // index created from the document. +} + +// NewDocument will create a new OpenAPI instance from an OpenAPI specification []byte array. If anything goes +// wrong when parsing, reading or processing the OpenAPI specification, there will be no document returned, instead +// a slice of errors will be returned that explain everything that failed. +// +// After creating a Document, the option to build a model becomes available, in either V2 or V3 flavors. The models +// are about 70% different between Swagger and OpenAPI 3, which is why two different models are available. +// +// This function will NOT automatically follow (meaning load) any file or remote references that are found. +// +// If this isn't the behavior you want, then you can use the NewDocumentWithConfiguration() function instead, which allows you to set a configuration that +// will allow you to control if file or remote references are allowed. In particular the `AllowFileReferences` and `FollowRemoteReferences` +// properties. +func NewDocument(specByteArray []byte) (Document, error) { + return NewDocumentWithTypeCheck(specByteArray, false) +} + +func NewDocumentWithTypeCheck(specByteArray []byte, bypassCheck bool) (Document, error) { + info, err := datamodel.ExtractSpecInfoWithDocumentCheck(specByteArray, bypassCheck) + if err != nil { + return nil, err + } + d := new(document) + d.version = info.Version + d.info = info + return d, nil +} + +// NewDocumentWithConfiguration is the same as NewDocument, except it's a convenience function that calls NewDocument +// under the hood and then calls SetConfiguration() on the returned Document. +func NewDocumentWithConfiguration(specByteArray []byte, configuration *datamodel.DocumentConfiguration) (Document, error) { + var d Document + var err error + if configuration != nil && configuration.BypassDocumentCheck { + d, err = NewDocumentWithTypeCheck(specByteArray, true) + } else { + d, err = NewDocument(specByteArray) + } + + if d != nil { + d.SetConfiguration(configuration) + } + return d, err +} + +func (d *document) GetRolodex() *index.Rolodex { + return d.rolodex +} + +func (d *document) GetVersion() string { + return d.version +} + +func (d *document) GetSpecInfo() *datamodel.SpecInfo { + return d.info +} + +func (d *document) GetConfiguration() *datamodel.DocumentConfiguration { + return d.config +} + +func (d *document) SetConfiguration(configuration *datamodel.DocumentConfiguration) { + d.config = configuration +} + +func (d *document) Serialize() ([]byte, error) { + if d.info == nil { + return nil, fmt.Errorf("unable to serialize, document has not yet been initialized") + } + if d.info.SpecFileType == datamodel.YAMLFileType { + return yaml.Marshal(d.info.RootNode) + } else { + yamlData, _ := yaml.Marshal(d.info.RootNode) + return utils.ConvertYAMLtoJSON(yamlData) + } +} + +func (d *document) RenderAndReload() ([]byte, Document, *DocumentModel[v3high.Document], error) { + newBytes, rerr := d.Render() + if rerr != nil { + return nil, nil, nil, rerr + } + + newDoc, err := NewDocumentWithConfiguration(newBytes, d.config) + if err != nil { + return nil, nil, nil, err + } + + // build the model. + m, buildErrs := newDoc.BuildV3Model() + if buildErrs != nil { + return newBytes, newDoc, m, buildErrs + } + // this document is now dead, long live the new document! + return newBytes, newDoc, m, nil +} + +func (d *document) Render() ([]byte, error) { + if d.highOpenAPI3Model == nil { + // check for Swagger model first, to give a more helpful error message. + if d.highSwaggerModel != nil { + return nil, errors.New("this method only supports OpenAPI 3 documents, not Swagger") + } + return nil, errors.New("unable to render, no openapi model has been built for the document") + } + if d.info == nil { + return nil, errors.New("unable to render, no specification has been loaded") + } + + var newBytes []byte + var jsonErr error + if d.info.SpecFileType == datamodel.JSONFileType { + jsonIndent := " " + i := d.info.OriginalIndentation + if i > 2 { + for l := 0; l < i-2; l++ { + jsonIndent += " " + } + } + newBytes, jsonErr = d.highOpenAPI3Model.Model.RenderJSON(jsonIndent) + } + if d.info.SpecFileType == datamodel.YAMLFileType { + newBytes = d.highOpenAPI3Model.Model.RenderWithIndention(d.info.OriginalIndentation) + } + return newBytes, jsonErr +} + +func (d *document) BuildV2Model() (*DocumentModel[v2high.Swagger], error) { + if d.highSwaggerModel != nil { + return d.highSwaggerModel, nil + } + var errs []error + if d.info == nil { + return nil, fmt.Errorf("unable to build swagger document, no specification has been loaded") + } + if d.info.SpecFormat != datamodel.OAS2 { + return nil, fmt.Errorf("unable to build swagger document, "+ + "supplied spec is a different version (%v). Try 'BuildV3Model()'", d.info.SpecFormat) + } + + var lowDoc *v2low.Swagger + if d.config == nil { + d.config = datamodel.NewDocumentConfiguration() + } + + var docErr error + lowDoc, docErr = v2low.CreateDocumentFromConfig(d.info, d.config) + d.rolodex = lowDoc.Rolodex + + if docErr != nil { + errs = append(errs, utils.UnwrapErrors(docErr)...) + } + + // Do not short-circuit on circular reference errors, so the client + // has the option of ignoring them. + for _, err := range errs { + var refErr *index.ResolvingError + if errors.As(err, &refErr) { + if refErr.CircularReference == nil { + return nil, errors.Join(errs...) + } + } + } + highDoc := v2high.NewSwaggerDocument(lowDoc) + + d.highSwaggerModel = &DocumentModel[v2high.Swagger]{ + Model: *highDoc, + Index: lowDoc.Index, + } + lowbase.SchemaQuickHashMap.Clear() + return d.highSwaggerModel, errors.Join(errs...) +} + +func (d *document) BuildV3Model() (*DocumentModel[v3high.Document], error) { + if d.highOpenAPI3Model != nil { + return d.highOpenAPI3Model, nil + } + var errs []error + if d.info == nil { + return nil, fmt.Errorf("unable to build document, no specification has been loaded") + } + if d.info.SpecFormat != datamodel.OAS3 && d.info.SpecFormat != datamodel.OAS31 && d.info.SpecFormat != datamodel.OAS32 { + return nil, fmt.Errorf("unable to build openapi document, "+ + "supplied spec is a different version (%v). Try 'BuildV2Model()'", d.info.SpecFormat) + } + + var lowDoc *v3low.Document + if d.config == nil { + d.config = &datamodel.DocumentConfiguration{ + AllowFileReferences: false, + BasePath: "", + AllowRemoteReferences: false, + BaseURL: nil, + } + } + + var docErr error + lowDoc, docErr = v3low.CreateDocumentFromConfig(d.info, d.config) + d.rolodex = lowDoc.Rolodex + + if docErr != nil { + errs = append(errs, utils.UnwrapErrors(docErr)...) + } + + // Do not short-circuit on circular reference errors, so the client + // has the option of ignoring them. + for _, err := range utils.UnwrapErrors(docErr) { + var refErr *index.ResolvingError + if errors.As(err, &refErr) { + if refErr.CircularReference == nil { + return nil, errors.Join(errs...) + } + } + } + + highDoc := v3high.NewDocument(lowDoc) + highDoc.Rolodex = lowDoc.Index.GetRolodex() + + d.highOpenAPI3Model = &DocumentModel[v3high.Document]{ + Model: *highDoc, + Index: lowDoc.Index, + } + lowbase.SchemaQuickHashMap.Clear() + return d.highOpenAPI3Model, errors.Join(errs...) +} + +// CompareDocuments will accept a left and right Document implementing struct, build a model for the correct +// version and then compare model documents for changes. +// +// If there are any errors when building the models, those errors are returned with a nil pointer for the +// model.DocumentChanges. If there are any changes found however between either Document, then a pointer to +// model.DocumentChanges is returned containing every single change, broken down, model by model. +func CompareDocuments(original, updated Document) (*model.DocumentChanges, error) { + var errs []error + if original.GetSpecInfo().SpecType == utils.OpenApi3 && updated.GetSpecInfo().SpecType == utils.OpenApi3 { + v3ModelLeft, oErrs := original.BuildV3Model() + if oErrs != nil { + errs = append(errs, oErrs) + } + v3ModelRight, uErrs := updated.BuildV3Model() + if uErrs != nil { + errs = append(errs, uErrs) + } + if v3ModelLeft != nil && v3ModelRight != nil { + return what_changed.CompareOpenAPIDocuments(v3ModelLeft.Model.GoLow(), v3ModelRight.Model.GoLow()), + errors.Join(errs...) + } else { + return nil, errors.Join(errs...) + } + } + if original.GetSpecInfo().SpecType == utils.OpenApi2 && updated.GetSpecInfo().SpecType == utils.OpenApi2 { + v2ModelLeft, oErrs := original.BuildV2Model() + if oErrs != nil { + errs = append(errs, oErrs) + } + v2ModelRight, uErrs := updated.BuildV2Model() + if uErrs != nil { + errs = append(errs, uErrs) + } + return what_changed.CompareSwaggerDocuments(v2ModelLeft.Model.GoLow(), v2ModelRight.Model.GoLow()), + errors.Join(errs...) + } + return nil, fmt.Errorf("unable to compare documents, one or both documents are not of the same version") +} diff --git a/vendor/github.com/pb33f/libopenapi/index/cache.go b/vendor/github.com/pb33f/libopenapi/index/cache.go new file mode 100644 index 000000000..2a962a230 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/index/cache.go @@ -0,0 +1,123 @@ +// Copyright 2023-2024 Princess Beef Heavy Industries, LLC / Dave Shanley +// https://pb33f.io + +package index + +import ( + "sync" + "sync/atomic" +) + +// SetCache sets a sync map as a temporary cache for the index. +func (index *SpecIndex) SetCache(sync *sync.Map) { + index.cache = sync +} + +// HighCacheHit increments the counter of high cache hits by one, and returns the current value of hits. +func (index *SpecIndex) HighCacheHit() uint64 { + index.highModelCache.AddHit() + return index.highModelCache.GetHits() +} + +// HighCacheMiss increments the counter of high cache misses by one, and returns the current value of misses. +func (index *SpecIndex) HighCacheMiss() uint64 { + index.highModelCache.AddMiss() + return index.highModelCache.GetMisses() +} + +// GetHighCacheHits returns the number of hits on the high model cache. +func (index *SpecIndex) GetHighCacheHits() uint64 { + return index.highModelCache.GetHits() +} + +// GetHighCacheMisses returns the number of misses on the high model cache. +func (index *SpecIndex) GetHighCacheMisses() uint64 { + return index.highModelCache.GetMisses() +} + +// GetHighCache returns the high model cache for this index. +func (index *SpecIndex) GetHighCache() Cache { + return index.highModelCache +} + +// InitHighCache allocates a new high model cache onto the index. +func (index *SpecIndex) InitHighCache() { + index.highModelCache = CreateNewCache() +} + +// SetHighCache sets the high model cache for this index. +func (index *SpecIndex) SetHighCache(cache *SimpleCache) { + index.highModelCache = cache +} + +// Cache is an interface for a simple cache that can be used by any consumer. +type Cache interface { + SetStore(*sync.Map) + GetStore() *sync.Map + AddHit() uint64 + AddMiss() uint64 + GetHits() uint64 + GetMisses() uint64 + Clear() + Load(any) (any, bool) + Store(any, any) +} + +// Below is an implementation of Cache called SimpleCache. + +// SimpleCache is a simple cache for the index, or any other consumer that needs it. +type SimpleCache struct { + store *sync.Map + hits atomic.Uint64 + misses atomic.Uint64 +} + +// CreateNewCache creates a new simple cache with a sync.Map store. +func CreateNewCache() Cache { + return &SimpleCache{store: &sync.Map{}} +} + +// SetStore sets the store for the cache. +func (c *SimpleCache) SetStore(store *sync.Map) { + c.store = store +} + +// GetStore returns the store for the cache. +func (c *SimpleCache) GetStore() *sync.Map { + return c.store +} + +// Load retrieves a value from the cache. +func (c *SimpleCache) Load(key any) (value any, ok bool) { + return c.store.Load(key) +} + +// Store stores a key-value pair in the cache. +func (c *SimpleCache) Store(key, value any) { + c.store.Store(key, value) +} + +// AddHit increments the hit counter by one, and returns the current value of hits. +func (c *SimpleCache) AddHit() uint64 { + return c.hits.Add(1) +} + +// AddMiss increments the miss counter by one, and returns the current value of misses. +func (c *SimpleCache) AddMiss() uint64 { + return c.misses.Add(1) +} + +// GetHits returns the current value of hits. +func (c *SimpleCache) GetHits() uint64 { + return c.hits.Load() +} + +// GetMisses returns the current value of misses. +func (c *SimpleCache) GetMisses() uint64 { + return c.misses.Load() +} + +// Clear clears the cache. +func (c *SimpleCache) Clear() { + c.store.Clear() +} diff --git a/vendor/github.com/pb33f/libopenapi/index/circular_reference_result.go b/vendor/github.com/pb33f/libopenapi/index/circular_reference_result.go new file mode 100644 index 000000000..2a9c4b3e8 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/index/circular_reference_result.go @@ -0,0 +1,34 @@ +package index + +import ( + "strings" + + "go.yaml.in/yaml/v4" +) + +// CircularReferenceResult contains a circular reference found when traversing the graph. +type CircularReferenceResult struct { + Journey []*Reference + ParentNode *yaml.Node + Start *Reference + LoopIndex int + LoopPoint *Reference + IsArrayResult bool // if this result comes from an array loop. + PolymorphicType string // which type of polymorphic loop is this? (oneOf, anyOf, allOf) + IsPolymorphicResult bool // if this result comes from a polymorphic loop. + IsInfiniteLoop bool // if all the definitions in the reference loop are marked as required, this is an infinite circular reference, thus is not allowed. +} + +// GenerateJourneyPath generates a string representation of the journey taken to find the circular reference. +func (c *CircularReferenceResult) GenerateJourneyPath() string { + buf := strings.Builder{} + for i, ref := range c.Journey { + if i > 0 { + buf.WriteString(" -> ") + } + + buf.WriteString(ref.Name) + } + + return buf.String() +} diff --git a/vendor/github.com/pb33f/libopenapi/index/extract_refs.go b/vendor/github.com/pb33f/libopenapi/index/extract_refs.go new file mode 100644 index 000000000..1f83c238f --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/index/extract_refs.go @@ -0,0 +1,1037 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package index + +import ( + "context" + "errors" + "fmt" + "net/url" + "os" + "path/filepath" + "runtime" + "slices" + "sort" + "strconv" + "strings" + "sync" + + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" + "golang.org/x/sync/singleflight" +) + +// preserveLegacyRefOrder allows opt-out of deterministic ordering if issues arise. +// Set LIBOPENAPI_LEGACY_REF_ORDER=true to use the old non-deterministic ordering. +var preserveLegacyRefOrder = os.Getenv("LIBOPENAPI_LEGACY_REF_ORDER") == "true" + +// indexedRef pairs a resolved reference with its original input position for deterministic ordering. +type indexedRef struct { + ref *Reference + pos int +} + +// ExtractRefs will return a deduplicated slice of references for every unique ref found in the document. +// The total number of refs, will generally be much higher, you can extract those from GetRawReferenceCount() +func (index *SpecIndex) ExtractRefs(ctx context.Context, node, parent *yaml.Node, seenPath []string, level int, poly bool, pName string) []*Reference { + if node == nil { + return nil + } + + // Initialize $id scope if not present (uses document base as initial scope) + scope := GetSchemaIdScope(ctx) + if scope == nil { + scope = NewSchemaIdScope(index.specAbsolutePath) + ctx = WithSchemaIdScope(ctx, scope) + } + + // Capture the parent's base URI BEFORE any $id in this node is processed + // This is used for registering any $id found in this node + parentBaseUri := scope.BaseUri + + // Check if THIS node has a $id and update scope for processing children + // This must happen before iterating children so they see the updated scope + if node.Kind == yaml.MappingNode { + if nodeId := FindSchemaIdInNode(node); nodeId != "" { + resolvedNodeId, _ := ResolveSchemaId(nodeId, parentBaseUri) + if resolvedNodeId == "" { + resolvedNodeId = nodeId + } + // Update scope for children of this node + scope = scope.Copy() + scope.PushId(resolvedNodeId) + ctx = WithSchemaIdScope(ctx, scope) + } + } + + var found []*Reference + if len(node.Content) > 0 { + var prev, polyName string + for i, n := range node.Content { + if utils.IsNodeMap(n) || utils.IsNodeArray(n) { + level++ + // check if we're using polymorphic values. These tend to create rabbit warrens of circular + // references if every single link is followed. We don't resolve polymorphic values. + isPoly, _ := index.checkPolymorphicNode(prev) + polyName = pName + if isPoly { + poly = true + if prev != "" { + polyName = prev + } + } + + found = append(found, index.ExtractRefs(ctx, n, node, seenPath, level, poly, polyName)...) + } + + // check if we're dealing with an inline schema definition, that isn't part of an array + // (which means it's being used as a value in an array, and it's not a label) + // https://github.com/pb33f/libopenapi/issues/76 + schemaContainingNodes := []string{"schema", "items", "additionalProperties", "contains", "not", "unevaluatedItems", "unevaluatedProperties"} + if i%2 == 0 && slices.Contains(schemaContainingNodes, n.Value) && !utils.IsNodeArray(node) && (i+1 < len(node.Content)) { + + var jsonPath, definitionPath, fullDefinitionPath string + + if len(seenPath) > 0 || n.Value != "" { + loc := append(seenPath, n.Value) + // create definition and full definition paths + locPath := strings.Join(loc, "/") + definitionPath = "#/" + locPath + fullDefinitionPath = index.specAbsolutePath + "#/" + locPath + _, jsonPath = utils.ConvertComponentIdIntoFriendlyPathSearch(definitionPath) + } + + ref := &Reference{ + ParentNode: parent, + FullDefinition: fullDefinitionPath, + Definition: definitionPath, + Node: node.Content[i+1], + KeyNode: node.Content[i], + Path: jsonPath, + Index: index, + } + + isRef, _, _ := utils.IsNodeRefValue(node.Content[i+1]) + if isRef { + // record this reference + index.allRefSchemaDefinitions = append(index.allRefSchemaDefinitions, ref) + continue + } + + if n.Value == "additionalProperties" || n.Value == "unevaluatedProperties" { + if utils.IsNodeBoolValue(node.Content[i+1]) { + continue + } + } + + index.allInlineSchemaDefinitions = append(index.allInlineSchemaDefinitions, ref) + + // check if the schema is an object or an array, + // and if so, add it to the list of inline schema object definitions. + k, v := utils.FindKeyNodeTop("type", node.Content[i+1].Content) + if k != nil && v != nil { + if v.Value == "object" || v.Value == "array" { + index.allInlineSchemaObjectDefinitions = append(index.allInlineSchemaObjectDefinitions, ref) + } + } + } + + // Perform the same check for all maps of schemas like properties and patternProperties + // https://github.com/pb33f/libopenapi/issues/76 + mapOfSchemaContainingNodes := []string{"properties", "patternProperties"} + if i%2 == 0 && slices.Contains(mapOfSchemaContainingNodes, n.Value) && !utils.IsNodeArray(node) && (i+1 < len(node.Content)) { + + // if 'examples' or 'example' exists in the seenPath, skip this 'properties' node. + // https://github.com/pb33f/libopenapi/issues/160 + if len(seenPath) > 0 { + skip := false + + // iterate through the path and look for an item named 'examples' or 'example' + for _, p := range seenPath { + if p == "examples" || p == "example" { + skip = true + break + } + // look for any extension in the path and ignore it + if strings.HasPrefix(p, "x-") { + skip = true + break + } + } + if skip { + continue + } + } + + // for each property add it to our schema definitions + label := "" + for h, prop := range node.Content[i+1].Content { + + if h%2 == 0 { + label = prop.Value + continue + } + var jsonPath, definitionPath, fullDefinitionPath string + if len(seenPath) > 0 || n.Value != "" && label != "" { + loc := append(seenPath, n.Value, label) + locPath := strings.Join(loc, "/") + definitionPath = "#/" + locPath + fullDefinitionPath = index.specAbsolutePath + "#/" + locPath + _, jsonPath = utils.ConvertComponentIdIntoFriendlyPathSearch(definitionPath) + } + ref := &Reference{ + ParentNode: parent, + FullDefinition: fullDefinitionPath, + Definition: definitionPath, + Node: prop, + KeyNode: node.Content[i], + Path: jsonPath, + Index: index, + } + + isRef, _, _ := utils.IsNodeRefValue(prop) + if isRef { + // record this reference + index.allRefSchemaDefinitions = append(index.allRefSchemaDefinitions, ref) + continue + } + + index.allInlineSchemaDefinitions = append(index.allInlineSchemaDefinitions, ref) + + // check if the schema is an object or an array, + // and if so, add it to the list of inline schema object definitions. + k, v := utils.FindKeyNodeTop("type", prop.Content) + if k != nil && v != nil { + if v.Value == "object" || v.Value == "array" { + index.allInlineSchemaObjectDefinitions = append(index.allInlineSchemaObjectDefinitions, ref) + } + } + } + } + + // Perform the same check for all arrays of schemas like allOf, anyOf, oneOf + arrayOfSchemaContainingNodes := []string{"allOf", "anyOf", "oneOf", "prefixItems"} + if i%2 == 0 && slices.Contains(arrayOfSchemaContainingNodes, n.Value) && !utils.IsNodeArray(node) && (i+1 < len(node.Content)) { + // for each element in the array, add it to our schema definitions + for h, element := range node.Content[i+1].Content { + + var jsonPath, definitionPath, fullDefinitionPath string + if len(seenPath) > 0 { + loc := append(seenPath, n.Value, strconv.Itoa(h)) + locPath := strings.Join(loc, "/") + definitionPath = "#/" + locPath + fullDefinitionPath = index.specAbsolutePath + "#/" + locPath + _, jsonPath = utils.ConvertComponentIdIntoFriendlyPathSearch(definitionPath) + } else { + definitionPath = "#/" + n.Value + fullDefinitionPath = index.specAbsolutePath + "#/" + n.Value + _, jsonPath = utils.ConvertComponentIdIntoFriendlyPathSearch(definitionPath) + } + + ref := &Reference{ + ParentNode: parent, + FullDefinition: fullDefinitionPath, + Definition: definitionPath, + Node: element, + KeyNode: node.Content[i], + Path: jsonPath, + Index: index, + } + + isRef, _, _ := utils.IsNodeRefValue(element) + if isRef { // record this reference + index.allRefSchemaDefinitions = append(index.allRefSchemaDefinitions, ref) + continue + } + index.allInlineSchemaDefinitions = append(index.allInlineSchemaDefinitions, ref) + + // check if the schema is an object or an array, + // and if so, add it to the list of inline schema object definitions. + k, v := utils.FindKeyNodeTop("type", element.Content) + if k != nil && v != nil { + if v.Value == "object" || v.Value == "array" { + index.allInlineSchemaObjectDefinitions = append(index.allInlineSchemaObjectDefinitions, ref) + } + } + } + } + + if i%2 == 0 && n.Value == "$ref" { + + // Check if this reference is under an extension path (x-* field). + // Always compute this so we can mark refs with IsExtensionRef. + isExtensionPath := false + for _, spi := range seenPath { + if strings.HasPrefix(spi, "x-") { + isExtensionPath = true + break + } + } + + // If configured to exclude extension refs, skip it entirely. + if index.config.ExcludeExtensionRefs && isExtensionPath { + continue + } + + // only look at scalar values, not maps (looking at you k8s) + if len(node.Content) > i+1 { + if !utils.IsNodeStringValue(node.Content[i+1]) { + continue + } + // issue #481, don't look at refs in arrays, the next node isn't the value. + if utils.IsNodeArray(node) { + continue + } + } + + index.linesWithRefs[n.Line] = true + + fp := make([]string, len(seenPath)) + copy(fp, seenPath) + + if len(node.Content) > i+1 { + + value := node.Content[i+1].Value + schemaIdBase := "" + if scope != nil && len(scope.Chain) > 0 { + schemaIdBase = scope.BaseUri + } + // extract last path segment without allocating a full slice + lastSlash := strings.LastIndexByte(value, '/') + var name string + if lastSlash >= 0 { + name = value[lastSlash+1:] + } else { + name = value + } + uri := strings.Split(value, "#/") + + // determine absolute path to this definition + var defRoot string + if strings.HasPrefix(index.specAbsolutePath, "http") { + defRoot = index.specAbsolutePath + } else { + defRoot = filepath.Dir(index.specAbsolutePath) + } + + var componentName string + var fullDefinitionPath string + if len(uri) == 2 { + // Check if we are dealing with a ref to a local definition. + if uri[0] == "" { + fullDefinitionPath = fmt.Sprintf("%s#/%s", index.specAbsolutePath, uri[1]) + componentName = value + } else { + if strings.HasPrefix(uri[0], "http") { + fullDefinitionPath = value + componentName = fmt.Sprintf("#/%s", uri[1]) + } else { + if filepath.IsAbs(uri[0]) { + fullDefinitionPath = value + componentName = fmt.Sprintf("#/%s", uri[1]) + } else { + // if the index has a base URL, use that to resolve the path. + if index.config.BaseURL != nil && !filepath.IsAbs(defRoot) { + var u url.URL + if strings.HasPrefix(defRoot, "http") { + up, _ := url.Parse(defRoot) + up.Path = utils.ReplaceWindowsDriveWithLinuxPath(filepath.Dir(up.Path)) + u = *up + } else { + u = *index.config.BaseURL + } + // abs, _ := filepath.Abs(filepath.Join(u.Path, uri[0])) + // abs, _ := filepath.Abs(utils.CheckPathOverlap(u.Path, uri[0], string(os.PathSeparator))) + abs := utils.CheckPathOverlap(u.Path, uri[0], string(os.PathSeparator)) + u.Path = utils.ReplaceWindowsDriveWithLinuxPath(abs) + fullDefinitionPath = fmt.Sprintf("%s#/%s", u.String(), uri[1]) + componentName = fmt.Sprintf("#/%s", uri[1]) + + } else { + abs := index.resolveRelativeFilePath(defRoot, uri[0]) + fullDefinitionPath = fmt.Sprintf("%s#/%s", abs, uri[1]) + componentName = fmt.Sprintf("#/%s", uri[1]) + } + } + } + } + } else { + if strings.HasPrefix(uri[0], "http") { + fullDefinitionPath = value + } else { + // is it a relative file include? + if !strings.Contains(uri[0], "#") { + if strings.HasPrefix(defRoot, "http") { + if !filepath.IsAbs(uri[0]) { + u, _ := url.Parse(defRoot) + pathDir := filepath.Dir(u.Path) + // pathAbs, _ := filepath.Abs(filepath.Join(pathDir, uri[0])) + pathAbs, _ := filepath.Abs(utils.CheckPathOverlap(pathDir, uri[0], string(os.PathSeparator))) + pathAbs = utils.ReplaceWindowsDriveWithLinuxPath(pathAbs) + u.Path = pathAbs + fullDefinitionPath = u.String() + } + } else { + if !filepath.IsAbs(uri[0]) { + // if the index has a base URL, use that to resolve the path. + if index.config.BaseURL != nil { + + u := *index.config.BaseURL + abs := utils.CheckPathOverlap(u.Path, uri[0], string(os.PathSeparator)) + abs = utils.ReplaceWindowsDriveWithLinuxPath(abs) + u.Path = abs + fullDefinitionPath = u.String() + componentName = uri[0] + } else { + abs := index.resolveRelativeFilePath(defRoot, uri[0]) + fullDefinitionPath = abs + componentName = uri[0] + } + } + } + } + } + } + + if fullDefinitionPath == "" && value != "" { + fullDefinitionPath = value + } + if componentName == "" { + componentName = value + } + + _, p := utils.ConvertComponentIdIntoFriendlyPathSearch(componentName) + + // check for sibling properties + siblingProps := make(map[string]*yaml.Node) + var siblingKeys []*yaml.Node + hasSiblings := len(node.Content) > 2 + + if hasSiblings { + for j := 0; j < len(node.Content); j += 2 { + if j+1 < len(node.Content) && node.Content[j].Value != "$ref" { + siblingProps[node.Content[j].Value] = node.Content[j+1] + siblingKeys = append(siblingKeys, node.Content[j]) + } + } + } + + ref := &Reference{ + ParentNode: parent, + FullDefinition: fullDefinitionPath, + Definition: componentName, + RawRef: value, + SchemaIdBase: schemaIdBase, + Name: name, + Node: node, + KeyNode: node.Content[i+1], + Path: p, + Index: index, + IsExtensionRef: isExtensionPath, + HasSiblingProperties: len(siblingProps) > 0, + SiblingProperties: siblingProps, + SiblingKeys: siblingKeys, + } + + // add to raw sequenced refs + index.rawSequencedRefs = append(index.rawSequencedRefs, ref) + + // add ref by line number + refNameIndex := strings.LastIndex(value, "/") + refName := value[refNameIndex+1:] + if len(index.refsByLine[refName]) > 0 { + index.refsByLine[refName][n.Line] = true + } else { + v := make(map[int]bool) + v[n.Line] = true + index.refsByLine[refName] = v + } + + // if this ref value has any siblings (node.Content is larger than two elements) + // then add to refs with siblings + if len(node.Content) > 2 { + copiedNode := *node + + // extract sibling properties and keys + siblingProps := make(map[string]*yaml.Node) + var siblingKeys []*yaml.Node + + for j := 0; j < len(node.Content); j += 2 { + if j+1 < len(node.Content) && node.Content[j].Value != "$ref" { + siblingProps[node.Content[j].Value] = node.Content[j+1] + siblingKeys = append(siblingKeys, node.Content[j]) + } + } + + copied := Reference{ + ParentNode: parent, + FullDefinition: fullDefinitionPath, + Definition: ref.Definition, + RawRef: ref.RawRef, + SchemaIdBase: ref.SchemaIdBase, + Name: ref.Name, + Node: &copiedNode, + KeyNode: node.Content[i], + Path: p, + Index: index, + IsExtensionRef: isExtensionPath, + HasSiblingProperties: len(siblingProps) > 0, + SiblingProperties: siblingProps, + SiblingKeys: siblingKeys, + } + // protect this data using a copy, prevent the resolver from destroying things. + index.refsWithSiblings[value] = copied + } + + // if this is a polymorphic reference, we're going to leave it out + // allRefs. We don't ever want these resolved, so instead of polluting + // the timeline, we will keep each poly ref in its own collection for later + // analysis. + if poly { + index.polymorphicRefs[value] = ref + + // index each type + switch pName { + case "anyOf": + index.polymorphicAnyOfRefs = append(index.polymorphicAnyOfRefs, ref) + case "allOf": + index.polymorphicAllOfRefs = append(index.polymorphicAllOfRefs, ref) + case "oneOf": + index.polymorphicOneOfRefs = append(index.polymorphicOneOfRefs, ref) + } + continue + } + + // check if this is a dupe, if so, skip it, we don't care now. + if index.allRefs[value] != nil { // seen before, skip. + continue + } + + if value == "" { + + completedPath := fmt.Sprintf("$.%s", strings.Join(fp, ".")) + c := node.Content[i] + if len(node.Content) > i+1 { // if the next node exists, use that. + c = node.Content[i+1] + } + + indexError := &IndexingError{ + Err: errors.New("schema reference is empty and cannot be processed"), + Node: c, + KeyNode: node.Content[i], + Path: completedPath, + } + + index.refErrors = append(index.refErrors, indexError) + continue + } + + // This sets the ref in the path using the full URL and sub-path. + index.allRefs[fullDefinitionPath] = ref + found = append(found, ref) + } + } + + // Detect and register JSON Schema 2020-12 $id declarations + if i%2 == 0 && n.Value == "$id" { + if len(node.Content) > i+1 && utils.IsNodeStringValue(node.Content[i+1]) { + idValue := node.Content[i+1].Value + idNode := node.Content[i+1] + + // Build the definition path for this schema + var definitionPath string + if len(seenPath) > 0 { + definitionPath = "#/" + strings.Join(seenPath, "/") + } else { + definitionPath = "#" + } + + // Validate the $id (must not contain fragment) + if err := ValidateSchemaId(idValue); err != nil { + index.errorLock.Lock() + index.refErrors = append(index.refErrors, &IndexingError{ + Err: fmt.Errorf("invalid $id value '%s': %w", idValue, err), + Node: idNode, + KeyNode: node.Content[i], + Path: definitionPath, + }) + index.errorLock.Unlock() + continue + } + + // Resolve the $id against the PARENT scope's base URI (nearest ancestor $id) + // This implements JSON Schema 2020-12 hierarchical $id resolution + // We use parentBaseUri which was captured before this node's $id was pushed + baseUri := parentBaseUri + if baseUri == "" { + baseUri = index.specAbsolutePath + } + resolvedUri, resolveErr := ResolveSchemaId(idValue, baseUri) + if resolveErr != nil { + if index.logger != nil { + index.logger.Warn("failed to resolve $id", + "id", idValue, + "base", baseUri, + "definitionPath", definitionPath, + "error", resolveErr.Error(), + "line", idNode.Line) + } + resolvedUri = idValue // Use original as fallback + } + + // Create and register the schema ID entry + // ParentId is the parent scope's base URI (if it differs from document base) + parentId := "" + if parentBaseUri != index.specAbsolutePath && parentBaseUri != "" { + parentId = parentBaseUri + } + entry := &SchemaIdEntry{ + Id: idValue, + ResolvedUri: resolvedUri, + SchemaNode: node, + ParentId: parentId, + Index: index, + DefinitionPath: definitionPath, + Line: idNode.Line, + Column: idNode.Column, + } + + // Register in the index (validation already done above) + _ = index.RegisterSchemaId(entry) + } + } + + // Skip $ref and $id from path building - they are keywords, not schema properties + if i%2 == 0 && n.Value != "$ref" && n.Value != "$id" && n.Value != "" { + + v := n.Value + if strings.HasPrefix(v, "/") { + v = strings.Replace(v, "/", "~1", 1) + } + + loc := append(seenPath, v) + definitionPath := "#/" + strings.Join(loc, "/") + _, jsonPath := utils.ConvertComponentIdIntoFriendlyPathSearch(definitionPath) + + // capture descriptions and summaries + if n.Value == "description" { + + // if the parent is a sequence, ignore. + if utils.IsNodeArray(node) { + continue + } + // Skip if "description" is a property name inside schema properties + // We check if the previous element in seenPath is "properties" and this is at an even index + // (property names are at even indices, values at odd) + if len(seenPath) > 0 && (seenPath[len(seenPath)-1] == "properties" || seenPath[len(seenPath)-1] == "patternProperties") { + // This means "description" is a property name, not a description field, skip extraction + seenPath = append(seenPath, strings.ReplaceAll(n.Value, "/", "~1")) + prev = n.Value + continue + } + if !slices.Contains(seenPath, "example") && !slices.Contains(seenPath, "examples") { + ref := &DescriptionReference{ + ParentNode: parent, + Content: node.Content[i+1].Value, + Path: jsonPath, + Node: node.Content[i+1], + KeyNode: node.Content[i], + IsSummary: false, + } + + if !utils.IsNodeMap(ref.Node) { + index.allDescriptions = append(index.allDescriptions, ref) + index.descriptionCount++ + } + } + } + + if n.Value == "summary" { + + // Skip if "summary" is a property name inside schema properties + // We check if the previous element in seenPath is "properties" and this is at an even index + // (property names are at even indices, values at odd) + if len(seenPath) > 0 && (seenPath[len(seenPath)-1] == "properties" || seenPath[len(seenPath)-1] == "patternProperties") { + // This means "summary" is a property name, not a summary field, skip extraction + seenPath = append(seenPath, strings.ReplaceAll(n.Value, "/", "~1")) + prev = n.Value + continue + } + + if slices.Contains(seenPath, "example") || slices.Contains(seenPath, "examples") { + continue + } + + var b *yaml.Node + if len(node.Content) == i+1 { + b = node.Content[i] + } else { + b = node.Content[i+1] + } + ref := &DescriptionReference{ + ParentNode: parent, + Content: b.Value, + Path: jsonPath, + Node: b, + KeyNode: n, + IsSummary: true, + } + + index.allSummaries = append(index.allSummaries, ref) + index.summaryCount++ + } + + // capture security requirement references (these are not traditional references, but they + // are used as a look-up. This is the only exception to the design. + if n.Value == "security" { + var b *yaml.Node + if len(node.Content) == i+1 { + b = node.Content[i] + } else { + b = node.Content[i+1] + } + if utils.IsNodeArray(b) { + var secKey string + for k := range b.Content { + if utils.IsNodeMap(b.Content[k]) { + for g := range b.Content[k].Content { + if g%2 == 0 { + secKey = b.Content[k].Content[g].Value + continue + } + if utils.IsNodeArray(b.Content[k].Content[g]) { + var refMap map[string][]*Reference + if index.securityRequirementRefs[secKey] == nil { + index.securityRequirementRefs[secKey] = make(map[string][]*Reference) + refMap = index.securityRequirementRefs[secKey] + } else { + refMap = index.securityRequirementRefs[secKey] + } + for r := range b.Content[k].Content[g].Content { + var refs []*Reference + if refMap[b.Content[k].Content[g].Content[r].Value] != nil { + refs = refMap[b.Content[k].Content[g].Content[r].Value] + } + + refs = append(refs, &Reference{ + Definition: b.Content[k].Content[g].Content[r].Value, + Path: fmt.Sprintf("%s.security[%d].%s[%d]", jsonPath, k, secKey, r), + Node: b.Content[k].Content[g].Content[r], + KeyNode: b.Content[k].Content[g], + }) + + index.securityRequirementRefs[secKey][b.Content[k].Content[g].Content[r].Value] = refs + } + } + } + } + } + } + } + // capture enums + if n.Value == "enum" { + + if len(seenPath) > 0 { + lastItem := seenPath[len(seenPath)-1] + if lastItem == "properties" { + seenPath = append(seenPath, strings.ReplaceAll(n.Value, "/", "~1")) + prev = n.Value + continue + } + } + + // all enums need to have a type, extract the type from the node where the enum was found. + _, enumKeyValueNode := utils.FindKeyNodeTop("type", node.Content) + + if enumKeyValueNode != nil { + ref := &EnumReference{ + ParentNode: parent, + Path: jsonPath, + Node: node.Content[i+1], + KeyNode: node.Content[i], + Type: enumKeyValueNode, + SchemaNode: node, + } + + index.allEnums = append(index.allEnums, ref) + index.enumCount++ + } + } + // capture all objects with properties + if n.Value == "properties" { + _, typeKeyValueNode := utils.FindKeyNodeTop("type", node.Content) + + if typeKeyValueNode != nil { + isObject := false + + if typeKeyValueNode.Value == "object" { + isObject = true + } + + for _, v := range typeKeyValueNode.Content { + if v.Value == "object" { + isObject = true + } + } + + if isObject { + index.allObjectsWithProperties = append(index.allObjectsWithProperties, &ObjectReference{ + Path: jsonPath, + Node: node, + KeyNode: n, + ParentNode: parent, + }) + } + } + } + + seenPath = append(seenPath, strings.ReplaceAll(n.Value, "/", "~1")) + // seenPath = append(seenPath, n.Value) + prev = n.Value + } + + // if next node is map, don't add segment. + if i < len(node.Content)-1 { + next := node.Content[i+1] + + if i%2 != 0 && next != nil && !utils.IsNodeArray(next) && !utils.IsNodeMap(next) && len(seenPath) > 0 { + seenPath = seenPath[:len(seenPath)-1] + } + } + } + } + + index.refCount = len(index.allRefs) + + return found +} + +// ExtractComponentsFromRefs returns located components from references. The returned nodes from here +// can be used for resolving as they contain the actual object properties. +// +// This function uses singleflight to deduplicate concurrent lookups for the same reference, +// channel-based collection to avoid mutex contention during resolution, and sorts results +// by input position for deterministic ordering. +func (index *SpecIndex) ExtractComponentsFromRefs(ctx context.Context, refs []*Reference) []*Reference { + if len(refs) == 0 { + return nil + } + + refsToCheck := refs + mappedRefsInSequence := make([]*ReferenceMapped, len(refsToCheck)) + + // Sequential mode: process refs one at a time (used for bundling) + if index.config.ExtractRefsSequentially { + found := make([]*Reference, 0, len(refsToCheck)) + for i, ref := range refsToCheck { + located := index.locateRef(ctx, ref) + if located != nil { + index.refLock.Lock() + if index.allMappedRefs[located.FullDefinition] == nil { + index.allMappedRefs[located.FullDefinition] = located + found = append(found, located) + } + mappedRefsInSequence[i] = &ReferenceMapped{ + OriginalReference: ref, + Reference: located, + Definition: located.Definition, + FullDefinition: located.FullDefinition, + } + index.refLock.Unlock() + } else { + // If SkipExternalRefResolution is enabled, don't record errors for external refs + if index.config != nil && index.config.SkipExternalRefResolution && utils.IsExternalRef(ref.Definition) { + continue + } + // Record error for definitive failure + _, path := utils.ConvertComponentIdIntoFriendlyPathSearch(ref.Definition) + index.errorLock.Lock() + index.refErrors = append(index.refErrors, &IndexingError{ + Err: fmt.Errorf("component `%s` does not exist in the specification", ref.Definition), + Node: ref.Node, + Path: path, + KeyNode: ref.KeyNode, + }) + index.errorLock.Unlock() + } + } + // Collect sequenced results + for _, rm := range mappedRefsInSequence { + if rm != nil { + index.allMappedRefsSequenced = append(index.allMappedRefsSequenced, rm) + } + } + return found + } + + // Async mode: use singleflight for deduplication and channel-based collection + var wg sync.WaitGroup + var sfGroup singleflight.Group // Local to this call - no cross-index coupling + + // Channel-based collection - no mutex needed during resolution + resultsChan := make(chan indexedRef, len(refsToCheck)) + + // Concurrency control + maxConcurrency := runtime.GOMAXPROCS(0) + if maxConcurrency < 4 { + maxConcurrency = 4 + } + sem := make(chan struct{}, maxConcurrency) + + for i, ref := range refsToCheck { + i, ref := i, ref // capture loop variables + wg.Add(1) + + go func() { + sem <- struct{}{} + defer func() { <-sem }() + defer wg.Done() + + // Singleflight deduplication - one lookup per FullDefinition + result, _, _ := sfGroup.Do(ref.FullDefinition, func() (interface{}, error) { + // Fast path: already mapped + index.refLock.RLock() + if existing := index.allMappedRefs[ref.FullDefinition]; existing != nil { + index.refLock.RUnlock() + return existing, nil + } + index.refLock.RUnlock() + + // Do the actual lookup (only one goroutine per FullDefinition) + return index.locateRef(ctx, ref), nil + }) + + // Type assert and check for nil - interface containing nil pointer is not nil + located := result.(*Reference) + if located != nil { + resultsChan <- indexedRef{ref: located, pos: i} + } else { + resultsChan <- indexedRef{ref: nil, pos: i} // Track failures for reconciliation + } + }() + } + + // Close channel after all goroutines complete + go func() { + wg.Wait() + close(resultsChan) + }() + + // Collect results - single consumer, no lock needed + collected := make([]indexedRef, 0, len(refsToCheck)) + for r := range resultsChan { + collected = append(collected, r) + } + + // Sort by input position for deterministic ordering + if !preserveLegacyRefOrder { + sort.Slice(collected, func(i, j int) bool { + return collected[i].pos < collected[j].pos + }) + } + + // RECONCILIATION PHASE: Build final results with minimal locking + found := make([]*Reference, 0, len(collected)) + + for _, c := range collected { + ref := refsToCheck[c.pos] + located := c.ref + + // Reconcile nil results - check if another goroutine succeeded. + // We use ref.FullDefinition here because that's the singleflight key, + // and located.FullDefinition should match ref.FullDefinition for the + // same reference (FindComponent returns the component at that definition). + if located == nil { + index.refLock.RLock() + located = index.allMappedRefs[ref.FullDefinition] + index.refLock.RUnlock() + } + + if located != nil { + // Add to allMappedRefs if not present + index.refLock.Lock() + if index.allMappedRefs[located.FullDefinition] == nil { + index.allMappedRefs[located.FullDefinition] = located + found = append(found, located) + } + mappedRefsInSequence[c.pos] = &ReferenceMapped{ + OriginalReference: ref, + Reference: located, + Definition: located.Definition, + FullDefinition: located.FullDefinition, + } + index.refLock.Unlock() + } else { + // If SkipExternalRefResolution is enabled, don't record errors for external refs + if index.config != nil && index.config.SkipExternalRefResolution && utils.IsExternalRef(ref.Definition) { + continue + } + // Definitive failure - record error + _, path := utils.ConvertComponentIdIntoFriendlyPathSearch(ref.Definition) + index.errorLock.Lock() + index.refErrors = append(index.refErrors, &IndexingError{ + Err: fmt.Errorf("component `%s` does not exist in the specification", ref.Definition), + Node: ref.Node, + Path: path, + KeyNode: ref.KeyNode, + }) + index.errorLock.Unlock() + } + } + + // Collect sequenced results in input order + for _, rm := range mappedRefsInSequence { + if rm != nil { + index.allMappedRefsSequenced = append(index.allMappedRefsSequenced, rm) + } + } + + return found +} + +// locateRef finds a component for a reference, including KeyNode extraction. +// This is a helper used by ExtractComponentsFromRefs to isolate the lookup logic. +func (index *SpecIndex) locateRef(ctx context.Context, ref *Reference) *Reference { + // External references require a full Lock (not RLock) during FindComponent because + // FindComponent may trigger rolodex file loading which mutates index state. + // Internal references can proceed without locking since they only read from + // already-populated data structures. + uri := strings.Split(ref.FullDefinition, "#/") + isExternalRef := len(uri) == 2 && len(uri[0]) > 0 + if isExternalRef { + index.refLock.Lock() + } + located := index.FindComponent(ctx, ref.FullDefinition) + if isExternalRef { + index.refLock.Unlock() + } + + if located == nil { + rawRef := ref.RawRef + if rawRef == "" { + rawRef = ref.FullDefinition + } + normalizedRef := resolveRefWithSchemaBase(rawRef, ref.SchemaIdBase) + if resolved := index.ResolveRefViaSchemaId(normalizedRef); resolved != nil { + located = resolved + } else { + return nil + } + } + + // Extract KeyNode - yamlpath API returns subnodes only, so we need to + // rollback in the nodemap a line (if we can) to extract the keynode. + if located.Node != nil { + index.nodeMapLock.RLock() + if located.Node.Line > 1 && len(index.nodeMap[located.Node.Line-1]) > 0 { + for _, v := range index.nodeMap[located.Node.Line-1] { + located.KeyNode = v + break + } + } + index.nodeMapLock.RUnlock() + } + + return located +} diff --git a/vendor/github.com/pb33f/libopenapi/index/find_component.go b/vendor/github.com/pb33f/libopenapi/index/find_component.go new file mode 100644 index 000000000..4234b7194 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/index/find_component.go @@ -0,0 +1,275 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package index + +import ( + "context" + "fmt" + "net/url" + "os" + "path/filepath" + "strings" + + jsonpathconfig "github.com/pb33f/jsonpath/pkg/jsonpath/config" + + "github.com/pb33f/jsonpath/pkg/jsonpath" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// FindComponent will locate a component by its reference, returns nil if nothing is found. +// This method will recurse through remote, local and file references. For each new external reference +// a new index will be created. These indexes can then be traversed recursively. +func (index *SpecIndex) FindComponent(ctx context.Context, componentId string) *Reference { + if index.root == nil { + return nil + } + + if strings.HasPrefix(componentId, "/") { + baseUri, fragment := SplitRefFragment(componentId) + if resolved := index.resolveRefViaSchemaIdPath(baseUri); resolved != nil { + if fragment != "" && resolved.Node != nil { + if fragmentNode := navigateToFragment(resolved.Node, fragment); fragmentNode != nil { + resolved.Node = fragmentNode + } + } + return resolved + } + } + + uri := strings.Split(componentId, "#/") + if len(uri) == 2 { + if uri[0] != "" { + if index.specAbsolutePath == uri[0] { + return index.FindComponentInRoot(ctx, fmt.Sprintf("#/%s", uri[1])) + } else { + return index.lookupRolodex(ctx, uri) + } + } else { + return index.FindComponentInRoot(ctx, fmt.Sprintf("#/%s", uri[1])) + } + } else { + + // does it contain a file extension? + fileExt := filepath.Ext(componentId) + if fileExt != "" { + + // check if the context has a root index set, if so this is a deep search that has moved through multiple + // indexes and we need to adjust the URI to reflect the location of the root index. + // + // the below code has been commended out due to being handled in the index. Keeping it for legacy and for + // future bugs. + // + //if ctx.Value(RootIndexKey) != nil { + // rootIndex := ctx.Value(RootIndexKey).(*SpecIndex) + // if rootIndex != nil && rootIndex.specAbsolutePath != "" { + // dir := filepath.Dir(rootIndex.specAbsolutePath) + // // create an absolute path to the file. + // absoluteFilePath := filepath.Join(dir, componentId) + // // split into a URI. + // uri = []string{absoluteFilePath} + // } + //} + + return index.lookupRolodex(ctx, uri) + } + + // root search + return index.FindComponentInRoot(ctx, componentId) + } +} + +func FindComponent(_ context.Context, root *yaml.Node, componentId, absoluteFilePath string, index *SpecIndex) *Reference { + // check component for url encoding. + if strings.Contains(componentId, "%") { + // decode the url. + componentId, _ = url.QueryUnescape(componentId) + } + + name, friendlySearch := utils.ConvertComponentIdIntoFriendlyPathSearch(componentId) + if friendlySearch == "$." { + friendlySearch = "$" + } + path, err := jsonpath.NewPath(friendlySearch, jsonpathconfig.WithPropertyNameExtension()) + if path == nil || err != nil || root == nil { + return nil // no component found + } + res := path.Query(root) + + if len(res) == 1 { + resNode := res[0] + fullDef := fmt.Sprintf("%s%s", absoluteFilePath, componentId) + // extract properties + + // check if we have already seen this reference and there is a parent, use it + var parentNode *yaml.Node + if index.allRefs[componentId] != nil { + parentNode = index.allRefs[componentId].ParentNode + } + if index.allRefs[fullDef] != nil { + parentNode = index.allRefs[fullDef].ParentNode + } + + ref := &Reference{ + FullDefinition: fullDef, + Definition: componentId, + Name: name, + Node: resNode, + Path: friendlySearch, + RemoteLocation: absoluteFilePath, + ParentNode: parentNode, + Index: index, + RequiredRefProperties: extractDefinitionRequiredRefProperties(resNode, map[string][]string{}, fullDef, index), + } + return ref + } + return nil +} + +func (index *SpecIndex) FindComponentInRoot(ctx context.Context, componentId string) *Reference { + if index.root != nil { + + componentId = utils.ReplaceWindowsDriveWithLinuxPath(componentId) + if !strings.HasPrefix(componentId, "#/") { + spl := strings.Split(componentId, "#/") + if len(spl) == 2 { + if spl[0] != "" { + componentId = fmt.Sprintf("#/%s", spl[1]) + } + } + } + + return FindComponent(ctx, index.root, componentId, index.specAbsolutePath, index) + } + return nil +} + +func (index *SpecIndex) lookupRolodex(ctx context.Context, uri []string) *Reference { + if index.rolodex == nil { + return nil + } + + // If SkipExternalRefResolution is enabled, don't attempt rolodex lookups + if index.config != nil && index.config.SkipExternalRefResolution { + return nil + } + + if len(uri) > 0 { + + // split string to remove file reference + file := strings.ReplaceAll(uri[0], "file:", "") + + var absoluteFileLocation, fileName string + fileName = filepath.Base(file) + absoluteFileLocation = file + if !filepath.IsAbs(file) && !strings.HasPrefix(file, "http") { + // When resolving relative references, use the directory of the current index's spec file + // if available, not the root BasePath. This ensures that external files can resolve + // relative paths correctly (e.g., "./models/foo.yaml" from an external file should + // resolve relative to that external file's directory, not the root spec directory). + basePath := index.config.BasePath + if index.specAbsolutePath != "" { + basePath = filepath.Dir(index.specAbsolutePath) + } + absoluteFileLocation, _ = filepath.Abs(utils.CheckPathOverlap(basePath, file, string(os.PathSeparator))) + } + + // if the absolute file location has no file ext, then get the rolodex root. + ext := filepath.Ext(absoluteFileLocation) + var parsedDocument *yaml.Node + idx := index + if ext != "" { + // extract the document from the rolodex. + rFile, rError := index.rolodex.OpenWithContext(ctx, absoluteFileLocation) + + if rError != nil { + index.logger.Error("unable to open the rolodex file, check specification references and base path", + "file", absoluteFileLocation, "error", rError) + return nil + } + + if rFile == nil { + index.logger.Error("cannot locate file in the rolodex, check specification references and base path", + "file", absoluteFileLocation) + return nil + } + // Check if the index is already available (handles recursive lookups within same goroutine). + // Only wait for indexing if the index isn't already set - this prevents deadlocks + // in recursive scenarios where A->B->A would otherwise wait forever. + if rFile.GetIndex() == nil { + // Check if this file is being indexed in the current call chain. + // If so, we have a circular dependency and should NOT wait (would deadlock). + // Instead, proceed without the index - the component lookup will still work + // using the parsed YAML content, and circular references will be detected later. + if !IsFileBeingIndexed(ctx, absoluteFileLocation) { + // Wait for the file's index to be ready before using it. + // This handles the case where another goroutine is still indexing the file. + rFile.WaitForIndexing() + } + } + if rFile.GetIndex() != nil { + idx = rFile.GetIndex() + } + + parsedDocument, _ = rFile.GetContentAsYAMLNode() + + } else { + parsedDocument = index.root + } + + wholeFile := false + query := "" + if len(uri) < 2 { + wholeFile = true + } else { + query = fmt.Sprintf("#/%s", uri[1]) + } + + // check if there is a component we want to suck in, or if the + // entire root needs to come in. + var foundRef *Reference + if wholeFile { + + if parsedDocument != nil { + if parsedDocument.Kind == yaml.DocumentNode { + parsedDocument = parsedDocument.Content[0] + } + } + + var parentNode *yaml.Node + if index.allRefs[absoluteFileLocation] != nil { + parentNode = index.allRefs[absoluteFileLocation].ParentNode + } + + foundRef = &Reference{ + ParentNode: parentNode, + FullDefinition: absoluteFileLocation, + Definition: fileName, + Name: fileName, + Index: idx, + Node: parsedDocument, + IsRemote: true, + RemoteLocation: absoluteFileLocation, + Path: "$", + RequiredRefProperties: extractDefinitionRequiredRefProperties(parsedDocument, map[string][]string{}, absoluteFileLocation, index), + } + return foundRef + } else { + foundRef = FindComponent(ctx, parsedDocument, query, absoluteFileLocation, index) + if foundRef != nil { + foundRef.IsRemote = true + foundRef.RemoteLocation = absoluteFileLocation + return foundRef + } else { + // Debug: log when FindComponent returns nil + index.logger.Debug("[lookupRolodex] FindComponent returned nil", + "absoluteFileLocation", absoluteFileLocation, + "query", query, + "parsedDocument_nil", parsedDocument == nil, + "idx_nil", idx == nil) + } + } + } + return nil +} diff --git a/vendor/github.com/pb33f/libopenapi/index/index_model.go b/vendor/github.com/pb33f/libopenapi/index/index_model.go new file mode 100644 index 000000000..73ee111c9 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/index/index_model.go @@ -0,0 +1,497 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package index + +import ( + "encoding/json" + "io/fs" + "log/slog" + "net/http" + "net/url" + "path/filepath" + "sync" + + "github.com/pb33f/libopenapi/utils" + + "github.com/pb33f/libopenapi/datamodel" + "go.yaml.in/yaml/v4" +) + +// Reference is a wrapper around *yaml.Node results to make things more manageable when performing +// algorithms on data models. the *yaml.Node def is just a bit too low level for tracking state. +type Reference struct { + FullDefinition string `json:"fullDefinition,omitempty"` + Definition string `json:"definition,omitempty"` + RawRef string `json:"-"` + SchemaIdBase string `json:"-"` + Name string `json:"name,omitempty"` + Node *yaml.Node `json:"-"` + KeyNode *yaml.Node `json:"-"` + ParentNode *yaml.Node `json:"-"` + ParentNodeSchemaType string `json:"-"` // used to determine if the parent node is an array or not. + ParentNodeTypes []string `json:"-"` // used to capture deep journeys, if any item is an array, we need to know. + Resolved bool `json:"-"` + Circular bool `json:"-"` + Seen bool `json:"-"` + IsRemote bool `json:"isRemote,omitempty"` + IsExtensionRef bool `json:"isExtensionRef,omitempty"` // true if ref is under an x-* extension path + Index *SpecIndex `json:"-"` // index that contains this reference. + RemoteLocation string `json:"remoteLocation,omitempty"` + Path string `json:"path,omitempty"` // this won't always be available. + RequiredRefProperties map[string][]string `json:"requiredProperties,omitempty"` // definition names (eg, #/definitions/One) to a list of required properties on this definition which reference that definition + HasSiblingProperties bool `json:"-"` // indicates if ref has sibling properties + SiblingProperties map[string]*yaml.Node `json:"-"` // stores sibling property nodes + SiblingKeys []*yaml.Node `json:"-"` // stores sibling key nodes + In string `json:"-"` // parameter location (path, query, header, cookie) - cached for performance +} + +// ReferenceMapped is a helper struct for mapped references put into sequence (we lose the key) +type ReferenceMapped struct { + OriginalReference *Reference `json:"originalReference,omitempty"` + Reference *Reference `json:"reference,omitempty"` + Definition string `json:"definition,omitempty"` + FullDefinition string `json:"fullDefinition,omitempty"` + IsPolymorphic bool `json:"isPolymorphic,omitempty"` +} + +// MarshalJSON is a custom JSON marshaller for the ReferenceMapped struct. +func (rm *ReferenceMapped) MarshalJSON() ([]byte, error) { + d := map[string]interface{}{ + "definition": rm.Definition, + "fullDefinition": rm.FullDefinition, + "jsonPath": rm.OriginalReference.Path, + "line": rm.OriginalReference.Node.Line, + "startColumn": rm.OriginalReference.Node.Column, + "endColumn": rm.OriginalReference.Node.Content[1].Column + + (len(rm.OriginalReference.Node.Content[1].Value) + 2), + } + if rm.IsPolymorphic { + d["isPolymorphic"] = true + } + + if rm.Reference != nil && rm.Reference.KeyNode != nil { + d["targetLine"] = rm.Reference.KeyNode.Line + d["targetColumn"] = rm.Reference.KeyNode.Column + } + return json.Marshal(d) +} + +// SpecIndexConfig is a configuration struct for the SpecIndex introduced in 0.6.0 that provides an expandable +// set of granular options. The first being the ability to set the Base URL for resolving relative references, and +// allowing or disallowing remote or local file lookups. +// - https://github.com/pb33f/libopenapi/issues/73 +type SpecIndexConfig struct { + // The BaseURL will be the root from which relative references will be resolved from if they can't be found locally. + // + // For example: + // - $ref: somefile.yaml#/components/schemas/SomeSchema + // + // Might not be found locally, if the file was pulled in from a remote server (a good example is the DigitalOcean API). + // so by setting a BaseURL, the reference will try to be resolved from the remote server. + // + // If our baseURL is set to https://pb33f.io/libopenapi then our reference will try to be resolved from: + // - $ref: https://pb33f.io/libopenapi/somefile.yaml#/components/schemas/SomeSchema + // + // More details on relative references can be found in issue #73: https://github.com/pb33f/libopenapi/issues/73 + BaseURL *url.URL // set the Base URL for resolving relative references if the spec is exploded. + + // If resolving remotely, the RemoteURLHandler will be used to fetch the remote document. + // If not set, the default http client will be used. + // Resolves [#132]: https://github.com/pb33f/libopenapi/issues/132 + // deprecated: Use the Rolodex instead + RemoteURLHandler func(url string) (*http.Response, error) + + // FSHandler is an entity that implements the `fs.FS` interface that will be used to fetch local or remote documents. + // This is useful if you want to use a custom file system handler, or if you want to use a custom http client or + // custom network implementation for a lookup. + // + // libopenapi will pass the path to the FSHandler, and it will be up to the handler to determine how to fetch + // the document. This is really useful if your application has a custom file system or uses a database for storing + // documents. + // + // Is the FSHandler is set, it will be used for all lookups, regardless of whether they are local or remote. + // it also overrides the RemoteURLHandler if set. + // + // Resolves[#85] https://github.com/pb33f/libopenapi/issues/85 + // deprecated: Use the Rolodex instead + FSHandler fs.FS + + // If resolving locally, the BasePath will be the root from which relative references will be resolved from + BasePath string // set the Base Path for resolving relative references if the spec is exploded. + + // SpecFilePath is the name of the root specification file (usually named "openapi.yaml"). + SpecFilePath string + + // In an earlier version of libopenapi (pre 0.6.0) the index would automatically resolve all references + // They could have been local, or they could have been remote. This was a problem because it meant + // There was a potential for a remote exploit if a remote reference was malicious. There aren't any known + // exploits, but it's better to be safe than sorry. + // + // To read more about this, you can find a discussion here: https://github.com/pb33f/libopenapi/pull/64 + AllowRemoteLookup bool // Allow remote lookups for references. Defaults to false + AllowFileLookup bool // Allow file lookups for references. Defaults to false + + // If set to true, the index will not be built out, which means only the foundational elements will be + // parsed and added to the index. This is useful to avoid building out an index if the specification is + // broken up into references and want it fully resolved. + // + // Use the `BuildIndex()` method on the index to build it out once resolved/ready. + AvoidBuildIndex bool + + // If set to true, the index will not check for circular references automatically, this should be triggered + // manually, otherwise resolving may explode. + AvoidCircularReferenceCheck bool + + // Logger is a logger that will be used for logging errors and warnings. If not set, the default logger + // will be used, set to the Error level. + Logger *slog.Logger + + // SpecInfo is a pointer to the SpecInfo struct that contains the root node and the spec version. It's the + // struct that was used to create this index. + SpecInfo *datamodel.SpecInfo + + // Rolodex is what provides all file and remote based lookups. Without the rolodex, no remote or file lookups + // can be used. Normally you won't need to worry about setting this as each root document gets a rolodex + // of its own automatically. + Rolodex *Rolodex + + // The absolute path to the spec file for the index. Will be absolute, either as a http link or a file. + // If the index is for a single file spec, then the root will be empty. + SpecAbsolutePath string + + // IgnorePolymorphicCircularReferences will skip over checking for circular references in polymorphic schemas. + // A polymorphic schema is any schema that is composed other schemas using references via `oneOf`, `anyOf` of `allOf`. + // This is disabled by default, which means polymorphic circular references will be checked. + IgnorePolymorphicCircularReferences bool + + // IgnoreArrayCircularReferences will skip over checking for circular references in arrays. Sometimes a circular + // reference is required to describe a data-shape correctly. Often those shapes are valid circles if the + // type of the schema implementing the loop is an array. An empty array would technically break the loop. + // So if libopenapi is returning circular references for this use case, then this option should be enabled. + // this is disabled by default, which means array circular references will be checked. + IgnoreArrayCircularReferences bool + + // SkipDocumentCheck will skip the document check when building the index. A document check will look for an 'openapi' + // or 'swagger' node in the root of the document. If it's not found, then the document is not a valid OpenAPI or + // the file is a JSON Schema. To allow JSON Schema files to be included set this to true. + SkipDocumentCheck bool + + // SkipExternalRefResolution will skip resolving external $ref references (those not starting with #). + // When enabled, external references will be left as-is during model building. + SkipExternalRefResolution bool + + // ExtractRefsSequentially will extract all references sequentially, which means the index will look up references + // as it finds them, vs looking up everything asynchronously. + // This is a more thorough way of building the index, but it's slower. It's required building a document + // to be bundled. + ExtractRefsSequentially bool + + // ExcludeExtensionReferences will prevent the indexing of any $ref pointers buried under extensions. + // defaults to false (which means extensions will be included) + ExcludeExtensionRefs bool + + // UseSchemaQuickHash will use a quick hash to determine if a schema is the same as another schema if its a reference. + // This is important when a root / entry document does not have a components/schemas node, and schemas are defined in + // external documents. Enabling this will allow the what-changed module to perform deeper schema reference checks. + // -- IMPORTANT -- + // Enabling this (default is false) will stop changes from being detected if a schema is circular. + // As identified in https://github.com/pb33f/libopenapi/pull/441 + // So, in the edge case where you have circular references in your root / entry components/schemas and you also + // want changes in them to be picked up, then you should not enable this. + UseSchemaQuickHash bool + + // AllowUnknownExtensionContentDetection will enable content detection for remote URLs that don't have + // a known file extension. When enabled, libopenapi will fetch the first 1-2KB of unknown URLs to determine + // if they contain valid JSON or YAML content. This is disabled by default for security and performance. + // + // If disabled, URLs without recognized extensions (.yaml, .yml, .json) will be rejected. + // If enabled, unknown URLs will be fetched and analyzed for JSON/YAML content with retry logic. + AllowUnknownExtensionContentDetection bool + + // TransformSiblingRefs enables OpenAPI 3.1/JSON Schema Draft 2020-12 compliance for sibling refs. + // When enabled, schemas with $ref and additional properties will be transformed to use allOf. + TransformSiblingRefs bool + + // MergeReferencedProperties enables merging of properties from referenced schemas with local properties. + // When enabled, properties from referenced schemas will be merged with local sibling properties. + MergeReferencedProperties bool + + // ResolveNestedRefsWithDocumentContext uses the referenced document's path/index as the base for any nested refs. + // This is disabled by default to preserve historical resolver behavior. + ResolveNestedRefsWithDocumentContext bool + + // PropertyMergeStrategy defines how to handle conflicts when merging properties. + PropertyMergeStrategy datamodel.PropertyMergeStrategy + + // private fields + uri []string + id string +} + +// SetTheoreticalRoot sets the spec file paths to point to a theoretical spec file, which does not exist but is required +// +// to formulate the absolute path to root references correctly. +func (s *SpecIndexConfig) SetTheoreticalRoot() { + s.SpecFilePath = filepath.Join(s.BasePath, theoreticalRoot) + + basePath := s.BasePath + if !filepath.IsAbs(basePath) { + basePath, _ = filepath.Abs(basePath) + } + s.SpecAbsolutePath = filepath.Join(basePath, theoreticalRoot) +} + +// GetId returns the id of the SpecIndexConfig. If the id is not set, it will generate a random alphanumeric string +func (s *SpecIndexConfig) GetId() string { + if s.id == "" { + s.id = utils.GenerateAlphanumericString(6) + } + return s.id +} + +// ToDocumentConfiguration converts SpecIndexConfig to DocumentConfiguration for compatibility +func (s *SpecIndexConfig) ToDocumentConfiguration() *datamodel.DocumentConfiguration { + if s == nil { + return nil + } + // default strategy if not set + strategy := s.PropertyMergeStrategy + if strategy == 0 { + strategy = datamodel.PreserveLocal + } + return &datamodel.DocumentConfiguration{ + BaseURL: s.BaseURL, + BasePath: s.BasePath, + SpecFilePath: s.SpecFilePath, + AllowFileReferences: s.AllowFileLookup, + AllowRemoteReferences: s.AllowRemoteLookup, + BypassDocumentCheck: s.SkipDocumentCheck, + IgnorePolymorphicCircularReferences: s.IgnorePolymorphicCircularReferences, + IgnoreArrayCircularReferences: s.IgnoreArrayCircularReferences, + UseSchemaQuickHash: s.UseSchemaQuickHash, + AllowUnknownExtensionContentDetection: s.AllowUnknownExtensionContentDetection, + TransformSiblingRefs: s.TransformSiblingRefs, + MergeReferencedProperties: s.MergeReferencedProperties, + PropertyMergeStrategy: strategy, + SkipExternalRefResolution: s.SkipExternalRefResolution, + Logger: s.Logger, + } +} + +// CreateOpenAPIIndexConfig is a helper function to create a new SpecIndexConfig with the AllowRemoteLookup and +// AllowFileLookup set to true. This is the default behavior of the index in previous versions of libopenapi. (pre 0.6.0) +// +// The default BasePath is the current working directory. +func CreateOpenAPIIndexConfig() *SpecIndexConfig { + return &SpecIndexConfig{ + AllowRemoteLookup: true, + AllowFileLookup: true, + id: utils.GenerateAlphanumericString(6), + } +} + +// CreateClosedAPIIndexConfig is a helper function to create a new SpecIndexConfig with the AllowRemoteLookup and +// AllowFileLookup set to false. This is the default behavior of the index in versions 0.6.0+ +// +// The default BasePath is the current working directory. +func CreateClosedAPIIndexConfig() *SpecIndexConfig { + return &SpecIndexConfig{id: utils.GenerateAlphanumericString(6)} +} + +// SpecIndex is a complete pre-computed index of the entire specification. Numbers are pre-calculated and +// quick direct access to paths, operations, tags are all available. No need to walk the entire node tree in rules, +// everything is pre-walked if you need it. +type SpecIndex struct { + specAbsolutePath string + rolodex *Rolodex // the rolodex is used to fetch remote and file based documents. + allRefs map[string]*Reference // all (deduplicated) refs + rawSequencedRefs []*Reference // all raw references in sequence as they are scanned, not deduped. + linesWithRefs map[int]bool // lines that link to references. + allMappedRefs map[string]*Reference // these are the located mapped refs + allMappedRefsSequenced []*ReferenceMapped // sequenced mapped refs + refsByLine map[string]map[int]bool // every reference and the lines it's referenced from + pathRefs map[string]map[string]*Reference // all path references + paramOpRefs map[string]map[string]map[string][]*Reference // params in operations. + paramCompRefs map[string]*Reference // params in components + paramAllRefs map[string]*Reference // combined components and ops + paramInlineDuplicateNames map[string][]*Reference // inline params all with the same name + globalTagRefs map[string]*Reference // top level global tags + securitySchemeRefs map[string]*Reference // top level security schemes + requestBodiesRefs map[string]*Reference // top level request bodies + responsesRefs map[string]*Reference // top level responses + headersRefs map[string]*Reference // top level responses + examplesRefs map[string]*Reference // top level examples + securityRequirementRefs map[string]map[string][]*Reference // (NOT $ref) but a name based lookup for requirements + callbacksRefs map[string]map[string][]*Reference // all links + linksRefs map[string]map[string][]*Reference // all callbacks + operationTagsRefs map[string]map[string][]*Reference // tags found in operations + operationDescriptionRefs map[string]map[string]*Reference // descriptions in operations. + operationSummaryRefs map[string]map[string]*Reference // summaries in operations + callbackRefs map[string]*Reference // top level callback refs + serversRefs []*Reference // all top level server refs + rootServersNode *yaml.Node // servers root node + opServersRefs map[string]map[string][]*Reference // all operation level server overrides. + polymorphicRefs map[string]*Reference // every reference to a polymorphic ref + polymorphicAllOfRefs []*Reference // every reference to 'allOf' references + polymorphicOneOfRefs []*Reference // every reference to 'oneOf' references + polymorphicAnyOfRefs []*Reference // every reference to 'anyOf' references + externalDocumentsRef []*Reference // all external documents in spec + rootSecurity []*Reference // root security definitions. + rootSecurityNode *yaml.Node // root security node. + refsWithSiblings map[string]Reference // references with sibling elements next to them + pathRefsLock sync.RWMutex // create lock for all refs maps, we want to build data as fast as we can + externalDocumentsCount int // number of externalDocument nodes found + operationTagsCount int // number of unique tags in operations + globalTagsCount int // number of global tags defined + totalTagsCount int // number unique tags in spec + globalLinksCount int // component links + globalCallbacksCount int // component callbacks + pathCount int // number of paths + operationCount int // number of operations + operationParamCount int // number of params defined in operations + componentParamCount int // number of params defined in components + componentsInlineParamUniqueCount int // number of inline params with unique names + componentsInlineParamDuplicateCount int // number of inline params with duplicate names + schemaCount int // number of schemas + refCount int // total ref count + root *yaml.Node // the root document + pathsNode *yaml.Node // paths node + tagsNode *yaml.Node // tags node + parametersNode *yaml.Node // components/parameters node + allParameters map[string]*Reference // all parameters (components/defs) + schemasNode *yaml.Node // components/schemas node + allRefSchemaDefinitions []*Reference // all schemas found that are references. + allInlineSchemaDefinitions []*Reference // all schemas found in document outside of components (openapi) or definitions (swagger). + allInlineSchemaObjectDefinitions []*Reference // all schemas that are objects found in document outside of components (openapi) or definitions (swagger). + allComponentSchemaDefinitions *sync.Map // all schemas found in components (openapi) or definitions (swagger). + securitySchemesNode *yaml.Node // components/securitySchemes node + allSecuritySchemes *sync.Map // all security schemes / definitions. + allComponentSchemas map[string]*Reference // all component schema definitions + allComponentSchemasLock sync.RWMutex // prevent concurrent read writes to the schema file which causes a race condition + requestBodiesNode *yaml.Node // components/requestBodies node + allRequestBodies map[string]*Reference // all request bodies + responsesNode *yaml.Node // components/responses node + allResponses map[string]*Reference // all responses + headersNode *yaml.Node // components/headers node + allHeaders map[string]*Reference // all headers + examplesNode *yaml.Node // components/examples node + allExamples map[string]*Reference // all components examples + linksNode *yaml.Node // components/links node + allLinks map[string]*Reference // all links + callbacksNode *yaml.Node // components/callbacks node + pathItemsNode *yaml.Node // components/pathItems node + allCallbacks map[string]*Reference // all components callbacks + allComponentPathItems map[string]*Reference // all components path items examples + allExternalDocuments map[string]*Reference // all external documents + externalSpecIndex map[string]*SpecIndex // create a primary index of all external specs and componentIds + refErrors []error // errors when indexing references + operationParamErrors []error // errors when indexing parameters + allDescriptions []*DescriptionReference // every single description found in the spec. + allSummaries []*DescriptionReference // every single summary found in the spec. + allEnums []*EnumReference // every single enum found in the spec. + allObjectsWithProperties []*ObjectReference // every single object with properties found in the spec. + enumCount int + descriptionCount int + summaryCount int + refLock sync.RWMutex + nodeMapLock sync.RWMutex + componentLock sync.RWMutex + errorLock sync.RWMutex + circularReferences []*CircularReferenceResult // only available when the resolver has been used. + polyCircularReferences []*CircularReferenceResult // only available when the resolver has been used. + arrayCircularReferences []*CircularReferenceResult // only available when the resolver has been used. + tagCircularReferences []*CircularReferenceResult // tag parent-child circular references for OpenAPI 3.2+ + allowCircularReferences bool // decide if you want to error out, or allow circular references, default is false. + config *SpecIndexConfig // configuration for the index + componentIndexChan chan struct{} + polyComponentIndexChan chan struct{} + resolver *Resolver + resolverLock sync.Mutex + cache *sync.Map + built bool + uri []string + logger *slog.Logger + nodeMap map[int]map[int]*yaml.Node + nodeMapCompleted chan struct{} + pendingResolve []refMap + highModelCache Cache + schemaIdRegistry map[string]*SchemaIdEntry // registry of $id declarations for JSON Schema 2020-12 + schemaIdRegistryLock sync.RWMutex // lock for concurrent access to schemaIdRegistry +} + +// GetResolver returns the resolver for this index. +func (index *SpecIndex) GetResolver() *Resolver { + return index.resolver +} + +func (index *SpecIndex) SetResolver(resolver *Resolver) { + index.resolver = resolver +} + +// GetConfig returns the SpecIndexConfig for this index. +func (index *SpecIndex) GetConfig() *SpecIndexConfig { + return index.config +} + +func (index *SpecIndex) GetNodeMap() map[int]map[int]*yaml.Node { + return index.nodeMap +} + +func (index *SpecIndex) GetCache() *sync.Map { + return index.cache +} + +// SetAbsolutePath sets the absolute path to the spec file for the index. Will be absolute, either as a http link or a file. +func (index *SpecIndex) SetAbsolutePath(absolutePath string) { + index.specAbsolutePath = absolutePath +} + +// GetSpecAbsolutePath returns the absolute path to the spec file for the index. Will be absolute, either as a http link or a file. +func (index *SpecIndex) GetSpecAbsolutePath() string { + return index.specAbsolutePath +} + +// ExternalLookupFunction is for lookup functions that take a JSONSchema reference and tries to find that node in the +// URI based document. Decides if the reference is local, remote or in a file. +type ExternalLookupFunction func(id string) (foundNode *yaml.Node, rootNode *yaml.Node, lookupError error) + +// IndexingError holds data about something that went wrong during indexing. +type IndexingError struct { + Err error + Node *yaml.Node + KeyNode *yaml.Node + Path string +} + +func (i *IndexingError) Error() string { + return i.Err.Error() +} + +// DescriptionReference holds data about a description that was found and where it was found. +type DescriptionReference struct { + Content string + Path string + KeyNode *yaml.Node + Node *yaml.Node + ParentNode *yaml.Node + IsSummary bool +} + +type EnumReference struct { + Node *yaml.Node + KeyNode *yaml.Node + Type *yaml.Node + Path string + SchemaNode *yaml.Node + ParentNode *yaml.Node +} + +type ObjectReference struct { + Node *yaml.Node + KeyNode *yaml.Node + Path string + ParentNode *yaml.Node +} + +var methodTypes = []string{"get", "post", "put", "patch", "options", "head", "delete"} diff --git a/vendor/github.com/pb33f/libopenapi/index/index_utils.go b/vendor/github.com/pb33f/libopenapi/index/index_utils.go new file mode 100644 index 000000000..12b209c50 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/index/index_utils.go @@ -0,0 +1,71 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package index + +import ( + "strings" + "sync" +) + +func isHttpMethod(val string) bool { + switch strings.ToLower(val) { + case methodTypes[0]: + return true + case methodTypes[1]: + return true + case methodTypes[2]: + return true + case methodTypes[3]: + return true + case methodTypes[4]: + return true + case methodTypes[5]: + return true + case methodTypes[6]: + return true + } + return false +} + +func boostrapIndexCollections(index *SpecIndex) { + index.allRefs = make(map[string]*Reference) + index.allMappedRefs = make(map[string]*Reference) + index.refsByLine = make(map[string]map[int]bool) + index.linesWithRefs = make(map[int]bool) + index.pathRefs = make(map[string]map[string]*Reference) + index.paramOpRefs = make(map[string]map[string]map[string][]*Reference) + index.operationTagsRefs = make(map[string]map[string][]*Reference) + index.operationDescriptionRefs = make(map[string]map[string]*Reference) + index.operationSummaryRefs = make(map[string]map[string]*Reference) + index.paramCompRefs = make(map[string]*Reference) + index.paramAllRefs = make(map[string]*Reference) + index.paramInlineDuplicateNames = make(map[string][]*Reference) + index.globalTagRefs = make(map[string]*Reference) + index.securitySchemeRefs = make(map[string]*Reference) + index.requestBodiesRefs = make(map[string]*Reference) + index.responsesRefs = make(map[string]*Reference) + index.headersRefs = make(map[string]*Reference) + index.examplesRefs = make(map[string]*Reference) + index.callbacksRefs = make(map[string]map[string][]*Reference) + index.linksRefs = make(map[string]map[string][]*Reference) + index.callbackRefs = make(map[string]*Reference) + index.externalSpecIndex = make(map[string]*SpecIndex) + index.allComponentSchemaDefinitions = &sync.Map{} + index.allParameters = make(map[string]*Reference) + index.allSecuritySchemes = &sync.Map{} + index.allRequestBodies = make(map[string]*Reference) + index.allResponses = make(map[string]*Reference) + index.allHeaders = make(map[string]*Reference) + index.allExamples = make(map[string]*Reference) + index.allLinks = make(map[string]*Reference) + index.allCallbacks = make(map[string]*Reference) + index.allExternalDocuments = make(map[string]*Reference) + index.securityRequirementRefs = make(map[string]map[string][]*Reference) + index.polymorphicRefs = make(map[string]*Reference) + index.refsWithSiblings = make(map[string]Reference) + index.opServersRefs = make(map[string]map[string][]*Reference) + index.componentIndexChan = make(chan struct{}) + index.polyComponentIndexChan = make(chan struct{}) + index.allComponentPathItems = make(map[string]*Reference) +} diff --git a/vendor/github.com/pb33f/libopenapi/index/map_index_nodes.go b/vendor/github.com/pb33f/libopenapi/index/map_index_nodes.go new file mode 100644 index 000000000..b99593b39 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/index/map_index_nodes.go @@ -0,0 +1,112 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package index + +import ( + "go.yaml.in/yaml/v4" +) + +type nodeMap struct { + line int + column int + node *yaml.Node +} + +// NodeOrigin represents where a node has come from within a specification. This is not useful for single file specs, +// but becomes very, very important when dealing with exploded specifications, and we need to know where in the mass +// of files a node has come from. +type NodeOrigin struct { + // Node is the node in question (defaults to key node) + Node *yaml.Node `json:"-"` + + // ValueNode is the value node of the node in question, if has a different origin + ValueNode *yaml.Node `json:"-"` + + // Line is yhe original line of where the node was found in the original file + Line int `json:"line" yaml:"line"` + + // Column is the original column of where the node was found in the original file + Column int `json:"column" yaml:"column"` + + // LineValue is the line of the value (if the origin of the key and value are different) + LineValue int `json:"lineValue,omitempty" yaml:"lineValue,omitempty"` + + // ColumnValue is the line of the value (if the origin of the key and value are different) + ColumnValue int `json:"columnKey,omitempty" yaml:"columnKey,omitempty"` + + // AbsoluteLocation is the absolute path to the reference was extracted from. + // This can either be an absolute path to a file, or a URL. + AbsoluteLocation string `json:"absoluteLocation" yaml:"absoluteLocation"` + + // AbsoluteLocationValue is the absolute path to where the ValueNode was extracted from. + // this only applies when keys and values have different origins. + AbsoluteLocationValue string `json:"absoluteLocationValue,omitempty" yaml:"absoluteLocationValue,omitempty"` + + // Index is the index that contains the node that was located in. + Index *SpecIndex `json:"-" yaml:"-"` +} + +// GetNode returns a node from the spec based on a line and column. The second return var bool is true +// if the node was found, false if not. +func (index *SpecIndex) GetNode(line int, column int) (*yaml.Node, bool) { + index.nodeMapLock.RLock() + if index.nodeMap[line] == nil { + return nil, false + } + node := index.nodeMap[line][column] + index.nodeMapLock.RUnlock() + return node, node != nil +} + +// MapNodes maps all nodes in the document to a map of line/column to node. +func (index *SpecIndex) MapNodes(rootNode *yaml.Node) { + cruising := make(chan struct{}) + nodeChan := make(chan *nodeMap) + go func(nodeChan chan *nodeMap) { + done := false + for !done { + node, ok := <-nodeChan + if !ok { + done = true + cruising <- struct{}{} + return + } + index.nodeMapLock.Lock() + if index.nodeMap[node.line] == nil { + index.nodeMap[node.line] = make(map[int]*yaml.Node) + } + index.nodeMap[node.line][node.column] = node.node + index.nodeMapLock.Unlock() + } + }(nodeChan) + go enjoyALuxuryCruise(rootNode, nodeChan, true) + <-cruising + close(cruising) + index.nodeMapCompleted <- struct{}{} + close(index.nodeMapCompleted) +} + +func enjoyALuxuryCruise(node *yaml.Node, nodeChan chan *nodeMap, root bool) { + if node.Kind == yaml.DocumentNode { + node = node.Content[0] + } + if len(node.Content) > 0 { + for _, child := range node.Content { + nodeChan <- &nodeMap{ + line: child.Line, + column: child.Column, + node: child, + } + enjoyALuxuryCruise(child, nodeChan, false) + } + } + nodeChan <- &nodeMap{ + line: node.Line, + column: node.Column, + node: node, + } + if root { + close(nodeChan) + } +} diff --git a/vendor/github.com/pb33f/libopenapi/index/path_resolution.go b/vendor/github.com/pb33f/libopenapi/index/path_resolution.go new file mode 100644 index 000000000..d33d1e5fc --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/index/path_resolution.go @@ -0,0 +1,118 @@ +// Copyright 2026 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package index + +import ( + "io/fs" + "os" + "path/filepath" + "sort" + "strings" + + "github.com/pb33f/libopenapi/utils" +) + +// resolveRelativeFilePath resolves a relative file reference against a base directory. +// It prefers paths that actually exist in the configured local file systems, falling +// back to defRoot if no match is found. +func (index *SpecIndex) resolveRelativeFilePath(defRoot, ref string) string { + sep := string(os.PathSeparator) + resolveAbs := func(base string) string { + p := utils.CheckPathOverlap(base, ref, sep) + abs, _ := filepath.Abs(p) + return abs + } + + fallback := resolveAbs(defRoot) + + if index == nil || index.config == nil || index.config.BaseURL != nil || index.rolodex == nil { + return fallback + } + + // Prefer the path relative to the current file if it exists. + if len(index.rolodex.localFS) > 0 { + bases := make([]string, 0, len(index.rolodex.localFS)) + for base := range index.rolodex.localFS { + bases = append(bases, base) + } + sort.Strings(bases) + for _, base := range bases { + if pathExistsInFS(base, index.rolodex.localFS[base], fallback) { + return fallback + } + } + } + + // Prefer the configured BasePath if present and it yields an existing file. + if index.config.BasePath != "" { + baseAbs, _ := filepath.Abs(index.config.BasePath) + if fsys, ok := index.rolodex.localFS[baseAbs]; ok { + cand := resolveAbs(baseAbs) + if pathExistsInFS(baseAbs, fsys, cand) { + return cand + } + } + } + + // Otherwise, try each registered local filesystem base directory. + if len(index.rolodex.localFS) > 0 { + bases := make([]string, 0, len(index.rolodex.localFS)) + for base := range index.rolodex.localFS { + bases = append(bases, base) + } + sort.Strings(bases) + for _, base := range bases { + cand := resolveAbs(base) + if pathExistsInFS(base, index.rolodex.localFS[base], cand) { + return cand + } + } + } + + return fallback +} + +// ResolveRelativeFilePath is a public wrapper for resolving local file references. +func (index *SpecIndex) ResolveRelativeFilePath(defRoot, ref string) string { + return index.resolveRelativeFilePath(defRoot, ref) +} + +func pathExistsInFS(baseDir string, fsys fs.FS, absPath string) bool { + if !filepath.IsAbs(absPath) { + absPath, _ = filepath.Abs(utils.CheckPathOverlap(baseDir, absPath, string(os.PathSeparator))) + } + + if lfs, ok := fsys.(*LocalFS); ok { + if lfs.fsConfig != nil && lfs.fsConfig.DirFS != nil { + rel, err := filepath.Rel(baseDir, absPath) + if err != nil { + return false + } + rel = filepath.ToSlash(rel) + if !fs.ValidPath(rel) || rel == "." || rel == ".." || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) { + return false + } + _, err = fs.Stat(lfs.fsConfig.DirFS, rel) + return err == nil + } + + rel, err := filepath.Rel(baseDir, absPath) + if err != nil || rel == "." || rel == ".." || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) { + return false + } + _, err = os.Stat(absPath) + return err == nil + } + + rel, err := filepath.Rel(baseDir, absPath) + if err != nil { + return false + } + rel = filepath.ToSlash(rel) + if !fs.ValidPath(rel) || rel == "." || rel == ".." || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) { + return false + } + _, err = fs.Stat(fsys, rel) + return err == nil +} diff --git a/vendor/github.com/pb33f/libopenapi/index/resolver.go b/vendor/github.com/pb33f/libopenapi/index/resolver.go new file mode 100644 index 000000000..7a0fcba56 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/index/resolver.go @@ -0,0 +1,1037 @@ +// Copyright 2022 Dave Shanley / Quobix +// SPDX-License-Identifier: MIT + +package index + +import ( + "context" + "errors" + "fmt" + "net/url" + "path" + "path/filepath" + "slices" + "sort" + "strings" + + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// ResolvingError represents an issue the resolver had trying to stitch the tree together. +type ResolvingError struct { + // ErrorRef is the error thrown by the resolver + ErrorRef error + + // Node is the *yaml.Node reference that contains the resolving error + Node *yaml.Node + + // Path is the shortened journey taken by the resolver + Path string + + // CircularReference is set if the error is a reference to the circular reference. + CircularReference *CircularReferenceResult +} + +func (r *ResolvingError) Error() string { + errs := utils.UnwrapErrors(r.ErrorRef) + var msgs []string + for _, e := range errs { + var idxErr *IndexingError + if errors.As(e, &idxErr) { + msgs = append(msgs, fmt.Sprintf("%s: %s [%d:%d]", idxErr.Error(), + idxErr.Path, idxErr.Node.Line, idxErr.Node.Column)) + } else { + var l, c int + if r.Node != nil { + l = r.Node.Line + c = r.Node.Column + } + msgs = append(msgs, fmt.Sprintf("%s: %s [%d:%d]", e.Error(), + r.Path, l, c)) + } + } + return strings.Join(msgs, "\n") +} + +// Resolver will use a *index.SpecIndex to stitch together a resolved root tree using all the discovered +// references in the doc. +type Resolver struct { + specIndex *SpecIndex + resolvedRoot *yaml.Node + resolvingErrors []*ResolvingError + circularReferences []*CircularReferenceResult + ignoredPolyReferences []*CircularReferenceResult + ignoredArrayReferences []*CircularReferenceResult + referencesVisited int + indexesVisited int + journeysTaken int + relativesSeen int + IgnorePoly bool + IgnoreArray bool + circChecked bool +} + +// NewResolver will create a new resolver from a *index.SpecIndex +func NewResolver(index *SpecIndex) *Resolver { + if index == nil { + return nil + } + r := &Resolver{ + specIndex: index, + resolvedRoot: index.GetRootNode(), + } + index.resolver = r + return r +} + +// GetIgnoredCircularPolyReferences returns all ignored circular references that are polymorphic +func (resolver *Resolver) GetIgnoredCircularPolyReferences() []*CircularReferenceResult { + return resolver.ignoredPolyReferences +} + +// GetIgnoredCircularArrayReferences returns all ignored circular references that are arrays +func (resolver *Resolver) GetIgnoredCircularArrayReferences() []*CircularReferenceResult { + return resolver.ignoredArrayReferences +} + +// GetResolvingErrors returns all errors found during resolving +func (resolver *Resolver) GetResolvingErrors() []*ResolvingError { + return resolver.resolvingErrors +} + +func (resolver *Resolver) GetCircularReferences() []*CircularReferenceResult { + return resolver.GetSafeCircularReferences() +} + +// GetSafeCircularReferences returns all circular reference errors found. +func (resolver *Resolver) GetSafeCircularReferences() []*CircularReferenceResult { + var refs []*CircularReferenceResult + for _, ref := range resolver.circularReferences { + if !ref.IsInfiniteLoop { + refs = append(refs, ref) + } + } + return refs +} + +// GetInfiniteCircularReferences returns all circular reference errors found that are infinite / unrecoverable +func (resolver *Resolver) GetInfiniteCircularReferences() []*CircularReferenceResult { + var refs []*CircularReferenceResult + for _, ref := range resolver.circularReferences { + if ref.IsInfiniteLoop { + refs = append(refs, ref) + } + } + return refs +} + +// GetPolymorphicCircularErrors returns all circular errors that stem from polymorphism +func (resolver *Resolver) GetPolymorphicCircularErrors() []*CircularReferenceResult { + var res []*CircularReferenceResult + for i := range resolver.circularReferences { + if !resolver.circularReferences[i].IsInfiniteLoop { + continue + } + if !resolver.circularReferences[i].IsPolymorphicResult { + continue + } + res = append(res, resolver.circularReferences[i]) + } + return res +} + +// GetNonPolymorphicCircularErrors returns all circular errors that DO NOT stem from polymorphism +func (resolver *Resolver) GetNonPolymorphicCircularErrors() []*CircularReferenceResult { + var res []*CircularReferenceResult + for i := range resolver.circularReferences { + if !resolver.circularReferences[i].IsInfiniteLoop { + continue + } + + if !resolver.circularReferences[i].IsPolymorphicResult { + res = append(res, resolver.circularReferences[i]) + } + } + return res +} + +// IgnorePolymorphicCircularReferences will ignore any circular references that are polymorphic (oneOf, anyOf, allOf) +// This must be set before any resolving is done. +func (resolver *Resolver) IgnorePolymorphicCircularReferences() { + resolver.IgnorePoly = true +} + +// IgnoreArrayCircularReferences will ignore any circular references that stem from arrays. This must be set before +// any resolving is done. +func (resolver *Resolver) IgnoreArrayCircularReferences() { + resolver.IgnoreArray = true +} + +// GetJourneysTaken returns the number of journeys taken by the resolver +func (resolver *Resolver) GetJourneysTaken() int { + return resolver.journeysTaken +} + +// GetReferenceVisited returns the number of references visited by the resolver +func (resolver *Resolver) GetReferenceVisited() int { + return resolver.referencesVisited +} + +// GetIndexesVisited returns the number of indexes visited by the resolver +func (resolver *Resolver) GetIndexesVisited() int { + return resolver.indexesVisited +} + +// GetRelativesSeen returns the number of siblings (nodes at the same level) seen for each reference found. +func (resolver *Resolver) GetRelativesSeen() int { + return resolver.relativesSeen +} + +// Resolve will resolve the specification, everything that is not polymorphic and not circular, will be resolved. +// this data can get big, it results in a massive duplication of data. This is a destructive method and will permanently +// re-organize the node tree. Make sure you have copied your original tree before running this (if you want to preserve +// original data) +func (resolver *Resolver) Resolve() []*ResolvingError { + visitIndex(resolver, resolver.specIndex) + + for _, circRef := range resolver.circularReferences { + // If the circular reference is not required, we can ignore it, as it's a terminable loop rather than an infinite one + if !circRef.IsInfiniteLoop { + continue + } + + if !resolver.circChecked { + resolver.resolvingErrors = append(resolver.resolvingErrors, &ResolvingError{ + ErrorRef: fmt.Errorf("infinite circular reference detected: %s", circRef.Start.Definition), + Node: circRef.ParentNode, + Path: circRef.GenerateJourneyPath(), + CircularReference: circRef, + }) + } + } + resolver.specIndex.SetCircularReferences(resolver.circularReferences) + resolver.specIndex.SetIgnoredArrayCircularReferences(resolver.ignoredArrayReferences) + resolver.specIndex.SetIgnoredPolymorphicCircularReferences(resolver.ignoredPolyReferences) + resolver.circChecked = true + return resolver.resolvingErrors +} + +// CheckForCircularReferences Check for circular references, without resolving, a non-destructive run. +func (resolver *Resolver) CheckForCircularReferences() []*ResolvingError { + visitIndexWithoutDamagingIt(resolver, resolver.specIndex) + for _, circRef := range resolver.circularReferences { + // If the circular reference is not required, we can ignore it, as it's a terminable loop rather than an infinite one + if !circRef.IsInfiniteLoop { + continue + } + if !resolver.circChecked { + resolver.resolvingErrors = append(resolver.resolvingErrors, &ResolvingError{ + ErrorRef: fmt.Errorf("infinite circular reference detected: %s", circRef.Start.Name), + Node: circRef.ParentNode, + Path: circRef.GenerateJourneyPath(), + CircularReference: circRef, + }) + } + } + // update our index with any circular refs we found. + resolver.specIndex.SetCircularReferences(resolver.circularReferences) + resolver.specIndex.SetIgnoredArrayCircularReferences(resolver.ignoredArrayReferences) + resolver.specIndex.SetIgnoredPolymorphicCircularReferences(resolver.ignoredPolyReferences) + resolver.circChecked = true + return resolver.resolvingErrors +} + +func visitIndexWithoutDamagingIt(res *Resolver, idx *SpecIndex) { + mapped := idx.GetMappedReferencesSequenced() + mappedIndex := idx.GetMappedReferences() + res.indexesVisited++ + for _, ref := range mapped { + seenReferences := make(map[string]bool) + var journey []*Reference + res.journeysTaken++ + res.VisitReference(ref.Reference, seenReferences, journey, false) + } + + // Sort schema keys for deterministic iteration order + schemas := idx.GetAllComponentSchemas() + schemaKeys := make([]string, 0, len(schemas)) + for k := range schemas { + schemaKeys = append(schemaKeys, k) + } + sort.Strings(schemaKeys) + + for _, s := range schemaKeys { + schemaRef := schemas[s] + if mappedIndex[s] == nil { + seenReferences := make(map[string]bool) + var journey []*Reference + res.journeysTaken++ + res.VisitReference(schemaRef, seenReferences, journey, false) + } + } +} + +type refMap struct { + ref *Reference + nodes []*yaml.Node +} + +func visitIndex(res *Resolver, idx *SpecIndex) { + mapped := idx.GetMappedReferencesSequenced() + mappedIndex := idx.GetMappedReferences() + res.indexesVisited++ + + var refs []refMap + for _, ref := range mapped { + seenReferences := make(map[string]bool) + var journey []*Reference + res.journeysTaken++ + if ref != nil && ref.Reference != nil { + n := res.VisitReference(ref.Reference, seenReferences, journey, true) + if !ref.Reference.Circular { + // make a note of the reference and map the original ref after we're done + if ok, _, _ := utils.IsNodeRefValue(ref.OriginalReference.Node); ok { + refs = append(refs, refMap{ + ref: ref.OriginalReference, + nodes: n, + }) + } + } + } + } + idx.pendingResolve = refs + + // Sort schema keys for deterministic iteration order + schemas := idx.GetAllComponentSchemas() + schemaKeys := make([]string, 0, len(schemas)) + for k := range schemas { + schemaKeys = append(schemaKeys, k) + } + sort.Strings(schemaKeys) + + for _, s := range schemaKeys { + schemaRef := schemas[s] + if mappedIndex[s] == nil { + seenReferences := make(map[string]bool) + var journey []*Reference + res.journeysTaken++ + schemaRef.Node.Content = res.VisitReference(schemaRef, seenReferences, journey, true) + } + } + + // Sort security scheme keys for deterministic iteration order + securitySchemes := idx.GetAllSecuritySchemes() + securityKeys := make([]string, 0, len(securitySchemes)) + for k := range securitySchemes { + securityKeys = append(securityKeys, k) + } + sort.Strings(securityKeys) + + for _, s := range securityKeys { + schemaRef := securitySchemes[s] + if mappedIndex[s] == nil { + seenReferences := make(map[string]bool) + var journey []*Reference + res.journeysTaken++ + schemaRef.Node.Content = res.VisitReference(schemaRef, seenReferences, journey, true) + } + } + + // map everything + for _, sequenced := range idx.GetAllSequencedReferences() { + locatedDef := mappedIndex[sequenced.FullDefinition] + if locatedDef != nil { + if !locatedDef.Circular { + sequenced.Node.Content = locatedDef.Node.Content + } + } + } +} + +// searchReferenceWithContext resolves a reference using document context when enabled in the config. +func (resolver *Resolver) searchReferenceWithContext(sourceRef, searchRef *Reference) (*Reference, *SpecIndex, context.Context) { + if resolver.specIndex == nil || resolver.specIndex.config == nil || !resolver.specIndex.config.ResolveNestedRefsWithDocumentContext { + ref, idx := resolver.specIndex.SearchIndexForReferenceByReference(searchRef) + return ref, idx, context.Background() + } + + searchIndex := resolver.specIndex + if searchRef != nil && searchRef.Index != nil { + searchIndex = searchRef.Index + } else if sourceRef != nil && sourceRef.Index != nil { + searchIndex = sourceRef.Index + } + + ctx := context.Background() + currentPath := "" + if sourceRef != nil { + currentPath = sourceRef.RemoteLocation + } + if currentPath == "" && searchIndex != nil { + currentPath = searchIndex.specAbsolutePath + } + if currentPath != "" { + ctx = context.WithValue(ctx, CurrentPathKey, currentPath) + } + if searchRef != nil || sourceRef != nil { + base := "" + if searchRef != nil && searchRef.SchemaIdBase != "" { + base = searchRef.SchemaIdBase + } else if sourceRef != nil && sourceRef.SchemaIdBase != "" { + base = sourceRef.SchemaIdBase + } + if base != "" { + scope := NewSchemaIdScope(base) + scope.PushId(base) + ctx = WithSchemaIdScope(ctx, scope) + } + } + + return searchIndex.SearchIndexForReferenceByReferenceWithContext(ctx, searchRef) +} + +// VisitReference will visit a reference as part of a journey and will return resolved nodes. +func (resolver *Resolver) VisitReference(ref *Reference, seen map[string]bool, journey []*Reference, resolve bool) []*yaml.Node { + resolver.referencesVisited++ + if resolve && ref != nil && ref.Seen { + if ref.Resolved { + return ref.Node.Content + } + } + if !resolve && ref != nil && ref.Seen { + return ref.Node.Content + } + if ref != nil { + journey = append(journey, ref) + seenRelatives := make(map[int]bool) + base := resolver.resolveSchemaIdBase(ref.SchemaIdBase, ref.Node) + relatives := resolver.extractRelatives(ref, ref.Node, nil, seen, journey, seenRelatives, resolve, 0, base) + + seen = make(map[string]bool) + + seen[ref.FullDefinition] = true + for _, r := range relatives { + // check if we have seen this on the journey before, if so! it's circular + skip := false + for i, j := range journey { + if j.FullDefinition == r.FullDefinition { + + var foundDup *Reference + foundRef, _, _ := resolver.searchReferenceWithContext(ref, r) + if foundRef != nil { + foundDup = foundRef + } + + var circRef *CircularReferenceResult + if !foundDup.Circular { + loop := append(journey, foundDup) + + visitedDefinitions := make(map[string]bool) + isInfiniteLoop, _ := resolver.isInfiniteCircularDependency(foundDup, + visitedDefinitions, nil) + + isArray := false + if r.ParentNodeSchemaType == "array" || slices.Contains(r.ParentNodeTypes, "array") { + isArray = true + } + circRef = &CircularReferenceResult{ + ParentNode: foundDup.ParentNode, + Journey: loop, + Start: foundDup, + LoopIndex: i, + LoopPoint: foundDup, + IsArrayResult: isArray, + IsInfiniteLoop: isInfiniteLoop, + } + + if resolver.IgnorePoly && !isArray { + resolver.ignoredPolyReferences = append(resolver.ignoredPolyReferences, circRef) + } else if resolver.IgnoreArray && isArray { + resolver.ignoredArrayReferences = append(resolver.ignoredArrayReferences, circRef) + } else { + if !resolver.circChecked { + resolver.circularReferences = append(resolver.circularReferences, circRef) + } + } + r.Seen = true + r.Circular = true + foundDup.Seen = true + foundDup.Circular = true + } + skip = true + } + } + + if !skip { + var original *Reference + foundRef, _, _ := resolver.searchReferenceWithContext(ref, r) + if foundRef != nil { + original = foundRef + } + resolved := resolver.VisitReference(original, seen, journey, resolve) + if resolve && !original.Circular { + ref.Resolved = true + r.Resolved = true + r.Node.Content = resolved // this is where we perform the actual resolving. + } + r.Seen = true + ref.Seen = true + } + } + + ref.Seen = true + + if ref.Node != nil { + return ref.Node.Content + } + } + return nil +} + +func (resolver *Resolver) isInfiniteCircularDependency(ref *Reference, visitedDefinitions map[string]bool, + initialRef *Reference, +) (bool, map[string]bool) { + if ref == nil { + return false, visitedDefinitions + } + for refDefinition := range ref.RequiredRefProperties { + r, _ := resolver.specIndex.SearchIndexForReference(refDefinition) + if initialRef != nil && initialRef.FullDefinition == r.FullDefinition { + return true, visitedDefinitions + } + if len(visitedDefinitions) > 0 && ref.FullDefinition == r.FullDefinition { + return true, visitedDefinitions + } + + if visitedDefinitions[r.FullDefinition] { + continue + } + + visitedDefinitions[r.FullDefinition] = true + + ir := initialRef + if ir == nil { + ir = ref + } + + var isChildICD bool + + isChildICD, visitedDefinitions = resolver.isInfiniteCircularDependency(r, visitedDefinitions, ir) + if isChildICD { + return true, visitedDefinitions + } + } + + return false, visitedDefinitions +} + +func (resolver *Resolver) extractRelatives(ref *Reference, node, parent *yaml.Node, + foundRelatives map[string]bool, + journey []*Reference, seen map[int]bool, resolve bool, depth int, schemaIdBase string, +) []*Reference { + if len(journey) > 100 { + return nil + } + + // this is a safety check to prevent a stack overflow. + if depth > 500 { + def := "unknown" + if ref != nil { + def = ref.FullDefinition + } + if resolver.specIndex != nil && resolver.specIndex.logger != nil { + resolver.specIndex.logger.Warn("libopenapi resolver: relative depth exceeded 100 levels, "+ + "check for circular references - resolving may be incomplete", + "reference", def) + } + + loop := append(journey, ref) + circRef := &CircularReferenceResult{ + Journey: loop, + Start: ref, + LoopIndex: depth, + LoopPoint: ref, + IsInfiniteLoop: true, + } + if !resolver.circChecked { + resolver.circularReferences = append(resolver.circularReferences, circRef) + ref.Circular = true + } + return nil + } + + currentBase := resolver.resolveSchemaIdBase(schemaIdBase, node) + var found []*Reference + + if node != nil && len(node.Content) > 0 { + skip := false + for i, n := range node.Content { + if skip { + skip = false + continue + } + if utils.IsNodeMap(n) || utils.IsNodeArray(n) { + depth++ + + var foundRef *Reference + foundRef, _, _ = resolver.searchReferenceWithContext(ref, ref) + if foundRef != nil && !foundRef.Circular { + found = append(found, resolver.extractRelatives(foundRef, n, node, foundRelatives, journey, seen, resolve, depth, currentBase)...) + depth-- + } + if foundRef == nil { + found = append(found, resolver.extractRelatives(ref, n, node, foundRelatives, journey, seen, resolve, depth, currentBase)...) + depth-- + } + + } + + if i%2 == 0 && n.Value == "$ref" && len(node.Content) > i%2+1 { + + if !utils.IsNodeStringValue(node.Content[i+1]) { + continue + } + // issue #481 cannot look at an array value, the next not is not the value! + if utils.IsNodeArray(node) { + continue + } + + value := node.Content[i+1].Value + value = strings.ReplaceAll(value, "\\\\", "\\") + + // If SkipExternalRefResolution is enabled, skip external refs entirely + if resolver.specIndex != nil && resolver.specIndex.config != nil && + resolver.specIndex.config.SkipExternalRefResolution && utils.IsExternalRef(value) { + skip = true + continue + } + + var locatedRef *Reference + var fullDef string + var definition string + + // explode value + exp := strings.Split(value, "#/") + if len(exp) == 2 { + definition = fmt.Sprintf("#/%s", exp[1]) + if exp[0] != "" { + if strings.HasPrefix(exp[0], "http") { + fullDef = value + } else { + if strings.HasPrefix(ref.FullDefinition, "http") { + + // split the http URI into parts + httpExp := strings.Split(ref.FullDefinition, "#/") + + u, _ := url.Parse(httpExp[0]) + abs, _ := filepath.Abs(utils.CheckPathOverlap(path.Dir(u.Path), exp[0], string(filepath.Separator))) + u.Path = utils.ReplaceWindowsDriveWithLinuxPath(abs) + u.Fragment = "" + fullDef = fmt.Sprintf("%s#/%s", u.String(), exp[1]) + + } else { + + // split the referring ref full def into parts + fileDef := strings.Split(ref.FullDefinition, "#/") + + // extract the location of the ref and build a full def path. + abs := resolver.resolveLocalRefPath(filepath.Dir(fileDef[0]), exp[0]) + // abs = utils.ReplaceWindowsDriveWithLinuxPath(abs) + fullDef = fmt.Sprintf("%s#/%s", abs, exp[1]) + + } + } + } else { + // local component, full def is based on passed in ref + baseLocation := ref.FullDefinition + if ref.RemoteLocation != "" { + baseLocation = ref.RemoteLocation + } + if strings.HasPrefix(baseLocation, "http") { + + // split the http URI into parts + httpExp := strings.Split(baseLocation, "#/") + + // parse a URL from the full def + u, _ := url.Parse(httpExp[0]) + + // extract the location of the ref and build a full def path. + fullDef = fmt.Sprintf("%s#/%s", u.String(), exp[1]) + + } else { + // split the full def into parts + fileDef := strings.Split(baseLocation, "#/") + fullDef = fmt.Sprintf("%s#/%s", fileDef[0], exp[1]) + } + } + } else { + + definition = value + + // if the reference is a http link + if strings.HasPrefix(value, "http") { + fullDef = value + } else { + + // split the full def into parts + baseLocation := ref.FullDefinition + if ref.RemoteLocation != "" { + baseLocation = ref.RemoteLocation + } + fileDef := strings.Split(baseLocation, "#/") + + // is the file def a http link? + if strings.HasPrefix(fileDef[0], "http") { + u, _ := url.Parse(fileDef[0]) + absPath, _ := filepath.Abs(utils.CheckPathOverlap(path.Dir(u.Path), exp[0], string(filepath.Separator))) + u.Path = utils.ReplaceWindowsDriveWithLinuxPath(absPath) + fullDef = u.String() + + } else { + fullDef = resolver.resolveLocalRefPath(filepath.Dir(fileDef[0]), exp[0]) + } + + } + } + + if currentBase != "" { + fullDef = resolveRefWithSchemaBase(value, currentBase) + } + + searchRef := &Reference{ + Definition: definition, + FullDefinition: fullDef, + RawRef: value, + SchemaIdBase: currentBase, + RemoteLocation: ref.RemoteLocation, + IsRemote: true, + Index: ref.Index, + } + + locatedRef, _, _ = resolver.searchReferenceWithContext(ref, searchRef) + + if locatedRef == nil { + _, path := utils.ConvertComponentIdIntoFriendlyPathSearch(value) + err := &ResolvingError{ + ErrorRef: fmt.Errorf("cannot resolve reference `%s`, it's missing", value), + Node: n, + Path: path, + } + resolver.resolvingErrors = append(resolver.resolvingErrors, err) + continue + } + + if resolve { + // if this is a reference also, we want to resolve it. + if ok, _, _ := utils.IsNodeRefValue(ref.Node); ok { + ref.Node.Content = locatedRef.Node.Content + ref.Resolved = true + } + } + + schemaType := "" + if parent != nil { + _, arrayTypevn := utils.FindKeyNodeTop("type", parent.Content) + if arrayTypevn != nil { + if arrayTypevn.Value == "array" { + schemaType = "array" + } + } + } + if ref.ParentNodeSchemaType != "" { + locatedRef.ParentNodeTypes = append(locatedRef.ParentNodeTypes, ref.ParentNodeSchemaType) + } + locatedRef.ParentNodeSchemaType = schemaType + found = append(found, locatedRef) + foundRelatives[value] = true + } + + if i%2 == 0 && n.Value != "$ref" && n.Value != "" { + // Check if we're inside a properties object + isInsideProperties := false + if parent != nil { + for j := 0; j < len(parent.Content); j += 2 { + if j < len(parent.Content) && parent.Content[j].Value == "properties" { + isInsideProperties = true + break + } + } + } + + // Only treat as polymorphic keywords if not inside properties + if !isInsideProperties && (n.Value == "allOf" || n.Value == "oneOf" || n.Value == "anyOf") { + + // if this is a polymorphic link, we want to follow it and see if it becomes circular + if i+1 < len(node.Content) && utils.IsNodeMap(node.Content[i+1]) { // check for nested items + // check if items is present, to indicate an array + if k, v := utils.FindKeyNodeTop("items", node.Content[i+1].Content); v != nil { + if utils.IsNodeMap(v) { + if d, _, l := utils.IsNodeRefValue(v); d { + + // create full definition lookup based on ref. + def := resolver.buildDefPathWithSchemaBase(ref, l, currentBase) + + mappedRefs, _ := resolver.specIndex.SearchIndexForReference(def) + if mappedRefs != nil && !mappedRefs.Circular { + circ := false + for f := range journey { + if journey[f].FullDefinition == mappedRefs.FullDefinition { + circ = true + break + } + } + if !circ { + resolver.VisitReference(mappedRefs, foundRelatives, journey, resolve) + } else { + loop := append(journey, mappedRefs) + circRef := &CircularReferenceResult{ + ParentNode: k, + Journey: loop, + Start: mappedRefs, + LoopIndex: i, + LoopPoint: mappedRefs, + PolymorphicType: n.Value, + IsPolymorphicResult: true, + } + + mappedRefs.Seen = true + mappedRefs.Circular = true + if resolver.IgnorePoly { + resolver.ignoredPolyReferences = append(resolver.ignoredPolyReferences, circRef) + } else { + if !resolver.circChecked { + resolver.circularReferences = append(resolver.circularReferences, circRef) + } + } + } + } + } + } + } else { + // no items discovered, continue on and investigate anyway. + v := node.Content[i+1] + if utils.IsNodeMap(v) { + if d, _, l := utils.IsNodeRefValue(v); d { + + // create full definition lookup based on ref. + def := resolver.buildDefPathWithSchemaBase(ref, l, currentBase) + + mappedRefs, _ := resolver.specIndex.SearchIndexForReference(def) + if mappedRefs != nil && !mappedRefs.Circular { + circ := false + for f := range journey { + if journey[f].FullDefinition == mappedRefs.FullDefinition { + circ = true + break + } + } + if !circ { + resolver.VisitReference(mappedRefs, foundRelatives, journey, resolve) + } else { + loop := append(journey, mappedRefs) + circRef := &CircularReferenceResult{ + ParentNode: node.Content[i], + Journey: loop, + Start: mappedRefs, + LoopIndex: i, + LoopPoint: mappedRefs, + PolymorphicType: n.Value, + IsPolymorphicResult: true, + } + + mappedRefs.Seen = true + mappedRefs.Circular = true + if resolver.IgnorePoly { + resolver.ignoredPolyReferences = append(resolver.ignoredPolyReferences, circRef) + } else { + if !resolver.circChecked { + resolver.circularReferences = append(resolver.circularReferences, circRef) + } + } + } + } + } + } + } + } + // for array based polymorphic items + if i+1 < len(node.Content) && utils.IsNodeArray(node.Content[i+1]) { // check for nested items + for q := range node.Content[i+1].Content { + v := node.Content[i+1].Content[q] + if utils.IsNodeMap(v) { + if d, _, l := utils.IsNodeRefValue(v); d { + def := resolver.buildDefPathWithSchemaBase(ref, l, currentBase) + mappedRefs, _ := resolver.specIndex.SearchIndexForReference(def) + if mappedRefs != nil && !mappedRefs.Circular { + circ := false + for f := range journey { + if journey[f].FullDefinition == mappedRefs.FullDefinition { + circ = true + break + } + } + if !circ { + resolver.VisitReference(mappedRefs, foundRelatives, journey, resolve) + } else { + loop := append(journey, mappedRefs) + + circRef := &CircularReferenceResult{ + ParentNode: node.Content[i], + Journey: loop, + Start: mappedRefs, + LoopIndex: i, + LoopPoint: mappedRefs, + PolymorphicType: n.Value, + IsPolymorphicResult: true, + } + + mappedRefs.Seen = true + mappedRefs.Circular = true + if resolver.IgnorePoly { + resolver.ignoredPolyReferences = append(resolver.ignoredPolyReferences, circRef) + } else { + if !resolver.circChecked { + resolver.circularReferences = append(resolver.circularReferences, circRef) + } + } + } + } + } else { + depth++ + found = append(found, resolver.extractRelatives(ref, v, n, + foundRelatives, journey, seen, resolve, depth, currentBase)...) + } + } + } + } + skip = true + continue + } + } + } + } + resolver.relativesSeen += len(found) + return found +} + +func (resolver *Resolver) buildDefPath(ref *Reference, l string) string { + def := "" + exp := strings.Split(l, "#/") + if len(exp) == 2 { + if exp[0] != "" { + if !strings.HasPrefix(exp[0], "http") { + if !filepath.IsAbs(exp[0]) { + if strings.HasPrefix(ref.FullDefinition, "http") { + + u, _ := url.Parse(ref.FullDefinition) + p, _ := filepath.Abs(utils.CheckPathOverlap(path.Dir(u.Path), exp[0], string(filepath.Separator))) + u.Path = utils.ReplaceWindowsDriveWithLinuxPath(p) + def = fmt.Sprintf("%s#/%s", u.String(), exp[1]) + + } else { + z := strings.Split(ref.FullDefinition, "#/") + if len(z) == 2 { + if len(z[0]) > 0 { + abs := resolver.resolveLocalRefPath(filepath.Dir(z[0]), exp[0]) + def = fmt.Sprintf("%s#/%s", abs, exp[1]) + } else { + abs, _ := filepath.Abs(exp[0]) + def = fmt.Sprintf("%s#/%s", abs, exp[1]) + } + } else { + abs := resolver.resolveLocalRefPath(filepath.Dir(ref.FullDefinition), exp[0]) + def = fmt.Sprintf("%s#/%s", abs, exp[1]) + } + } + } + } else { + if len(exp[1]) > 0 { + def = l + } else { + def = exp[0] + } + } + } else { + if strings.HasPrefix(ref.FullDefinition, "http") { + u, _ := url.Parse(ref.FullDefinition) + u.Fragment = "" + def = fmt.Sprintf("%s#/%s", u.String(), exp[1]) + + } else { + if strings.HasPrefix(ref.FullDefinition, "#/") { + def = fmt.Sprintf("#/%s", exp[1]) + } else { + fdexp := strings.Split(ref.FullDefinition, "#/") + def = fmt.Sprintf("%s#/%s", fdexp[0], exp[1]) + } + } + } + } else { + if strings.HasPrefix(l, "http") { + def = l + } else { + // check if were dealing with a remote file + if strings.HasPrefix(ref.FullDefinition, "http") { + + // split the url. + u, _ := url.Parse(ref.FullDefinition) + abs, _ := filepath.Abs(utils.CheckPathOverlap(path.Dir(u.Path), l, string(filepath.Separator))) + u.Path = utils.ReplaceWindowsDriveWithLinuxPath(abs) + u.Fragment = "" + def = u.String() + } else { + lookupRef := strings.Split(ref.FullDefinition, "#/") + abs := resolver.resolveLocalRefPath(filepath.Dir(lookupRef[0]), l) + def = abs + } + } + } + + return def +} + +func (resolver *Resolver) resolveLocalRefPath(base, ref string) string { + if resolver != nil && resolver.specIndex != nil { + return resolver.specIndex.ResolveRelativeFilePath(base, ref) + } + abs, _ := filepath.Abs(utils.CheckPathOverlap(base, ref, string(filepath.Separator))) + return abs +} + +func (resolver *Resolver) buildDefPathWithSchemaBase(ref *Reference, l string, schemaIdBase string) string { + if schemaIdBase != "" { + normalized := resolveRefWithSchemaBase(l, schemaIdBase) + if normalized != l { + return normalized + } + } + return resolver.buildDefPath(ref, l) +} + +func (resolver *Resolver) resolveSchemaIdBase(parentBase string, node *yaml.Node) string { + if node == nil { + return parentBase + } + idValue := FindSchemaIdInNode(node) + if idValue == "" { + return parentBase + } + base := parentBase + if base == "" && resolver.specIndex != nil { + base = resolver.specIndex.specAbsolutePath + } + resolved, err := ResolveSchemaId(idValue, base) + if err != nil || resolved == "" { + return idValue + } + return resolved +} + +func (resolver *Resolver) ResolvePendingNodes() { + // map everything afterwards + for _, r := range resolver.specIndex.pendingResolve { + // r.Node.Content = refs[r].nodes + r.ref.Node.Content = r.nodes + } +} diff --git a/vendor/github.com/pb33f/libopenapi/index/rolodex.go b/vendor/github.com/pb33f/libopenapi/index/rolodex.go new file mode 100644 index 000000000..be89ea775 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/index/rolodex.go @@ -0,0 +1,989 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package index + +import ( + "errors" + "fmt" + "io" + "io/fs" + "log/slog" + "maps" + "math" + "os" + "path/filepath" + "sort" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/pb33f/libopenapi/utils" + + "context" + + "go.yaml.in/yaml/v4" +) + +// CanBeIndexed is an interface that allows a file to be indexed. +type CanBeIndexed interface { + Index(config *SpecIndexConfig) (*SpecIndex, error) +} + +// RolodexFile is an interface that represents a file in the rolodex. It combines multiple `fs` interfaces +// like `fs.FileInfo` and `fs.File` into one interface, so the same struct can be used for everything. +type RolodexFile interface { + GetContent() string + GetFileExtension() FileExtension + GetFullPath() string + GetErrors() []error + GetContentAsYAMLNode() (*yaml.Node, error) + GetIndex() *SpecIndex + // WaitForIndexing blocks until the file's index is ready. + // This is used to coordinate between concurrent goroutines when one is loading + // a file and another needs to use its index. + WaitForIndexing() + Name() string + ModTime() time.Time + IsDir() bool + Sys() any + Size() int64 + Mode() os.FileMode +} + +// RolodexFS is an interface that represents a RolodexFS, is the same interface as `fs.FS`, except it +// also exposes a GetFiles() signature, to extract all files in the FS. +type RolodexFS interface { + Open(name string) (fs.File, error) + GetFiles() map[string]RolodexFile +} + +// Rolodex is a file system abstraction that allows for the indexing of multiple file systems +// and the ability to resolve references across those file systems. It is used to hold references to external +// files, and the indexes they hold. The rolodex is the master lookup for all references. +type Rolodex struct { + localFS map[string]fs.FS + remoteFS map[string]fs.FS + indexed bool + built bool + manualBuilt bool + resolved bool + circChecked bool + indexConfig *SpecIndexConfig + indexingDuration time.Duration + indexes []*SpecIndex + indexMap map[string]*SpecIndex + indexLock sync.Mutex + rootIndex *SpecIndex + rootNode *yaml.Node + caughtErrors []error + safeCircularReferences []*CircularReferenceResult + infiniteCircularReferences []*CircularReferenceResult + ignoredCircularReferences []*CircularReferenceResult + logger *slog.Logger + id string // unique ID for the rolodex, can be used to identify it in logs or other contexts. + globalSchemaIdRegistry map[string]*SchemaIdEntry + schemaIdRegistryLock sync.RWMutex +} + +// NewRolodex creates a new rolodex with the provided index configuration. +func NewRolodex(indexConfig *SpecIndexConfig) *Rolodex { + logger := indexConfig.Logger + if logger == nil { + logger = slog.New( + slog.NewJSONHandler( + os.Stdout, &slog.HandlerOptions{ + Level: slog.LevelError, + }, + ), + ) + } + + r := &Rolodex{ + indexConfig: indexConfig, + id: utils.GenerateAlphanumericString(6), + localFS: make(map[string]fs.FS), + remoteFS: make(map[string]fs.FS), + logger: logger, + indexMap: make(map[string]*SpecIndex), + } + indexConfig.Rolodex = r + return r +} + +// RotateId generates a new unique ID for the rolodex. +func (r *Rolodex) RotateId() string { + r.id = utils.GenerateAlphanumericString(6) + return r.id +} + +// GetId returns the unique ID for the rolodex. +func (r *Rolodex) GetId() string { + return r.id +} + +// GetIgnoredCircularReferences returns a list of circular references that were ignored during the indexing process. +// These can be an array or polymorphic references. Will return an empty slice if no ignored circular references are found. +func (r *Rolodex) GetIgnoredCircularReferences() []*CircularReferenceResult { + debounced := make(map[string]*CircularReferenceResult) + var debouncedResults []*CircularReferenceResult + if r == nil { + return debouncedResults + } + for _, c := range r.ignoredCircularReferences { + if _, ok := debounced[c.LoopPoint.FullDefinition]; !ok { + debounced[c.LoopPoint.FullDefinition] = c + } + } + for _, v := range debounced { + debouncedResults = append(debouncedResults, v) + } + return debouncedResults +} + +// GetSafeCircularReferences returns a list of circular references that were found to be safe during the indexing process. +// These can be an array or polymorphic references. Will return an empty slice if no safe circular references are found. +func (r *Rolodex) GetSafeCircularReferences() []*CircularReferenceResult { + debounced := make(map[string]*CircularReferenceResult) + var debouncedResults []*CircularReferenceResult + if r == nil { + return debouncedResults + } + + // if this rolodex has not been manually checked for circular references or resolved, + // then we need to perform that check now, looking at all indexes and extracting + // results from the resolvers. + if !r.circChecked { + var extracted []*CircularReferenceResult + for _, idx := range append(r.GetIndexes(), r.GetRootIndex()) { + if idx != nil { + res := idx.resolver + if res != nil { + extracted = append(extracted, res.GetSafeCircularReferences()...) + } + } + } + if len(extracted) > 0 { + r.safeCircularReferences = append(r.safeCircularReferences, extracted...) + } + } + + for _, c := range r.safeCircularReferences { + if _, ok := debounced[c.LoopPoint.FullDefinition]; !ok { + debounced[c.LoopPoint.FullDefinition] = c + } + } + for _, v := range debounced { + debouncedResults = append(debouncedResults, v) + } + return debouncedResults +} + +// SetSafeCircularReferences sets the safe circular references for the rolodex. +func (r *Rolodex) SetSafeCircularReferences(refs []*CircularReferenceResult) { + r.safeCircularReferences = refs +} + +// GetIndexingDuration returns the duration it took to index the rolodex. +func (r *Rolodex) GetIndexingDuration() time.Duration { + return r.indexingDuration +} + +// GetRootIndex returns the root index of the rolodex (the entry point, the main document) +func (r *Rolodex) GetRootIndex() *SpecIndex { + return r.rootIndex +} + +// GetConfig returns the index configuration of the rolodex. +func (r *Rolodex) GetConfig() *SpecIndexConfig { + return r.indexConfig +} + +// GetRootNode returns the root index of the rolodex (the entry point, the main document) +func (r *Rolodex) GetRootNode() *yaml.Node { + return r.rootNode +} + +// GetIndexes returns all the indexes in the rolodex. +func (r *Rolodex) GetIndexes() []*SpecIndex { + r.indexLock.Lock() + defer r.indexLock.Unlock() + return r.indexes +} + +// GetCaughtErrors returns all the errors that were caught during the indexing process. +func (r *Rolodex) GetCaughtErrors() []error { + return r.caughtErrors +} + +// AddLocalFS adds a local file system to the rolodex. +func (r *Rolodex) AddLocalFS(baseDir string, fileSystem fs.FS) { + absBaseDir, _ := filepath.Abs(baseDir) + if f, ok := fileSystem.(Rolodexable); ok { + f.SetRolodex(r) + f.SetLogger(r.logger) + } + r.localFS[absBaseDir] = fileSystem +} + +// SetRootNode sets the root node of the rolodex (the entry point, the main document) +func (r *Rolodex) SetRootNode(node *yaml.Node) { + r.rootNode = node +} + +// SetRootIndex sets the root index of the rolodex (the entry point, the main document). +func (r *Rolodex) SetRootIndex(rootIndex *SpecIndex) { + r.rootIndex = rootIndex +} + +func (r *Rolodex) AddExternalIndex(idx *SpecIndex, location string) { + r.indexLock.Lock() + defer r.indexLock.Unlock() + for _, ix := range r.indexes { + if ix.specAbsolutePath == location { + return // already exists, no need to add again. + } + } + + r.indexes = append(r.indexes, idx) + if r.indexMap[location] == nil { + r.indexMap[location] = idx + } + + // Aggregate $id registrations from this index into the global registry + r.RegisterIdsFromIndex(idx) +} + +func (r *Rolodex) AddIndex(idx *SpecIndex) { + if idx != nil { + p := idx.specAbsolutePath + r.AddExternalIndex(idx, p) + } +} + +// AddRemoteFS adds a remote file system to the rolodex. +func (r *Rolodex) AddRemoteFS(baseURL string, fileSystem fs.FS) { + if f, ok := fileSystem.(*RemoteFS); ok { + f.rolodex = r + f.logger = r.logger + } + r.remoteFS[baseURL] = fileSystem +} + +// IndexTheRolodex indexes the rolodex, building out the indexes for each file, and then building the root index. +func (r *Rolodex) IndexTheRolodex(ctx context.Context) error { + if r.indexed { + return nil + } + + var caughtErrors []error + + var indexBuildQueue []*SpecIndex + + indexRolodexFile := func( + location string, fs fs.FS, + doneChan chan struct{}, + errChan chan error, + indexChan chan *SpecIndex, + ) { + var wg sync.WaitGroup + + indexFileFunc := func(idxFile CanBeIndexed, fullPath string) { + defer wg.Done() + + // copy config and set the + copiedConfig := *r.indexConfig + copiedConfig.Rolodex = r + copiedConfig.SpecAbsolutePath = fullPath + copiedConfig.AvoidBuildIndex = true // we will build out everything in two steps. + idx, err := idxFile.Index(&copiedConfig) + + if err == nil { // Index() does not throw an error anymore. + // for each index, we need a resolver + resolver := NewResolver(idx) + + // check if the config has been set to ignore circular references in arrays and polymorphic schemas + if copiedConfig.IgnoreArrayCircularReferences { + resolver.IgnoreArrayCircularReferences() + } + if copiedConfig.IgnorePolymorphicCircularReferences { + resolver.IgnorePolymorphicCircularReferences() + } + indexChan <- idx + } + } + + if lfs, ok := fs.(RolodexFS); ok { + wait := false + + for _, f := range lfs.GetFiles() { + if idxFile, ko := f.(CanBeIndexed); ko { + wg.Add(1) + wait = true + go indexFileFunc(idxFile, f.GetFullPath()) + } + } + if wait { + wg.Wait() + } + doneChan <- struct{}{} + return + } else { + errChan <- errors.New("rolodex file system is not a RolodexFS") + doneChan <- struct{}{} + } + } + + indexingCompleted := 0 + totalToIndex := len(r.localFS) + len(r.remoteFS) + doneChan := make(chan struct{}) + errChan := make(chan error) + indexChan := make(chan *SpecIndex) + + // run through every file system and index every file, fan out as many goroutines as possible. + started := time.Now() + for k, v := range r.localFS { + go indexRolodexFile(k, v, doneChan, errChan, indexChan) + } + for k, v := range r.remoteFS { + go indexRolodexFile(k, v, doneChan, errChan, indexChan) + } + + for indexingCompleted < totalToIndex { + select { + case <-doneChan: + indexingCompleted++ + case err := <-errChan: + indexingCompleted++ + caughtErrors = append(caughtErrors, err) + case idx := <-indexChan: + indexBuildQueue = append(indexBuildQueue, idx) + } + } + + // now that we have indexed all the files, we can build the index. + sort.Slice( + indexBuildQueue, func(i, j int) bool { + return indexBuildQueue[i].specAbsolutePath < indexBuildQueue[j].specAbsolutePath + }, + ) + + r.indexes = indexBuildQueue + + for _, idx := range indexBuildQueue { + idx.BuildIndex() + if r.indexConfig.AvoidCircularReferenceCheck { + continue + } + errs := idx.resolver.CheckForCircularReferences() + for e := range errs { + caughtErrors = append(caughtErrors, errs[e]) + } + if len(idx.resolver.GetIgnoredCircularPolyReferences()) > 0 { + r.ignoredCircularReferences = append( + r.ignoredCircularReferences, idx.resolver.GetIgnoredCircularPolyReferences()..., + ) + } + if len(idx.resolver.GetIgnoredCircularArrayReferences()) > 0 { + r.ignoredCircularReferences = append( + r.ignoredCircularReferences, idx.resolver.GetIgnoredCircularArrayReferences()..., + ) + } + } + + // indexed and built every supporting file, we can build the root index (our entry point) + if r.rootNode != nil { + r.indexConfig.Rolodex = r + + // if there is a base path but no SpecFilePath, then we need to set the root spec config to point to a theoretical root.yaml + // which does not exist, but is used to formulate the absolute path to root references correctly. + if r.indexConfig.BasePath != "" && r.indexConfig.BaseURL == nil { + basePath := r.indexConfig.BasePath + if !filepath.IsAbs(basePath) { + basePath, _ = filepath.Abs(basePath) + } + + if len(r.localFS) > 0 || len(r.remoteFS) > 0 { + if r.indexConfig.SpecFilePath != "" { + // Compute the absolute path to the spec file. + // - If SpecFilePath is already absolute, use it directly. + // - If SpecFilePath is relative, it needs careful handling to avoid path doubling. + // + // The original code used filepath.Base() which incorrectly stripped directory + // segments like /myproject/api-spec/ from nested paths. + // + // Handle cases: + // 1. SpecFilePath = "test_data/nested/doc.yaml", BasePath = "/abs/test_data/nested" + // -> Should NOT double to /abs/test_data/nested/test_data/nested/doc.yaml + // 2. SpecFilePath = "subdir/doc.yaml", BasePath = "/abs/test_data" + // -> Should produce /abs/test_data/subdir/doc.yaml + if filepath.IsAbs(r.indexConfig.SpecFilePath) { + r.indexConfig.SpecAbsolutePath = r.indexConfig.SpecFilePath + } else { + specPath := r.indexConfig.SpecFilePath + // Check if SpecFilePath starts with the relative basePath or its original value + // This handles cases where SpecFilePath = "test_data/file.yaml" and + // BasePath was originally "test_data" (now absolute) + origBasePath := r.indexConfig.BasePath + + // Normalize paths to use OS-specific separators for Windows compatibility + // On Windows, paths may use / but os.PathSeparator is \, causing mismatches + normalizedSpecPath := filepath.FromSlash(specPath) + normalizedOrigBasePath := filepath.FromSlash(origBasePath) + + if strings.HasPrefix(normalizedSpecPath, normalizedOrigBasePath+string(os.PathSeparator)) { + // SpecFilePath includes the original basePath, make it absolute directly + r.indexConfig.SpecAbsolutePath, _ = filepath.Abs(normalizedSpecPath) + } else if strings.HasPrefix(normalizedSpecPath, "..") { + // SpecFilePath starts with ".." (parent directory), resolve it from cwd + // Using filepath.Join with basePath would incorrectly double paths + // e.g., basePath="/Users/foo/bar" + "../bar/file.yaml" would give + // "/Users/foo/bar/bar/file.yaml" instead of "/Users/foo/bar/file.yaml" + r.indexConfig.SpecAbsolutePath, _ = filepath.Abs(normalizedSpecPath) + } else { + // SpecFilePath is relative to basePath, join them + r.indexConfig.SpecAbsolutePath = filepath.Join(basePath, normalizedSpecPath) + } + } + } else { + r.indexConfig.SetTheoreticalRoot() + } + } + } + + // Here we take the root node and also build the index for it. + // This involves extracting references. + index := NewSpecIndexWithConfigAndContext(ctx, r.rootNode, r.indexConfig) + resolver := NewResolver(index) + + if r.indexConfig.IgnoreArrayCircularReferences { + resolver.IgnoreArrayCircularReferences() + } + if r.indexConfig.IgnorePolymorphicCircularReferences { + resolver.IgnorePolymorphicCircularReferences() + } + r.rootIndex = index + r.logger.Debug("[rolodex] starting root index build") + index.BuildIndex() + r.logger.Debug("[rolodex] root index build completed") + + if !r.indexConfig.AvoidCircularReferenceCheck { + resolvingErrors := resolver.CheckForCircularReferences() + r.circChecked = true + for e := range resolvingErrors { + caughtErrors = append(caughtErrors, resolvingErrors[e]) + } + if len(resolver.GetIgnoredCircularPolyReferences()) > 0 { + r.ignoredCircularReferences = append( + r.ignoredCircularReferences, resolver.GetIgnoredCircularPolyReferences()..., + ) + } + if len(resolver.GetIgnoredCircularArrayReferences()) > 0 { + r.ignoredCircularReferences = append( + r.ignoredCircularReferences, resolver.GetIgnoredCircularArrayReferences()..., + ) + } + } + + if len(index.refErrors) > 0 { + caughtErrors = append(caughtErrors, index.refErrors...) + } + } + r.indexingDuration = time.Since(started) + r.indexed = true + r.caughtErrors = caughtErrors + r.built = true + return errors.Join(caughtErrors...) +} + +// CheckForCircularReferences checks for circular references in the rolodex. +func (r *Rolodex) CheckForCircularReferences() { + if !r.circChecked { + if r.rootIndex != nil && r.rootIndex.resolver != nil { + resolvingErrors := r.rootIndex.resolver.CheckForCircularReferences() + for e := range resolvingErrors { + r.caughtErrors = append(r.caughtErrors, resolvingErrors[e]) + } + if len(r.rootIndex.resolver.ignoredPolyReferences) > 0 { + r.ignoredCircularReferences = append( + r.ignoredCircularReferences, r.rootIndex.resolver.ignoredPolyReferences..., + ) + } + if len(r.rootIndex.resolver.ignoredArrayReferences) > 0 { + r.ignoredCircularReferences = append( + r.ignoredCircularReferences, r.rootIndex.resolver.ignoredArrayReferences..., + ) + } + r.safeCircularReferences = append( + r.safeCircularReferences, r.rootIndex.resolver.GetSafeCircularReferences()..., + ) + r.infiniteCircularReferences = append( + r.infiniteCircularReferences, r.rootIndex.resolver.GetInfiniteCircularReferences()..., + ) + } + r.circChecked = true + } +} + +// Resolve resolves references in the rolodex. +func (r *Rolodex) Resolve() { + var resolvers []*Resolver + if r.rootIndex != nil && r.rootIndex.resolver != nil { + resolvers = append(resolvers, r.rootIndex.resolver) + } + for _, idx := range r.indexes { + if idx.resolver != nil { + resolvers = append(resolvers, idx.resolver) + } + } + for _, res := range resolvers { + resolvingErrors := res.Resolve() + for e := range resolvingErrors { + r.caughtErrors = append(r.caughtErrors, resolvingErrors[e]) + } + if r.rootIndex != nil && len(r.rootIndex.resolver.ignoredPolyReferences) > 0 { + r.ignoredCircularReferences = append(r.ignoredCircularReferences, res.ignoredPolyReferences...) + } + if r.rootIndex != nil && len(r.rootIndex.resolver.ignoredArrayReferences) > 0 { + r.ignoredCircularReferences = append(r.ignoredCircularReferences, res.ignoredArrayReferences...) + } + r.safeCircularReferences = append(r.safeCircularReferences, res.GetSafeCircularReferences()...) + r.infiniteCircularReferences = append(r.infiniteCircularReferences, res.GetInfiniteCircularReferences()...) + } + + // resolve pending nodes + for _, res := range resolvers { + res.ResolvePendingNodes() + } + r.resolved = true +} + +// BuildIndexes builds the indexes in the rolodex, this is generally not required unless manually building a rolodex. +func (r *Rolodex) BuildIndexes() { + if r.manualBuilt { + return + } + for _, idx := range r.indexes { + idx.BuildIndex() + } + if r.rootIndex != nil { + r.rootIndex.BuildIndex() + } + r.manualBuilt = true +} + +// GetAllReferences returns all references found in the root and all other indices +func (r *Rolodex) GetAllReferences() map[string]*Reference { + allRefs := make(map[string]*Reference) + for _, idx := range append(r.GetIndexes(), r.GetRootIndex()) { + if idx == nil { + continue + } + refs := idx.GetAllReferences() + maps.Copy(allRefs, refs) + } + return allRefs +} + +// GetAllMappedReferences returns all mapped references found in the root and all other indices +func (r *Rolodex) GetAllMappedReferences() map[string]*Reference { + mappedRefs := make(map[string]*Reference) + for _, idx := range append(r.GetIndexes(), r.GetRootIndex()) { + if idx == nil { + continue + } + refs := idx.GetMappedReferences() + maps.Copy(mappedRefs, refs) + } + return mappedRefs +} + +// OpenWithContext opens a file in the rolodex, and returns a RolodexFile - providing a context. +// The method supports both custom file systems (like LocalFS) and standard fs.FS implementations. +// For standard fs.FS implementations, paths are automatically converted to relative paths as required +// by the fs.FS interface specification (which mandates relative, slash-separated paths). +func (r *Rolodex) OpenWithContext(ctx context.Context, location string) (RolodexFile, error) { + if r == nil { + return nil, fmt.Errorf("rolodex has not been initialized, cannot open file '%s'", location) + } + + if len(r.localFS) <= 0 && len(r.remoteFS) <= 0 { + return nil, fmt.Errorf( + "rolodex has no file systems configured, cannot open '%s'. Add a BaseURL or BasePath to your configuration so the rolodex knows how to resolve references", + location, + ) + } + + var errorStack []error + var localFile *LocalFile + var remoteFile *RemoteFile + fileLookup := location + isUrl := false + if strings.HasPrefix(location, "http") { + isUrl = true + } + + if !isUrl { + if len(r.localFS) <= 0 { + r.logger.Warn("[rolodex] no local file systems configured, cannot open local file", "location", location) + return nil, fmt.Errorf( + "the rolodex has no local file systems configured, cannot open local file '%s'", location, + ) + } + + for k, v := range r.localFS { + + // check if this is a URL or an abs/rel reference. + if !filepath.IsAbs(location) { + fileLookup, _ = filepath.Abs(utils.CheckPathOverlap(k, location, string(os.PathSeparator))) + } + + // For generic fs.FS implementations, we need to use relative paths + // The fs.FS interface requires paths to be relative and slash-separated. + // This ensures compatibility with standard Go file systems like embed.FS, + // fstest.MapFS, afero.NewIOFS, and others that strictly follow the fs.FS contract. + var pathForOpen string + + // Check if this is our custom LocalFS which supports absolute paths + if _, isLocalFS := v.(*LocalFS); isLocalFS { + // LocalFS can handle absolute paths directly for backward compatibility + pathForOpen = fileLookup + } else { + // For standard fs.FS implementations, convert to relative path + // Calculate relative path from base directory k to target fileLookup + relPath, _ := filepath.Rel(k, fileLookup) + pathForOpen = filepath.ToSlash(relPath) + } + + f, err := openFile(ctx, pathForOpen, v) + + if err != nil { + // If the first attempt failed and we haven't already tried with the original location, + // try a lookup with the original location path + if pathForOpen != location { + f, err = openFile(ctx, location, v) + } + + if err != nil { + errorStack = append(errorStack, err) + continue + } + } + // check if this is a native rolodex FS, then the work is done. + if lf, ko := interface{}(f).(*LocalFile); ko { + localFile = lf + break + } + + if lf, ko := f.(RolodexFile); ko { + var atm atomic.Value + atm.Store(lf.GetIndex()) + var parsed *yaml.Node + var parseErrors []error + if p, e := lf.GetContentAsYAMLNode(); e == nil { + parsed = p + } else { + parseErrors = append(parseErrors, e) + } + parseErrors = append(parseErrors, lf.GetErrors()...) + + localFile = &LocalFile{ + filename: lf.Name(), + name: lf.Name(), + extension: ExtractFileType(lf.Name()), + data: []byte(lf.GetContent()), + fullPath: lf.GetFullPath(), + lastModified: lf.ModTime(), + index: atm, + readingErrors: parseErrors, + parsed: parsed, + } + // If there were errors processing the file content, we should return them + if len(parseErrors) > 0 { + errorStack = append(errorStack, parseErrors...) + } + break + } + + // not a native FS, so we need to read the file and create a local file. + bytes, rErr := io.ReadAll(f) + if rErr != nil { + errorStack = append(errorStack, rErr) + continue + } + s, sErr := f.Stat() + if sErr != nil { + errorStack = append(errorStack, sErr) + continue + } + if len(bytes) > 0 { + var atm atomic.Value + idx := r.rootIndex + atm.Store(idx) + + localFile = &LocalFile{ + filename: filepath.Base(fileLookup), + name: filepath.Base(fileLookup), + extension: ExtractFileType(fileLookup), + data: bytes, + fullPath: fileLookup, + lastModified: s.ModTime(), + index: atm, + } + break + } + + } + + } else { + + if !r.indexConfig.AllowRemoteLookup { + return nil, fmt.Errorf( + "remote lookup for '%s' not allowed, please set the index configuration to "+ + "AllowRemoteLookup to true", fileLookup, + ) + } + + for _, v := range r.remoteFS { + + var f fs.File + var err error + if fscw, ok := v.(RolodexFSWithContext); ok { + f, err = fscw.OpenWithContext(ctx, fileLookup) + } else { + f, err = v.Open(fileLookup) + } + + if err != nil { + r.logger.Warn("[rolodex] errors opening remote file", "location", fileLookup, "error", err) + } + if f != nil { + if rf, ok := interface{}(f).(*RemoteFile); ok { + remoteFile = rf + break + } else { + + bytes, rErr := io.ReadAll(f) + if rErr != nil { + errorStack = append(errorStack, rErr) + continue + } + s, sErr := f.Stat() + if sErr != nil { + errorStack = append(errorStack, sErr) + continue + } + if len(bytes) > 0 { + var atm atomic.Value + atm.Store(r.rootIndex) + remoteFile = &RemoteFile{ + filename: filepath.Base(fileLookup), + name: filepath.Base(fileLookup), + extension: ExtractFileType(fileLookup), + data: bytes, + fullPath: fileLookup, + lastModified: s.ModTime(), + index: atm, + } + break + } + } + } + } + } + + if localFile != nil { + // Check if the localFile has any reading errors that should be returned + var fileErrors []error + fileErrors = localFile.readingErrors + return &rolodexFile{ + rolodex: r, + location: localFile.fullPath, + localFile: localFile, + }, errors.Join(fileErrors...) + } + + if remoteFile != nil { + // Check if the remoteFile has any seeking errors that should be returned + var fileErrors []error + fileErrors = remoteFile.seekingErrors + return &rolodexFile{ + rolodex: r, + location: remoteFile.fullPath, + remoteFile: remoteFile, + }, errors.Join(fileErrors...) + } + + return nil, errors.Join(errorStack...) +} + +func openFile(ctx context.Context, location string, v fs.FS) (fs.File, error) { + var err error + var f fs.File + if fscw, ok := v.(RolodexFSWithContext); ok { + f, err = fscw.OpenWithContext(ctx, location) + } else { + f, err = v.Open(location) + } + return f, err +} + +// Open opens a file in the rolodex, and returns a RolodexFile. +func (r *Rolodex) Open(location string) (RolodexFile, error) { + return r.OpenWithContext(context.Background(), location) +} + +var suffixes = []string{"B", "KB", "MB", "GB", "TB"} + +func Round(val float64, roundOn float64, places int) (newVal float64) { + var round float64 + pow := math.Pow(10, float64(places)) + digit := pow * val + _, div := math.Modf(digit) + if div >= roundOn { + round = math.Ceil(digit) + } else { + round = math.Floor(digit) + } + newVal = round / pow + return +} + +func HumanFileSize(size float64) string { + base := math.Log(size) / math.Log(1024) + getSize := Round(math.Pow(1024, base-math.Floor(base)), .5, 2) + getSuffix := suffixes[int(math.Floor(base))] + return strconv.FormatFloat(getSize, 'f', -1, 64) + " " + string(getSuffix) +} + +func (r *Rolodex) RolodexFileSizeAsString() string { + size := r.RolodexFileSize() + return HumanFileSize(float64(size)) +} + +func (r *Rolodex) RolodexTotalFiles() int { + // look through each file system and count the files + var total int + for _, v := range r.localFS { + if lfs, ok := v.(RolodexFS); ok { + total += len(lfs.GetFiles()) + } + } + for _, v := range r.remoteFS { + if lfs, ok := v.(RolodexFS); ok { + total += len(lfs.GetFiles()) + } + } + return total +} + +func (r *Rolodex) RolodexFileSize() int64 { + var size int64 + for _, v := range r.localFS { + if lfs, ok := v.(RolodexFS); ok { + for _, f := range lfs.GetFiles() { + size += f.Size() + } + } + } + for _, v := range r.remoteFS { + if lfs, ok := v.(RolodexFS); ok { + for _, f := range lfs.GetFiles() { + size += f.Size() + } + } + } + return size +} + +// GetFullLineCount returns the total number of lines from all files in the Rolodex +func (r *Rolodex) GetFullLineCount() int64 { + var lineCount int64 + for _, v := range r.localFS { + if lfs, ok := v.(RolodexFS); ok { + for _, f := range lfs.GetFiles() { + lineCount += int64(strings.Count(f.GetContent(), "\n")) + 1 + } + } + } + for _, v := range r.remoteFS { + if lfs, ok := v.(RolodexFS); ok { + for _, f := range lfs.GetFiles() { + lineCount += int64(strings.Count(f.GetContent(), "\n")) + 1 + } + } + } + // add in root count + if r.indexConfig != nil && r.indexConfig.SpecInfo != nil { + lineCount += int64(strings.Count(string(*r.indexConfig.SpecInfo.SpecBytes), "\n")) + 1 + } + return lineCount +} + +func (r *Rolodex) ClearIndexCaches() { + if r.rootIndex != nil { + r.rootIndex.GetHighCache().Clear() + } + for _, idx := range r.indexes { + idx.GetHighCache().Clear() + } +} + +// RegisterGlobalSchemaId registers a schema $id in the Rolodex global registry. +// Returns an error if the $id is invalid. +func (r *Rolodex) RegisterGlobalSchemaId(entry *SchemaIdEntry) error { + if r == nil { + return fmt.Errorf("cannot register $id on nil Rolodex") + } + + r.schemaIdRegistryLock.Lock() + defer r.schemaIdRegistryLock.Unlock() + + if r.globalSchemaIdRegistry == nil { + r.globalSchemaIdRegistry = make(map[string]*SchemaIdEntry) + } + + _, err := registerSchemaIdToRegistry(r.globalSchemaIdRegistry, entry, r.logger, "global registry") + return err +} + +// LookupSchemaById looks up a schema by its $id URI across all indexes. +func (r *Rolodex) LookupSchemaById(uri string) *SchemaIdEntry { + if r == nil { + return nil + } + + r.schemaIdRegistryLock.RLock() + defer r.schemaIdRegistryLock.RUnlock() + + if r.globalSchemaIdRegistry == nil { + return nil + } + return r.globalSchemaIdRegistry[uri] +} + +// GetAllGlobalSchemaIds returns a copy of all registered $id entries across all indexes. +func (r *Rolodex) GetAllGlobalSchemaIds() map[string]*SchemaIdEntry { + if r == nil { + return make(map[string]*SchemaIdEntry) + } + + r.schemaIdRegistryLock.RLock() + defer r.schemaIdRegistryLock.RUnlock() + return copySchemaIdRegistry(r.globalSchemaIdRegistry) +} + +// RegisterIdsFromIndex aggregates all $id registrations from an index into the global registry. +// Called after each index is built to populate the Rolodex global registry. +func (r *Rolodex) RegisterIdsFromIndex(idx *SpecIndex) { + if r == nil || idx == nil { + return + } + + entries := idx.GetAllSchemaIds() + for _, entry := range entries { + _ = r.RegisterGlobalSchemaId(entry) + } +} diff --git a/vendor/github.com/pb33f/libopenapi/index/rolodex_file.go b/vendor/github.com/pb33f/libopenapi/index/rolodex_file.go new file mode 100644 index 000000000..4b3519f83 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/index/rolodex_file.go @@ -0,0 +1,166 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package index + +import ( + "os" + "time" + + "github.com/pb33f/libopenapi/datamodel" + "go.yaml.in/yaml/v4" +) + +type rolodexFile struct { + location string + rolodex *Rolodex + index *SpecIndex + localFile *LocalFile + remoteFile *RemoteFile +} + +func (rf *rolodexFile) Name() string { + if rf.localFile != nil { + return rf.localFile.filename + } + if rf.remoteFile != nil { + return rf.remoteFile.filename + } + return "" +} + +func (rf *rolodexFile) GetIndex() *SpecIndex { + if rf.localFile != nil { + return rf.localFile.GetIndex() + } + if rf.remoteFile != nil { + return rf.remoteFile.GetIndex() + } + return nil +} + +func (rf *rolodexFile) Index(config *SpecIndexConfig) (*SpecIndex, error) { + if rf.index != nil { + return rf.index, nil + } + var content []byte + if rf.localFile != nil { + content = rf.localFile.data + } + if rf.remoteFile != nil { + content = rf.remoteFile.data + } + + // first, we must parse the content of the file + info, err := datamodel.ExtractSpecInfoWithDocumentCheckSync(content, config.SkipDocumentCheck) + if err != nil { + return nil, err + } + + // create a new index for this file and link it to this rolodex. + config.Rolodex = rf.rolodex + index := NewSpecIndexWithConfig(info.RootNode, config) + rf.index = index + return index, nil +} + +func (rf *rolodexFile) GetContent() string { + if rf.localFile != nil { + return string(rf.localFile.data) + } + if rf.remoteFile != nil { + return string(rf.remoteFile.data) + } + return "" +} + +func (rf *rolodexFile) GetContentAsYAMLNode() (*yaml.Node, error) { + if rf.localFile != nil { + return rf.localFile.GetContentAsYAMLNode() + } + if rf.remoteFile != nil { + return rf.remoteFile.GetContentAsYAMLNode() + } + return nil, nil +} + +func (rf *rolodexFile) GetFileExtension() FileExtension { + if rf.localFile != nil { + return rf.localFile.extension + } + if rf.remoteFile != nil { + return rf.remoteFile.extension + } + return UNSUPPORTED +} + +func (rf *rolodexFile) GetFullPath() string { + if rf.localFile != nil { + return rf.localFile.fullPath + } + if rf.remoteFile != nil { + return rf.remoteFile.fullPath + } + return "" +} + +func (rf *rolodexFile) ModTime() time.Time { + if rf.localFile != nil { + return rf.localFile.lastModified + } + if rf.remoteFile != nil { + return rf.remoteFile.lastModified + } + return time.Now() +} + +func (rf *rolodexFile) Size() int64 { + if rf.localFile != nil { + return rf.localFile.Size() + } + if rf.remoteFile != nil { + return rf.remoteFile.Size() + } + return 0 +} + +func (rf *rolodexFile) IsDir() bool { + // always false. + return false +} + +func (rf *rolodexFile) Sys() interface{} { + // not implemented. + return nil +} + +func (rf *rolodexFile) Mode() os.FileMode { + if rf.localFile != nil { + return rf.localFile.Mode() + } + if rf.remoteFile != nil { + return rf.remoteFile.Mode() + } + return os.FileMode(0) +} + +func (rf *rolodexFile) GetErrors() []error { + if rf.localFile != nil { + return rf.localFile.readingErrors + } + if rf.remoteFile != nil { + return rf.remoteFile.seekingErrors + } + return nil +} + +func (rf *rolodexFile) WaitForIndexing() { + if rf.localFile != nil { + rf.localFile.WaitForIndexing() + return + } + if rf.remoteFile != nil { + rf.remoteFile.WaitForIndexing() + return + } +} diff --git a/vendor/github.com/pb33f/libopenapi/index/rolodex_file_loader.go b/vendor/github.com/pb33f/libopenapi/index/rolodex_file_loader.go new file mode 100644 index 000000000..9de011667 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/index/rolodex_file_loader.go @@ -0,0 +1,538 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package index + +import ( + "fmt" + "io" + "io/fs" + "log/slog" + "os" + "path/filepath" + "slices" + "strings" + "sync" + "sync/atomic" + "time" + + "context" + + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +type Rolodexable interface { + SetRolodex(rolodex *Rolodex) + SetLogger(logger *slog.Logger) +} + +// LocalFS is a file system that indexes local files. +type LocalFS struct { + fsConfig *LocalFSConfig + indexConfig *SpecIndexConfig + entryPointDirectory string + baseDirectory string + Files sync.Map + extractedFiles map[string]RolodexFile + logger *slog.Logger + readingErrors []error + rolodex *Rolodex + processingFiles sync.Map +} + +// GetFiles returns the files that have been indexed. A map of RolodexFile objects keyed by the full path of the file. +func (l *LocalFS) GetFiles() map[string]RolodexFile { + files := make(map[string]RolodexFile) + l.Files.Range(func(key, value interface{}) bool { + files[key.(string)] = value.(*LocalFile) + return true + }) + l.extractedFiles = files + return files +} + +func (l *LocalFS) SetRolodex(rolodex *Rolodex) { + l.rolodex = rolodex +} + +func (l *LocalFS) SetLogger(logger *slog.Logger) { + l.logger = logger +} + +// GetErrors returns any errors that occurred during the indexing process. +func (l *LocalFS) GetErrors() []error { + return l.readingErrors +} + +type waiterLocal struct { + f string + done bool + file *LocalFile + listeners int + error error + mu sync.RWMutex + //cond *sync.Cond +} + +func (l *LocalFS) OpenWithContext(ctx context.Context, name string) (fs.File, error) { + if l.indexConfig != nil && !l.indexConfig.AllowFileLookup { + return nil, &fs.PathError{ + Op: "open", Path: name, + Err: fmt.Errorf("file lookup for '%s' not allowed, set the index configuration "+ + "to AllowFileLookup to be true", name), + } + } + + if !filepath.IsAbs(name) { + name, _ = filepath.Abs(utils.CheckPathOverlap(l.baseDirectory, name, string(os.PathSeparator))) + } + + if f, ok := l.Files.Load(name); ok { + return f.(*LocalFile), nil + } + + // Only enter new-file logic if DirFS is not set + if l.fsConfig != nil && l.fsConfig.DirFS == nil { + + // Use LoadOrStore to atomically check if someone is already processing this file. + // This prevents the race condition where two goroutines both see "not processing" + // and both start processing the same file. + processingWaiter := &waiterLocal{f: name} + processingWaiter.mu.Lock() + + if existing, loaded := l.processingFiles.LoadOrStore(name, processingWaiter); loaded { + // Someone else is already processing this file, wait for them + processingWaiter.mu.Unlock() // Release our unused waiter's lock + wait := existing.(*waiterLocal) + + wait.mu.Lock() + l.logger.Debug("[rolodex file loader]: waiting for existing OS load to complete", "file", name, "listeners", wait.listeners) + f := wait.file + e := wait.error + l.logger.Debug("[rolodex file loader]: waiting done, OS load completed, returning file", "file", name, "listeners", wait.listeners) + wait.mu.Unlock() + return f, e + } + + // We successfully stored our waiter, so we're responsible for processing this file + + var extractedFile *LocalFile + var extErr error + l.logger.Debug("[rolodex file loader]: extracting file from OS", "file", name) + extractedFile, extErr = l.extractFile(name) + + if extErr != nil { + processingWaiter.error = extErr + processingWaiter.done = true + l.processingFiles.Delete(name) + processingWaiter.mu.Unlock() + + return nil, extErr + } + + // Store in Files and release the waiter BEFORE indexing to prevent deadlocks. + // If file A needs file B and file B needs file A, holding the lock during indexing + // would cause a deadlock. The indexOnce in IndexWithContext handles concurrent + // access to index creation safely. + if extractedFile != nil { + l.Files.Store(name, extractedFile) + } + + processingWaiter.file = extractedFile + processingWaiter.error = extErr + processingWaiter.done = true + l.processingFiles.Delete(name) + processingWaiter.mu.Unlock() + + // Now index the file AFTER releasing the lock + if extractedFile != nil && l.indexConfig != nil { + copiedCfg := *l.indexConfig + copiedCfg.SpecAbsolutePath = name + copiedCfg.AvoidBuildIndex = true + copiedCfg.SpecInfo = nil + + // Add this file to the context's indexing set to prevent deadlocks + // when circular references cause the same file to be looked up recursively. + indexingCtx := AddIndexingFile(ctx, name) + + idx, _ := extractedFile.IndexWithContext(indexingCtx, &copiedCfg) + if idx != nil && l.rolodex != nil { + idx.rolodex = l.rolodex + } + + if idx != nil { + resolver := NewResolver(idx) + idx.resolver = resolver + idx.BuildIndex() + } + if len(extractedFile.data) > 0 { + l.logger.Debug("[rolodex file loader]: successfully loaded and indexed file", "file", name) + } + if l.rolodex != nil { + l.rolodex.AddIndex(idx) + } + } + + // Signal that indexing is complete - other goroutines waiting for this file can proceed + if extractedFile != nil { + extractedFile.signalIndexingComplete() + } + + return extractedFile, nil + } + + return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist} +} + +// Open opens a file, returning it or an error. If the file is not found, the error is of type *PathError. +func (l *LocalFS) Open(name string) (fs.File, error) { + return l.OpenWithContext(context.Background(), name) +} + +// LocalFile is a file that has been indexed by the LocalFS. It implements the RolodexFile interface. +type LocalFile struct { + filename string + name string + extension FileExtension + data []byte + fullPath string + lastModified time.Time + readingErrors []error + index atomic.Value + parsed *yaml.Node + offset int64 + parseMutex sync.Mutex + indexOnce sync.Once + indexingComplete chan struct{} // Closed when indexing is complete +} + +// GetIndex returns the *SpecIndex for the file. +func (l *LocalFile) GetIndex() *SpecIndex { + if v := l.index.Load(); v != nil { + return v.(*SpecIndex) + } + return nil +} + +// WaitForIndexing blocks until the file's index is ready. +// This is used to coordinate between concurrent goroutines when one is loading +// a file and another needs to use its index. +func (l *LocalFile) WaitForIndexing() { + if l.indexingComplete != nil { + <-l.indexingComplete + } +} + +// signalIndexingComplete marks the file as ready for use. +// This should be called after indexing completes or for batch-loaded files. +func (l *LocalFile) signalIndexingComplete() { + if l.indexingComplete != nil { + select { + case <-l.indexingComplete: + // Already closed, do nothing + default: + close(l.indexingComplete) + } + } +} + +// Index returns the *SpecIndex for the file. If the index has not been created, it will be created (indexed) +func (l *LocalFile) Index(config *SpecIndexConfig) (*SpecIndex, error) { + return l.IndexWithContext(context.Background(), config) +} + +// // IndexWithContext returns the *SpecIndex for the file. If the index has not been created, it will be created (indexed), also supplied context +func (l *LocalFile) IndexWithContext(ctx context.Context, config *SpecIndexConfig) (*SpecIndex, error) { + var result *SpecIndex + var resultErr error + l.indexOnce.Do(func() { + content := l.data + // first, we must parse the content of the file, + // the check is bypassed, so as long as it's readable, we're good. + info, _ := datamodel.ExtractSpecInfoWithDocumentCheck(content, true) + if config.SpecInfo == nil { + config.SpecInfo = info + } + index := NewSpecIndexWithConfigAndContext(ctx, info.RootNode, config) + index.specAbsolutePath = l.fullPath + l.index.Store(index) + result = index + }) + if v := l.index.Load(); v != nil { + result = v.(*SpecIndex) + } + return result, resultErr + +} + +// GetContent returns the content of the file as a string. +func (l *LocalFile) GetContent() string { + return string(l.data) +} + +// GetContentAsYAMLNode returns the content of the file as a *yaml.Node. If something went wrong +// then an error is returned. +func (l *LocalFile) GetContentAsYAMLNode() (*yaml.Node, error) { + idx := l.GetIndex() + if idx != nil && idx.root != nil { + return idx.GetRootNode(), nil + } + + // Lock before proceeding with parsing or modifications + l.parseMutex.Lock() + defer l.parseMutex.Unlock() + + // Check again after locking in case another goroutine completed parsing + // while we were waiting for the lock + if l.parsed != nil { + return l.parsed, nil + } + + if l.data == nil { + return nil, fmt.Errorf("no data to parse for file: %s", l.fullPath) + } + var root yaml.Node + err := yaml.Unmarshal(l.data, &root) + if err != nil { + // we can't parse it, so create a fake document node with a single string content + root = yaml.Node{ + Kind: yaml.DocumentNode, + Content: []*yaml.Node{ + { + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: string(l.data), + }, + }, + } + } + if idx != nil && idx.root == nil { + idx.root = &root + } + l.parsed = &root + return &root, err +} + +// GetFileExtension returns the FileExtension of the file. +func (l *LocalFile) GetFileExtension() FileExtension { + return l.extension +} + +// GetFullPath returns the full path of the file. +func (l *LocalFile) GetFullPath() string { + return l.fullPath +} + +// GetErrors returns any errors that occurred during the indexing process. +func (l *LocalFile) GetErrors() []error { + return l.readingErrors +} + +// FullPath returns the full path of the file. +func (l *LocalFile) FullPath() string { + return l.fullPath +} + +// Name returns the name of the file. +func (l *LocalFile) Name() string { + return l.name +} + +// Size returns the size of the file. +func (l *LocalFile) Size() int64 { + return int64(len(l.data)) +} + +// Mode returns the file mode bits for the file. +func (l *LocalFile) Mode() fs.FileMode { + return fs.FileMode(0) +} + +// ModTime returns the modification time of the file. +func (l *LocalFile) ModTime() time.Time { + return l.lastModified +} + +// IsDir returns true if the file is a directory, it always returns false +func (l *LocalFile) IsDir() bool { + return false +} + +// Sys returns the underlying data source (always returns nil) +func (l *LocalFile) Sys() interface{} { + return nil +} + +// Close closes the file (doesn't do anything, returns no error) +func (l *LocalFile) Close() error { + return nil +} + +// Stat returns the FileInfo for the file. +func (l *LocalFile) Stat() (fs.FileInfo, error) { + return l, nil +} + +// Read reads the file into a byte slice, makes it compatible with io.Reader. +func (l *LocalFile) Read(b []byte) (int, error) { + if l.offset >= int64(len(l.GetContent())) { + return 0, io.EOF + } + if l.offset < 0 { + return 0, &fs.PathError{Op: "read", Path: l.GetFullPath(), Err: fs.ErrInvalid} + } + n := copy(b, l.GetContent()[l.offset:]) + l.offset += int64(n) + return n, nil +} + +// LocalFSConfig is the configuration for the LocalFS. +type LocalFSConfig struct { + // the base directory to index + BaseDirectory string + + // supply your own logger + Logger *slog.Logger + + // supply a list of specific files to index only + FileFilters []string + + // supply a custom fs.FS to use + DirFS fs.FS + + // supply an index configuration to use + IndexConfig *SpecIndexConfig +} + +// NewLocalFSWithConfig creates a new LocalFS with the supplied configuration. +func NewLocalFSWithConfig(config *LocalFSConfig) (*LocalFS, error) { + var allErrors []error + + log := config.Logger + if log == nil { + log = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ + Level: slog.LevelError, + })) + } + + // if the basedir is an absolute file, we're just going to index that file. + ext := filepath.Ext(config.BaseDirectory) + file := filepath.Base(config.BaseDirectory) + + var absBaseDir string + absBaseDir, _ = filepath.Abs(config.BaseDirectory) + + localFS := &LocalFS{ + indexConfig: config.IndexConfig, + fsConfig: config, + logger: log, + baseDirectory: absBaseDir, + entryPointDirectory: config.BaseDirectory, + } + + // if a directory filesystem is supplied, use that to walk the directory and pick up everything it finds. + if config.DirFS != nil { + walkErr := fs.WalkDir(config.DirFS, ".", func(p string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + // we don't care about directories, or errors, just read everything we can. + if d.IsDir() { + if d.Name() != config.BaseDirectory { + return nil + } + } + if len(ext) > 2 && p != file { + return nil + } + if strings.HasPrefix(p, ".") { + return nil + } + if len(config.FileFilters) > 0 { + if !slices.Contains(config.FileFilters, p) { + return nil + } + } + lf, fErr := localFS.extractFile(p) + if lf != nil { + // For batch loading (DirFS mode), store immediately. + // Indexing happens later in IndexTheRolodex. + localFS.Files.Store(lf.fullPath, lf) + // Signal that this file is ready - for batch loading, indexing + // is handled separately by IndexTheRolodex, so we signal immediately. + lf.signalIndexingComplete() + } + return fErr + }) + + if walkErr != nil { + return nil, walkErr + } + } + + localFS.readingErrors = allErrors + return localFS, nil +} + +func (l *LocalFS) extractFile(p string) (*LocalFile, error) { + extension := ExtractFileType(p) + var readingErrors []error + abs := p + config := l.fsConfig + if !filepath.IsAbs(p) { + if config != nil && config.BaseDirectory != "" { + abs, _ = filepath.Abs(utils.CheckPathOverlap(config.BaseDirectory, p, string(os.PathSeparator))) + } else { + abs, _ = filepath.Abs(p) + } + } + var fileData []byte + switch extension { + case YAML, JSON, JS, GO, TS, CS, C, CPP, PHP, PY, HTML, MD, JAVA, RS, ZIG, RB: + var file fs.File + if config != nil && config.DirFS != nil { + l.logger.Debug("[rolodex file loader]: collecting file from dirFS", "file", extension, "location", abs) + file, _ = config.DirFS.Open(p) + } else { + l.logger.Debug("[rolodex file loader]: reading local file from OS", "file", extension, "location", abs) + var fileError error + file, fileError = os.Open(abs) + // if reading without a directory FS, error out on any error, do not continue. + if fileError != nil { + return nil, fileError + } + defer file.Close() + } + + modTime := time.Now() + stat, _ := file.Stat() + if stat != nil { + modTime = stat.ModTime() + } + fileData, _ = io.ReadAll(file) + + lf := &LocalFile{ + filename: p, + name: filepath.Base(p), + extension: ExtractFileType(p), + data: fileData, + fullPath: abs, + lastModified: modTime, + readingErrors: readingErrors, + indexingComplete: make(chan struct{}), + } + // Note: We intentionally don't store in l.Files here. + // The caller is responsible for storing after the file is fully processed + // (including indexing). This prevents race conditions where a concurrent + // call gets an un-indexed file from the cache. + return lf, nil + case UNSUPPORTED: + if config != nil && config.DirFS != nil { + l.logger.Warn("[rolodex file loader]: skipping non JSON/YAML file", "file", abs) + } + } + return nil, nil +} diff --git a/vendor/github.com/pb33f/libopenapi/index/rolodex_ref_extractor.go b/vendor/github.com/pb33f/libopenapi/index/rolodex_ref_extractor.go new file mode 100644 index 000000000..3ff9882b7 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/index/rolodex_ref_extractor.go @@ -0,0 +1,100 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package index + +import ( + "fmt" + "strings" +) + +const ( + Local RefType = iota + File + HTTP +) + +type RefType int + +type ExtractedRef struct { + Location string + Type RefType +} + +// GetFile returns the file path of the reference. +func (r *ExtractedRef) GetFile() string { + switch r.Type { + case File, HTTP: + location := strings.Split(r.Location, "#/") + return location[0] + default: + return r.Location + } +} + +// GetReference returns the reference path of the reference. +func (r *ExtractedRef) GetReference() string { + switch r.Type { + case File, HTTP: + location := strings.Split(r.Location, "#/") + return fmt.Sprintf("#/%s", location[1]) + default: + return r.Location + } +} + +// ExtractFileType returns the file extension of the reference. +func ExtractFileType(ref string) FileExtension { + if strings.HasSuffix(ref, ".yaml") { + return YAML + } + if strings.HasSuffix(ref, ".yml") { + return YAML + } + if strings.HasSuffix(ref, ".json") { + return JSON + } + if strings.HasSuffix(ref, ".js") { + return JS + } + if strings.HasSuffix(ref, ".go") { + return GO + } + if strings.HasSuffix(ref, ".ts") { + return TS + } + if strings.HasSuffix(ref, ".cs") { + return CS + } + if strings.HasSuffix(ref, ".c") { + return C + } + if strings.HasSuffix(ref, ".cpp") { + return CPP + } + if strings.HasSuffix(ref, ".php") { + return PHP + } + if strings.HasSuffix(ref, ".py") { + return PY + } + if strings.HasSuffix(ref, ".html") { + return HTML + } + if strings.HasSuffix(ref, ".md") { + return MD + } + if strings.HasSuffix(ref, ".java") { + return JAVA + } + if strings.HasSuffix(ref, ".rs") { + return RS + } + if strings.HasSuffix(ref, ".zig") { + return ZIG + } + if strings.HasSuffix(ref, ".rb") { + return RB + } + return UNSUPPORTED +} diff --git a/vendor/github.com/pb33f/libopenapi/index/rolodex_remote_loader.go b/vendor/github.com/pb33f/libopenapi/index/rolodex_remote_loader.go new file mode 100644 index 000000000..d5959e474 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/index/rolodex_remote_loader.go @@ -0,0 +1,797 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package index + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "io/fs" + "log/slog" + "net/http" + "net/url" + "os" + "path/filepath" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/utils" + + "go.yaml.in/yaml/v4" +) + +const ( + YAML FileExtension = iota + JSON + JS + GO + TS + CS + C + CPP + PHP + PY + HTML + MD + JAVA + RS + ZIG + RB + UNSUPPORTED +) + +// FileExtension is the type of file extension. +type FileExtension int + +// contentDetectionCache is a simple cache for content type detection results +// to avoid repeated fetches of the same URL +var contentDetectionCache = make(map[string]FileExtension) +var contentDetectionMutex sync.RWMutex + +// detectContentType attempts to identify if the data contains JSON or YAML content +// by analyzing patterns in the first ~1KB of data +func detectContentType(data []byte) FileExtension { + if len(data) == 0 { + return UNSUPPORTED + } + + // Trim leading whitespace + data = bytes.TrimLeft(data, " \t\r\n") + if len(data) == 0 { + return UNSUPPORTED + } + + // Check for JSON patterns + if data[0] == '{' || data[0] == '[' { + // Quick validation - count braces/brackets to ensure it's not malformed + if data[0] == '{' { + openBraces := 0 + for _, b := range data { + if b == '{' { + openBraces++ + } else if b == '}' { + openBraces-- + } + } + if openBraces >= 0 { + return JSON + } + } else if data[0] == '[' { + openBrackets := 0 + for _, b := range data { + if b == '[' { + openBrackets++ + } else if b == ']' { + openBrackets-- + } + } + if openBrackets >= 0 { + return JSON + } + } + } + + // Check for YAML patterns + dataStr := string(data) + // YAML document markers + if strings.HasPrefix(dataStr, "---") { + return YAML + } + + // Look for key-value patterns common in YAML + lines := strings.Split(dataStr, "\n") + yamlPatterns := 0 + for i, line := range lines { + if i > 10 { + break // Only check first few lines for efficiency + } + line = strings.TrimSpace(line) + if line == "" || strings.HasPrefix(line, "#") { + continue // Skip empty lines and comments + } + // Look for key: value patterns + if strings.Contains(line, ":") && !strings.HasPrefix(line, "http") { + parts := strings.SplitN(line, ":", 2) + if len(parts) == 2 { + key := strings.TrimSpace(parts[0]) + // Ensure it looks like a YAML key (not a URL) + if key != "" && !strings.Contains(key, " ") && !strings.Contains(key, "/") { + yamlPatterns++ + } + } + } + } + + // If we found multiple YAML-like patterns, it's probably YAML + if yamlPatterns >= 2 { + return YAML + } + + return UNSUPPORTED +} + +// fetchWithRetry fetches content from URL with retry logic +func fetchWithRetry(url string, handler utils.RemoteURLHandler, maxSize int, logger *slog.Logger) ([]byte, error) { + const maxRetries = 3 + const retryDelay = time.Second + + var lastErr error + for attempt := 1; attempt <= maxRetries; attempt++ { + if logger != nil { + logger.Debug("fetching content for type detection", "url", url, "attempt", attempt) + } + + resp, err := handler(url) + if err != nil { + lastErr = err + if attempt < maxRetries { + time.Sleep(retryDelay) + continue + } + break + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + lastErr = fmt.Errorf("HTTP %d: %s", resp.StatusCode, resp.Status) + if attempt < maxRetries { + time.Sleep(retryDelay) + continue + } + break + } + + // Create a limited reader to avoid reading huge files + limitedReader := io.LimitReader(resp.Body, int64(maxSize)) + data, err := io.ReadAll(limitedReader) + if err != nil { + lastErr = err + if attempt < maxRetries { + time.Sleep(retryDelay) + continue + } + break + } + + return data, nil + } + + return nil, fmt.Errorf("failed to fetch after %d attempts: %v", maxRetries, lastErr) +} + +// detectRemoteContentType fetches a small portion of remote content to determine its type +func detectRemoteContentType(url string, handler utils.RemoteURLHandler, logger *slog.Logger) FileExtension { + // Check cache first + contentDetectionMutex.RLock() + if cached, exists := contentDetectionCache[url]; exists { + contentDetectionMutex.RUnlock() + return cached + } + contentDetectionMutex.RUnlock() + + // Fetch content with retry logic + const maxDetectionSize = 2048 // 2KB should be enough for detection + data, err := fetchWithRetry(url, handler, maxDetectionSize, logger) + if err != nil { + if logger != nil { + logger.Warn("failed to fetch content for type detection", "url", url, "error", err) + } + // Cache the failure to avoid repeated attempts + contentDetectionMutex.Lock() + contentDetectionCache[url] = UNSUPPORTED + contentDetectionMutex.Unlock() + return UNSUPPORTED + } + + // Detect content type + detectedType := detectContentType(data) + + // Cache the result + contentDetectionMutex.Lock() + contentDetectionCache[url] = detectedType + contentDetectionMutex.Unlock() + + if logger != nil { + typeStr := "UNSUPPORTED" + if detectedType == JSON { + typeStr = "JSON" + } else if detectedType == YAML { + typeStr = "YAML" + } + logger.Debug("detected content type", "url", url, "type", typeStr) + } + + return detectedType +} + +// clearContentDetectionCache clears the content detection cache +func clearContentDetectionCache() { + contentDetectionMutex.Lock() + contentDetectionCache = make(map[string]FileExtension) + contentDetectionMutex.Unlock() +} + +// RolodexFSWithContext is an interface like fs.FS, but with a context parameter for the Open method. +type RolodexFSWithContext interface { + OpenWithContext(ctx context.Context, name string) (fs.File, error) +} + +// RemoteFS is a file system that indexes remote files. It implements the fs.FS interface. Files are located remotely +// and served via HTTP. +type RemoteFS struct { + indexConfig *SpecIndexConfig + rootURL string + rootURLParsed *url.URL + RemoteHandlerFunc utils.RemoteURLHandler + Files sync.Map + ProcessingFiles sync.Map + FetchTime int64 + FetchChannel chan *RemoteFile + remoteErrors []error + logger *slog.Logger + extractedFiles map[string]RolodexFile + rolodex *Rolodex + errMutex sync.Mutex +} + +// RemoteFile is a file that has been indexed by the RemoteFS. It implements the RolodexFile interface. +type RemoteFile struct { + filename string + name string + extension FileExtension + data []byte + fullPath string + URL *url.URL + lastModified time.Time + seekingErrors []error + index atomic.Value // *SpecIndex + parsed *yaml.Node + offset int64 + indexOnce sync.Once + contentLock sync.Mutex + indexingComplete chan struct{} // Closed when indexing is complete +} + +// GetFileName returns the name of the file. +func (f *RemoteFile) GetFileName() string { + return f.filename +} + +// GetContent returns the content of the file as a string. +func (f *RemoteFile) GetContent() string { + return string(f.data) +} + +// GetContentAsYAMLNode returns the content of the file as a yaml.Node. +func (f *RemoteFile) GetContentAsYAMLNode() (*yaml.Node, error) { + f.contentLock.Lock() + idx := f.GetIndex() + if idx != nil && idx.root != nil { + f.contentLock.Unlock() + return idx.GetRootNode(), nil + } + if f.data == nil { + f.contentLock.Unlock() + return nil, fmt.Errorf("no data to parse for file: %s", f.fullPath) + } + var root yaml.Node + err := yaml.Unmarshal(f.data, &root) + + if err != nil { + f.contentLock.Unlock() + return nil, err + } + if idx != nil && idx.root == nil { + idx.root = &root + } + if f.parsed == nil { + f.parsed = &root + } + f.contentLock.Unlock() + return &root, nil +} + +// GetFileExtension returns the file extension of the file. +func (f *RemoteFile) GetFileExtension() FileExtension { + return f.extension +} + +// GetLastModified returns the last modified time of the file. +func (f *RemoteFile) GetLastModified() time.Time { + return f.lastModified +} + +// GetErrors returns any errors that occurred while reading the file. +func (f *RemoteFile) GetErrors() []error { + return f.seekingErrors +} + +// GetFullPath returns the full path of the file. +func (f *RemoteFile) GetFullPath() string { + return f.fullPath +} + +// fs.FileInfo interfaces + +// Name returns the name of the file. +func (f *RemoteFile) Name() string { + return f.name +} + +// Size returns the size of the file. +func (f *RemoteFile) Size() int64 { + return int64(len(f.data)) +} + +// Mode returns the file mode bits for the file. +func (f *RemoteFile) Mode() fs.FileMode { + return fs.FileMode(0) +} + +// ModTime returns the modification time of the file. +func (f *RemoteFile) ModTime() time.Time { + return f.lastModified +} + +// IsDir returns true if the file is a directory. +func (f *RemoteFile) IsDir() bool { + return false +} + +// fs.File interfaces + +// Sys returns the underlying data source (always returns nil) +func (f *RemoteFile) Sys() interface{} { + return nil +} + +// Close closes the file (doesn't do anything, returns no error) +func (f *RemoteFile) Close() error { + return nil +} + +// Stat returns the FileInfo for the file. +func (f *RemoteFile) Stat() (fs.FileInfo, error) { + return f, nil +} + +// Read reads the file. Makes it compatible with io.Reader. +func (f *RemoteFile) Read(b []byte) (int, error) { + if f.offset >= int64(len(f.data)) { + return 0, io.EOF + } + if f.offset < 0 { + return 0, &fs.PathError{Op: "read", Path: f.name, Err: fs.ErrInvalid} + } + n := copy(b, f.data[f.offset:]) + f.offset += int64(n) + return n, nil +} + +// Index indexes the file and returns a *SpecIndex, any errors are returned as well. +func (f *RemoteFile) Index(ctx context.Context, config *SpecIndexConfig) (*SpecIndex, error) { + var result *SpecIndex + var resultErr error + f.indexOnce.Do(func() { + + content := f.data + // first, we must parse the content of the file, + // the check is bypassed, so as long as it's readable, we're good. + info, _ := datamodel.ExtractSpecInfoWithDocumentCheck(content, true) + if config.SpecInfo == nil { + config.SpecInfo = info + } + index := NewSpecIndexWithConfigAndContext(ctx, info.RootNode, config) + index.specAbsolutePath = config.SpecAbsolutePath + + if info.RootNode == nil && index.root == nil { + resultErr = fmt.Errorf("nothing was extracted from the file '%s'", f.fullPath) + } else { + result = index + f.index.Store(index) + } + }) + if v := f.index.Load(); v != nil { + result = v.(*SpecIndex) + } + return result, resultErr +} + +// GetIndex returns the index for the file. +func (f *RemoteFile) GetIndex() *SpecIndex { + if v := f.index.Load(); v != nil { + return v.(*SpecIndex) + } + return nil +} + +// WaitForIndexing blocks until the file's index is ready. +// This is used to coordinate between concurrent goroutines when one is loading +// a file and another needs to use its index. +func (f *RemoteFile) WaitForIndexing() { + if f.indexingComplete != nil { + <-f.indexingComplete + } +} + +// signalIndexingComplete marks the file as ready for use. +// This should be called after indexing completes. +func (f *RemoteFile) signalIndexingComplete() { + if f.indexingComplete != nil { + select { + case <-f.indexingComplete: + // Already closed, do nothing + default: + close(f.indexingComplete) + } + } +} + +// NewRemoteFSWithConfig creates a new RemoteFS using the supplied SpecIndexConfig. +func NewRemoteFSWithConfig(specIndexConfig *SpecIndexConfig) (*RemoteFS, error) { + if specIndexConfig == nil { + return nil, errors.New("no spec index config provided") + } + remoteRootURL := specIndexConfig.BaseURL + log := specIndexConfig.Logger + if log == nil { + log = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ + Level: slog.LevelError, + })) + } + + rfs := &RemoteFS{ + indexConfig: specIndexConfig, + logger: log, + rootURLParsed: remoteRootURL, + FetchChannel: make(chan *RemoteFile), + } + if remoteRootURL != nil { + rfs.rootURL = remoteRootURL.String() + } + if specIndexConfig.RemoteURLHandler != nil { + rfs.RemoteHandlerFunc = specIndexConfig.RemoteURLHandler + } else { + // default http client + client := &http.Client{ + Timeout: time.Second * 120, + } + rfs.RemoteHandlerFunc = func(url string) (*http.Response, error) { + return client.Get(url) + } + } + return rfs, nil +} + +// NewRemoteFSWithRootURL creates a new RemoteFS using the supplied root URL. +func NewRemoteFSWithRootURL(rootURL string) (*RemoteFS, error) { + remoteRootURL, err := url.Parse(rootURL) + if err != nil { + return nil, err + } + config := CreateOpenAPIIndexConfig() + config.BaseURL = remoteRootURL + return NewRemoteFSWithConfig(config) +} + +// SetRemoteHandlerFunc sets the remote handler function. +func (i *RemoteFS) SetRemoteHandlerFunc(handlerFunc utils.RemoteURLHandler) { + i.RemoteHandlerFunc = handlerFunc +} + +// SetIndexConfig sets the index configuration. +func (i *RemoteFS) SetIndexConfig(config *SpecIndexConfig) { + i.indexConfig = config +} + +// GetFiles returns the files that have been indexed. +func (i *RemoteFS) GetFiles() map[string]RolodexFile { + files := make(map[string]RolodexFile) + i.Files.Range(func(key, value interface{}) bool { + files[key.(string)] = value.(*RemoteFile) + return true + }) + i.extractedFiles = files + return files +} + +// GetErrors returns any errors that occurred during the indexing process. +func (i *RemoteFS) GetErrors() []error { + return i.remoteErrors +} + +type waiterRemote struct { + f string + done bool + file *RemoteFile + listeners int + error error + mu sync.Mutex +} + +func (i *RemoteFS) OpenWithContext(ctx context.Context, remoteURL string) (fs.File, error) { + if i.indexConfig != nil && !i.indexConfig.AllowRemoteLookup { + return nil, fmt.Errorf("remote lookup for '%s' is not allowed, please set "+ + "AllowRemoteLookup to true as part of the index configuration", remoteURL) + } + + if !strings.HasPrefix(remoteURL, "http") { + if i.logger != nil { + i.logger.Debug("[rolodex remote loader] not a remote file, ignoring", "file", remoteURL) + } + return nil, fmt.Errorf("not a remote file: %s", remoteURL) + } + + remoteParsedURL, err := url.Parse(remoteURL) + if err != nil { + return nil, err + } + remoteParsedURLOriginal, _ := url.Parse(remoteURL) + + // try path first + if r, ok := i.Files.Load(remoteParsedURL.Path); ok { + return r.(*RemoteFile), nil + } + + fileExt := ExtractFileType(remoteParsedURL.Path) + + // Handle unsupported file extensions with content detection if enabled + if fileExt == UNSUPPORTED { + if i.indexConfig != nil && i.indexConfig.AllowUnknownExtensionContentDetection { + if i.logger != nil { + i.logger.Debug("[rolodex remote loader] attempting content detection for unknown file extension", "url", remoteParsedURL.String()) + } + // Attempt to detect content type + fileExt = detectRemoteContentType(remoteParsedURL.String(), i.RemoteHandlerFunc, i.logger) + if fileExt == UNSUPPORTED { + // Clear cache entry on completion to keep memory usage low + defer func() { + contentDetectionMutex.Lock() + delete(contentDetectionCache, remoteParsedURL.String()) + contentDetectionMutex.Unlock() + }() + + i.remoteErrors = append(i.remoteErrors, fs.ErrInvalid) + if i.logger != nil { + i.logger.Warn("[rolodex remote loader] content detection failed, unsupported content type", "url", remoteParsedURL.String()) + } + return nil, &fs.PathError{Op: "open", Path: remoteURL, Err: fs.ErrInvalid} + } + if i.logger != nil { + typeStr := "UNSUPPORTED" + if fileExt == JSON { + typeStr = "JSON" + } else if fileExt == YAML { + typeStr = "YAML" + } + i.logger.Debug("[rolodex remote loader] content detection successful", "url", remoteParsedURL.String(), "detectedType", typeStr) + } + } else { + // Content detection disabled, treat as unsupported + i.remoteErrors = append(i.remoteErrors, fs.ErrInvalid) + if i.logger != nil { + i.logger.Warn("[rolodex remote loader] unknown file extension and content detection disabled", "file", remoteURL, "remoteURL", remoteParsedURL.String()) + } + return nil, &fs.PathError{Op: "open", Path: remoteURL, Err: fs.ErrInvalid} + } + } + + // Use LoadOrStore to atomically check if someone is already processing this file. + // This prevents the race condition where two goroutines both see "not processing" + // and both start processing the same file. + processingWaiter := &waiterRemote{f: remoteParsedURL.Path} + processingWaiter.mu.Lock() + + if existing, loaded := i.ProcessingFiles.LoadOrStore(remoteParsedURL.Path, processingWaiter); loaded { + // Someone else is already processing this file, wait for them + processingWaiter.mu.Unlock() // Release our unused waiter's lock + wait := existing.(*waiterRemote) + + wait.mu.Lock() + i.logger.Debug("[rolodex remote loader] waiting for existing fetch to complete", "file", remoteURL, + "remoteURL", remoteParsedURL.String()) + f := wait.file + e := wait.error + i.logger.Debug("[rolodex remote loader]: waiting done, remote completed, returning file", "file", + remoteParsedURL.String(), "listeners", wait.listeners) + wait.mu.Unlock() + return f, e + } + + // We successfully stored our waiter, so we're responsible for processing this file + + // if the remote URL is absolute (http:// or https://), and we have a rootURL defined, we need to override + // the host being defined by this URL, and use the rootURL instead, but keep the path. + if i.rootURLParsed != nil { + remoteParsedURL.Host = i.rootURLParsed.Host + remoteParsedURL.Scheme = i.rootURLParsed.Scheme + // this has been disabled, because I don't think it has value, it causes more problems than it solves currently. + // if !strings.HasPrefix(remoteParsedURL.Path, "/") { + // remoteParsedURL.Path = filepath.Join(i.rootURLParsed.Path, remoteParsedURL.Path) + // remoteParsedURL.Path = strings.ReplaceAll(remoteParsedURL.Path, "\\", "/") + // } + } + + if remoteParsedURL.Scheme == "" { + + processingWaiter.done = true + i.ProcessingFiles.Delete(remoteParsedURL.Path) + processingWaiter.mu.Unlock() + return nil, nil // not a remote file, nothing wrong with that - just we can't keep looking here partner. + } + + i.logger.Debug("[rolodex remote loader] loading remote file", "file", remoteURL, "remoteURL", remoteParsedURL.String()) + + response, clientErr := i.RemoteHandlerFunc(remoteParsedURL.String()) + if clientErr != nil { + + i.errMutex.Lock() + i.remoteErrors = append(i.remoteErrors, clientErr) + i.errMutex.Unlock() + + // remove from processing + processingWaiter.done = true + i.ProcessingFiles.Delete(remoteParsedURL.Path) + processingWaiter.mu.Unlock() + + if response != nil { + i.logger.Error("client error", "error", clientErr, "status", response.StatusCode) + } else { + i.logger.Error("client error", "error", clientErr.Error()) + } + return nil, clientErr + } + if response == nil { + // remove from processing + processingWaiter.done = true + i.ProcessingFiles.Delete(remoteParsedURL.Path) + processingWaiter.mu.Unlock() + return nil, fmt.Errorf("empty response from remote URL: %s", remoteParsedURL.String()) + } + responseBytes, readError := io.ReadAll(response.Body) + if readError != nil { + + // remove from processing + processingWaiter.error = readError + processingWaiter.done = true + i.ProcessingFiles.Delete(remoteParsedURL.Path) + processingWaiter.mu.Unlock() + return nil, fmt.Errorf("error reading bytes from remote file '%s': [%s]", + remoteParsedURL.String(), readError.Error()) + } + + if response.StatusCode >= 400 { + + // remove from processing + processingWaiter.error = fmt.Errorf("remote file '%s' returned status code %d", remoteParsedURL.String(), response.StatusCode) + processingWaiter.done = true + i.ProcessingFiles.Delete(remoteParsedURL.Path) + i.logger.Error("unable to fetch remote document", + "file", remoteParsedURL.Path, "status", response.StatusCode, "resp", string(responseBytes)) + processingWaiter.mu.Unlock() + return nil, fmt.Errorf("unable to fetch remote document '%s' (error %d)", remoteParsedURL.String(), + response.StatusCode) + } + + absolutePath := remoteParsedURL.Path + + // extract last modified from response + lastModified := response.Header.Get("Last-Modified") + + // parse the last modified date into a time object + lastModifiedTime, parseErr := time.Parse(time.RFC1123, lastModified) + + if parseErr != nil { + // can't extract last modified, so use now + lastModifiedTime = time.Now() + } + + filename := filepath.Base(remoteParsedURL.Path) + + remoteFile := &RemoteFile{ + filename: filename, + name: remoteParsedURL.Path, + extension: fileExt, + data: responseBytes, + fullPath: remoteParsedURL.String(), + URL: remoteParsedURL, + lastModified: lastModifiedTime, + indexingComplete: make(chan struct{}), + } + + copiedCfg := *i.indexConfig + + newBase := fmt.Sprintf("%s://%s%s", remoteParsedURLOriginal.Scheme, remoteParsedURLOriginal.Host, + filepath.Dir(remoteParsedURL.Path)) + newBaseURL, _ := url.Parse(newBase) + + if newBaseURL != nil { + copiedCfg.BaseURL = newBaseURL + } + copiedCfg.SpecAbsolutePath = remoteParsedURL.String() + // Force sequential extraction for child indexes to avoid cascading async issues. + // When the main index uses async mode, spawning async operations for each remote + // file creates complex timing dependencies that can result in missing references. + copiedCfg.ExtractRefsSequentially = true + + if len(remoteFile.data) > 0 { + i.logger.Debug("[rolodex remote loaded] successfully loaded file", "file", absolutePath) + } + + // Store in Files and release the waiter BEFORE indexing to prevent deadlocks. + // If file A needs file B and file B needs file A, holding the lock during indexing + // would cause a deadlock. The indexOnce in Index handles concurrent access safely. + i.Files.Store(absolutePath, remoteFile) + + // remove from processing + processingWaiter.file = remoteFile + processingWaiter.done = true + i.ProcessingFiles.Delete(remoteParsedURL.Path) + processingWaiter.mu.Unlock() + + // Add this file to the context's indexing set to prevent deadlocks + // when circular references cause the same file to be looked up recursively. + // We add multiple forms of the URL to handle different lookup patterns: + // - absolutePath: the path portion (e.g., /second.yaml) + // - remoteParsedURL.String(): the normalized URL (after host override) + // - remoteParsedURLOriginal.String(): the ORIGINAL URL before host normalization + // The original URL is critical because lookupRolodex uses the original reference URL + // when checking IsFileBeingIndexed, not the normalized one. + indexingCtx := AddIndexingFile(ctx, absolutePath) + indexingCtx = AddIndexingFile(indexingCtx, remoteParsedURL.String()) + indexingCtx = AddIndexingFile(indexingCtx, remoteParsedURLOriginal.String()) + + // Now index the file AFTER releasing the lock + idx, idxError := remoteFile.Index(indexingCtx, &copiedCfg) + + if idxError != nil && idx == nil { + i.errMutex.Lock() + i.remoteErrors = append(i.remoteErrors, idxError) + i.errMutex.Unlock() + } else { + + // for each index, we need a resolver + resolver := NewResolver(idx) + idx.resolver = resolver + idx.BuildIndex() + if i.rolodex != nil { + i.rolodex.AddExternalIndex(idx, remoteParsedURL.String()) + } + } + + // Signal that indexing is complete - other goroutines waiting for this file can proceed + remoteFile.signalIndexingComplete() + + return remoteFile, errors.Join(i.remoteErrors...) +} + +// Open opens a file, returning it or an error. If the file is not found, the error is of type *PathError. +func (i *RemoteFS) Open(remoteURL string) (fs.File, error) { + return i.OpenWithContext(context.Background(), remoteURL) +} diff --git a/vendor/github.com/pb33f/libopenapi/index/schema_id_context.go b/vendor/github.com/pb33f/libopenapi/index/schema_id_context.go new file mode 100644 index 000000000..5b9995156 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/index/schema_id_context.go @@ -0,0 +1,57 @@ +// Copyright 2022-2025 Princess Beef Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package index + +import ( + "context" +) + +// ResolvingIdsKey is the context key for tracking $id values currently being resolved. +const ResolvingIdsKey ContextKey = "resolvingIds" + +// SchemaIdScopeKey is the context key for tracking the current $id scope during extraction. +const SchemaIdScopeKey ContextKey = "schemaIdScope" + +// GetSchemaIdScope returns the current $id scope from the context. +func GetSchemaIdScope(ctx context.Context) *SchemaIdScope { + if v := ctx.Value(SchemaIdScopeKey); v != nil { + return v.(*SchemaIdScope) + } + return nil +} + +// WithSchemaIdScope returns a new context with the given $id scope. +func WithSchemaIdScope(ctx context.Context, scope *SchemaIdScope) context.Context { + return context.WithValue(ctx, SchemaIdScopeKey, scope) +} + +// GetResolvingIds returns the set of $id values currently being resolved in the call chain. +func GetResolvingIds(ctx context.Context) map[string]bool { + if v := ctx.Value(ResolvingIdsKey); v != nil { + return v.(map[string]bool) + } + return nil +} + +// AddResolvingId adds a $id to the resolving set in the context. +// Returns a new context with the updated set (copy-on-write for thread safety). +func AddResolvingId(ctx context.Context, id string) context.Context { + existing := GetResolvingIds(ctx) + newSet := make(map[string]bool, len(existing)+1) + for k, v := range existing { + newSet[k] = v + } + newSet[id] = true + return context.WithValue(ctx, ResolvingIdsKey, newSet) +} + +// IsIdBeingResolved checks if a $id is currently being resolved in the call chain. +// Used to detect and prevent circular $id resolution. +func IsIdBeingResolved(ctx context.Context, id string) bool { + ids := GetResolvingIds(ctx) + if ids == nil { + return false + } + return ids[id] +} diff --git a/vendor/github.com/pb33f/libopenapi/index/schema_id_registry.go b/vendor/github.com/pb33f/libopenapi/index/schema_id_registry.go new file mode 100644 index 000000000..0661c0e6b --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/index/schema_id_registry.go @@ -0,0 +1,77 @@ +// Copyright 2022-2025 Princess Beef Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package index + +import ( + "fmt" + "log/slog" +) + +// schemaIdRegistrationResult holds the result of a schema ID registration attempt. +type schemaIdRegistrationResult struct { + registered bool // true if successfully registered + duplicate bool // true if a duplicate was found (first-wins policy applied) + key string // the key used for registration +} + +// registerSchemaIdToRegistry is the common registration logic for both SpecIndex and Rolodex. +// Returns the registration result. Duplicates are logged but not treated as errors. +func registerSchemaIdToRegistry( + registry map[string]*SchemaIdEntry, + entry *SchemaIdEntry, + logger *slog.Logger, + registryName string, +) (*schemaIdRegistrationResult, error) { + if entry == nil { + return nil, fmt.Errorf("cannot register nil SchemaIdEntry") + } + + if err := ValidateSchemaId(entry.Id); err != nil { + if logger != nil { + logger.Warn("invalid $id value, skipping registration", + "registry", registryName, + "id", entry.Id, + "error", err.Error(), + "line", entry.Line, + "column", entry.Column) + } + return nil, err + } + + key := entry.GetKey() + + if existing, ok := registry[key]; ok { + if logger != nil { + existingPath := "" + newPath := "" + if existing.Index != nil { + existingPath = existing.Index.GetSpecAbsolutePath() + } + if entry.Index != nil { + newPath = entry.Index.GetSpecAbsolutePath() + } + logger.Warn("duplicate $id detected, keeping first registration", + "registry", registryName, + "id", key, + "first_location", fmt.Sprintf("%s:%d:%d", existingPath, existing.Line, existing.Column), + "duplicate_location", fmt.Sprintf("%s:%d:%d", newPath, entry.Line, entry.Column)) + } + return &schemaIdRegistrationResult{registered: false, duplicate: true, key: key}, nil + } + + registry[key] = entry + return &schemaIdRegistrationResult{registered: true, duplicate: false, key: key}, nil +} + +// copySchemaIdRegistry creates a defensive copy of a schema ID registry. +func copySchemaIdRegistry(registry map[string]*SchemaIdEntry) map[string]*SchemaIdEntry { + if registry == nil { + return make(map[string]*SchemaIdEntry) + } + result := make(map[string]*SchemaIdEntry, len(registry)) + for k, v := range registry { + result[k] = v + } + return result +} diff --git a/vendor/github.com/pb33f/libopenapi/index/schema_id_resolve.go b/vendor/github.com/pb33f/libopenapi/index/schema_id_resolve.go new file mode 100644 index 000000000..1dd948c30 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/index/schema_id_resolve.go @@ -0,0 +1,285 @@ +// Copyright 2022-2025 Princess Beef Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package index + +import ( + "fmt" + "net/url" + "strings" + + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// FindSchemaIdInNode looks for a $id key in a mapping node and returns its value. +// Returns empty string if not found or if the node is not a mapping. +func FindSchemaIdInNode(node *yaml.Node) string { + if node == nil { + return "" + } + if node.Kind == yaml.DocumentNode && len(node.Content) > 0 { + node = node.Content[0] + } + if node == nil || node.Kind != yaml.MappingNode { + return "" + } + for i := 0; i < len(node.Content)-1; i += 2 { + if node.Content[i].Value == "$id" && utils.IsNodeStringValue(node.Content[i+1]) { + return node.Content[i+1].Value + } + } + return "" +} + +// ValidateSchemaId checks if a $id value is valid per JSON Schema 2020-12 spec. +// Per the spec, $id MUST NOT contain a fragment identifier (#). +func ValidateSchemaId(id string) error { + if id == "" { + return fmt.Errorf("$id cannot be empty") + } + if strings.Contains(id, "#") { + return fmt.Errorf("$id must not contain fragment identifier '#': %s (use $anchor instead)", id) + } + return nil +} + +// ResolveSchemaId resolves a potentially relative $id against a base URI. +// Returns the fully resolved absolute URI. +func ResolveSchemaId(id string, baseUri string) (string, error) { + if id == "" { + return "", fmt.Errorf("$id cannot be empty") + } + + parsedId, err := url.Parse(id) + if err != nil { + return "", fmt.Errorf("invalid $id URI: %s: %w", id, err) + } + + // Absolute $id is used directly + if parsedId.IsAbs() { + return id, nil + } + + // Relative $id without base - return as-is for later resolution + if baseUri == "" { + return id, nil + } + + parsedBase, err := url.Parse(baseUri) + if err != nil { + return "", fmt.Errorf("invalid base URI: %s: %w", baseUri, err) + } + + resolved := parsedBase.ResolveReference(parsedId) + return resolved.String(), nil +} + +// ResolveRefAgainstSchemaId resolves a $ref value against the current $id scope. +// Absolute refs are returned as-is; relative refs are resolved against the nearest ancestor $id. +func ResolveRefAgainstSchemaId(ref string, scope *SchemaIdScope) (string, error) { + if ref == "" { + return "", fmt.Errorf("$ref cannot be empty") + } + + parsedRef, err := url.Parse(ref) + if err != nil { + return "", fmt.Errorf("invalid $ref URI: %s: %w", ref, err) + } + + if parsedRef.IsAbs() { + return ref, nil + } + + if scope == nil || scope.BaseUri == "" { + return ref, nil + } + + parsedBase, err := url.Parse(scope.BaseUri) + if err != nil { + return "", fmt.Errorf("invalid base URI in scope: %s: %w", scope.BaseUri, err) + } + + resolved := parsedBase.ResolveReference(parsedRef) + return resolved.String(), nil +} + +// resolveRefWithSchemaBase resolves a ref against a base URI if provided. +// Returns the original ref if no base is provided or resolution fails. +func resolveRefWithSchemaBase(ref string, base string) string { + if ref == "" || base == "" { + return ref + } + resolved, err := ResolveRefAgainstSchemaId(ref, &SchemaIdScope{BaseUri: base}) + if err != nil || resolved == "" { + return ref + } + return resolved +} + +// SplitRefFragment splits a reference into base URI and fragment components. +// Example: "https://example.com/schema.json#/definitions/Pet" -> +// baseUri="https://example.com/schema.json", fragment="#/definitions/Pet" +func SplitRefFragment(ref string) (baseUri string, fragment string) { + idx := strings.Index(ref, "#") + if idx == -1 { + return ref, "" + } + return ref[:idx], ref[idx:] +} + +// ResolveRefViaSchemaId attempts to resolve a $ref via the $id registry. +// Implements JSON Schema 2020-12 $id-based resolution: +// 1. Split ref into base URI and fragment +// 2. Look up base URI in $id registry +// 3. Navigate to fragment within found schema if present +// Returns nil if the ref cannot be resolved via $id. +func (index *SpecIndex) ResolveRefViaSchemaId(ref string) *Reference { + if ref == "" { + return nil + } + + baseUri, fragment := SplitRefFragment(ref) + + // Local fragment refs are not $id-based + if baseUri == "" { + return nil + } + + // Check local index first, then rolodex global registry + entry := index.GetSchemaById(baseUri) + if entry == nil && index.rolodex != nil { + entry = index.rolodex.LookupSchemaById(baseUri) + } + + if entry == nil { + return nil + } + + r := &Reference{ + FullDefinition: ref, + Definition: ref, + Name: baseUri, + RawRef: ref, + SchemaIdBase: baseUri, + Node: entry.SchemaNode, + IsRemote: entry.Index != index, + RemoteLocation: entry.Index.GetSpecAbsolutePath(), + Index: entry.Index, + } + + // Navigate to fragment if present + if fragment != "" && entry.SchemaNode != nil { + if fragmentNode := navigateToFragment(entry.SchemaNode, fragment); fragmentNode != nil { + r.Node = fragmentNode + } + } + + return r +} + +func (index *SpecIndex) resolveRefViaSchemaIdPath(path string) *Reference { + if path == "" || !strings.HasPrefix(path, "/") { + return nil + } + + entries := index.GetAllSchemaIds() + if index.rolodex != nil { + global := index.rolodex.GetAllGlobalSchemaIds() + if len(global) > 0 { + entries = global + } + } + + var match *SchemaIdEntry + for _, entry := range entries { + if entry == nil { + continue + } + u, err := url.Parse(entry.GetKey()) + if err != nil || !u.IsAbs() || u.Path == "" { + continue + } + if u.Path == path { + if match != nil { + return nil + } + match = entry + } + } + if match == nil { + return nil + } + + baseUri := match.GetKey() + return &Reference{ + FullDefinition: baseUri, + Definition: baseUri, + Name: baseUri, + RawRef: path, + SchemaIdBase: baseUri, + Node: match.SchemaNode, + IsRemote: match.Index != index, + RemoteLocation: match.Index.GetSpecAbsolutePath(), + Index: match.Index, + } +} + +// navigateToFragment navigates to a JSON pointer fragment within a YAML node. +// Fragment format: "#/path/to/node" or "/path/to/node" +func navigateToFragment(root *yaml.Node, fragment string) *yaml.Node { + if root == nil || fragment == "" { + return nil + } + + path := strings.TrimPrefix(fragment, "#") + if path == "" || path == "/" { + return root + } + + segments := strings.Split(strings.TrimPrefix(path, "/"), "/") + + current := root + if current.Kind == yaml.DocumentNode && len(current.Content) > 0 { + current = current.Content[0] + } + + for _, segment := range segments { + if segment == "" { + continue + } + + // Decode JSON pointer escapes (~1 = /, ~0 = ~) + segment = strings.ReplaceAll(segment, "~1", "/") + segment = strings.ReplaceAll(segment, "~0", "~") + + found := false + if current.Kind == yaml.MappingNode { + for i := 0; i < len(current.Content)-1; i += 2 { + if current.Content[i].Value == segment { + current = current.Content[i+1] + found = true + break + } + } + } else if current.Kind == yaml.SequenceNode { + idx := 0 + for _, c := range segment { + if c < '0' || c > '9' { + return nil + } + idx = idx*10 + int(c-'0') + } + if idx < len(current.Content) { + current = current.Content[idx] + found = true + } + } + + if !found { + return nil + } + } + + return current +} diff --git a/vendor/github.com/pb33f/libopenapi/index/schema_id_types.go b/vendor/github.com/pb33f/libopenapi/index/schema_id_types.go new file mode 100644 index 000000000..752b01fdb --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/index/schema_id_types.go @@ -0,0 +1,73 @@ +// Copyright 2022-2025 Princess Beef Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package index + +import ( + "go.yaml.in/yaml/v4" +) + +// SchemaIdEntry represents a schema registered by its JSON Schema 2020-12 $id. +// This enables $ref resolution against $id values per JSON Schema specification. +type SchemaIdEntry struct { + Id string // The $id value as declared in the schema + ResolvedUri string // Fully resolved absolute URI after applying base URI resolution + SchemaNode *yaml.Node // The YAML node containing the schema with this $id + ParentId string // The $id of the parent scope (for nested schemas with $id) + Index *SpecIndex // Reference to the SpecIndex containing this schema + DefinitionPath string // JSON pointer path to this schema (e.g., #/components/schemas/Pet) + Line int // Line number where $id was declared (for error reporting) + Column int // Column number where $id was declared (for error reporting) +} + +// GetKey returns the registry key for this entry. +// Uses ResolvedUri if available, otherwise falls back to Id. +func (e *SchemaIdEntry) GetKey() string { + if e.ResolvedUri != "" { + return e.ResolvedUri + } + return e.Id +} + +// SchemaIdScope tracks the resolution context during tree walking. +// Used to maintain the base URI hierarchy when extracting $id values. +type SchemaIdScope struct { + BaseUri string // Current base URI for relative $id and $ref resolution + Chain []string // Stack of $id URIs from root to current location +} + +// NewSchemaIdScope initializes scope tracking for base URI resolution during schema tree traversal. +func NewSchemaIdScope(baseUri string) *SchemaIdScope { + return &SchemaIdScope{ + BaseUri: baseUri, + Chain: make([]string, 0), + } +} + +// PushId updates the base URI context when entering a schema with $id. +// The new $id becomes the base for resolving relative references in child schemas. +func (s *SchemaIdScope) PushId(id string) { + s.Chain = append(s.Chain, id) + s.BaseUri = id +} + +// PopId restores the previous base URI when exiting a schema scope. +func (s *SchemaIdScope) PopId() { + if len(s.Chain) > 0 { + s.Chain = s.Chain[:len(s.Chain)-1] + if len(s.Chain) > 0 { + s.BaseUri = s.Chain[len(s.Chain)-1] + } + } +} + +// Copy creates an independent scope for exploring alternative branches without +// affecting the parent scope's state (used in anyOf/oneOf/allOf traversal). +func (s *SchemaIdScope) Copy() *SchemaIdScope { + chainCopy := make([]string, len(s.Chain)) + copy(chainCopy, s.Chain) + return &SchemaIdScope{ + BaseUri: s.BaseUri, + Chain: chainCopy, + } +} diff --git a/vendor/github.com/pb33f/libopenapi/index/search_index.go b/vendor/github.com/pb33f/libopenapi/index/search_index.go new file mode 100644 index 000000000..6362177fc --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/index/search_index.go @@ -0,0 +1,385 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package index + +import ( + "context" + "fmt" + "net/url" + "path/filepath" + "strings" +) + +type ContextKey string + +const ( + CurrentPathKey ContextKey = "currentPath" + FoundIndexKey ContextKey = "foundIndex" + RootIndexKey ContextKey = "currentIndex" + IndexingFilesKey ContextKey = "indexingFiles" // Tracks files being indexed in current call chain +) + +// GetIndexingFiles returns the set of files currently being indexed in the call chain. +// Returns nil if not set. +func GetIndexingFiles(ctx context.Context) map[string]bool { + if v := ctx.Value(IndexingFilesKey); v != nil { + return v.(map[string]bool) + } + return nil +} + +// AddIndexingFile adds a file to the indexing set in the context. +// Returns a new context with the updated set. +func AddIndexingFile(ctx context.Context, filePath string) context.Context { + existing := GetIndexingFiles(ctx) + newSet := make(map[string]bool) + for k, v := range existing { + newSet[k] = v + } + newSet[filePath] = true + return context.WithValue(ctx, IndexingFilesKey, newSet) +} + +// IsFileBeingIndexed checks if a file is currently being indexed in the call chain. +// For HTTP URLs, it also checks if the PATH portion matches any indexed file, +// since the same file might be referenced with different hostnames (which get normalized +// to a common server). +func IsFileBeingIndexed(ctx context.Context, filePath string) bool { + files := GetIndexingFiles(ctx) + if files == nil { + return false + } + // Direct match + if files[filePath] { + return true + } + // For HTTP URLs, also check if the path matches any indexed file's path + if strings.HasPrefix(filePath, "http") { + if u, err := url.Parse(filePath); err == nil { + // Check if the path portion matches any indexed file + for indexedFile := range files { + if strings.HasPrefix(indexedFile, "http") { + if indexedU, err2 := url.Parse(indexedFile); err2 == nil { + // Compare paths (the filename portion) + if u.Path == indexedU.Path || filepath.Base(u.Path) == filepath.Base(indexedU.Path) { + return true + } + } + } else { + // Compare with non-HTTP paths (just the filename) + if filepath.Base(u.Path) == filepath.Base(indexedFile) { + return true + } + } + } + } + } + return false +} + +func (index *SpecIndex) SearchIndexForReferenceByReference(fullRef *Reference) (*Reference, *SpecIndex) { + r, idx, _ := index.SearchIndexForReferenceByReferenceWithContext(context.Background(), fullRef) + return r, idx +} + +// SearchIndexForReference searches the index for a reference, first looking through the mapped references +// and then externalSpecIndex for a match. If no match is found, it will recursively search the child indexes +// extracted when parsing the OpenAPI Spec. +func (index *SpecIndex) SearchIndexForReference(ref string) (*Reference, *SpecIndex) { + return index.SearchIndexForReferenceByReference(&Reference{FullDefinition: ref}) +} + +func (index *SpecIndex) SearchIndexForReferenceWithContext(ctx context.Context, ref string) (*Reference, *SpecIndex, context.Context) { + return index.SearchIndexForReferenceByReferenceWithContext(ctx, &Reference{FullDefinition: ref}) +} + +func (index *SpecIndex) SearchIndexForReferenceByReferenceWithContext(ctx context.Context, searchRef *Reference) (*Reference, *SpecIndex, context.Context) { + if index.cache != nil { + if v, ok := index.cache.Load(searchRef.FullDefinition); ok { + idx := index.extractIndex(v.(*Reference)) + return v.(*Reference), idx, context.WithValue(ctx, CurrentPathKey, v.(*Reference).RemoteLocation) + } + } + + schemaIdBase := searchRef.SchemaIdBase + if schemaIdBase == "" { + if scope := GetSchemaIdScope(ctx); scope != nil && scope.BaseUri != "" { + schemaIdBase = scope.BaseUri + } + } + + rawRef := searchRef.FullDefinition + if searchRef.RawRef != "" && schemaIdBase != "" { + rawRef = searchRef.RawRef + } + normalizedRef := resolveRefWithSchemaBase(rawRef, schemaIdBase) + + if index.cache != nil && normalizedRef != searchRef.FullDefinition { + if v, ok := index.cache.Load(normalizedRef); ok { + idx := index.extractIndex(v.(*Reference)) + return v.(*Reference), idx, context.WithValue(ctx, CurrentPathKey, v.(*Reference).RemoteLocation) + } + } + + // Try to resolve via JSON Schema 2020-12 $id registry first + // This handles refs like "a.json" resolving to schemas with $id: "https://example.com/a.json" + if resolved := index.ResolveRefViaSchemaId(normalizedRef); resolved != nil { + if index.cache != nil { + index.cache.Store(searchRef.FullDefinition, resolved) + if normalizedRef != searchRef.FullDefinition { + index.cache.Store(normalizedRef, resolved) + } + } + return resolved, resolved.Index, context.WithValue(ctx, CurrentPathKey, resolved.RemoteLocation) + } + pathRef := "" + if strings.HasPrefix(normalizedRef, "/") { + pathRef = normalizedRef + } else if strings.HasPrefix(rawRef, "/") { + pathRef = rawRef + } + if pathRef != "" { + if resolved := index.resolveRefViaSchemaIdPath(pathRef); resolved != nil { + if index.cache != nil { + index.cache.Store(searchRef.FullDefinition, resolved) + if normalizedRef != searchRef.FullDefinition { + index.cache.Store(normalizedRef, resolved) + } + } + return resolved, resolved.Index, context.WithValue(ctx, CurrentPathKey, resolved.RemoteLocation) + } + } + + ref := normalizedRef + refAlt := ref + absPath := index.specAbsolutePath + if searchRef.RemoteLocation != "" { + absPath = searchRef.RemoteLocation + } + if absPath == "" && index.config != nil { + absPath = index.config.BasePath + } + var roloLookup string + uri := strings.Split(ref, "#/") + if len(uri) == 2 { + if uri[0] != "" { + if strings.HasPrefix(uri[0], "http") { + roloLookup = searchRef.FullDefinition + } else { + if filepath.IsAbs(uri[0]) { + roloLookup = uri[0] + } else { + if filepath.Ext(absPath) != "" { + absPath = filepath.Dir(absPath) + } + roloLookup = index.resolveRelativeFilePath(absPath, uri[0]) + } + } + } else { + + if filepath.Ext(uri[1]) != "" { + roloLookup = absPath + } else { + roloLookup = "" + } + + ref = fmt.Sprintf("#/%s", uri[1]) + refAlt = fmt.Sprintf("%s#/%s", absPath, uri[1]) + + } + } else { + if filepath.IsAbs(uri[0]) { + roloLookup = uri[0] + } else { + if strings.HasPrefix(uri[0], "http") { + roloLookup = ref + } else { + if filepath.Ext(absPath) != "" { + absPath = filepath.Dir(absPath) + } + roloLookup = index.resolveRelativeFilePath(absPath, uri[0]) + } + } + ref = uri[0] + } + if strings.Contains(ref, "%") { + // decode the url. + ref, _ = url.QueryUnescape(ref) + refAlt, _ = url.QueryUnescape(refAlt) + } + + if r, ok := index.allMappedRefs[ref]; ok { + idx := index.extractIndex(r) + index.cache.Store(ref, r) + return r, idx, context.WithValue(ctx, CurrentPathKey, r.RemoteLocation) + } + + if r, ok := index.allMappedRefs[refAlt]; ok { + idx := index.extractIndex(r) + idx.cache.Store(refAlt, r) + return r, idx, context.WithValue(ctx, CurrentPathKey, r.RemoteLocation) + } + + if r, ok := index.allComponentSchemaDefinitions.Load(refAlt); ok { + rf := r.(*Reference) + idx := index.extractIndex(rf) + index.cache.Store(refAlt, r) + return rf, idx, context.WithValue(ctx, CurrentPathKey, rf.RemoteLocation) + } + + // check security schemes + if index.allSecuritySchemes != nil { + if r, ok := index.allSecuritySchemes.Load(refAlt); ok { + rf := r.(*Reference) + idx := index.extractIndex(rf) + index.cache.Store(refAlt, r) + return rf, idx, context.WithValue(ctx, CurrentPathKey, rf.RemoteLocation) + } + } + + // check the rolodex for the reference. + if roloLookup != "" { + + if strings.Contains(roloLookup, "#") { + roloLookup = strings.Split(roloLookup, "#")[0] + } + + b := filepath.Base(roloLookup) + sfn := index.GetSpecFileName() + + abp := index.GetSpecAbsolutePath() + + if b == sfn && roloLookup == abp { + // if the reference is the same as the spec file name, we should look through the index for the component + var r *Reference + if len(uri) == 2 { + r = index.FindComponentInRoot(ctx, fmt.Sprintf("#/%s", uri[1])) + } + return r, index, ctx + } + rFile, err := index.rolodex.Open(roloLookup) + if err != nil { + return nil, index, ctx + } + + // extract the index from the rolodex file. + if rFile != nil { + + n := rFile.GetFullPath() + refParsed := ref + + // do we have a relative reference and an exact match on the suffix? + if strings.HasPrefix(ref, "./") { + refParsed = strings.ReplaceAll(ref, "./", "") + } + + // if the reference starts with ../, then we need to create an absolute path from the current path context. + if strings.HasPrefix(ref, "../") { + // check if there is a current path in the context and then create an absolute path from it. + if currentPath, ok := ctx.Value(CurrentPathKey).(string); ok { + refParsed = filepath.Join(filepath.Dir(currentPath), ref) + } + } + + // Normalize separators for Windows comparisons. + normPath := filepath.ToSlash(n) + normRef := filepath.ToSlash(refParsed) + if strings.HasSuffix(normPath, normRef) { + node, _ := rFile.GetContentAsYAMLNode() + if node != nil { + r := &Reference{ + FullDefinition: n, + Definition: n, + IsRemote: true, + RemoteLocation: n, + Index: rFile.GetIndex(), + Node: node.Content[0], + ParentNode: node, + } + index.cache.Store(ref, r) + return r, rFile.GetIndex(), context.WithValue(ctx, CurrentPathKey, rFile.GetFullPath()) + } + } + + idx := rFile.GetIndex() + if index.resolver != nil { + index.resolverLock.Lock() + index.resolver.indexesVisited++ + index.resolverLock.Unlock() + } + if idx != nil { + + // check mapped refs. + if r, ok := idx.allMappedRefs[ref]; ok { + i := index.extractIndex(r) + return r, i, context.WithValue(ctx, CurrentPathKey, r.RemoteLocation) + } + + // build a collection of all the inline schemas and search them + // for the reference. + var d []*Reference + d = append(d, idx.allInlineSchemaDefinitions...) + d = append(d, idx.allRefSchemaDefinitions...) + d = append(d, idx.allInlineSchemaObjectDefinitions...) + for _, s := range d { + if s.FullDefinition == ref { + i := index.extractIndex(s) + idx.cache.Store(ref, s) + index.cache.Store(ref, s) + return s, i, context.WithValue(ctx, CurrentPathKey, s.RemoteLocation) + } + } + + // does component exist in the root? + node, _ := rFile.GetContentAsYAMLNode() + if node != nil { + var found *Reference + exp := strings.Split(ref, "#/") + compId := ref + + if len(exp) == 2 { + compId = fmt.Sprintf("#/%s", exp[1]) + found = FindComponent(ctx, node, compId, exp[0], idx) + } + if found == nil { + found = idx.FindComponent(ctx, ref) + } + + if found != nil { + i := index.extractIndex(found) + i.cache.Store(ref, found) + return found, i, context.WithValue(ctx, CurrentPathKey, found.RemoteLocation) + } + } + } + } + } + + if index.logger != nil { + // this is a last ditch effort. if this fails, all hope is lost. + if index.GetRolodex() != nil { + for _, i := range index.GetRolodex().GetIndexes() { + v := i.FindComponent(ctx, ref) + if v != nil { + return v, v.Index, ctx + } + } + } + index.logger.Error("unable to locate reference anywhere in the rolodex", "reference", ref) + } + return nil, index, ctx +} + +func (index *SpecIndex) extractIndex(r *Reference) *SpecIndex { + idx := r.Index + if idx != nil && r.Index.GetSpecAbsolutePath() != r.RemoteLocation { + for i := range r.Index.rolodex.indexes { + if r.Index.rolodex.indexes[i].GetSpecAbsolutePath() == r.RemoteLocation { + idx = r.Index.rolodex.indexes[i] + break + } + } + } + return idx +} diff --git a/vendor/github.com/pb33f/libopenapi/index/search_rolodex.go b/vendor/github.com/pb33f/libopenapi/index/search_rolodex.go new file mode 100644 index 000000000..6eb3f4bb4 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/index/search_rolodex.go @@ -0,0 +1,202 @@ +// Copyright 2023-2024 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package index + +import ( + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +// FindNodeOriginWithValue searches all indexes for the origin of a node with a specific value. If the node is found, a NodeOrigin +// is returned, otherwise nil is returned. The key and the value have to be provided. If the refNode and refValue are provided, the +// returned value will be the key origin, not the value origin. +func (r *Rolodex) FindNodeOriginWithValue(key, value, refNode *yaml.Node, refValue string) *NodeOrigin { + if key == nil { + return nil + } + keyOrigin := r.FindNodeOrigin(key) + var valueOrigin *NodeOrigin + var valueHash string + if value != nil { + if keyOrigin != nil && keyOrigin.AbsoluteLocation == r.GetRootIndex().specAbsolutePath { + valueOrigin = r.GetRootIndex().FindNodeOrigin(value) + valueHash = HashNode(value) + if refNode != nil && refValue != "" { + return keyOrigin + } + origin, done := checkOrigin(originCheck{ + valueOrigin: valueOrigin, + valueHash: valueHash, + keyOrigin: keyOrigin, + value: value, + rolodex: r, + ref: refValue, + refNode: refNode, + }) + if done { + return origin + } else { + return nil + } + } else { + // the value is not in the root index, so we need to search all indexes + for i := range r.indexes { + idx := r.indexes[i] + if keyOrigin == nil { + keyOrigin = idx.FindNodeOrigin(key) + } + n := idx.FindNodeOrigin(value) + if n != nil { + if refNode != nil && refValue != "" { + return keyOrigin + } + + valueHash = HashNode(value) + nHash := HashNode(n.Node) + + if valueHash == nHash { + if keyOrigin.AbsoluteLocation != n.AbsoluteLocation { + if refNode == nil && refValue == "" { + keyOrigin.AbsoluteLocationValue = n.AbsoluteLocation + keyOrigin.LineValue = n.Line + keyOrigin.ColumnValue = n.Column + keyOrigin.ValueNode = n.Node + } + } + return keyOrigin + } + } + } + } + } + return keyOrigin +} + +// FindNodeOrigin searches all indexes for the origin of a node. If the node is found, a NodeOrigin +// is returned, otherwise nil is returned. +func (r *Rolodex) FindNodeOrigin(node *yaml.Node) *NodeOrigin { + if node == nil { + return nil + } + found := r.GetRootIndex().FindNodeOrigin(node) + if found != nil { + return found + } + for i := range r.indexes { + idx := r.indexes[i] + n := idx.FindNodeOrigin(node) + if n != nil { + return n + } + } + return nil +} + +// FindNodeOrigin searches this index for a matching node. If the node is found, a NodeOrigin +// is returned, otherwise nil is returned. +func (index *SpecIndex) FindNodeOrigin(node *yaml.Node) *NodeOrigin { + if node != nil { + index.nodeMapLock.RLock() + if index.nodeMap[node.Line] != nil { + if index.nodeMap[node.Line][node.Column] != nil { + foundNode := index.nodeMap[node.Line][node.Column] + match := false + + if foundNode == node { + match = true + } + + // if the found node is a map. iterate through the content until we locate the node at that position + if !match && (utils.IsNodeMap(foundNode) || + utils.IsNodeArray(foundNode)) && (utils.IsNodeMap(node) || utils.IsNodeArray(node)) { + if len(node.Content) == len(foundNode.Content) { + // hash node and found node + match = checkHash(node, foundNode) + } + } else { + if !match { + // hash node and found node + match = checkHash(node, foundNode) + + if !match { + // check if the found node is a map and if the first item in the map + // has the same line and column, as well as the same value + if utils.IsNodeMap(foundNode) && len(foundNode.Content) > 0 { + if foundNode.Content[0].Line == node.Line && + foundNode.Content[0].Column == node.Column && + foundNode.Content[0].Value == node.Value { + match = true + } + } + } + } + } + + if match { + index.nodeMapLock.RUnlock() + return &NodeOrigin{ + Node: foundNode, + Line: node.Line, + Column: node.Column, + AbsoluteLocation: index.specAbsolutePath, + Index: index, + } + } + } + } + index.nodeMapLock.RUnlock() + } + return nil +} + +type originCheck struct { + valueOrigin *NodeOrigin + valueHash string + keyOrigin *NodeOrigin + rolodex *Rolodex + value *yaml.Node + ref string + refNode *yaml.Node +} + +func checkHash(node, foundNode *yaml.Node) bool { + nodeHash := HashNode(node) + foundNodeHash := HashNode(foundNode) + if nodeHash == foundNodeHash { + return true + } + return false +} + +func checkOrigin(check originCheck) (*NodeOrigin, bool) { + if check.valueOrigin != nil { + // hash value and value origin + valueOriginHash := HashNode(check.valueOrigin.Node) + if check.valueHash == valueOriginHash { + return check.keyOrigin, true + } + } else { + // no hit on the root, but we know the value is in the spec, so we need to search all indexes + for i := range check.rolodex.indexes { + idx := check.rolodex.indexes[i] + n := idx.FindNodeOrigin(check.value) + if n != nil && n.Node != nil { + // do the hashes match? + valueOriginHash := HashNode(n.Node) + if check.valueHash == valueOriginHash { + if check.keyOrigin.AbsoluteLocation != n.AbsoluteLocation { + if check.refNode == nil && check.ref == "" { + check.keyOrigin.AbsoluteLocationValue = n.AbsoluteLocation + check.keyOrigin.LineValue = n.Line + check.keyOrigin.ColumnValue = n.Column + check.keyOrigin.ValueNode = n.Node + } + } + return check.keyOrigin, true + } + } + } + } + return nil, false +} diff --git a/vendor/github.com/pb33f/libopenapi/index/spec_index.go b/vendor/github.com/pb33f/libopenapi/index/spec_index.go new file mode 100644 index 000000000..1ef24fb69 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/index/spec_index.go @@ -0,0 +1,1524 @@ +// Copyright 2022-2033 Dave Shanley / Quobix +// SPDX-License-Identifier: MIT + +// Package index contains an OpenAPI indexer that will very quickly scan through an OpenAPI specification (all versions) +// and extract references to all the important nodes you might want to look up, as well as counts on total objects. +// +// When extracting references, the index can determine if the reference is local to the file (recommended) or the +// reference is located in another local file, or a remote file. The index will then attempt to load in those remote +// files and look up the references there, or continue following the chain. +// +// When the index loads in a local or remote file, it will also index that remote spec as well. This means everything +// is indexed and stored as a tree, depending on how deep the remote references go. +package index + +import ( + "context" + "fmt" + "log/slog" + "os" + "path/filepath" + "sort" + "strings" + "sync" + + "github.com/pb33f/jsonpath/pkg/jsonpath" + jsonpathconfig "github.com/pb33f/jsonpath/pkg/jsonpath/config" + + "github.com/pb33f/libopenapi/utils" + + "go.yaml.in/yaml/v4" +) + +const ( + // theoreticalRoot is the name of the theoretical spec file used when a root spec file does not exist + theoreticalRoot = "root.yaml" +) + +func NewSpecIndexWithConfigAndContext(ctx context.Context, rootNode *yaml.Node, config *SpecIndexConfig) *SpecIndex { + index := new(SpecIndex) + boostrapIndexCollections(index) + index.InitHighCache() + index.config = config + index.rolodex = config.Rolodex + index.uri = config.uri + index.specAbsolutePath = config.SpecAbsolutePath + if config.Logger != nil { + index.logger = config.Logger + } else { + index.logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ + Level: slog.LevelError, + })) + } + if rootNode == nil || len(rootNode.Content) <= 0 { + return index + } + index.root = rootNode + return createNewIndex(ctx, rootNode, index, config.AvoidBuildIndex) +} + +// NewSpecIndexWithConfig will create a new index of an OpenAPI or Swagger spec. It uses the same logic as NewSpecIndex +// except it sets a base URL for resolving relative references, except it also allows for granular control over +// how the index is set up. +func NewSpecIndexWithConfig(rootNode *yaml.Node, config *SpecIndexConfig) *SpecIndex { + return NewSpecIndexWithConfigAndContext(context.Background(), rootNode, config) +} + +// NewSpecIndex will create a new index of an OpenAPI or Swagger spec. It's not resolved or converted into anything +// other than a raw index of every node for every content type in the specification. This process runs as fast as +// possible so dependencies looking through the tree, don't need to walk the entire thing over, and over. +// +// This creates a new index using a default 'open' configuration. This means if a BaseURL or BasePath are supplied +// the rolodex will automatically read those files or open those h +func NewSpecIndex(rootNode *yaml.Node) *SpecIndex { + index := new(SpecIndex) + index.InitHighCache() + index.config = CreateOpenAPIIndexConfig() + index.root = rootNode + boostrapIndexCollections(index) + return createNewIndex(context.Background(), rootNode, index, false) +} + +func createNewIndex(ctx context.Context, rootNode *yaml.Node, index *SpecIndex, avoidBuildOut bool) *SpecIndex { + // there is no node! return an empty index. + if rootNode == nil { + return index + } + index.nodeMapCompleted = make(chan struct{}) + index.nodeMap = make(map[int]map[int]*yaml.Node) + go index.MapNodes(rootNode) // this can run async. + + index.cache = new(sync.Map) + + // boot index. + results := index.ExtractRefs(ctx, index.root.Content[0], index.root, []string{}, 0, false, "") + + // dedupe refs + dd := make(map[string]struct{}) + var dedupedResults []*Reference + for _, ref := range results { + if _, ok := dd[ref.FullDefinition]; !ok { + dd[ref.FullDefinition] = struct{}{} + dedupedResults = append(dedupedResults, ref) + } + } + + // map poly refs - sort keys for deterministic ordering + polyKeys := make([]string, 0, len(index.polymorphicRefs)) + for k := range index.polymorphicRefs { + polyKeys = append(polyKeys, k) + } + sort.Strings(polyKeys) + poly := make([]*Reference, len(index.polymorphicRefs)) + for i, k := range polyKeys { + poly[i] = index.polymorphicRefs[k] + } + + // pull out references + if len(dedupedResults) > 0 { + index.ExtractComponentsFromRefs(ctx, dedupedResults) + } + if len(poly) > 0 { + index.ExtractComponentsFromRefs(ctx, poly) + } + + index.ExtractExternalDocuments(index.root) + index.GetPathCount() + + // build out the index. + if !avoidBuildOut { + index.BuildIndex() + } + <-index.nodeMapCompleted + return index +} + +// BuildIndex will run all the count operations required to build up maps of everything. It's what makes the index +// useful for looking up things, the count operations are all run in parallel and then the final calculations are run +// the index is ready. +func (index *SpecIndex) BuildIndex() { + if index.built { + return + } + countFuncs := []func() int{ + index.GetOperationCount, + index.GetComponentSchemaCount, + index.GetGlobalTagsCount, + index.GetComponentParameterCount, + index.GetOperationsParameterCount, + } + + var wg sync.WaitGroup + wg.Add(len(countFuncs)) + runIndexFunction(countFuncs, &wg) // run as fast as we can. + wg.Wait() + + // these functions are aggregate and can only run once the rest of the datamodel is ready + countFuncs = []func() int{ + index.GetInlineUniqueParamCount, + index.GetOperationTagsCount, + index.GetGlobalLinksCount, + index.GetGlobalCallbacksCount, + } + wg.Add(len(countFuncs)) + runIndexFunction(countFuncs, &wg) // run as fast as we can. + wg.Wait() + + // these have final calculation dependencies + index.GetInlineDuplicateParamCount() + index.GetAllDescriptionsCount() + index.GetTotalTagsCount() + index.built = true +} + +func (index *SpecIndex) GetLogger() *slog.Logger { + return index.logger +} + +// GetRootNode returns document root node. +func (index *SpecIndex) GetRootNode() *yaml.Node { + return index.root +} + +// SetRootNode will override the root node with a supplied one. Be careful with this! +func (index *SpecIndex) SetRootNode(node *yaml.Node) { + index.root = node +} + +func (index *SpecIndex) GetRolodex() *Rolodex { + return index.rolodex +} + +func (index *SpecIndex) SetRolodex(rolodex *Rolodex) { + index.rolodex = rolodex +} + +// GetSpecFileName returns the root spec filename, if it exists, otherwise returns the theoretical root spec +func (index *SpecIndex) GetSpecFileName() string { + if index == nil || index.rolodex == nil || index.rolodex.indexConfig == nil || index.rolodex.indexConfig.SpecFilePath == "" { + return theoreticalRoot + } + return filepath.Base(index.rolodex.indexConfig.SpecFilePath) +} + +// GetGlobalTagsNode returns document root tags node. +func (index *SpecIndex) GetGlobalTagsNode() *yaml.Node { + return index.tagsNode +} + +// SetCircularReferences is a convenience method for the resolver to pass in circular references +// if the resolver is used. +func (index *SpecIndex) SetCircularReferences(refs []*CircularReferenceResult) { + index.circularReferences = refs +} + +// GetCircularReferences will return any circular reference results that were found by the resolver. +func (index *SpecIndex) GetCircularReferences() []*CircularReferenceResult { + return index.circularReferences +} + +// GetTagCircularReferences will return any circular reference results found in tag parent-child relationships. +// This is used for OpenAPI 3.2+ tag hierarchies where a tag can reference another tag as its parent. +func (index *SpecIndex) GetTagCircularReferences() []*CircularReferenceResult { + return index.tagCircularReferences +} + +// SetIgnoredPolymorphicCircularReferences passes on any ignored poly circular refs captured using +// `IgnorePolymorphicCircularReferences` +func (index *SpecIndex) SetIgnoredPolymorphicCircularReferences(refs []*CircularReferenceResult) { + index.polyCircularReferences = refs +} + +func (index *SpecIndex) SetIgnoredArrayCircularReferences(refs []*CircularReferenceResult) { + index.arrayCircularReferences = refs +} + +// GetIgnoredPolymorphicCircularReferences will return any polymorphic circular references that were 'ignored' by +// using the `IgnorePolymorphicCircularReferences` configuration option. +func (index *SpecIndex) GetIgnoredPolymorphicCircularReferences() []*CircularReferenceResult { + return index.polyCircularReferences +} + +// GetIgnoredArrayCircularReferences will return any array based circular references that were 'ignored' by +// using the `IgnoreArrayCircularReferences` configuration option. +func (index *SpecIndex) GetIgnoredArrayCircularReferences() []*CircularReferenceResult { + return index.arrayCircularReferences +} + +// GetPathsNode returns document root node. +func (index *SpecIndex) GetPathsNode() *yaml.Node { + return index.pathsNode +} + +// GetDiscoveredReferences will return all unique references found in the spec +func (index *SpecIndex) GetDiscoveredReferences() map[string]*Reference { + return index.allRefs +} + +// GetPolyReferences will return every polymorphic reference in the doc +func (index *SpecIndex) GetPolyReferences() map[string]*Reference { + return index.polymorphicRefs +} + +// GetPolyAllOfReferences will return every 'allOf' polymorphic reference in the doc +func (index *SpecIndex) GetPolyAllOfReferences() []*Reference { + return index.polymorphicAllOfRefs +} + +// GetPolyAnyOfReferences will return every 'anyOf' polymorphic reference in the doc +func (index *SpecIndex) GetPolyAnyOfReferences() []*Reference { + return index.polymorphicAnyOfRefs +} + +// GetPolyOneOfReferences will return every 'allOf' polymorphic reference in the doc +func (index *SpecIndex) GetPolyOneOfReferences() []*Reference { + return index.polymorphicOneOfRefs +} + +// GetAllCombinedReferences will return the number of unique and polymorphic references discovered. +func (index *SpecIndex) GetAllCombinedReferences() map[string]*Reference { + combined := make(map[string]*Reference) + for k, ref := range index.allRefs { + combined[k] = ref + } + for k, ref := range index.polymorphicRefs { + combined[k] = ref + } + return combined +} + +// GetRefsByLine will return all references and the lines at which they were found. +func (index *SpecIndex) GetRefsByLine() map[string]map[int]bool { + return index.refsByLine +} + +// GetLinesWithReferences will return a map of lines that have a $ref +func (index *SpecIndex) GetLinesWithReferences() map[int]bool { + return index.linesWithRefs +} + +// GetMappedReferences will return all references that were mapped successfully to actual property nodes. +// this collection is completely unsorted, traversing it may produce random results when resolving it and +// encountering circular references can change results depending on where in the collection the resolver started +// its journey through the index. +func (index *SpecIndex) GetMappedReferences() map[string]*Reference { + return index.allMappedRefs +} + +// SetMappedReferences will set the mapped references to the index. Not something you need every day unless you're +// doing some kind of index hacking. +func (index *SpecIndex) SetMappedReferences(mappedRefs map[string]*Reference) { + index.allMappedRefs = mappedRefs +} + +// GetRawReferencesSequenced returns a slice of every single reference found in the document, extracted raw from the doc +// returned in the exact order they were found in the document. +func (index *SpecIndex) GetRawReferencesSequenced() []*Reference { + return index.rawSequencedRefs +} + +// GetExtensionRefsSequenced returns all references that are under extension paths (x-* fields), +// in the order they were found in the document. +func (index *SpecIndex) GetExtensionRefsSequenced() []*Reference { + var extensionRefs []*Reference + for _, ref := range index.rawSequencedRefs { + if ref.IsExtensionRef { + extensionRefs = append(extensionRefs, ref) + } + } + return extensionRefs +} + +// GetMappedReferencesSequenced will return all references that were mapped successfully to nodes, performed in sequence +// as they were read in from the document. +func (index *SpecIndex) GetMappedReferencesSequenced() []*ReferenceMapped { + return index.allMappedRefsSequenced +} + +// GetOperationParameterReferences will return all references to operation parameters +func (index *SpecIndex) GetOperationParameterReferences() map[string]map[string]map[string][]*Reference { + return index.paramOpRefs +} + +// GetAllSchemas returns references to ALL schemas found in the document: +// - Inline schemas (defined directly in operations, parameters, etc.) +// - Component schemas (defined under components/schemas or definitions) +// - Reference schemas ($ref pointers to schemas) +// +// Results are sorted by line number in the source document. +// +// Note: This is the only GetAll* function that returns inline and $ref variants. +// Other GetAll* functions (GetAllRequestBodies, GetAllResponses, etc.) only return +// items defined in the components section. Use GetAllInlineSchemas, GetAllComponentSchemas, +// and GetAllReferenceSchemas for more granular access. +func (index *SpecIndex) GetAllSchemas() []*Reference { + componentSchemas := index.GetAllComponentSchemas() + inlineSchemas := index.GetAllInlineSchemas() + refSchemas := index.GetAllReferenceSchemas() + combined := make([]*Reference, len(inlineSchemas)+len(componentSchemas)+len(refSchemas)) + i := 0 + for x := range inlineSchemas { + combined[i] = inlineSchemas[x] + i++ + } + for x := range componentSchemas { + combined[i] = componentSchemas[x] + i++ + } + for x := range refSchemas { + combined[i] = refSchemas[x] + i++ + } + sort.Slice(combined, func(i, j int) bool { + return combined[i].Node.Line < combined[j].Node.Line + }) + return combined +} + +// GetAllInlineSchemaObjects will return all schemas that are inline (not inside components) and that are also typed +// as 'object' or 'array' (not primitives). +func (index *SpecIndex) GetAllInlineSchemaObjects() []*Reference { + return index.allInlineSchemaObjectDefinitions +} + +// GetAllInlineSchemas will return all schemas defined in the components section of the document. +func (index *SpecIndex) GetAllInlineSchemas() []*Reference { + return index.allInlineSchemaDefinitions +} + +// GetAllReferenceSchemas will return all schemas that are not inline, but $ref'd from somewhere. +func (index *SpecIndex) GetAllReferenceSchemas() []*Reference { + return index.allRefSchemaDefinitions +} + +// GetAllComponentSchemas will return all schemas defined in the components section of the document. +func (index *SpecIndex) GetAllComponentSchemas() map[string]*Reference { + if index == nil { + return nil + } + + // Acquire read lock + index.allComponentSchemasLock.RLock() + if index.allComponentSchemas != nil { + defer index.allComponentSchemasLock.RUnlock() + return index.allComponentSchemas + } + // Release the read lock before acquiring write lock + index.allComponentSchemasLock.RUnlock() + + // Acquire write lock to initialize the map + index.allComponentSchemasLock.Lock() + defer index.allComponentSchemasLock.Unlock() + + // Double-check if another goroutine initialized it + if index.allComponentSchemas == nil { + schemaMap := syncMapToMap[string, *Reference](index.allComponentSchemaDefinitions) + index.allComponentSchemas = schemaMap + } + + return index.allComponentSchemas +} + +// GetAllSecuritySchemes returns all security schemes defined in the components section +// (components/securitySchemes in OpenAPI 3.x, or securityDefinitions in Swagger 2.0). +func (index *SpecIndex) GetAllSecuritySchemes() map[string]*Reference { + return syncMapToMap[string, *Reference](index.allSecuritySchemes) +} + +// GetAllHeaders returns all headers defined in the components section (components/headers). +// This does not include inline headers defined directly in operations or $ref pointers. +func (index *SpecIndex) GetAllHeaders() map[string]*Reference { + return index.allHeaders +} + +// GetAllExternalDocuments will return all external documents found +func (index *SpecIndex) GetAllExternalDocuments() map[string]*Reference { + return index.allExternalDocuments +} + +// GetAllExamples returns all examples defined in the components section (components/examples). +// This does not include inline examples defined directly in operations or $ref pointers. +func (index *SpecIndex) GetAllExamples() map[string]*Reference { + return index.allExamples +} + +// GetAllDescriptions will return all descriptions found in the document +func (index *SpecIndex) GetAllDescriptions() []*DescriptionReference { + return index.allDescriptions +} + +// GetAllEnums will return all enums found in the document +func (index *SpecIndex) GetAllEnums() []*EnumReference { + return index.allEnums +} + +// GetAllObjectsWithProperties will return all objects with properties found in the document +func (index *SpecIndex) GetAllObjectsWithProperties() []*ObjectReference { + return index.allObjectsWithProperties +} + +// GetAllSummaries will return all summaries found in the document +func (index *SpecIndex) GetAllSummaries() []*DescriptionReference { + return index.allSummaries +} + +// GetAllRequestBodies returns all request bodies defined in the components section (components/requestBodies). +// This does not include inline request bodies defined directly in operations or $ref pointers. +func (index *SpecIndex) GetAllRequestBodies() map[string]*Reference { + return index.allRequestBodies +} + +// GetAllLinks returns all links defined in the components section (components/links). +// This does not include inline links defined directly in responses or $ref pointers. +func (index *SpecIndex) GetAllLinks() map[string]*Reference { + return index.allLinks +} + +// GetAllParameters returns all parameters defined in the components section (components/parameters). +// This does not include inline parameters defined directly in operations or path items. +// For operation-level parameters, use GetOperationParameterReferences. +func (index *SpecIndex) GetAllParameters() map[string]*Reference { + return index.allParameters +} + +// GetAllResponses returns all responses defined in the components section (components/responses). +// This does not include inline responses defined directly in operations or $ref pointers. +func (index *SpecIndex) GetAllResponses() map[string]*Reference { + return index.allResponses +} + +// GetAllCallbacks returns all callbacks defined in the components section (components/callbacks). +// This does not include inline callbacks defined directly in operations or $ref pointers. +func (index *SpecIndex) GetAllCallbacks() map[string]*Reference { + return index.allCallbacks +} + +// GetAllComponentPathItems returns all path items defined in the components section (components/pathItems). +// This does not include path items defined directly under the paths object or $ref pointers. +// For paths-level path items, use GetAllPaths. +func (index *SpecIndex) GetAllComponentPathItems() map[string]*Reference { + return index.allComponentPathItems +} + +// GetInlineOperationDuplicateParameters will return a map of duplicates located in operation parameters. +func (index *SpecIndex) GetInlineOperationDuplicateParameters() map[string][]*Reference { + return index.paramInlineDuplicateNames +} + +// GetReferencesWithSiblings will return a map of all the references with sibling nodes (illegal) +func (index *SpecIndex) GetReferencesWithSiblings() map[string]Reference { + return index.refsWithSiblings +} + +// GetAllReferences will return every reference found in the spec, after being de-duplicated. +func (index *SpecIndex) GetAllReferences() map[string]*Reference { + return index.allRefs +} + +// GetAllSequencedReferences will return every reference (in sequence) that was found (non-polymorphic) +func (index *SpecIndex) GetAllSequencedReferences() []*Reference { + return index.rawSequencedRefs +} + +// GetSchemasNode will return the schema's node found in the spec +func (index *SpecIndex) GetSchemasNode() *yaml.Node { + return index.schemasNode +} + +// GetParametersNode will return the schema's node found in the spec +func (index *SpecIndex) GetParametersNode() *yaml.Node { + return index.parametersNode +} + +// GetReferenceIndexErrors will return any errors that occurred when indexing references +func (index *SpecIndex) GetReferenceIndexErrors() []error { + return index.refErrors +} + +// GetOperationParametersIndexErrors any errors that occurred when indexing operation parameters +func (index *SpecIndex) GetOperationParametersIndexErrors() []error { + return index.operationParamErrors +} + +// GetAllPaths will return all paths indexed in the document +func (index *SpecIndex) GetAllPaths() map[string]map[string]*Reference { + return index.pathRefs +} + +// GetOperationTags will return all references to all tags found in operations. +func (index *SpecIndex) GetOperationTags() map[string]map[string][]*Reference { + return index.operationTagsRefs +} + +// GetAllParametersFromOperations will return all paths indexed in the document +func (index *SpecIndex) GetAllParametersFromOperations() map[string]map[string]map[string][]*Reference { + return index.paramOpRefs +} + +// GetRootSecurityReferences will return all root security settings +func (index *SpecIndex) GetRootSecurityReferences() []*Reference { + return index.rootSecurity +} + +// GetSecurityRequirementReferences will return all security requirement definitions found in the document +func (index *SpecIndex) GetSecurityRequirementReferences() map[string]map[string][]*Reference { + return index.securityRequirementRefs +} + +// GetRootSecurityNode will return the root security node +func (index *SpecIndex) GetRootSecurityNode() *yaml.Node { + return index.rootSecurityNode +} + +// GetRootServersNode will return the root servers node +func (index *SpecIndex) GetRootServersNode() *yaml.Node { + return index.rootServersNode +} + +// GetAllRootServers will return all root servers defined +func (index *SpecIndex) GetAllRootServers() []*Reference { + return index.serversRefs +} + +// GetAllOperationsServers will return all operation overrides for servers. +func (index *SpecIndex) GetAllOperationsServers() map[string]map[string][]*Reference { + return index.opServersRefs +} + +// SetAllowCircularReferenceResolving will flip a bit that can be used by any consumers to determine if they want +// to allow or disallow circular references to be resolved or visited +func (index *SpecIndex) SetAllowCircularReferenceResolving(allow bool) { + index.allowCircularReferences = allow +} + +// AllowCircularReferenceResolving will return a bit that allows developers to determine what to do with circular refs. +func (index *SpecIndex) AllowCircularReferenceResolving() bool { + return index.allowCircularReferences +} + +func (index *SpecIndex) checkPolymorphicNode(name string) (bool, string) { + switch name { + case "anyOf": + return true, "anyOf" + case "allOf": + return true, "allOf" + case "oneOf": + return true, "oneOf" + } + return false, "" +} + +// GetPathCount will return the number of paths found in the spec +func (index *SpecIndex) GetPathCount() int { + if index.root == nil { + return -1 + } + + if index.pathCount > 0 { + return index.pathCount + } + pc := 0 + for i, n := range index.root.Content[0].Content { + if i%2 == 0 { + if n.Value == "paths" { + pn := index.root.Content[0].Content[i+1].Content + index.pathsNode = index.root.Content[0].Content[i+1] + pc = len(pn) / 2 + } + } + } + index.pathCount = pc + return pc +} + +// ExtractExternalDocuments will extract the number of externalDocs nodes found in the document. +func (index *SpecIndex) ExtractExternalDocuments(node *yaml.Node) []*Reference { + if node == nil { + return nil + } + var found []*Reference + if len(node.Content) > 0 { + for i, n := range node.Content { + if utils.IsNodeMap(n) || utils.IsNodeArray(n) { + found = append(found, index.ExtractExternalDocuments(n)...) + } + + if i%2 == 0 && n.Value == "externalDocs" { + docNode := node.Content[i+1] + _, urlNode := utils.FindKeyNode("url", docNode.Content) + if urlNode != nil { + ref := &Reference{ + Definition: urlNode.Value, + Name: urlNode.Value, + Node: docNode, + } + index.externalDocumentsRef = append(index.externalDocumentsRef, ref) + } + } + } + } + index.externalDocumentsCount = len(index.externalDocumentsRef) + return found +} + +// GetGlobalTagsCount will return the number of tags found in the top level 'tags' node of the document. +func (index *SpecIndex) GetGlobalTagsCount() int { + if index.root == nil { + return -1 + } + + if index.globalTagsCount > 0 { + return index.globalTagsCount + } + + for i, n := range index.root.Content[0].Content { + if i%2 == 0 { + if n.Value == "tags" { + tagsNode := index.root.Content[0].Content[i+1] + if tagsNode != nil { + index.tagsNode = tagsNode + index.globalTagsCount = len(tagsNode.Content) // tags is an array, don't divide by 2. + for x, tagNode := range index.tagsNode.Content { + + _, name := utils.FindKeyNode("name", tagNode.Content) + _, description := utils.FindKeyNode("description", tagNode.Content) + + var desc string + if description == nil { + desc = "" + } + if name != nil { + ref := &Reference{ + Definition: desc, + Name: name.Value, + Node: tagNode, + Path: fmt.Sprintf("$.tags[%d]", x), + } + index.globalTagRefs[name.Value] = ref + } + } + + // Check for tag circular references (OpenAPI 3.2+) + index.checkTagCircularReferences() + } + } + } + } + return index.globalTagsCount +} + +// checkTagCircularReferences performs circular reference detection for OpenAPI 3.2+ tag parent-child relationships. +// It builds a parent-child map and then uses depth-first search to detect cycles. +func (index *SpecIndex) checkTagCircularReferences() { + if index.tagsNode == nil { + return + } + + // Build parent-child mapping from tag nodes + tagParentMap := make(map[string]string) // tagName -> parentName + tagRefs := make(map[string]*Reference) // tagName -> Reference + tagNodes := make(map[string]*yaml.Node) // tagName -> yaml.Node + + for x, tagNode := range index.tagsNode.Content { + _, nameNode := utils.FindKeyNode("name", tagNode.Content) + _, parentNode := utils.FindKeyNode("parent", tagNode.Content) + + if nameNode != nil { + tagName := nameNode.Value + tagNodes[tagName] = tagNode + tagRefs[tagName] = &Reference{ + Name: tagName, + Node: tagNode, + Path: fmt.Sprintf("$.tags[%d]", x), + } + + if parentNode != nil { + parentName := parentNode.Value + tagParentMap[tagName] = parentName + } + } + } + + // Perform circular reference detection using depth-first search + visited := make(map[string]bool) + recStack := make(map[string]bool) // recursion stack to detect cycles + + for tagName := range tagRefs { + if !visited[tagName] { + // Only check tags that have parents - no point checking orphans + if _, hasParent := tagParentMap[tagName]; hasParent { + if path := index.detectTagCircularHelper(tagName, tagParentMap, tagRefs, visited, recStack, []string{}); len(path) > 0 { + // Circular reference detected, create CircularReferenceResult + journey := make([]*Reference, len(path)) + for i, name := range path { + journey[i] = tagRefs[name] + } + + loopIndex := -1 + loopStart := path[len(path)-1] // The repeated tag name + for i, name := range path { + if name == loopStart { + loopIndex = i + break + } + } + + circRef := &CircularReferenceResult{ + Journey: journey, + Start: tagRefs[path[0]], + LoopIndex: loopIndex, + LoopPoint: tagRefs[loopStart], + ParentNode: tagNodes[loopStart], + IsArrayResult: false, + IsPolymorphicResult: false, + IsInfiniteLoop: true, // Tag parent cycles are always problematic + } + + index.tagCircularReferences = append(index.tagCircularReferences, circRef) + } + } + } + } +} + +// detectTagCircularHelper is a recursive helper function for detecting circular references in tag hierarchies. +// Returns the path to the circular reference if found, empty slice otherwise. +func (index *SpecIndex) detectTagCircularHelper(tagName string, parentMap map[string]string, tagRefs map[string]*Reference, visited map[string]bool, recStack map[string]bool, path []string) []string { + // Check if this tag even exists - if not, we can't have a circular reference + if _, exists := tagRefs[tagName]; !exists { + return []string{} + } + + visited[tagName] = true + recStack[tagName] = true + path = append(path, tagName) + + // Check if this tag has a parent + if parentName, hasParent := parentMap[tagName]; hasParent { + // Validate that parent exists as a defined tag + if _, parentExists := tagRefs[parentName]; !parentExists { + // Parent doesn't exist - this is a validation error but not a circular reference + // Remove from recursion stack before returning + recStack[tagName] = false + return []string{} + } + + // If parent is already in recursion stack, we found a cycle + if recStack[parentName] { + return append(path, parentName) // Return path including the cycle + } + + // If parent not visited, recursively check it + if !visited[parentName] { + if cyclePath := index.detectTagCircularHelper(parentName, parentMap, tagRefs, visited, recStack, path); len(cyclePath) > 0 { + return cyclePath + } + } + } + + // Remove from recursion stack when backtracking + recStack[tagName] = false + return []string{} +} + +// GetOperationTagsCount will return the number of operation tags found (tags referenced in operations) +func (index *SpecIndex) GetOperationTagsCount() int { + if index.root == nil { + return -1 + } + + if index.operationTagsCount > 0 { + return index.operationTagsCount + } + + // this is an aggregate count function that can only be run after operations + // have been calculated. + seen := make(map[string]bool) + count := 0 + for _, path := range index.operationTagsRefs { + for _, method := range path { + for _, tag := range method { + if !seen[tag.Name] { + seen[tag.Name] = true + count++ + } + } + } + } + index.operationTagsCount = count + return index.operationTagsCount +} + +// GetTotalTagsCount will return the number of global and operation tags found that are unique. +func (index *SpecIndex) GetTotalTagsCount() int { + if index.root == nil { + return -1 + } + if index.totalTagsCount > 0 { + return index.totalTagsCount + } + + seen := make(map[string]bool) + count := 0 + + for _, gt := range index.globalTagRefs { + // TODO: do we still need this? + if !seen[gt.Name] { + seen[gt.Name] = true + count++ + } + } + for _, ot := range index.operationTagsRefs { + for _, m := range ot { + for _, t := range m { + if !seen[t.Name] { + seen[t.Name] = true + count++ + } + } + } + } + index.totalTagsCount = count + return index.totalTagsCount +} + +// GetGlobalCallbacksCount for each response of each operation method, multiple callbacks can be defined +func (index *SpecIndex) GetGlobalCallbacksCount() int { + if index.root == nil { + return -1 + } + + if index.globalCallbacksCount > 0 { + return index.globalCallbacksCount + } + + index.pathRefsLock.RLock() + for path, p := range index.pathRefs { + for _, m := range p { + + // look through method for callbacks + callbacks, _ := jsonpath.NewPath("$..callbacks", jsonpathconfig.WithPropertyNameExtension()) + var res []*yaml.Node + res = callbacks.Query(m.Node) + if len(res) > 0 { + for _, callback := range res[0].Content { + if utils.IsNodeMap(callback) { + + ref := &Reference{ + Definition: m.Name, + Name: m.Name, + Node: callback, + } + + if index.callbacksRefs[path] == nil { + index.callbacksRefs[path] = make(map[string][]*Reference) + } + if len(index.callbacksRefs[path][m.Name]) > 0 { + index.callbacksRefs[path][m.Name] = append(index.callbacksRefs[path][m.Name], ref) + } else { + index.callbacksRefs[path][m.Name] = []*Reference{ref} + } + index.globalCallbacksCount++ + } + } + } + } + } + index.pathRefsLock.RUnlock() + return index.globalCallbacksCount +} + +// GetGlobalLinksCount for each response of each operation method, multiple callbacks can be defined +func (index *SpecIndex) GetGlobalLinksCount() int { + if index.root == nil { + return -1 + } + + if index.globalLinksCount > 0 { + return index.globalLinksCount + } + + // index.pathRefsLock.Lock() + for path, p := range index.pathRefs { + for _, m := range p { + + // look through method for links + links, _ := jsonpath.NewPath("$..links", jsonpathconfig.WithPropertyNameExtension()) + var res []*yaml.Node + + res = links.Query(m.Node) + + if len(res) > 0 { + for _, link := range res[0].Content { + if utils.IsNodeMap(link) { + + ref := &Reference{ + Definition: m.Name, + Name: m.Name, + Node: link, + } + if index.linksRefs[path] == nil { + index.linksRefs[path] = make(map[string][]*Reference) + } + if len(index.linksRefs[path][m.Name]) > 0 { + index.linksRefs[path][m.Name] = append(index.linksRefs[path][m.Name], ref) + } + index.linksRefs[path][m.Name] = []*Reference{ref} + index.globalLinksCount++ + } + } + } + } + } + // index.pathRefsLock.Unlock() + return index.globalLinksCount +} + +// GetRawReferenceCount will return the number of raw references located in the document. +func (index *SpecIndex) GetRawReferenceCount() int { + return len(index.rawSequencedRefs) +} + +// GetComponentSchemaCount will return the number of schemas located in the 'components' or 'definitions' node. +func (index *SpecIndex) GetComponentSchemaCount() int { + if index.root == nil || len(index.root.Content) == 0 { + return -1 + } + + if index.schemaCount > 0 { + return index.schemaCount + } + + for i, n := range index.root.Content[0].Content { + if i%2 == 0 { + + // servers + if n.Value == "servers" { + index.rootServersNode = index.root.Content[0].Content[i+1] + if i+1 < len(index.root.Content[0].Content) { + serverDefinitions := index.root.Content[0].Content[i+1] + for x, def := range serverDefinitions.Content { + ref := &Reference{ + Definition: "servers", + Name: "server", + Node: def, + Path: fmt.Sprintf("$.servers[%d]", x), + ParentNode: index.rootServersNode, + } + index.serversRefs = append(index.serversRefs, ref) + } + } + } + + // root security definitions + if n.Value == "security" { + index.rootSecurityNode = index.root.Content[0].Content[i+1] + if i+1 < len(index.root.Content[0].Content) { + securityDefinitions := index.root.Content[0].Content[i+1] + for x, def := range securityDefinitions.Content { + if len(def.Content) > 0 { + name := def.Content[0] + ref := &Reference{ + Definition: name.Value, + Name: name.Value, + Node: def, + Path: fmt.Sprintf("$.security[%d]", x), + } + index.rootSecurity = append(index.rootSecurity, ref) + } + } + } + } + + if n.Value == "components" { + _, schemasNode := utils.FindKeyNode("schemas", index.root.Content[0].Content[i+1].Content) + + // while we are here, go ahead and extract everything in components. + _, parametersNode := utils.FindKeyNode("parameters", index.root.Content[0].Content[i+1].Content) + _, requestBodiesNode := utils.FindKeyNode("requestBodies", index.root.Content[0].Content[i+1].Content) + _, responsesNode := utils.FindKeyNode("responses", index.root.Content[0].Content[i+1].Content) + _, securitySchemesNode := utils.FindKeyNode("securitySchemes", index.root.Content[0].Content[i+1].Content) + _, headersNode := utils.FindKeyNode("headers", index.root.Content[0].Content[i+1].Content) + _, examplesNode := utils.FindKeyNode("examples", index.root.Content[0].Content[i+1].Content) + _, linksNode := utils.FindKeyNode("links", index.root.Content[0].Content[i+1].Content) + _, callbacksNode := utils.FindKeyNode("callbacks", index.root.Content[0].Content[i+1].Content) + _, pathItemsNode := utils.FindKeyNode("pathItems", index.root.Content[0].Content[i+1].Content) + + // extract schemas + if schemasNode != nil { + index.extractDefinitionsAndSchemas(schemasNode, "#/components/schemas/") + index.schemasNode = schemasNode + index.schemaCount = len(schemasNode.Content) / 2 + } + + // extract parameters + if parametersNode != nil { + index.extractComponentParameters(parametersNode, "#/components/parameters/") + index.componentLock.Lock() + index.parametersNode = parametersNode + index.componentLock.Unlock() + } + + // extract requestBodies + if requestBodiesNode != nil { + index.extractComponentRequestBodies(requestBodiesNode, "#/components/requestBodies/") + index.requestBodiesNode = requestBodiesNode + } + + // extract responses + if responsesNode != nil { + index.extractComponentResponses(responsesNode, "#/components/responses/") + index.responsesNode = responsesNode + } + + // extract security schemes + if securitySchemesNode != nil { + index.extractComponentSecuritySchemes(securitySchemesNode, "#/components/securitySchemes/") + index.securitySchemesNode = securitySchemesNode + } + + // extract headers + if headersNode != nil { + index.extractComponentHeaders(headersNode, "#/components/headers/") + index.headersNode = headersNode + } + + // extract examples + if examplesNode != nil { + index.extractComponentExamples(examplesNode, "#/components/examples/") + index.examplesNode = examplesNode + } + + // extract links + if linksNode != nil { + index.extractComponentLinks(linksNode, "#/components/links/") + index.linksNode = linksNode + } + + // extract callbacks + if callbacksNode != nil { + index.extractComponentCallbacks(callbacksNode, "#/components/callbacks/") + index.callbacksNode = callbacksNode + } + + // extract pathItems + if pathItemsNode != nil { + index.extractComponentPathItems(pathItemsNode, "#/components/pathItems/") + index.pathItemsNode = pathItemsNode + } + + } + + // swagger + if n.Value == "definitions" { + schemasNode := index.root.Content[0].Content[i+1] + if schemasNode != nil { + + // extract schemas + index.extractDefinitionsAndSchemas(schemasNode, "#/definitions/") + index.schemasNode = schemasNode + index.schemaCount = len(schemasNode.Content) / 2 + } + } + + // swagger + if n.Value == "parameters" { + parametersNode := index.root.Content[0].Content[i+1] + if parametersNode != nil { + // extract params + index.extractComponentParameters(parametersNode, "#/parameters/") + index.componentLock.Lock() + index.parametersNode = parametersNode + index.componentLock.Unlock() + } + } + + if n.Value == "responses" { + responsesNode := index.root.Content[0].Content[i+1] + if responsesNode != nil { + + // extract responses + index.extractComponentResponses(responsesNode, "#/responses/") + index.responsesNode = responsesNode + } + } + + if n.Value == "securityDefinitions" { + securityDefinitionsNode := index.root.Content[0].Content[i+1] + if securityDefinitionsNode != nil { + + // extract security definitions. + index.extractComponentSecuritySchemes(securityDefinitionsNode, "#/securityDefinitions/") + index.securitySchemesNode = securityDefinitionsNode + } + } + + } + } + return index.schemaCount +} + +// GetComponentParameterCount returns the number of parameter components defined +func (index *SpecIndex) GetComponentParameterCount() int { + if index.root == nil { + return -1 + } + + if index.componentParamCount > 0 { + return index.componentParamCount + } + + for i, n := range index.root.Content[0].Content { + if i%2 == 0 { + // openapi 3 + if n.Value == "components" { + _, parametersNode := utils.FindKeyNode("parameters", index.root.Content[0].Content[i+1].Content) + if parametersNode != nil { + index.componentLock.Lock() + index.parametersNode = parametersNode + index.componentParamCount = len(parametersNode.Content) / 2 + index.componentLock.Unlock() + } + } + // openapi 2 + if n.Value == "parameters" { + parametersNode := index.root.Content[0].Content[i+1] + if parametersNode != nil { + index.componentLock.Lock() + index.parametersNode = parametersNode + index.componentParamCount = len(parametersNode.Content) / 2 + index.componentLock.Unlock() + } + } + } + } + return index.componentParamCount +} + +// GetOperationCount returns the number of operations (for all paths) located in the document +func (index *SpecIndex) GetOperationCount() int { + if index.root == nil { + return -1 + } + + if index.pathsNode == nil { + return -1 + } + + if index.operationCount > 0 { + return index.operationCount + } + + opCount := 0 + + locatedPathRefs := make(map[string]map[string]*Reference) + + for x, p := range index.pathsNode.Content { + if x%2 == 0 { + + var method *yaml.Node + if utils.IsNodeArray(index.pathsNode) { + method = index.pathsNode.Content[x] + } else { + method = index.pathsNode.Content[x+1] + } + + // is the path a ref? + if isRef, _, ref := utils.IsNodeRefValue(method); isRef { + ctx := context.WithValue(context.Background(), CurrentPathKey, index.specAbsolutePath) + ctx = context.WithValue(ctx, RootIndexKey, index) + pNode := seekRefEnd(ctx, index, ref) + if pNode != nil { + method = pNode.Node + } + } + + // extract methods for later use. + for y, m := range method.Content { + if y%2 == 0 { + + // check node is a valid method + valid := false + for _, methodType := range methodTypes { + if m.Value == methodType { + valid = true + } + } + if valid { + ref := &Reference{ + Definition: m.Value, + Name: m.Value, + Node: method.Content[y+1], + Path: fmt.Sprintf("$.paths['%s'].%s", p.Value, m.Value), + ParentNode: m, + } + if locatedPathRefs[p.Value] == nil { + locatedPathRefs[p.Value] = make(map[string]*Reference) + } + locatedPathRefs[p.Value][ref.Name] = ref + // update + opCount++ + } + } + } + } + } + for k, v := range locatedPathRefs { + index.pathRefs[k] = v + } + index.operationCount = opCount + return opCount +} + +// GetOperationsParameterCount returns the number of parameters defined in paths and operations. +// this method looks in top level (path level) and inside each operation (get, post etc.). Parameters can +// be hiding within multiple places. +func (index *SpecIndex) GetOperationsParameterCount() int { + if index.root == nil { + return -1 + } + + if index.pathsNode == nil { + return -1 + } + + if index.operationParamCount > 0 { + return index.operationParamCount + } + + // parameters are sneaky, they can be in paths, in path operations or in components. + // sometimes they are refs, sometimes they are inline definitions, just for fun. + // some authors just LOVE to mix and match them all up. + // check paths first + for x, pathItemNode := range index.pathsNode.Content { + if x%2 == 0 { + + var pathPropertyNode *yaml.Node + if utils.IsNodeArray(index.pathsNode) { + pathPropertyNode = index.pathsNode.Content[x] + } else { + pathPropertyNode = index.pathsNode.Content[x+1] + } + + // is the path a ref? + if isRef, _, ref := utils.IsNodeRefValue(pathPropertyNode); isRef { + ctx := context.WithValue(context.Background(), CurrentPathKey, index.specAbsolutePath) + ctx = context.WithValue(ctx, RootIndexKey, index) + pNode := seekRefEnd(ctx, index, ref) + if pNode != nil { + pathPropertyNode = pNode.Node + } + } + + // extract methods for later use. + for y, prop := range pathPropertyNode.Content { + if y%2 == 0 { + + // while we're here, lets extract any top level servers + if prop.Value == "servers" { + serversNode := pathPropertyNode.Content[y+1] + if index.opServersRefs[pathItemNode.Value] == nil { + index.opServersRefs[pathItemNode.Value] = make(map[string][]*Reference) + } + var serverRefs []*Reference + for i, serverRef := range serversNode.Content { + ref := &Reference{ + Definition: serverRef.Value, + Name: serverRef.Value, + Node: serverRef, + ParentNode: prop, + Path: fmt.Sprintf("$.paths['%s'].servers[%d]", pathItemNode.Value, i), + } + serverRefs = append(serverRefs, ref) + } + index.opServersRefs[pathItemNode.Value]["top"] = serverRefs + } + + // top level params + if prop.Value == "parameters" { + + // let's look at params, check if they are refs or inline. + params := pathPropertyNode.Content[y+1].Content + index.scanOperationParams(params, pathPropertyNode.Content[y], pathItemNode, "top") + } + + // method level params. + if isHttpMethod(prop.Value) { + for z, httpMethodProp := range pathPropertyNode.Content[y+1].Content { + if z%2 == 0 { + if httpMethodProp.Value == "parameters" { + params := pathPropertyNode.Content[y+1].Content[z+1].Content + index.scanOperationParams(params, pathPropertyNode.Content[y+1].Content[z], pathItemNode, prop.Value) + } + + // extract operation tags if set. + if httpMethodProp.Value == "tags" { + tags := pathPropertyNode.Content[y+1].Content[z+1] + + if index.operationTagsRefs[pathItemNode.Value] == nil { + index.operationTagsRefs[pathItemNode.Value] = make(map[string][]*Reference) + } + + var tagRefs []*Reference + for _, tagRef := range tags.Content { + ref := &Reference{ + Definition: tagRef.Value, + Name: tagRef.Value, + Node: tagRef, + } + tagRefs = append(tagRefs, ref) + } + index.operationTagsRefs[pathItemNode.Value][prop.Value] = tagRefs + } + + // extract description and summaries + if httpMethodProp.Value == "description" { + desc := pathPropertyNode.Content[y+1].Content[z+1].Value + ref := &Reference{ + Definition: desc, + Name: "description", + Node: pathPropertyNode.Content[y+1].Content[z+1], + } + if index.operationDescriptionRefs[pathItemNode.Value] == nil { + index.operationDescriptionRefs[pathItemNode.Value] = make(map[string]*Reference) + } + + index.operationDescriptionRefs[pathItemNode.Value][prop.Value] = ref + } + if httpMethodProp.Value == "summary" { + summary := pathPropertyNode.Content[y+1].Content[z+1].Value + ref := &Reference{ + Definition: summary, + Name: "summary", + Node: pathPropertyNode.Content[y+1].Content[z+1], + } + + if index.operationSummaryRefs[pathItemNode.Value] == nil { + index.operationSummaryRefs[pathItemNode.Value] = make(map[string]*Reference) + } + + index.operationSummaryRefs[pathItemNode.Value][prop.Value] = ref + } + + // extract servers from method operation. + if httpMethodProp.Value == "servers" { + serversNode := pathPropertyNode.Content[y+1].Content[z+1] + + var serverRefs []*Reference + for i, serverRef := range serversNode.Content { + ref := &Reference{ + Definition: "servers", + Name: "servers", + Node: serverRef, + ParentNode: httpMethodProp, + Path: fmt.Sprintf("$.paths['%s'].%s.servers[%d]", pathItemNode.Value, prop.Value, i), + } + serverRefs = append(serverRefs, ref) + } + + if index.opServersRefs[pathItemNode.Value] == nil { + index.opServersRefs[pathItemNode.Value] = make(map[string][]*Reference) + } + + index.opServersRefs[pathItemNode.Value][prop.Value] = serverRefs + } + + } + } + } + } + } + } + } + + // Now that all the paths and operations are processed, lets pick out everything from our pre + // mapped refs and populate our ready to roll index of component params. + for key, component := range index.allMappedRefs { + if strings.Contains(key, "/parameters/") { + index.paramCompRefs[key] = component + index.paramAllRefs[key] = component + } + } + + // now build main index of all params by combining comp refs with inline params from operations. + // use the namespace path:::param for inline params to identify them as inline. + for path, params := range index.paramOpRefs { + for mName, mValue := range params { + for pName, pValue := range mValue { + if !strings.HasPrefix(pName, "#") { + index.paramInlineDuplicateNames[pName] = append(index.paramInlineDuplicateNames[pName], pValue...) + for i := range pValue { + if pValue[i] != nil { + _, in := utils.FindKeyNodeTop("in", pValue[i].Node.Content) + if in != nil { + index.paramAllRefs[fmt.Sprintf("%s:::%s:::%s", path, mName, in.Value)] = pValue[i] + } else { + index.paramAllRefs[fmt.Sprintf("%s:::%s", path, mName)] = pValue[i] + } + } + } + } + } + } + } + + index.operationParamCount = len(index.paramCompRefs) + len(index.paramInlineDuplicateNames) + return index.operationParamCount +} + +// GetInlineDuplicateParamCount returns the number of inline duplicate parameters (operation params) +func (index *SpecIndex) GetInlineDuplicateParamCount() int { + if index.componentsInlineParamDuplicateCount > 0 { + return index.componentsInlineParamDuplicateCount + } + dCount := len(index.paramInlineDuplicateNames) - index.countUniqueInlineDuplicates() + index.componentsInlineParamDuplicateCount = dCount + return dCount +} + +// GetInlineUniqueParamCount returns the number of unique inline parameters (operation params) +func (index *SpecIndex) GetInlineUniqueParamCount() int { + return index.countUniqueInlineDuplicates() +} + +// GetAllDescriptionsCount will collect together every single description found in the document +func (index *SpecIndex) GetAllDescriptionsCount() int { + return len(index.allDescriptions) +} + +// GetAllSummariesCount will collect together every single summary found in the document +func (index *SpecIndex) GetAllSummariesCount() int { + return len(index.allSummaries) +} + +// RegisterSchemaId registers a schema by its $id in this index. +// Returns an error if the $id is invalid (e.g., contains a fragment). +func (index *SpecIndex) RegisterSchemaId(entry *SchemaIdEntry) error { + index.schemaIdRegistryLock.Lock() + defer index.schemaIdRegistryLock.Unlock() + + if index.schemaIdRegistry == nil { + index.schemaIdRegistry = make(map[string]*SchemaIdEntry) + } + + _, err := registerSchemaIdToRegistry(index.schemaIdRegistry, entry, index.logger, "local index") + return err +} + +// GetSchemaById looks up a schema by its $id URI. +func (index *SpecIndex) GetSchemaById(uri string) *SchemaIdEntry { + index.schemaIdRegistryLock.RLock() + defer index.schemaIdRegistryLock.RUnlock() + + if index.schemaIdRegistry == nil { + return nil + } + return index.schemaIdRegistry[uri] +} + +// GetAllSchemaIds returns a copy of all registered $id entries in this index. +func (index *SpecIndex) GetAllSchemaIds() map[string]*SchemaIdEntry { + index.schemaIdRegistryLock.RLock() + defer index.schemaIdRegistryLock.RUnlock() + return copySchemaIdRegistry(index.schemaIdRegistry) +} diff --git a/vendor/github.com/pb33f/libopenapi/index/utility_methods.go b/vendor/github.com/pb33f/libopenapi/index/utility_methods.go new file mode 100644 index 000000000..76a38edfd --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/index/utility_methods.go @@ -0,0 +1,791 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package index + +import ( + "context" + "fmt" + "hash/maphash" + "net/url" + "os" + "path/filepath" + "strconv" + "strings" + "sync" + + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +func (index *SpecIndex) extractDefinitionsAndSchemas(schemasNode *yaml.Node, pathPrefix string) { + var name string + var keyNode *yaml.Node + for i, schema := range schemasNode.Content { + if i%2 == 0 { + name = schema.Value + keyNode = schema + continue + } + + def := fmt.Sprintf("%s%s", pathPrefix, name) + fullDef := fmt.Sprintf("%s%s", index.specAbsolutePath, def) + + ref := &Reference{ + FullDefinition: fullDef, + Definition: def, + Name: name, + KeyNode: keyNode, + Node: schema, + Path: fmt.Sprintf("$.components.schemas['%s']", name), + ParentNode: schemasNode, + RequiredRefProperties: extractDefinitionRequiredRefProperties(schemasNode, map[string][]string{}, fullDef, index), + } + index.allComponentSchemaDefinitions.Store(def, ref) + } +} + +// extractDefinitionRequiredRefProperties goes through the direct properties of a schema and extracts the map of required definitions from within it +func extractDefinitionRequiredRefProperties(schemaNode *yaml.Node, reqRefProps map[string][]string, fulldef string, idx *SpecIndex) map[string][]string { + if schemaNode == nil { + return reqRefProps + } + + // If the node we're looking at is a direct ref to another model without any properties, mark it as required, but still continue to look for required properties + isRef, _, defPath := utils.IsNodeRefValue(schemaNode) + if isRef { + if _, ok := reqRefProps[defPath]; !ok { + reqRefProps[defPath] = []string{} + } + } + + // Check for a required parameters list, and return if none exists, as any properties will be optional + _, requiredSeqNode := utils.FindKeyNodeTop("required", schemaNode.Content) + if requiredSeqNode == nil { + return reqRefProps + } + + _, propertiesMapNode := utils.FindKeyNodeTop("properties", schemaNode.Content) + if propertiesMapNode == nil { + // TODO: Log a warning on the resolver, because if you have required properties, but no actual properties, something is wrong + return reqRefProps + } + + name := "" + for i, param := range propertiesMapNode.Content { + if i%2 == 0 { + name = param.Value + continue + } + + // Check to see if the current property is directly embedded within the current schema, and handle its properties if so + _, paramPropertiesMapNode := utils.FindKeyNodeTop("properties", param.Content) + if paramPropertiesMapNode != nil { + reqRefProps = extractDefinitionRequiredRefProperties(param, reqRefProps, fulldef, idx) + } + + // Check to see if the current property is polymorphic, and dive into that model if so + for _, key := range []string{"allOf", "oneOf", "anyOf"} { + _, ofNode := utils.FindKeyNodeTop(key, param.Content) + if ofNode != nil { + for _, ofNodeItem := range ofNode.Content { + reqRefProps = extractRequiredReferenceProperties(fulldef, ofNodeItem, name, reqRefProps) + } + } + } + } + + // Run through each of the required properties and extract _their_ required references + for _, requiredPropertyNode := range requiredSeqNode.Content { + _, requiredPropDefNode := utils.FindKeyNodeTop(requiredPropertyNode.Value, propertiesMapNode.Content) + if requiredPropDefNode == nil { + continue + } + + reqRefProps = extractRequiredReferenceProperties(fulldef, requiredPropDefNode, requiredPropertyNode.Value, reqRefProps) + } + + return reqRefProps +} + +// extractRequiredReferenceProperties returns a map of definition names to the property or properties which reference it within a node +func extractRequiredReferenceProperties(fulldef string, requiredPropDefNode *yaml.Node, propName string, reqRefProps map[string][]string) map[string][]string { + isRef, _, refName := utils.IsNodeRefValue(requiredPropDefNode) + if !isRef { + _, defItems := utils.FindKeyNodeTop("items", requiredPropDefNode.Content) + if defItems != nil { + isRef, _, refName = utils.IsNodeRefValue(defItems) + } + } + + if /* still */ !isRef { + return reqRefProps + } + + defPath := fulldef + + if strings.HasPrefix(refName, "http") || filepath.IsAbs(refName) { + defPath = refName + } else { + exp := strings.Split(fulldef, "#/") + if len(exp) == 2 { + if exp[0] != "" { + if strings.HasPrefix(exp[0], "http") { + u, _ := url.Parse(exp[0]) + r := strings.Split(refName, "#/") + if len(r) == 2 { + var abs string + if r[0] == "" { + abs = u.Path + } else { + abs, _ = filepath.Abs(utils.CheckPathOverlap(filepath.Dir(u.Path), r[0], + string(os.PathSeparator))) + } + + u.Path = utils.ReplaceWindowsDriveWithLinuxPath(abs) + u.Fragment = "" + defPath = fmt.Sprintf("%s#/%s", u.String(), r[1]) + } else { + u.Path = utils.ReplaceWindowsDriveWithLinuxPath(utils.CheckPathOverlap(filepath.Dir(u.Path), + r[0], string(os.PathSeparator))) + u.Fragment = "" + defPath = u.String() + } + } else { + r := strings.Split(refName, "#/") + if len(r) == 2 { + var abs string + if r[0] == "" { + abs, _ = filepath.Abs(exp[0]) + } else { + abs, _ = filepath.Abs(utils.CheckPathOverlap(filepath.Dir(exp[0]), r[0], + string(os.PathSeparator))) + + // abs, _ = filepath.Abs(filepath.Join(filepath.Dir(exp[0]), r[0], + // string('J'))) + } + + defPath = fmt.Sprintf("%s#/%s", abs, r[1]) + } else { + defPath, _ = filepath.Abs(utils.CheckPathOverlap(filepath.Dir(exp[0]), + r[0], string(os.PathSeparator))) + } + } + } else { + defPath = refName + } + } else { + if strings.HasPrefix(exp[0], "http") { + u, _ := url.Parse(exp[0]) + r := strings.Split(refName, "#/") + if len(r) == 2 { + abs, _ := filepath.Abs(utils.CheckPathOverlap(filepath.Dir(u.Path), r[0], string(os.PathSeparator))) + u.Path = utils.ReplaceWindowsDriveWithLinuxPath(abs) + u.Fragment = "" + defPath = fmt.Sprintf("%s#/%s", u.String(), r[1]) + } else { + u.Path = utils.ReplaceWindowsDriveWithLinuxPath(utils.CheckPathOverlap(filepath.Dir(u.Path), + r[0], string(os.PathSeparator))) + u.Fragment = "" + defPath = u.String() + } + } else { + defPath, _ = filepath.Abs(utils.CheckPathOverlap(filepath.Dir(exp[0]), refName, string(os.PathSeparator))) + } + } + } + + if _, ok := reqRefProps[defPath]; !ok { + reqRefProps[defPath] = []string{} + } + reqRefProps[defPath] = append(reqRefProps[defPath], propName) + + return reqRefProps +} + +func (index *SpecIndex) extractComponentParameters(paramsNode *yaml.Node, pathPrefix string) { + var name string + var keyNode *yaml.Node + for i, param := range paramsNode.Content { + if i%2 == 0 { + name = param.Value + keyNode = param + continue + } + def := fmt.Sprintf("%s%s", pathPrefix, name) + ref := &Reference{ + Definition: def, + Name: name, + Node: param, + KeyNode: keyNode, + } + index.allParameters[def] = ref + } +} + +func (index *SpecIndex) extractComponentRequestBodies(requestBodiesNode *yaml.Node, pathPrefix string) { + var name string + var keyNode *yaml.Node + for i, reqBod := range requestBodiesNode.Content { + if i%2 == 0 { + name = reqBod.Value + keyNode = reqBod + continue + } + def := fmt.Sprintf("%s%s", pathPrefix, name) + ref := &Reference{ + Definition: def, + Name: name, + Node: reqBod, + KeyNode: keyNode, + } + index.allRequestBodies[def] = ref + } +} + +func (index *SpecIndex) extractComponentResponses(responsesNode *yaml.Node, pathPrefix string) { + var name string + var keyNode *yaml.Node + for i, response := range responsesNode.Content { + if i%2 == 0 { + name = response.Value + keyNode = response + continue + } + def := fmt.Sprintf("%s%s", pathPrefix, name) + ref := &Reference{ + Definition: def, + Name: name, + Node: response, + KeyNode: keyNode, + } + index.allResponses[def] = ref + } +} + +func (index *SpecIndex) extractComponentHeaders(headersNode *yaml.Node, pathPrefix string) { + var name string + var keyNode *yaml.Node + for i, header := range headersNode.Content { + if i%2 == 0 { + name = header.Value + keyNode = header + continue + } + def := fmt.Sprintf("%s%s", pathPrefix, name) + ref := &Reference{ + Definition: def, + Name: name, + Node: header, + KeyNode: keyNode, + } + index.allHeaders[def] = ref + } +} + +func (index *SpecIndex) extractComponentCallbacks(callbacksNode *yaml.Node, pathPrefix string) { + var name string + var keyNode *yaml.Node + for i, callback := range callbacksNode.Content { + if i%2 == 0 { + name = callback.Value + keyNode = callback + continue + } + def := fmt.Sprintf("%s%s", pathPrefix, name) + ref := &Reference{ + Definition: def, + Name: name, + Node: callback, + KeyNode: keyNode, + } + index.allCallbacks[def] = ref + } +} + +func (index *SpecIndex) extractComponentPathItems(pathItemsNode *yaml.Node, pathPrefix string) { + var name string + var keyNode *yaml.Node + for i, pathItemName := range pathItemsNode.Content { + if i%2 == 0 { + name = pathItemName.Value + keyNode = pathItemName + continue + } + def := fmt.Sprintf("%s%s", pathPrefix, name) + ref := &Reference{ + Definition: def, + Name: name, + Node: pathItemName, + KeyNode: keyNode, + } + index.allComponentPathItems[def] = ref + } +} + +func (index *SpecIndex) extractComponentLinks(linksNode *yaml.Node, pathPrefix string) { + var name string + var keyNode *yaml.Node + for i, link := range linksNode.Content { + if i%2 == 0 { + name = link.Value + keyNode = link + continue + } + def := fmt.Sprintf("%s%s", pathPrefix, name) + ref := &Reference{ + Definition: def, + Name: name, + Node: link, + KeyNode: keyNode, + } + index.allLinks[def] = ref + } +} + +func (index *SpecIndex) extractComponentExamples(examplesNode *yaml.Node, pathPrefix string) { + var name string + var keyNode *yaml.Node + for i, example := range examplesNode.Content { + if i%2 == 0 { + name = example.Value + keyNode = example + continue + } + def := fmt.Sprintf("%s%s", pathPrefix, name) + ref := &Reference{ + Definition: def, + Name: name, + Node: example, + KeyNode: keyNode, + } + index.allExamples[def] = ref + } +} + +func (index *SpecIndex) extractComponentSecuritySchemes(securitySchemesNode *yaml.Node, pathPrefix string) { + var name string + var keyNode *yaml.Node + for i, schema := range securitySchemesNode.Content { + if i%2 == 0 { + name = schema.Value + keyNode = schema + continue + } + def := fmt.Sprintf("%s%s", pathPrefix, name) + fullDef := fmt.Sprintf("%s%s", index.specAbsolutePath, def) + + ref := &Reference{ + FullDefinition: fullDef, + Definition: def, + Name: name, + Node: schema, + KeyNode: keyNode, + Path: fmt.Sprintf("$.components.securitySchemes.%s", name), + ParentNode: securitySchemesNode, + RequiredRefProperties: extractDefinitionRequiredRefProperties(securitySchemesNode, map[string][]string{}, fullDef, index), + } + index.allSecuritySchemes.Store(def, ref) + } +} + +func (index *SpecIndex) countUniqueInlineDuplicates() int { + if index.componentsInlineParamUniqueCount > 0 { + return index.componentsInlineParamUniqueCount + } + unique := 0 + for _, p := range index.paramInlineDuplicateNames { + if len(p) == 1 { + unique++ + } + } + index.componentsInlineParamUniqueCount = unique + return unique +} + +func seekRefEnd(ctx context.Context, index *SpecIndex, refName string) *Reference { + ref, _, nCtx := index.SearchIndexForReferenceWithContext(ctx, refName) + if ref != nil { + if ok, _, v := utils.IsNodeRefValue(ref.Node); ok { + return seekRefEnd(nCtx, ref.Index, v) + } + } + return ref +} + +// formatParameterPath creates a consistent JSON path for parameter error messages +func formatParameterPath(pathValue, method string, index int) string { + if method == "top" { + return fmt.Sprintf("$.paths['%s'].parameters[%d]", pathValue, index) + } + return fmt.Sprintf("$.paths['%s'].%s.parameters[%d]", pathValue, method, index) +} + +func (index *SpecIndex) scanOperationParams(params []*yaml.Node, keyNode, pathItemNode *yaml.Node, method string) { + for i, param := range params { + // param is ref + if len(param.Content) > 0 && param.Content[0].Value == "$ref" { + + paramRefName := param.Content[1].Value + paramRef := index.allMappedRefs[paramRefName] + if paramRef == nil { + // could be in the rolodex + searchInIndex := findIndex(index, param.Content[1]) + ctx := context.WithValue(context.Background(), CurrentPathKey, searchInIndex.specAbsolutePath) + ctx = context.WithValue(ctx, RootIndexKey, searchInIndex) + ref := seekRefEnd(ctx, searchInIndex, paramRefName) + if ref != nil { + paramRef = ref + if strings.Contains(paramRefName, "%") { + paramRefName, _ = url.QueryUnescape(paramRefName) + } + } + } + + if index.paramOpRefs[pathItemNode.Value] == nil { + index.paramOpRefs[pathItemNode.Value] = make(map[string]map[string][]*Reference) + index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference) + + } + // if we know the path, but it's a new method + if index.paramOpRefs[pathItemNode.Value][method] == nil { + index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference) + } + + // if this is a duplicate, add an error and ignore it + if index.paramOpRefs[pathItemNode.Value][method][paramRefName] != nil { + path := formatParameterPath(pathItemNode.Value, method, i) + + index.operationParamErrors = append(index.operationParamErrors, &IndexingError{ + Err: fmt.Errorf("the `%s` operation parameter at path `%s`, "+ + "index %d has a duplicate ref `%s`", strings.ToUpper(method), pathItemNode.Value, i, paramRefName), + Node: param, + Path: path, + }) + } else { + if paramRef != nil { + index.paramOpRefs[pathItemNode.Value][method][paramRefName] = append(index.paramOpRefs[pathItemNode.Value][method][paramRefName], paramRef) + } + } + + continue + + } else { + + // param is inline. + _, vn := utils.FindKeyNode("name", param.Content) + + path := formatParameterPath(pathItemNode.Value, method, i) + + if vn == nil { + index.operationParamErrors = append(index.operationParamErrors, &IndexingError{ + Err: fmt.Errorf("the `%s` operation parameter at path `%s`, index %d has no `name` value", + strings.ToUpper(method), pathItemNode.Value, i), + Node: param, + Path: path, + }) + continue + } + + ref := &Reference{ + Definition: vn.Value, + Name: vn.Value, + Node: param, + KeyNode: keyNode, + Path: path, + } + + // cache the 'in' value for performance optimization (fix for issue #379) + _, inNode := utils.FindKeyNodeTop("in", param.Content) + if inNode != nil { + ref.In = inNode.Value + } + if index.paramOpRefs[pathItemNode.Value] == nil { + index.paramOpRefs[pathItemNode.Value] = make(map[string]map[string][]*Reference) + index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference) + } + + // if we know the path but this is a new method. + if index.paramOpRefs[pathItemNode.Value][method] == nil { + index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference) + } + + // Fix for issue #379: Ensure consistent parameter counting regardless of ordering + // https://github.com/pb33f/libopenapi/issues/379 + // check if this parameter name already exists, and detect duplicates in the same location + if len(index.paramOpRefs[pathItemNode.Value][method][ref.Name]) > 0 { + + checkNodes := index.paramOpRefs[pathItemNode.Value][method][ref.Name] + + // check if there's a duplicate with the same 'in' type (query, path, header, cookie) + hasDuplicateInSameLocation := false + for _, checkNode := range checkNodes { + // both must have 'in' values and they must match to be a duplicate + if ref.In != "" && checkNode.In != "" && checkNode.In == ref.In { + // found a duplicate parameter with same name and location + hasDuplicateInSameLocation = true + + index.operationParamErrors = append(index.operationParamErrors, &IndexingError{ + Err: fmt.Errorf("the `%s` operation parameter at path `%s`, "+ + "index %d has a duplicate name `%s` and `in` type", strings.ToUpper(method), pathItemNode.Value, i, vn.Value), + Node: param, + Path: path, + }) + break // no need to check further once duplicate found + } + } + + // only add the parameter if it's not a duplicate in the same location + if !hasDuplicateInSameLocation { + index.paramOpRefs[pathItemNode.Value][method][ref.Name] = append(index.paramOpRefs[pathItemNode.Value][method][ref.Name], ref) + } + } else { + // First parameter with this name, add it + index.paramOpRefs[pathItemNode.Value][method][ref.Name] = append(index.paramOpRefs[pathItemNode.Value][method][ref.Name], ref) + } + continue + } + } +} + +func findIndex(index *SpecIndex, i *yaml.Node) *SpecIndex { + rolodex := index.GetRolodex() + if rolodex == nil { + return index + } + allIndexes := rolodex.GetIndexes() + for _, searchIndex := range allIndexes { + nodeMap := searchIndex.GetNodeMap() + line, ok := nodeMap[i.Line] + if !ok { + continue + } + node, ok := line[i.Column] + if !ok { + continue + } + if node == i { + return searchIndex + } + } + return index +} + +func runIndexFunction(funcs []func() int, wg *sync.WaitGroup) { + for _, cFunc := range funcs { + go func(wg *sync.WaitGroup, cf func() int) { + cf() + wg.Done() + }(wg, cFunc) + } +} + +func GenerateCleanSpecConfigBaseURL(baseURL *url.URL, dir string, includeFile bool) string { + cleanedPath := baseURL.Path // not cleaned yet! + + // create a slice of path segments from existing path + pathSegs := strings.Split(cleanedPath, "/") + dirSegs := strings.Split(dir, "/") + + var cleanedSegs []string + if !includeFile { + dirSegs = dirSegs[:len(dirSegs)-1] + } + + // relative paths are a pain in the ass, damn you digital ocean, use a single spec, and break them + // down into services, please don't blast apart specs into a billion shards. + if strings.Contains(dir, "../") { + for s := range dirSegs { + if dirSegs[s] == ".." { + // chop off the last segment of the base path. + if len(pathSegs) > 0 { + pathSegs = pathSegs[:len(pathSegs)-1] + } + } else { + cleanedSegs = append(cleanedSegs, dirSegs[s]) + } + } + cleanedPath = fmt.Sprintf("%s/%s", strings.Join(pathSegs, "/"), strings.Join(cleanedSegs, "/")) + } else { + if !strings.HasPrefix(dir, "http") { + if len(pathSegs) > 1 || len(dirSegs) > 1 { + cleanedPath = fmt.Sprintf("%s/%s", strings.Join(pathSegs, "/"), strings.Join(dirSegs, "/")) + } + } else { + cleanedPath = strings.Join(dirSegs, "/") + } + } + var p string + if baseURL.Scheme != "" && !strings.HasPrefix(dir, "http") { + p = fmt.Sprintf("%s://%s%s", baseURL.Scheme, baseURL.Host, cleanedPath) + } else { + if !strings.Contains(cleanedPath, "/") { + p = "" + } else { + p = cleanedPath + } + } + return strings.TrimSuffix(p, "/") +} + +func syncMapToMap[K comparable, V any](sm *sync.Map) map[K]V { + if sm == nil { + return nil + } + + m := make(map[K]V) + + sm.Range(func(key, value interface{}) bool { + m[key.(K)] = value.(V) + return true + }) + + return m +} + +// ClearHashCache clears the hash cache - useful for testing and memory management +func ClearHashCache() { + nodeHashCache.Range(func(key, value interface{}) bool { + nodeHashCache.Delete(key) + return true + }) +} + +// hasherPool pools maphash.Hash instances to avoid allocations. +// maphash is ~15x faster than SHA256 and has native WriteString support. +var hasherPool = sync.Pool{ + New: func() interface{} { + h := &maphash.Hash{} + h.SetSeed(globalHashSeed) // ensure consistent hashes across pooled instances + return h + }, +} + +// stackPool pools node pointer slices for HashNode traversal. +// avoids allocating ~1KB per HashNode call. +var stackPool = sync.Pool{ + New: func() interface{} { + s := make([]*yaml.Node, 0, 128) + return &s + }, +} + +// visitedPool pools visited maps for circular reference detection. +// avoids allocating ~2KB per HashNode call. +var visitedPool = sync.Pool{ + New: func() interface{} { + return make(map[*yaml.Node]struct{}, 64) + }, +} + +// nodeHashCache caches hash results by node pointer for repeated lookups. +// yaml.Node pointers are stable for the document lifetime. +var nodeHashCache = sync.Map{} // *yaml.Node -> string + +// hashCacheThreshold determines when to cache hash results. +// lowered from 200 to 20 for more aggressive caching of repeated patterns. +const hashCacheThreshold = 20 + +// globalHashSeed ensures all maphash instances produce consistent results. +// maphash uses random seeds by default; we need deterministic hashes for caching. +var globalHashSeed maphash.Seed + +// emptyNodeHash is the hash of a nil node (computed once at init). +var emptyNodeHash string + +func init() { + globalHashSeed = maphash.MakeSeed() + var h maphash.Hash + h.SetSeed(globalHashSeed) + emptyNodeHash = strconv.FormatUint(h.Sum64(), 16) +} + +// writeIntToHash writes a non-negative integer to the hash without heap allocations. +// Uses a stack-allocated buffer. Line/Column values are always non-negative. +func writeIntToHash(h *maphash.Hash, n int) { + if n == 0 { + h.WriteByte('0') + return + } + // max int64 is 19 digits, 20 is safe + var buf [20]byte + i := len(buf) + for n > 0 { + i-- + buf[i] = byte('0' + n%10) + n /= 10 + } + h.Write(buf[i:]) +} + +// HashNode returns a fast hash string of the node and its children. +// Uses maphash (same algorithm as Go maps) with WriteString for zero allocations. +// Iterative traversal avoids recursion overhead. +func HashNode(n *yaml.Node) string { + if n == nil { + return emptyNodeHash + } + + // check cache first (by pointer - yaml.Node pointers are stable) + if cached, ok := nodeHashCache.Load(n); ok { + return cached.(string) + } + + // get hasher from pool + h := hasherPool.Get().(*maphash.Hash) + h.Reset() + defer hasherPool.Put(h) + + // get stack from pool, reset length but keep capacity + stackPtr := stackPool.Get().(*[]*yaml.Node) + stack := (*stackPtr)[:0] + stack = append(stack, n) + defer func() { + *stackPtr = stack[:0] + stackPool.Put(stackPtr) + }() + + // get visited map from pool, clear entries + visited := visitedPool.Get().(map[*yaml.Node]struct{}) + clear(visited) + defer func() { + clear(visited) + visitedPool.Put(visited) + }() + + for len(stack) > 0 { + // pop from stack + node := stack[len(stack)-1] + stack = stack[:len(stack)-1] + + if node == nil { + continue + } + + // skip already visited nodes (handles circular references) + if _, seen := visited[node]; seen { + continue + } + visited[node] = struct{}{} + + // hash node content - WriteString for strings, writeIntToHash for ints (zero allocations) + h.WriteString(node.Tag) + writeIntToHash(h, node.Line) + writeIntToHash(h, node.Column) + h.WriteString(node.Value) + + // push children in reverse order for correct traversal order + for i := len(node.Content) - 1; i >= 0; i-- { + stack = append(stack, node.Content[i]) + } + } + + result := strconv.FormatUint(h.Sum64(), 16) + + // cache result for nodes with children (likely to be looked up again) + if len(n.Content) >= hashCacheThreshold { + nodeHashCache.Store(n, result) + } + + return result +} diff --git a/vendor/github.com/pb33f/libopenapi/json/json.go b/vendor/github.com/pb33f/libopenapi/json/json.go new file mode 100644 index 000000000..5c3737e42 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/json/json.go @@ -0,0 +1,98 @@ +package json + +import ( + "encoding/json" + "fmt" + "reflect" + + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// YAMLNodeToJSON converts yaml/json stored in a yaml.Node to json ordered matching the original yaml/json +func YAMLNodeToJSON(node *yaml.Node, indentation string) ([]byte, error) { + v, err := handleYAMLNode(node) + if err != nil { + return nil, err + } + + return json.MarshalIndent(v, "", indentation) +} + +func handleYAMLNode(node *yaml.Node) (any, error) { + switch node.Kind { + case yaml.DocumentNode: + return handleYAMLNode(node.Content[0]) + case yaml.SequenceNode: + return handleSequenceNode(node) + case yaml.MappingNode: + return handleMappingNode(node) + case yaml.ScalarNode: + return handleScalarNode(node) + case yaml.AliasNode: + return handleYAMLNode(node.Alias) + default: + return nil, fmt.Errorf("unknown node kind: %v", node.Kind) + } +} + +func handleMappingNode(node *yaml.Node) (any, error) { + v := orderedmap.New[string, any]() + for i, n := range node.Content { + if i%2 == 0 { + continue + } + keyNode := node.Content[i-1] + kv, err := handleYAMLNode(keyNode) + if err != nil { + return nil, err + } + + if reflect.TypeOf(kv).Kind() != reflect.String { + keyData, err := json.Marshal(kv) + if err != nil { + return nil, err // unreachable code in test case, but kept for safety + } + kv = string(keyData) + } + + vv, err := handleYAMLNode(n) + if err != nil { + return nil, err + } + + v.Set(fmt.Sprintf("%v", kv), vv) + } + + return v, nil +} + +func handleSequenceNode(node *yaml.Node) (any, error) { + var s []yaml.Node + + if err := node.Decode(&s); err != nil { + return nil, err // unreachable code in test case, but kept for safety + } + + v := make([]any, len(s)) + for i, n := range s { + vv, err := handleYAMLNode(&n) + if err != nil { + return nil, err + } + + v[i] = vv + } + + return v, nil +} + +func handleScalarNode(node *yaml.Node) (any, error) { + var v any + + if err := node.Decode(&v); err != nil { + return nil, err + } + + return v, nil +} diff --git a/vendor/github.com/pb33f/libopenapi/libopenapi-logo.png b/vendor/github.com/pb33f/libopenapi/libopenapi-logo.png new file mode 100644 index 000000000..78bf7e5fc Binary files /dev/null and b/vendor/github.com/pb33f/libopenapi/libopenapi-logo.png differ diff --git a/vendor/github.com/pb33f/libopenapi/orderedmap/builder.go b/vendor/github.com/pb33f/libopenapi/orderedmap/builder.go new file mode 100644 index 000000000..240314589 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/orderedmap/builder.go @@ -0,0 +1,123 @@ +package orderedmap + +import ( + "fmt" + "strings" + + "github.com/pb33f/libopenapi/datamodel/high/nodes" + "github.com/pb33f/libopenapi/utils" + "go.yaml.in/yaml/v4" +) + +type marshaler interface { + MarshalYAML() (interface{}, error) +} + +type NodeBuilder interface { + AddYAMLNode(parent *yaml.Node, entry *nodes.NodeEntry) *yaml.Node +} + +type MapToYamlNoder interface { + ToYamlNode(n NodeBuilder, l any) *yaml.Node +} + +type hasValueNode interface { + GetValueNode() *yaml.Node +} + +type hasValueUntyped interface { + GetValueUntyped() any +} + +type findValueUntyped interface { + FindValueUntyped(k string) any +} + +// ToYamlNode converts the ordered map to a yaml node ready for marshalling. +func (o *Map[K, V]) ToYamlNode(n NodeBuilder, l any) *yaml.Node { + p := utils.CreateEmptyMapNode() + if o != nil { + p.Content = make([]*yaml.Node, 0) + } + + var vn *yaml.Node + + i := 99999 + if l != nil { + if hvn, ok := l.(hasValueNode); ok { + vn = hvn.GetValueNode() + if vn != nil && len(vn.Content) > 0 { + i = vn.Content[0].Line + } + } + } + + for pair := First(o); pair != nil; pair = pair.Next() { + var k any = pair.Key() + if m, ok := k.(marshaler); ok { // TODO marshal inline? + mk, _ := m.MarshalYAML() + b, _ := yaml.Marshal(mk) + k = strings.TrimSpace(string(b)) + } + + ks := k.(string) + + var keyStyle yaml.Style + keyNode := findKeyNode(ks, vn) + if keyNode != nil { + keyStyle = keyNode.Style + } + + var lv any + if l != nil { + if hvut, ok := l.(hasValueUntyped); ok { + vut := hvut.GetValueUntyped() + if m, ok := vut.(findValueUntyped); ok { + lv = m.FindValueUntyped(ks) + } + } + } + + n.AddYAMLNode(p, &nodes.NodeEntry{ + Tag: ks, + Key: ks, + Line: i, + Value: pair.Value(), + KeyStyle: keyStyle, + LowValue: lv, + }) + i++ + } + + return p +} + +func findKeyNode(key string, m *yaml.Node) *yaml.Node { + if m == nil { + return nil + } + + for i := 0; i < len(m.Content); i += 2 { + if m.Content[i].Value == key { + return m.Content[i] + } + } + return nil +} + +// FindValueUntyped finds a value in the ordered map by key if the stored value for that key implements GetValueUntyped otherwise just returns the value. +func (o *Map[K, V]) FindValueUntyped(key string) any { + for pair := First(o); pair != nil; pair = pair.Next() { + var k any = pair.Key() + if hvut, ok := k.(hasValueUntyped); ok { + if fmt.Sprintf("%v", hvut.GetValueUntyped()) == key { + return pair.Value() + } + } + if fmt.Sprintf("%v", k) == key { + return pair.Value() + } + } + + return nil +} diff --git a/vendor/github.com/pb33f/libopenapi/orderedmap/orderedmap.go b/vendor/github.com/pb33f/libopenapi/orderedmap/orderedmap.go new file mode 100644 index 000000000..52f7bd4b6 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/orderedmap/orderedmap.go @@ -0,0 +1,323 @@ +// Ordered map container +// Works like the Golang `map` built-in, but preserves order that key/value +// pairs were added when iterating. + +package orderedmap + +import ( + "context" + "fmt" + "iter" + "reflect" + "slices" + "strings" + + wk8orderedmap "github.com/pb33f/ordered-map/v2" +) + +// Pair represents a key/value pair in an ordered map returned for iteration. +type Pair[K comparable, V any] interface { + Key() K + KeyPtr() *K + Value() V + ValuePtr() *V + Next() Pair[K, V] +} + +// Map represents an ordered map where the key must be a comparable type, the ordering is based on insertion order. +type Map[K comparable, V any] struct { + *wk8orderedmap.OrderedMap[K, V] +} + +type wrapPair[K comparable, V any] struct { + *wk8orderedmap.Pair[K, V] +} + +// New creates an ordered map generic object. +func New[K comparable, V any]() *Map[K, V] { + return &Map[K, V]{ + OrderedMap: wk8orderedmap.New[K, V](), + } +} + +// GetKeyType returns the reflection type of the key. +func (o *Map[K, V]) GetKeyType() reflect.Type { + return reflect.TypeOf(new(K)) +} + +// GetValueType returns the reflection type of the value. +func (o *Map[K, V]) GetValueType() reflect.Type { + return reflect.TypeOf(new(V)) +} + +// GetOrZero will return the value for the key if it exists, otherwise it will return the zero value for the value type. +func (o *Map[K, V]) GetOrZero(k K) V { + v, ok := o.OrderedMap.Get(k) + if !ok { + var zero V + return zero + } + return v +} + +// First returns the first pair in the map useful for iteration. +func (o *Map[K, V]) First() Pair[K, V] { + if o == nil { + return nil + } + + pair := o.OrderedMap.Oldest() + if pair == nil { + return nil + } + return &wrapPair[K, V]{ + Pair: pair, + } +} + +// FromOldest returns an iterator that yields the oldest key-value pair in the map. +func (o *Map[K, V]) FromOldest() iter.Seq2[K, V] { + return func(yield func(K, V) bool) { + if o == nil { + return + } + + for k, v := range o.OrderedMap.FromOldest() { + if !yield(k, v) { + return + } + } + } +} + +// FromNewest returns an iterator that yields the newest key-value pair in the map. +func (o *Map[K, V]) FromNewest() iter.Seq2[K, V] { + return func(yield func(K, V) bool) { + if o == nil { + return + } + + for k, v := range o.OrderedMap.FromNewest() { + if !yield(k, v) { + return + } + } + } +} + +// FromNewest returns an iterator that yields the newest key-value pair in the map. +func (o *Map[K, V]) KeysFromOldest() iter.Seq[K] { + return func(yield func(K) bool) { + if o == nil { + return + } + + for k := range o.OrderedMap.KeysFromOldest() { + if !yield(k) { + return + } + } + } +} + +// KeysFromNewest returns an iterator that yields the newest key in the map. +func (o *Map[K, V]) KeysFromNewest() iter.Seq[K] { + return func(yield func(K) bool) { + if o == nil { + return + } + + for k := range o.OrderedMap.KeysFromNewest() { + if !yield(k) { + return + } + } + } +} + +// ValuesFromOldest returns an iterator that yields the oldest value in the map. +func (o *Map[K, V]) ValuesFromOldest() iter.Seq[V] { + return func(yield func(V) bool) { + if o == nil { + return + } + for v := range o.OrderedMap.ValuesFromOldest() { + if !yield(v) { + return + } + } + } +} + +// ValuesFromNewest returns an iterator that yields the newest value in the map. +func (o *Map[K, V]) ValuesFromNewest() iter.Seq[V] { + return func(yield func(V) bool) { + if o == nil { + return + } + for v := range o.OrderedMap.ValuesFromNewest() { + if !yield(v) { + return + } + } + } +} + +// From creates a new ordered map from an iterator. +func From[K comparable, V any](iter iter.Seq2[K, V]) *Map[K, V] { + return &Map[K, V]{ + OrderedMap: wk8orderedmap.From(iter), + } +} + +// NewPair instantiates a `Pair` object for use with `FromPairs()`. +func NewPair[K comparable, V any](key K, value V) Pair[K, V] { + return &wrapPair[K, V]{ + Pair: &wk8orderedmap.Pair[K, V]{ + Key: key, + Value: value, + }, + } +} + +// FromPairs creates an `OrderedMap` from an array of pairs. +// Use `NewPair()` to generate input parameters. +func FromPairs[K comparable, V any](pairs ...Pair[K, V]) *Map[K, V] { + om := New[K, V]() + for _, pair := range pairs { + om.Set(pair.Key(), pair.Value()) + } + return om +} + +// IsZero is required to support `omitempty` tag for YAML/JSON marshaling. +func (o *Map[K, V]) IsZero() bool { + return Len(o) == 0 +} + +// Next returns the next pair in the map when iterating. +func (p *wrapPair[K, V]) Next() Pair[K, V] { + next := p.Pair.Next() + if next == nil { + return nil + } + return &wrapPair[K, V]{ + Pair: next, + } +} + +// Key returns the key of the pair. +func (p *wrapPair[K, V]) Key() K { + return p.Pair.Key +} + +// KeyPtr returns a pointer to the key of the pair. +func (p *wrapPair[K, V]) KeyPtr() *K { + return &p.Pair.Key +} + +// Value returns the value of the pair. +func (p *wrapPair[K, V]) Value() V { + return p.Pair.Value +} + +// ValuePtr returns a pointer to the value of the pair. +func (p *wrapPair[K, V]) ValuePtr() *V { + return &p.Pair.Value +} + +// Len returns the length of a container implementing a `Len()` method. +// Safely returns zero on nil pointer. +func Len[K comparable, V any](m *Map[K, V]) int { + if m == nil { + return 0 + } + return m.Len() +} + +// Iterate the map in order. +// Safely handles nil pointer. +// Be sure to iterate to end or cancel the context when done to release +// resources. +func Iterate[K comparable, V any](ctx context.Context, m *Map[K, V]) <-chan Pair[K, V] { + c := make(chan Pair[K, V]) + if Len(m) == 0 { + close(c) + return c + } + go func() { + defer close(c) + for pair := First(m); pair != nil; pair = pair.Next() { + select { + case c <- pair: + case <-ctx.Done(): + return + } + } + }() + return c +} + +// ToOrderedMap converts a `map` to `OrderedMap`. +func ToOrderedMap[K comparable, V any](m map[K]V) *Map[K, V] { + om := New[K, V]() + for k, v := range m { + om.Set(k, v) + } + return SortAlpha(om) +} + +// First returns map's first pair for iteration. +// Safely handles nil pointer. +func First[K comparable, V any](m *Map[K, V]) Pair[K, V] { + if m == nil { + return nil + } + return m.First() +} + +// Cast converts `any` to `Map`. +func Cast[K comparable, V any](v any) *Map[K, V] { + if v == nil { + return nil + } + + m, ok := v.(*Map[K, V]) + if !ok { + return nil + } + + return m +} + +// SortAlpha sorts the map by keys in alphabetical order. +func SortAlpha[K comparable, V any](m *Map[K, V]) *Map[K, V] { + if m == nil { + return nil + } + + om := New[K, V]() + + type key struct { + key string + k K + } + + keys := []key{} + for pair := First(m); pair != nil; pair = pair.Next() { + keys = append(keys, key{ + key: fmt.Sprintf("%v", pair.Key()), + k: pair.Key(), + }) + } + + slices.SortFunc(keys, func(a, b key) int { + return strings.Compare(a.key, b.key) + }) + + for _, k := range keys { + om.Set(k.k, m.GetOrZero(k.k)) + } + + return om +} diff --git a/vendor/github.com/pb33f/libopenapi/overlay.go b/vendor/github.com/pb33f/libopenapi/overlay.go new file mode 100644 index 000000000..2f475f0bd --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/overlay.go @@ -0,0 +1,134 @@ +// Copyright 2022-2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package libopenapi + +import ( + gocontext "context" + + "github.com/pb33f/libopenapi/datamodel" + highoverlay "github.com/pb33f/libopenapi/datamodel/high/overlay" + "github.com/pb33f/libopenapi/datamodel/low" + lowoverlay "github.com/pb33f/libopenapi/datamodel/low/overlay" + "github.com/pb33f/libopenapi/overlay" + "go.yaml.in/yaml/v4" +) + +// OverlayResult contains the result of applying an overlay to a target document. +type OverlayResult struct { + // Bytes raw YAML bytes of the modified document after the overlay has been applied. + Bytes []byte + + // OverlayDocument is the modified document, ready to have a model built from it. + // The document is created using the same configuration as the input document + // (for ApplyOverlay and ApplyOverlayFromBytes), or with a default configuration + // (for ApplyOverlayToSpecBytes and ApplyOverlayFromBytesToSpecBytes). + OverlayDocument Document + + // Warnings that occurred during overlay application. + Warnings []*overlay.Warning +} + +// NewOverlayDocument creates a new overlay document from the provided bytes. +// The overlay document can then be applied to a target OpenAPI document using ApplyOverlay. +func NewOverlayDocument(overlayBytes []byte) (*highoverlay.Overlay, error) { + var node yaml.Node + if err := yaml.Unmarshal(overlayBytes, &node); err != nil { + return nil, err + } + + if len(node.Content) == 0 { + return nil, overlay.ErrInvalidOverlay + } + + var lowOv lowoverlay.Overlay + if err := low.BuildModel(node.Content[0], &lowOv); err != nil { + return nil, err + } + + if err := lowOv.Build(gocontext.Background(), nil, node.Content[0], nil); err != nil { + return nil, err + } + + return highoverlay.NewOverlay(&lowOv), nil +} + +// ApplyOverlay applies the overlay to the target document and returns the modified document. +// This is the primary entry point for an overlay application when working with Document objects. +// +// The returned OverlayDocument uses the same configuration as the input document. +func ApplyOverlay(document Document, ov *highoverlay.Overlay) (*OverlayResult, error) { + specBytes := document.GetSpecInfo().SpecBytes + if specBytes == nil { + return nil, overlay.ErrNoTargetDocument + } + + result, err := overlay.Apply(*specBytes, ov) + if err != nil { + return nil, err + } + + newDoc, err := NewDocumentWithConfiguration(result.Bytes, document.GetConfiguration()) + if err != nil { + return nil, err + } + + return &OverlayResult{ + Bytes: result.Bytes, + OverlayDocument: newDoc, + Warnings: result.Warnings, + }, nil +} + +// ApplyOverlayFromBytes applies an overlay (provided as bytes) to the target document. +// This is a convenience function when you have a Document but the overlay as raw bytes. +// +// The returned OverlayDocument uses the same configuration as the input document. +func ApplyOverlayFromBytes(document Document, overlayBytes []byte) (*OverlayResult, error) { + ov, err := NewOverlayDocument(overlayBytes) + if err != nil { + return nil, err + } + return ApplyOverlay(document, ov) +} + +// ApplyOverlayToSpecBytes applies the overlay to the target document bytes. +// Use this when you have raw spec bytes and a parsed Overlay object. +// +// The returned OverlayDocument uses a default document configuration. +func ApplyOverlayToSpecBytes(docBytes []byte, ov *highoverlay.Overlay) (*OverlayResult, error) { + return applyOverlayToBytesWithConfig(docBytes, ov, nil) +} + +// ApplyOverlayFromBytesToSpecBytes applies an overlay to target document bytes, +// where both the overlay and target document are provided as raw bytes. +// This is the most convenient function when you don't need to configure either document. +// +// The returned OverlayDocument uses a default document configuration. +func ApplyOverlayFromBytesToSpecBytes(docBytes, overlayBytes []byte) (*OverlayResult, error) { + ov, err := NewOverlayDocument(overlayBytes) + if err != nil { + return nil, err + } + return applyOverlayToBytesWithConfig(docBytes, ov, nil) +} + +// applyOverlayToBytesWithConfig is the internal function that applies the overlay to bytes +// and creates a Document with the specified configuration (nil for default). +func applyOverlayToBytesWithConfig(targetBytes []byte, ov *highoverlay.Overlay, config *datamodel.DocumentConfiguration) (*OverlayResult, error) { + result, err := overlay.Apply(targetBytes, ov) + if err != nil { + return nil, err + } + + newDoc, err := NewDocumentWithConfiguration(result.Bytes, config) + if err != nil { + return nil, err + } + + return &OverlayResult{ + Bytes: result.Bytes, + OverlayDocument: newDoc, + Warnings: result.Warnings, + }, nil +} diff --git a/vendor/github.com/pb33f/libopenapi/overlay/engine.go b/vendor/github.com/pb33f/libopenapi/overlay/engine.go new file mode 100644 index 000000000..4a18b55e3 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/overlay/engine.go @@ -0,0 +1,275 @@ +// Copyright 2022-2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package overlay + +import ( + "github.com/pb33f/jsonpath/pkg/jsonpath" + "github.com/pb33f/jsonpath/pkg/jsonpath/config" + highoverlay "github.com/pb33f/libopenapi/datamodel/high/overlay" + "go.yaml.in/yaml/v4" +) + +// Apply applies the given overlay to the target document bytes. +// It returns the modified document bytes and any warnings encountered. +func Apply(targetBytes []byte, overlay *highoverlay.Overlay) (*Result, error) { + if overlay == nil { + return nil, ErrInvalidOverlay + } + + if err := validateOverlay(overlay); err != nil { + return nil, err + } + + var rootNode yaml.Node + if err := yaml.Unmarshal(targetBytes, &rootNode); err != nil { + return nil, err + } + + // Parent index is built lazily and rebuilt after updates/copies to ensure + // remove actions can target nodes created by earlier update/copy actions. + var parentIdx parentIndex + parentIdxStale := true + + var warnings []*Warning + for _, action := range overlay.Actions { + if action.Remove && parentIdxStale { + parentIdx = newParentIndex(&rootNode) + parentIdxStale = false + } + + actionWarnings, err := applyAction(&rootNode, action, parentIdx) + if err != nil { + return nil, &OverlayError{Action: action, Cause: err} + } + warnings = append(warnings, actionWarnings...) + + // Mark parent index as stale after update or copy operations + // (both can add new nodes that subsequent remove actions may target) + if action.Update != nil || action.Copy != "" { + parentIdxStale = true + } + } + + resultBytes, err := yaml.Marshal(&rootNode) + if err != nil { + return nil, err + } + + return &Result{ + Bytes: resultBytes, + Warnings: warnings, + }, nil +} + +func applyAction(root *yaml.Node, action *highoverlay.Action, parentIdx parentIndex) ([]*Warning, error) { + var warnings []*Warning + + if action.Target == "" { + return warnings, nil + } + + path, err := jsonpath.NewPath(action.Target, config.WithPropertyNameExtension()) + if err != nil { + return nil, ErrInvalidJSONPath + } + + nodes := path.Query(root) + + if len(nodes) == 0 { + warnings = append(warnings, &Warning{ + Action: action, + Target: action.Target, + Message: "target matched zero nodes", + }) + return warnings, nil + } + + // Operation order per spec: copy → update → remove + // This allows: + // - Copy to populate the target first + // - Update to override copied values + // - Remove to clean up afterwards (move pattern) + + // 1. Copy (if present) + if action.Copy != "" { + copyWarnings, err := applyCopyAction(root, nodes, action.Copy) + if err != nil { + return nil, err + } + warnings = append(warnings, copyWarnings...) + } + + // 2. Update (if present) + // Validate targets for UPDATE actions (must be objects or arrays, not primitives). + // Validation happens AFTER copy because copy may change the target node type. + // REMOVE actions can target any node type. + if action.Update != nil { + for _, node := range nodes { + if err := validateTarget(node); err != nil { + return nil, err + } + } + applyUpdateAction(nodes, action.Update) + } + + // 3. Remove (if present) + if action.Remove { + applyRemoveAction(parentIdx, nodes) + } + + return warnings, nil +} + +func applyCopyAction(root *yaml.Node, targetNodes []*yaml.Node, copyPath string) ([]*Warning, error) { + var warnings []*Warning + + path, err := jsonpath.NewPath(copyPath, config.WithPropertyNameExtension()) + if err != nil { + return nil, ErrInvalidJSONPath + } + + sourceNodes := path.Query(root) + + // Single-node constraint per spec: copy source must select exactly one node + if len(sourceNodes) == 0 { + return nil, ErrCopySourceNotFound + } + if len(sourceNodes) > 1 { + return nil, ErrCopySourceMultiple + } + + sourceNode := sourceNodes[0] + + // Type compatibility check per spec: "If the target expression and + // copy expression do not return the same type, an error MUST be reported" + for _, targetNode := range targetNodes { + if sourceNode.Kind != targetNode.Kind { + return nil, ErrCopyTypeMismatch + } + mergeNode(targetNode, sourceNode) + } + + return warnings, nil +} + +func applyRemoveAction(idx parentIndex, nodes []*yaml.Node) { + for _, node := range nodes { + removeNode(idx, node) + } +} + +func applyUpdateAction(nodes []*yaml.Node, update *yaml.Node) { + if update.IsZero() { + return + } + for _, node := range nodes { + mergeNode(node, update) + } +} + +type parentIndex map[*yaml.Node]*yaml.Node + +func newParentIndex(root *yaml.Node) parentIndex { + index := parentIndex{} + index.indexNodeRecursively(root) + return index +} + +func (index parentIndex) indexNodeRecursively(parent *yaml.Node) { + for _, child := range parent.Content { + index[child] = parent + index.indexNodeRecursively(child) + } +} + +func (index parentIndex) getParent(child *yaml.Node) *yaml.Node { + return index[child] +} + +func removeNode(idx parentIndex, node *yaml.Node) { + parent := idx.getParent(node) + if parent == nil { + return + } + + for i, child := range parent.Content { + if child == node { + switch parent.Kind { + case yaml.MappingNode: + // JSONPath returns value nodes (odd indices), so remove both key and value + parent.Content = append(parent.Content[:i-1], parent.Content[i+1:]...) + return + case yaml.SequenceNode: + parent.Content = append(parent.Content[:i], parent.Content[i+1:]...) + return + } + } + } +} + +func mergeNode(node *yaml.Node, merge *yaml.Node) { + if node.Kind != merge.Kind { + *node = *cloneNode(merge) + return + } + switch node.Kind { + default: + node.Value = merge.Value + case yaml.MappingNode: + mergeMappingNode(node, merge) + case yaml.SequenceNode: + mergeSequenceNode(node, merge) + } +} + +func mergeMappingNode(node *yaml.Node, merge *yaml.Node) { +NextKey: + for i := 0; i < len(merge.Content); i += 2 { + mergeKey := merge.Content[i].Value + mergeValue := merge.Content[i+1] + + for j := 0; j < len(node.Content); j += 2 { + nodeKey := node.Content[j].Value + if nodeKey == mergeKey { + mergeNode(node.Content[j+1], mergeValue) + continue NextKey + } + } + + node.Content = append(node.Content, merge.Content[i], cloneNode(mergeValue)) + } +} + +func mergeSequenceNode(node *yaml.Node, merge *yaml.Node) { + // clone each child individually to avoid wasteful intermediate allocation + for _, child := range merge.Content { + node.Content = append(node.Content, cloneNode(child)) + } +} + +func cloneNode(node *yaml.Node) *yaml.Node { + if node == nil { + return nil + } + newNode := &yaml.Node{ + Kind: node.Kind, + Style: node.Style, + Tag: node.Tag, + Value: node.Value, + Anchor: node.Anchor, + HeadComment: node.HeadComment, + LineComment: node.LineComment, + FootComment: node.FootComment, + } + if node.Alias != nil { + newNode.Alias = cloneNode(node.Alias) + } + if node.Content != nil { + newNode.Content = make([]*yaml.Node, len(node.Content)) + for i, child := range node.Content { + newNode.Content[i] = cloneNode(child) + } + } + return newNode +} diff --git a/vendor/github.com/pb33f/libopenapi/overlay/errors.go b/vendor/github.com/pb33f/libopenapi/overlay/errors.go new file mode 100644 index 000000000..68f385097 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/overlay/errors.go @@ -0,0 +1,61 @@ +// Copyright 2022-2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package overlay + +import ( + "errors" + "fmt" + + highoverlay "github.com/pb33f/libopenapi/datamodel/high/overlay" +) + +// Warning represents a non-fatal issue encountered during overlay application. +type Warning struct { + Action *highoverlay.Action + Target string + Message string +} + +func (w *Warning) String() string { + return fmt.Sprintf("overlay warning: target '%s': %s", w.Target, w.Message) +} + +// OverlayError represents an error that occurred during an overlay application. +type OverlayError struct { + Action *highoverlay.Action + Cause error +} + +func (e *OverlayError) Error() string { + if e.Action != nil { + return fmt.Sprintf("overlay error at target '%s': %v", e.Action.Target, e.Cause) + } + return fmt.Sprintf("overlay error: %v", e.Cause) +} + +func (e *OverlayError) Unwrap() error { + return e.Cause +} + +// Sentinel errors for overlay operations. +var ( + // Parsing errors + ErrInvalidOverlay = errors.New("invalid overlay document") + ErrMissingOverlayField = errors.New("missing required 'overlay' field") + ErrMissingInfo = errors.New("missing required 'info' field") + ErrMissingActions = errors.New("missing required 'actions' field") + ErrEmptyActions = errors.New("actions array must contain at least one action") + + // JSONPath errors + ErrInvalidJSONPath = errors.New("invalid JSONPath expression") + ErrPrimitiveTarget = errors.New("JSONPath target resolved to primitive/null; must be object or array") + + // Application errors + ErrNoTargetDocument = errors.New("no target document provided") + + // Copy action errors + ErrCopySourceNotFound = errors.New("copy source JSONPath matched zero nodes") + ErrCopySourceMultiple = errors.New("copy source JSONPath must match exactly one node") + ErrCopyTypeMismatch = errors.New("copy source and target must be the same type") +) diff --git a/vendor/github.com/pb33f/libopenapi/overlay/result.go b/vendor/github.com/pb33f/libopenapi/overlay/result.go new file mode 100644 index 000000000..47894337a --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/overlay/result.go @@ -0,0 +1,13 @@ +// Copyright 2022-2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package overlay + +// Result represents the result of applying an overlay to a target document. +type Result struct { + // Bytes is the raw YAML/JSON bytes of the modified document. + Bytes []byte + + // Warnings contains non-fatal issues encountered during application. + Warnings []*Warning +} diff --git a/vendor/github.com/pb33f/libopenapi/overlay/validation.go b/vendor/github.com/pb33f/libopenapi/overlay/validation.go new file mode 100644 index 000000000..155c2ac0d --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/overlay/validation.go @@ -0,0 +1,32 @@ +// Copyright 2022-2025 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package overlay + +import ( + highoverlay "github.com/pb33f/libopenapi/datamodel/high/overlay" + "go.yaml.in/yaml/v4" +) + +// validateOverlay checks that the overlay has all required fields. +func validateOverlay(overlay *highoverlay.Overlay) error { + if overlay.Overlay == "" { + return ErrMissingOverlayField + } + if overlay.Info == nil { + return ErrMissingInfo + } + if len(overlay.Actions) == 0 { + return ErrEmptyActions + } + return nil +} + +// validateTarget checks that a target node is a valid target (object or array). +// Per the Overlay Spec, primitive/null targets are invalid. +func validateTarget(node *yaml.Node) error { + if node.Kind == yaml.ScalarNode { + return ErrPrimitiveTarget + } + return nil +} diff --git a/vendor/github.com/pb33f/libopenapi/utils/nodes.go b/vendor/github.com/pb33f/libopenapi/utils/nodes.go new file mode 100644 index 000000000..17826f333 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/utils/nodes.go @@ -0,0 +1,83 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package utils + +import ( + "go.yaml.in/yaml/v4" +) + +func CreateRefNode(ref string) *yaml.Node { + m := CreateEmptyMapNode() + nodes := make([]*yaml.Node, 2) + nodes[0] = CreateStringNode("$ref") + nodes[1] = CreateStringNode(ref) + nodes[1].Style = yaml.SingleQuotedStyle + m.Content = nodes + return m +} + +func CreateEmptyMapNode() *yaml.Node { + n := &yaml.Node{ + Kind: yaml.MappingNode, + Tag: "!!map", + } + return n +} + +func CreateYamlNode(a any) *yaml.Node { + var n yaml.Node + _ = n.Encode(a) + + return &n +} + +func CreateEmptySequenceNode() *yaml.Node { + n := &yaml.Node{ + Kind: yaml.SequenceNode, + Tag: "!!seq", + } + return n +} + +func CreateStringNode(str string) *yaml.Node { + n := &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: str, + } + return n +} + +func CreateBoolNode(str string) *yaml.Node { + n := &yaml.Node{ + Kind: yaml.ScalarNode, + Value: str, + } + return n +} + +func CreateIntNode(str string) *yaml.Node { + n := &yaml.Node{ + Kind: yaml.ScalarNode, + Value: str, + } + return n +} + +func CreateEmptyScalarNode() *yaml.Node { + n := &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!null", + Value: "", + } + return n +} + +func CreateFloatNode(str string) *yaml.Node { + n := &yaml.Node{ + Kind: yaml.ScalarNode, + Value: str, + } + return n +} diff --git a/vendor/github.com/pb33f/libopenapi/utils/path_part_other.go b/vendor/github.com/pb33f/libopenapi/utils/path_part_other.go new file mode 100644 index 000000000..c043874f9 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/utils/path_part_other.go @@ -0,0 +1,8 @@ +//go:build !windows +// +build !windows + +package utils + +func pathPartEqual(a, b string) bool { + return a == b +} diff --git a/vendor/github.com/pb33f/libopenapi/utils/path_part_windows.go b/vendor/github.com/pb33f/libopenapi/utils/path_part_windows.go new file mode 100644 index 000000000..9d66f57d0 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/utils/path_part_windows.go @@ -0,0 +1,10 @@ +//go:build windows +// +build windows + +package utils + +import "strings" + +func pathPartEqual(a, b string) bool { + return strings.EqualFold(a, b) +} diff --git a/vendor/github.com/pb33f/libopenapi/utils/type_check.go b/vendor/github.com/pb33f/libopenapi/utils/type_check.go new file mode 100644 index 000000000..9cb962f80 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/utils/type_check.go @@ -0,0 +1,49 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package utils + +import "fmt" + +// AreValuesCorrectlyTyped will look through an array of unknown values and check they match +// against the supplied type as a string. The return value is empty if everything is OK, or it +// contains failures in the form of a value as a key and a message as to why it's not valid +func AreValuesCorrectlyTyped(valType string, values interface{}) map[string]string { + var arr []interface{} + if _, ok := values.([]interface{}); !ok { + return nil + } + arr = values.([]interface{}) + + results := make(map[string]string) + for _, v := range arr { + switch v := v.(type) { + case string: + if valType != "string" { + results[v] = fmt.Sprintf("enum value '%v' is a "+ + "string, but it's defined as a '%v'", v, valType) + } + case int64: + if valType != "integer" && valType != "number" { + results[fmt.Sprintf("%v", v)] = fmt.Sprintf("enum value '%v' is a "+ + "integer, but it's defined as a '%v'", v, valType) + } + case int: + if valType != "integer" && valType != "number" { + results[fmt.Sprintf("%v", v)] = fmt.Sprintf("enum value '%v' is a "+ + "integer, but it's defined as a '%v'", v, valType) + } + case float64: + if valType != "number" { + results[fmt.Sprintf("%v", v)] = fmt.Sprintf("enum value '%v' is a "+ + "number, but it's defined as a '%v'", v, valType) + } + case bool: + if valType != "boolean" { + results[fmt.Sprintf("%v", v)] = fmt.Sprintf("enum value '%v' is a "+ + "boolean, but it's defined as a '%v'", v, valType) + } + } + } + return results +} diff --git a/vendor/github.com/pb33f/libopenapi/utils/unwrap_errors.go b/vendor/github.com/pb33f/libopenapi/utils/unwrap_errors.go new file mode 100644 index 000000000..78788b7da --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/utils/unwrap_errors.go @@ -0,0 +1,15 @@ +// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package utils + +func UnwrapErrors(err error) []error { + if err == nil { + return []error{} + } + if uw, ok := err.(interface{ Unwrap() []error }); ok { + return uw.Unwrap() + } else { + return []error{err} + } +} diff --git a/vendor/github.com/pb33f/libopenapi/utils/utils.go b/vendor/github.com/pb33f/libopenapi/utils/utils.go new file mode 100644 index 000000000..b0827e5dc --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/utils/utils.go @@ -0,0 +1,1150 @@ +package utils + +import ( + "crypto/rand" + "encoding/json" + "fmt" + "math/big" + "net/http" + "net/url" + "regexp" + "sort" + "strconv" + "strings" + "sync" + "time" + + "github.com/pb33f/jsonpath/pkg/jsonpath" + jsonpathconfig "github.com/pb33f/jsonpath/pkg/jsonpath/config" + + "go.yaml.in/yaml/v4" +) + +type Case int8 + +const ( + // OpenApi3 is used by all OpenAPI 3+ docs + OpenApi3 = "openapi" + + // OpenApi2 is used by all OpenAPI 2 docs, formerly known as swagger. + OpenApi2 = "swagger" + + // AsyncApi is used by akk AsyncAPI docs, all versions. + AsyncApi = "asyncapi" + + PascalCase Case = iota + CamelCase + ScreamingSnakeCase + SnakeCase + KebabCase + ScreamingKebabCase + RegularCase + UnknownCase +) + +type cachedJSONPath struct { + path *jsonpath.JSONPath + err error +} + +// jsonPathCache stores compiled JSONPath expressions keyed by normalized string. +var jsonPathCache sync.Map + +var jsonPathQuery = func(path *jsonpath.JSONPath, node *yaml.Node) []*yaml.Node { + return path.Query(node) +} + +// getJSONPath returns a cached JSONPath when available, compiling and caching otherwise. +func getJSONPath(rawPath string) (*jsonpath.JSONPath, error) { + cleaned := FixContext(rawPath) + if cached, ok := jsonPathCache.Load(cleaned); ok { + entry := cached.(cachedJSONPath) + return entry.path, entry.err + } + + path, err := jsonpath.NewPath(cleaned, jsonpathconfig.WithPropertyNameExtension()) + jsonPathCache.Store(cleaned, cachedJSONPath{ + path: path, + err: err, + }) + return path, err +} + +// FindNodes will find a node based on JSONPath, it accepts raw yaml/json as input. +func FindNodes(yamlData []byte, jsonPath string) ([]*yaml.Node, error) { + var node yaml.Node + yaml.Unmarshal(yamlData, &node) + + path, err := getJSONPath(jsonPath) + if err != nil { + return nil, err + } + results := path.Query(&node) + return results, nil +} + +// FindLastChildNode will find the last node in a tree, based on a starting node. +// Deprecated: This function is deprecated, use FindLastChildNodeWithLevel instead. +// this has the potential to cause a stack overflow, so use with caution. It will be removed later. +func FindLastChildNode(node *yaml.Node) *yaml.Node { + s := len(node.Content) - 1 + if s < 0 { + s = 0 + } + if len(node.Content) > 0 && len(node.Content[s].Content) > 0 { + return FindLastChildNode(node.Content[s]) + } else { + if len(node.Content) > 0 { + return node.Content[s] + } + return node + } +} + +// FindLastChildNodeWithLevel will find the last node in a tree, based on a starting node. +// Will stop searching after 100 levels, because that's just silly, we probably have a loop. +func FindLastChildNodeWithLevel(node *yaml.Node, level int) *yaml.Node { + if level > 100 { + return node // we've gone too far, give up. + } + s := len(node.Content) - 1 + if s < 0 { + s = 0 + } + if len(node.Content) > 0 && len(node.Content[s].Content) > 0 { + level++ + return FindLastChildNodeWithLevel(node.Content[s], level) + } else { + if len(node.Content) > 0 { + return node.Content[s] + } + return node + } +} + +// BuildPath will construct a JSONPath from a base and an array of strings. +func BuildPath(basePath string, segs []string) string { + path := strings.Join(segs, ".") + + // trim that last period. + if len(path) > 0 && path[len(path)-1] == '.' { + path = path[:len(path)-1] + } + return fmt.Sprintf("%s.%s", basePath, path) +} + +// FindNodesWithoutDeserializing will find a node based on JSONPath, without deserializing from yaml/json +// This function will timeout after 500ms. +func FindNodesWithoutDeserializing(node *yaml.Node, jsonPath string) ([]*yaml.Node, error) { + return FindNodesWithoutDeserializingWithTimeout(node, jsonPath, 500*time.Millisecond) +} + +// FindNodesWithoutDeserializingWithTimeout will find a node based on JSONPath, without deserializing from yaml/json +// This function can be customized with a timeout. +func FindNodesWithoutDeserializingWithTimeout(node *yaml.Node, jsonPath string, timeout time.Duration) ([]*yaml.Node, error) { + path, err := getJSONPath(jsonPath) + if err != nil { + return nil, err + } + + // this can spin out, to lets gatekeep it. + done := make(chan struct{}, 1) + var results []*yaml.Node + timer := time.NewTimer(timeout) + defer timer.Stop() + queryFn := jsonPathQuery + go func() { + results = queryFn(path, node) + done <- struct{}{} + }() + + select { + case <-done: + return results, nil + case <-timer.C: + return nil, fmt.Errorf("node lookup timeout exceeded (%v)", timeout) + } +} + +// ConvertInterfaceIntoStringMap will convert an unknown input into a string map. +func ConvertInterfaceIntoStringMap(context interface{}) map[string]string { + converted := make(map[string]string) + if context != nil { + if v, ok := context.(map[string]interface{}); ok { + for k, n := range v { + if s, okB := n.(string); okB { + converted[k] = s + } + if s, okB := n.(float64); okB { + converted[k] = fmt.Sprint(s) + } + if s, okB := n.(bool); okB { + converted[k] = fmt.Sprint(s) + } + if s, okB := n.(int); okB { + converted[k] = fmt.Sprint(s) + } + if s, okB := n.(int64); okB { + converted[k] = fmt.Sprint(s) + } + } + } + if v, ok := context.(map[string]string); ok { + for k, n := range v { + converted[k] = n + } + } + } + return converted +} + +// ConvertInterfaceToStringArray will convert an unknown input map type into a string array/slice +func ConvertInterfaceToStringArray(raw interface{}) []string { + if vals, ok := raw.(map[string]interface{}); ok { + var s []string + for _, v := range vals { + if g, y := v.([]interface{}); y { + for _, q := range g { + s = append(s, fmt.Sprint(q)) + } + } + } + return s + } + if vals, ok := raw.(map[string][]string); ok { + var s []string + for _, v := range vals { + s = append(s, v...) + } + return s + } + return nil +} + +// ConvertInterfaceArrayToStringArray will convert an unknown interface array type, into a string slice +func ConvertInterfaceArrayToStringArray(raw interface{}) []string { + if vals, ok := raw.([]interface{}); ok { + s := make([]string, len(vals)) + for i, v := range vals { + s[i] = fmt.Sprint(v) + } + return s + } + if vals, ok := raw.([]string); ok { + return vals + } + return nil +} + +// ExtractValueFromInterfaceMap pulls out an unknown value from a map using a string key +func ExtractValueFromInterfaceMap(name string, raw interface{}) interface{} { + if propMap, ok := raw.(map[string]interface{}); ok { + if props, okn := propMap[name].([]interface{}); okn { + return props + } else { + return propMap[name] + } + } + if propMap, ok := raw.(map[string][]string); ok { + return propMap[name] + } + + return nil +} + +// FindFirstKeyNode will locate the first key and value yaml.Node based on a key. +func FindFirstKeyNode(key string, nodes []*yaml.Node, depth int) (keyNode *yaml.Node, valueNode *yaml.Node) { + if depth > 40 { + return nil, nil + } + if nodes != nil && len(nodes) > 0 && nodes[0].Tag == "!!merge" { + nodes = NodeAlias(nodes[1]).Content + } + for i, v := range nodes { + if key != "" && key == v.Value { + if i+1 >= len(nodes) { + return v, NodeAlias(nodes[i]) // this is the node we need. + } + return NodeAlias(v), NodeAlias(nodes[i+1]) // next node is what we need. + } + if len(v.Content) > 0 { + depth++ + x, y := FindFirstKeyNode(key, v.Content, depth) + if x != nil && y != nil { + return NodeAlias(x), NodeAlias(y) + } + } + } + return nil, nil +} + +// KeyNodeResult is a result from a KeyNodeSearch performed by the FindAllKeyNodesWithPath +type KeyNodeResult struct { + KeyNode *yaml.Node + ValueNode *yaml.Node + Parent *yaml.Node + Path []yaml.Node +} + +// KeyNodeSearch keeps a track of everything we have found on our adventure down the trees. +type KeyNodeSearch struct { + Key string + Ignore []string + Results []*KeyNodeResult + AllowExtensions bool +} + +// FindKeyNodeTop is a non-recursive search of top level nodes for a key, will not look at content. +// Returns the key and value +func FindKeyNodeTop(key string, nodes []*yaml.Node) (keyNode *yaml.Node, valueNode *yaml.Node) { + if nodes != nil && len(nodes) > 0 && nodes[0].Tag == "!!merge" { + nodes = NodeAlias(nodes[1]).Content + } + for i := 0; i < len(nodes); i++ { + v := nodes[i] + if i%2 != 0 { + continue + } + if strings.EqualFold(key, v.Value) { + if i+1 >= len(nodes) { + return NodeAlias(v), NodeAlias(nodes[i]) + } + return NodeAlias(v), NodeAlias(nodes[i+1]) // next node is what we need. + } + } + return nil, nil +} + +// FindKeyNode is a non-recursive search of a *yaml.Node Content for a child node with a key. +// Returns the key and value +func FindKeyNode(key string, nodes []*yaml.Node) (keyNode *yaml.Node, valueNode *yaml.Node) { + if nodes != nil && len(nodes) > 0 && nodes[0].Tag == "!!merge" { + nodes = NodeAlias(nodes[1]).Content + } + for i, v := range nodes { + if i%2 == 0 && key == v.Value { + if len(nodes) <= i+1 { + return NodeAlias(v), NodeAlias(nodes[i]) + } + return NodeAlias(v), NodeAlias(nodes[i+1]) // next node is what we need. + } + for x, j := range v.Content { + if key == j.Value { + if IsNodeMap(v) { + if x+1 == len(v.Content) { + return NodeAlias(v), NodeAlias(v.Content[x]) + } + return NodeAlias(v), NodeAlias(v.Content[x+1]) // next node is what we need. + + } + if IsNodeArray(v) { + return NodeAlias(v), NodeAlias(v.Content[x]) + } + } + } + } + return nil, nil +} + +// FindKeyNodeFull is an overloaded version of FindKeyNode. This version however returns keys, labels and values. +// generally different things are required from different node trees, so depending on what this function is looking at +// it will return different things. +func FindKeyNodeFull(key string, nodes []*yaml.Node) (keyNode *yaml.Node, labelNode *yaml.Node, valueNode *yaml.Node) { + if nodes != nil && len(nodes) > 0 && nodes[0].Tag == "!!merge" { + nodes = NodeAlias(nodes[1]).Content + } + for i := 0; i < len(nodes); i++ { + if i%2 == 0 && key == nodes[i].Value { + if i+1 >= len(nodes) { + return NodeAlias(nodes[i]), NodeAlias(nodes[i]), NodeAlias(nodes[i]) + } + return NodeAlias(nodes[i]), NodeAlias(nodes[i]), NodeAlias(nodes[i+1]) // next node is what we need. + } + } + for _, v := range nodes { + for x := 0; x < len(v.Content); x++ { + r := v.Content[x] + if x%2 == 0 { + if r.Tag == "!!merge" { + if len(nodes) > x+1 { + v = NodeAlias(nodes[x+1]) + } + } + } + + if len(v.Content) > 0 && key == v.Content[x].Value { + if IsNodeMap(v) { + if x+1 == len(v.Content) { + return v, v.Content[x], NodeAlias(v.Content[x]) + } + return NodeAlias(v), NodeAlias(v.Content[x]), NodeAlias(v.Content[x+1]) + } + if IsNodeArray(v) { + return NodeAlias(v), NodeAlias(v.Content[x]), NodeAlias(v.Content[x]) + } + } + } + } + return nil, nil, nil +} + +// FindKeyNodeFullTop is an overloaded version of FindKeyNodeFull. This version only looks at the top +// level of the node and not the children. +func FindKeyNodeFullTop(key string, nodes []*yaml.Node) (keyNode *yaml.Node, labelNode *yaml.Node, valueNode *yaml.Node) { + if nodes != nil && len(nodes) >= 0 && nodes[0].Tag == "!!merge" { + nodes = NodeAlias(nodes[1]).Content + } + for i := 0; i < len(nodes); i++ { + v := nodes[i] + if i%2 == 0 { + if v.Tag == "!!merge" { + if len(nodes) > i+1 { + v = NodeAlias(nodes[i+1]) + if len(v.Content) > 0 { + nodes = append(nodes, v.Content...) + } + } + } + } + if i%2 != 0 { + continue + } + if i%2 == 0 && key == nodes[i].Value { + return NodeAlias(nodes[i]), NodeAlias(nodes[i]), NodeAlias(nodes[i+1]) // next node is what we need. + } + } + return nil, nil, nil +} + +type ExtensionNode struct { + Key *yaml.Node + Value *yaml.Node +} + +func FindExtensionNodes(nodes []*yaml.Node) []*ExtensionNode { + var extensions []*ExtensionNode + for i, v := range nodes { + if i%2 == 0 && strings.HasPrefix(v.Value, "x-") { + if i+1 < len(nodes) { + extensions = append( + extensions, &ExtensionNode{ + Key: v, + Value: NodeAlias(nodes[i+1]), + }, + ) + } + } + } + return extensions +} + +var ( + ObjectLabel = "object" + IntegerLabel = "integer" + NumberLabel = "number" + StringLabel = "string" + BinaryLabel = "binary" + ArrayLabel = "array" + BooleanLabel = "boolean" + SchemaSource = "https://json-schema.org/draft/2020-12/schema" + SchemaId = "https://pb33f.io/openapi-changes/schema" +) + +func MakeTagReadable(node *yaml.Node) string { + switch node.Tag { + case "!!map": + return ObjectLabel + case "!!seq": + return ArrayLabel + case "!!str": + return StringLabel + case "!!int": + return IntegerLabel + case "!!float": + return NumberLabel + case "!!bool": + return BooleanLabel + } + return "unknown" +} + +// IsNodeMap checks if the node is a map type +func IsNodeMap(node *yaml.Node) bool { + if node == nil { + return false + } + n := NodeAlias(node) + if n.Kind == yaml.MappingNode { + return true + } + return n.Tag == "!!map" +} + +// IsNodeNull checks if the node is a null type +func IsNodeNull(node *yaml.Node) bool { + if node == nil { + return true + } + n := NodeAlias(node) + return n.Tag == "!!null" +} + +// IsNodeAlias checks if the node is an alias, and lifts out the anchor +func IsNodeAlias(node *yaml.Node) (*yaml.Node, bool) { + if node == nil { + return nil, false + } + if node.Kind == yaml.AliasNode { + node = node.Alias + return node, true + } + return node, false +} + +func NodeMerge(nodes []*yaml.Node) *yaml.Node { + for i, v := range nodes { + if v.Tag == "!!merge" { + if i+1 < len(nodes) { + return NodeAlias(nodes[i+1]) + } + } + } + if len(nodes) > 0 { + return NodeAlias(nodes[0]) + } + return nil +} + +// NodeAlias checks if the node is an alias, and lifts out the anchor +func NodeAlias(node *yaml.Node) *yaml.Node { + if node == nil { + return nil + } + + content := node.Content + if node.Kind == yaml.AliasNode { + content = node.Alias.Content + } + + for i, n := range content { + if i%2 == 0 { + if n.Tag == "!!merge" { + g := NodeMerge(content[i+1:]) + if g != nil { + node = g + } + } + } + } + + if node.Kind == yaml.AliasNode { + node = node.Alias + return node + } + return node +} + +// IsNodePolyMorphic will return true if the node contains polymorphic keys. +func IsNodePolyMorphic(node *yaml.Node) bool { + n := NodeAlias(node) + for i, v := range n.Content { + if i%2 == 0 { + if v.Value == "anyOf" || v.Value == "oneOf" || v.Value == "allOf" { + return true + } + } + } + return false +} + +// IsNodeArray checks if a node is an array type +func IsNodeArray(node *yaml.Node) bool { + if node == nil { + return false + } + n := NodeAlias(node) + if n.Tag == "!!seq" { + return true + } + return n.Kind == yaml.SequenceNode +} + +// IsNodeStringValue checks if a node is a string value +func IsNodeStringValue(node *yaml.Node) bool { + if node == nil { + return false + } + n := NodeAlias(node) + return n.Tag == "!!str" +} + +// IsNodeIntValue will check if a node is an int value +func IsNodeIntValue(node *yaml.Node) bool { + if node == nil { + return false + } + n := NodeAlias(node) + return n.Tag == "!!int" +} + +// IsNodeFloatValue will check is a node is a float value. +func IsNodeFloatValue(node *yaml.Node) bool { + if node == nil { + return false + } + n := NodeAlias(node) + return n.Tag == "!!float" +} + +// IsNodeNumberValue will check if a node can be parsed as a float value. +func IsNodeNumberValue(node *yaml.Node) bool { + if node == nil { + return false + } + return IsNodeIntValue(node) || IsNodeFloatValue(node) +} + +// IsNodeBoolValue will check is a node is a bool +func IsNodeBoolValue(node *yaml.Node) bool { + if node == nil { + return false + } + n := NodeAlias(node) + return n.Tag == "!!bool" +} + +func IsNodeRefValue(node *yaml.Node) (bool, *yaml.Node, string) { + if node == nil { + return false, nil, "" + } + n := NodeAlias(node) + for i, r := range n.Content { + if i%2 == 0 { + if r.Value == "$ref" { + if i+1 < len(n.Content) { + return true, r, n.Content[i+1].Value + } + } + } + } + return false, nil, "" +} + +// GetRefValueNode returns the $ref value node from a mapping node. +// Unlike IsNodeRefValue which returns the string value, this returns the actual node +// so it can be modified in place. This correctly handles OA 3.1 sibling properties +// where $ref may not be at position 0. +func GetRefValueNode(node *yaml.Node) *yaml.Node { + if node == nil { + return nil + } + n := NodeAlias(node) + for i, r := range n.Content { + if i%2 == 0 && r.Value == "$ref" { + if i+1 < len(n.Content) { + return n.Content[i+1] + } + } + } + return nil +} + +// FixContext will clean up a JSONpath string to be correctly traversable. +func FixContext(context string) string { + tokens := strings.Split(context, ".") + cleaned := []string{} + + for i, t := range tokens { + if v, err := strconv.Atoi(t); err == nil { + if v < 200 { // codes start here + if cleaned[i-1] != "" { + cleaned[i-1] += fmt.Sprintf("[%v]", t) + } + } else { + cleaned = append(cleaned, t) + } + continue + } + cleaned = append(cleaned, strings.ReplaceAll(t, "(root)", "$")) + } + + return strings.Join(cleaned, ".") +} + +// IsJSON will tell you if a string is JSON or not. +func IsJSON(testString string) bool { + if testString == "" { + return false + } + runes := []rune(strings.TrimSpace(testString)) + if runes[0] == '{' && runes[len(runes)-1] == '}' { + return true + } + return false +} + +// IsYAML will tell you if a string is YAML or not. +var ( + yamlKeyValuePattern = regexp.MustCompile(`(?m)^\s*[a-zA-Z0-9_-]+\s*:\s*.+$`) + yamlListPattern = regexp.MustCompile(`(?m)^\s*-\s+.+$`) + yamlHeaderPattern = regexp.MustCompile(`(?m)^---\s*$`) +) + +func IsYAML(testString string) bool { + if testString == "" { + return false + } + if IsJSON(testString) { + return false + } + + // Trim leading and trailing whitespace + s := strings.TrimSpace(testString) + + // Fast checks for common YAML features + if strings.Contains(s, ": ") || strings.Contains(s, "- ") || strings.Contains(s, "\n- ") { + return true + } + + // Regular expressions for more robust detection + if yamlKeyValuePattern.MatchString(s) || yamlListPattern.MatchString(s) || yamlHeaderPattern.MatchString(s) { + return true + } + + return false +} + +// ConvertYAMLtoJSON will do exactly what you think it will. It will deserialize YAML into serialized JSON. +func ConvertYAMLtoJSON(yamlData []byte) ([]byte, error) { + var decodedYaml map[string]interface{} + err := yaml.Unmarshal(yamlData, &decodedYaml) + if err != nil { + return nil, err + } + // if the data can be decoded, it can be encoded (that's my view anyway). no need for an error check. + jsonData, _ := json.Marshal(decodedYaml) + return jsonData, nil +} + +// IsHttpVerb will check if an operation is valid or not. +func IsHttpVerb(verb string) bool { + verbs := []string{"get", "post", "put", "patch", "delete", "options", "trace", "head"} + for _, v := range verbs { + if verb == v { + return true + } + } + return false +} + +// define bracket name expression +var ( + bracketNameExp = regexp.MustCompile(`^(\w+)\['?([\w/]+)'?]$`) +) + +// isPathChar checks if a string is valid for JSONPath dot notation. +// returns true only if the string contains only alphanumeric, underscore, or backslash characters +// and does not start with a digit (unless it's a pure integer, which is handled separately). +// jsonPath requires bracket notation for property names starting with digits like "403_permission_denied". +// this is an optimized replacement for the pathCharExp regex. +func isPathChar(s string) bool { + if len(s) == 0 { + return false + } + + firstChar := s[0] + startsWithDigit := firstChar >= '0' && firstChar <= '9' + allDigits := startsWithDigit + + // single pass: validate characters and track if all are digits + for _, r := range s { + if !((r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '_' || r == '\\') { + return false + } + if allDigits && (r < '0' || r > '9') { + allDigits = false + } + } + + // if starts with digit but not pure integer, requires bracket notation + // property names like "403_permission_denied" must use bracket notation + if startsWithDigit && !allDigits { + return false + } + + return true +} + +func appendSegment(sb *strings.Builder, segs []string, cleaned []string, i int, wrapInQuotes bool) { + sb.Reset() + if wrapInQuotes { + sb.WriteString("['") + sb.WriteString(segs[i]) + sb.WriteString("']") + } else { + sb.WriteString("[") + sb.WriteString(segs[i]) + sb.WriteString("]") + } + c := sb.String() + sb.Reset() + sb.WriteString(cleaned[len(cleaned)-1]) + sb.WriteString(c) + cleaned[len(cleaned)-1] = sb.String() +} + +// appendSegmentOptimized uses strings.Builder more efficiently to avoid allocations +func appendSegmentOptimized(segs []string, cleaned []string, i int, wrapInQuotes bool) { + var builder strings.Builder + if wrapInQuotes { + builder.Grow(len(cleaned[len(cleaned)-1]) + len(segs[i]) + 4) // existing + [''] + segment + builder.WriteString(cleaned[len(cleaned)-1]) + builder.WriteString("['") + builder.WriteString(segs[i]) + builder.WriteString("']") + } else { + builder.Grow(len(cleaned[len(cleaned)-1]) + len(segs[i]) + 2) // existing + [] + segment + builder.WriteString(cleaned[len(cleaned)-1]) + builder.WriteByte('[') + builder.WriteString(segs[i]) + builder.WriteByte(']') + } + cleaned[len(cleaned)-1] = builder.String() +} + +// ConvertComponentIdIntoFriendlyPathSearch will convert a JSON Path into a friendly path search string. +// the friendliness comes from it being suitable for use with any JSON Path parser. +// +// This function was re-written in v0.18.0 in order to fix a number of performance issues with the original +// implementation. Allocations were high and this function is used a lot, this new implementation is much +// lighter on string allocations by using a string builder. +func ConvertComponentIdIntoFriendlyPathSearch(id string) (string, string) { + if id == "" || id == "#/" { + return "", "$." + } + segs := strings.Split(id, "/") + name, _ := url.QueryUnescape(strings.ReplaceAll(segs[len(segs)-1], "~1", "/")) + + // Pre-allocate with estimated capacity + estimatedCap := len(segs) + (len(segs) / 2) + cleaned := make([]string, 0, estimatedCap) + + // check for strange spaces, chars and if found, wrap them up, clean them and create a new cleaned path. + for i := range segs { + if segs[i] == "" { + continue + } + if !isPathChar(segs[i]) { + + segs[i], _ = url.QueryUnescape(strings.ReplaceAll(segs[i], "~1", "/")) + + // Use string builder for bracket wrapping + var bracketBuilder strings.Builder + bracketBuilder.Grow(len(segs[i]) + 4) + bracketBuilder.WriteString("['") + bracketBuilder.WriteString(segs[i]) + bracketBuilder.WriteString("']") + segs[i] = bracketBuilder.String() + + if len(cleaned) > 0 && i < len(segs)-1 { + // Use string builder for concatenation with last cleaned element + var concatBuilder strings.Builder + concatBuilder.Grow(len(cleaned[len(cleaned)-1]) + len(segs[i])) + concatBuilder.WriteString(cleaned[len(cleaned)-1]) + concatBuilder.WriteString(segs[i]) + cleaned[len(cleaned)-1] = concatBuilder.String() + continue + } else { + if i > 0 && i < len(segs)-1 { + cleaned = append(cleaned, segs[i]) + continue + } + if i == len(segs)-1 { + l := len(cleaned) + if l > 0 { + // Use string builder for concatenation + var endBuilder strings.Builder + endBuilder.Grow(len(cleaned[l-1]) + len(segs[i])) + endBuilder.WriteString(cleaned[l-1]) + endBuilder.WriteString(segs[i]) + cleaned[l-1] = endBuilder.String() + } else { + cleaned = append(cleaned, segs[i]) + } + } + } + } else { + + // strip out any backslashes + if strings.Contains(id, "#") && strings.Contains(segs[i], `\`) { + segs[i] = strings.ReplaceAll(segs[i], `\`, "") + cleaned = append(cleaned, segs[i]) + continue + } + + intVal, err := strconv.Atoi(segs[i]) + if err == nil { + if intVal <= 99 { + if len(cleaned) > 0 { + appendSegmentOptimized(segs, cleaned, i, false) + } + } else { + if len(cleaned) > 0 { + appendSegmentOptimized(segs, cleaned, i, true) + } + } + continue + } + + // if we have a plural parent, wrap it in quotes. + if i > 0 && segs[i-1] != "" && segs[i-1][len(segs[i-1])-1] == 's' { + if i == 2 { // ignore first segment. + cleaned = append(cleaned, segs[i]) + continue + } + + // Use string builder for plural wrapping + var pluralBuilder strings.Builder + pluralBuilder.Grow(len(cleaned[len(cleaned)-1]) + len(segs[i]) + 4) + pluralBuilder.WriteString(cleaned[len(cleaned)-1]) + pluralBuilder.WriteString("['") + pluralBuilder.WriteString(segs[i]) + pluralBuilder.WriteString("']") + cleaned[len(cleaned)-1] = pluralBuilder.String() + continue + } + + cleaned = append(cleaned, segs[i]) + } + } + + // use single string builder for final assembly. + // note: we do NOT replace # with $ here. the leading # from JSON Pointer notation + // (e.g., "#/components/...") is already stripped when we split by "/", and any # + // characters within component names (e.g., "async_search.submit#wait_for_completion_timeout") + // should be preserved literally in the JSONPath query. see issue #485. + var finalBuilder strings.Builder + if len(cleaned) > 1 { + // Estimate final size + totalLen := 0 + for _, seg := range cleaned { + totalLen += len(seg) + } + finalBuilder.Grow(totalLen + len(cleaned) + 5) // segments + dots + $ + potential extra . + + finalBuilder.WriteByte('$') + for i, segment := range cleaned { + if i > 0 { + finalBuilder.WriteByte('.') + } + finalBuilder.WriteString(segment) + } + } else { + // Handle single segment case + if len(cleaned) == 1 { + finalBuilder.Grow(len(cleaned[0]) + 5) + finalBuilder.WriteString("$.") + finalBuilder.WriteString(cleaned[0]) + } else { + finalBuilder.WriteString("$.") + } + } + + replaced := finalBuilder.String() + + // Ensure proper format + if len(replaced) > 0 { + if len(replaced) > 1 && replaced[1] != '.' { + // Insert period after $ + var dotBuilder strings.Builder + dotBuilder.Grow(len(replaced) + 1) + dotBuilder.WriteByte(replaced[0]) // $ + dotBuilder.WriteByte('.') // . + dotBuilder.WriteString(replaced[1:]) + replaced = dotBuilder.String() + } + } + return name, replaced +} + +// ConvertComponentIdIntoPath will convert a JSON Path into a component ID +// TODO: This function is named incorrectly and should be changed to reflect the correct function +func ConvertComponentIdIntoPath(id string) (string, string) { + segs := strings.Split(id, ".") + name, _ := url.QueryUnescape(strings.ReplaceAll(segs[len(segs)-1], "~1", "/")) + var cleaned []string + + // check for strange spaces, chars and if found, wrap them up, clean them and create a new cleaned path. + for i := range segs { + brackets := bracketNameExp.FindStringSubmatch(segs[i]) + if i == 0 { + if segs[i] == "$" { + cleaned = append(cleaned, "#") + continue + } + } + + // if there are brackets, shift the path to encapsulate them correctly. + if len(brackets) > 0 { + + // bracketNameExp/. + key := bracketNameExp.ReplaceAllString(segs[i], "$1") + val := strings.ReplaceAll(bracketNameExp.ReplaceAllString(segs[i], "$2"), "/", "~1") + cleaned = append( + cleaned[:i], + append([]string{fmt.Sprintf("%s/%s", key, val)}, cleaned[i:]...)..., + ) + continue + } + cleaned = append(cleaned, segs[i]) + } + + if cleaned[0] != "#" { + cleaned = append(cleaned[:0], append([]string{"#"}, cleaned[0:]...)...) + } + replaced := strings.ReplaceAll(strings.Join(cleaned, "/"), "$", "#") + + return name, replaced +} + +func RenderCodeSnippet(startNode *yaml.Node, specData []string, before, after int) string { + buf := new(strings.Builder) + + startLine := startNode.Line - before + endLine := startNode.Line + after + + if startLine < 0 { + startLine = 0 + } + + if endLine >= len(specData) { + endLine = len(specData) - 1 + } + + delta := endLine - startLine + + for i := 0; i < delta; i++ { + l := startLine + i + if l < len(specData) { + line := specData[l] + buf.WriteString(fmt.Sprintf("%s\n", line)) + } + } + + return buf.String() +} + +func DetectCase(input string) Case { + trim := strings.TrimSpace(input) + if trim == "" { + return UnknownCase + } + + pascalCase := regexp.MustCompile("^[A-Z][a-z]+(?:[A-Z][a-z]+)*$") + camelCase := regexp.MustCompile("^[a-z]+(?:[A-Z][a-z]+)*$") + screamingSnakeCase := regexp.MustCompile("^[A-Z]+(_[A-Z]+)*$") + snakeCase := regexp.MustCompile("^[a-z]+(_[a-z]+)*$") + kebabCase := regexp.MustCompile("^[a-z]+(-[a-z]+)*$") + screamingKebabCase := regexp.MustCompile("^[A-Z]+(-[A-Z]+)*$") + if pascalCase.MatchString(trim) { + return PascalCase + } + if camelCase.MatchString(trim) { + return CamelCase + } + if screamingSnakeCase.MatchString(trim) { + return ScreamingSnakeCase + } + if snakeCase.MatchString(trim) { + return SnakeCase + } + if kebabCase.MatchString(trim) { + return KebabCase + } + if screamingKebabCase.MatchString(trim) { + return ScreamingKebabCase + } + return RegularCase +} + +// CheckEnumForDuplicates will check an array of nodes to check if there are any duplicate values. +func CheckEnumForDuplicates(seq []*yaml.Node) []*yaml.Node { + var res []*yaml.Node + seen := make(map[string]*yaml.Node) + + for _, enum := range seq { + if seen[enum.Value] != nil { + res = append(res, enum) + continue + } + seen[enum.Value] = enum + } + return res +} + +var whitespaceExp = regexp.MustCompile(`\n( +)`) + +// DetermineWhitespaceLength will determine the length of the whitespace for a JSON or YAML file. +func DetermineWhitespaceLength(input string) int { + whiteSpace := whitespaceExp.FindAllStringSubmatch(input, -1) + var filtered []string + for i := range whiteSpace { + filtered = append(filtered, whiteSpace[i][1]) + } + sort.Strings(filtered) + if len(filtered) > 0 { + return len(filtered[0]) + } else { + return 0 + } +} + +// CheckForMergeNodes will check the top level of the schema for merge nodes. If any are found, then the merged nodes +// will be appended to the end of the rest of the nodes in the schema. +// Note: this is a destructive operation, so the in-memory node structure will be modified +func CheckForMergeNodes(node *yaml.Node) { + if node == nil { + return + } + total := len(node.Content) + for i := 0; i < total; i++ { + mn := node.Content[i] + if i%2 == 0 { + if mn.Tag == "!!merge" { + an := node.Content[i+1].Alias + if an != nil { + node.Content = append(node.Content, an.Content...) // append the merged nodes + total = len(node.Content) + i += 2 + } + } + } + } +} + +// IsExternalRef returns true if the reference string points to an external resource +// (i.e., it is non-empty and does not start with '#'). +func IsExternalRef(ref string) bool { + return ref != "" && !strings.HasPrefix(ref, "#") +} + +type RemoteURLHandler = func(url string) (*http.Response, error) + +// GenerateAlphanumericString creates a random alphanumeric string of length n +// using characters matching the regex [0-9A-Za-z] +func GenerateAlphanumericString(n int) string { + const charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + charsetLength := big.NewInt(int64(len(charset))) + + result := make([]byte, n) + + for i := 0; i < n; i++ { + // Generate a cryptographically secure random number + randomIndex, _ := rand.Int(rand.Reader, charsetLength) + + // Use the random number as an index into the charset + result[i] = charset[randomIndex.Int64()] + } + + return string(result) +} diff --git a/vendor/github.com/pb33f/libopenapi/utils/windows_drive.go b/vendor/github.com/pb33f/libopenapi/utils/windows_drive.go new file mode 100644 index 000000000..240a9540a --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/utils/windows_drive.go @@ -0,0 +1,112 @@ +package utils + +import ( + "path/filepath" + "strings" +) + +func ReplaceWindowsDriveWithLinuxPath(path string) string { + if len(path) > 1 && path[1] == ':' { + path = strings.ReplaceAll(path, "\\", "/") + return path[2:] + } + return strings.ReplaceAll(path, "\\", "/") +} + +// CheckPathOverlap joins pathA and pathB while avoiding duplicated overlapping segments. +// It tolerates mixed separators in the inputs and uses OS-specific path comparison rules. +// It also handles leading ".." segments in pathB by first resolving them against pathA +// before checking for overlap, preventing path doubling issues. +func CheckPathOverlap(pathA, pathB, sep string) string { + if sep == "" { + sep = string(filepath.Separator) + } + if pathB != "" { + if pathB[0] == '/' || pathB[0] == '\\' || (len(pathB) > 1 && pathB[1] == ':') { + return filepath.Clean(pathB) + } + } + + // Split on both separators so mixed-path inputs are handled safely. + split := func(p string) []string { + return strings.FieldsFunc(p, func(r rune) bool { + return r == '/' || r == '\\' + }) + } + + // preserve path prefix (absolute path marker or Windows drive letter) + prefix := "" + if len(pathA) > 0 && (pathA[0] == '/' || pathA[0] == '\\') { + prefix = string(pathA[0]) + } + if len(pathA) > 2 && pathA[1] == ':' { + prefix = pathA[:2] + sep + } + + aParts := split(pathA) + bParts := split(pathB) + + // If pathA has a Windows drive letter, drop it from aParts + // to avoid duplicating it when rebuilding with prefix. + if len(pathA) > 2 && pathA[1] == ':' { + if len(aParts) > 0 && strings.EqualFold(aParts[0], pathA[:2]) { + aParts = aParts[1:] + } + } + + // Handle leading ".." segments in pathB by going up pathA. + // This prevents path doubling when resolving refs like "../components/schemas/X.yaml" + // from a base path like "/path/to/components/schemas". + dotDotResolved := false + for len(bParts) > 0 && bParts[0] == ".." && len(aParts) > 0 { + aParts = aParts[:len(aParts)-1] // Go up one directory + bParts = bParts[1:] // Remove the ".." + dotDotResolved = true + } + + // rebuild pathA if we resolved any ".." segments + if dotDotResolved { + if len(aParts) == 0 { + pathA = prefix + } else { + // Use the OS separator to join parts, then add a prefix + pathA = prefix + strings.Join(aParts, string(filepath.Separator)) + } + } + + // find the longest suffix of aParts that matches the prefix of bParts. + overlap := 0 + maxCheck := len(aParts) + if len(bParts) < maxCheck { + maxCheck = len(bParts) + } + for i := maxCheck; i > 0; i-- { + start := len(aParts) - i + match := true + for j := 0; j < i; j++ { + if !pathPartEqual(aParts[start+j], bParts[j]) { + match = false + break + } + } + if match { + overlap = i + break + } + } + + if overlap > 0 { + bParts = bParts[overlap:] + } + + // sep is used to build the tail; filepath.Join normalizes separators for the OS. + tail := strings.Join(bParts, sep) + if tail == "" { + if pathA == "" { + return "" + } + return filepath.Clean(pathA) + } + + return filepath.Join(pathA, tail) +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/changed.go b/vendor/github.com/pb33f/libopenapi/what-changed/changed.go new file mode 100644 index 000000000..b9186797b --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/changed.go @@ -0,0 +1,24 @@ +// Copyright 2023-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// https://pb33f.io + +package what_changed + +import "github.com/pb33f/libopenapi/what-changed/model" + +// Changed represents an object that was changed +type Changed interface { + // GetAllChanges returns all top level changes made to properties in this object + GetAllChanges() []*model.Change + + // TotalChanges returns a count of all changes made on the object, including all children + TotalChanges() int + + // TotalBreakingChanges returns a count of all breaking changes on this object + TotalBreakingChanges() int + + // GetPropertyChanges + GetPropertyChanges() []*model.Change + + // PropertiesOnly will set a change object to only render properties and not the whole timeline. + PropertiesOnly() +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/breaking_rules.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/breaking_rules.go new file mode 100644 index 000000000..642a07d22 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/breaking_rules.go @@ -0,0 +1,451 @@ +// Copyright 2022-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "reflect" + "strings" + "sync" +) + +// ResetDefaultBreakingRules resets the cached default rules. This is primarily +// intended for testing scenarios where the cache needs to be cleared. +func ResetDefaultBreakingRules() { + defaultRulesOnce = sync.Once{} + defaultRulesCache = nil +} + +// SetActiveBreakingRulesConfig sets the active breaking rules configuration used +// by comparison functions. Pass nil to reset to defaults. +func SetActiveBreakingRulesConfig(config *BreakingRulesConfig) { + activeConfigMu.Lock() + defer activeConfigMu.Unlock() + activeConfig = config +} + +// GetActiveBreakingRulesConfig returns the currently active breaking rules config. +// If no custom config has been set, returns the default rules. +func GetActiveBreakingRulesConfig() *BreakingRulesConfig { + activeConfigMu.RLock() + defer activeConfigMu.RUnlock() + if activeConfig != nil { + return activeConfig + } + return GenerateDefaultBreakingRules() +} + +// ResetActiveBreakingRulesConfig clears any custom config and reverts to defaults. +func ResetActiveBreakingRulesConfig() { + activeConfigMu.Lock() + defer activeConfigMu.Unlock() + activeConfig = nil +} + +// GenerateDefaultBreakingRules returns the default breaking change rules for OpenAPI 3.x. +// These rules match the currently hardcoded behavior in the comparison functions. +// The returned config is cached and reused for performance. +func GenerateDefaultBreakingRules() *BreakingRulesConfig { + defaultRulesOnce.Do(func() { + defaultRulesCache = buildDefaultRules() + }) + return defaultRulesCache +} + +// IsBreakingChange is a package-level helper that looks up whether a change is breaking +// using the currently active configuration. +func IsBreakingChange(component, property, changeType string) bool { + return GetActiveBreakingRulesConfig().IsBreaking(component, property, changeType) +} + +// BreakingAdded returns whether adding the specified property is a breaking change. +func BreakingAdded(component, property string) bool { + return IsBreakingChange(component, property, ChangeTypeAdded) +} + +// BreakingModified returns whether modifying the specified property is a breaking change. +func BreakingModified(component, property string) bool { + return IsBreakingChange(component, property, ChangeTypeModified) +} + +// BreakingRemoved returns whether removing the specified property is a breaking change. +func BreakingRemoved(component, property string) bool { + return IsBreakingChange(component, property, ChangeTypeRemoved) +} + +func boolPtr(b bool) *bool { + return &b +} + +func rule(added, modified, removed bool) *BreakingChangeRule { + return &BreakingChangeRule{ + Added: boolPtr(added), + Modified: boolPtr(modified), + Removed: boolPtr(removed), + } +} + +// jsonTagName extracts the field name from a JSON struct tag. +func jsonTagName(field reflect.StructField) string { + tag := field.Tag.Get("json") + if tag == "" || tag == "-" { + return "" + } + if idx := strings.Index(tag, ","); idx != -1 { + return tag[:idx] + } + return tag +} + +// mergeRulesStruct merges all *BreakingChangeRule fields from override into base. +func mergeRulesStruct(base, override reflect.Value) { + rulesType := base.Type() + for i := 0; i < rulesType.NumField(); i++ { + field := rulesType.Field(i) + if field.Type != ruleType { + continue + } + bField := base.Field(i) + oField := override.Field(i) + if bField.CanSet() { + bField.Set(reflect.ValueOf(mergeRule( + bField.Interface().(*BreakingChangeRule), + oField.Interface().(*BreakingChangeRule), + ))) + } + } +} + +// addRulesToCache adds all *BreakingChangeRule fields from a rule struct to the cache. +func addRulesToCache(cache map[string]*BreakingChangeRule, compName string, rulesVal reflect.Value) { + rulesType := rulesVal.Type() + for i := 0; i < rulesType.NumField(); i++ { + field := rulesType.Field(i) + fVal := rulesVal.Field(i) + + propName := jsonTagName(field) + if propName == "" || field.Type != ruleType { + continue + } + cache[compName+"."+propName] = fVal.Interface().(*BreakingChangeRule) + } +} + +// mergeRule merges an override rule into a base rule. +// nil values in override are ignored, non-nil values replace the base. +func mergeRule(base, override *BreakingChangeRule) *BreakingChangeRule { + if override == nil { + return base + } + if base == nil { + return override + } + result := &BreakingChangeRule{ + Added: base.Added, + Modified: base.Modified, + Removed: base.Removed, + } + if override.Added != nil { + result.Added = override.Added + } + if override.Modified != nil { + result.Modified = override.Modified + } + if override.Removed != nil { + result.Removed = override.Removed + } + return result +} + +// buildDefaultRules creates the actual default rules configuration. +func buildDefaultRules() *BreakingRulesConfig { + return &BreakingRulesConfig{ + OpenAPI: rule(true, true, true), + JSONSchemaDialect: rule(true, true, true), + Self: rule(true, true, true), + Components: rule(false, false, true), + + Info: &InfoRules{ + Title: rule(false, false, false), + Summary: rule(false, false, false), + Description: rule(false, false, false), + TermsOfService: rule(false, false, false), + Version: rule(false, false, false), + Contact: rule(false, false, false), + License: rule(false, false, false), + }, + + Contact: &ContactRules{ + URL: rule(false, false, false), + Name: rule(false, false, false), + Email: rule(false, false, false), + }, + + License: &LicenseRules{ + URL: rule(false, false, false), + Name: rule(false, false, false), + Identifier: rule(false, false, false), + }, + + Paths: &PathsRules{ + Path: rule(false, false, true), + }, + + PathItem: &PathItemRules{ + Description: rule(false, false, false), + Summary: rule(false, false, false), + Get: rule(false, false, true), + Put: rule(false, false, true), + Post: rule(false, false, true), + Delete: rule(false, false, true), + Options: rule(false, false, true), + Head: rule(false, false, true), + Patch: rule(false, false, true), + Trace: rule(false, false, true), + Query: rule(false, false, true), + AdditionalOperations: rule(false, false, true), + Servers: rule(false, false, true), + Parameters: rule(false, false, true), + }, + + Operation: &OperationRules{ + Tags: rule(false, false, true), + Summary: rule(false, false, false), + Description: rule(false, false, false), + Deprecated: rule(false, false, false), + OperationID: rule(true, true, true), + ExternalDocs: rule(false, false, false), + Responses: rule(false, false, true), + Parameters: rule(false, false, true), + Security: rule(false, false, true), + RequestBody: rule(true, false, true), + Callbacks: rule(false, false, true), + Servers: rule(false, false, true), + }, + + Parameter: &ParameterRules{ + Name: rule(true, true, true), + In: rule(true, true, true), + Description: rule(false, false, false), + Required: rule(false, true, false), + AllowEmptyValue: rule(true, true, true), + Style: rule(false, false, false), + AllowReserved: rule(true, true, true), + Explode: rule(false, false, false), + Deprecated: rule(false, false, false), + Example: rule(false, false, false), + Schema: rule(true, false, true), + Items: rule(true, false, true), + }, + + RequestBody: &RequestBodyRules{ + Description: rule(false, false, false), + Required: rule(true, true, true), + }, + + Responses: &ResponsesRules{ + Default: rule(false, false, true), + Codes: rule(false, false, true), + }, + + Response: &ResponseRules{ + Description: rule(false, false, false), + Summary: rule(false, false, false), + Schema: rule(true, false, true), + Examples: rule(false, false, false), + }, + + MediaType: &MediaTypeRules{ + Example: rule(false, false, false), + Schema: rule(true, false, true), + ItemSchema: rule(true, false, true), + ItemEncoding: rule(false, false, true), + }, + + Encoding: &EncodingRules{ + ContentType: rule(true, true, true), + Style: rule(true, true, true), + Explode: rule(true, true, true), + AllowReserved: rule(false, false, false), + }, + + Header: &HeaderRules{ + Description: rule(false, false, false), + Style: rule(false, false, false), + AllowReserved: rule(false, false, false), + AllowEmptyValue: rule(true, true, true), + Explode: rule(false, false, false), + Example: rule(false, false, false), + Deprecated: rule(false, false, false), + Required: rule(true, true, true), + Schema: rule(true, false, true), + Items: rule(true, false, true), + }, + + Schemas: rule(true, false, true), + Servers: rule(false, false, true), + + Schema: &SchemaRules{ + Ref: rule(false, false, false), + Type: rule(false, true, false), + Title: rule(false, false, false), + Description: rule(false, false, false), + Format: rule(false, true, false), + Maximum: rule(false, true, false), + Minimum: rule(false, true, false), + ExclusiveMaximum: rule(false, true, false), + ExclusiveMinimum: rule(false, true, false), + MaxLength: rule(false, true, false), + MinLength: rule(false, true, false), + Pattern: rule(false, true, false), + MaxItems: rule(false, true, false), + MinItems: rule(false, true, false), + MaxProperties: rule(false, true, false), + MinProperties: rule(false, true, false), + UniqueItems: rule(false, true, false), + MultipleOf: rule(false, true, false), + ContentEncoding: rule(false, true, false), + ContentMediaType: rule(false, true, false), + Default: rule(false, true, false), + Const: rule(false, true, false), + Nullable: rule(false, true, false), + ReadOnly: rule(false, true, false), + WriteOnly: rule(false, true, false), + Deprecated: rule(false, false, false), + Example: rule(false, false, false), + Examples: rule(false, false, false), + Required: rule(true, false, true), + Enum: rule(false, false, true), + Properties: rule(false, false, true), + AdditionalProperties: rule(true, true, true), + AllOf: rule(false, false, true), + AnyOf: rule(false, false, true), + OneOf: rule(false, false, true), + PrefixItems: rule(false, false, true), + Items: rule(true, true, true), + Discriminator: rule(true, false, true), + ExternalDocs: rule(false, false, false), + Not: rule(true, false, true), + If: rule(true, false, true), + Then: rule(true, false, true), + Else: rule(true, false, true), + PropertyNames: rule(true, false, true), + Contains: rule(true, false, true), + UnevaluatedItems: rule(true, false, true), + UnevaluatedProperties: rule(true, true, true), + DynamicAnchor: rule(false, true, true), // $dynamicAnchor: modification/removal is breaking + DynamicRef: rule(false, true, true), // $dynamicRef: modification/removal is breaking + Id: rule(true, true, true), // $id: all changes are breaking (affects reference resolution) + Comment: rule(false, false, false), // $comment: does not affect API contracts + ContentSchema: rule(true, true, true), // contentSchema: affects content validation + Vocabulary: rule(true, true, true), // $vocabulary: affects schema interpretation + DependentRequired: rule(false, true, true), + XML: rule(false, false, true), + SchemaDialect: rule(true, true, true), + }, + + Discriminator: &DiscriminatorRules{ + PropertyName: rule(true, true, true), + DefaultMapping: rule(true, true, true), + Mapping: rule(false, true, true), + }, + + XML: &XMLRules{ + Name: rule(true, true, true), + Namespace: rule(true, true, true), + Prefix: rule(true, true, true), + Attribute: rule(true, true, true), + NodeType: rule(true, true, true), + Wrapped: rule(true, true, true), + }, + + Server: &ServerRules{ + Name: rule(true, true, true), + URL: rule(true, true, true), + Description: rule(false, false, false), + }, + + ServerVariable: &ServerVariableRules{ + Enum: rule(false, false, true), + Default: rule(true, true, true), + Description: rule(false, false, false), + }, + + Tags: rule(false, false, true), + + Security: rule(false, false, true), + + Tag: &TagRules{ + Name: rule(false, true, true), + Summary: rule(false, false, false), + Description: rule(false, false, false), + Parent: rule(true, true, true), + Kind: rule(false, false, false), + ExternalDocs: rule(false, false, false), + }, + + ExternalDocs: &ExternalDocsRules{ + URL: rule(false, false, false), + Description: rule(false, false, false), + }, + + SecurityScheme: &SecuritySchemeRules{ + Type: rule(true, true, true), + Description: rule(false, false, false), + Name: rule(true, true, true), + In: rule(true, true, true), + Scheme: rule(true, true, true), + BearerFormat: rule(false, false, false), + OpenIDConnectURL: rule(false, false, false), + OAuth2MetadataUrl: rule(false, false, false), + Flows: rule(false, false, true), + Scopes: rule(false, false, true), + Flow: rule(true, true, true), // Swagger 2.0 + AuthorizationURL: rule(true, true, true), // Swagger 2.0 + TokenURL: rule(true, true, true), // Swagger 2.0 + Deprecated: rule(false, false, false), + }, + + SecurityRequirement: &SecurityRequirementRules{ + Schemes: rule(false, false, true), + Scopes: rule(false, false, true), + }, + + OAuthFlows: &OAuthFlowsRules{ + Implicit: rule(false, false, true), + Password: rule(false, false, true), + ClientCredentials: rule(false, false, true), + AuthorizationCode: rule(false, false, true), + Device: rule(false, false, true), + }, + + OAuthFlow: &OAuthFlowRules{ + AuthorizationURL: rule(true, true, true), + TokenURL: rule(true, true, true), + RefreshURL: rule(true, true, true), + Scopes: rule(false, true, true), + }, + + Callback: &CallbackRules{ + Expressions: rule(false, false, true), + }, + + Link: &LinkRules{ + OperationRef: rule(true, true, true), + OperationID: rule(true, true, true), + RequestBody: rule(true, true, true), + Description: rule(false, false, false), + Server: rule(true, false, true), + Parameters: rule(true, true, true), + }, + + Example: &ExampleRules{ + Summary: rule(false, false, false), + Description: rule(false, false, false), + Value: rule(false, false, false), + ExternalValue: rule(false, false, false), + DataValue: rule(false, false, false), + SerializedValue: rule(false, false, false), + }, + } +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/breaking_rules_config.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/breaking_rules_config.go new file mode 100644 index 000000000..b18834718 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/breaking_rules_config.go @@ -0,0 +1,449 @@ +// Copyright 2022-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "fmt" + "reflect" + "strings" + "sync" + + "go.yaml.in/yaml/v4" +) + +// BreakingRulesConfig holds all breaking change rules organized by OpenAPI component. +// Structure mirrors the OpenAPI 3.x specification. +type BreakingRulesConfig struct { + OpenAPI *BreakingChangeRule `json:"openapi,omitempty" yaml:"openapi,omitempty"` + JSONSchemaDialect *BreakingChangeRule `json:"jsonSchemaDialect,omitempty" yaml:"jsonSchemaDialect,omitempty"` + Self *BreakingChangeRule `json:"$self,omitempty" yaml:"$self,omitempty"` + Components *BreakingChangeRule `json:"components,omitempty" yaml:"components,omitempty"` + Info *InfoRules `json:"info,omitempty" yaml:"info,omitempty"` + Contact *ContactRules `json:"contact,omitempty" yaml:"contact,omitempty"` + License *LicenseRules `json:"license,omitempty" yaml:"license,omitempty"` + Paths *PathsRules `json:"paths,omitempty" yaml:"paths,omitempty"` + PathItem *PathItemRules `json:"pathItem,omitempty" yaml:"pathItem,omitempty"` + Operation *OperationRules `json:"operation,omitempty" yaml:"operation,omitempty"` + Parameter *ParameterRules `json:"parameter,omitempty" yaml:"parameter,omitempty"` + RequestBody *RequestBodyRules `json:"requestBody,omitempty" yaml:"requestBody,omitempty"` + Responses *ResponsesRules `json:"responses,omitempty" yaml:"responses,omitempty"` + Response *ResponseRules `json:"response,omitempty" yaml:"response,omitempty"` + MediaType *MediaTypeRules `json:"mediaType,omitempty" yaml:"mediaType,omitempty"` + Encoding *EncodingRules `json:"encoding,omitempty" yaml:"encoding,omitempty"` + Header *HeaderRules `json:"header,omitempty" yaml:"header,omitempty"` + Schema *SchemaRules `json:"schema,omitempty" yaml:"schema,omitempty"` + Schemas *BreakingChangeRule `json:"schemas,omitempty" yaml:"schemas,omitempty"` + Servers *BreakingChangeRule `json:"servers,omitempty" yaml:"servers,omitempty"` + Discriminator *DiscriminatorRules `json:"discriminator,omitempty" yaml:"discriminator,omitempty"` + XML *XMLRules `json:"xml,omitempty" yaml:"xml,omitempty"` + Server *ServerRules `json:"server,omitempty" yaml:"server,omitempty"` + ServerVariable *ServerVariableRules `json:"serverVariable,omitempty" yaml:"serverVariable,omitempty"` + Tags *BreakingChangeRule `json:"tags,omitempty" yaml:"tags,omitempty"` + Tag *TagRules `json:"tag,omitempty" yaml:"tag,omitempty"` + Security *BreakingChangeRule `json:"security,omitempty" yaml:"security,omitempty"` + ExternalDocs *ExternalDocsRules `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` + SecurityScheme *SecuritySchemeRules `json:"securityScheme,omitempty" yaml:"securityScheme,omitempty"` + SecurityRequirement *SecurityRequirementRules `json:"securityRequirement,omitempty" yaml:"securityRequirement,omitempty"` + OAuthFlows *OAuthFlowsRules `json:"oauthFlows,omitempty" yaml:"oauthFlows,omitempty"` + OAuthFlow *OAuthFlowRules `json:"oauthFlow,omitempty" yaml:"oauthFlow,omitempty"` + Callback *CallbackRules `json:"callback,omitempty" yaml:"callback,omitempty"` + Link *LinkRules `json:"link,omitempty" yaml:"link,omitempty"` + Example *ExampleRules `json:"example,omitempty" yaml:"example,omitempty"` + + ruleCache map[string]*BreakingChangeRule + cacheOnce sync.Once +} + +// Merge applies user overrides to the configuration. Only non-nil values from +// the override config replace the current values. Uses reflection to reduce boilerplate. +func (c *BreakingRulesConfig) Merge(override *BreakingRulesConfig) { + if override == nil { + return + } + + cVal := reflect.ValueOf(c).Elem() + oVal := reflect.ValueOf(override).Elem() + + for i := 0; i < configType.NumField(); i++ { + field := configType.Field(i) + cField := cVal.Field(i) + oField := oVal.Field(i) + + if !cField.CanSet() { + continue + } + + if field.Type == ruleType { + cField.Set(reflect.ValueOf(mergeRule( + cField.Interface().(*BreakingChangeRule), + oField.Interface().(*BreakingChangeRule), + ))) + continue + } + + if field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct { + if oField.IsNil() { + continue + } + if cField.IsNil() { + cField.Set(reflect.New(field.Type.Elem())) + } + mergeRulesStruct(cField.Elem(), oField.Elem()) + } + } + + c.invalidateCache() +} + +// IsBreaking looks up whether a change is breaking based on the component, property, and change type. +// Returns the configured breaking status, or false if the rule is not found. +func (c *BreakingRulesConfig) IsBreaking(component, property, changeType string) bool { + rule := c.GetRule(component, property) + if rule == nil { + return false + } + + switch changeType { + case ChangeTypeAdded: + if rule.Added != nil { + return *rule.Added + } + case ChangeTypeModified: + if rule.Modified != nil { + return *rule.Modified + } + case ChangeTypeRemoved: + if rule.Removed != nil { + return *rule.Removed + } + } + return false +} + +// GetRule returns the BreakingChangeRule for a given component and property. +// Returns nil if no rule is defined. Uses internal cache for O(1) lookups. +func (c *BreakingRulesConfig) GetRule(component, property string) *BreakingChangeRule { + c.cacheOnce.Do(func() { + c.ruleCache = c.buildRuleCache() + }) + if property == "" { + return c.ruleCache[component] + } + return c.ruleCache[component+"."+property] +} + +// buildRuleCache creates a flat map of all rules for O(1) lookups using reflection. +func (c *BreakingRulesConfig) buildRuleCache() map[string]*BreakingChangeRule { + cache := make(map[string]*BreakingChangeRule, 200) + cVal := reflect.ValueOf(c).Elem() + + for i := 0; i < configType.NumField(); i++ { + field := configType.Field(i) + fVal := cVal.Field(i) + + compName := jsonTagName(field) + if compName == "" || !fVal.CanInterface() { + continue + } + + if field.Type == ruleType { + cache[compName] = fVal.Interface().(*BreakingChangeRule) + continue + } + + if field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct { + if fVal.IsNil() { + continue + } + addRulesToCache(cache, compName, fVal.Elem()) + } + } + return cache +} + +// invalidateCache resets the cache so it will be rebuilt on next access. +func (c *BreakingRulesConfig) invalidateCache() { + c.cacheOnce = sync.Once{} + c.ruleCache = nil +} + +// --- Config Validation --- + +// ConfigValidationError represents a single validation issue in a breaking rules config. +type ConfigValidationError struct { + // Message is a human-readable description of the issue. + Message string + + // Path is the YAML path where the issue was found (e.g., "schema.discriminator"). + Path string + + // Line is the 1-based line number in the YAML source (0 if unknown). + Line int + + // Column is the 1-based column number in the YAML source (0 if unknown). + Column int + + // FoundKey is the misplaced key that was detected. + FoundKey string + + // SuggestedPath is where the key should be placed instead. + SuggestedPath string +} + +// Error implements the error interface. +func (e *ConfigValidationError) Error() string { + if e.Line > 0 { + return fmt.Sprintf("%s (line %d, column %d)", e.Message, e.Line, e.Column) + } + return e.Message +} + +// ConfigValidationResult holds the results of validating a breaking rules config. +type ConfigValidationResult struct { + // Errors contains all validation issues found. + Errors []*ConfigValidationError +} + +// HasErrors returns true if any validation errors were found. +func (r *ConfigValidationResult) HasErrors() bool { + return len(r.Errors) > 0 +} + +// Error implements the error interface, joining all errors. +func (r *ConfigValidationResult) Error() string { + if !r.HasErrors() { + return "" + } + msgs := make([]string, len(r.Errors)) + for i, e := range r.Errors { + msgs[i] = e.Error() + } + return strings.Join(msgs, "\n") +} + +// validTopLevelComponents is the set of valid top-level keys in a breaking rules config. +// Built from BreakingRulesConfig struct field tags at init time. +var validTopLevelComponents = buildValidComponentSet() + +// buildValidComponentSet creates a set of valid top-level component names +// by reflecting on the BreakingRulesConfig struct tags. +func buildValidComponentSet() map[string]bool { + result := make(map[string]bool) + t := reflect.TypeOf(BreakingRulesConfig{}) + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + name := jsonTagName(field) + if name != "" && name != "-" { + result[name] = true + } + } + return result +} + +// ValidateBreakingRulesConfigYAML validates raw YAML bytes for a breaking rules config. +// It detects misplaced nested configurations (e.g., "schema.discriminator" should be +// just "discriminator" at the top level) and returns all validation errors found. +// Returns nil if the configuration is valid. +func ValidateBreakingRulesConfigYAML(yamlBytes []byte) *ConfigValidationResult { + var rootNode yaml.Node + if err := yaml.Unmarshal(yamlBytes, &rootNode); err != nil { + return &ConfigValidationResult{ + Errors: []*ConfigValidationError{{ + Message: fmt.Sprintf("invalid YAML: %v", err), + }}, + } + } + + result := &ConfigValidationResult{} + validateConfigNode(&rootNode, "", result) + + if result.HasErrors() { + return result + } + return nil +} + +// breakingRuleFields are the valid fields for BreakingChangeRule +var breakingRuleFields = map[string]bool{ + "added": true, + "modified": true, + "removed": true, +} + +// simpleRuleComponents are components that are directly BreakingChangeRule (not nested) +// For these, added/modified/removed at depth 1 is correct (e.g., "openapi.modified: false") +var simpleRuleComponents = map[string]bool{ + "openapi": true, + "jsonSchemaDialect": true, + "$self": true, + "components": true, + "schemas": true, + "servers": true, + "tags": true, + "security": true, +} + +// componentProperties maps each component to its valid property names +// Built from reflection on BreakingRulesConfig struct +var componentProperties = buildComponentPropertiesMap() + +func buildComponentPropertiesMap() map[string]map[string]bool { + result := make(map[string]map[string]bool) + t := reflect.TypeOf(BreakingRulesConfig{}) + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + componentName := jsonTagName(field) + if componentName == "" || componentName == "-" { + continue + } + // Get the field type (pointer to rules struct) + fieldType := field.Type + if fieldType.Kind() == reflect.Ptr { + fieldType = fieldType.Elem() + } + // Build property set for this component + props := make(map[string]bool) + for j := 0; j < fieldType.NumField(); j++ { + propField := fieldType.Field(j) + propName := jsonTagName(propField) + if propName != "" && propName != "-" { + props[propName] = true + } + } + result[componentName] = props + } + return result +} + +// isValidPropertyForComponent checks if a property is valid for the given component +func isValidPropertyForComponent(component, property string) bool { + if props, ok := componentProperties[component]; ok { + return props[property] + } + return false +} + +// validateConfigNode recursively walks the YAML tree looking for misplaced configurations. +func validateConfigNode(node *yaml.Node, path string, result *ConfigValidationResult) { + validateConfigNodeWithDepth(node, path, 0, "", result) +} + +// validateConfigNodeWithDepth recursively walks the YAML tree with depth tracking. +// depth 0 = root, depth 1 = under a component (e.g., "paths"), depth 2 = under a property (e.g., "paths.path") +// parentComponent tracks the root-level component we're under (e.g., "paths", "schema", "openapi") +// parentProperty tracks the property we're under within that component (e.g., "path" under "paths") +func validateConfigNodeWithDepth(node *yaml.Node, path string, depth int, parentComponent string, result *ConfigValidationResult) { + validateConfigNodeWithDepthAndProperty(node, path, depth, parentComponent, "", result) +} + +// validateConfigNodeWithDepthAndProperty is the internal recursive validator. +func validateConfigNodeWithDepthAndProperty(node *yaml.Node, path string, depth int, parentComponent, parentProperty string, result *ConfigValidationResult) { + // Document nodes contain a single content node + if node.Kind == yaml.DocumentNode && len(node.Content) > 0 { + validateConfigNodeWithDepthAndProperty(node.Content[0], path, depth, parentComponent, parentProperty, result) + return + } + + // Only process mapping nodes (objects) + if node.Kind != yaml.MappingNode { + return + } + + // Process key-value pairs in the mapping + for i := 0; i < len(node.Content); i += 2 { + keyNode := node.Content[i] + valueNode := node.Content[i+1] + + if keyNode.Kind != yaml.ScalarNode { + continue + } + + key := keyNode.Value + currentPath := buildConfigPath(path, key) + + // Track the parent component when we enter a top-level component + currentParent := parentComponent + currentProperty := parentProperty + if depth == 0 && validTopLevelComponents[key] { + currentParent = key + currentProperty = "" + } else if depth == 1 && parentComponent != "" { + // At depth 1, the key is a property name under a component + currentProperty = key + } + + // If we're already nested under a component and find another top-level component name, + // this is a misplacement error UNLESS the key is a valid property of the parent component. + // For example, "parameter.example" is valid because ParameterRules has an Example property, + // even though "example" is also a top-level component. + if path != "" && validTopLevelComponents[key] && !isValidPropertyForComponent(parentComponent, key) { + result.Errors = append(result.Errors, &ConfigValidationError{ + Message: fmt.Sprintf("found '%s' nested under '%s'; '%s' should be a top-level key", key, path, key), + Path: currentPath, + Line: keyNode.Line, + Column: keyNode.Column, + FoundKey: key, + SuggestedPath: key, + }) + } + + // Check for breaking rule fields (added/modified/removed) at wrong depth + // depth 1 = directly under a component (e.g., "paths.added" is wrong) + // These should only appear at depth 2 (e.g., "paths.path.added" is correct) + // Exception: "simple" components like openapi, schemas, servers are directly BreakingChangeRule + // so "openapi.modified: false" is correct + if depth == 1 && breakingRuleFields[key] && !simpleRuleComponents[parentComponent] { + // Extract the component name from the path + componentName := path + result.Errors = append(result.Errors, &ConfigValidationError{ + Message: fmt.Sprintf("'%s' found directly under '%s'; breaking rules must be nested under a property name (e.g., '%s.path.%s' not '%s.%s')", key, componentName, componentName, key, componentName, key), + Path: currentPath, + Line: keyNode.Line, + Column: keyNode.Column, + FoundKey: key, + SuggestedPath: fmt.Sprintf("%s..%s", componentName, key), + }) + } + + // At depth 2+, check if we're trying to add invalid keys under a BreakingChangeRule. + // A BreakingChangeRule (like schema.discriminator) can only have added/modified/removed. + // If we find anything else (like "propertyName"), it's someone trying to configure + // a component's sub-rules under the wrong parent. + // + // Only check this if: + // 1. The parentProperty is also a valid top-level component name (like "discriminator") + // 2. The key is a property of that top-level component (like "propertyName" is a property of DiscriminatorRules) + // 3. The key is NOT a breaking rule field (added/modified/removed) + // + // This catches cases like schema.discriminator.propertyName where: + // - schema.discriminator is valid (SchemaRules has Discriminator property) + // - BUT propertyName under it is wrong (should be discriminator.propertyName at top level) + if depth >= 2 && parentProperty != "" && !breakingRuleFields[key] && validTopLevelComponents[parentProperty] { + // Check if this key is a property of the top-level component with the same name as parentProperty + if props, ok := componentProperties[parentProperty]; ok && props[key] { + result.Errors = append(result.Errors, &ConfigValidationError{ + Message: fmt.Sprintf("'%s' is incorrectly nested under '%s'; move your '%s:' block to the top level of your config (current: %s.%s.%s, should be: %s.%s)", parentProperty, parentComponent, parentProperty, parentComponent, parentProperty, key, parentProperty, key), + Path: currentPath, + Line: keyNode.Line, + Column: keyNode.Column, + FoundKey: parentProperty, + SuggestedPath: parentProperty, + }) + } + } + + // Recurse into nested mappings + if valueNode.Kind == yaml.MappingNode { + validateConfigNodeWithDepthAndProperty(valueNode, currentPath, depth+1, currentParent, currentProperty, result) + } + } +} + +// buildConfigPath constructs a dotted path from parent and child components. +func buildConfigPath(parent, child string) string { + if parent == "" { + return child + } + return parent + "." + child +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/breaking_rules_constants.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/breaking_rules_constants.go new file mode 100644 index 000000000..36544361a --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/breaking_rules_constants.go @@ -0,0 +1,209 @@ +// Copyright 2022-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "reflect" + "sync" +) + +// Component name constants for breaking change rule lookups. +// These match the JSON keys used in BreakingRulesConfig. +const ( + CompCallback = "callback" + CompComponents = "components" + CompContact = "contact" + CompDiscriminator = "discriminator" + CompEncoding = "encoding" + CompExample = "example" + CompExternalDocs = "externalDocs" + CompHeader = "header" + CompInfo = "info" + CompJSONSchemaDialect = "jsonSchemaDialect" + CompLicense = "license" + CompLink = "link" + CompMediaType = "mediaType" + CompOAuthFlow = "oauthFlow" + CompOAuthFlows = "oauthFlows" + CompOpenAPI = "openapi" + CompOperation = "operation" + CompParameter = "parameter" + CompPathItem = "pathItem" + CompPaths = "paths" + CompRequestBody = "requestBody" + CompResponse = "response" + CompResponses = "responses" + CompSchema = "schema" + CompSchemas = "schemas" + CompSecurity = "security" + CompSecurityRequirement = "securityRequirement" + CompServers = "servers" + CompSelf = "$self" + CompTags = "tags" + CompSecurityScheme = "securityScheme" + CompServer = "server" + CompServerVariable = "serverVariable" + CompTag = "tag" + CompXML = "xml" +) + +// Property name constants for breaking change rule lookups. +// These match the JSON keys used in the various *Rules structs. +const ( + PropAdditionalOperations = "additionalOperations" + PropAdditionalProperties = "additionalProperties" + PropAllOf = "allOf" + PropAllowEmptyValue = "allowEmptyValue" + PropAllowReserved = "allowReserved" + PropAnyOf = "anyOf" + PropAttribute = "attribute" + PropAuthorizationCode = "authorizationCode" + PropAuthorizationURL = "authorizationUrl" + PropBearerFormat = "bearerFormat" + PropCallbacks = "callbacks" + PropCodes = "codes" + PropClientCredentials = "clientCredentials" + PropCollectionFormat = "collectionFormat" + PropComment = "$comment" + PropConst = "const" + PropContact = "contact" + PropContains = "contains" + PropContentEncoding = "contentEncoding" + PropContentMediaType = "contentMediaType" + PropContentSchema = "contentSchema" + PropContentType = "contentType" + PropDataValue = "dataValue" + PropDefault = "default" + PropDefaultMapping = "defaultMapping" + PropDelete = "delete" + PropDeprecated = "deprecated" + PropDependentRequired = "dependentRequired" + PropDescription = "description" + PropDevice = "device" + PropDiscriminator = "discriminator" + PropDynamicAnchor = "$dynamicAnchor" + PropDynamicRef = "$dynamicRef" + PropId = "$id" + PropElse = "else" + PropEmail = "email" + PropEnum = "enum" + PropExample = "example" + PropExamples = "examples" + PropExclusiveMaximum = "exclusiveMaximum" + PropExclusiveMinimum = "exclusiveMinimum" + PropExplode = "explode" + PropExpressions = "expressions" + PropExternalDocs = "externalDocs" + PropExternalValue = "externalValue" + PropFlow = "flow" + PropFlows = "flows" + PropFormat = "format" + PropGet = "get" + PropHead = "head" + PropIdentifier = "identifier" + PropIf = "if" + PropImplicit = "implicit" + PropIn = "in" + PropItems = "items" + PropItemEncoding = "itemEncoding" + PropItemSchema = "itemSchema" + PropKind = "kind" + PropLicense = "license" + PropMapping = "mapping" + PropMaxItems = "maxItems" + PropMaxLength = "maxLength" + PropMaxProperties = "maxProperties" + PropMaximum = "maximum" + PropMinItems = "minItems" + PropMinLength = "minLength" + PropMinProperties = "minProperties" + PropMinimum = "minimum" + PropMultipleOf = "multipleOf" + PropName = "name" + PropNamespace = "namespace" + PropNodeType = "nodeType" + PropNot = "not" + PropNullable = "nullable" + PropOAuth2MetadataUrl = "oauth2MetadataUrl" + PropOneOf = "oneOf" + PropOpenIDConnectURL = "openIdConnectUrl" + PropOperationID = "operationId" + PropOperationRef = "operationRef" + PropOptions = "options" + PropParameters = "parameters" + PropParent = "parent" + PropPassword = "password" + PropPatch = "patch" + PropPath = "path" + PropPattern = "pattern" + PropPost = "post" + PropPrefix = "prefix" + PropPrefixItems = "prefixItems" + PropProperties = "properties" + PropPropertyName = "propertyName" + PropPropertyNames = "propertyNames" + PropPut = "put" + PropQuery = "query" + PropReadOnly = "readOnly" + PropRef = "$ref" + PropRefreshURL = "refreshUrl" + PropRequired = "required" + PropRequestBody = "requestBody" + PropResponses = "responses" + PropScheme = "scheme" + PropSchemaDialect = "schemaDialect" + PropSchemas = "schemas" + PropSchemes = "schemes" + PropSchema = "schema" + PropScopes = "scopes" + PropSecurity = "security" + PropSelf = "$self" + PropSerializedValue = "serializedValue" + PropServer = "server" + PropServers = "servers" + PropStyle = "style" + PropSummary = "summary" + PropTags = "tags" + PropTermsOfService = "termsOfService" + PropThen = "then" + PropTitle = "title" + PropTokenURL = "tokenUrl" + PropTrace = "trace" + PropType = "type" + PropUnevaluatedItems = "unevaluatedItems" + PropUnevaluatedProps = "unevaluatedProperties" + PropUniqueItems = "uniqueItems" + PropURL = "url" + PropValue = "value" + PropVersion = "version" + PropVocabulary = "$vocabulary" + PropWrapped = "wrapped" + PropWriteOnly = "writeOnly" + PropXML = "xml" +) + +// ChangeType constants for IsBreaking lookup +const ( + ChangeTypeAdded = "added" + ChangeTypeModified = "modified" + ChangeTypeRemoved = "removed" +) + +// reflection types cached at init to avoid repeated TypeOf calls in hot paths +var ( + ruleType = reflect.TypeOf((*BreakingChangeRule)(nil)) + configType = reflect.TypeOf(BreakingRulesConfig{}) +) + +// singleton cache for default rules to avoid repeated allocations +var ( + defaultRulesOnce sync.Once + defaultRulesCache *BreakingRulesConfig +) + +// active config used by comparison functions +var ( + activeConfigMu sync.RWMutex + activeConfig *BreakingRulesConfig +) diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/breaking_rules_model.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/breaking_rules_model.go new file mode 100644 index 000000000..89cc2bf69 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/breaking_rules_model.go @@ -0,0 +1,315 @@ +// Copyright 2022-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +// BreakingChangeRule holds the breaking status for a property's change types. +// nil values mean "use default" - only set values override the defaults. +type BreakingChangeRule struct { + Added *bool `json:"added,omitempty" yaml:"added,omitempty"` + Modified *bool `json:"modified,omitempty" yaml:"modified,omitempty"` + Removed *bool `json:"removed,omitempty" yaml:"removed,omitempty"` +} + +// InfoRules defines breaking rules for the Info object properties. +type InfoRules struct { + Title *BreakingChangeRule `json:"title,omitempty" yaml:"title,omitempty"` + Summary *BreakingChangeRule `json:"summary,omitempty" yaml:"summary,omitempty"` + Description *BreakingChangeRule `json:"description,omitempty" yaml:"description,omitempty"` + TermsOfService *BreakingChangeRule `json:"termsOfService,omitempty" yaml:"termsOfService,omitempty"` + Version *BreakingChangeRule `json:"version,omitempty" yaml:"version,omitempty"` + Contact *BreakingChangeRule `json:"contact,omitempty" yaml:"contact,omitempty"` + License *BreakingChangeRule `json:"license,omitempty" yaml:"license,omitempty"` +} + +// ContactRules defines breaking rules for the Contact object properties. +type ContactRules struct { + URL *BreakingChangeRule `json:"url,omitempty" yaml:"url,omitempty"` + Name *BreakingChangeRule `json:"name,omitempty" yaml:"name,omitempty"` + Email *BreakingChangeRule `json:"email,omitempty" yaml:"email,omitempty"` +} + +// LicenseRules defines breaking rules for the License object properties. +type LicenseRules struct { + URL *BreakingChangeRule `json:"url,omitempty" yaml:"url,omitempty"` + Name *BreakingChangeRule `json:"name,omitempty" yaml:"name,omitempty"` + Identifier *BreakingChangeRule `json:"identifier,omitempty" yaml:"identifier,omitempty"` +} + +// PathsRules defines breaking rules for the Paths object properties. +type PathsRules struct { + Path *BreakingChangeRule `json:"path,omitempty" yaml:"path,omitempty"` +} + +// PathItemRules defines breaking rules for the Path Item object properties. +type PathItemRules struct { + Description *BreakingChangeRule `json:"description,omitempty" yaml:"description,omitempty"` + Summary *BreakingChangeRule `json:"summary,omitempty" yaml:"summary,omitempty"` + Get *BreakingChangeRule `json:"get,omitempty" yaml:"get,omitempty"` + Put *BreakingChangeRule `json:"put,omitempty" yaml:"put,omitempty"` + Post *BreakingChangeRule `json:"post,omitempty" yaml:"post,omitempty"` + Delete *BreakingChangeRule `json:"delete,omitempty" yaml:"delete,omitempty"` + Options *BreakingChangeRule `json:"options,omitempty" yaml:"options,omitempty"` + Head *BreakingChangeRule `json:"head,omitempty" yaml:"head,omitempty"` + Patch *BreakingChangeRule `json:"patch,omitempty" yaml:"patch,omitempty"` + Trace *BreakingChangeRule `json:"trace,omitempty" yaml:"trace,omitempty"` + Query *BreakingChangeRule `json:"query,omitempty" yaml:"query,omitempty"` + AdditionalOperations *BreakingChangeRule `json:"additionalOperations,omitempty" yaml:"additionalOperations,omitempty"` + Servers *BreakingChangeRule `json:"servers,omitempty" yaml:"servers,omitempty"` + Parameters *BreakingChangeRule `json:"parameters,omitempty" yaml:"parameters,omitempty"` +} + +// OperationRules defines breaking rules for the Operation object properties. +type OperationRules struct { + Tags *BreakingChangeRule `json:"tags,omitempty" yaml:"tags,omitempty"` + Summary *BreakingChangeRule `json:"summary,omitempty" yaml:"summary,omitempty"` + Description *BreakingChangeRule `json:"description,omitempty" yaml:"description,omitempty"` + Deprecated *BreakingChangeRule `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` + OperationID *BreakingChangeRule `json:"operationId,omitempty" yaml:"operationId,omitempty"` + ExternalDocs *BreakingChangeRule `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` + Responses *BreakingChangeRule `json:"responses,omitempty" yaml:"responses,omitempty"` + Parameters *BreakingChangeRule `json:"parameters,omitempty" yaml:"parameters,omitempty"` + Security *BreakingChangeRule `json:"security,omitempty" yaml:"security,omitempty"` + RequestBody *BreakingChangeRule `json:"requestBody,omitempty" yaml:"requestBody,omitempty"` + Callbacks *BreakingChangeRule `json:"callbacks,omitempty" yaml:"callbacks,omitempty"` + Servers *BreakingChangeRule `json:"servers,omitempty" yaml:"servers,omitempty"` +} + +// ParameterRules defines breaking rules for the Parameter object properties. +type ParameterRules struct { + Name *BreakingChangeRule `json:"name,omitempty" yaml:"name,omitempty"` + In *BreakingChangeRule `json:"in,omitempty" yaml:"in,omitempty"` + Description *BreakingChangeRule `json:"description,omitempty" yaml:"description,omitempty"` + Required *BreakingChangeRule `json:"required,omitempty" yaml:"required,omitempty"` + AllowEmptyValue *BreakingChangeRule `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"` + Style *BreakingChangeRule `json:"style,omitempty" yaml:"style,omitempty"` + AllowReserved *BreakingChangeRule `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"` + Explode *BreakingChangeRule `json:"explode,omitempty" yaml:"explode,omitempty"` + Deprecated *BreakingChangeRule `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` + Example *BreakingChangeRule `json:"example,omitempty" yaml:"example,omitempty"` + Schema *BreakingChangeRule `json:"schema,omitempty" yaml:"schema,omitempty"` + Items *BreakingChangeRule `json:"items,omitempty" yaml:"items,omitempty"` +} + +// RequestBodyRules defines breaking rules for the Request Body object properties. +type RequestBodyRules struct { + Description *BreakingChangeRule `json:"description,omitempty" yaml:"description,omitempty"` + Required *BreakingChangeRule `json:"required,omitempty" yaml:"required,omitempty"` +} + +// ResponsesRules defines breaking rules for the Responses object properties. +type ResponsesRules struct { + Default *BreakingChangeRule `json:"default,omitempty" yaml:"default,omitempty"` + Codes *BreakingChangeRule `json:"codes,omitempty" yaml:"codes,omitempty"` +} + +// ResponseRules defines breaking rules for the Response object properties. +type ResponseRules struct { + Description *BreakingChangeRule `json:"description,omitempty" yaml:"description,omitempty"` + Summary *BreakingChangeRule `json:"summary,omitempty" yaml:"summary,omitempty"` + Schema *BreakingChangeRule `json:"schema,omitempty" yaml:"schema,omitempty"` + Examples *BreakingChangeRule `json:"examples,omitempty" yaml:"examples,omitempty"` +} + +// MediaTypeRules defines breaking rules for the Media Type object properties. +type MediaTypeRules struct { + Example *BreakingChangeRule `json:"example,omitempty" yaml:"example,omitempty"` + Schema *BreakingChangeRule `json:"schema,omitempty" yaml:"schema,omitempty"` + ItemSchema *BreakingChangeRule `json:"itemSchema,omitempty" yaml:"itemSchema,omitempty"` + ItemEncoding *BreakingChangeRule `json:"itemEncoding,omitempty" yaml:"itemEncoding,omitempty"` +} + +// EncodingRules defines breaking rules for the Encoding object properties. +type EncodingRules struct { + ContentType *BreakingChangeRule `json:"contentType,omitempty" yaml:"contentType,omitempty"` + Style *BreakingChangeRule `json:"style,omitempty" yaml:"style,omitempty"` + Explode *BreakingChangeRule `json:"explode,omitempty" yaml:"explode,omitempty"` + AllowReserved *BreakingChangeRule `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"` +} + +// HeaderRules defines breaking rules for the Header object properties. +type HeaderRules struct { + Description *BreakingChangeRule `json:"description,omitempty" yaml:"description,omitempty"` + Style *BreakingChangeRule `json:"style,omitempty" yaml:"style,omitempty"` + AllowReserved *BreakingChangeRule `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"` + AllowEmptyValue *BreakingChangeRule `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"` + Explode *BreakingChangeRule `json:"explode,omitempty" yaml:"explode,omitempty"` + Example *BreakingChangeRule `json:"example,omitempty" yaml:"example,omitempty"` + Deprecated *BreakingChangeRule `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` + Required *BreakingChangeRule `json:"required,omitempty" yaml:"required,omitempty"` + Schema *BreakingChangeRule `json:"schema,omitempty" yaml:"schema,omitempty"` + Items *BreakingChangeRule `json:"items,omitempty" yaml:"items,omitempty"` +} + +// SchemaRules defines breaking rules for the Schema object properties. +type SchemaRules struct { + Ref *BreakingChangeRule `json:"$ref,omitempty" yaml:"$ref,omitempty"` + Type *BreakingChangeRule `json:"type,omitempty" yaml:"type,omitempty"` + Title *BreakingChangeRule `json:"title,omitempty" yaml:"title,omitempty"` + Description *BreakingChangeRule `json:"description,omitempty" yaml:"description,omitempty"` + Format *BreakingChangeRule `json:"format,omitempty" yaml:"format,omitempty"` + Maximum *BreakingChangeRule `json:"maximum,omitempty" yaml:"maximum,omitempty"` + Minimum *BreakingChangeRule `json:"minimum,omitempty" yaml:"minimum,omitempty"` + ExclusiveMaximum *BreakingChangeRule `json:"exclusiveMaximum,omitempty" yaml:"exclusiveMaximum,omitempty"` + ExclusiveMinimum *BreakingChangeRule `json:"exclusiveMinimum,omitempty" yaml:"exclusiveMinimum,omitempty"` + MaxLength *BreakingChangeRule `json:"maxLength,omitempty" yaml:"maxLength,omitempty"` + MinLength *BreakingChangeRule `json:"minLength,omitempty" yaml:"minLength,omitempty"` + Pattern *BreakingChangeRule `json:"pattern,omitempty" yaml:"pattern,omitempty"` + MaxItems *BreakingChangeRule `json:"maxItems,omitempty" yaml:"maxItems,omitempty"` + MinItems *BreakingChangeRule `json:"minItems,omitempty" yaml:"minItems,omitempty"` + MaxProperties *BreakingChangeRule `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"` + MinProperties *BreakingChangeRule `json:"minProperties,omitempty" yaml:"minProperties,omitempty"` + UniqueItems *BreakingChangeRule `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"` + MultipleOf *BreakingChangeRule `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"` + ContentEncoding *BreakingChangeRule `json:"contentEncoding,omitempty" yaml:"contentEncoding,omitempty"` + ContentMediaType *BreakingChangeRule `json:"contentMediaType,omitempty" yaml:"contentMediaType,omitempty"` + Default *BreakingChangeRule `json:"default,omitempty" yaml:"default,omitempty"` + Const *BreakingChangeRule `json:"const,omitempty" yaml:"const,omitempty"` + Nullable *BreakingChangeRule `json:"nullable,omitempty" yaml:"nullable,omitempty"` + ReadOnly *BreakingChangeRule `json:"readOnly,omitempty" yaml:"readOnly,omitempty"` + WriteOnly *BreakingChangeRule `json:"writeOnly,omitempty" yaml:"writeOnly,omitempty"` + Deprecated *BreakingChangeRule `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` + Example *BreakingChangeRule `json:"example,omitempty" yaml:"example,omitempty"` + Examples *BreakingChangeRule `json:"examples,omitempty" yaml:"examples,omitempty"` + Required *BreakingChangeRule `json:"required,omitempty" yaml:"required,omitempty"` + Enum *BreakingChangeRule `json:"enum,omitempty" yaml:"enum,omitempty"` + Properties *BreakingChangeRule `json:"properties,omitempty" yaml:"properties,omitempty"` + AdditionalProperties *BreakingChangeRule `json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty"` + AllOf *BreakingChangeRule `json:"allOf,omitempty" yaml:"allOf,omitempty"` + AnyOf *BreakingChangeRule `json:"anyOf,omitempty" yaml:"anyOf,omitempty"` + OneOf *BreakingChangeRule `json:"oneOf,omitempty" yaml:"oneOf,omitempty"` + PrefixItems *BreakingChangeRule `json:"prefixItems,omitempty" yaml:"prefixItems,omitempty"` + Items *BreakingChangeRule `json:"items,omitempty" yaml:"items,omitempty"` + Discriminator *BreakingChangeRule `json:"discriminator,omitempty" yaml:"discriminator,omitempty"` + ExternalDocs *BreakingChangeRule `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` + Not *BreakingChangeRule `json:"not,omitempty" yaml:"not,omitempty"` + If *BreakingChangeRule `json:"if,omitempty" yaml:"if,omitempty"` + Then *BreakingChangeRule `json:"then,omitempty" yaml:"then,omitempty"` + Else *BreakingChangeRule `json:"else,omitempty" yaml:"else,omitempty"` + PropertyNames *BreakingChangeRule `json:"propertyNames,omitempty" yaml:"propertyNames,omitempty"` + Contains *BreakingChangeRule `json:"contains,omitempty" yaml:"contains,omitempty"` + UnevaluatedItems *BreakingChangeRule `json:"unevaluatedItems,omitempty" yaml:"unevaluatedItems,omitempty"` + UnevaluatedProperties *BreakingChangeRule `json:"unevaluatedProperties,omitempty" yaml:"unevaluatedProperties,omitempty"` + DynamicAnchor *BreakingChangeRule `json:"$dynamicAnchor,omitempty" yaml:"$dynamicAnchor,omitempty"` + DynamicRef *BreakingChangeRule `json:"$dynamicRef,omitempty" yaml:"$dynamicRef,omitempty"` + Id *BreakingChangeRule `json:"$id,omitempty" yaml:"$id,omitempty"` + Comment *BreakingChangeRule `json:"$comment,omitempty" yaml:"$comment,omitempty"` + ContentSchema *BreakingChangeRule `json:"contentSchema,omitempty" yaml:"contentSchema,omitempty"` + Vocabulary *BreakingChangeRule `json:"$vocabulary,omitempty" yaml:"$vocabulary,omitempty"` + DependentRequired *BreakingChangeRule `json:"dependentRequired,omitempty" yaml:"dependentRequired,omitempty"` + XML *BreakingChangeRule `json:"xml,omitempty" yaml:"xml,omitempty"` + SchemaDialect *BreakingChangeRule `json:"schemaDialect,omitempty" yaml:"schemaDialect,omitempty"` +} + +// DiscriminatorRules defines breaking rules for the Discriminator object properties. +type DiscriminatorRules struct { + PropertyName *BreakingChangeRule `json:"propertyName,omitempty" yaml:"propertyName,omitempty"` + DefaultMapping *BreakingChangeRule `json:"defaultMapping,omitempty" yaml:"defaultMapping,omitempty"` + Mapping *BreakingChangeRule `json:"mapping,omitempty" yaml:"mapping,omitempty"` +} + +// XMLRules defines breaking rules for the XML object properties. +type XMLRules struct { + Name *BreakingChangeRule `json:"name,omitempty" yaml:"name,omitempty"` + Namespace *BreakingChangeRule `json:"namespace,omitempty" yaml:"namespace,omitempty"` + Prefix *BreakingChangeRule `json:"prefix,omitempty" yaml:"prefix,omitempty"` + Attribute *BreakingChangeRule `json:"attribute,omitempty" yaml:"attribute,omitempty"` + NodeType *BreakingChangeRule `json:"nodeType,omitempty" yaml:"nodeType,omitempty"` + Wrapped *BreakingChangeRule `json:"wrapped,omitempty" yaml:"wrapped,omitempty"` +} + +// ServerRules defines breaking rules for the Server object properties. +type ServerRules struct { + Name *BreakingChangeRule `json:"name,omitempty" yaml:"name,omitempty"` + URL *BreakingChangeRule `json:"url,omitempty" yaml:"url,omitempty"` + Description *BreakingChangeRule `json:"description,omitempty" yaml:"description,omitempty"` +} + +// ServerVariableRules defines breaking rules for the Server Variable object properties. +type ServerVariableRules struct { + Enum *BreakingChangeRule `json:"enum,omitempty" yaml:"enum,omitempty"` + Default *BreakingChangeRule `json:"default,omitempty" yaml:"default,omitempty"` + Description *BreakingChangeRule `json:"description,omitempty" yaml:"description,omitempty"` +} + +// TagRules defines breaking rules for the Tag object properties. +type TagRules struct { + Name *BreakingChangeRule `json:"name,omitempty" yaml:"name,omitempty"` + Summary *BreakingChangeRule `json:"summary,omitempty" yaml:"summary,omitempty"` + Description *BreakingChangeRule `json:"description,omitempty" yaml:"description,omitempty"` + Parent *BreakingChangeRule `json:"parent,omitempty" yaml:"parent,omitempty"` + Kind *BreakingChangeRule `json:"kind,omitempty" yaml:"kind,omitempty"` + ExternalDocs *BreakingChangeRule `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` +} + +// ExternalDocsRules defines breaking rules for the External Documentation object properties. +type ExternalDocsRules struct { + URL *BreakingChangeRule `json:"url,omitempty" yaml:"url,omitempty"` + Description *BreakingChangeRule `json:"description,omitempty" yaml:"description,omitempty"` +} + +// SecuritySchemeRules defines breaking rules for the Security Scheme object properties. +type SecuritySchemeRules struct { + Type *BreakingChangeRule `json:"type,omitempty" yaml:"type,omitempty"` + Description *BreakingChangeRule `json:"description,omitempty" yaml:"description,omitempty"` + Name *BreakingChangeRule `json:"name,omitempty" yaml:"name,omitempty"` + In *BreakingChangeRule `json:"in,omitempty" yaml:"in,omitempty"` + Scheme *BreakingChangeRule `json:"scheme,omitempty" yaml:"scheme,omitempty"` + BearerFormat *BreakingChangeRule `json:"bearerFormat,omitempty" yaml:"bearerFormat,omitempty"` + OpenIDConnectURL *BreakingChangeRule `json:"openIdConnectUrl,omitempty" yaml:"openIdConnectUrl,omitempty"` + OAuth2MetadataUrl *BreakingChangeRule `json:"oauth2MetadataUrl,omitempty" yaml:"oauth2MetadataUrl,omitempty"` + Flows *BreakingChangeRule `json:"flows,omitempty" yaml:"flows,omitempty"` + Scopes *BreakingChangeRule `json:"scopes,omitempty" yaml:"scopes,omitempty"` + Flow *BreakingChangeRule `json:"flow,omitempty" yaml:"flow,omitempty"` // Swagger 2.0 + AuthorizationURL *BreakingChangeRule `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"` // Swagger 2.0 + TokenURL *BreakingChangeRule `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"` // Swagger 2.0 + Deprecated *BreakingChangeRule `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` +} + +// SecurityRequirementRules defines breaking rules for the Security Requirement object properties. +type SecurityRequirementRules struct { + Schemes *BreakingChangeRule `json:"schemes,omitempty" yaml:"schemes,omitempty"` + Scopes *BreakingChangeRule `json:"scopes,omitempty" yaml:"scopes,omitempty"` +} + +// OAuthFlowsRules defines breaking rules for the OAuth Flows object properties. +type OAuthFlowsRules struct { + Implicit *BreakingChangeRule `json:"implicit,omitempty" yaml:"implicit,omitempty"` + Password *BreakingChangeRule `json:"password,omitempty" yaml:"password,omitempty"` + ClientCredentials *BreakingChangeRule `json:"clientCredentials,omitempty" yaml:"clientCredentials,omitempty"` + AuthorizationCode *BreakingChangeRule `json:"authorizationCode,omitempty" yaml:"authorizationCode,omitempty"` + Device *BreakingChangeRule `json:"device,omitempty" yaml:"device,omitempty"` +} + +// OAuthFlowRules defines breaking rules for the OAuth Flow object properties. +type OAuthFlowRules struct { + AuthorizationURL *BreakingChangeRule `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"` + TokenURL *BreakingChangeRule `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"` + RefreshURL *BreakingChangeRule `json:"refreshUrl,omitempty" yaml:"refreshUrl,omitempty"` + Scopes *BreakingChangeRule `json:"scopes,omitempty" yaml:"scopes,omitempty"` +} + +// CallbackRules defines breaking rules for the Callback object properties. +type CallbackRules struct { + Expressions *BreakingChangeRule `json:"expressions,omitempty" yaml:"expressions,omitempty"` +} + +// LinkRules defines breaking rules for the Link object properties. +type LinkRules struct { + OperationRef *BreakingChangeRule `json:"operationRef,omitempty" yaml:"operationRef,omitempty"` + OperationID *BreakingChangeRule `json:"operationId,omitempty" yaml:"operationId,omitempty"` + RequestBody *BreakingChangeRule `json:"requestBody,omitempty" yaml:"requestBody,omitempty"` + Description *BreakingChangeRule `json:"description,omitempty" yaml:"description,omitempty"` + Server *BreakingChangeRule `json:"server,omitempty" yaml:"server,omitempty"` + Parameters *BreakingChangeRule `json:"parameters,omitempty" yaml:"parameters,omitempty"` +} + +// ExampleRules defines breaking rules for the Example object properties. +type ExampleRules struct { + Summary *BreakingChangeRule `json:"summary,omitempty" yaml:"summary,omitempty"` + Description *BreakingChangeRule `json:"description,omitempty" yaml:"description,omitempty"` + Value *BreakingChangeRule `json:"value,omitempty" yaml:"value,omitempty"` + ExternalValue *BreakingChangeRule `json:"externalValue,omitempty" yaml:"externalValue,omitempty"` + DataValue *BreakingChangeRule `json:"dataValue,omitempty" yaml:"dataValue,omitempty"` + SerializedValue *BreakingChangeRule `json:"serializedValue,omitempty" yaml:"serializedValue,omitempty"` +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/callback.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/callback.go new file mode 100644 index 000000000..f5685b927 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/callback.go @@ -0,0 +1,159 @@ +// Copyright 2022-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "github.com/pb33f/libopenapi/datamodel/low" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +// CallbackChanges represents all changes made between two Callback OpenAPI objects. +type CallbackChanges struct { + *PropertyChanges + ExpressionChanges map[string]*PathItemChanges `json:"expressions,omitempty" yaml:"expressions,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` +} + +// TotalChanges returns a total count of all changes made between Callback objects +func (c *CallbackChanges) TotalChanges() int { + if c == nil { + return 0 + } + d := c.PropertyChanges.TotalChanges() + for k := range c.ExpressionChanges { + if c.ExpressionChanges[k] != nil { + d += c.ExpressionChanges[k].TotalChanges() + } + } + if c.ExtensionChanges != nil { + d += c.ExtensionChanges.TotalChanges() + } + return d +} + +// GetAllChanges returns a slice of all changes made between Callback objects +func (c *CallbackChanges) GetAllChanges() []*Change { + if c == nil { + return nil + } + var changes []*Change + changes = append(changes, c.Changes...) + for k := range c.ExpressionChanges { + changes = append(changes, c.ExpressionChanges[k].GetAllChanges()...) + } + if c.ExtensionChanges != nil { + changes = append(changes, c.ExtensionChanges.GetAllChanges()...) + } + return changes +} + +// TotalBreakingChanges returns a total count of all changes made between Callback objects +func (c *CallbackChanges) TotalBreakingChanges() int { + d := c.PropertyChanges.TotalBreakingChanges() + for k := range c.ExpressionChanges { + d += c.ExpressionChanges[k].TotalBreakingChanges() + } + if c.ExtensionChanges != nil { + d += c.ExtensionChanges.TotalBreakingChanges() + } + return d +} + +// CompareCallback will compare two Callback objects and return a pointer to CallbackChanges with all the things +// that have changed between them. Handles nil inputs for added/removed callback scenarios. +func CompareCallback(l, r *v3.Callback) *CallbackChanges { + cc := new(CallbackChanges) + var changes []*Change + + if l == nil && r == nil { + return nil + } + + // whole callback added - use operation.callbacks breaking rules + if l == nil { + expChanges := make(map[string]*PathItemChanges) + for k, v := range r.Expression.FromOldest() { + CreateChange(&changes, ObjectAdded, k.Value, + nil, v.GetValueNode(), BreakingAdded(CompOperation, PropCallbacks), + nil, v.GetValue()) + } + cc.ExpressionChanges = expChanges + cc.ExtensionChanges = CompareExtensions(nil, r.Extensions) + cc.PropertyChanges = NewPropertyChanges(changes) + if cc.TotalChanges() <= 0 { + return nil + } + return cc + } + + // whole callback removed - use operation.callbacks breaking rules + if r == nil { + expChanges := make(map[string]*PathItemChanges) + for k, v := range l.Expression.FromOldest() { + CreateChange(&changes, ObjectRemoved, k.Value, + v.GetValueNode(), nil, BreakingRemoved(CompOperation, PropCallbacks), + v.GetValue(), nil) + } + cc.ExpressionChanges = expChanges + cc.ExtensionChanges = CompareExtensions(l.Extensions, nil) + cc.PropertyChanges = NewPropertyChanges(changes) + if cc.TotalChanges() <= 0 { + return nil + } + return cc + } + + // Both exist - compare them + lHashes := make(map[string]string) + rHashes := make(map[string]string) + + lValues := make(map[string]low.ValueReference[*v3.PathItem]) + rValues := make(map[string]low.ValueReference[*v3.PathItem]) + + for k, v := range l.Expression.FromOldest() { + lHashes[k.Value] = low.GenerateHashString(v.Value) + lValues[k.Value] = v + } + + for k, v := range r.Expression.FromOldest() { + rHashes[k.Value] = low.GenerateHashString(v.Value) + rValues[k.Value] = v + } + + expChanges := make(map[string]*PathItemChanges) + + // check left path item hashes + for k := range lHashes { + rhash := rHashes[k] + if rhash == "" { + CreateChange(&changes, ObjectRemoved, k, + lValues[k].GetValueNode(), nil, BreakingRemoved(CompCallback, PropExpressions), + lValues[k].GetValue(), nil) + continue + } + if lHashes[k] == rHashes[k] { + continue + } + // run comparison. + expChanges[k] = ComparePathItems(lValues[k].Value, rValues[k].Value) + } + + // check right path item hashes + for k := range rHashes { + lhash := lHashes[k] + if lhash == "" { + CreateChange(&changes, ObjectAdded, k, + nil, rValues[k].GetValueNode(), BreakingAdded(CompCallback, PropExpressions), + nil, rValues[k].GetValue()) + continue + } + } + cc.ExpressionChanges = expChanges + cc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) + cc.PropertyChanges = NewPropertyChanges(changes) + if cc.TotalChanges() <= 0 { + return nil + } + return cc +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/change_types.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/change_types.go new file mode 100644 index 000000000..668423eba --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/change_types.go @@ -0,0 +1,286 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "encoding/json" + + "go.yaml.in/yaml/v4" +) + +// Definitions of the possible changes between two items +const ( + + // Modified means that was a modification of a value was made + Modified = iota + 1 + + // PropertyAdded means that a new property to an object was added + PropertyAdded + + // ObjectAdded means that a new object was added to a parent object + ObjectAdded + + // ObjectRemoved means that an object was removed from a parent object + ObjectRemoved + + // PropertyRemoved means that a property of an object was removed + PropertyRemoved +) + +// WhatChanged is a summary object that contains a high level summary of everything changed. +type WhatChanged struct { + Added int `json:"added,omitempty" yaml:"added,omitempty"` + Removed int `json:"removed,omitempty" yaml:"removed,omitempty"` + Modified int `json:"modified,omitempty" yaml:"modified,omitempty"` + TotalChanges int `json:"total,omitempty" yaml:"total,omitempty"` +} + +// ChangeContext holds a reference to the line and column positions of original and new change. +type ChangeContext struct { + DocumentLocation string `json:"document,omitempty" yaml:"document,omitempty"` + OriginalLine *int `json:"originalLine,omitempty" yaml:"originalLine,omitempty"` + OriginalColumn *int `json:"originalColumn,omitempty" yaml:"originalColumn,omitempty"` + NewLine *int `json:"newLine,omitempty" yaml:"newLine,omitempty"` + NewColumn *int `json:"newColumn,omitempty" yaml:"newColumn,omitempty"` +} + +type ChangeIsReferenced interface { + GetChangeReference() string + SetChangeReference(ref string) +} + +// HasChanged determines if the line and column numbers of the original and new values have changed. +// +// It's worth noting that there is no guarantee to the positions of anything in either left or right, so +// considering these values as 'changes' is going to add a considerable amount of noise to results. +func (c *ChangeContext) HasChanged() bool { + if c.NewLine != nil && c.OriginalLine != nil && *c.NewLine != *c.OriginalLine { + return true + } + //if c.NewColumn != nil && c.OriginalColumn != nil && *c.NewColumn != *c.OriginalColumn { + // return true + //} + if (c.NewLine == nil && c.OriginalLine != nil) || (c.NewLine != nil && c.OriginalLine == nil) { + return true + } + //if (c.NewColumn == nil && c.OriginalColumn != nil) || (c.NewColumn != nil && c.OriginalColumn == nil) { + // return true + //} + return false +} + +// Change represents a change between two different elements inside an OpenAPI specification. +type Change struct { + // Context represents the lines and column numbers of the original and new values + // It's worth noting that these values may frequently be different and are not used to calculate + // a change. If the positions change, but values do not, then no change is recorded. + Context *ChangeContext `json:"context,omitempty" yaml:"context,omitempty"` + + // ChangeType represents the type of change that occurred. stored as an integer, defined by constants above. + ChangeType int `json:"change,omitempty" yaml:"change,omitempty"` + + // Property is the property name key being changed. + Property string `json:"property,omitempty" yaml:"property,omitempty"` + + // Original is the original value represented as a string. + Original string `json:"original,omitempty" yaml:"original,omitempty"` + + // New is the new value represented as a string. + New string `json:"new,omitempty" yaml:"new,omitempty"` + + // OriginalEncoded is the original value serialized to YAML (for complex types like extensions). + // Only populated for specific use cases (e.g., extension values that are objects/arrays). + OriginalEncoded string `json:"originalEncoded,omitempty" yaml:"originalEncoded,omitempty"` + + // NewEncoded is the new value serialized to YAML (for complex types like extensions). + // Only populated for specific use cases (e.g., extension values that are objects/arrays). + NewEncoded string `json:"newEncoded,omitempty" yaml:"newEncoded,omitempty"` + + // Breaking determines if the change is a breaking one or not. + Breaking bool `json:"breaking" yaml:"breaking"` + + // OriginalObject represents the original object that was changed. + OriginalObject any `json:"-" yaml:"-"` + + // NewObject represents the new object that has been modified. + NewObject any `json:"-" yaml:"-"` + + // Type represents the type of object that was changed. (not used in the current implementation). + Type string `json:"type,omitempty"` + + // Path represents the path to the object that was changed (not used in the current implementation). + Path string `json:"path,omitempty"` + + // Reference is populated when the change is related to a $ref change. + Reference string `json:"reference,omitempty"` +} + +// MarshalJSON is a custom JSON marshaller for the Change object. +func (c *Change) MarshalJSON() ([]byte, error) { + changeType := "" + switch c.ChangeType { + case Modified: + changeType = "modified" + case PropertyAdded: + changeType = "property_added" + case ObjectAdded: + changeType = "object_added" + case ObjectRemoved: + changeType = "object_removed" + case PropertyRemoved: + changeType = "property_removed" + } + data := map[string]interface{}{ + "change": c.ChangeType, + "changeText": changeType, + "property": c.Property, + "breaking": c.Breaking, + } + + if c.Original != "" { + data["original"] = c.Original + } + + if c.New != "" { + data["new"] = c.New + } + + if c.OriginalEncoded != "" { + data["originalEncoded"] = c.OriginalEncoded + } + + if c.NewEncoded != "" { + data["newEncoded"] = c.NewEncoded + } + + if c.Context != nil { + data["context"] = c.Context + } + if c.Type != "" { + data["type"] = c.Type + } + if c.Path != "" { + data["path"] = c.Path + } + return json.Marshal(data) +} + +// PropertyChanges holds a slice of Change pointers +type PropertyChanges struct { + RenderPropertiesOnly bool `json:"-" yaml:"-"` + ChangeReference string `json:"changeReference,omitempty""` + Changes []*Change `json:"changes,omitempty" yaml:"changes,omitempty"` +} + +func (p *PropertyChanges) SetChangeReference(ref string) { + p.ChangeReference = ref +} + +func (p *PropertyChanges) GetChangeReference() string { + return p.ChangeReference +} + +// TotalChanges returns the total number of property changes made. +func (p *PropertyChanges) TotalChanges() int { + if p == nil { + return 0 + } + return len(p.Changes) +} + +// TotalBreakingChanges returns the total number of property breaking changes made. +func (p *PropertyChanges) TotalBreakingChanges() int { + return CountBreakingChanges(p.Changes) +} + +// PropertiesOnly will set the change object to only render properties, not the timeline. +func (p *PropertyChanges) PropertiesOnly() { + p.RenderPropertiesOnly = true +} + +// GetPropertyChanges will return just the property changes +func (p *PropertyChanges) GetPropertyChanges() []*Change { + return p.Changes +} + +func NewPropertyChanges(changes []*Change) *PropertyChanges { + return &PropertyChanges{Changes: changes} +} + +// PropertyCheck is used by functions to check the state of left and right values. +type PropertyCheck struct { + // Original is the property we're checking on the left + Original any + + // New is s the property we're checking on the right + New any + + // Label is the identifier we're looking for on the left and right hand sides + Label string + + // LeftNode is the yaml.Node pointer that holds the original node structure of the value + LeftNode *yaml.Node + + // RightNode is the yaml.Node pointer that holds the new node structure of the value + RightNode *yaml.Node + + // Breaking determines if the check is a breaking change (modifications or removals etc.) + // + // Deprecated: Use Component and Property fields for configurable breaking rules. + // This field is used as a fallback when Component is not set. + // + // TODO: Migration to Component/Property-based breaking rules + // + // Current state: CreateChange() takes a `breaking bool` parameter that is computed via + // BreakingAdded/Modified/Removed(component, property) and stored directly on this field. + // The breaking status is fixed at Change creation time. + // + // Target state: CreateChange() should accept `component, property string` parameters instead, + // store them on the Change, and breaking status should be computed at runtime via IsBreaking(). + // This allows users to apply different breaking rule configs to the same set of changes. + // + // Migration steps: + // 1. Extend CreateChange signature to accept component, property string + // 2. Update ~198 CreateChange call sites across 23 files + // 3. Add IsBreaking() method on Change that computes from Component/Property + // 4. Keep Breaking field populated for backward compatibility + // + // Files with most CreateChange calls: schema.go (51), path_item.go (42), operation.go (22) + Breaking bool + + // Component is the OpenAPI component type (e.g., CompTag, CompSchema) for breaking rules lookup. + // When set along with Property, the configurable breaking rules system is used instead of Breaking. + // TODO: Currently not populated - see Breaking field TODO for migration plan. + Component string + + // Property is the property name within the component (e.g., PropParent, PropName) for breaking rules lookup. + // Used together with Component to look up the correct breaking rule for each change type. + // TODO: Currently not populated - see Breaking field TODO for migration plan. + Property string + + // Changes represents a pointer to the slice to contain all changes found. + Changes *[]*Change +} + +// NewPropertyCheck creates a PropertyCheck with the Component and Property fields set for +// configurable breaking rules. This is the preferred way to create PropertyCheck instances. +func NewPropertyCheck( + component, property string, + leftNode, rightNode *yaml.Node, + label string, + changes *[]*Change, + original, new any, +) *PropertyCheck { + return &PropertyCheck{ + LeftNode: leftNode, + RightNode: rightNode, + Label: label, + Changes: changes, + Breaking: BreakingModified(component, property), // fallback for legacy code paths + Component: component, + Property: property, + Original: original, + New: new, + } +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/comparison_functions.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/comparison_functions.go new file mode 100644 index 000000000..c53f9030f --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/comparison_functions.go @@ -0,0 +1,816 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "fmt" + "reflect" + "strings" + "sync" + + "github.com/pb33f/libopenapi/datamodel/low/base" + + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + + "github.com/pb33f/libopenapi/datamodel/low" + "go.yaml.in/yaml/v4" +) + +const ( + HashPh = "%x" + EMPTY_STR = "" +) + +var changeMutex sync.Mutex + +// SetReferenceIfExists checks if a low-level value has a reference and sets it on the change object +// if the change object implements the ChangeIsReferenced interface. +func SetReferenceIfExists[T any](value *low.ValueReference[T], changeObj any) { + if value != nil && value.IsReference() { + if refChange, ok := changeObj.(ChangeIsReferenced); ok { + refChange.SetChangeReference(value.GetReference()) + } + } +} + +// PreserveParameterReference checks if a parameter is a reference and preserves it on the changes object. +// This eliminates duplicate reference preservation logic in operation.go and path_item.go. +func PreserveParameterReference[T any](lRefs, rRefs map[string]*low.ValueReference[T], name string, changes ChangeIsReferenced) { + if lRef := lRefs[name]; lRef != nil && lRef.IsReference() { + SetReferenceIfExists(lRef, changes) + } else if rRef := rRefs[name]; rRef != nil && rRef.IsReference() { + SetReferenceIfExists(rRef, changes) + } +} + +func checkLocation(ctx *ChangeContext, hs base.HasIndex) bool { + if !reflect.ValueOf(hs).IsNil() { + idx := hs.GetIndex() + if idx == nil { + return false + } + if idx.GetRolodex() != nil { + r := idx.GetRolodex() + rIdx := r.GetRootIndex() + if rIdx.GetSpecAbsolutePath() != idx.GetSpecAbsolutePath() { + ctx.DocumentLocation = idx.GetSpecAbsolutePath() + return true + } + } + } + return false +} + +// CreateChange is a generic function that will create a Change of type T, populate all properties if set and then +// add a pointer to Change[T] in the slice of Change pointers provided +func CreateChange(changes *[]*Change, changeType int, property string, leftValueNode, rightValueNode *yaml.Node, + breaking bool, originalObject, newObject any, +) *[]*Change { + // create a new context for the left and right nodes. + ctx := CreateContext(leftValueNode, rightValueNode) + c := &Change{ + Context: ctx, + ChangeType: changeType, + Property: property, + Breaking: breaking, + } + + // lets find out if the objects are local to the root, or if it's come from another document in the tree. + if originalObject != nil { + if hs, ok := originalObject.(base.HasIndex); ok { + checkLocation(ctx, hs) + } + } + if newObject != nil { + if hs, ok := newObject.(base.HasIndex); ok { + checkLocation(ctx, hs) + } + } + + // if the left is not nil, we have an original value + if leftValueNode != nil && leftValueNode.Value != EMPTY_STR { + c.Original = leftValueNode.Value + } + // if the right is not nil, then we have a new value + if rightValueNode != nil && rightValueNode.Value != EMPTY_STR { + c.New = rightValueNode.Value + } + + // If node is nil but object is a string, use the object value as fallback + // This handles cases where the value is provided as the object parameter (e.g., security requirements) + if leftValueNode == nil && c.Original == "" { + if str, ok := originalObject.(string); ok { + c.Original = str + } + } + if rightValueNode == nil && c.New == "" { + if str, ok := newObject.(string); ok { + c.New = str + } + } + + // original and new objects + c.OriginalObject = originalObject + c.NewObject = newObject + + // add the change to supplied changes slice + changeMutex.Lock() + *changes = append(*changes, c) + changeMutex.Unlock() + return changes +} + +// CreateChangeWithEncoding is like CreateChange but also populates the encoded fields for complex values. +// use this ONLY for extensions or other cases where complex YAML structures need to be serialized. +// the encoded values are serialized to YAML format. +func CreateChangeWithEncoding(changes *[]*Change, changeType int, property string, leftValueNode, rightValueNode *yaml.Node, + breaking bool, originalObject, newObject any, +) *[]*Change { + CreateChange(changes, changeType, property, leftValueNode, rightValueNode, breaking, originalObject, newObject) + + c := (*changes)[len(*changes)-1] + + // serialize complex values to YAML for extension rendering (avoid inflating memory for scalar values) + if leftValueNode != nil && (utils.IsNodeArray(leftValueNode) || utils.IsNodeMap(leftValueNode)) { + if encoded, err := yaml.Marshal(leftValueNode); err == nil { + c.OriginalEncoded = string(encoded) + } + } + if rightValueNode != nil && (utils.IsNodeArray(rightValueNode) || utils.IsNodeMap(rightValueNode)) { + if encoded, err := yaml.Marshal(rightValueNode); err == nil { + c.NewEncoded = string(encoded) + } + } + + return changes +} + +// CreateContext will return a pointer to a ChangeContext containing the original and new line and column numbers +// of the left and right value nodes. +func CreateContext(l, r *yaml.Node) *ChangeContext { + ctx := new(ChangeContext) + if l != nil { + ctx.OriginalLine = &l.Line + ctx.OriginalColumn = &l.Column + } + if r != nil { + ctx.NewLine = &r.Line + ctx.NewColumn = &r.Column + } + return ctx +} + +func FlattenLowLevelOrderedMap[T any]( + lowMap *orderedmap.Map[low.KeyReference[string], low.ValueReference[T]], +) map[string]*low.ValueReference[T] { + flat := make(map[string]*low.ValueReference[T]) + + for k, l := range lowMap.FromOldest() { + flat[k.Value] = &l + } + return flat +} + +// CountBreakingChanges counts the number of changes in a slice that are breaking +func CountBreakingChanges(changes []*Change) int { + b := 0 + for i := range changes { + if changes[i].Breaking { + b++ + } + } + return b +} + +// checkForObjectAdditionOrRemovalInternal is the internal implementation that handles both encoding modes. +func checkForObjectAdditionOrRemovalInternal[T any](l, r map[string]*low.ValueReference[T], label string, changes *[]*Change, + breakingAdd, breakingRemove bool, withEncoding bool, +) { + createFn := CreateChange + if withEncoding { + createFn = CreateChangeWithEncoding + } + var left, right T + if CheckSpecificObjectRemoved(l, r, label) { + left = l[label].GetValue() + createFn(changes, ObjectRemoved, label, l[label].GetValueNode(), nil, + breakingRemove, left, nil) + } + if CheckSpecificObjectAdded(l, r, label) { + right = r[label].GetValue() + createFn(changes, ObjectAdded, label, nil, r[label].GetValueNode(), + breakingAdd, nil, right) + } +} + +// CheckForObjectAdditionOrRemoval will check for the addition or removal of an object from left and right maps. +// The label is the key to look for in the left and right maps. +// +// To determine this a breaking change for an addition then set breakingAdd to true (however I can't think of many +// scenarios that adding things should break anything). Removals are generally breaking, except for non contract +// properties like descriptions, summaries and other non-binding values, so a breakingRemove value can be tuned for +// these circumstances. +func CheckForObjectAdditionOrRemoval[T any](l, r map[string]*low.ValueReference[T], label string, changes *[]*Change, + breakingAdd, breakingRemove bool, +) { + checkForObjectAdditionOrRemovalInternal(l, r, label, changes, breakingAdd, breakingRemove, false) +} + +// CheckForObjectAdditionOrRemovalWithEncoding is like CheckForObjectAdditionOrRemoval but populates encoded fields. +// Use this for extensions where complex values need to be serialized to YAML. +func CheckForObjectAdditionOrRemovalWithEncoding[T any](l, r map[string]*low.ValueReference[T], label string, changes *[]*Change, + breakingAdd, breakingRemove bool, +) { + checkForObjectAdditionOrRemovalInternal(l, r, label, changes, breakingAdd, breakingRemove, true) +} + +// CheckSpecificObjectRemoved returns true if a specific value is not in both maps. +func CheckSpecificObjectRemoved[T any](l, r map[string]*T, label string) bool { + return l[label] != nil && r[label] == nil +} + +// CheckSpecificObjectAdded returns true if a specific value is not in both maps. +func CheckSpecificObjectAdded[T any](l, r map[string]*T, label string) bool { + return l[label] == nil && r[label] != nil +} + +// CheckProperties will iterate through a slice of PropertyCheck pointers of type T. The method is a convenience method +// for running checks on the following methods in order: +// +// CheckPropertyAdditionOrRemoval +// CheckForModification +// +// When PropertyCheck has Component set, the configurable breaking rules system is used +// to look up the correct breaking value for each change type (added, modified, removed). +func CheckProperties(properties []*PropertyCheck) { + checkPropertiesInternal(properties, false) +} + +// checkPropertiesInternal is the shared implementation for CheckProperties and CheckPropertiesWithEncoding. +// The withEncoding parameter controls whether to use encoding-aware functions for complex YAML values. +func checkPropertiesInternal(properties []*PropertyCheck, withEncoding bool) { + // cache config once outside the loop for performance (avoids repeated mutex operations) + config := GetActiveBreakingRulesConfig() + + for _, n := range properties { + var breakingAdded, breakingModified, breakingRemoved bool + + if n.Component != "" { + // use configurable breaking rules via cached config if rule exists + if rule := config.GetRule(n.Component, n.Property); rule != nil { + // extract breaking values directly from rule (avoids 3 redundant lookups) + breakingAdded = rule.Added != nil && *rule.Added + breakingModified = rule.Modified != nil && *rule.Modified + breakingRemoved = rule.Removed != nil && *rule.Removed + } else { + // no rule found - fallback to legacy Breaking field + breakingAdded = n.Breaking + breakingModified = n.Breaking + breakingRemoved = n.Breaking + } + } else { + // no component set - fallback to legacy Breaking field + breakingAdded = n.Breaking + breakingModified = n.Breaking + breakingRemoved = n.Breaking + } + + // run the checks with the determined breaking values + if withEncoding { + checkForRemovalInternal(n.LeftNode, n.RightNode, n.Label, n.Changes, breakingRemoved, n.Original, n.New, true) + checkForAdditionInternal(n.LeftNode, n.RightNode, n.Label, n.Changes, breakingAdded, n.Original, n.New, true) + checkForModificationInternal(n.LeftNode, n.RightNode, n.Label, n.Changes, breakingModified, n.Original, n.New, true) + } else { + checkForRemovalInternal(n.LeftNode, n.RightNode, n.Label, n.Changes, breakingRemoved, n.Original, n.New, false) + checkForAdditionInternal(n.LeftNode, n.RightNode, n.Label, n.Changes, breakingAdded, n.Original, n.New, false) + checkForModificationInternal(n.LeftNode, n.RightNode, n.Label, n.Changes, breakingModified, n.Original, n.New, false) + } + } +} + +// CheckPropertiesWithEncoding is like CheckProperties but uses CreateChangeWithEncoding for complex values. +// Use this for extensions where YAML serialization is needed. +func CheckPropertiesWithEncoding(properties []*PropertyCheck) { + checkPropertiesInternal(properties, true) +} + +// CheckPropertyAdditionOrRemovalWithEncoding checks for additions and removals with encoding. +func CheckPropertyAdditionOrRemovalWithEncoding[T any](l, r *yaml.Node, + label string, changes *[]*Change, breaking bool, orig, new T, +) { + checkForRemovalInternal(l, r, label, changes, breaking, orig, new, true) + checkForAdditionInternal(l, r, label, changes, breaking, orig, new, true) +} + +// CheckForRemovalWithEncoding checks for removals with YAML encoding. +func CheckForRemovalWithEncoding[T any](l, r *yaml.Node, label string, changes *[]*Change, breaking bool, orig, new T) { + checkForRemovalInternal(l, r, label, changes, breaking, orig, new, true) +} + +// CheckForAdditionWithEncoding checks for additions with YAML encoding. +func CheckForAdditionWithEncoding[T any](l, r *yaml.Node, label string, changes *[]*Change, breaking bool, orig, new T) { + checkForAdditionInternal(l, r, label, changes, breaking, orig, new, true) +} + +// CheckForModificationWithEncoding checks for modifications with YAML encoding. +func CheckForModificationWithEncoding[T any](l, r *yaml.Node, label string, changes *[]*Change, breaking bool, orig, new T) { + checkForModificationInternal(l, r, label, changes, breaking, orig, new, true) +} + +// CheckPropertyAdditionOrRemoval will run both CheckForRemoval (first) and CheckForAddition (second) +func CheckPropertyAdditionOrRemoval[T any](l, r *yaml.Node, + label string, changes *[]*Change, breaking bool, orig, new T, +) { + CheckForRemoval[T](l, r, label, changes, breaking, orig, new) + CheckForAddition[T](l, r, label, changes, breaking, orig, new) +} + +// checkForRemovalInternal is the internal implementation for removal checks with configurable encoding. +func checkForRemovalInternal[T any](l, r *yaml.Node, label string, changes *[]*Change, breaking bool, orig, new T, withEncoding bool) { + createFn := CreateChange + if withEncoding { + createFn = CreateChangeWithEncoding + } + if l != nil && l.Value != EMPTY_STR && (r == nil || r.Value == EMPTY_STR && !utils.IsNodeArray(r) && !utils.IsNodeMap(r)) { + createFn(changes, PropertyRemoved, label, l, r, breaking, orig, new) + return + } + if l != nil && r == nil { + createFn(changes, PropertyRemoved, label, l, nil, breaking, orig, nil) + } +} + +// CheckForRemoval will check left and right yaml.Node instances for changes. Anything that is found missing on the +// right, but present on the left, is considered a removal. A new Change[T] will be created with the type +// +// PropertyRemoved +// +// The Change is then added to the slice of []Change[T] instances provided as a pointer. +func CheckForRemoval[T any](l, r *yaml.Node, label string, changes *[]*Change, breaking bool, orig, new T) { + checkForRemovalInternal(l, r, label, changes, breaking, orig, new, false) +} + +// checkForAdditionInternal is the internal implementation for addition checks with configurable encoding. +func checkForAdditionInternal[T any](l, r *yaml.Node, label string, changes *[]*Change, breaking bool, orig, new T, withEncoding bool) { + createFn := CreateChange + if withEncoding { + createFn = CreateChangeWithEncoding + } + // left doesn't exist if: nil OR (empty scalar AND not a map/array) OR (empty map/array) + leftDoesNotExist := l == nil || + (l.Value == EMPTY_STR && !utils.IsNodeMap(l) && !utils.IsNodeArray(l)) || + ((utils.IsNodeMap(l) || utils.IsNodeArray(l)) && len(l.Content) == 0) + // right exists if: not nil AND (has value OR is array OR is map) + rightExists := r != nil && (r.Value != EMPTY_STR || utils.IsNodeArray(r) || utils.IsNodeMap(r)) + + if leftDoesNotExist && rightExists { + createFn(changes, PropertyAdded, label, l, r, breaking, orig, new) + } +} + +// CheckForAddition will check left and right yaml.Node instances for changes. Anything that is found missing on the +// left, but present on the right, is considered an addition. A new Change[T] will be created with the type +// +// PropertyAdded +// +// The Change is then added to the slice of []Change[T] instances provided as a pointer. +func CheckForAddition[T any](l, r *yaml.Node, label string, changes *[]*Change, breaking bool, orig, new T) { + checkForAdditionInternal(l, r, label, changes, breaking, orig, new, false) +} + +// checkForModificationInternal is the internal implementation for modification checks with configurable encoding. +func checkForModificationInternal[T any](l, r *yaml.Node, label string, changes *[]*Change, breaking bool, orig, new T, withEncoding bool) { + createFn := CreateChange + if withEncoding { + createFn = CreateChangeWithEncoding + } + if l != nil && l.Value != EMPTY_STR && r != nil && r.Value != EMPTY_STR && (r.Value != l.Value || r.Tag != l.Tag) { + createFn(changes, Modified, label, l, r, breaking, orig, new) + return + } + if l != nil && utils.IsNodeArray(l) && r != nil && !utils.IsNodeArray(r) { + createFn(changes, Modified, label, l, r, breaking, orig, new) + return + } + if l != nil && !utils.IsNodeArray(l) && r != nil && utils.IsNodeArray(r) { + createFn(changes, Modified, label, l, r, breaking, orig, new) + return + } + if l != nil && utils.IsNodeMap(l) && r != nil && !utils.IsNodeMap(r) { + createFn(changes, Modified, label, l, r, breaking, orig, new) + return + } + if l != nil && !utils.IsNodeMap(l) && r != nil && utils.IsNodeMap(r) { + createFn(changes, Modified, label, l, r, breaking, orig, new) + return + } + if l != nil && utils.IsNodeArray(l) && r != nil && utils.IsNodeArray(r) { + if len(l.Content) != len(r.Content) { + createFn(changes, Modified, label, l, r, breaking, orig, new) + return + } + + // Compare the YAML node trees directly without marshaling + if !low.CompareYAMLNodes(l, r) { + createFn(changes, Modified, label, l, r, breaking, orig, new) + } + return + } + if l != nil && utils.IsNodeMap(l) && r != nil && utils.IsNodeMap(r) { + // Compare the YAML node trees directly without marshaling + if !low.CompareYAMLNodes(l, r) { + createFn(changes, Modified, label, l, r, breaking, orig, new) + } + return + } +} + +// CheckForModification will check left and right yaml.Node instances for changes. Anything that is found in both +// sides, but vary in value is considered a modification. +// +// If there is a change in value the function adds a change type of Modified. +// +// The Change is then added to the slice of []Change[T] instances provided as a pointer. +func CheckForModification[T any](l, r *yaml.Node, label string, changes *[]*Change, breaking bool, orig, new T) { + checkForModificationInternal(l, r, label, changes, breaking, orig, new, false) +} + +// CheckMapForChanges checks a left and right low level map for any additions, subtractions or modifications to +// values. The compareFunc argument should reference the correct comparison function for the generic type. +// Uses original hardcoded breaking behavior (removals breaking, additions non-breaking). +func CheckMapForChanges[T any, R any](expLeft, expRight *orderedmap.Map[low.KeyReference[string], low.ValueReference[T]], + changes *[]*Change, label string, compareFunc func(l, r T) R, +) map[string]R { + return checkMapForChangesInternal(expLeft, expRight, changes, label, compareFunc, true, false, true) +} + +// CheckMapForChangesWithRules checks a left and right low level map for any additions, subtractions or modifications +// to values, using the configurable breaking rules system for the specified component and property. +func CheckMapForChangesWithRules[T any, R any](expLeft, expRight *orderedmap.Map[low.KeyReference[string], low.ValueReference[T]], + changes *[]*Change, label string, compareFunc func(l, r T) R, component, property string, +) map[string]R { + return checkMapForChangesInternal(expLeft, expRight, changes, label, compareFunc, true, + BreakingAdded(component, property), BreakingRemoved(component, property)) +} + +// CheckMapForAdditionRemoval checks a left and right low level map for any additions or subtractions, but not modifications +func CheckMapForAdditionRemoval[T any](expLeft, expRight *orderedmap.Map[low.KeyReference[string], low.ValueReference[T]], + changes *[]*Change, label string, +) any { + doNothing := func(l, r T) any { + return nil + } + // adding purely to make sure code is called for coverage. + var l, r T + doNothing(l, r) + return checkMapForChangesInternal(expLeft, expRight, changes, label, doNothing, false, false, true) +} + +// CheckMapForChangesWithComp checks a left and right low level map for any additions, subtractions or modifications to +// values. The compareFunc argument should reference the correct comparison function for the generic type. The compare +// bit determines if the comparison should be run or not. +// Deprecated: Use checkMapForChangesInternal with explicit breaking parameters instead. +func CheckMapForChangesWithComp[T any, R any](expLeft, expRight *orderedmap.Map[low.KeyReference[string], low.ValueReference[T]], + changes *[]*Change, label string, compareFunc func(l, r T) R, compare bool, +) map[string]R { + return checkMapForChangesInternal(expLeft, expRight, changes, label, compareFunc, compare, false, true) +} + +// CheckMapForChangesWithNilSupport checks a left and right low level map for any additions, subtractions or modifications. +// Unlike CheckMapForChanges, this function calls compareFunc for added/removed items by passing nil for the missing side. +// The compareFunc MUST handle nil inputs gracefully (return appropriate changes for added/removed cases). +// This allows the returned map to include entries for added/removed items, enabling proper tree rendering. +func CheckMapForChangesWithNilSupport[T any, R any](expLeft, expRight *orderedmap.Map[low.KeyReference[string], low.ValueReference[T]], + changes *[]*Change, label string, compareFunc func(l, r T) R, +) map[string]R { + return checkMapForChangesWithNilSupportInternal(expLeft, expRight, changes, label, compareFunc, false, true) +} + +// checkMapForChangesWithNilSupportInternal is the core implementation that calls compareFunc with nil for added/removed items. +func checkMapForChangesWithNilSupportInternal[T any, R any](expLeft, expRight *orderedmap.Map[low.KeyReference[string], low.ValueReference[T]], + changes *[]*Change, label string, compareFunc func(l, r T) R, + breakingAdded, breakingRemoved bool, +) map[string]R { + var chLock sync.Mutex + + lHashes := make(map[string]string) + rHashes := make(map[string]string) + lValues := make(map[string]low.ValueReference[T]) + rValues := make(map[string]low.ValueReference[T]) + + if expLeft != nil { + for k, v := range expLeft.FromOldest() { + lHashes[k.Value] = low.GenerateHashString(v.Value) + lValues[k.Value] = v + } + } + + if expRight != nil { + for k, v := range expRight.FromOldest() { + rHashes[k.Value] = low.GenerateHashString(v.Value) + rValues[k.Value] = v + } + } + + expChanges := make(map[string]R) + + checkLeft := func(k string, doneChan chan struct{}, f, g map[string]string, p, h map[string]low.ValueReference[T]) { + rhash := g[k] + if rhash == EMPTY_STR { + // Item was removed - call compareFunc with nil/zero right side + chLock.Lock() + var zero T + ch := compareFunc(p[k].Value, zero) + if !reflect.ValueOf(&ch).Elem().IsZero() { + expChanges[k] = ch + var cr any = ch + pVal := p[k] + SetReferenceIfExists(&pVal, cr) + } + chLock.Unlock() + doneChan <- struct{}{} + return + } + if f[k] == g[k] { + doneChan <- struct{}{} + return + } + // Item was modified + chLock.Lock() + ch := compareFunc(p[k].Value, h[k].Value) + if !reflect.ValueOf(&ch).Elem().IsZero() { + expChanges[k] = ch + var cr any = ch + pVal := p[k] + SetReferenceIfExists(&pVal, cr) + } + chLock.Unlock() + doneChan <- struct{}{} + } + + checkRight := func(k string, doneChan chan struct{}, f map[string]string, p map[string]low.ValueReference[T]) { + lhash := f[k] + if lhash == EMPTY_STR { + // Item was added - call compareFunc with nil/zero left side + chLock.Lock() + var zero T + ch := compareFunc(zero, p[k].Value) + if !reflect.ValueOf(&ch).Elem().IsZero() { + expChanges[k] = ch + var cr any = ch + pVal := p[k] + SetReferenceIfExists(&pVal, cr) + } + chLock.Unlock() + } + doneChan <- struct{}{} + } + + doneChan := make(chan struct{}) + count := 0 + + for k := range lHashes { + count++ + go checkLeft(k, doneChan, lHashes, rHashes, lValues, rValues) + } + + for k := range rHashes { + count++ + go checkRight(k, doneChan, lHashes, rValues) + } + + completed := 0 + for completed < count { + <-doneChan + completed++ + } + return expChanges +} + +// checkMapForChangesInternal is the core implementation that checks a left and right low level map for any +// additions, subtractions or modifications to values. The breakingAdded and breakingRemoved parameters control +// whether additions and removals are marked as breaking changes. +func checkMapForChangesInternal[T any, R any](expLeft, expRight *orderedmap.Map[low.KeyReference[string], low.ValueReference[T]], + changes *[]*Change, label string, compareFunc func(l, r T) R, compare bool, + breakingAdded, breakingRemoved bool, +) map[string]R { + var chLock sync.Mutex + + lHashes := make(map[string]string) + rHashes := make(map[string]string) + lValues := make(map[string]low.ValueReference[T]) + rValues := make(map[string]low.ValueReference[T]) + + if expLeft != nil { + for k, v := range expLeft.FromOldest() { + lHashes[k.Value] = low.GenerateHashString(v.Value) + lValues[k.Value] = v + } + } + + if expRight != nil { + for k, v := range expRight.FromOldest() { + rHashes[k.Value] = low.GenerateHashString(v.Value) + rValues[k.Value] = v + } + } + + expChanges := make(map[string]R) + + checkLeft := func(k string, doneChan chan struct{}, f, g map[string]string, p, h map[string]low.ValueReference[T]) { + rhash := g[k] + if rhash == EMPTY_STR { + chLock.Lock() + if p[k].GetValueNode().Value == EMPTY_STR { + p[k].GetValueNode().Value = k + } + CreateChange(changes, ObjectRemoved, label, + p[k].GetValueNode(), nil, breakingRemoved, + p[k].GetValue(), nil) + chLock.Unlock() + doneChan <- struct{}{} + return + } + if f[k] == g[k] { + doneChan <- struct{}{} + return + } + if compare { + chLock.Lock() + ch := compareFunc(p[k].Value, h[k].Value) + // incorrect map results were being generated causing panics. + // https://github.com/pb33f/libopenapi/issues/61 + if !reflect.ValueOf(&ch).Elem().IsZero() { + expChanges[k] = ch + var cr any = ch + pVal := p[k] + SetReferenceIfExists(&pVal, cr) + } + chLock.Unlock() + } + doneChan <- struct{}{} + } + + checkRight := func(k string, doneChan chan struct{}, f map[string]string, p map[string]low.ValueReference[T]) { + lhash := f[k] + if lhash == EMPTY_STR { + chLock.Lock() + if p[k].GetValueNode().Value == EMPTY_STR { + p[k].GetValueNode().Value = k + } + CreateChange(changes, ObjectAdded, label, + nil, p[k].GetValueNode(), breakingAdded, + nil, p[k].GetValue()) + chLock.Unlock() + } + doneChan <- struct{}{} + } + + doneChan := make(chan struct{}) + count := 0 + + for k := range lHashes { + count++ + go checkLeft(k, doneChan, lHashes, rHashes, lValues, rValues) + } + + for k := range rHashes { + count++ + go checkRight(k, doneChan, lHashes, rValues) + } + + completed := 0 + for completed < count { + <-doneChan + completed++ + } + return expChanges +} + +// ExtractStringValueSliceChanges will compare two low level string slices for changes. +// The breaking parameter is deprecated - use ExtractStringValueSliceChangesWithRules instead. +func ExtractStringValueSliceChanges(lParam, rParam []low.ValueReference[string], + changes *[]*Change, label string, breaking bool, +) { + lKeys := make([]string, len(lParam)) + rKeys := make([]string, len(rParam)) + lValues := make(map[string]low.ValueReference[string]) + rValues := make(map[string]low.ValueReference[string]) + for i := range lParam { + lKeys[i] = strings.ToLower(lParam[i].Value) + lValues[lKeys[i]] = lParam[i] + } + for i := range rParam { + rKeys[i] = strings.ToLower(rParam[i].Value) + rValues[rKeys[i]] = rParam[i] + } + for i := range lValues { + if _, ok := rValues[i]; !ok { + CreateChange(changes, PropertyRemoved, label, + lValues[i].ValueNode, + nil, + breaking, + lValues[i].Value, + nil) + } + } + for i := range rValues { + if _, ok := lValues[i]; !ok { + CreateChange(changes, PropertyAdded, label, + nil, + rValues[i].ValueNode, + false, + nil, + rValues[i].Value) + } + } +} + +// ExtractStringValueSliceChangesWithRules compares two low level string slices for changes, +// using the configurable breaking rules system to determine breaking status. +func ExtractStringValueSliceChangesWithRules(lParam, rParam []low.ValueReference[string], + changes *[]*Change, label string, component, property string, +) { + lKeys := make([]string, len(lParam)) + rKeys := make([]string, len(rParam)) + lValues := make(map[string]low.ValueReference[string]) + rValues := make(map[string]low.ValueReference[string]) + for i := range lParam { + lKeys[i] = strings.ToLower(lParam[i].Value) + lValues[lKeys[i]] = lParam[i] + } + for i := range rParam { + rKeys[i] = strings.ToLower(rParam[i].Value) + rValues[rKeys[i]] = rParam[i] + } + for i := range lValues { + if _, ok := rValues[i]; !ok { + CreateChange(changes, PropertyRemoved, label, + lValues[i].ValueNode, + nil, + BreakingRemoved(component, property), + lValues[i].Value, + nil) + } + } + for i := range rValues { + if _, ok := lValues[i]; !ok { + CreateChange(changes, PropertyAdded, label, + nil, + rValues[i].ValueNode, + BreakingAdded(component, property), + nil, + rValues[i].Value) + } + } +} + +func toString(v any) string { + if y, ok := v.(*yaml.Node); ok { + copy := *y + _ = copy.Encode(©) + return fmt.Sprint(copy) + } + + return fmt.Sprint(v) +} + +// ExtractRawValueSliceChanges will compare two low level interface{} slices for changes. +func ExtractRawValueSliceChanges[T any](lParam, rParam []low.ValueReference[T], + changes *[]*Change, label string, breaking bool, +) { + lKeys := make([]string, len(lParam)) + rKeys := make([]string, len(rParam)) + lValues := make(map[string]low.ValueReference[T]) + rValues := make(map[string]low.ValueReference[T]) + for i := range lParam { + lKeys[i] = strings.ToLower(toString(lParam[i].Value)) + lValues[lKeys[i]] = lParam[i] + } + for i := range rParam { + rKeys[i] = strings.ToLower(toString(rParam[i].Value)) + rValues[rKeys[i]] = rParam[i] + } + for i := range lValues { + if _, ok := rValues[i]; !ok { + CreateChange(changes, PropertyRemoved, label, + lValues[i].ValueNode, + nil, + breaking, + lValues[i].Value, + nil) + } + } + for i := range rValues { + if _, ok := lValues[i]; !ok { + CreateChange(changes, PropertyAdded, label, + nil, + rValues[i].ValueNode, + false, + nil, + rValues[i].Value) + } + } +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/components.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/components.go new file mode 100644 index 000000000..f9403a0c1 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/components.go @@ -0,0 +1,286 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "reflect" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + v2 "github.com/pb33f/libopenapi/datamodel/low/v2" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/orderedmap" +) + +// ComponentsChanges represents changes made to both OpenAPI and Swagger documents. This model is based on OpenAPI 3 +// components, however it's also used to contain Swagger definitions changes. Swagger for some reason decided to not +// contain definitions inside a single parent like Components, and instead scattered them across the root of the +// Swagger document, giving everything a `Definitions` postfix. This design attempts to unify those models into +// a single entity that contains all changes. +// +// Schemas are treated differently from every other component / definition in this library. Schemas can be highly +// recursive, and are not resolved by the model, every ref is recorded, but it's not looked at essentially. This means +// that when what-changed performs a check, everything that is *not* a schema is checked *inline*, Those references are +// resolved in place and a change is recorded in place. Schemas however are *not* resolved. which means no change +// will be recorded in place for any object referencing it. +// +// That is why there is a separate SchemaChanges object in ComponentsChanges. Schemas are checked at the source, and +// not inline when referenced. A schema change will only be found once, however a change to ANY other definition or +// component, will be found inline (and will duplicate for every use). +// +// The other oddity here is SecuritySchemes. For some reason OpenAPI does not use a $ref for these entities, it +// uses a name lookup, which means there are no direct links between any model and a security scheme reference. +// So like Schemas, SecuritySchemes are treated differently and handled individually. +// +// An important note: Everything EXCEPT Schemas and SecuritySchemes is ONLY checked for additions or removals. +// modifications are not checked, these checks occur in-place by implementing objects as they are autp-resolved +// when the model is built. +type ComponentsChanges struct { + *PropertyChanges + SchemaChanges map[string]*SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"` + SecuritySchemeChanges map[string]*SecuritySchemeChanges `json:"securitySchemes,omitempty" yaml:"securitySchemes,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` +} + +// CompareComponents will compare OpenAPI components for any changes. Accepts Swagger Definition objects +// like ParameterDefinitions or Definitions etc. +func CompareComponents(l, r any) *ComponentsChanges { + var changes []*Change + + cc := new(ComponentsChanges) + + // Swagger Parameters + if reflect.TypeOf(&v2.ParameterDefinitions{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v2.ParameterDefinitions{}) == reflect.TypeOf(r) { + lDef := l.(*v2.ParameterDefinitions) + rDef := r.(*v2.ParameterDefinitions) + var a, b *orderedmap.Map[low.KeyReference[string], low.ValueReference[*v2.Parameter]] + if lDef != nil { + a = lDef.Definitions + } + if rDef != nil { + b = rDef.Definitions + } + CheckMapForAdditionRemoval(a, b, &changes, v3.ParametersLabel) + } + + // Swagger Responses + if reflect.TypeOf(&v2.ResponsesDefinitions{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v2.ResponsesDefinitions{}) == reflect.TypeOf(r) { + lDef := l.(*v2.ResponsesDefinitions) + rDef := r.(*v2.ResponsesDefinitions) + var a, b *orderedmap.Map[low.KeyReference[string], low.ValueReference[*v2.Response]] + if lDef != nil { + a = lDef.Definitions + } + if rDef != nil { + b = rDef.Definitions + } + CheckMapForAdditionRemoval(a, b, &changes, v3.ResponsesLabel) + } + + // Swagger Schemas + if reflect.TypeOf(&v2.Definitions{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v2.Definitions{}) == reflect.TypeOf(r) { + lDef := l.(*v2.Definitions) + rDef := r.(*v2.Definitions) + var a, b *orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.SchemaProxy]] + if lDef != nil { + a = lDef.Schemas + } + if rDef != nil { + b = rDef.Schemas + } + cc.SchemaChanges = CheckMapForChanges(a, b, &changes, v2.DefinitionsLabel, CompareSchemas) + } + + // Swagger Security Definitions + if reflect.TypeOf(&v2.SecurityDefinitions{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v2.SecurityDefinitions{}) == reflect.TypeOf(r) { + lDef := l.(*v2.SecurityDefinitions) + rDef := r.(*v2.SecurityDefinitions) + var a, b *orderedmap.Map[low.KeyReference[string], low.ValueReference[*v2.SecurityScheme]] + if lDef != nil { + a = lDef.Definitions + } + if rDef != nil { + b = rDef.Definitions + } + cc.SecuritySchemeChanges = CheckMapForChanges(a, b, &changes, + v3.SecurityDefinitionLabel, CompareSecuritySchemesV2) + } + + // OpenAPI Components + if reflect.TypeOf(&v3.Components{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v3.Components{}) == reflect.TypeOf(r) { + + lComponents := l.(*v3.Components) + rComponents := r.(*v3.Components) + + //if low.AreEqual(lComponents, rComponents) { + // return nil + //} + + doneChan := make(chan componentComparison) + comparisons := 0 + + // run as fast as we can, thread all the things. + if !lComponents.Schemas.IsEmpty() || !rComponents.Schemas.IsEmpty() { + comparisons++ + go runComparison(lComponents.Schemas.Value, rComponents.Schemas.Value, + &changes, v3.SchemasLabel, CompareSchemas, doneChan) + } + + if !lComponents.Responses.IsEmpty() || !rComponents.Responses.IsEmpty() { + comparisons++ + go runComparison(lComponents.Responses.Value, rComponents.Responses.Value, + &changes, v3.ResponsesLabel, CompareResponseV3, doneChan) + } + + if !lComponents.Parameters.IsEmpty() || !rComponents.Parameters.IsEmpty() { + comparisons++ + go runComparison(lComponents.Parameters.Value, rComponents.Parameters.Value, + &changes, v3.ParametersLabel, CompareParametersV3, doneChan) + } + + if !lComponents.Examples.IsEmpty() || !rComponents.Examples.IsEmpty() { + comparisons++ + go runComparison(lComponents.Examples.Value, rComponents.Examples.Value, + &changes, v3.ExamplesLabel, CompareExamples, doneChan) + } + + if !lComponents.RequestBodies.IsEmpty() || !rComponents.RequestBodies.IsEmpty() { + comparisons++ + go runComparison(lComponents.RequestBodies.Value, rComponents.RequestBodies.Value, + &changes, v3.RequestBodiesLabel, CompareRequestBodies, doneChan) + } + + if !lComponents.Headers.IsEmpty() || !rComponents.Headers.IsEmpty() { + comparisons++ + go runComparison(lComponents.Headers.Value, rComponents.Headers.Value, + &changes, v3.HeadersLabel, CompareHeadersV3, doneChan) + } + + if !lComponents.SecuritySchemes.IsEmpty() || !rComponents.SecuritySchemes.IsEmpty() { + comparisons++ + go runComparison(lComponents.SecuritySchemes.Value, rComponents.SecuritySchemes.Value, + &changes, v3.SecuritySchemesLabel, CompareSecuritySchemesV3, doneChan) + } + + if !lComponents.Links.IsEmpty() || !rComponents.Links.IsEmpty() { + comparisons++ + go runComparison(lComponents.Links.Value, rComponents.Links.Value, + &changes, v3.LinksLabel, CompareLinks, doneChan) + } + + if !lComponents.Callbacks.IsEmpty() || !rComponents.Callbacks.IsEmpty() { + comparisons++ + go runComparison(lComponents.Callbacks.Value, rComponents.Callbacks.Value, + &changes, v3.CallbacksLabel, CompareCallback, doneChan) + } + + if !lComponents.MediaTypes.IsEmpty() || !rComponents.MediaTypes.IsEmpty() { + comparisons++ + go runComparison(lComponents.MediaTypes.Value, rComponents.MediaTypes.Value, + &changes, v3.MediaTypesLabel, CompareMediaTypes, doneChan) + } + + cc.ExtensionChanges = CompareExtensions(lComponents.Extensions, rComponents.Extensions) + + completedComponents := 0 + for completedComponents < comparisons { + res := <-doneChan + switch res.prop { + case v3.SchemasLabel: + completedComponents++ + cc.SchemaChanges = res.result.(map[string]*SchemaChanges) + case v3.SecuritySchemesLabel: + completedComponents++ + cc.SecuritySchemeChanges = res.result.(map[string]*SecuritySchemeChanges) + case v3.ResponsesLabel, v3.ParametersLabel, v3.ExamplesLabel, v3.RequestBodiesLabel, v3.HeadersLabel, + v3.LinksLabel, v3.CallbacksLabel, v3.MediaTypesLabel: + completedComponents++ + } + } + } + + cc.PropertyChanges = NewPropertyChanges(changes) + if cc.TotalChanges() <= 0 { + return nil + } + return cc +} + +type componentComparison struct { + prop string + result any +} + +// run a generic comparison in a thread which in turn splits checks into further threads. +func runComparison[T any, R any](l, r *orderedmap.Map[low.KeyReference[string], low.ValueReference[T]], + changes *[]*Change, label string, compareFunc func(l, r T) R, doneChan chan componentComparison, +) { + // for schemas + if label == v3.SchemasLabel || label == v2.DefinitionsLabel || label == v3.SecuritySchemesLabel { + doneChan <- componentComparison{ + prop: label, + result: CheckMapForChanges(l, r, changes, label, compareFunc), + } + return + } else { + doneChan <- componentComparison{ + prop: label, + result: CheckMapForAdditionRemoval(l, r, changes, label), + } + } +} + +// GetAllChanges returns a slice of all changes made between Callback objects +func (c *ComponentsChanges) GetAllChanges() []*Change { + if c == nil { + return nil + } + var changes []*Change + changes = append(changes, c.Changes...) + for k := range c.SchemaChanges { + changes = append(changes, c.SchemaChanges[k].GetAllChanges()...) + } + for k := range c.SecuritySchemeChanges { + changes = append(changes, c.SecuritySchemeChanges[k].GetAllChanges()...) + } + if c.ExtensionChanges != nil { + changes = append(changes, c.ExtensionChanges.GetAllChanges()...) + } + return changes +} + +// TotalChanges returns total changes for all Components and Definitions +func (c *ComponentsChanges) TotalChanges() int { + if c == nil { + return 0 + } + v := c.PropertyChanges.TotalChanges() + for k := range c.SchemaChanges { + v += c.SchemaChanges[k].TotalChanges() + } + for k := range c.SecuritySchemeChanges { + v += c.SecuritySchemeChanges[k].TotalChanges() + } + if c.ExtensionChanges != nil { + v += c.ExtensionChanges.TotalChanges() + } + return v +} + +// TotalBreakingChanges returns all breaking changes found for all Components and Definitions +func (c *ComponentsChanges) TotalBreakingChanges() int { + v := c.PropertyChanges.TotalBreakingChanges() + for k := range c.SchemaChanges { + v += c.SchemaChanges[k].TotalBreakingChanges() + } + for k := range c.SecuritySchemeChanges { + v += c.SecuritySchemeChanges[k].TotalBreakingChanges() + } + return v +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/contact.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/contact.go new file mode 100644 index 000000000..adcde04a1 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/contact.go @@ -0,0 +1,67 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +// ContactChanges Represent changes to a Contact object that is a child of Info, part of an OpenAPI document. +type ContactChanges struct { + *PropertyChanges +} + +// GetAllChanges returns a slice of all changes made between Callback objects +func (c *ContactChanges) GetAllChanges() []*Change { + if c == nil { + return nil + } + return c.Changes +} + +// TotalChanges represents the total number of changes that have occurred to a Contact object +func (c *ContactChanges) TotalChanges() int { + if c == nil { + return 0 + } + return c.PropertyChanges.TotalChanges() +} + +// TotalBreakingChanges returns the total number of breaking changes in Contact objects. +func (c *ContactChanges) TotalBreakingChanges() int { + if c == nil { + return 0 + } + return c.PropertyChanges.TotalBreakingChanges() +} + +// CompareContact will check a left (original) and right (new) Contact object for any changes. If there +// were any, a pointer to a ContactChanges object is returned, otherwise if nothing changed - the function +// returns nil. +func CompareContact(l, r *base.Contact) *ContactChanges { + var changes []*Change + props := make([]*PropertyCheck, 0, 3) + + props = append(props, + NewPropertyCheck(CompContact, PropURL, + l.URL.ValueNode, r.URL.ValueNode, + v3.URLLabel, &changes, l, r), + NewPropertyCheck(CompContact, PropName, + l.Name.ValueNode, r.Name.ValueNode, + v3.NameLabel, &changes, l, r), + NewPropertyCheck(CompContact, PropEmail, + l.Email.ValueNode, r.Email.ValueNode, + v3.EmailLabel, &changes, l, r), + ) + + CheckProperties(props) + + dc := new(ContactChanges) + dc.PropertyChanges = NewPropertyChanges(changes) + if dc.TotalChanges() <= 0 { + return nil + } + return dc +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/discriminator.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/discriminator.go new file mode 100644 index 000000000..aaf3fb37f --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/discriminator.go @@ -0,0 +1,97 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "github.com/pb33f/libopenapi/datamodel/low/base" +) + +// DiscriminatorChanges represents changes made to a Discriminator OpenAPI object +type DiscriminatorChanges struct { + *PropertyChanges + MappingChanges []*Change `json:"mappings,omitempty" yaml:"mappings,omitempty"` +} + +// TotalChanges returns a count of everything changed within the Discriminator object +func (d *DiscriminatorChanges) TotalChanges() int { + if d == nil { + return 0 + } + l := 0 + if k := d.PropertyChanges.TotalChanges(); k > 0 { + l += k + } + if k := len(d.MappingChanges); k > 0 { + l += k + } + return l +} + +// GetAllChanges returns a slice of all changes made between Callback objects +func (c *DiscriminatorChanges) GetAllChanges() []*Change { + if c == nil { + return nil + } + var changes []*Change + changes = append(changes, c.Changes...) + if c.MappingChanges != nil { + changes = append(changes, c.MappingChanges...) + } + return changes +} + +// TotalBreakingChanges returns the number of breaking changes made by the Discriminator +func (d *DiscriminatorChanges) TotalBreakingChanges() int { + return d.PropertyChanges.TotalBreakingChanges() + CountBreakingChanges(d.MappingChanges) +} + +// CompareDiscriminator will check a left (original) and right (new) Discriminator object for changes +// and will return a pointer to DiscriminatorChanges +func CompareDiscriminator(l, r *base.Discriminator) *DiscriminatorChanges { + dc := new(DiscriminatorChanges) + var changes []*Change + props := make([]*PropertyCheck, 0, 2) + var mappingChanges []*Change + + props = append(props, + NewPropertyCheck(CompDiscriminator, PropPropertyName, + l.PropertyName.ValueNode, r.PropertyName.ValueNode, + base.PropertyNameLabel, &changes, l, r), + NewPropertyCheck(CompDiscriminator, PropDefaultMapping, + l.DefaultMapping.ValueNode, r.DefaultMapping.ValueNode, + base.DefaultMappingLabel, &changes, l, r), + ) + + CheckProperties(props) + + // flatten maps + lMap := FlattenLowLevelOrderedMap[string](l.Mapping.Value) + rMap := FlattenLowLevelOrderedMap[string](r.Mapping.Value) + + // check for removals, modifications and moves + for i := range lMap { + CheckForObjectAdditionOrRemoval[string](lMap, rMap, i, &mappingChanges, BreakingAdded(CompDiscriminator, PropMapping), BreakingRemoved(CompDiscriminator, PropMapping)) + // if the existing tag exists, let's check it. + if rMap[i] != nil { + if lMap[i].Value != rMap[i].Value { + CreateChange(&mappingChanges, Modified, i, lMap[i].GetValueNode(), + rMap[i].GetValueNode(), BreakingModified(CompDiscriminator, PropMapping), lMap[i].GetValue(), rMap[i].GetValue()) + } + } + } + + for i := range rMap { + if lMap[i] == nil { + CreateChange(&mappingChanges, ObjectAdded, i, nil, + rMap[i].GetValueNode(), BreakingAdded(CompDiscriminator, PropMapping), nil, rMap[i].GetValue()) + } + } + + dc.PropertyChanges = NewPropertyChanges(changes) + dc.MappingChanges = mappingChanges + if dc.TotalChanges() <= 0 { + return nil + } + return dc +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/document.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/document.go new file mode 100644 index 000000000..5c9263c10 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/document.go @@ -0,0 +1,349 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +// Package model +// +// What-changed models are unified across OpenAPI and Swagger. Everything is kept flat for simplicity, so please +// excuse the size of the package. There is a lot of data to crunch! +// +// Every model in here is either universal (works across both versions of OpenAPI) or is bound to a specific version +// of OpenAPI. There is only a single model however - so version specific objects are marked accordingly. +package model + +import ( + "reflect" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + v2 "github.com/pb33f/libopenapi/datamodel/low/v2" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +// DocumentChanges represents all the changes made to an OpenAPI document. +type DocumentChanges struct { + *PropertyChanges + InfoChanges *InfoChanges `json:"info,omitempty" yaml:"info,omitempty"` + PathsChanges *PathsChanges `json:"paths,omitempty" yaml:"paths,omitempty"` + TagChanges []*TagChanges `json:"tags,omitempty" yaml:"tags,omitempty"` + ExternalDocChanges *ExternalDocChanges `json:"externalDoc,omitempty" yaml:"externalDoc,omitempty"` + WebhookChanges map[string]*PathItemChanges `json:"webhooks,omitempty" yaml:"webhooks,omitempty"` + ServerChanges []*ServerChanges `json:"servers,omitempty" yaml:"servers,omitempty"` + SecurityRequirementChanges []*SecurityRequirementChanges `json:"securityRequirements,omitempty" yaml:"securityRequirements,omitempty"` + ComponentsChanges *ComponentsChanges `json:"components,omitempty" yaml:"components,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` +} + +// TotalChanges returns a total count of all changes made in the Document +func (d *DocumentChanges) TotalChanges() int { + if d == nil { + return 0 + } + + c := d.PropertyChanges.TotalChanges() + if d.InfoChanges != nil { + c += d.InfoChanges.TotalChanges() + } + if d.PathsChanges != nil { + c += d.PathsChanges.TotalChanges() + } + for k := range d.TagChanges { + c += d.TagChanges[k].TotalChanges() + } + if d.ExternalDocChanges != nil { + c += d.ExternalDocChanges.TotalChanges() + } + for k := range d.WebhookChanges { + c += d.WebhookChanges[k].TotalChanges() + } + for k := range d.ServerChanges { + c += d.ServerChanges[k].TotalChanges() + } + for k := range d.SecurityRequirementChanges { + c += d.SecurityRequirementChanges[k].TotalChanges() + } + if d.ComponentsChanges != nil { + c += d.ComponentsChanges.TotalChanges() + } + if d.ExtensionChanges != nil { + c += d.ExtensionChanges.TotalChanges() + } + return c +} + +// GetAllChanges returns a slice of all changes made between Document objects +func (d *DocumentChanges) GetAllChanges() []*Change { + if d == nil { + return nil + } + + var changes []*Change + changes = append(changes, d.Changes...) + if d.InfoChanges != nil { + changes = append(changes, d.InfoChanges.GetAllChanges()...) + } + if d.PathsChanges != nil { + changes = append(changes, d.PathsChanges.GetAllChanges()...) + } + for k := range d.TagChanges { + changes = append(changes, d.TagChanges[k].GetAllChanges()...) + } + if d.ExternalDocChanges != nil { + changes = append(changes, d.ExternalDocChanges.GetAllChanges()...) + } + for k := range d.WebhookChanges { + changes = append(changes, d.WebhookChanges[k].GetAllChanges()...) + } + for k := range d.ServerChanges { + changes = append(changes, d.ServerChanges[k].GetAllChanges()...) + } + for k := range d.SecurityRequirementChanges { + changes = append(changes, d.SecurityRequirementChanges[k].GetAllChanges()...) + } + if d.ComponentsChanges != nil { + changes = append(changes, d.ComponentsChanges.GetAllChanges()...) + } + if d.ExtensionChanges != nil { + changes = append(changes, d.ExtensionChanges.GetAllChanges()...) + } + return changes +} + +// TotalBreakingChanges returns a total count of all breaking changes made in the Document +func (d *DocumentChanges) TotalBreakingChanges() int { + if d == nil { + return 0 + } + + c := d.PropertyChanges.TotalBreakingChanges() + if d.InfoChanges != nil { + c += d.InfoChanges.TotalBreakingChanges() + } + if d.PathsChanges != nil { + c += d.PathsChanges.TotalBreakingChanges() + } + for k := range d.TagChanges { + c += d.TagChanges[k].TotalBreakingChanges() + } + if d.ExternalDocChanges != nil { + c += d.ExternalDocChanges.TotalBreakingChanges() + } + for k := range d.WebhookChanges { + c += d.WebhookChanges[k].TotalBreakingChanges() + } + for k := range d.ServerChanges { + c += d.ServerChanges[k].TotalBreakingChanges() + } + for k := range d.SecurityRequirementChanges { + c += d.SecurityRequirementChanges[k].TotalBreakingChanges() + } + if d.ComponentsChanges != nil { + c += d.ComponentsChanges.TotalBreakingChanges() + } + return c +} + +// CompareDocuments will compare any two OpenAPI documents (either Swagger or OpenAPI) and return a pointer to +// DocumentChanges that outlines everything that was found to have changed. +func CompareDocuments(l, r any) *DocumentChanges { + var changes []*Change + var props []*PropertyCheck + + dc := new(DocumentChanges) + + // reset schema hashmap + base.SchemaQuickHashMap.Clear() + + // clear hash cache to ensure clean state for comparison + low.ClearHashCache() + + if reflect.TypeOf(&v2.Swagger{}) == reflect.TypeOf(l) && reflect.TypeOf(&v2.Swagger{}) == reflect.TypeOf(r) { + lDoc := l.(*v2.Swagger) + rDoc := r.(*v2.Swagger) + + // version + addPropertyCheck(&props, lDoc.Swagger.ValueNode, rDoc.Swagger.ValueNode, + lDoc.Swagger.Value, rDoc.Swagger.Value, &changes, v3.SwaggerLabel, true, CompOpenAPI, "") + + // host + addPropertyCheck(&props, lDoc.Host.ValueNode, rDoc.Host.ValueNode, + lDoc.Host.Value, rDoc.Host.Value, &changes, v3.HostLabel, true, "", "") + + // base path + addPropertyCheck(&props, lDoc.BasePath.ValueNode, rDoc.BasePath.ValueNode, + lDoc.BasePath.Value, rDoc.BasePath.Value, &changes, v3.BasePathLabel, true, "", "") + + // schemes + if len(lDoc.Schemes.Value) > 0 || len(rDoc.Schemes.Value) > 0 { + ExtractStringValueSliceChanges(lDoc.Schemes.Value, rDoc.Schemes.Value, + &changes, v3.SchemesLabel, true) + } + // consumes + if len(lDoc.Consumes.Value) > 0 || len(rDoc.Consumes.Value) > 0 { + ExtractStringValueSliceChanges(lDoc.Consumes.Value, rDoc.Consumes.Value, + &changes, v3.ConsumesLabel, true) + } + // produces + if len(lDoc.Produces.Value) > 0 || len(rDoc.Produces.Value) > 0 { + ExtractStringValueSliceChanges(lDoc.Produces.Value, rDoc.Produces.Value, + &changes, v3.ProducesLabel, true) + } + + // tags + dc.TagChanges = CompareTags(lDoc.Tags.Value, rDoc.Tags.Value) + + // paths + if !lDoc.Paths.IsEmpty() || !rDoc.Paths.IsEmpty() { + dc.PathsChanges = ComparePaths(lDoc.Paths.Value, rDoc.Paths.Value) + } + + // external docs + compareDocumentExternalDocs(lDoc, rDoc, dc, &changes) + + // info + compareDocumentInfo(&lDoc.Info, &rDoc.Info, dc, &changes) + + // security + if !lDoc.Security.IsEmpty() || !rDoc.Security.IsEmpty() { + checkSecurity(lDoc.Security, rDoc.Security, &changes, dc) + } + + // components / definitions + // swagger (damn you) decided to put all this stuff at the document root, rather than cleanly + // placing it under a parent, like they did with OpenAPI. This means picking through each definition + // creating a new set of changes and then morphing them into a single changes object. + cc := new(ComponentsChanges) + cc.PropertyChanges = new(PropertyChanges) + if n := CompareComponents(lDoc.Definitions.Value, rDoc.Definitions.Value); n != nil { + cc.SchemaChanges = n.SchemaChanges + } + if n := CompareComponents(lDoc.SecurityDefinitions.Value, rDoc.SecurityDefinitions.Value); n != nil { + cc.SecuritySchemeChanges = n.SecuritySchemeChanges + } + if n := CompareComponents(lDoc.Parameters.Value, rDoc.Parameters.Value); n != nil { + cc.PropertyChanges.Changes = append(cc.PropertyChanges.Changes, n.Changes...) + } + if n := CompareComponents(lDoc.Responses.Value, rDoc.Responses.Value); n != nil { + cc.Changes = append(cc.Changes, n.Changes...) + } + dc.ExtensionChanges = CompareExtensions(lDoc.Extensions, rDoc.Extensions) + if cc.TotalChanges() > 0 { + dc.ComponentsChanges = cc + } + } + + if reflect.TypeOf(&v3.Document{}) == reflect.TypeOf(l) && reflect.TypeOf(&v3.Document{}) == reflect.TypeOf(r) { + lDoc := l.(*v3.Document) + rDoc := r.(*v3.Document) + + // version + addPropertyCheck(&props, lDoc.Version.ValueNode, rDoc.Version.ValueNode, + lDoc.Version.Value, rDoc.Version.Value, &changes, v3.OpenAPILabel, + BreakingModified(CompOpenAPI, ""), CompOpenAPI, "") + + // schema dialect + addPropertyCheck(&props, lDoc.JsonSchemaDialect.ValueNode, rDoc.JsonSchemaDialect.ValueNode, + lDoc.JsonSchemaDialect.Value, rDoc.JsonSchemaDialect.Value, &changes, v3.JSONSchemaDialectLabel, + BreakingModified(CompJSONSchemaDialect, ""), CompJSONSchemaDialect, "") + + // $self field (3.2+) + addPropertyCheck(&props, lDoc.Self.ValueNode, rDoc.Self.ValueNode, + lDoc.Self.Value, rDoc.Self.Value, &changes, v3.SelfLabel, + BreakingModified(CompSelf, ""), CompSelf, "") + + // tags + dc.TagChanges = CompareTags(lDoc.Tags.Value, rDoc.Tags.Value) + + // paths + if !lDoc.Paths.IsEmpty() || !rDoc.Paths.IsEmpty() { + dc.PathsChanges = ComparePaths(lDoc.Paths.Value, rDoc.Paths.Value) + } + + // external docs + compareDocumentExternalDocs(lDoc, rDoc, dc, &changes) + + // info + compareDocumentInfo(&lDoc.Info, &rDoc.Info, dc, &changes) + + // security + if !lDoc.Security.IsEmpty() || !rDoc.Security.IsEmpty() { + checkSecurity(lDoc.Security, rDoc.Security, &changes, dc) + } + + // compare components. + if !lDoc.Components.IsEmpty() && !rDoc.Components.IsEmpty() { + if n := CompareComponents(lDoc.Components.Value, rDoc.Components.Value); n != nil { + dc.ComponentsChanges = n + } + } + if !lDoc.Components.IsEmpty() && rDoc.Components.IsEmpty() { + CreateChange(&changes, PropertyRemoved, v3.ComponentsLabel, + lDoc.Components.ValueNode, nil, BreakingRemoved(CompComponents, ""), lDoc.Components.Value, nil) + } + if lDoc.Components.IsEmpty() && !rDoc.Components.IsEmpty() { + CreateChange(&changes, PropertyAdded, v3.ComponentsLabel, + nil, rDoc.Components.ValueNode, BreakingAdded(CompComponents, ""), nil, lDoc.Components.Value) + } + + // compare servers + if n := checkServers(lDoc.Servers, rDoc.Servers, CompServers, ""); n != nil { + dc.ServerChanges = n + } + + // compare webhooks + dc.WebhookChanges = CheckMapForChanges(lDoc.Webhooks.Value, rDoc.Webhooks.Value, &changes, + v3.WebhooksLabel, ComparePathItemsV3) + + // extensions + dc.ExtensionChanges = CompareExtensions(lDoc.Extensions, rDoc.Extensions) + } + + CheckProperties(props) + dc.PropertyChanges = NewPropertyChanges(changes) + if dc.TotalChanges() <= 0 { + return nil + } + base.SchemaQuickHashMap.Clear() + return dc +} + +func compareDocumentExternalDocs(l, r low.HasExternalDocs, dc *DocumentChanges, changes *[]*Change) { + // external docs + if !l.GetExternalDocs().IsEmpty() && !r.GetExternalDocs().IsEmpty() { + lExtDoc := l.GetExternalDocs().Value.(*base.ExternalDoc) + rExtDoc := r.GetExternalDocs().Value.(*base.ExternalDoc) + if !low.AreEqual(lExtDoc, rExtDoc) { + dc.ExternalDocChanges = CompareExternalDocs(lExtDoc, rExtDoc) + } + } + if l.GetExternalDocs().IsEmpty() && !r.GetExternalDocs().IsEmpty() { + CreateChange(changes, PropertyAdded, v3.ExternalDocsLabel, + nil, r.GetExternalDocs().ValueNode, false, nil, + r.GetExternalDocs().Value) + } + if !l.GetExternalDocs().IsEmpty() && r.GetExternalDocs().IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.ExternalDocsLabel, + l.GetExternalDocs().ValueNode, nil, false, l.GetExternalDocs().Value, + nil) + } +} + +func compareDocumentInfo(l, r *low.NodeReference[*base.Info], dc *DocumentChanges, changes *[]*Change) { + // info + if !l.IsEmpty() && !r.IsEmpty() { + lInfo := l.Value + rInfo := r.Value + if !low.AreEqual(lInfo, rInfo) { + dc.InfoChanges = CompareInfo(lInfo, rInfo) + } + } + if l.IsEmpty() && !r.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.InfoLabel, + nil, r.ValueNode, false, nil, + r.Value) + } + if !l.IsEmpty() && r.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.InfoLabel, + l.ValueNode, nil, false, l.Value, + nil) + } +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/document_flat.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/document_flat.go new file mode 100644 index 000000000..21f7771a3 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/document_flat.go @@ -0,0 +1,17 @@ +// Copyright 2023-2024 Princess Beef Heavy Industries, LLC / Dave Shanley +// https://pb33f.io + +package model + +type DocumentChangesFlat struct { + *PropertyChanges + InfoChanges []*Change `json:"info,omitempty" yaml:"info,omitempty"` + PathsChanges []*Change `json:"paths,omitempty" yaml:"paths,omitempty"` + TagChanges []*Change `json:"tags,omitempty" yaml:"tags,omitempty"` + ExternalDocChanges []*Change `json:"externalDoc,omitempty" yaml:"externalDoc,omitempty"` + WebhookChanges []*Change `json:"webhooks,omitempty" yaml:"webhooks,omitempty"` + ServerChanges []*Change `json:"servers,omitempty" yaml:"servers,omitempty"` + SecurityRequirementChanges []*Change `json:"securityRequirements,omitempty" yaml:"securityRequirements,omitempty"` + ComponentsChanges []*Change `json:"components,omitempty" yaml:"components,omitempty"` + ExtensionChanges []*Change `json:"extensions,omitempty" yaml:"extensions,omitempty"` +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/encoding.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/encoding.go new file mode 100644 index 000000000..f42520167 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/encoding.go @@ -0,0 +1,86 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +// EncodingChanges represent all the changes made to an Encoding object +type EncodingChanges struct { + *PropertyChanges + HeaderChanges map[string]*HeaderChanges `json:"headers,omitempty" yaml:"headers,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between Encoding objects +func (e *EncodingChanges) GetAllChanges() []*Change { + if e == nil { + return nil + } + var changes []*Change + changes = append(changes, e.Changes...) + for k := range e.HeaderChanges { + changes = append(changes, e.HeaderChanges[k].GetAllChanges()...) + } + return changes +} + +// TotalChanges returns the total number of changes made between two Encoding objects +func (e *EncodingChanges) TotalChanges() int { + if e == nil { + return 0 + } + c := e.PropertyChanges.TotalChanges() + if e.HeaderChanges != nil { + for i := range e.HeaderChanges { + c += e.HeaderChanges[i].TotalChanges() + } + } + return c +} + +// TotalBreakingChanges returns the number of changes made between two Encoding objects that were breaking. +func (e *EncodingChanges) TotalBreakingChanges() int { + c := e.PropertyChanges.TotalBreakingChanges() + if e.HeaderChanges != nil { + for i := range e.HeaderChanges { + c += e.HeaderChanges[i].TotalBreakingChanges() + } + } + return c +} + +// CompareEncoding returns a pointer to *EncodingChanges that contain all changes made between a left and right +// set of Encoding objects. +func CompareEncoding(l, r *v3.Encoding) *EncodingChanges { + var changes []*Change + props := make([]*PropertyCheck, 0, 4) + + props = append(props, + NewPropertyCheck(CompEncoding, PropContentType, + l.ContentType.ValueNode, r.ContentType.ValueNode, + v3.ContentTypeLabel, &changes, l, r), + NewPropertyCheck(CompEncoding, PropStyle, + l.Style.ValueNode, r.Style.ValueNode, + v3.StyleLabel, &changes, l, r), + NewPropertyCheck(CompEncoding, PropExplode, + l.Explode.ValueNode, r.Explode.ValueNode, + v3.ExplodeLabel, &changes, l, r), + NewPropertyCheck(CompEncoding, PropAllowReserved, + l.AllowReserved.ValueNode, r.AllowReserved.ValueNode, + v3.AllowReservedLabel, &changes, l, r), + ) + + // check everything. + CheckProperties(props) + ec := new(EncodingChanges) + + // headers + ec.HeaderChanges = CheckMapForChanges(l.Headers.Value, r.Headers.Value, &changes, v3.HeadersLabel, CompareHeadersV3) + ec.PropertyChanges = NewPropertyChanges(changes) + if ec.TotalChanges() <= 0 { + return nil + } + return ec +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/example.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/example.go new file mode 100644 index 000000000..8e6ea7306 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/example.go @@ -0,0 +1,174 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "fmt" + "sort" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/utils" +) + +// ExampleChanges represent changes to an Example object, part of an OpenAPI specification. +type ExampleChanges struct { + *PropertyChanges + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between Example objects +func (e *ExampleChanges) GetAllChanges() []*Change { + if e == nil { + return nil + } + var changes []*Change + changes = append(changes, e.Changes...) + if e.ExtensionChanges != nil { + changes = append(changes, e.ExtensionChanges.GetAllChanges()...) + } + return changes +} + +// TotalChanges returns the total number of changes made to Example +func (e *ExampleChanges) TotalChanges() int { + if e == nil { + return 0 + } + l := e.PropertyChanges.TotalChanges() + if e.ExtensionChanges != nil { + l += e.ExtensionChanges.PropertyChanges.TotalChanges() + } + return l +} + +// TotalBreakingChanges returns the total number of breaking changes made to Example +func (e *ExampleChanges) TotalBreakingChanges() int { + l := e.PropertyChanges.TotalBreakingChanges() + return l +} + +// CompareExamples returns a pointer to ExampleChanges that contains all changes made between +// left and right Example instances. If l is nil, the example was added. If r is nil, it was removed. +func CompareExamples(l, r *base.Example) *ExampleChanges { + ec := new(ExampleChanges) + var changes []*Change + + if l == nil { + // Example was added - use RootNode for proper line/column location + CreateChange(&changes, ObjectAdded, v3.ExampleLabel, + nil, r.RootNode, BreakingAdded(CompExample, PropValue), nil, r) + ec.PropertyChanges = NewPropertyChanges(changes) + return ec + } + if r == nil { + // Example was removed - use RootNode for proper line/column location + CreateChange(&changes, ObjectRemoved, v3.ExampleLabel, + l.RootNode, nil, BreakingRemoved(CompExample, PropValue), l, nil) + ec.PropertyChanges = NewPropertyChanges(changes) + return ec + } + + props := make([]*PropertyCheck, 0, 2) + + props = append(props, + NewPropertyCheck(CompExample, PropSummary, + l.Summary.ValueNode, r.Summary.ValueNode, + v3.SummaryLabel, &changes, l, r), + NewPropertyCheck(CompExample, PropDescription, + l.Description.ValueNode, r.Description.ValueNode, + v3.DescriptionLabel, &changes, l, r), + ) + + // Value + if utils.IsNodeMap(l.Value.ValueNode) && utils.IsNodeMap(r.Value.ValueNode) { + lKeys := make([]string, len(l.Value.ValueNode.Content)/2) + rKeys := make([]string, len(r.Value.ValueNode.Content)/2) + z := 0 + for k := range l.Value.ValueNode.Content { + if k%2 == 0 { + // if there is no value (value is another map or something else), render the node into yaml and hash it. + // https://github.com/pb33f/libopenapi/issues/61 + val := l.Value.ValueNode.Content[k+1].Value + if val == "" { + val = low.HashYAMLNodeSlice(l.Value.ValueNode.Content[k+1].Content) + } + lKeys[z] = fmt.Sprintf("%v-%v-%v", + l.Value.ValueNode.Content[k].Value, + l.Value.ValueNode.Content[k+1].Tag, + fmt.Sprintf("%x", val)) + z++ + } else { + continue + } + } + z = 0 + for k := range r.Value.ValueNode.Content { + if k%2 == 0 { + // if there is no value (value is another map or something else), render the node into yaml and hash it. + // https://github.com/pb33f/libopenapi/issues/61 + val := r.Value.ValueNode.Content[k+1].Value + if val == "" { + val = low.HashYAMLNodeSlice(r.Value.ValueNode.Content[k+1].Content) + } + rKeys[z] = fmt.Sprintf("%v-%v-%v", + r.Value.ValueNode.Content[k].Value, + r.Value.ValueNode.Content[k+1].Tag, + fmt.Sprintf("%x", val)) + z++ + } else { + continue + } + } + sort.Strings(lKeys) + sort.Strings(rKeys) + for k := range lKeys { + if k < len(rKeys) && lKeys[k] != rKeys[k] { + CreateChangeWithEncoding(&changes, Modified, v3.ValueLabel, + l.Value.GetValueNode(), r.Value.GetValueNode(), BreakingModified(CompExample, PropValue), l.Value.GetValue(), r.Value.GetValue()) + continue + } + if k >= len(rKeys) { + CreateChangeWithEncoding(&changes, PropertyRemoved, v3.ValueLabel, + l.Value.ValueNode, r.Value.ValueNode, BreakingRemoved(CompExample, PropValue), l.Value.Value, r.Value.Value) + } + } + for k := range rKeys { + if k >= len(lKeys) { + CreateChangeWithEncoding(&changes, PropertyAdded, v3.ValueLabel, + l.Value.ValueNode, r.Value.ValueNode, BreakingAdded(CompExample, PropValue), l.Value.Value, r.Value.Value) + } + } + } else { + props = append(props, NewPropertyCheck(CompExample, PropValue, + l.Value.ValueNode, r.Value.ValueNode, + v3.ValueLabel, &changes, l, r)) + } + // ExternalValue + props = append(props, NewPropertyCheck(CompExample, PropExternalValue, + l.ExternalValue.ValueNode, r.ExternalValue.ValueNode, + v3.ExternalValue, &changes, l, r)) + + // DataValue (OpenAPI 3.2+) + props = append(props, NewPropertyCheck(CompExample, PropDataValue, + l.DataValue.ValueNode, r.DataValue.ValueNode, + base.DataValueLabel, &changes, l, r)) + + // SerializedValue (OpenAPI 3.2+) + props = append(props, NewPropertyCheck(CompExample, PropSerializedValue, + l.SerializedValue.ValueNode, r.SerializedValue.ValueNode, + base.SerializedValueLabel, &changes, l, r)) + + // check properties + CheckProperties(props) + + // check extensions + ec.ExtensionChanges = CheckExtensions(l, r) + ec.PropertyChanges = NewPropertyChanges(changes) + if ec.TotalChanges() <= 0 { + return nil + } + return ec +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/examples.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/examples.go new file mode 100644 index 000000000..c271c2700 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/examples.go @@ -0,0 +1,92 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "github.com/pb33f/libopenapi/datamodel/low" + v2 "github.com/pb33f/libopenapi/datamodel/low/v2" + "go.yaml.in/yaml/v4" +) + +// ExamplesChanges represents changes made between Swagger Examples objects (Not OpenAPI 3). +type ExamplesChanges struct { + *PropertyChanges +} + +// GetAllChanges returns a slice of all changes made between Examples objects +func (a *ExamplesChanges) GetAllChanges() []*Change { + if a == nil { + return nil + } + return a.Changes +} + +// TotalChanges represents the total number of changes made between Example instances. +func (a *ExamplesChanges) TotalChanges() int { + if a == nil { + return 0 + } + return a.PropertyChanges.TotalChanges() +} + +// TotalBreakingChanges will always return 0. Examples cannot break a contract. +func (a *ExamplesChanges) TotalBreakingChanges() int { + return 0 // not supported. +} + +// CompareExamplesV2 compares two Swagger Examples objects, returning a pointer to +// ExamplesChanges if anything was found. +func CompareExamplesV2(l, r *v2.Examples) *ExamplesChanges { + lHashes := make(map[string]string) + rHashes := make(map[string]string) + lValues := make(map[string]low.ValueReference[*yaml.Node]) + rValues := make(map[string]low.ValueReference[*yaml.Node]) + + for k, v := range l.Values.FromOldest() { + lHashes[k.Value] = low.GenerateHashString(v.Value) + lValues[k.Value] = v + } + + for k, v := range r.Values.FromOldest() { + rHashes[k.Value] = low.GenerateHashString(v.Value) + rValues[k.Value] = v + } + var changes []*Change + + // check left example hashes + for k := range lHashes { + rhash := rHashes[k] + if rhash == "" { + CreateChange(&changes, ObjectRemoved, k, + lValues[k].GetValueNode(), nil, false, + lValues[k].GetValue(), nil) + continue + } + if lHashes[k] == rHashes[k] { + continue + } + CreateChange(&changes, Modified, k, + lValues[k].GetValueNode(), rValues[k].GetValueNode(), false, + lValues[k].GetValue(), lValues[k].GetValue()) + + } + + // check right example hashes + for k := range rHashes { + lhash := lHashes[k] + if lhash == "" { + CreateChange(&changes, ObjectAdded, k, + nil, lValues[k].GetValueNode(), false, + nil, lValues[k].GetValue()) + continue + } + } + + ex := new(ExamplesChanges) + ex.PropertyChanges = NewPropertyChanges(changes) + if ex.TotalChanges() <= 0 { + return nil + } + return ex +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/extensions.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/extensions.go new file mode 100644 index 000000000..6aa571b84 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/extensions.go @@ -0,0 +1,107 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "strings" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// ExtensionChanges represents any changes to custom extensions defined for an OpenAPI object. +type ExtensionChanges struct { + *PropertyChanges +} + +// GetAllChanges returns a slice of all changes made between Extension objects +func (e *ExtensionChanges) GetAllChanges() []*Change { + if e == nil { + return nil + } + return e.Changes +} + +// TotalChanges returns the total number of object extensions that were made. +func (e *ExtensionChanges) TotalChanges() int { + if e == nil { + return 0 + } + return e.PropertyChanges.TotalChanges() +} + +// TotalBreakingChanges returns the total number of breaking changes in Extension objects. +func (e *ExtensionChanges) TotalBreakingChanges() int { + if e == nil { + return 0 + } + return e.PropertyChanges.TotalBreakingChanges() +} + +// CompareExtensions will compare a left and right map of Tag/ValueReference models for any changes to +// anything. This function does not try and cast the value of an extension to perform checks, it +// will perform a basic value check. +// +// A current limitation relates to extensions being objects and a property of the object changes, +// there is currently no support for knowing anything changed - so it is ignored. +func CompareExtensions(l, r *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]]) *ExtensionChanges { + // look at the original and then look through the new. + seenLeft := make(map[string]*low.ValueReference[*yaml.Node]) + seenRight := make(map[string]*low.ValueReference[*yaml.Node]) + + for k, h := range l.FromOldest() { + seenLeft[strings.ToLower(k.Value)] = &h + } + for k, h := range r.FromOldest() { + seenRight[strings.ToLower(k.Value)] = &h + } + + var changes []*Change + for i := range seenLeft { + + CheckForObjectAdditionOrRemovalWithEncoding[*yaml.Node](seenLeft, seenRight, i, &changes, false, true) + + if seenRight[i] != nil { + var props []*PropertyCheck + + props = append(props, &PropertyCheck{ + LeftNode: seenLeft[i].ValueNode, + RightNode: seenRight[i].ValueNode, + Label: i, + Changes: &changes, + Breaking: false, + Original: seenLeft[i].Value, + New: seenRight[i].Value, + }) + + // check properties with encoding for extensions + CheckPropertiesWithEncoding(props) + } + } + for i := range seenRight { + if seenLeft[i] == nil { + CheckForObjectAdditionOrRemovalWithEncoding[*yaml.Node](seenLeft, seenRight, i, &changes, false, true) + } + } + ex := new(ExtensionChanges) + ex.PropertyChanges = NewPropertyChanges(changes) + if ex.TotalChanges() <= 0 { + return nil + } + return ex +} + +// CheckExtensions is a helper method to un-pack a left and right model that contains extensions. Once unpacked +// the extensions are compared and returns a pointer to ExtensionChanges. If nothing changed, nil is returned. +func CheckExtensions[T low.HasExtensions[T]](l, r T) *ExtensionChanges { + var lExt, rExt *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + if orderedmap.Len(l.GetExtensions()) > 0 { + lExt = l.GetExtensions() + } + if orderedmap.Len(r.GetExtensions()) > 0 { + rExt = r.GetExtensions() + } + return CompareExtensions(lExt, rExt) +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/external_docs.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/external_docs.go new file mode 100644 index 000000000..532b902ef --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/external_docs.go @@ -0,0 +1,81 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +// ExternalDocChanges represents changes made to any ExternalDoc object from an OpenAPI document. +type ExternalDocChanges struct { + *PropertyChanges + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between Example objects +func (e *ExternalDocChanges) GetAllChanges() []*Change { + if e == nil { + return nil + } + var changes []*Change + changes = append(changes, e.Changes...) + if e.ExtensionChanges != nil { + changes = append(changes, e.ExtensionChanges.GetAllChanges()...) + } + return changes +} + +// TotalChanges returns a count of everything that changed +func (e *ExternalDocChanges) TotalChanges() int { + if e == nil { + return 0 + } + c := e.PropertyChanges.TotalChanges() + if e.ExtensionChanges != nil { + c += e.ExtensionChanges.TotalChanges() + } + return c +} + +// TotalBreakingChanges returns the total number of breaking changes in ExternalDoc objects. +func (e *ExternalDocChanges) TotalBreakingChanges() int { + if e == nil { + return 0 + } + c := e.PropertyChanges.TotalBreakingChanges() + if e.ExtensionChanges != nil { + c += e.ExtensionChanges.TotalBreakingChanges() + } + return c +} + +// CompareExternalDocs will compare a left (original) and a right (new) slice of ValueReference +// nodes for any changes between them. If there are changes, then a pointer to ExternalDocChanges +// is returned, otherwise if nothing changed - then nil is returned. +func CompareExternalDocs(l, r *base.ExternalDoc) *ExternalDocChanges { + var changes []*Change + props := make([]*PropertyCheck, 0, 2) + + props = append(props, + NewPropertyCheck(CompExternalDocs, PropURL, + l.URL.ValueNode, r.URL.ValueNode, + v3.URLLabel, &changes, l, r), + NewPropertyCheck(CompExternalDocs, PropDescription, + l.Description.ValueNode, r.Description.ValueNode, + v3.DescriptionLabel, &changes, l, r), + ) + + CheckProperties(props) + + dc := new(ExternalDocChanges) + dc.PropertyChanges = NewPropertyChanges(changes) + + // check extensions + dc.ExtensionChanges = CheckExtensions(l, r) + if dc.TotalChanges() <= 0 { + return nil + } + return dc +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/header.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/header.go new file mode 100644 index 000000000..c3d5f8cc7 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/header.go @@ -0,0 +1,298 @@ +// Copyright 2022-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "reflect" + + "github.com/pb33f/libopenapi/datamodel/low" + v2 "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +// HeaderChanges represents changes made between two Header objects. Supports both Swagger and OpenAPI header +// objects, V2 only property Items is broken out into its own. +type HeaderChanges struct { + *PropertyChanges + SchemaChanges *SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"` + ExamplesChanges map[string]*ExampleChanges `json:"examples,omitempty" yaml:"examples,omitempty"` + ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + + // Items only supported by Swagger (V2) + ItemsChanges *ItemsChanges `json:"items,omitempty" yaml:"items,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between Header objects +func (h *HeaderChanges) GetAllChanges() []*Change { + if h == nil { + return nil + } + var changes []*Change + changes = append(changes, h.Changes...) + for k := range h.ExamplesChanges { + changes = append(changes, h.ExamplesChanges[k].GetAllChanges()...) + } + for k := range h.ContentChanges { + changes = append(changes, h.ContentChanges[k].GetAllChanges()...) + } + if h.ExtensionChanges != nil { + changes = append(changes, h.ExtensionChanges.GetAllChanges()...) + } + if h.SchemaChanges != nil { + changes = append(changes, h.SchemaChanges.GetAllChanges()...) + } + if h.ItemsChanges != nil { + changes = append(changes, h.ItemsChanges.GetAllChanges()...) + } + return changes +} + +// TotalChanges returns the total number of changes made between two Header objects. +func (h *HeaderChanges) TotalChanges() int { + if h == nil { + return 0 + } + c := h.PropertyChanges.TotalChanges() + for k := range h.ExamplesChanges { + c += h.ExamplesChanges[k].TotalChanges() + } + for k := range h.ContentChanges { + c += h.ContentChanges[k].TotalChanges() + } + if h.ExtensionChanges != nil { + c += h.ExtensionChanges.TotalChanges() + } + if h.SchemaChanges != nil { + c += h.SchemaChanges.TotalChanges() + } + if h.ItemsChanges != nil { + c += h.ItemsChanges.TotalChanges() + } + return c +} + +// TotalBreakingChanges returns the total number of breaking changes made between two Header instances. +func (h *HeaderChanges) TotalBreakingChanges() int { + c := h.PropertyChanges.TotalBreakingChanges() + for k := range h.ContentChanges { + c += h.ContentChanges[k].TotalBreakingChanges() + } + if h.ItemsChanges != nil { + c += h.ItemsChanges.TotalBreakingChanges() + } + if h.SchemaChanges != nil { + c += h.SchemaChanges.TotalBreakingChanges() + } + return c +} + +// shared header properties +func addOpenAPIHeaderProperties(left, right low.OpenAPIHeader, changes *[]*Change) []*PropertyCheck { + var props []*PropertyCheck + + // style + addPropertyCheck(&props, left.GetStyle().ValueNode, right.GetStyle().ValueNode, + left.GetStyle(), right.GetStyle(), changes, v3.StyleLabel, + BreakingModified(CompHeader, PropStyle), CompHeader, PropStyle) + + // allow reserved + addPropertyCheck(&props, left.GetAllowReserved().ValueNode, right.GetAllowReserved().ValueNode, + left.GetAllowReserved(), right.GetAllowReserved(), changes, v3.AllowReservedLabel, + BreakingModified(CompHeader, PropAllowReserved), CompHeader, PropAllowReserved) + + // allow empty value + addPropertyCheck(&props, left.GetAllowEmptyValue().ValueNode, right.GetAllowEmptyValue().ValueNode, + left.GetAllowEmptyValue(), right.GetAllowEmptyValue(), changes, v3.AllowEmptyValueLabel, + BreakingModified(CompHeader, PropAllowEmptyValue), CompHeader, PropAllowEmptyValue) + + // explode + addPropertyCheck(&props, left.GetExplode().ValueNode, right.GetExplode().ValueNode, + left.GetExplode(), right.GetExplode(), changes, v3.ExplodeLabel, + BreakingModified(CompHeader, PropExplode), CompHeader, PropExplode) + + // example + CheckPropertyAdditionOrRemovalWithEncoding(left.GetExample().ValueNode, right.GetExample().ValueNode, + v3.ExampleLabel, changes, + BreakingAdded(CompHeader, PropExample) || BreakingRemoved(CompHeader, PropExample), + left.GetExample(), right.GetExample()) + CheckForModificationWithEncoding(left.GetExample().ValueNode, right.GetExample().ValueNode, + v3.ExampleLabel, changes, BreakingModified(CompHeader, PropExample), + left.GetExample(), right.GetExample()) + + // deprecated + addPropertyCheck(&props, left.GetDeprecated().ValueNode, right.GetDeprecated().ValueNode, + left.GetDeprecated(), right.GetDeprecated(), changes, v3.DeprecatedLabel, + BreakingModified(CompHeader, PropDeprecated), CompHeader, PropDeprecated) + + // required + addPropertyCheck(&props, left.GetRequired().ValueNode, right.GetRequired().ValueNode, + left.GetRequired(), right.GetRequired(), changes, v3.RequiredLabel, + BreakingModified(CompHeader, PropRequired), CompHeader, PropRequired) + + return props +} + +// swagger only properties +func addSwaggerHeaderProperties(left, right low.SwaggerHeader, changes *[]*Change) []*PropertyCheck { + var props []*PropertyCheck + + // type + addPropertyCheck(&props, left.GetType().ValueNode, right.GetType().ValueNode, + left.GetType(), right.GetType(), changes, v3.TypeLabel, true, CompHeader, PropType) + + // format + addPropertyCheck(&props, left.GetFormat().ValueNode, right.GetFormat().ValueNode, + left.GetFormat(), right.GetFormat(), changes, v3.FormatLabel, true, CompHeader, PropFormat) + + // collection format + addPropertyCheck(&props, left.GetCollectionFormat().ValueNode, right.GetCollectionFormat().ValueNode, + left.GetCollectionFormat(), right.GetCollectionFormat(), changes, v3.CollectionFormatLabel, true, CompHeader, PropCollectionFormat) + + // maximum + addPropertyCheck(&props, left.GetMaximum().ValueNode, right.GetMaximum().ValueNode, + left.GetMaximum(), right.GetMaximum(), changes, v3.MaximumLabel, true, CompHeader, PropMaximum) + + // minimum + addPropertyCheck(&props, left.GetMinimum().ValueNode, right.GetMinimum().ValueNode, + left.GetMinimum(), right.GetMinimum(), changes, v3.MinimumLabel, true, CompHeader, PropMinimum) + + // exclusive maximum + addPropertyCheck(&props, left.GetExclusiveMaximum().ValueNode, right.GetExclusiveMaximum().ValueNode, + left.GetExclusiveMaximum(), right.GetExclusiveMaximum(), changes, v3.ExclusiveMaximumLabel, true, CompHeader, PropExclusiveMaximum) + + // exclusive minimum + addPropertyCheck(&props, left.GetExclusiveMinimum().ValueNode, right.GetExclusiveMinimum().ValueNode, + left.GetExclusiveMinimum(), right.GetExclusiveMinimum(), changes, v3.ExclusiveMinimumLabel, true, CompHeader, PropExclusiveMinimum) + + // max length + addPropertyCheck(&props, left.GetMaxLength().ValueNode, right.GetMaxLength().ValueNode, + left.GetMaxLength(), right.GetMaxLength(), changes, v3.MaxLengthLabel, true, CompHeader, PropMaxLength) + + // min length + addPropertyCheck(&props, left.GetMinLength().ValueNode, right.GetMinLength().ValueNode, + left.GetMinLength(), right.GetMinLength(), changes, v3.MinLengthLabel, true, CompHeader, PropMinLength) + + // pattern + addPropertyCheck(&props, left.GetPattern().ValueNode, right.GetPattern().ValueNode, + left.GetPattern(), right.GetPattern(), changes, v3.PatternLabel, true, CompHeader, PropPattern) + + // max items + addPropertyCheck(&props, left.GetMaxItems().ValueNode, right.GetMaxItems().ValueNode, + left.GetMaxItems(), right.GetMaxItems(), changes, v3.MaxItemsLabel, true, CompHeader, PropMaxItems) + + // min items + addPropertyCheck(&props, left.GetMinItems().ValueNode, right.GetMinItems().ValueNode, + left.GetMinItems(), right.GetMinItems(), changes, v3.MinItemsLabel, true, CompHeader, PropMinItems) + + // unique items + addPropertyCheck(&props, left.GetUniqueItems().ValueNode, right.GetUniqueItems().ValueNode, + left.GetUniqueItems(), right.GetUniqueItems(), changes, v3.UniqueItemsLabel, true, CompHeader, PropUniqueItems) + + // multiple of + addPropertyCheck(&props, left.GetMultipleOf().ValueNode, right.GetMultipleOf().ValueNode, + left.GetMultipleOf(), right.GetMultipleOf(), changes, v3.MultipleOfLabel, true, CompHeader, PropMultipleOf) + + return props +} + +// common header properties +func addCommonHeaderProperties(left, right low.HasDescription, changes *[]*Change) []*PropertyCheck { + var props []*PropertyCheck + + // description + addPropertyCheck(&props, left.GetDescription().ValueNode, right.GetDescription().ValueNode, + left.GetDescription(), right.GetDescription(), changes, v3.DescriptionLabel, + BreakingModified(CompHeader, PropDescription), CompHeader, PropDescription) + + return props +} + +// CompareHeadersV2 is a Swagger compatible, typed signature used for other generic functions. It simply +// wraps CompareHeaders and provides nothing other that a typed interface. +func CompareHeadersV2(l, r *v2.Header) *HeaderChanges { + return CompareHeaders(l, r) +} + +// CompareHeadersV3 is an OpenAPI 3+ compatible, typed signature used for other generic functions. It simply +// wraps CompareHeaders and provides nothing other that a typed interface. +func CompareHeadersV3(l, r *v3.Header) *HeaderChanges { + return CompareHeaders(l, r) +} + +// CompareHeaders will compare left and right Header objects (any version of Swagger or OpenAPI) and return +// a pointer to HeaderChanges with anything that has changed, or nil if nothing changed. +func CompareHeaders(l, r any) *HeaderChanges { + var changes []*Change + var props []*PropertyCheck + hc := new(HeaderChanges) + + // handle swagger. + if reflect.TypeOf(&v2.Header{}) == reflect.TypeOf(l) && reflect.TypeOf(&v2.Header{}) == reflect.TypeOf(r) { + lHeader := l.(*v2.Header) + rHeader := r.(*v2.Header) + + // perform hash check to avoid further processing + if low.AreEqual(lHeader, rHeader) { + return nil + } + + props = append(props, addCommonHeaderProperties(lHeader, rHeader, &changes)...) + props = append(props, addSwaggerHeaderProperties(lHeader, rHeader, &changes)...) + + // enum + if len(lHeader.Enum.Value) > 0 || len(rHeader.Enum.Value) > 0 { + ExtractRawValueSliceChanges(lHeader.Enum.Value, rHeader.Enum.Value, &changes, v3.EnumLabel, true) + } + + // items + if !lHeader.Items.IsEmpty() && !rHeader.Items.IsEmpty() { + if !low.AreEqual(lHeader.Items.Value, rHeader.Items.Value) { + hc.ItemsChanges = CompareItems(lHeader.Items.Value, rHeader.Items.Value) + } + } + if lHeader.Items.IsEmpty() && !rHeader.Items.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.ItemsLabel, nil, + rHeader.Items.ValueNode, BreakingAdded(CompHeader, PropItems), nil, rHeader.Items.Value) + } + if !lHeader.Items.IsEmpty() && rHeader.Items.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.SchemaLabel, lHeader.Items.ValueNode, + nil, BreakingRemoved(CompHeader, PropItems), lHeader.Items.Value, nil) + } + hc.ExtensionChanges = CompareExtensions(lHeader.Extensions, rHeader.Extensions) + } + + // handle OpenAPI + if reflect.TypeOf(&v3.Header{}) == reflect.TypeOf(l) && reflect.TypeOf(&v3.Header{}) == reflect.TypeOf(r) { + lHeader := l.(*v3.Header) + rHeader := r.(*v3.Header) + + // perform hash check to avoid further processing + if low.AreEqual(lHeader, rHeader) { + return nil + } + + props = append(props, addCommonHeaderProperties(lHeader, rHeader, &changes)...) + props = append(props, addOpenAPIHeaderProperties(lHeader, rHeader, &changes)...) + + // header + if !lHeader.Schema.IsEmpty() || !rHeader.Schema.IsEmpty() { + hc.SchemaChanges = CompareSchemas(lHeader.Schema.Value, rHeader.Schema.Value) + } + + // examples + hc.ExamplesChanges = CheckMapForChanges(lHeader.Examples.Value, rHeader.Examples.Value, + &changes, v3.ExamplesLabel, CompareExamples) + + // content + hc.ContentChanges = CheckMapForChanges(lHeader.Content.Value, rHeader.Content.Value, + &changes, v3.ContentLabel, CompareMediaTypes) + + hc.ExtensionChanges = CompareExtensions(lHeader.Extensions, rHeader.Extensions) + + } + CheckProperties(props) + hc.PropertyChanges = NewPropertyChanges(changes) + return hc +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/info.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/info.go new file mode 100644 index 000000000..b042ed9b8 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/info.go @@ -0,0 +1,140 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "github.com/pb33f/libopenapi/datamodel/low/base" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +// InfoChanges represents the number of changes to an Info object. Part of an OpenAPI document +type InfoChanges struct { + *PropertyChanges + ContactChanges *ContactChanges `json:"contact,omitempty" yaml:"contact,omitempty"` + LicenseChanges *LicenseChanges `json:"license,omitempty" yaml:"license,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between Info objects +func (i *InfoChanges) GetAllChanges() []*Change { + if i == nil { + return nil + } + var changes []*Change + changes = append(changes, i.Changes...) + if i.ContactChanges != nil { + changes = append(changes, i.ContactChanges.GetAllChanges()...) + } + if i.LicenseChanges != nil { + changes = append(changes, i.LicenseChanges.GetAllChanges()...) + } + if i.ExtensionChanges != nil { + changes = append(changes, i.ExtensionChanges.GetAllChanges()...) + } + return changes +} + +// TotalChanges represents the total number of changes made to an Info object. +func (i *InfoChanges) TotalChanges() int { + if i == nil { + return 0 + } + t := i.PropertyChanges.TotalChanges() + if i.ContactChanges != nil { + t += i.ContactChanges.TotalChanges() + } + if i.LicenseChanges != nil { + t += i.LicenseChanges.TotalChanges() + } + if i.ExtensionChanges != nil { + t += i.ExtensionChanges.TotalChanges() + } + return t +} + +// TotalBreakingChanges returns the total number of breaking changes in Info objects. +func (i *InfoChanges) TotalBreakingChanges() int { + if i == nil { + return 0 + } + c := i.PropertyChanges.TotalBreakingChanges() + if i.ContactChanges != nil { + c += i.ContactChanges.TotalBreakingChanges() + } + if i.LicenseChanges != nil { + c += i.LicenseChanges.TotalBreakingChanges() + } + if i.ExtensionChanges != nil { + c += i.ExtensionChanges.TotalBreakingChanges() + } + return c +} + +// CompareInfo will compare a left (original) and a right (new) Info object. Any changes +// will be returned in a pointer to InfoChanges, otherwise if nothing is found, then nil is +// returned instead. +func CompareInfo(l, r *base.Info) *InfoChanges { + var changes []*Change + props := make([]*PropertyCheck, 0, 5) + + props = append(props, + NewPropertyCheck(CompInfo, PropTitle, + l.Title.ValueNode, r.Title.ValueNode, + v3.TitleLabel, &changes, l, r), + NewPropertyCheck(CompInfo, PropSummary, + l.Summary.ValueNode, r.Summary.ValueNode, + v3.SummaryLabel, &changes, l, r), + NewPropertyCheck(CompInfo, PropDescription, + l.Description.ValueNode, r.Description.ValueNode, + v3.DescriptionLabel, &changes, l, r), + NewPropertyCheck(CompInfo, PropTermsOfService, + l.TermsOfService.ValueNode, r.TermsOfService.ValueNode, + v3.TermsOfServiceLabel, &changes, l, r), + NewPropertyCheck(CompInfo, PropVersion, + l.Version.ValueNode, r.Version.ValueNode, + v3.VersionLabel, &changes, l, r), + ) + + // check properties + CheckProperties(props) + + i := new(InfoChanges) + + // compare contact. + if l.Contact.Value != nil && r.Contact.Value != nil { + i.ContactChanges = CompareContact(l.Contact.Value, r.Contact.Value) + } else { + if l.Contact.Value == nil && r.Contact.Value != nil { + CreateChange(&changes, ObjectAdded, v3.ContactLabel, + nil, r.Contact.ValueNode, BreakingAdded(CompInfo, PropContact), nil, r.Contact.Value) + } + if l.Contact.Value != nil && r.Contact.Value == nil { + CreateChange(&changes, ObjectRemoved, v3.ContactLabel, + l.Contact.ValueNode, nil, BreakingRemoved(CompInfo, PropContact), l.Contact.Value, nil) + } + } + + // compare license. + if l.License.Value != nil && r.License.Value != nil { + i.LicenseChanges = CompareLicense(l.License.Value, r.License.Value) + } else { + if l.License.Value == nil && r.License.Value != nil { + CreateChange(&changes, ObjectAdded, v3.LicenseLabel, + nil, r.License.ValueNode, BreakingAdded(CompInfo, PropLicense), nil, r.License.Value) + } + if l.License.Value != nil && r.License.Value == nil { + CreateChange(&changes, ObjectRemoved, v3.LicenseLabel, + l.License.ValueNode, nil, BreakingRemoved(CompInfo, PropLicense), r.License.Value, nil) + } + } + + // check extensions. + i.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) + + i.PropertyChanges = NewPropertyChanges(changes) + if i.TotalChanges() <= 0 { + return nil + } + return i +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/items.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/items.go new file mode 100644 index 000000000..04b016188 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/items.go @@ -0,0 +1,92 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + v2 "github.com/pb33f/libopenapi/datamodel/low/v2" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +// ItemsChanges represent changes found between a left (original) and right (modified) object. Items is only +// used by Swagger documents. +type ItemsChanges struct { + *PropertyChanges + ItemsChanges *ItemsChanges `json:"items,omitempty" yaml:"items,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between Items objects +func (i *ItemsChanges) GetAllChanges() []*Change { + if i == nil { + return nil + } + var changes []*Change + changes = append(changes, i.Changes...) + if i.ItemsChanges != nil { + changes = append(changes, i.ItemsChanges.GetAllChanges()...) + } + return changes +} + +// TotalChanges returns the total number of changes found between two Items objects +// This is a recursive function because Items can contain Items. Be careful! +func (i *ItemsChanges) TotalChanges() int { + if i == nil { + return 0 + } + c := i.PropertyChanges.TotalChanges() + if i.ItemsChanges != nil { + c += i.ItemsChanges.TotalChanges() + } + return c +} + +// TotalBreakingChanges returns the total number of breaking changes found between two Swagger Items objects +// This is a recursive method, Items are recursive, be careful! +func (i *ItemsChanges) TotalBreakingChanges() int { + c := i.PropertyChanges.TotalBreakingChanges() + if i.ItemsChanges != nil { + c += i.ItemsChanges.TotalBreakingChanges() + } + return c +} + +// CompareItems compares two sets of Swagger Item objects. If there are any changes found then a pointer to +// ItemsChanges will be returned, otherwise nil is returned. +// +// It is worth nothing that Items can contain Items. This means recursion is possible and has the potential for +// runaway code if not using the resolver's circular reference checking. +func CompareItems(l, r *v2.Items) *ItemsChanges { + var changes []*Change + var props []*PropertyCheck + + ic := new(ItemsChanges) + + // header is identical to items, except for a description. + props = append(props, addSwaggerHeaderProperties(l, r, &changes)...) + CheckProperties(props) + + if !l.Items.IsEmpty() && !r.Items.IsEmpty() { + // inline, check hashes, if they don't match, compare. + if l.Items.Value.Hash() != r.Items.Value.Hash() { + // compare. + ic.ItemsChanges = CompareItems(l.Items.Value, r.Items.Value) + } + } + if l.Items.IsEmpty() && !r.Items.IsEmpty() { + // added items + CreateChange(&changes, PropertyAdded, v3.ItemsLabel, + nil, r.Items.GetValueNode(), true, nil, r.Items.GetValue()) + } + if !l.Items.IsEmpty() && r.Items.IsEmpty() { + // removed items + CreateChange(&changes, PropertyRemoved, v3.ItemsLabel, + l.Items.GetValueNode(), nil, true, l.Items.GetValue(), + nil) + } + ic.PropertyChanges = NewPropertyChanges(changes) + if ic.TotalChanges() <= 0 { + return nil + } + return ic +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/license.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/license.go new file mode 100644 index 000000000..ba361e455 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/license.go @@ -0,0 +1,83 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "github.com/pb33f/libopenapi/datamodel/low/base" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +// LicenseChanges represent changes to a License object that is a child of Info object. Part of an OpenAPI document +type LicenseChanges struct { + *PropertyChanges + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between License objects +func (l *LicenseChanges) GetAllChanges() []*Change { + if l == nil { + return nil + } + var changes []*Change + changes = append(changes, l.Changes...) + if l.ExtensionChanges != nil { + changes = append(changes, l.ExtensionChanges.GetAllChanges()...) + } + return changes +} + +// TotalChanges represents the total number of changes made to a License instance. +func (l *LicenseChanges) TotalChanges() int { + if l == nil { + return 0 + } + c := l.PropertyChanges.TotalChanges() + + if l.ExtensionChanges != nil { + c += l.ExtensionChanges.TotalChanges() + } + return c +} + +// TotalBreakingChanges returns the total number of breaking changes in License objects. +func (l *LicenseChanges) TotalBreakingChanges() int { + if l == nil { + return 0 + } + c := l.PropertyChanges.TotalBreakingChanges() + if l.ExtensionChanges != nil { + c += l.ExtensionChanges.TotalBreakingChanges() + } + return c +} + +// CompareLicense will check a left (original) and right (new) License object for any changes. If there +// were any, a pointer to a LicenseChanges object is returned, otherwise if nothing changed - the function +// returns nil. +func CompareLicense(l, r *base.License) *LicenseChanges { + var changes []*Change + props := make([]*PropertyCheck, 0, 3) + + props = append(props, + NewPropertyCheck(CompLicense, PropURL, + l.URL.ValueNode, r.URL.ValueNode, + v3.URLLabel, &changes, l, r), + NewPropertyCheck(CompLicense, PropName, + l.Name.ValueNode, r.Name.ValueNode, + v3.NameLabel, &changes, l, r), + NewPropertyCheck(CompLicense, PropIdentifier, + l.Identifier.ValueNode, r.Identifier.ValueNode, + v3.Identifier, &changes, l, r), + ) + + CheckProperties(props) + + lc := new(LicenseChanges) + lc.PropertyChanges = NewPropertyChanges(changes) + lc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) + if lc.TotalChanges() <= 0 { + return nil + } + return lc +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/link.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/link.go new file mode 100644 index 000000000..aabdda126 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/link.go @@ -0,0 +1,137 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "github.com/pb33f/libopenapi/datamodel/low" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +// LinkChanges represent changes made between two OpenAPI Link Objects. +type LinkChanges struct { + *PropertyChanges + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + ServerChanges *ServerChanges `json:"server,omitempty" yaml:"server,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between Link objects +func (l *LinkChanges) GetAllChanges() []*Change { + if l == nil { + return nil + } + var changes []*Change + changes = append(changes, l.Changes...) + if l.ServerChanges != nil { + changes = append(changes, l.ServerChanges.GetAllChanges()...) + } + if l.ExtensionChanges != nil { + changes = append(changes, l.ExtensionChanges.GetAllChanges()...) + } + return changes +} + +// TotalChanges returns the total changes made between OpenAPI Link objects +func (l *LinkChanges) TotalChanges() int { + if l == nil { + return 0 + } + c := l.PropertyChanges.TotalChanges() + if l.ExtensionChanges != nil { + c += l.ExtensionChanges.TotalChanges() + } + if l.ServerChanges != nil { + c += l.ServerChanges.TotalChanges() + } + return c +} + +// TotalBreakingChanges returns the number of breaking changes made between two OpenAPI Link Objects +func (l *LinkChanges) TotalBreakingChanges() int { + c := l.PropertyChanges.TotalBreakingChanges() + if l.ServerChanges != nil { + c += l.ServerChanges.TotalBreakingChanges() + } + return c +} + +// CompareLinks checks a left and right OpenAPI Link for any changes. If they are found, returns a pointer to +// LinkChanges, and returns nil if nothing is found. +func CompareLinks(l, r *v3.Link) *LinkChanges { + if low.AreEqual(l, r) { + return nil + } + + var changes []*Change + props := make([]*PropertyCheck, 0, 4) + + props = append(props, + NewPropertyCheck(CompLink, PropOperationRef, + l.OperationRef.ValueNode, r.OperationRef.ValueNode, + v3.OperationRefLabel, &changes, l, r), + NewPropertyCheck(CompLink, PropOperationID, + l.OperationId.ValueNode, r.OperationId.ValueNode, + v3.OperationIdLabel, &changes, l, r), + NewPropertyCheck(CompLink, PropRequestBody, + l.RequestBody.ValueNode, r.RequestBody.ValueNode, + v3.RequestBodyLabel, &changes, l, r), + NewPropertyCheck(CompLink, PropDescription, + l.Description.ValueNode, r.Description.ValueNode, + v3.DescriptionLabel, &changes, l, r), + ) + + CheckProperties(props) + lc := new(LinkChanges) + lc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) + + // server + if !l.Server.IsEmpty() && !r.Server.IsEmpty() { + if !low.AreEqual(l.Server.Value, r.Server.Value) { + lc.ServerChanges = CompareServers(l.Server.Value, r.Server.Value) + } + } + if !l.Server.IsEmpty() && r.Server.IsEmpty() { + CreateChange(&changes, PropertyRemoved, v3.ServerLabel, + l.Server.ValueNode, nil, BreakingRemoved(CompLink, PropServer), + l.Server.Value, nil) + } + if l.Server.IsEmpty() && !r.Server.IsEmpty() { + CreateChange(&changes, PropertyAdded, v3.ServerLabel, + nil, r.Server.ValueNode, BreakingAdded(CompLink, PropServer), + nil, r.Server.Value) + } + + // parameters + lValues := make(map[string]low.ValueReference[string]) + rValues := make(map[string]low.ValueReference[string]) + for k, v := range l.Parameters.Value.FromOldest() { + lValues[k.Value] = v + } + for k, v := range r.Parameters.Value.FromOldest() { + rValues[k.Value] = v + } + for k := range lValues { + if _, ok := rValues[k]; !ok { + CreateChange(&changes, ObjectRemoved, v3.ParametersLabel, + lValues[k].ValueNode, nil, BreakingRemoved(CompLink, PropParameters), + k, nil) + continue + } + if lValues[k].Value != rValues[k].Value { + CreateChange(&changes, Modified, v3.ParametersLabel, + lValues[k].ValueNode, rValues[k].ValueNode, BreakingModified(CompLink, PropParameters), + k, k) + } + + } + for k := range rValues { + if _, ok := lValues[k]; !ok { + CreateChange(&changes, ObjectAdded, v3.ParametersLabel, + nil, rValues[k].ValueNode, BreakingAdded(CompLink, PropParameters), + nil, k) + } + } + + lc.PropertyChanges = NewPropertyChanges(changes) + return lc +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/media_type.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/media_type.go new file mode 100644 index 000000000..90de6f7ae --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/media_type.go @@ -0,0 +1,170 @@ +// Copyright 2022-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +// MediaTypeChanges represent changes made between two OpenAPI MediaType instances. +type MediaTypeChanges struct { + *PropertyChanges + SchemaChanges *SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"` + ItemSchemaChanges *SchemaChanges `json:"itemSchemas,omitempty" yaml:"itemSchemas,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + ExampleChanges map[string]*ExampleChanges `json:"examples,omitempty" yaml:"examples,omitempty"` + EncodingChanges map[string]*EncodingChanges `json:"encoding,omitempty" yaml:"encoding,omitempty"` + ItemEncodingChanges map[string]*EncodingChanges `json:"itemEncoding,omitempty" yaml:"itemEncoding,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between MediaType objects +func (m *MediaTypeChanges) GetAllChanges() []*Change { + if m == nil { + return nil + } + var changes []*Change + changes = append(changes, m.Changes...) + if m.SchemaChanges != nil { + changes = append(changes, m.SchemaChanges.GetAllChanges()...) + } + if m.ItemSchemaChanges != nil { + changes = append(changes, m.ItemSchemaChanges.GetAllChanges()...) + } + for k := range m.ExampleChanges { + changes = append(changes, m.ExampleChanges[k].GetAllChanges()...) + } + for k := range m.EncodingChanges { + changes = append(changes, m.EncodingChanges[k].GetAllChanges()...) + } + for k := range m.ItemEncodingChanges { + changes = append(changes, m.ItemEncodingChanges[k].GetAllChanges()...) + } + if m.ExtensionChanges != nil { + changes = append(changes, m.ExtensionChanges.GetAllChanges()...) + } + return changes +} + +// TotalChanges returns the total number of changes between two MediaType instances. +func (m *MediaTypeChanges) TotalChanges() int { + if m == nil { + return 0 + } + c := m.PropertyChanges.TotalChanges() + for k := range m.ExampleChanges { + c += m.ExampleChanges[k].TotalChanges() + } + if m.SchemaChanges != nil { + c += m.SchemaChanges.TotalChanges() + } + if m.ItemSchemaChanges != nil { + c += m.ItemSchemaChanges.TotalChanges() + } + if len(m.EncodingChanges) > 0 { + for i := range m.EncodingChanges { + c += m.EncodingChanges[i].TotalChanges() + } + } + if len(m.ItemEncodingChanges) > 0 { + for i := range m.ItemEncodingChanges { + c += m.ItemEncodingChanges[i].TotalChanges() + } + } + if m.ExtensionChanges != nil { + c += m.ExtensionChanges.TotalChanges() + } + return c +} + +// TotalBreakingChanges returns the total number of breaking changes made between two MediaType instances. +func (m *MediaTypeChanges) TotalBreakingChanges() int { + c := m.PropertyChanges.TotalBreakingChanges() + for k := range m.ExampleChanges { + c += m.ExampleChanges[k].TotalBreakingChanges() + } + if m.SchemaChanges != nil { + c += m.SchemaChanges.TotalBreakingChanges() + } + if m.ItemSchemaChanges != nil { + c += m.ItemSchemaChanges.TotalBreakingChanges() + } + if len(m.EncodingChanges) > 0 { + for i := range m.EncodingChanges { + c += m.EncodingChanges[i].TotalBreakingChanges() + } + } + if len(m.ItemEncodingChanges) > 0 { + for i := range m.ItemEncodingChanges { + c += m.ItemEncodingChanges[i].TotalBreakingChanges() + } + } + return c +} + +// CompareMediaTypes compares a left and a right MediaType object for any changes. If found, a pointer to a +// MediaTypeChanges instance is returned; otherwise nothing is returned. +func CompareMediaTypes(l, r *v3.MediaType) *MediaTypeChanges { + var props []*PropertyCheck + var changes []*Change + + mc := new(MediaTypeChanges) + + if low.AreEqual(l, r) { + return nil + } + + // Example + CheckPropertyAdditionOrRemovalWithEncoding(l.Example.ValueNode, r.Example.ValueNode, + v3.ExampleLabel, &changes, + BreakingAdded(CompMediaType, PropExample) || BreakingRemoved(CompMediaType, PropExample), + l.Example.Value, r.Example.Value) + CheckForModificationWithEncoding(l.Example.ValueNode, r.Example.ValueNode, + v3.ExampleLabel, &changes, BreakingModified(CompMediaType, PropExample), + l.Example.Value, r.Example.Value) + + CheckProperties(props) + + // schema + if !l.Schema.IsEmpty() && !r.Schema.IsEmpty() { + mc.SchemaChanges = CompareSchemas(l.Schema.Value, r.Schema.Value) + } + if !l.Schema.IsEmpty() && r.Schema.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.SchemaLabel, l.Schema.ValueNode, + nil, BreakingRemoved(CompMediaType, PropSchema), l.Schema.Value, nil) + } + if l.Schema.IsEmpty() && !r.Schema.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.SchemaLabel, nil, + r.Schema.ValueNode, BreakingAdded(CompMediaType, PropSchema), nil, r.Schema.Value) + } + + // examples - use nil-aware version so added/removed examples appear in the map for tree rendering + mc.ExampleChanges = CheckMapForChangesWithNilSupport(l.Examples.Value, r.Examples.Value, + &changes, v3.ExamplesLabel, CompareExamples) + + // encoding + mc.EncodingChanges = CheckMapForChanges(l.Encoding.Value, r.Encoding.Value, + &changes, v3.EncodingLabel, CompareEncoding) + + // itemSchema + if !l.ItemSchema.IsEmpty() && !r.ItemSchema.IsEmpty() { + mc.ItemSchemaChanges = CompareSchemas(l.ItemSchema.Value, r.ItemSchema.Value) + } + if !l.ItemSchema.IsEmpty() && r.ItemSchema.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.ItemSchemaLabel, l.ItemSchema.ValueNode, + nil, BreakingRemoved(CompMediaType, PropItemSchema), l.ItemSchema.Value, nil) + } + if l.ItemSchema.IsEmpty() && !r.ItemSchema.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.ItemSchemaLabel, nil, + r.ItemSchema.ValueNode, BreakingAdded(CompMediaType, PropItemSchema), nil, r.ItemSchema.Value) + } + + // itemEncoding + mc.ItemEncodingChanges = CheckMapForChanges(l.ItemEncoding.Value, r.ItemEncoding.Value, + &changes, v3.ItemEncodingLabel, CompareEncoding) + + mc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) + mc.PropertyChanges = NewPropertyChanges(changes) + return mc +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/oauth_flows.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/oauth_flows.go new file mode 100644 index 000000000..b6373299f --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/oauth_flows.go @@ -0,0 +1,270 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "github.com/pb33f/libopenapi/datamodel/low" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +// OAuthFlowsChanges represents changes found between two OpenAPI OAuthFlows objects. +type OAuthFlowsChanges struct { + *PropertyChanges + ImplicitChanges *OAuthFlowChanges `json:"implicit,omitempty" yaml:"implicit,omitempty"` + PasswordChanges *OAuthFlowChanges `json:"password,omitempty" yaml:"password,omitempty"` + ClientCredentialsChanges *OAuthFlowChanges `json:"clientCredentials,omitempty" yaml:"clientCredentials,omitempty"` + AuthorizationCodeChanges *OAuthFlowChanges `json:"authCode,omitempty" yaml:"authCode,omitempty"` + DeviceChanges *OAuthFlowChanges `json:"device,omitempty" yaml:"device,omitempty"` // OpenAPI 3.2+ device flow + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between OAuthFlows objects +func (o *OAuthFlowsChanges) GetAllChanges() []*Change { + if o == nil { + return nil + } + var changes []*Change + changes = append(changes, o.Changes...) + if o.ImplicitChanges != nil { + changes = append(changes, o.ImplicitChanges.GetAllChanges()...) + } + if o.PasswordChanges != nil { + changes = append(changes, o.PasswordChanges.GetAllChanges()...) + } + if o.ClientCredentialsChanges != nil { + changes = append(changes, o.ClientCredentialsChanges.GetAllChanges()...) + } + if o.AuthorizationCodeChanges != nil { + changes = append(changes, o.AuthorizationCodeChanges.GetAllChanges()...) + } + if o.DeviceChanges != nil { + changes = append(changes, o.DeviceChanges.GetAllChanges()...) + } + if o.ExtensionChanges != nil { + changes = append(changes, o.ExtensionChanges.GetAllChanges()...) + } + return changes +} + +// TotalChanges returns the number of changes made between two OAuthFlows instances. +func (o *OAuthFlowsChanges) TotalChanges() int { + if o == nil { + return 0 + } + c := o.PropertyChanges.TotalChanges() + if o.ImplicitChanges != nil { + c += o.ImplicitChanges.TotalChanges() + } + if o.PasswordChanges != nil { + c += o.PasswordChanges.TotalChanges() + } + if o.ClientCredentialsChanges != nil { + c += o.ClientCredentialsChanges.TotalChanges() + } + if o.AuthorizationCodeChanges != nil { + c += o.AuthorizationCodeChanges.TotalChanges() + } + if o.DeviceChanges != nil { + c += o.DeviceChanges.TotalChanges() + } + if o.ExtensionChanges != nil { + c += o.ExtensionChanges.TotalChanges() + } + return c +} + +// TotalBreakingChanges returns the number of breaking changes made between two OAuthFlows objects. +func (o *OAuthFlowsChanges) TotalBreakingChanges() int { + c := o.PropertyChanges.TotalBreakingChanges() + if o.ImplicitChanges != nil { + c += o.ImplicitChanges.TotalBreakingChanges() + } + if o.PasswordChanges != nil { + c += o.PasswordChanges.TotalBreakingChanges() + } + if o.ClientCredentialsChanges != nil { + c += o.ClientCredentialsChanges.TotalBreakingChanges() + } + if o.AuthorizationCodeChanges != nil { + c += o.AuthorizationCodeChanges.TotalBreakingChanges() + } + if o.DeviceChanges != nil { + c += o.DeviceChanges.TotalBreakingChanges() + } + return c +} + +// CompareOAuthFlows compares a left and right OAuthFlows object. If changes are found a pointer to *OAuthFlowsChanges +// is returned, otherwise nil is returned. +func CompareOAuthFlows(l, r *v3.OAuthFlows) *OAuthFlowsChanges { + if low.AreEqual(l, r) { + return nil + } + + oa := new(OAuthFlowsChanges) + var changes []*Change + + // client credentials + if !l.ClientCredentials.IsEmpty() && !r.ClientCredentials.IsEmpty() { + oa.ClientCredentialsChanges = CompareOAuthFlow(l.ClientCredentials.Value, r.ClientCredentials.Value) + } + if !l.ClientCredentials.IsEmpty() && r.ClientCredentials.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.ClientCredentialsLabel, + l.ClientCredentials.ValueNode, nil, BreakingRemoved(CompOAuthFlows, PropClientCredentials), + l.ClientCredentials.Value, nil) + } + if l.ClientCredentials.IsEmpty() && !r.ClientCredentials.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.ClientCredentialsLabel, + nil, r.ClientCredentials.ValueNode, BreakingAdded(CompOAuthFlows, PropClientCredentials), + nil, r.ClientCredentials.Value) + } + + // implicit + if !l.Implicit.IsEmpty() && !r.Implicit.IsEmpty() { + oa.ImplicitChanges = CompareOAuthFlow(l.Implicit.Value, r.Implicit.Value) + } + if !l.Implicit.IsEmpty() && r.Implicit.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.ImplicitLabel, + l.Implicit.ValueNode, nil, BreakingRemoved(CompOAuthFlows, PropImplicit), + l.Implicit.Value, nil) + } + if l.Implicit.IsEmpty() && !r.Implicit.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.ImplicitLabel, + nil, r.Implicit.ValueNode, BreakingAdded(CompOAuthFlows, PropImplicit), + nil, r.Implicit.Value) + } + + // password + if !l.Password.IsEmpty() && !r.Password.IsEmpty() { + oa.PasswordChanges = CompareOAuthFlow(l.Password.Value, r.Password.Value) + } + if !l.Password.IsEmpty() && r.Password.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.PasswordLabel, + l.Password.ValueNode, nil, BreakingRemoved(CompOAuthFlows, PropPassword), + l.Password.Value, nil) + } + if l.Password.IsEmpty() && !r.Password.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.PasswordLabel, + nil, r.Password.ValueNode, BreakingAdded(CompOAuthFlows, PropPassword), + nil, r.Password.Value) + } + + // auth code + if !l.AuthorizationCode.IsEmpty() && !r.AuthorizationCode.IsEmpty() { + oa.AuthorizationCodeChanges = CompareOAuthFlow(l.AuthorizationCode.Value, r.AuthorizationCode.Value) + } + if !l.AuthorizationCode.IsEmpty() && r.AuthorizationCode.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.AuthorizationCodeLabel, + l.AuthorizationCode.ValueNode, nil, BreakingRemoved(CompOAuthFlows, PropAuthorizationCode), + l.AuthorizationCode.Value, nil) + } + if l.AuthorizationCode.IsEmpty() && !r.AuthorizationCode.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.AuthorizationCodeLabel, + nil, r.AuthorizationCode.ValueNode, BreakingAdded(CompOAuthFlows, PropAuthorizationCode), + nil, r.AuthorizationCode.Value) + } + + // device flow (OpenAPI 3.2+) + if !l.Device.IsEmpty() && !r.Device.IsEmpty() { + oa.DeviceChanges = CompareOAuthFlow(l.Device.Value, r.Device.Value) + } + if !l.Device.IsEmpty() && r.Device.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.DeviceLabel, + l.Device.ValueNode, nil, BreakingRemoved(CompOAuthFlows, PropDevice), + l.Device.Value, nil) + } + if l.Device.IsEmpty() && !r.Device.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.DeviceLabel, + nil, r.Device.ValueNode, BreakingAdded(CompOAuthFlows, PropDevice), + nil, r.Device.Value) + } + + oa.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) + oa.PropertyChanges = NewPropertyChanges(changes) + return oa +} + +// OAuthFlowChanges represents an OpenAPI OAuthFlow object. +type OAuthFlowChanges struct { + *PropertyChanges + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between OAuthFlow objects +func (o *OAuthFlowChanges) GetAllChanges() []*Change { + if o == nil { + return nil + } + var changes []*Change + changes = append(changes, o.Changes...) + if o.ExtensionChanges != nil { + changes = append(changes, o.ExtensionChanges.GetAllChanges()...) + } + return changes +} + +// TotalChanges returns the total number of changes made between two OAuthFlow objects +func (o *OAuthFlowChanges) TotalChanges() int { + if o == nil { + return 0 + } + c := o.PropertyChanges.TotalChanges() + if o.ExtensionChanges != nil { + c += o.ExtensionChanges.TotalChanges() + } + return c +} + +// TotalBreakingChanges returns the total number of breaking changes made between two OAuthFlow objects +func (o *OAuthFlowChanges) TotalBreakingChanges() int { + return o.PropertyChanges.TotalBreakingChanges() +} + +// CompareOAuthFlow checks a left and a right OAuthFlow object for changes. If found, returns a pointer to +// an OAuthFlowChanges instance, or nil if nothing is found. +func CompareOAuthFlow(l, r *v3.OAuthFlow) *OAuthFlowChanges { + if low.AreEqual(l, r) { + return nil + } + + var changes []*Change + props := make([]*PropertyCheck, 0, 3) + + props = append(props, + NewPropertyCheck(CompOAuthFlow, PropAuthorizationURL, + l.AuthorizationUrl.ValueNode, r.AuthorizationUrl.ValueNode, + v3.AuthorizationUrlLabel, &changes, l, r), + NewPropertyCheck(CompOAuthFlow, PropTokenURL, + l.TokenUrl.ValueNode, r.TokenUrl.ValueNode, + v3.TokenUrlLabel, &changes, l, r), + NewPropertyCheck(CompOAuthFlow, PropRefreshURL, + l.RefreshUrl.ValueNode, r.RefreshUrl.ValueNode, + v3.RefreshUrlLabel, &changes, l, r), + ) + + CheckProperties(props) + + for k, v := range l.Scopes.Value.FromOldest() { + if r != nil && r.FindScope(k.Value) == nil { + CreateChange(&changes, ObjectRemoved, v3.Scopes, v.ValueNode, nil, BreakingRemoved(CompOAuthFlow, PropScopes), k.Value, nil) + continue + } + if r != nil && r.FindScope(k.Value) != nil { + if v.Value != r.FindScope(k.Value).Value { + CreateChange(&changes, Modified, v3.Scopes, + v.ValueNode, r.FindScope(k.Value).ValueNode, BreakingModified(CompOAuthFlow, PropScopes), + v.Value, r.FindScope(k.Value).Value) + } + } + } + for k, v := range r.Scopes.Value.FromOldest() { + if l != nil && l.FindScope(k.Value) == nil { + CreateChange(&changes, ObjectAdded, v3.Scopes, nil, v.ValueNode, BreakingAdded(CompOAuthFlow, PropScopes), nil, k.Value) + } + } + oa := new(OAuthFlowChanges) + oa.PropertyChanges = NewPropertyChanges(changes) + oa.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) + return oa +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/operation.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/operation.go new file mode 100644 index 000000000..664362398 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/operation.go @@ -0,0 +1,613 @@ +// Copyright 2022-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "reflect" + "sort" + "strings" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + v2 "github.com/pb33f/libopenapi/datamodel/low/v2" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "go.yaml.in/yaml/v4" +) + +// OperationChanges represent changes made between two Swagger or OpenAPI Operation objects. +type OperationChanges struct { + *PropertyChanges + ExternalDocChanges *ExternalDocChanges `json:"externalDoc,omitempty" yaml:"externalDoc,omitempty"` + ParameterChanges []*ParameterChanges `json:"parameters,omitempty" yaml:"parameters,omitempty"` + ResponsesChanges *ResponsesChanges `json:"responses,omitempty" yaml:"responses,omitempty"` + SecurityRequirementChanges []*SecurityRequirementChanges `json:"securityRequirements,omitempty" yaml:"securityRequirements,omitempty"` + + // OpenAPI 3+ only changes + RequestBodyChanges *RequestBodyChanges `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"` + ServerChanges []*ServerChanges `json:"servers,omitempty" yaml:"servers,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + CallbackChanges map[string]*CallbackChanges `json:"callbacks,omitempty" yaml:"callbacks,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between Operation objects +func (o *OperationChanges) GetAllChanges() []*Change { + if o == nil { + return nil + } + var changes []*Change + changes = append(changes, o.Changes...) + if o.ExternalDocChanges != nil { + changes = append(changes, o.ExternalDocChanges.GetAllChanges()...) + } + for k := range o.ParameterChanges { + changes = append(changes, o.ParameterChanges[k].GetAllChanges()...) + } + if o.ResponsesChanges != nil { + changes = append(changes, o.ResponsesChanges.GetAllChanges()...) + } + for k := range o.SecurityRequirementChanges { + changes = append(changes, o.SecurityRequirementChanges[k].GetAllChanges()...) + } + if o.RequestBodyChanges != nil { + changes = append(changes, o.RequestBodyChanges.GetAllChanges()...) + } + for k := range o.ServerChanges { + changes = append(changes, o.ServerChanges[k].GetAllChanges()...) + } + for k := range o.CallbackChanges { + changes = append(changes, o.CallbackChanges[k].GetAllChanges()...) + } + if o.ExtensionChanges != nil { + changes = append(changes, o.ExtensionChanges.GetAllChanges()...) + } + return changes +} + +// TotalChanges returns the total number of changes made between two Swagger or OpenAPI Operation objects. +func (o *OperationChanges) TotalChanges() int { + if o == nil { + return 0 + } + c := o.PropertyChanges.TotalChanges() + if o.ExternalDocChanges != nil { + c += o.ExternalDocChanges.TotalChanges() + } + for k := range o.ParameterChanges { + c += o.ParameterChanges[k].TotalChanges() + } + if o.ResponsesChanges != nil { + c += o.ResponsesChanges.TotalChanges() + } + for k := range o.SecurityRequirementChanges { + c += o.SecurityRequirementChanges[k].TotalChanges() + } + if o.RequestBodyChanges != nil { + c += o.RequestBodyChanges.TotalChanges() + } + for k := range o.ServerChanges { + c += o.ServerChanges[k].TotalChanges() + } + for k := range o.CallbackChanges { + c += o.CallbackChanges[k].TotalChanges() + } + if o.ExtensionChanges != nil { + c += o.ExtensionChanges.TotalChanges() + } + return c +} + +// TotalBreakingChanges returns the total number of breaking changes made between two Swagger +// or OpenAPI Operation objects. +func (o *OperationChanges) TotalBreakingChanges() int { + c := o.PropertyChanges.TotalBreakingChanges() + if o.ExternalDocChanges != nil { + c += o.ExternalDocChanges.TotalBreakingChanges() + } + for k := range o.ParameterChanges { + c += o.ParameterChanges[k].TotalBreakingChanges() + } + if o.ResponsesChanges != nil { + c += o.ResponsesChanges.TotalBreakingChanges() + } + for k := range o.SecurityRequirementChanges { + c += o.SecurityRequirementChanges[k].TotalBreakingChanges() + } + for k := range o.CallbackChanges { + c += o.CallbackChanges[k].TotalBreakingChanges() + } + if o.RequestBodyChanges != nil { + c += o.RequestBodyChanges.TotalBreakingChanges() + } + for k := range o.ServerChanges { + c += o.ServerChanges[k].TotalBreakingChanges() + } + return c +} + +// check for properties shared between operations objects. +func addSharedOperationProperties(left, right low.SharedOperations, changes *[]*Change) []*PropertyCheck { + var props []*PropertyCheck + + // tags + if len(left.GetTags().Value) > 0 || len(right.GetTags().Value) > 0 { + ExtractStringValueSliceChangesWithRules(left.GetTags().Value, right.GetTags().Value, + changes, v3.TagsLabel, CompOperation, PropTags) + } + + // summary + addPropertyCheck(&props, left.GetSummary().ValueNode, right.GetSummary().ValueNode, + left.GetSummary(), right.GetSummary(), changes, v3.SummaryLabel, + BreakingModified(CompOperation, PropSummary), CompOperation, PropSummary) + + // description + addPropertyCheck(&props, left.GetDescription().ValueNode, right.GetDescription().ValueNode, + left.GetDescription(), right.GetDescription(), changes, v3.DescriptionLabel, + BreakingModified(CompOperation, PropDescription), CompOperation, PropDescription) + + // deprecated + addPropertyCheck(&props, left.GetDeprecated().ValueNode, right.GetDeprecated().ValueNode, + left.GetDeprecated(), right.GetDeprecated(), changes, v3.DeprecatedLabel, + BreakingModified(CompOperation, PropDeprecated), CompOperation, PropDeprecated) + + // operation id + addPropertyCheck(&props, left.GetOperationId().ValueNode, right.GetOperationId().ValueNode, + left.GetOperationId(), right.GetOperationId(), changes, v3.OperationIdLabel, + BreakingModified(CompOperation, PropOperationID), CompOperation, PropOperationID) + + return props +} + +// check shared objects +func compareSharedOperationObjects(l, r low.SharedOperations, changes *[]*Change, opChanges *OperationChanges) { + // external docs + if !l.GetExternalDocs().IsEmpty() && !r.GetExternalDocs().IsEmpty() { + lExtDoc := l.GetExternalDocs().Value.(*base.ExternalDoc) + rExtDoc := r.GetExternalDocs().Value.(*base.ExternalDoc) + if !low.AreEqual(lExtDoc, rExtDoc) { + opChanges.ExternalDocChanges = CompareExternalDocs(lExtDoc, rExtDoc) + } + } + if l.GetExternalDocs().IsEmpty() && !r.GetExternalDocs().IsEmpty() { + CreateChange(changes, PropertyAdded, v3.ExternalDocsLabel, + nil, r.GetExternalDocs().ValueNode, BreakingAdded(CompOperation, PropExternalDocs), nil, + r.GetExternalDocs().Value) + } + if !l.GetExternalDocs().IsEmpty() && r.GetExternalDocs().IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.ExternalDocsLabel, + l.GetExternalDocs().ValueNode, nil, BreakingRemoved(CompOperation, PropExternalDocs), l.GetExternalDocs().Value, + nil) + } + + // responses + if !l.GetResponses().IsEmpty() && !r.GetResponses().IsEmpty() { + opChanges.ResponsesChanges = CompareResponses(l.GetResponses().Value, r.GetResponses().Value) + } + if l.GetResponses().IsEmpty() && !r.GetResponses().IsEmpty() { + CreateChange(changes, PropertyAdded, v3.ResponsesLabel, + nil, r.GetResponses().ValueNode, BreakingAdded(CompOperation, PropResponses), nil, + r.GetResponses().Value) + } + if !l.GetResponses().IsEmpty() && r.GetResponses().IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.ResponsesLabel, + l.GetResponses().ValueNode, nil, BreakingRemoved(CompOperation, PropResponses), l.GetResponses().Value, + nil) + } +} + +// CompareOperations compares a left and right Swagger or OpenAPI Operation object. If changes are found, returns +// a pointer to an OperationChanges instance, or nil if nothing is found. +func CompareOperations(l, r any) *OperationChanges { + var changes []*Change + var props []*PropertyCheck + + oc := new(OperationChanges) + + // Swagger + if reflect.TypeOf(&v2.Operation{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v2.Operation{}) == reflect.TypeOf(r) { + + lOperation := l.(*v2.Operation) + rOperation := r.(*v2.Operation) + + // perform hash check to avoid further processing + if low.AreEqual(lOperation, rOperation) { + return nil + } + + props = append(props, addSharedOperationProperties(lOperation, rOperation, &changes)...) + + compareSharedOperationObjects(lOperation, rOperation, &changes, oc) + + // parameters + lParamsUntyped := lOperation.GetParameters() + rParamsUntyped := rOperation.GetParameters() + if !lParamsUntyped.IsEmpty() && !rParamsUntyped.IsEmpty() { + lParams := lParamsUntyped.Value.([]low.ValueReference[*v2.Parameter]) + rParams := rParamsUntyped.Value.([]low.ValueReference[*v2.Parameter]) + + lv := make(map[string]*v2.Parameter, len(lParams)) + rv := make(map[string]*v2.Parameter, len(rParams)) + lRefs := make(map[string]*low.ValueReference[*v2.Parameter], len(lParams)) + rRefs := make(map[string]*low.ValueReference[*v2.Parameter], len(rParams)) + + for i := range lParams { + s := lParams[i].Value.Name.Value + lv[s] = lParams[i].Value + lRefs[s] = &lParams[i] // Keep the reference wrapper + } + for i := range rParams { + s := rParams[i].Value.Name.Value + rv[s] = rParams[i].Value + rRefs[s] = &rParams[i] // Keep the reference wrapper + } + + var paramChanges []*ParameterChanges + for n := range lv { + if _, ok := rv[n]; ok { + if !low.AreEqual(lv[n], rv[n]) { + ch := CompareParameters(lv[n], rv[n]) + if ch != nil { + // Preserve reference information if this parameter is a $ref + PreserveParameterReference(lRefs, rRefs, n, ch) + paramChanges = append(paramChanges, ch) + } + } + continue + } + CreateChange(&changes, ObjectRemoved, v3.ParametersLabel, + lv[n].Name.ValueNode, nil, BreakingRemoved(CompOperation, PropParameters), lv[n], + nil) + + } + for n := range rv { + if _, ok := lv[n]; !ok { + CreateChange(&changes, ObjectAdded, v3.ParametersLabel, + nil, rv[n].Name.ValueNode, rv[n].Required.Value, nil, + rv[n]) + } + } + oc.ParameterChanges = paramChanges + } + if !lParamsUntyped.IsEmpty() && rParamsUntyped.IsEmpty() { + CreateChange(&changes, PropertyRemoved, v3.ParametersLabel, + lParamsUntyped.ValueNode, nil, true, lParamsUntyped.Value, + nil) + } + if lParamsUntyped.IsEmpty() && !rParamsUntyped.IsEmpty() { + rParams := rParamsUntyped.Value.([]low.ValueReference[*v2.Parameter]) + breaking := false + for i := range rParams { + if rParams[i].Value.Required.Value { + breaking = true + } + } + CreateChange(&changes, PropertyAdded, v3.ParametersLabel, + nil, rParamsUntyped.ValueNode, breaking, nil, + rParamsUntyped.Value) + } + + // security + if !lOperation.Security.IsEmpty() || !rOperation.Security.IsEmpty() { + checkSecurity(lOperation.Security, rOperation.Security, &changes, oc) + } + + // produces + if len(lOperation.Produces.Value) > 0 || len(rOperation.Produces.Value) > 0 { + ExtractStringValueSliceChanges(lOperation.Produces.Value, rOperation.Produces.Value, + &changes, v3.ProducesLabel, true) + } + + // consumes + if len(lOperation.Consumes.Value) > 0 || len(rOperation.Consumes.Value) > 0 { + ExtractStringValueSliceChanges(lOperation.Consumes.Value, rOperation.Consumes.Value, + &changes, v3.ConsumesLabel, true) + } + + // schemes + if len(lOperation.Schemes.Value) > 0 || len(rOperation.Schemes.Value) > 0 { + ExtractStringValueSliceChanges(lOperation.Schemes.Value, rOperation.Schemes.Value, + &changes, v3.SchemesLabel, true) + } + + oc.ExtensionChanges = CompareExtensions(lOperation.Extensions, rOperation.Extensions) + } + + // OpenAPI + if reflect.TypeOf(&v3.Operation{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v3.Operation{}) == reflect.TypeOf(r) { + + lOperation := l.(*v3.Operation) + rOperation := r.(*v3.Operation) + + // perform hash check to avoid further processing + if low.AreEqual(lOperation, rOperation) { + return nil + } + + props = append(props, addSharedOperationProperties(lOperation, rOperation, &changes)...) + compareSharedOperationObjects(lOperation, rOperation, &changes, oc) + + // parameters + lParamsUntyped := lOperation.GetParameters() + rParamsUntyped := rOperation.GetParameters() + if !lParamsUntyped.IsEmpty() && !rParamsUntyped.IsEmpty() { + lParams := lParamsUntyped.Value.([]low.ValueReference[*v3.Parameter]) + rParams := rParamsUntyped.Value.([]low.ValueReference[*v3.Parameter]) + + lv := make(map[string]*v3.Parameter, len(lParams)) + rv := make(map[string]*v3.Parameter, len(rParams)) + lRefs := make(map[string]*low.ValueReference[*v3.Parameter], len(lParams)) + rRefs := make(map[string]*low.ValueReference[*v3.Parameter], len(rParams)) + + for i := range lParams { + s := lParams[i].Value.Name.Value + lv[s] = lParams[i].Value + lRefs[s] = &lParams[i] // Keep the reference wrapper + } + for i := range rParams { + s := rParams[i].Value.Name.Value + rv[s] = rParams[i].Value + rRefs[s] = &rParams[i] // Keep the reference wrapper + } + + var paramChanges []*ParameterChanges + for n := range lv { + if _, ok := rv[n]; ok { + if !low.AreEqual(lv[n], rv[n]) { + ch := CompareParameters(lv[n], rv[n]) + if ch != nil { + // Preserve reference information if this parameter is a $ref + PreserveParameterReference(lRefs, rRefs, n, ch) + paramChanges = append(paramChanges, ch) + } + } + continue + } + CreateChange(&changes, ObjectRemoved, v3.ParametersLabel, + lv[n].Name.ValueNode, nil, BreakingRemoved(CompOperation, PropParameters), lv[n], + nil) + + } + for n := range rv { + if _, ok := lv[n]; !ok { + // Check configurable breaking rules first + breaking := BreakingAdded(CompOperation, PropParameters) + // If config doesn't say breaking, fall back to semantic check (required parameter) + if !breaking { + breaking = rv[n].Required.Value + } + CreateChange(&changes, ObjectAdded, v3.ParametersLabel, + nil, rv[n].Name.ValueNode, breaking, nil, + rv[n]) + } + } + oc.ParameterChanges = paramChanges + } + if !lParamsUntyped.IsEmpty() && rParamsUntyped.IsEmpty() { + CreateChange(&changes, PropertyRemoved, v3.ParametersLabel, + lParamsUntyped.ValueNode, nil, BreakingRemoved(CompOperation, PropParameters), lParamsUntyped.Value, + nil) + } + if lParamsUntyped.IsEmpty() && !rParamsUntyped.IsEmpty() { + rParams := rParamsUntyped.Value.([]low.ValueReference[*v3.Parameter]) + // Check configurable breaking rules first + breaking := BreakingAdded(CompOperation, PropParameters) + // If config doesn't say breaking, fall back to semantic check (required parameter) + if !breaking { + for i := range rParams { + if rParams[i].Value.Required.Value { + breaking = true + break + } + } + } + CreateChange(&changes, PropertyAdded, v3.ParametersLabel, + nil, rParamsUntyped.ValueNode, breaking, nil, + rParamsUntyped.Value) + } + + // security + if !lOperation.Security.IsEmpty() || !rOperation.Security.IsEmpty() { + checkSecurity(lOperation.Security, rOperation.Security, &changes, oc) + } + + // request body + if !lOperation.RequestBody.IsEmpty() && !rOperation.RequestBody.IsEmpty() { + if !low.AreEqual(lOperation.RequestBody.Value, rOperation.RequestBody.Value) { + oc.RequestBodyChanges = CompareRequestBodies(lOperation.RequestBody.Value, rOperation.RequestBody.Value) + } + } + if !lOperation.RequestBody.IsEmpty() && rOperation.RequestBody.IsEmpty() { + CreateChange(&changes, PropertyRemoved, v3.RequestBodyLabel, + lOperation.RequestBody.ValueNode, nil, BreakingRemoved(CompOperation, PropRequestBody), lOperation.RequestBody.Value, + nil) + } + if lOperation.RequestBody.IsEmpty() && !rOperation.RequestBody.IsEmpty() { + CreateChange(&changes, PropertyAdded, v3.RequestBodyLabel, + nil, rOperation.RequestBody.ValueNode, BreakingAdded(CompOperation, PropRequestBody), nil, + rOperation.RequestBody.Value) + } + + // callbacks - use CheckMapForChangesWithNilSupport to properly populate CallbackChanges + // for added/removed callbacks, enabling proper tree hierarchy rendering + oc.CallbackChanges = CheckMapForChangesWithNilSupport(lOperation.Callbacks.Value, rOperation.Callbacks.Value, + &changes, v3.CallbacksLabel, CompareCallback) + + // servers + oc.ServerChanges = checkServers(lOperation.Servers, rOperation.Servers, CompOperation, PropServers) + oc.ExtensionChanges = CompareExtensions(lOperation.Extensions, rOperation.Extensions) + + } + CheckProperties(props) + oc.PropertyChanges = NewPropertyChanges(changes) + return oc +} + +// check servers property +// component and property are used for breaking rules lookup (e.g., CompOperation/PropServers or CompServers/"") +func checkServers(lServers, rServers low.NodeReference[[]low.ValueReference[*v3.Server]], component, property string) []*ServerChanges { + var serverChanges []*ServerChanges + + if !lServers.IsEmpty() && !rServers.IsEmpty() { + + lv := make(map[string]low.ValueReference[*v3.Server], len(lServers.Value)) + rv := make(map[string]low.ValueReference[*v3.Server], len(rServers.Value)) + + for i := range lServers.Value { + var s string + if !lServers.Value[i].Value.URL.IsEmpty() { + s = lServers.Value[i].Value.URL.Value + } else { + s = low.GenerateHashString(lServers.Value[i].Value) + } + lv[s] = lServers.Value[i] + } + for i := range rServers.Value { + var s string + if !rServers.Value[i].Value.URL.IsEmpty() { + s = rServers.Value[i].Value.URL.Value + } else { + s = low.GenerateHashString(rServers.Value[i].Value) + } + rv[s] = rServers.Value[i] + } + + for k := range lv { + + var changes []*Change + + if _, ok := rv[k]; ok { + if !low.AreEqual(lv[k].Value, rv[k].Value) { + serverChanges = append(serverChanges, CompareServers(lv[k].Value, rv[k].Value)) + } + continue + } + lv[k].ValueNode.Value = lv[k].Value.URL.Value + CreateChange(&changes, ObjectRemoved, v3.ServersLabel, + lv[k].ValueNode, nil, BreakingRemoved(component, property), lv[k].Value, + nil) + sc := new(ServerChanges) + sc.PropertyChanges = NewPropertyChanges(changes) + serverChanges = append(serverChanges, sc) + + } + + for k := range rv { + if _, ok := lv[k]; !ok { + + var changes []*Change + rv[k].ValueNode.Value = rv[k].Value.URL.Value + CreateChange(&changes, ObjectAdded, v3.ServersLabel, + nil, rv[k].ValueNode, BreakingAdded(component, property), nil, + rv[k].Value) + + sc := new(ServerChanges) + sc.PropertyChanges = NewPropertyChanges(changes) + serverChanges = append(serverChanges, sc) + } + } + } + var changes []*Change + sc := new(ServerChanges) + if !lServers.IsEmpty() && rServers.IsEmpty() { + CreateChange(&changes, PropertyRemoved, v3.ServersLabel, + lServers.ValueNode, nil, BreakingRemoved(component, property), lServers.Value, + nil) + } + if lServers.IsEmpty() && !rServers.IsEmpty() { + CreateChange(&changes, PropertyAdded, v3.ServersLabel, + nil, rServers.ValueNode, BreakingAdded(component, property), nil, + rServers.Value) + } + sc.PropertyChanges = NewPropertyChanges(changes) + if len(changes) > 0 { + serverChanges = append(serverChanges, sc) + } + if len(serverChanges) <= 0 { + return nil + } + return serverChanges +} + +// check security property. +func checkSecurity(lSecurity, rSecurity low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]], + changes *[]*Change, oc any, +) { + lv := make(map[string]*base.SecurityRequirement, len(lSecurity.Value)) + rv := make(map[string]*base.SecurityRequirement, len(rSecurity.Value)) + lvn := make(map[string]*yaml.Node, len(lSecurity.Value)) + rvn := make(map[string]*yaml.Node, len(rSecurity.Value)) + + for i := range lSecurity.Value { + keys := lSecurity.Value[i].Value.GetKeys() + sort.Strings(keys) + s := strings.Join(keys, "|") + lv[s] = lSecurity.Value[i].Value + lvn[s] = lSecurity.Value[i].ValueNode + + } + for i := range rSecurity.Value { + keys := rSecurity.Value[i].Value.GetKeys() + sort.Strings(keys) + s := strings.Join(keys, "|") + rv[s] = rSecurity.Value[i].Value + rvn[s] = rSecurity.Value[i].ValueNode + } + + // Determine breaking rules based on type (zero allocations using type switch) + var addedBreaking, removedBreaking bool + switch oc.(type) { + case *DocumentChanges: + addedBreaking = BreakingAdded(CompSecurity, "") + removedBreaking = BreakingRemoved(CompSecurity, "") + case *OperationChanges: + addedBreaking = BreakingAdded(CompOperation, PropSecurity) + removedBreaking = BreakingRemoved(CompOperation, PropSecurity) + } + + var secChanges []*SecurityRequirementChanges + for n := range lv { + if _, ok := rv[n]; ok { + if !low.AreEqual(lv[n], rv[n]) { + ch := CompareSecurityRequirement(lv[n], rv[n]) + if ch != nil { + secChanges = append(secChanges, ch) + } + } + continue + } + // Whole security requirement was removed - create SecurityRequirementChanges + // so it appears under "Security Requirements" section + schemeNames := strings.Join(lv[n].GetKeys(), ", ") + + var reqChanges []*Change + CreateChange(&reqChanges, ObjectRemoved, schemeNames, + lvn[n], nil, removedBreaking, lv[n], nil) + secChanges = append(secChanges, &SecurityRequirementChanges{ + PropertyChanges: NewPropertyChanges(reqChanges), + }) + } + for n := range rv { + if _, ok := lv[n]; !ok { + // Whole security requirement was added - create SecurityRequirementChanges + // so it appears under "Security Requirements" section + schemeNames := strings.Join(rv[n].GetKeys(), ", ") + + var reqChanges []*Change + CreateChange(&reqChanges, ObjectAdded, schemeNames, + nil, rvn[n], addedBreaking, nil, rv[n]) + secChanges = append(secChanges, &SecurityRequirementChanges{ + PropertyChanges: NewPropertyChanges(reqChanges), + }) + } + } + + // Assign to correct type using type switch (zero allocations) + switch v := oc.(type) { + case *OperationChanges: + v.SecurityRequirementChanges = secChanges + case *DocumentChanges: + v.SecurityRequirementChanges = secChanges + } +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/parameter.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/parameter.go new file mode 100644 index 000000000..a533bfe38 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/parameter.go @@ -0,0 +1,366 @@ +// Copyright 2022-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "reflect" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + v2 "github.com/pb33f/libopenapi/datamodel/low/v2" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// ParameterChanges represents changes found between Swagger or OpenAPI Parameter objects. +type ParameterChanges struct { + *PropertyChanges + Name string `json:"name,omitempty" yaml:"name,omitempty"` + SchemaChanges *SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + + // Swagger supports Items. + ItemsChanges *ItemsChanges `json:"items,omitempty" yaml:"items,omitempty"` + + // OpenAPI supports examples and content types. + ExamplesChanges map[string]*ExampleChanges `json:"examples,omitempty" yaml:"examples,omitempty"` + ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between Parameter objects +func (p *ParameterChanges) GetAllChanges() []*Change { + if p == nil { + return nil + } + var changes []*Change + changes = append(changes, p.Changes...) + if p.SchemaChanges != nil { + changes = append(changes, p.SchemaChanges.GetAllChanges()...) + } + for i := range p.ExamplesChanges { + changes = append(changes, p.ExamplesChanges[i].GetAllChanges()...) + } + if p.ItemsChanges != nil { + changes = append(changes, p.ItemsChanges.GetAllChanges()...) + } + if p.ExtensionChanges != nil { + changes = append(changes, p.ExtensionChanges.GetAllChanges()...) + } + for i := range p.ContentChanges { + changes = append(changes, p.ContentChanges[i].GetAllChanges()...) + } + return changes +} + +// TotalChanges returns a count of everything that changed +func (p *ParameterChanges) TotalChanges() int { + if p == nil { + return 0 + } + c := p.PropertyChanges.TotalChanges() + if p.SchemaChanges != nil { + c += p.SchemaChanges.TotalChanges() + } + for i := range p.ExamplesChanges { + c += p.ExamplesChanges[i].TotalChanges() + } + if p.ItemsChanges != nil { + c += p.ItemsChanges.TotalChanges() + } + if p.ExtensionChanges != nil { + c += p.ExtensionChanges.TotalChanges() + } + for i := range p.ContentChanges { + c += p.ContentChanges[i].TotalChanges() + } + return c +} + +// TotalBreakingChanges always returns 0 for ExternalDoc objects, they are non-binding. +func (p *ParameterChanges) TotalBreakingChanges() int { + c := p.PropertyChanges.TotalBreakingChanges() + if p.SchemaChanges != nil { + c += p.SchemaChanges.TotalBreakingChanges() + } + if p.ItemsChanges != nil { + c += p.ItemsChanges.TotalBreakingChanges() + } + for i := range p.ContentChanges { + c += p.ContentChanges[i].TotalBreakingChanges() + } + return c +} + +func addPropertyCheck(props *[]*PropertyCheck, + lvn, rvn *yaml.Node, lv, rv any, changes *[]*Change, label string, breaking bool, + component, property string, +) { + *props = append(*props, &PropertyCheck{ + LeftNode: lvn, + RightNode: rvn, + Label: label, + Changes: changes, + Breaking: breaking, + Component: component, + Property: property, + Original: lv, + New: rv, + }) +} + +func addOpenAPIParameterProperties(left, right low.OpenAPIParameter, changes *[]*Change) []*PropertyCheck { + var props []*PropertyCheck + + // style + addPropertyCheck(&props, left.GetStyle().ValueNode, right.GetStyle().ValueNode, + left.GetStyle(), right.GetStyle(), changes, v3.StyleLabel, + BreakingModified(CompParameter, PropStyle), CompParameter, PropStyle) + + // allow reserved + addPropertyCheck(&props, left.GetAllowReserved().ValueNode, right.GetAllowReserved().ValueNode, + left.GetAllowReserved(), right.GetAllowReserved(), changes, v3.AllowReservedLabel, + BreakingModified(CompParameter, PropAllowReserved), CompParameter, PropAllowReserved) + + // explode + addPropertyCheck(&props, left.GetExplode().ValueNode, right.GetExplode().ValueNode, + left.GetExplode(), right.GetExplode(), changes, v3.ExplodeLabel, + BreakingModified(CompParameter, PropExplode), CompParameter, PropExplode) + + // deprecated + addPropertyCheck(&props, left.GetDeprecated().ValueNode, right.GetDeprecated().ValueNode, + left.GetDeprecated(), right.GetDeprecated(), changes, v3.DeprecatedLabel, + BreakingModified(CompParameter, PropDeprecated), CompParameter, PropDeprecated) + + // example + addPropertyCheck(&props, left.GetExample().ValueNode, right.GetExample().ValueNode, + left.GetExample(), right.GetExample(), changes, v3.ExampleLabel, + BreakingModified(CompParameter, PropExample), CompParameter, PropExample) + + return props +} + +func addSwaggerParameterProperties(left, right low.SwaggerParameter, changes *[]*Change) []*PropertyCheck { + var props []*PropertyCheck + + // type + addPropertyCheck(&props, left.GetType().ValueNode, right.GetType().ValueNode, + left.GetType(), right.GetType(), changes, v3.TypeLabel, true, CompParameter, PropType) + + // format + addPropertyCheck(&props, left.GetFormat().ValueNode, right.GetFormat().ValueNode, + left.GetFormat(), right.GetFormat(), changes, v3.FormatLabel, true, CompParameter, PropFormat) + + // collection format + addPropertyCheck(&props, left.GetCollectionFormat().ValueNode, right.GetCollectionFormat().ValueNode, + left.GetCollectionFormat(), right.GetCollectionFormat(), changes, v3.CollectionFormatLabel, true, CompParameter, PropCollectionFormat) + + // maximum + addPropertyCheck(&props, left.GetMaximum().ValueNode, right.GetMaximum().ValueNode, + left.GetMaximum(), right.GetMaximum(), changes, v3.MaximumLabel, true, CompParameter, PropMaximum) + + // minimum + addPropertyCheck(&props, left.GetMinimum().ValueNode, right.GetMinimum().ValueNode, + left.GetMinimum(), right.GetMinimum(), changes, v3.MinimumLabel, true, CompParameter, PropMinimum) + + // exclusive maximum + addPropertyCheck(&props, left.GetExclusiveMaximum().ValueNode, right.GetExclusiveMaximum().ValueNode, + left.GetExclusiveMaximum(), right.GetExclusiveMaximum(), changes, v3.ExclusiveMaximumLabel, true, CompParameter, PropExclusiveMaximum) + + // exclusive minimum + addPropertyCheck(&props, left.GetExclusiveMinimum().ValueNode, right.GetExclusiveMinimum().ValueNode, + left.GetExclusiveMinimum(), right.GetExclusiveMinimum(), changes, v3.ExclusiveMinimumLabel, true, CompParameter, PropExclusiveMinimum) + + // max length + addPropertyCheck(&props, left.GetMaxLength().ValueNode, right.GetMaxLength().ValueNode, + left.GetMaxLength(), right.GetMaxLength(), changes, v3.MaxLengthLabel, true, CompParameter, PropMaxLength) + + // min length + addPropertyCheck(&props, left.GetMinLength().ValueNode, right.GetMinLength().ValueNode, + left.GetMinLength(), right.GetMinLength(), changes, v3.MinLengthLabel, true, CompParameter, PropMinLength) + + // pattern + addPropertyCheck(&props, left.GetPattern().ValueNode, right.GetPattern().ValueNode, + left.GetPattern(), right.GetPattern(), changes, v3.PatternLabel, true, CompParameter, PropPattern) + + // max items + addPropertyCheck(&props, left.GetMaxItems().ValueNode, right.GetMaxItems().ValueNode, + left.GetMaxItems(), right.GetMaxItems(), changes, v3.MaxItemsLabel, true, CompParameter, PropMaxItems) + + // min items + addPropertyCheck(&props, left.GetMinItems().ValueNode, right.GetMinItems().ValueNode, + left.GetMinItems(), right.GetMinItems(), changes, v3.MinItemsLabel, true, CompParameter, PropMinItems) + + // unique items + addPropertyCheck(&props, left.GetUniqueItems().ValueNode, right.GetUniqueItems().ValueNode, + left.GetUniqueItems(), right.GetUniqueItems(), changes, v3.UniqueItemsLabel, true, CompParameter, PropUniqueItems) + + // default + addPropertyCheck(&props, left.GetDefault().ValueNode, right.GetDefault().ValueNode, + left.GetDefault(), right.GetDefault(), changes, v3.DefaultLabel, true, CompParameter, PropDefault) + + // multiple of + addPropertyCheck(&props, left.GetMultipleOf().ValueNode, right.GetMultipleOf().ValueNode, + left.GetMultipleOf(), right.GetMultipleOf(), changes, v3.MultipleOfLabel, true, CompParameter, PropMultipleOf) + + return props +} + +func addCommonParameterProperties(left, right low.SharedParameters, changes *[]*Change) []*PropertyCheck { + var props []*PropertyCheck + + addPropertyCheck(&props, left.GetName().ValueNode, right.GetName().ValueNode, + left.GetName(), right.GetName(), changes, v3.NameLabel, + BreakingModified(CompParameter, PropName), CompParameter, PropName) + + // in + addPropertyCheck(&props, left.GetIn().ValueNode, right.GetIn().ValueNode, + left.GetIn(), right.GetIn(), changes, v3.InLabel, + BreakingModified(CompParameter, PropIn), CompParameter, PropIn) + + // description + addPropertyCheck(&props, left.GetDescription().ValueNode, right.GetDescription().ValueNode, + left.GetDescription(), right.GetDescription(), changes, v3.DescriptionLabel, + BreakingModified(CompParameter, PropDescription), CompParameter, PropDescription) + + // required + addPropertyCheck(&props, left.GetRequired().ValueNode, right.GetRequired().ValueNode, + left.GetRequired(), right.GetRequired(), changes, v3.RequiredLabel, + BreakingModified(CompParameter, PropRequired), CompParameter, PropRequired) + + // allow empty value + addPropertyCheck(&props, left.GetAllowEmptyValue().ValueNode, right.GetAllowEmptyValue().ValueNode, + left.GetAllowEmptyValue(), right.GetAllowEmptyValue(), changes, v3.AllowEmptyValueLabel, + BreakingModified(CompParameter, PropAllowEmptyValue), CompParameter, PropAllowEmptyValue) + + return props +} + +// CompareParametersV3 is an OpenAPI type safe proxy for CompareParameters +func CompareParametersV3(l, r *v3.Parameter) *ParameterChanges { + return CompareParameters(l, r) +} + +// CompareParameters compares a left and right Swagger or OpenAPI Parameter object for any changes. If found returns +// a pointer to ParameterChanges. If nothing is found, returns nil. +func CompareParameters(l, r any) *ParameterChanges { + var changes []*Change + var props []*PropertyCheck + + pc := new(ParameterChanges) + var lSchema *base.SchemaProxy + var rSchema *base.SchemaProxy + var lext, rext *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + + if reflect.TypeOf(&v2.Parameter{}) == reflect.TypeOf(l) && reflect.TypeOf(&v2.Parameter{}) == reflect.TypeOf(r) { + lParam := l.(*v2.Parameter) + rParam := r.(*v2.Parameter) + pc.Name = lParam.Name.Value + + // perform hash check to avoid further processing + if low.AreEqual(lParam, rParam) { + return nil + } + + props = append(props, addSwaggerParameterProperties(lParam, rParam, &changes)...) + props = append(props, addCommonParameterProperties(lParam, rParam, &changes)...) + + // extract schema + if lParam != nil { + lSchema = lParam.Schema.Value + lext = lParam.Extensions + } + if rParam != nil { + rext = rParam.Extensions + rSchema = rParam.Schema.Value + } + + // items + if !lParam.Items.IsEmpty() && !rParam.Items.IsEmpty() { + if lParam.Items.Value.Hash() != rParam.Items.Value.Hash() { + pc.ItemsChanges = CompareItems(lParam.Items.Value, rParam.Items.Value) + } + } + if lParam.Items.IsEmpty() && !rParam.Items.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.ItemsLabel, + nil, rParam.Items.ValueNode, BreakingAdded(CompParameter, PropItems), nil, + rParam.Items.Value) + } + if !lParam.Items.IsEmpty() && rParam.Items.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.ItemsLabel, + lParam.Items.ValueNode, nil, BreakingRemoved(CompParameter, PropItems), lParam.Items.Value, + nil) + } + + // enum + if len(lParam.Enum.Value) > 0 || len(rParam.Enum.Value) > 0 { + ExtractRawValueSliceChanges(lParam.Enum.Value, rParam.Enum.Value, &changes, v3.EnumLabel, true) + } + } + + // OpenAPI + if reflect.TypeOf(&v3.Parameter{}) == reflect.TypeOf(l) && reflect.TypeOf(&v3.Parameter{}) == reflect.TypeOf(r) { + + lParam := l.(*v3.Parameter) + rParam := r.(*v3.Parameter) + pc.Name = lParam.Name.Value + + // perform hash check to avoid further processing + if low.AreEqual(lParam, rParam) { + return nil + } + + props = append(props, addOpenAPIParameterProperties(lParam, rParam, &changes)...) + props = append(props, addCommonParameterProperties(lParam, rParam, &changes)...) + if lParam != nil { + lext = lParam.Extensions + lSchema = lParam.Schema.Value + } + if rParam != nil { + rext = rParam.Extensions + rSchema = rParam.Schema.Value + } + + // example + checkParameterExample(lParam.Example, rParam.Example, changes) + + // examples + pc.ExamplesChanges = CheckMapForChanges(lParam.Examples.Value, rParam.Examples.Value, + &changes, v3.ExamplesLabel, CompareExamples) + + // content + pc.ContentChanges = CheckMapForChanges(lParam.Content.Value, rParam.Content.Value, + &changes, v3.ContentLabel, CompareMediaTypes) + } + CheckProperties(props) + + if lSchema != nil && rSchema != nil { + pc.SchemaChanges = CompareSchemas(lSchema, rSchema) + } + if lSchema != nil && rSchema == nil { + CreateChange(&changes, ObjectRemoved, v3.SchemaLabel, + lSchema.GetValueNode(), nil, BreakingRemoved(CompParameter, PropSchema), lSchema, + nil) + } + + if lSchema == nil && rSchema != nil { + CreateChange(&changes, ObjectAdded, v3.SchemaLabel, + nil, rSchema.GetValueNode(), BreakingAdded(CompParameter, PropSchema), nil, + rSchema) + } + + pc.PropertyChanges = NewPropertyChanges(changes) + pc.ExtensionChanges = CompareExtensions(lext, rext) + return pc +} + +func checkParameterExample(expLeft, expRight low.NodeReference[*yaml.Node], changes []*Change) { + CheckPropertyAdditionOrRemovalWithEncoding(expLeft.ValueNode, expRight.ValueNode, + v3.ExampleLabel, &changes, + BreakingAdded(CompParameter, PropExample) || BreakingRemoved(CompParameter, PropExample), + expLeft.Value, expRight.Value) + CheckForModificationWithEncoding(expLeft.ValueNode, expRight.ValueNode, + v3.ExampleLabel, &changes, BreakingModified(CompParameter, PropExample), + expLeft.Value, expRight.Value) +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/path_item.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/path_item.go new file mode 100644 index 000000000..803be6f6c --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/path_item.go @@ -0,0 +1,746 @@ +// Copyright 2022-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "reflect" + + "github.com/pb33f/libopenapi/datamodel/low" + v2 "github.com/pb33f/libopenapi/datamodel/low/v2" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +// PathItemChanges represents changes found between to Swagger or OpenAPI PathItem object. +type PathItemChanges struct { + *PropertyChanges + GetChanges *OperationChanges `json:"get,omitempty" yaml:"get,omitempty"` + PutChanges *OperationChanges `json:"put,omitempty" yaml:"put,omitempty"` + PostChanges *OperationChanges `json:"post,omitempty" yaml:"post,omitempty"` + DeleteChanges *OperationChanges `json:"delete,omitempty" yaml:"delete,omitempty"` + OptionsChanges *OperationChanges `json:"options,omitempty" yaml:"options,omitempty"` + HeadChanges *OperationChanges `json:"head,omitempty" yaml:"head,omitempty"` + PatchChanges *OperationChanges `json:"patch,omitempty" yaml:"patch,omitempty"` + TraceChanges *OperationChanges `json:"trace,omitempty" yaml:"trace,omitempty"` + QueryChanges *OperationChanges `json:"query,omitempty" yaml:"query,omitempty"` + AdditionalOperationChanges map[string]*OperationChanges `json:"additionalOperations,omitempty" yaml:"additionalOperations,omitempty"` // OpenAPI 3.2+ additional operations + ServerChanges []*ServerChanges `json:"servers,omitempty" yaml:"servers,omitempty"` + ParameterChanges []*ParameterChanges `json:"parameters,omitempty" yaml:"parameters,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between PathItem objects +func (p *PathItemChanges) GetAllChanges() []*Change { + if p == nil { + return nil + } + var changes []*Change + changes = append(changes, p.Changes...) + if p.GetChanges != nil { + changes = append(changes, p.GetChanges.GetAllChanges()...) + } + if p.PutChanges != nil { + changes = append(changes, p.PutChanges.GetAllChanges()...) + } + if p.PostChanges != nil { + changes = append(changes, p.PostChanges.GetAllChanges()...) + } + if p.DeleteChanges != nil { + changes = append(changes, p.DeleteChanges.GetAllChanges()...) + } + if p.OptionsChanges != nil { + changes = append(changes, p.OptionsChanges.GetAllChanges()...) + } + if p.HeadChanges != nil { + changes = append(changes, p.HeadChanges.GetAllChanges()...) + } + if p.PatchChanges != nil { + changes = append(changes, p.PatchChanges.GetAllChanges()...) + } + if p.TraceChanges != nil { + changes = append(changes, p.TraceChanges.GetAllChanges()...) + } + if p.QueryChanges != nil { + changes = append(changes, p.QueryChanges.GetAllChanges()...) + } + for k := range p.AdditionalOperationChanges { + changes = append(changes, p.AdditionalOperationChanges[k].GetAllChanges()...) + } + for i := range p.ServerChanges { + changes = append(changes, p.ServerChanges[i].GetAllChanges()...) + } + for i := range p.ParameterChanges { + changes = append(changes, p.ParameterChanges[i].GetAllChanges()...) + } + if p.ExtensionChanges != nil { + changes = append(changes, p.ExtensionChanges.GetAllChanges()...) + } + return changes +} + +// TotalChanges returns the total number of changes found between two Swagger or OpenAPI PathItems +func (p *PathItemChanges) TotalChanges() int { + if p == nil { + return 0 + } + c := p.PropertyChanges.TotalChanges() + if p.GetChanges != nil { + c += p.GetChanges.TotalChanges() + } + if p.PutChanges != nil { + c += p.PutChanges.TotalChanges() + } + if p.PostChanges != nil { + c += p.PostChanges.TotalChanges() + } + if p.DeleteChanges != nil { + c += p.DeleteChanges.TotalChanges() + } + if p.OptionsChanges != nil { + c += p.OptionsChanges.TotalChanges() + } + if p.HeadChanges != nil { + c += p.HeadChanges.TotalChanges() + } + if p.PatchChanges != nil { + c += p.PatchChanges.TotalChanges() + } + if p.TraceChanges != nil { + c += p.TraceChanges.TotalChanges() + } + if p.QueryChanges != nil { + c += p.QueryChanges.TotalChanges() + } + for k := range p.AdditionalOperationChanges { + c += p.AdditionalOperationChanges[k].TotalChanges() + } + for i := range p.ServerChanges { + c += p.ServerChanges[i].TotalChanges() + } + for i := range p.ParameterChanges { + c += p.ParameterChanges[i].TotalChanges() + } + if p.ExtensionChanges != nil { + c += p.ExtensionChanges.TotalChanges() + } + return c +} + +// TotalBreakingChanges returns the total number of breaking changes found between two Swagger or OpenAPI PathItems +func (p *PathItemChanges) TotalBreakingChanges() int { + c := p.PropertyChanges.TotalBreakingChanges() + if p.GetChanges != nil { + c += p.GetChanges.TotalBreakingChanges() + } + if p.PutChanges != nil { + c += p.PutChanges.TotalBreakingChanges() + } + if p.PostChanges != nil { + c += p.PostChanges.TotalBreakingChanges() + } + if p.DeleteChanges != nil { + c += p.DeleteChanges.TotalBreakingChanges() + } + if p.OptionsChanges != nil { + c += p.OptionsChanges.TotalBreakingChanges() + } + if p.HeadChanges != nil { + c += p.HeadChanges.TotalBreakingChanges() + } + if p.PatchChanges != nil { + c += p.PatchChanges.TotalBreakingChanges() + } + if p.TraceChanges != nil { + c += p.TraceChanges.TotalBreakingChanges() + } + if p.QueryChanges != nil { + c += p.QueryChanges.TotalBreakingChanges() + } + for k := range p.AdditionalOperationChanges { + c += p.AdditionalOperationChanges[k].TotalBreakingChanges() + } + for i := range p.ServerChanges { + c += p.ServerChanges[i].TotalBreakingChanges() + } + for i := range p.ParameterChanges { + c += p.ParameterChanges[i].TotalBreakingChanges() + } + return c +} + +type opCheck struct { + label string + changes *OperationChanges +} + +// ComparePathItemsV3 is an OpenAPI typesafe proxy method for ComparePathItems +func ComparePathItemsV3(l, r *v3.PathItem) *PathItemChanges { + return ComparePathItems(l, r) +} + +// ComparePathItems compare a left and right Swagger or OpenAPI PathItem object for changes. If found, returns +// a pointer to PathItemChanges, or returns nil if nothing is found. +func ComparePathItems(l, r any) *PathItemChanges { + var changes []*Change + var props []*PropertyCheck + + pc := new(PathItemChanges) + + // Swagger + if reflect.TypeOf(&v2.PathItem{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v2.PathItem{}) == reflect.TypeOf(r) { + + lPath := l.(*v2.PathItem) + rPath := r.(*v2.PathItem) + + // perform hash check to avoid further processing + if low.AreEqual(lPath, rPath) { + return nil + } + + props = append(props, compareSwaggerPathItem(lPath, rPath, &changes, pc)...) + } + + // OpenAPI + if reflect.TypeOf(&v3.PathItem{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v3.PathItem{}) == reflect.TypeOf(r) { + + lPath := l.(*v3.PathItem) + rPath := r.(*v3.PathItem) + + // perform hash check to avoid further processing + if low.AreEqual(lPath, rPath) { + return nil + } + + // description + props = append(props, NewPropertyCheck(CompPathItem, PropDescription, + lPath.Description.ValueNode, rPath.Description.ValueNode, + v3.DescriptionLabel, &changes, lPath, rPath)) + + // summary + props = append(props, NewPropertyCheck(CompPathItem, PropSummary, + lPath.Summary.ValueNode, rPath.Summary.ValueNode, + v3.SummaryLabel, &changes, lPath, rPath)) + + compareOpenAPIPathItem(lPath, rPath, &changes, pc) + } + + CheckProperties(props) + pc.PropertyChanges = NewPropertyChanges(changes) + return pc +} + +func compareSwaggerPathItem(lPath, rPath *v2.PathItem, changes *[]*Change, pc *PathItemChanges) []*PropertyCheck { + var props []*PropertyCheck + + totalOps := 0 + opChan := make(chan opCheck) + // get + if !lPath.Get.IsEmpty() && !rPath.Get.IsEmpty() { + totalOps++ + go checkOperation(lPath.Get.Value, rPath.Get.Value, opChan, v3.GetLabel) + } + if !lPath.Get.IsEmpty() && rPath.Get.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.GetLabel, + lPath.Get.ValueNode, nil, BreakingRemoved(CompPathItem, PropGet), lPath.Get.Value, nil) + } + if lPath.Get.IsEmpty() && !rPath.Get.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.GetLabel, + nil, rPath.Get.ValueNode, BreakingAdded(CompPathItem, PropGet), nil, rPath.Get.Value) + } + + // put + if !lPath.Put.IsEmpty() && !rPath.Put.IsEmpty() { + totalOps++ + go checkOperation(lPath.Put.Value, rPath.Put.Value, opChan, v3.PutLabel) + } + if !lPath.Put.IsEmpty() && rPath.Put.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.PutLabel, + lPath.Put.ValueNode, nil, BreakingRemoved(CompPathItem, PropPut), lPath.Put.Value, nil) + } + if lPath.Put.IsEmpty() && !rPath.Put.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.PutLabel, + nil, rPath.Put.ValueNode, BreakingAdded(CompPathItem, PropPut), nil, lPath.Put.Value) + } + + // post + if !lPath.Post.IsEmpty() && !rPath.Post.IsEmpty() { + totalOps++ + go checkOperation(lPath.Post.Value, rPath.Post.Value, opChan, v3.PostLabel) + } + if !lPath.Post.IsEmpty() && rPath.Post.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.PostLabel, + lPath.Post.ValueNode, nil, BreakingRemoved(CompPathItem, PropPost), lPath.Post.Value, nil) + } + if lPath.Post.IsEmpty() && !rPath.Post.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.PostLabel, + nil, rPath.Post.ValueNode, BreakingAdded(CompPathItem, PropPost), nil, lPath.Post.Value) + } + + // delete + if !lPath.Delete.IsEmpty() && !rPath.Delete.IsEmpty() { + totalOps++ + go checkOperation(lPath.Delete.Value, rPath.Delete.Value, opChan, v3.DeleteLabel) + } + if !lPath.Delete.IsEmpty() && rPath.Delete.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.DeleteLabel, + lPath.Delete.ValueNode, nil, BreakingRemoved(CompPathItem, PropDelete), lPath.Delete.Value, nil) + } + if lPath.Delete.IsEmpty() && !rPath.Delete.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.DeleteLabel, + nil, rPath.Delete.ValueNode, BreakingAdded(CompPathItem, PropDelete), nil, lPath.Delete.Value) + } + + // options + if !lPath.Options.IsEmpty() && !rPath.Options.IsEmpty() { + totalOps++ + go checkOperation(lPath.Options.Value, rPath.Options.Value, opChan, v3.OptionsLabel) + } + if !lPath.Options.IsEmpty() && rPath.Options.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.OptionsLabel, + lPath.Options.ValueNode, nil, BreakingRemoved(CompPathItem, PropOptions), lPath.Options.Value, nil) + } + if lPath.Options.IsEmpty() && !rPath.Options.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.OptionsLabel, + nil, rPath.Options.ValueNode, BreakingAdded(CompPathItem, PropOptions), nil, lPath.Options.Value) + } + + // head + if !lPath.Head.IsEmpty() && !rPath.Head.IsEmpty() { + totalOps++ + go checkOperation(lPath.Head.Value, rPath.Head.Value, opChan, v3.HeadLabel) + } + if !lPath.Head.IsEmpty() && rPath.Head.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.HeadLabel, + lPath.Head.ValueNode, nil, BreakingRemoved(CompPathItem, PropHead), lPath.Head.Value, nil) + } + if lPath.Head.IsEmpty() && !rPath.Head.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.HeadLabel, + nil, rPath.Head.ValueNode, BreakingAdded(CompPathItem, PropHead), nil, lPath.Head.Value) + } + + // patch + if !lPath.Patch.IsEmpty() && !rPath.Patch.IsEmpty() { + totalOps++ + go checkOperation(lPath.Patch.Value, rPath.Patch.Value, opChan, v3.PatchLabel) + } + if !lPath.Patch.IsEmpty() && rPath.Patch.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.PatchLabel, + lPath.Patch.ValueNode, nil, BreakingRemoved(CompPathItem, PropPatch), lPath.Patch.Value, nil) + } + if lPath.Patch.IsEmpty() && !rPath.Patch.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.PatchLabel, + nil, rPath.Patch.ValueNode, BreakingAdded(CompPathItem, PropPatch), nil, lPath.Patch.Value) + } + + // parameters + if !lPath.Parameters.IsEmpty() && !rPath.Parameters.IsEmpty() { + lParams := lPath.Parameters.Value + rParams := rPath.Parameters.Value + lp, rp := extractV2ParametersIntoInterface(lParams, rParams) + checkParameters(lp, rp, changes, pc) + } + if !lPath.Parameters.IsEmpty() && rPath.Parameters.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.ParametersLabel, + lPath.Parameters.ValueNode, nil, BreakingRemoved(CompPathItem, PropParameters), lPath.Parameters.Value, + nil) + } + if lPath.Parameters.IsEmpty() && !rPath.Parameters.IsEmpty() { + // Check configurable breaking rules first + breaking := BreakingAdded(CompPathItem, PropParameters) + // If config says not breaking, fall back to semantic check (required params are breaking) + if !breaking { + for i := range rPath.Parameters.Value { + param := rPath.Parameters.Value[i].Value + if param.Required.Value { + breaking = true + break + } + } + } + CreateChange(changes, PropertyAdded, v3.ParametersLabel, + nil, rPath.Parameters.ValueNode, breaking, nil, + rPath.Parameters.Value) + } + + // collect up operations changes. + completedOperations := 0 + for completedOperations < totalOps { + n := <-opChan + switch n.label { + case v3.GetLabel: + pc.GetChanges = n.changes + case v3.PutLabel: + pc.PutChanges = n.changes + case v3.PostLabel: + pc.PostChanges = n.changes + case v3.DeleteLabel: + pc.DeleteChanges = n.changes + case v3.OptionsLabel: + pc.OptionsChanges = n.changes + case v2.HeadLabel: + pc.HeadChanges = n.changes + case v2.PatchLabel: + pc.PatchChanges = n.changes + } + completedOperations++ + + } + pc.ExtensionChanges = CompareExtensions(lPath.Extensions, rPath.Extensions) + return props +} + +func extractV2ParametersIntoInterface(l, r []low.ValueReference[*v2.Parameter]) ([]low.ValueReference[low.SharedParameters], + []low.ValueReference[low.SharedParameters], +) { + lp := make([]low.ValueReference[low.SharedParameters], len(l)) + rp := make([]low.ValueReference[low.SharedParameters], len(r)) + for i := range l { + lp[i] = low.ValueReference[low.SharedParameters]{ + Value: l[i].Value, + ValueNode: l[i].ValueNode, + } + } + for i := range r { + rp[i] = low.ValueReference[low.SharedParameters]{ + Value: r[i].Value, + ValueNode: r[i].ValueNode, + } + } + return lp, rp +} + +func extractV3ParametersIntoInterface(l, r []low.ValueReference[*v3.Parameter]) ([]low.ValueReference[low.SharedParameters], + []low.ValueReference[low.SharedParameters], +) { + lp := make([]low.ValueReference[low.SharedParameters], len(l)) + rp := make([]low.ValueReference[low.SharedParameters], len(r)) + for i := range l { + lp[i] = low.ValueReference[low.SharedParameters]{ + Value: l[i].Value, + ValueNode: l[i].ValueNode, + } + } + for i := range r { + rp[i] = low.ValueReference[low.SharedParameters]{ + Value: r[i].Value, + ValueNode: r[i].ValueNode, + } + } + return lp, rp +} + +func checkParameters(lParams, rParams []low.ValueReference[low.SharedParameters], changes *[]*Change, pc *PathItemChanges) { + lv := make(map[string]low.SharedParameters, len(lParams)) + rv := make(map[string]low.SharedParameters, len(rParams)) + lRefs := make(map[string]*low.ValueReference[low.SharedParameters], len(lParams)) + rRefs := make(map[string]*low.ValueReference[low.SharedParameters], len(rParams)) + + for i := range lParams { + s := lParams[i].Value.GetName().Value + lv[s] = lParams[i].Value + lRefs[s] = &lParams[i] // Keep the reference wrapper + } + for i := range rParams { + s := rParams[i].Value.GetName().Value + rv[s] = rParams[i].Value + rRefs[s] = &rParams[i] // Keep the reference wrapper + } + + var paramChanges []*ParameterChanges + for n := range lv { + if _, ok := rv[n]; ok { + if !low.AreEqual(lv[n], rv[n]) { + ch := CompareParameters(lv[n], rv[n]) + if ch != nil { + // Preserve reference information if this parameter is a $ref + PreserveParameterReference(lRefs, rRefs, n, ch) + paramChanges = append(paramChanges, ch) + } + } + continue + } + CreateChange(changes, ObjectRemoved, v3.ParametersLabel, + lv[n].GetName().ValueNode, nil, true, lv[n], + nil) + + } + for n := range rv { + if _, ok := lv[n]; !ok { + CreateChange(changes, ObjectAdded, v3.ParametersLabel, + nil, rv[n].GetName().ValueNode, rv[n].GetRequired().Value, nil, + rv[n]) + } + } + pc.ParameterChanges = paramChanges +} + +func compareOpenAPIPathItem(lPath, rPath *v3.PathItem, changes *[]*Change, pc *PathItemChanges) { + // var props []*PropertyCheck + + totalOps := 0 + opChan := make(chan opCheck) + + // get + if !lPath.Get.IsEmpty() && !rPath.Get.IsEmpty() { + totalOps++ + go checkOperation(lPath.Get.Value, rPath.Get.Value, opChan, v3.GetLabel) + } + if !lPath.Get.IsEmpty() && rPath.Get.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.GetLabel, + lPath.Get.ValueNode, nil, BreakingRemoved(CompPathItem, PropGet), lPath.Get.Value, nil) + } + if lPath.Get.IsEmpty() && !rPath.Get.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.GetLabel, + nil, rPath.Get.ValueNode, BreakingAdded(CompPathItem, PropGet), nil, lPath.Get.Value) + } + + // put + if !lPath.Put.IsEmpty() && !rPath.Put.IsEmpty() { + totalOps++ + go checkOperation(lPath.Put.Value, rPath.Put.Value, opChan, v3.PutLabel) + } + if !lPath.Put.IsEmpty() && rPath.Put.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.PutLabel, + lPath.Put.ValueNode, nil, BreakingRemoved(CompPathItem, PropPut), lPath.Put.Value, nil) + } + if lPath.Put.IsEmpty() && !rPath.Put.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.PutLabel, + nil, rPath.Put.ValueNode, BreakingAdded(CompPathItem, PropPut), nil, lPath.Put.Value) + } + + // post + if !lPath.Post.IsEmpty() && !rPath.Post.IsEmpty() { + totalOps++ + go checkOperation(lPath.Post.Value, rPath.Post.Value, opChan, v3.PostLabel) + } + if !lPath.Post.IsEmpty() && rPath.Post.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.PostLabel, + lPath.Post.ValueNode, nil, BreakingRemoved(CompPathItem, PropPost), lPath.Post.Value, nil) + } + if lPath.Post.IsEmpty() && !rPath.Post.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.PostLabel, + nil, rPath.Post.ValueNode, BreakingAdded(CompPathItem, PropPost), nil, lPath.Post.Value) + } + + // delete + if !lPath.Delete.IsEmpty() && !rPath.Delete.IsEmpty() { + totalOps++ + go checkOperation(lPath.Delete.Value, rPath.Delete.Value, opChan, v3.DeleteLabel) + } + if !lPath.Delete.IsEmpty() && rPath.Delete.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.DeleteLabel, + lPath.Delete.ValueNode, nil, BreakingRemoved(CompPathItem, PropDelete), lPath.Delete.Value, nil) + } + if lPath.Delete.IsEmpty() && !rPath.Delete.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.DeleteLabel, + nil, rPath.Delete.ValueNode, BreakingAdded(CompPathItem, PropDelete), nil, lPath.Delete.Value) + } + + // options + if !lPath.Options.IsEmpty() && !rPath.Options.IsEmpty() { + totalOps++ + go checkOperation(lPath.Options.Value, rPath.Options.Value, opChan, v3.OptionsLabel) + } + if !lPath.Options.IsEmpty() && rPath.Options.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.OptionsLabel, + lPath.Options.ValueNode, nil, BreakingRemoved(CompPathItem, PropOptions), lPath.Options.Value, nil) + } + if lPath.Options.IsEmpty() && !rPath.Options.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.OptionsLabel, + nil, rPath.Options.ValueNode, BreakingAdded(CompPathItem, PropOptions), nil, lPath.Options.Value) + } + + // head + if !lPath.Head.IsEmpty() && !rPath.Head.IsEmpty() { + totalOps++ + go checkOperation(lPath.Head.Value, rPath.Head.Value, opChan, v3.HeadLabel) + } + if !lPath.Head.IsEmpty() && rPath.Head.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.HeadLabel, + lPath.Head.ValueNode, nil, BreakingRemoved(CompPathItem, PropHead), lPath.Head.Value, nil) + } + if lPath.Head.IsEmpty() && !rPath.Head.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.HeadLabel, + nil, rPath.Head.ValueNode, BreakingAdded(CompPathItem, PropHead), nil, lPath.Head.Value) + } + + // patch + if !lPath.Patch.IsEmpty() && !rPath.Patch.IsEmpty() { + totalOps++ + go checkOperation(lPath.Patch.Value, rPath.Patch.Value, opChan, v3.PatchLabel) + } + if !lPath.Patch.IsEmpty() && rPath.Patch.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.PatchLabel, + lPath.Patch.ValueNode, nil, BreakingRemoved(CompPathItem, PropPatch), lPath.Patch.Value, nil) + } + if lPath.Patch.IsEmpty() && !rPath.Patch.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.PatchLabel, + nil, rPath.Patch.ValueNode, BreakingAdded(CompPathItem, PropPatch), nil, lPath.Patch.Value) + } + + // trace + if !lPath.Trace.IsEmpty() && !rPath.Trace.IsEmpty() { + totalOps++ + go checkOperation(lPath.Trace.Value, rPath.Trace.Value, opChan, v3.TraceLabel) + } + if !lPath.Trace.IsEmpty() && rPath.Trace.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.TraceLabel, + lPath.Trace.ValueNode, nil, BreakingRemoved(CompPathItem, PropTrace), lPath.Trace.Value, nil) + } + if lPath.Trace.IsEmpty() && !rPath.Trace.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.TraceLabel, + nil, rPath.Trace.ValueNode, BreakingAdded(CompPathItem, PropTrace), nil, lPath.Trace.Value) + } + + // query + if !lPath.Query.IsEmpty() && !rPath.Query.IsEmpty() { + totalOps++ + go checkOperation(lPath.Query.Value, rPath.Query.Value, opChan, v3.QueryLabel) + } + if !lPath.Query.IsEmpty() && rPath.Query.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.QueryLabel, + lPath.Query.ValueNode, nil, BreakingRemoved(CompPathItem, PropQuery), lPath.Query.Value, nil) + } + if lPath.Query.IsEmpty() && !rPath.Query.IsEmpty() { + CreateChange(changes, PropertyAdded, v3.QueryLabel, + nil, rPath.Query.ValueNode, BreakingAdded(CompPathItem, PropQuery), nil, rPath.Query.Value) + } + + // additionalOperations (OpenAPI 3.2+) + if lPath.AdditionalOperations.Value != nil && rPath.AdditionalOperations.Value == nil { + CreateChange(changes, PropertyRemoved, v3.AdditionalOperationsLabel, + lPath.AdditionalOperations.ValueNode, nil, BreakingRemoved(CompPathItem, PropAdditionalOperations), lPath.AdditionalOperations.Value, nil) + } + if lPath.AdditionalOperations.Value == nil && rPath.AdditionalOperations.Value != nil { + CreateChange(changes, PropertyAdded, v3.AdditionalOperationsLabel, + nil, rPath.AdditionalOperations.ValueNode, BreakingAdded(CompPathItem, PropAdditionalOperations), nil, rPath.AdditionalOperations.Value) + } + if lPath.AdditionalOperations.Value != nil && rPath.AdditionalOperations.Value != nil { + + lKeys := make([]low.KeyReference[string], 0, lPath.AdditionalOperations.Value.Len()) + for lk := range lPath.AdditionalOperations.Value.FromOldest() { + lKeys = append(lKeys, lk) + } + rKeys := make([]low.KeyReference[string], 0, rPath.AdditionalOperations.Value.Len()) + for rk := range rPath.AdditionalOperations.Value.FromOldest() { + rKeys = append(rKeys, rk) + } + + for i := range lKeys { + // check right keys for match + found := false + for j := range rKeys { + if lKeys[i].Value == rKeys[j].Value { + found = true + // compare the two operations + totalOps++ + go checkOperation(lPath.AdditionalOperations.Value.GetOrZero(lKeys[j]).Value, + rPath.AdditionalOperations.Value.GetOrZero(rKeys[j]).Value, opChan, lKeys[i].Value) + break + } + } + // not found, was removed + if !found { + CreateChange(changes, PropertyRemoved, v3.AdditionalOperationsLabel, + lPath.AdditionalOperations.Value.GetOrZero(lKeys[i]).ValueNode, nil, BreakingRemoved(CompPathItem, PropAdditionalOperations), + lPath.AdditionalOperations.Value.GetOrZero(lKeys[i]).Value, nil) + } + } + + // check for added operations + for i := range rKeys { + // check left keys for match + found := false + for j := range lKeys { + if rKeys[i].Value == lKeys[j].Value { + found = true + break + } + } + // not found, was added + if !found { + CreateChange(changes, PropertyAdded, v3.AdditionalOperationsLabel, + nil, rPath.AdditionalOperations.Value.GetOrZero(rKeys[i]).ValueNode, BreakingAdded(CompPathItem, PropAdditionalOperations), + nil, rPath.AdditionalOperations.Value.GetOrZero(rKeys[i]).Value) + } + } + } + + // servers + pc.ServerChanges = checkServers(lPath.Servers, rPath.Servers, CompPathItem, PropServers) + + // parameters + if !lPath.Parameters.IsEmpty() && !rPath.Parameters.IsEmpty() { + lParams := lPath.Parameters.Value + rParams := rPath.Parameters.Value + lp, rp := extractV3ParametersIntoInterface(lParams, rParams) + checkParameters(lp, rp, changes, pc) + } + + if !lPath.Parameters.IsEmpty() && rPath.Parameters.IsEmpty() { + CreateChange(changes, PropertyRemoved, v3.ParametersLabel, + lPath.Parameters.ValueNode, nil, BreakingRemoved(CompPathItem, PropParameters), lPath.Parameters.Value, + nil) + } + if lPath.Parameters.IsEmpty() && !rPath.Parameters.IsEmpty() { + // Check configurable breaking rules first + breaking := BreakingAdded(CompPathItem, PropParameters) + // If config says not breaking, fall back to semantic check (required params are breaking) + if !breaking { + for i := range rPath.Parameters.Value { + param := rPath.Parameters.Value[i].Value + if param.Required.Value { + breaking = true + break + } + } + } + CreateChange(changes, PropertyAdded, v3.ParametersLabel, + nil, rPath.Parameters.ValueNode, breaking, nil, + rPath.Parameters.Value) + } + + // collect up operations changes. + completedOperations := 0 + for completedOperations < totalOps { + n := <-opChan + switch n.label { + case v3.GetLabel: + pc.GetChanges = n.changes + case v3.PutLabel: + pc.PutChanges = n.changes + case v3.PostLabel: + pc.PostChanges = n.changes + case v3.DeleteLabel: + pc.DeleteChanges = n.changes + case v3.OptionsLabel: + pc.OptionsChanges = n.changes + case v3.HeadLabel: + pc.HeadChanges = n.changes + case v3.PatchLabel: + pc.PatchChanges = n.changes + case v3.TraceLabel: + pc.TraceChanges = n.changes + case v3.QueryLabel: + pc.QueryChanges = n.changes + default: + if pc.AdditionalOperationChanges == nil { + pc.AdditionalOperationChanges = make(map[string]*OperationChanges) + } + if n.changes != nil { + pc.AdditionalOperationChanges[n.label] = n.changes + } + } + completedOperations++ + } + pc.ExtensionChanges = CompareExtensions(lPath.Extensions, rPath.Extensions) +} + +func checkOperation(l, r any, done chan opCheck, method string) { + done <- opCheck{ + label: method, + changes: CompareOperations(l, r), + } +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/paths.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/paths.go new file mode 100644 index 000000000..2b35d36e4 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/paths.go @@ -0,0 +1,229 @@ +// Copyright 2022-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "reflect" + "sync" + + "github.com/pb33f/libopenapi/datamodel/low" + v2 "github.com/pb33f/libopenapi/datamodel/low/v2" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// PathsChanges represents changes found between two Swagger or OpenAPI Paths Objects. +type PathsChanges struct { + *PropertyChanges + PathItemsChanges map[string]*PathItemChanges `json:"pathItems,omitempty" yaml:"pathItems,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between Paths objects +func (p *PathsChanges) GetAllChanges() []*Change { + if p == nil { + return nil + } + var changes []*Change + changes = append(changes, p.Changes...) + for k := range p.PathItemsChanges { + if p.PathItemsChanges[k] != nil { + changes = append(changes, p.PathItemsChanges[k].GetAllChanges()...) + } + } + if p.ExtensionChanges != nil { + changes = append(changes, p.ExtensionChanges.GetAllChanges()...) + } + return changes +} + +// TotalChanges returns the total number of changes between two Swagger or OpenAPI Paths Objects +func (p *PathsChanges) TotalChanges() int { + if p == nil { + return 0 + } + c := p.PropertyChanges.TotalChanges() + for k := range p.PathItemsChanges { + if p.PathItemsChanges[k] != nil { + c += p.PathItemsChanges[k].TotalChanges() + } + } + if p.ExtensionChanges != nil { + c += p.ExtensionChanges.TotalChanges() + } + return c +} + +// TotalBreakingChanges returns tht total number of changes found between two Swagger or OpenAPI Path Objects +func (p *PathsChanges) TotalBreakingChanges() int { + c := p.PropertyChanges.TotalBreakingChanges() + for k := range p.PathItemsChanges { + if p.PathItemsChanges[k] != nil { + c += p.PathItemsChanges[k].TotalBreakingChanges() + } + } + return c +} + +// ComparePaths compares a left and right Swagger or OpenAPI Paths Object for changes. If found, returns a pointer +// to a PathsChanges instance. Returns nil if nothing is found. +func ComparePaths(l, r any) *PathsChanges { + var changes []*Change + + pc := new(PathsChanges) + pathChanges := make(map[string]*PathItemChanges) + + // Swagger + if reflect.TypeOf(&v2.Paths{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v2.Paths{}) == reflect.TypeOf(r) { + + lPath := l.(*v2.Paths) + rPath := r.(*v2.Paths) + + // perform hash check to avoid further processing + if low.AreEqual(lPath, rPath) { + return nil + } + + lKeys := make(map[string]low.ValueReference[*v2.PathItem]) + rKeys := make(map[string]low.ValueReference[*v2.PathItem]) + for k, v := range lPath.PathItems.FromOldest() { + lKeys[k.Value] = v + } + for k, v := range rPath.PathItems.FromOldest() { + rKeys[k.Value] = v + } + + // run every comparison in a thread. + var mLock sync.Mutex + compare := func(path string, _ map[string]*PathItemChanges, l, r *v2.PathItem, doneChan chan struct{}) { + if !low.AreEqual(l, r) { + mLock.Lock() + pathChanges[path] = ComparePathItems(l, r) + mLock.Unlock() + } + doneChan <- struct{}{} + } + + doneChan := make(chan struct{}) + pathsChecked := 0 + + for k := range lKeys { + if _, ok := rKeys[k]; ok { + go compare(k, pathChanges, lKeys[k].Value, rKeys[k].Value, doneChan) + pathsChecked++ + continue + } + g, p := lPath.FindPathAndKey(k) + CreateChange(&changes, ObjectRemoved, k, + g.KeyNode, nil, BreakingRemoved(CompPaths, PropPath), + p.Value, nil) + } + + for k := range rKeys { + if _, ok := lKeys[k]; !ok { + g, p := rPath.FindPathAndKey(k) + CreateChange(&changes, ObjectAdded, k, + nil, g.KeyNode, BreakingAdded(CompPaths, PropPath), + nil, p.Value) + } + } + + // wait for the things to be done. + completedChecks := 0 + for completedChecks < pathsChecked { + <-doneChan + completedChecks++ + } + if len(pathChanges) > 0 { + pc.PathItemsChanges = pathChanges + } + + pc.ExtensionChanges = CompareExtensions(lPath.Extensions, rPath.Extensions) + } + + // OpenAPI + if reflect.TypeOf(&v3.Paths{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v3.Paths{}) == reflect.TypeOf(r) { + + lPath := l.(*v3.Paths) + rPath := r.(*v3.Paths) + + // perform hash check to avoid further processing + if low.AreEqual(lPath, rPath) { + return nil + } + + lKeys := make(map[string]low.ValueReference[*v3.PathItem]) + rKeys := make(map[string]low.ValueReference[*v3.PathItem]) + + if lPath != nil && lPath.PathItems != nil { + for k, v := range lPath.PathItems.FromOldest() { + lKeys[k.Value] = v + } + } + if rPath != nil && rPath.PathItems != nil { + for k, v := range rPath.PathItems.FromOldest() { + rKeys[k.Value] = v + } + } + + // run every comparison in a thread. + var mLock sync.Mutex + compare := func(path string, _ map[string]*PathItemChanges, l, r *v3.PathItem, doneChan chan struct{}) { + if !low.AreEqual(l, r) { + mLock.Lock() + pathChanges[path] = ComparePathItems(l, r) + mLock.Unlock() + } + doneChan <- struct{}{} + } + + doneChan := make(chan struct{}) + pathsChecked := 0 + + for k := range lKeys { + if _, ok := rKeys[k]; ok { + go compare(k, pathChanges, lKeys[k].Value, rKeys[k].Value, doneChan) + pathsChecked++ + continue + } + g, p := lPath.FindPathAndKey(k) + CreateChange(&changes, ObjectRemoved, k, + g.KeyNode, nil, BreakingRemoved(CompPaths, PropPath), + p.Value, nil) + } + + for k := range rKeys { + if _, ok := lKeys[k]; !ok { + g, p := rPath.FindPathAndKey(k) + CreateChange(&changes, ObjectAdded, k, + nil, g.KeyNode, BreakingAdded(CompPaths, PropPath), + nil, p.Value) + } + } + // wait for the things to be done. + completedChecks := 0 + for completedChecks < pathsChecked { + <-doneChan + completedChecks++ + } + if len(pathChanges) > 0 { + pc.PathItemsChanges = pathChanges + } + + var lExt, rExt *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + if lPath != nil { + lExt = lPath.Extensions + } + if rPath != nil { + rExt = rPath.Extensions + } + + pc.ExtensionChanges = CompareExtensions(lExt, rExt) + } + pc.PropertyChanges = NewPropertyChanges(changes) + return pc +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/request_body.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/request_body.go new file mode 100644 index 000000000..95afa6384 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/request_body.go @@ -0,0 +1,85 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +// RequestBodyChanges represents changes made between two OpenAPI RequestBody Objects +type RequestBodyChanges struct { + *PropertyChanges + ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between RequestBody objects +func (rb *RequestBodyChanges) GetAllChanges() []*Change { + if rb == nil { + return nil + } + var changes []*Change + changes = append(changes, rb.Changes...) + for k := range rb.ContentChanges { + changes = append(changes, rb.ContentChanges[k].GetAllChanges()...) + } + if rb.ExtensionChanges != nil { + changes = append(changes, rb.ExtensionChanges.GetAllChanges()...) + } + return changes +} + +// TotalChanges returns the total number of changes found between two OpenAPI RequestBody objects +func (rb *RequestBodyChanges) TotalChanges() int { + if rb == nil { + return 0 + } + c := rb.PropertyChanges.TotalChanges() + for k := range rb.ContentChanges { + c += rb.ContentChanges[k].TotalChanges() + } + if rb.ExtensionChanges != nil { + c += rb.ExtensionChanges.TotalChanges() + } + return c +} + +// TotalBreakingChanges returns the total number of breaking changes found between OpenAPI RequestBody objects +func (rb *RequestBodyChanges) TotalBreakingChanges() int { + c := rb.PropertyChanges.TotalBreakingChanges() + for k := range rb.ContentChanges { + c += rb.ContentChanges[k].TotalBreakingChanges() + } + return c +} + +// CompareRequestBodies compares a left and right OpenAPI RequestBody object for changes. If found returns a pointer +// to a RequestBodyChanges instance. Returns nil if nothing was found. +func CompareRequestBodies(l, r *v3.RequestBody) *RequestBodyChanges { + if low.AreEqual(l, r) { + return nil + } + + var changes []*Change + props := make([]*PropertyCheck, 0, 2) + + props = append(props, + NewPropertyCheck(CompRequestBody, PropDescription, + l.Description.ValueNode, r.Description.ValueNode, + v3.DescriptionLabel, &changes, l, r), + NewPropertyCheck(CompRequestBody, PropRequired, + l.Required.ValueNode, r.Required.ValueNode, + v3.RequiredLabel, &changes, l, r), + ) + + CheckProperties(props) + + rbc := new(RequestBodyChanges) + rbc.ContentChanges = CheckMapForChanges(l.Content.Value, r.Content.Value, + &changes, v3.ContentLabel, CompareMediaTypes) + rbc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) + rbc.PropertyChanges = NewPropertyChanges(changes) + return rbc +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/response.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/response.go new file mode 100644 index 000000000..69225839b --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/response.go @@ -0,0 +1,207 @@ +// Copyright 2022-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "reflect" + + "github.com/pb33f/libopenapi/datamodel/low" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +import ( + "github.com/pb33f/libopenapi/datamodel/low/v2" +) + +// ResponseChanges represents changes found between two Swagger or OpenAPI Response objects. +type ResponseChanges struct { + *PropertyChanges + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + HeadersChanges map[string]*HeaderChanges `json:"headers,omitempty" yaml:"headers,omitempty"` + + // Swagger Response Properties. + SchemaChanges *SchemaChanges `json:"schemas,omitempty" yaml:"schemas,omitempty"` + ExamplesChanges *ExamplesChanges `json:"examples,omitempty" yaml:"examples,omitempty"` + + // OpenAPI Response Properties. + ContentChanges map[string]*MediaTypeChanges `json:"content,omitempty" yaml:"content,omitempty"` + LinkChanges map[string]*LinkChanges `json:"links,omitempty" yaml:"links,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between RequestBody objects +func (r *ResponseChanges) GetAllChanges() []*Change { + if r == nil { + return nil + } + var changes []*Change + changes = append(changes, r.Changes...) + if r.ExtensionChanges != nil { + changes = append(changes, r.ExtensionChanges.GetAllChanges()...) + } + if r.SchemaChanges != nil { + changes = append(changes, r.SchemaChanges.GetAllChanges()...) + } + if r.ExamplesChanges != nil { + changes = append(changes, r.ExamplesChanges.GetAllChanges()...) + } + for k := range r.HeadersChanges { + changes = append(changes, r.HeadersChanges[k].GetAllChanges()...) + } + for k := range r.ContentChanges { + changes = append(changes, r.ContentChanges[k].GetAllChanges()...) + } + for k := range r.LinkChanges { + changes = append(changes, r.LinkChanges[k].GetAllChanges()...) + } + return changes +} + +// TotalChanges returns the total number of changes found between two Swagger or OpenAPI Response Objects +func (r *ResponseChanges) TotalChanges() int { + if r == nil { + return 0 + } + c := r.PropertyChanges.TotalChanges() + if r.ExtensionChanges != nil { + c += r.ExtensionChanges.TotalChanges() + } + if r.SchemaChanges != nil { + c += r.SchemaChanges.TotalChanges() + } + if r.ExamplesChanges != nil { + c += r.ExamplesChanges.TotalChanges() + } + for k := range r.HeadersChanges { + c += r.HeadersChanges[k].TotalChanges() + } + for k := range r.ContentChanges { + c += r.ContentChanges[k].TotalChanges() + } + for k := range r.LinkChanges { + c += r.LinkChanges[k].TotalChanges() + } + return c +} + +// TotalBreakingChanges returns the total number of breaking changes found between two swagger or OpenAPI +// Response Objects +func (r *ResponseChanges) TotalBreakingChanges() int { + c := r.PropertyChanges.TotalBreakingChanges() + if r.SchemaChanges != nil { + c += r.SchemaChanges.TotalBreakingChanges() + } + for k := range r.HeadersChanges { + c += r.HeadersChanges[k].TotalBreakingChanges() + } + for k := range r.ContentChanges { + c += r.ContentChanges[k].TotalBreakingChanges() + } + for k := range r.LinkChanges { + c += r.LinkChanges[k].TotalBreakingChanges() + } + return c +} + +// CompareResponseV2 is a Swagger type safe proxy for CompareResponse +func CompareResponseV2(l, r *v2.Response) *ResponseChanges { + return CompareResponse(l, r) +} + +// CompareResponseV3 is an OpenAPI type safe proxy for CompareResponse +func CompareResponseV3(l, r *v3.Response) *ResponseChanges { + return CompareResponse(l, r) +} + +// CompareResponse compares a left and right Swagger or OpenAPI Response object. If anything is found +// a pointer to a ResponseChanges is returned, otherwise it returns nil. +func CompareResponse(l, r any) *ResponseChanges { + var changes []*Change + var props []*PropertyCheck + + rc := new(ResponseChanges) + + if reflect.TypeOf(&v2.Response{}) == reflect.TypeOf(l) && reflect.TypeOf(&v2.Response{}) == reflect.TypeOf(r) { + + lResponse := l.(*v2.Response) + rResponse := r.(*v2.Response) + + // perform hash check to avoid further processing + if low.AreEqual(lResponse, rResponse) { + return nil + } + + // description + addPropertyCheck(&props, lResponse.Description.ValueNode, rResponse.Description.ValueNode, + lResponse.Description.Value, rResponse.Description.Value, &changes, v3.DescriptionLabel, false, CompResponse, PropDescription) + + if !lResponse.Schema.IsEmpty() && !rResponse.Schema.IsEmpty() { + rc.SchemaChanges = CompareSchemas(lResponse.Schema.Value, rResponse.Schema.Value) + } + if !lResponse.Schema.IsEmpty() && rResponse.Schema.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.SchemaLabel, + lResponse.Schema.ValueNode, nil, BreakingRemoved(CompResponse, PropSchema), + lResponse.Schema.Value, nil) + } + if lResponse.Schema.IsEmpty() && !rResponse.Schema.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.SchemaLabel, + nil, rResponse.Schema.ValueNode, BreakingAdded(CompResponse, PropSchema), + nil, rResponse.Schema.Value) + } + + rc.HeadersChanges = CheckMapForChanges(lResponse.Headers.Value, rResponse.Headers.Value, + &changes, v3.HeadersLabel, CompareHeadersV2) + + if !lResponse.Examples.IsEmpty() && !rResponse.Examples.IsEmpty() { + rc.ExamplesChanges = CompareExamplesV2(lResponse.Examples.Value, rResponse.Examples.Value) + } + if !lResponse.Examples.IsEmpty() && rResponse.Examples.IsEmpty() { + CreateChange(&changes, PropertyRemoved, v3.ExamplesLabel, + lResponse.Schema.ValueNode, nil, BreakingRemoved(CompResponse, PropExamples), + lResponse.Schema.Value, nil) + } + if lResponse.Examples.IsEmpty() && !rResponse.Examples.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.ExamplesLabel, + nil, rResponse.Schema.ValueNode, BreakingAdded(CompResponse, PropExamples), + nil, lResponse.Schema.Value) + } + + rc.ExtensionChanges = CompareExtensions(lResponse.Extensions, rResponse.Extensions) + } + + if reflect.TypeOf(&v3.Response{}) == reflect.TypeOf(l) && reflect.TypeOf(&v3.Response{}) == reflect.TypeOf(r) { + + lResponse := l.(*v3.Response) + rResponse := r.(*v3.Response) + + // perform hash check to avoid further processing + if low.AreEqual(lResponse, rResponse) { + return nil + } + + // summary (OpenAPI 3.2+) + addPropertyCheck(&props, lResponse.Summary.ValueNode, rResponse.Summary.ValueNode, + lResponse.Summary.Value, rResponse.Summary.Value, &changes, v3.SummaryLabel, + BreakingModified(CompResponse, PropSummary), CompResponse, PropSummary) + + // description + addPropertyCheck(&props, lResponse.Description.ValueNode, rResponse.Description.ValueNode, + lResponse.Description.Value, rResponse.Description.Value, &changes, v3.DescriptionLabel, + BreakingModified(CompResponse, PropDescription), CompResponse, PropDescription) + + rc.HeadersChanges = CheckMapForChanges(lResponse.Headers.Value, rResponse.Headers.Value, + &changes, v3.HeadersLabel, CompareHeadersV3) + + rc.ContentChanges = CheckMapForChanges(lResponse.Content.Value, rResponse.Content.Value, + &changes, v3.ContentLabel, CompareMediaTypes) + + rc.LinkChanges = CheckMapForChanges(lResponse.Links.Value, rResponse.Links.Value, + &changes, v3.LinksLabel, CompareLinks) + + rc.ExtensionChanges = CompareExtensions(lResponse.Extensions, rResponse.Extensions) + } + + CheckProperties(props) + rc.PropertyChanges = NewPropertyChanges(changes) + return rc +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/responses.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/responses.go new file mode 100644 index 000000000..7a6ecc767 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/responses.go @@ -0,0 +1,146 @@ +// Copyright 2022-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "reflect" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +// ResponsesChanges represents changes made between two Swagger or OpenAPI Responses objects. +type ResponsesChanges struct { + *PropertyChanges + ResponseChanges map[string]*ResponseChanges `json:"response,omitempty" yaml:"response,omitempty"` + DefaultChanges *ResponseChanges `json:"default,omitempty" yaml:"default,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between Responses objects +func (r *ResponsesChanges) GetAllChanges() []*Change { + if r == nil { + return nil + } + var changes []*Change + changes = append(changes, r.Changes...) + for k := range r.ResponseChanges { + changes = append(changes, r.ResponseChanges[k].GetAllChanges()...) + } + if r.DefaultChanges != nil { + changes = append(changes, r.DefaultChanges.GetAllChanges()...) + } + if r.ExtensionChanges != nil { + changes = append(changes, r.ExtensionChanges.GetAllChanges()...) + } + return changes +} + +// TotalChanges returns the total number of changes found between two Swagger or OpenAPI Responses objects +func (r *ResponsesChanges) TotalChanges() int { + if r == nil { + return 0 + } + c := r.PropertyChanges.TotalChanges() + for k := range r.ResponseChanges { + c += r.ResponseChanges[k].TotalChanges() + } + if r.DefaultChanges != nil { + c += r.DefaultChanges.TotalChanges() + } + if r.ExtensionChanges != nil { + c += r.ExtensionChanges.TotalChanges() + } + return c +} + +// TotalBreakingChanges returns the total number of changes found between two Swagger or OpenAPI +// Responses Objects +func (r *ResponsesChanges) TotalBreakingChanges() int { + c := r.PropertyChanges.TotalBreakingChanges() + for k := range r.ResponseChanges { + c += r.ResponseChanges[k].TotalBreakingChanges() + } + if r.DefaultChanges != nil { + c += r.DefaultChanges.TotalBreakingChanges() + } + return c +} + +// CompareResponses compares a left and right Swagger or OpenAPI Responses object for any changes. If found +// returns a pointer to ResponsesChanges, or returns nil. +func CompareResponses(l, r any) *ResponsesChanges { + var changes []*Change + + rc := new(ResponsesChanges) + + // swagger + if reflect.TypeOf(&v2.Responses{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v2.Responses{}) == reflect.TypeOf(r) { + + lResponses := l.(*v2.Responses) + rResponses := r.(*v2.Responses) + + // perform hash check to avoid further processing + if low.AreEqual(lResponses, rResponses) { + return nil + } + + if !lResponses.Default.IsEmpty() && !rResponses.Default.IsEmpty() { + rc.DefaultChanges = CompareResponse(lResponses.Default.Value, rResponses.Default.Value) + } + if !lResponses.Default.IsEmpty() && rResponses.Default.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.DefaultLabel, + lResponses.Default.ValueNode, nil, BreakingRemoved(CompResponses, PropDefault), + lResponses.Default.Value, nil) + } + if lResponses.Default.IsEmpty() && !rResponses.Default.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.DefaultLabel, + nil, rResponses.Default.ValueNode, BreakingAdded(CompResponses, PropDefault), + nil, lResponses.Default.Value) + } + + rc.ResponseChanges = CheckMapForChangesWithRules(lResponses.Codes, rResponses.Codes, + &changes, v3.CodesLabel, CompareResponseV2, CompResponses, PropCodes) + + rc.ExtensionChanges = CompareExtensions(lResponses.Extensions, rResponses.Extensions) + } + + // openapi + if reflect.TypeOf(&v3.Responses{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v3.Responses{}) == reflect.TypeOf(r) { + + lResponses := l.(*v3.Responses) + rResponses := r.(*v3.Responses) + + // perform hash check to avoid further processing + if low.AreEqual(lResponses, rResponses) { + return nil + } + + if !lResponses.Default.IsEmpty() && !rResponses.Default.IsEmpty() { + rc.DefaultChanges = CompareResponse(lResponses.Default.Value, rResponses.Default.Value) + } + if !lResponses.Default.IsEmpty() && rResponses.Default.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.DefaultLabel, + lResponses.Default.ValueNode, nil, BreakingRemoved(CompResponses, PropDefault), + lResponses.Default.Value, nil) + } + if lResponses.Default.IsEmpty() && !rResponses.Default.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.DefaultLabel, + nil, rResponses.Default.ValueNode, BreakingAdded(CompResponses, PropDefault), + nil, lResponses.Default.Value) + } + + rc.ResponseChanges = CheckMapForChangesWithRules(lResponses.Codes, rResponses.Codes, + &changes, v3.CodesLabel, CompareResponseV3, CompResponses, PropCodes) + + rc.ExtensionChanges = CompareExtensions(lResponses.Extensions, rResponses.Extensions) + + } + + rc.PropertyChanges = NewPropertyChanges(changes) + return rc +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/schema.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/schema.go new file mode 100644 index 000000000..96b073147 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/schema.go @@ -0,0 +1,2104 @@ +// Copyright 2022-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "fmt" + "slices" + "sort" + "sync" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// SchemaChanges represent all changes to a base.Schema OpenAPI object. These changes are represented +// by all versions of OpenAPI. +// +// Any additions or removals to slice based results will be recorded in the PropertyChanges of the parent +// changes, and not the child for example, adding a new schema to `anyOf` will create a new change result in +// PropertyChanges.Changes, and not in the AnyOfChanges property. +type SchemaChanges struct { + *PropertyChanges + DiscriminatorChanges *DiscriminatorChanges `json:"discriminator,omitempty" yaml:"discriminator,omitempty"` + AllOfChanges []*SchemaChanges `json:"allOf,omitempty" yaml:"allOf,omitempty"` + AnyOfChanges []*SchemaChanges `json:"anyOf,omitempty" yaml:"anyOf,omitempty"` + OneOfChanges []*SchemaChanges `json:"oneOf,omitempty" yaml:"oneOf,omitempty"` + PrefixItemsChanges []*SchemaChanges `json:"prefixItems,omitempty" yaml:"prefixItems,omitempty"` + NotChanges *SchemaChanges `json:"not,omitempty" yaml:"not,omitempty"` + ItemsChanges *SchemaChanges `json:"items,omitempty" yaml:"items,omitempty"` + SchemaPropertyChanges map[string]*SchemaChanges `json:"properties,omitempty" yaml:"properties,omitempty"` + ExternalDocChanges *ExternalDocChanges `json:"externalDoc,omitempty" yaml:"externalDoc,omitempty"` + XMLChanges *XMLChanges `json:"xml,omitempty" yaml:"xml,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + AdditionalPropertiesChanges *SchemaChanges `json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty"` + + // 3.1 specifics + IfChanges *SchemaChanges `json:"if,omitempty" yaml:"if,omitempty"` + ElseChanges *SchemaChanges `json:"else,omitempty" yaml:"else,omitempty"` + ThenChanges *SchemaChanges `json:"then,omitempty" yaml:"then,omitempty"` + PropertyNamesChanges *SchemaChanges `json:"propertyNames,omitempty" yaml:"propertyNames,omitempty"` + ContainsChanges *SchemaChanges `json:"contains,omitempty" yaml:"contains,omitempty"` + UnevaluatedItemsChanges *SchemaChanges `json:"unevaluatedItems,omitempty" yaml:"unevaluatedItems,omitempty"` + UnevaluatedPropertiesChanges *SchemaChanges `json:"unevaluatedProperties,omitempty" yaml:"unevaluatedProperties,omitempty"` + DependentSchemasChanges map[string]*SchemaChanges `json:"dependentSchemas,omitempty" yaml:"dependentSchemas,omitempty"` + DependentRequiredChanges []*Change `json:"dependentRequired,omitempty" yaml:"dependentRequired,omitempty"` + PatternPropertiesChanges map[string]*SchemaChanges `json:"patternProperties,omitempty" yaml:"patternProperties,omitempty"` + ContentSchemaChanges *SchemaChanges `json:"contentSchema,omitempty" yaml:"contentSchema,omitempty"` + VocabularyChanges []*Change `json:"$vocabulary,omitempty" yaml:"$vocabulary,omitempty"` +} + +func (s *SchemaChanges) GetPropertyChanges() []*Change { + if s == nil { + return nil + } + changes := s.Changes + if s.SchemaPropertyChanges != nil { + for n := range s.SchemaPropertyChanges { + if s.SchemaPropertyChanges[n] != nil { + changes = append(changes, s.SchemaPropertyChanges[n].GetAllChanges()...) + } + } + } + if s.DependentSchemasChanges != nil { + for n := range s.DependentSchemasChanges { + if s.DependentSchemasChanges[n] != nil { + changes = append(changes, s.DependentSchemasChanges[n].GetAllChanges()...) + } + } + } + if len(s.DependentRequiredChanges) > 0 { + changes = append(changes, s.DependentRequiredChanges...) + } + if s.PatternPropertiesChanges != nil { + for n := range s.PatternPropertiesChanges { + if s.PatternPropertiesChanges[n] != nil { + changes = append(changes, s.PatternPropertiesChanges[n].GetAllChanges()...) + } + } + } + if s.XMLChanges != nil { + changes = append(changes, s.XMLChanges.GetAllChanges()...) + } + return changes +} + +// GetAllChanges returns a slice of all changes made between Responses objects +func (s *SchemaChanges) GetAllChanges() []*Change { + if s == nil { + return nil + } + var changes []*Change + changes = append(changes, s.Changes...) + if s.DiscriminatorChanges != nil { + changes = append(changes, s.DiscriminatorChanges.GetAllChanges()...) + } + if len(s.AllOfChanges) > 0 { + for n := range s.AllOfChanges { + if s.AllOfChanges[n] != nil { + changes = append(changes, s.AllOfChanges[n].GetAllChanges()...) + } + } + } + if len(s.AnyOfChanges) > 0 { + for n := range s.AnyOfChanges { + if s.AnyOfChanges[n] != nil { + changes = append(changes, s.AnyOfChanges[n].GetAllChanges()...) + } + } + } + if len(s.OneOfChanges) > 0 { + for n := range s.OneOfChanges { + if s.OneOfChanges[n] != nil { + changes = append(changes, s.OneOfChanges[n].GetAllChanges()...) + } + } + } + if len(s.PrefixItemsChanges) > 0 { + for n := range s.PrefixItemsChanges { + if s.PrefixItemsChanges[n] != nil { + changes = append(changes, s.PrefixItemsChanges[n].GetAllChanges()...) + } + } + } + if s.NotChanges != nil { + changes = append(changes, s.NotChanges.GetAllChanges()...) + } + if s.ItemsChanges != nil { + changes = append(changes, s.ItemsChanges.GetAllChanges()...) + } + if s.IfChanges != nil { + changes = append(changes, s.IfChanges.GetAllChanges()...) + } + if s.ElseChanges != nil { + changes = append(changes, s.ElseChanges.GetAllChanges()...) + } + if s.ThenChanges != nil { + changes = append(changes, s.ThenChanges.GetAllChanges()...) + } + if s.PropertyNamesChanges != nil { + changes = append(changes, s.PropertyNamesChanges.GetAllChanges()...) + } + if s.ContainsChanges != nil { + changes = append(changes, s.ContainsChanges.GetAllChanges()...) + } + if s.UnevaluatedItemsChanges != nil { + changes = append(changes, s.UnevaluatedItemsChanges.GetAllChanges()...) + } + if s.UnevaluatedPropertiesChanges != nil { + changes = append(changes, s.UnevaluatedPropertiesChanges.GetAllChanges()...) + } + if s.AdditionalPropertiesChanges != nil { + changes = append(changes, s.AdditionalPropertiesChanges.GetAllChanges()...) + } + if s.SchemaPropertyChanges != nil { + for n := range s.SchemaPropertyChanges { + if s.SchemaPropertyChanges[n] != nil { + changes = append(changes, s.SchemaPropertyChanges[n].GetAllChanges()...) + } + } + } + if s.DependentSchemasChanges != nil { + for n := range s.DependentSchemasChanges { + if s.DependentSchemasChanges[n] != nil { + changes = append(changes, s.DependentSchemasChanges[n].GetAllChanges()...) + } + } + } + if len(s.DependentRequiredChanges) > 0 { + changes = append(changes, s.DependentRequiredChanges...) + } + if s.PatternPropertiesChanges != nil { + for n := range s.PatternPropertiesChanges { + if s.PatternPropertiesChanges[n] != nil { + changes = append(changes, s.PatternPropertiesChanges[n].GetAllChanges()...) + } + } + } + if s.ExternalDocChanges != nil { + changes = append(changes, s.ExternalDocChanges.GetAllChanges()...) + } + if s.XMLChanges != nil { + changes = append(changes, s.XMLChanges.GetAllChanges()...) + } + if s.ExtensionChanges != nil { + changes = append(changes, s.ExtensionChanges.GetAllChanges()...) + } + return changes +} + +// TotalChanges returns a count of the total number of changes made to this schema and all sub-schemas +func (s *SchemaChanges) TotalChanges() int { + if s == nil { + return 0 + } + t := s.PropertyChanges.TotalChanges() + if s.DiscriminatorChanges != nil { + t += s.DiscriminatorChanges.TotalChanges() + } + if len(s.AllOfChanges) > 0 { + for n := range s.AllOfChanges { + t += s.AllOfChanges[n].TotalChanges() + } + } + if len(s.AnyOfChanges) > 0 { + for n := range s.AnyOfChanges { + if s.AnyOfChanges[n] != nil { + t += s.AnyOfChanges[n].TotalChanges() + } + } + } + if len(s.OneOfChanges) > 0 { + for n := range s.OneOfChanges { + t += s.OneOfChanges[n].TotalChanges() + } + } + if len(s.PrefixItemsChanges) > 0 { + for n := range s.PrefixItemsChanges { + t += s.PrefixItemsChanges[n].TotalChanges() + } + } + + if s.NotChanges != nil { + t += s.NotChanges.TotalChanges() + } + if s.ItemsChanges != nil { + t += s.ItemsChanges.TotalChanges() + } + if s.IfChanges != nil { + t += s.IfChanges.TotalChanges() + } + if s.ElseChanges != nil { + t += s.ElseChanges.TotalChanges() + } + if s.ThenChanges != nil { + t += s.ThenChanges.TotalChanges() + } + if s.PropertyNamesChanges != nil { + t += s.PropertyNamesChanges.TotalChanges() + } + if s.ContainsChanges != nil { + t += s.ContainsChanges.TotalChanges() + } + if s.UnevaluatedItemsChanges != nil { + t += s.UnevaluatedItemsChanges.TotalChanges() + } + if s.UnevaluatedPropertiesChanges != nil { + t += s.UnevaluatedPropertiesChanges.TotalChanges() + } + if s.AdditionalPropertiesChanges != nil { + t += s.AdditionalPropertiesChanges.TotalChanges() + } + if s.SchemaPropertyChanges != nil { + for n := range s.SchemaPropertyChanges { + if s.SchemaPropertyChanges[n] != nil { + t += s.SchemaPropertyChanges[n].TotalChanges() + } + } + } + if s.DependentSchemasChanges != nil { + for n := range s.DependentSchemasChanges { + t += s.DependentSchemasChanges[n].TotalChanges() + } + } + if len(s.DependentRequiredChanges) > 0 { + t += len(s.DependentRequiredChanges) + } + if s.PatternPropertiesChanges != nil { + for n := range s.PatternPropertiesChanges { + t += s.PatternPropertiesChanges[n].TotalChanges() + } + } + if s.ContentSchemaChanges != nil { + t += s.ContentSchemaChanges.TotalChanges() + } + if len(s.VocabularyChanges) > 0 { + t += len(s.VocabularyChanges) + } + if s.ExternalDocChanges != nil { + t += s.ExternalDocChanges.TotalChanges() + } + if s.XMLChanges != nil { + t += s.XMLChanges.TotalChanges() + } + if s.ExtensionChanges != nil { + t += s.ExtensionChanges.TotalChanges() + } + return t +} + +// TotalBreakingChanges returns the total number of breaking changes made to this schema and all sub-schemas. +func (s *SchemaChanges) TotalBreakingChanges() int { + if s == nil { + return 0 + } + t := s.PropertyChanges.TotalBreakingChanges() + if s.DiscriminatorChanges != nil { + t += s.DiscriminatorChanges.TotalBreakingChanges() + } + if len(s.AllOfChanges) > 0 { + for n := range s.AllOfChanges { + t += s.AllOfChanges[n].TotalBreakingChanges() + } + } + if len(s.AllOfChanges) > 0 { + for n := range s.AllOfChanges { + t += s.AllOfChanges[n].TotalBreakingChanges() + } + } + if len(s.AnyOfChanges) > 0 { + for n := range s.AnyOfChanges { + t += s.AnyOfChanges[n].TotalBreakingChanges() + } + } + if len(s.OneOfChanges) > 0 { + for n := range s.OneOfChanges { + t += s.OneOfChanges[n].TotalBreakingChanges() + } + } + if len(s.PrefixItemsChanges) > 0 { + for n := range s.PrefixItemsChanges { + t += s.PrefixItemsChanges[n].TotalBreakingChanges() + } + } + if s.NotChanges != nil { + t += s.NotChanges.TotalBreakingChanges() + } + if s.ItemsChanges != nil { + t += s.ItemsChanges.TotalBreakingChanges() + } + if s.IfChanges != nil { + t += s.IfChanges.TotalBreakingChanges() + } + if s.ElseChanges != nil { + t += s.ElseChanges.TotalBreakingChanges() + } + if s.ThenChanges != nil { + t += s.ThenChanges.TotalBreakingChanges() + } + if s.PropertyNamesChanges != nil { + t += s.PropertyNamesChanges.TotalBreakingChanges() + } + if s.ContainsChanges != nil { + t += s.ContainsChanges.TotalBreakingChanges() + } + if s.UnevaluatedItemsChanges != nil { + t += s.UnevaluatedItemsChanges.TotalBreakingChanges() + } + if s.UnevaluatedPropertiesChanges != nil { + t += s.UnevaluatedPropertiesChanges.TotalBreakingChanges() + } + if s.AdditionalPropertiesChanges != nil { + t += s.AdditionalPropertiesChanges.TotalBreakingChanges() + } + if s.DependentSchemasChanges != nil { + for n := range s.DependentSchemasChanges { + t += s.DependentSchemasChanges[n].TotalBreakingChanges() + } + } + if len(s.DependentRequiredChanges) > 0 { + // Count breaking changes in dependent required changes + for _, change := range s.DependentRequiredChanges { + if change.Breaking { + t++ + } + } + } + if s.PatternPropertiesChanges != nil { + for n := range s.PatternPropertiesChanges { + t += s.PatternPropertiesChanges[n].TotalBreakingChanges() + } + } + if s.ContentSchemaChanges != nil { + t += s.ContentSchemaChanges.TotalBreakingChanges() + } + if len(s.VocabularyChanges) > 0 { + for _, change := range s.VocabularyChanges { + if change.Breaking { + t++ + } + } + } + if s.XMLChanges != nil { + t += s.XMLChanges.TotalBreakingChanges() + } + if s.SchemaPropertyChanges != nil { + for n := range s.SchemaPropertyChanges { + t += s.SchemaPropertyChanges[n].TotalBreakingChanges() + } + } + return t +} + +// CompareSchemas accepts a left and right SchemaProxy and checks for changes. If anything is found, returns +// a pointer to SchemaChanges, otherwise returns nil +func CompareSchemas(l, r *base.SchemaProxy) *SchemaChanges { + sc := new(SchemaChanges) + var changes []*Change + + // Added + if l == nil && r != nil { + CreateChange(&changes, ObjectAdded, v3.SchemaLabel, + nil, nil, BreakingAdded(CompSchemas, ""), nil, r) + sc.PropertyChanges = NewPropertyChanges(changes) + } + + // Removed + if l != nil && r == nil { + CreateChange(&changes, ObjectRemoved, v3.SchemaLabel, + nil, nil, BreakingRemoved(CompSchemas, ""), l, nil) + sc.PropertyChanges = NewPropertyChanges(changes) + } + + if l != nil && r != nil { + + // if left proxy is a reference and right is a reference (we won't recurse into circular references here) + if l.IsReference() && r.IsReference() { + + // points to the same schema + if l.GetReference() == r.GetReference() { + + // check if this is a circular ref. + if base.CheckSchemaProxyForCircularRefs(l) || base.CheckSchemaProxyForCircularRefs(r) { + // if we have a circular reference, we can't do any more work here. + return nil + } + + if r.GetIndex() != nil && r.GetIndex().GetSpecAbsolutePath() == "" || + r.GetIndex().GetSpecAbsolutePath() == "root.yaml" { + // local reference doesn't need following + return nil + } + + // continue on because the external references are the same and we need to check things going forward. + + } else { + // references are different, that's all we care to know. + CreateChange(&changes, Modified, v3.RefLabel, + l.GetValueNode().Content[1], r.GetValueNode().Content[1], BreakingModified(CompSchema, PropRef), l.GetReference(), + r.GetReference()) + sc.PropertyChanges = NewPropertyChanges(changes) + + // check if this is a circular ref. + if base.CheckSchemaProxyForCircularRefs(l) || base.CheckSchemaProxyForCircularRefs(r) { + // if we have a circular reference, we can't do any more work here. + return nil + } + return sc + } + } + + // changed from inline to ref + if !l.IsReference() && r.IsReference() { + // check if the referenced schema matches or not + // https://github.com/pb33f/libopenapi/issues/218 + lHash := l.Schema().Hash() + rHash := r.Schema().Hash() + if lHash != rHash { + CreateChange(&changes, Modified, v3.RefLabel, + l.GetValueNode(), r.GetValueNode().Content[1], BreakingModified(CompSchema, PropRef), l, r.GetReference()) + sc.PropertyChanges = NewPropertyChanges(changes) + + // check if this is a circular ref. + if base.CheckSchemaProxyForCircularRefs(r) { + // if we have a circular reference, we can't do any more work here. + return nil + } + return sc + } + } + + // changed from ref to inline + if l.IsReference() && !r.IsReference() { + // check if the referenced schema matches or not + // https://github.com/pb33f/libopenapi/issues/218 + lHash := l.Schema().Hash() + rHash := r.Schema().Hash() + if lHash != rHash { + CreateChange(&changes, Modified, v3.RefLabel, + l.GetValueNode().Content[1], r.GetValueNode(), BreakingModified(CompSchema, PropRef), l.GetReference(), r) + sc.PropertyChanges = NewPropertyChanges(changes) + + // check if this is a circular ref. + if base.CheckSchemaProxyForCircularRefs(l) { + // if we have a circular reference, we can't do any more work here. + return nil + } + return sc + } + } + + lSchema := l.Schema() + rSchema := r.Schema() + + if low.AreEqual(lSchema, rSchema) { + // there is no point going on, we know nothing changed! + return nil + } + + // check XML + checkSchemaXML(lSchema, rSchema, &changes, sc) + + // check examples + checkExamples(lSchema, rSchema, &changes) + + // check schema core properties for changes. + checkSchemaPropertyChanges(lSchema, rSchema, l, r, &changes, sc) + + // now for the confusing part, there is also a schema's 'properties' property to parse. + // inception, eat your heart out. + var lProperties, rProperties, lDepSchemas, rDepSchemas, lPattProp, rPattProp *orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.SchemaProxy]] + var loneOf, lallOf, lanyOf, roneOf, rallOf, ranyOf, lprefix, rprefix []low.ValueReference[*base.SchemaProxy] + if lSchema != nil { + lProperties = lSchema.Properties.Value + lDepSchemas = lSchema.DependentSchemas.Value + lPattProp = lSchema.PatternProperties.Value + loneOf = lSchema.OneOf.Value + lallOf = lSchema.AllOf.Value + lanyOf = lSchema.AnyOf.Value + lprefix = lSchema.PrefixItems.Value + } + if rSchema != nil { + rProperties = rSchema.Properties.Value + rDepSchemas = rSchema.DependentSchemas.Value + rPattProp = rSchema.PatternProperties.Value + roneOf = rSchema.OneOf.Value + rallOf = rSchema.AllOf.Value + ranyOf = rSchema.AnyOf.Value + rprefix = rSchema.PrefixItems.Value + } + + props := checkMappedSchemaOfASchema(lProperties, rProperties, &changes) + sc.SchemaPropertyChanges = props + + deps := checkMappedSchemaOfASchema(lDepSchemas, rDepSchemas, &changes) + sc.DependentSchemasChanges = deps + + // Check dependent required changes + var lDepRequired, rDepRequired *orderedmap.Map[low.KeyReference[string], low.ValueReference[[]string]] + if lSchema != nil { + lDepRequired = lSchema.DependentRequired.Value + } + if rSchema != nil { + rDepRequired = rSchema.DependentRequired.Value + } + + depRequiredChanges := checkDependentRequiredChanges(lDepRequired, rDepRequired) + if len(depRequiredChanges) > 0 { + sc.DependentRequiredChanges = depRequiredChanges + } + + patterns := checkMappedSchemaOfASchema(lPattProp, rPattProp, &changes) + sc.PatternPropertiesChanges = patterns + + var wg sync.WaitGroup + wg.Add(4) + go func() { + extractSchemaChanges(loneOf, roneOf, v3.OneOfLabel, + &sc.OneOfChanges, &changes) + wg.Done() + }() + go func() { + extractSchemaChanges(lallOf, rallOf, v3.AllOfLabel, + &sc.AllOfChanges, &changes) + wg.Done() + }() + go func() { + extractSchemaChanges(lanyOf, ranyOf, v3.AnyOfLabel, + &sc.AnyOfChanges, &changes) + wg.Done() + }() + go func() { + extractSchemaChanges(lprefix, rprefix, v3.PrefixItemsLabel, + &sc.PrefixItemsChanges, &changes) + wg.Done() + }() + wg.Wait() + + } + // done + if changes != nil { + sc.PropertyChanges = NewPropertyChanges(changes) + } else { + sc.PropertyChanges = NewPropertyChanges(nil) + } + if sc.TotalChanges() > 0 { + return sc + } + return nil +} + +func checkSchemaXML(lSchema *base.Schema, rSchema *base.Schema, changes *[]*Change, sc *SchemaChanges) { + // XML removed + if lSchema == nil || rSchema == nil { + return + } + if lSchema.XML.Value != nil && rSchema.XML.Value == nil { + CreateChange(changes, ObjectRemoved, v3.XMLLabel, + lSchema.XML.GetValueNode(), nil, BreakingRemoved(CompSchema, PropXML), lSchema.XML.GetValue(), nil) + } + // XML added + if lSchema.XML.Value == nil && rSchema.XML.Value != nil { + CreateChange(changes, ObjectAdded, v3.XMLLabel, + nil, rSchema.XML.GetValueNode(), BreakingAdded(CompSchema, PropXML), nil, rSchema.XML.GetValue()) + } + + // compare XML + if lSchema.XML.Value != nil && rSchema.XML.Value != nil { + if !low.AreEqual(lSchema.XML.Value, rSchema.XML.Value) { + sc.XMLChanges = CompareXML(lSchema.XML.Value, rSchema.XML.Value) + } + } +} + +func checkMappedSchemaOfASchema( + lSchema, + rSchema *orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.SchemaProxy]], + changes *[]*Change, +) map[string]*SchemaChanges { + var syncPropChanges sync.Map // concurrent-safe map + var lProps []string + lEntities := make(map[string]*base.SchemaProxy) + lKeyNodes := make(map[string]*yaml.Node) + var rProps []string + rEntities := make(map[string]*base.SchemaProxy) + rKeyNodes := make(map[string]*yaml.Node) + + for k, v := range lSchema.FromOldest() { + lProps = append(lProps, k.Value) + lEntities[k.Value] = v.Value + lKeyNodes[k.Value] = k.KeyNode + } + for k, v := range rSchema.FromOldest() { + rProps = append(rProps, k.Value) + rEntities[k.Value] = v.Value + rKeyNodes[k.Value] = k.KeyNode + } + sort.Strings(lProps) + sort.Strings(rProps) + buildProperty(lProps, rProps, lEntities, rEntities, &syncPropChanges, changes, rKeyNodes, lKeyNodes) + + // Convert the sync.Map into a regular map[string]*SchemaChanges. + propChanges := make(map[string]*SchemaChanges) + syncPropChanges.Range(func(key, value interface{}) bool { + propChanges[key.(string)] = value.(*SchemaChanges) + return true + }) + return propChanges +} + +func buildProperty(lProps, rProps []string, lEntities, rEntities map[string]*base.SchemaProxy, + propChanges *sync.Map, changes *[]*Change, rKeyNodes, lKeyNodes map[string]*yaml.Node, +) { + var wg sync.WaitGroup + checkProperty := func(key string, lp, rp *base.SchemaProxy) { + defer wg.Done() + if low.AreEqual(lp, rp) { + return + } + s := CompareSchemas(lp, rp) + propChanges.Store(key, s) + } + + // left and right equal. + if len(lProps) == len(rProps) { + for w := range lProps { + lp := lEntities[lProps[w]] + rp := rEntities[rProps[w]] + if lProps[w] == rProps[w] && lp != nil && rp != nil { + wg.Add(1) + go checkProperty(lProps[w], lp, rp) + } + // Handle keys that do not match. + if lProps[w] != rProps[w] { + if !slices.Contains(lProps, rProps[w]) { + // new property added. + CreateChange(changes, ObjectAdded, v3.PropertiesLabel, + nil, rKeyNodes[rProps[w]], BreakingAdded(CompSchema, PropProperties), nil, rEntities[rProps[w]]) + } + if !slices.Contains(rProps, lProps[w]) { + CreateChange(changes, ObjectRemoved, v3.PropertiesLabel, + lKeyNodes[lProps[w]], nil, BreakingRemoved(CompSchema, PropProperties), lEntities[lProps[w]], nil) + } + if slices.Contains(lProps, rProps[w]) { + h := slices.Index(lProps, rProps[w]) + lp = lEntities[lProps[h]] + rp = rEntities[rProps[w]] + wg.Add(1) + go checkProperty(lProps[h], lp, rp) + } + } + } + } + + // things removed + if len(lProps) > len(rProps) { + for w := range lProps { + if rEntities[lProps[w]] != nil { + wg.Add(1) + go checkProperty(lProps[w], lEntities[lProps[w]], rEntities[lProps[w]]) + } else { + CreateChange(changes, ObjectRemoved, v3.PropertiesLabel, + lKeyNodes[lProps[w]], nil, BreakingRemoved(CompSchema, PropProperties), lEntities[lProps[w]], nil) + } + } + for w := range rProps { + if lEntities[rProps[w]] != nil { + wg.Add(1) + go checkProperty(rProps[w], lEntities[rProps[w]], rEntities[rProps[w]]) + } else { + CreateChange(changes, ObjectAdded, v3.PropertiesLabel, + nil, rKeyNodes[rProps[w]], BreakingAdded(CompSchema, PropProperties), nil, rEntities[rProps[w]]) + } + } + } + + // stuff added + if len(rProps) > len(lProps) { + for _, propName := range rProps { + if lEntities[propName] != nil { + wg.Add(1) + go checkProperty(propName, lEntities[propName], rEntities[propName]) + } else { + CreateChange(changes, ObjectAdded, v3.PropertiesLabel, + nil, rKeyNodes[propName], BreakingAdded(CompSchema, PropProperties), nil, rEntities[propName]) + } + } + for _, propName := range lProps { + if rEntities[propName] != nil { + wg.Add(1) + go checkProperty(propName, lEntities[propName], rEntities[propName]) + } else { + CreateChange(changes, ObjectRemoved, v3.PropertiesLabel, + nil, lKeyNodes[propName], BreakingRemoved(CompSchema, PropProperties), lEntities[propName], nil) + } + } + } + + // Wait for all property comparisons to finish. + wg.Wait() +} + +func checkSchemaPropertyChanges( + lSchema *base.Schema, + rSchema *base.Schema, + lProxy *base.SchemaProxy, + rProxy *base.SchemaProxy, + changes *[]*Change, sc *SchemaChanges, +) { + var props []*PropertyCheck + + // $schema (breaking change) + var lnv, rnv *yaml.Node + if lSchema != nil && lSchema.SchemaTypeRef.ValueNode != nil { + lnv = lSchema.SchemaTypeRef.ValueNode + } + if rSchema != nil && rSchema.SchemaTypeRef.ValueNode != nil { + rnv = rSchema.SchemaTypeRef.ValueNode + } + + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.SchemaDialectLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropSchemaDialect), + Component: CompSchema, + Property: PropSchemaDialect, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.ExclusiveMaximum.ValueNode != nil { + lnv = lSchema.ExclusiveMaximum.ValueNode + } + if rSchema != nil && rSchema.ExclusiveMaximum.ValueNode != nil { + rnv = rSchema.ExclusiveMaximum.ValueNode + } + // ExclusiveMaximum + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.ExclusiveMaximumLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropExclusiveMaximum), + Component: CompSchema, + Property: PropExclusiveMaximum, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.ExclusiveMinimum.ValueNode != nil { + lnv = lSchema.ExclusiveMinimum.ValueNode + } + if rSchema != nil && rSchema.ExclusiveMinimum.ValueNode != nil { + rnv = rSchema.ExclusiveMinimum.ValueNode + } + + // ExclusiveMinimum + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.ExclusiveMinimumLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropExclusiveMinimum), + Component: CompSchema, + Property: PropExclusiveMinimum, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.Type.ValueNode != nil { + lnv = lSchema.Type.ValueNode + } + if rSchema != nil && rSchema.Type.ValueNode != nil { + rnv = rSchema.Type.ValueNode + } + // Type + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.TypeLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropType), + Component: CompSchema, + Property: PropType, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.Title.ValueNode != nil { + lnv = lSchema.Title.ValueNode + } + if rSchema != nil && rSchema.Title.ValueNode != nil { + rnv = rSchema.Title.ValueNode + } + // Title + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.TitleLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropTitle), + Component: CompSchema, + Property: PropTitle, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.MultipleOf.ValueNode != nil { + lnv = lSchema.MultipleOf.ValueNode + } + if rSchema != nil && rSchema.MultipleOf.ValueNode != nil { + rnv = rSchema.MultipleOf.ValueNode + } + + // MultipleOf + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.MultipleOfLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropMultipleOf), + Component: CompSchema, + Property: PropMultipleOf, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.Maximum.ValueNode != nil { + lnv = lSchema.Maximum.ValueNode + } + if rSchema != nil && rSchema.Maximum.ValueNode != nil { + rnv = rSchema.Maximum.ValueNode + } + // Maximum + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.MaximumLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropMaximum), + Component: CompSchema, + Property: PropMaximum, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.Minimum.ValueNode != nil { + lnv = lSchema.Minimum.ValueNode + } + if rSchema != nil && rSchema.Minimum.ValueNode != nil { + rnv = rSchema.Minimum.ValueNode + } + // Minimum + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.MinimumLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropMinimum), + Component: CompSchema, + Property: PropMinimum, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.MaxLength.ValueNode != nil { + lnv = lSchema.MaxLength.ValueNode + } + if rSchema != nil && rSchema.MaxLength.ValueNode != nil { + rnv = rSchema.MaxLength.ValueNode + } + // MaxLength + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.MaxLengthLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropMaxLength), + Component: CompSchema, + Property: PropMaxLength, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.MinLength.ValueNode != nil { + lnv = lSchema.MinLength.ValueNode + } + if rSchema != nil && rSchema.MinLength.ValueNode != nil { + rnv = rSchema.MinLength.ValueNode + } + // MinLength + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.MinLengthLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropMinLength), + Component: CompSchema, + Property: PropMinLength, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.Pattern.ValueNode != nil { + lnv = lSchema.Pattern.ValueNode + } + if rSchema != nil && rSchema.Pattern.ValueNode != nil { + rnv = rSchema.Pattern.ValueNode + } + // Pattern + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.PatternLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropPattern), + Component: CompSchema, + Property: PropPattern, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.Format.ValueNode != nil { + lnv = lSchema.Format.ValueNode + } + if rSchema != nil && rSchema.Format.ValueNode != nil { + rnv = rSchema.Format.ValueNode + } + // Format + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.FormatLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropFormat), + Component: CompSchema, + Property: PropFormat, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.MaxItems.ValueNode != nil { + lnv = lSchema.MaxItems.ValueNode + } + if rSchema != nil && rSchema.MaxItems.ValueNode != nil { + rnv = rSchema.MaxItems.ValueNode + } + // MaxItems + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.MaxItemsLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropMaxItems), + Component: CompSchema, + Property: PropMaxItems, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.MinItems.ValueNode != nil { + lnv = lSchema.MinItems.ValueNode + } + if rSchema != nil && rSchema.MinItems.ValueNode != nil { + rnv = rSchema.MinItems.ValueNode + } + // MinItems + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.MinItemsLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropMinItems), + Component: CompSchema, + Property: PropMinItems, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.MaxProperties.ValueNode != nil { + lnv = lSchema.MaxProperties.ValueNode + } + if rSchema != nil && rSchema.MaxProperties.ValueNode != nil { + rnv = rSchema.MaxProperties.ValueNode + } + // MaxProperties + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.MaxPropertiesLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropMaxProperties), + Component: CompSchema, + Property: PropMaxProperties, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.MinProperties.ValueNode != nil { + lnv = lSchema.MinProperties.ValueNode + } + if rSchema != nil && rSchema.MinProperties.ValueNode != nil { + rnv = rSchema.MinProperties.ValueNode + } + + // MinProperties + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.MinPropertiesLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropMinProperties), + Component: CompSchema, + Property: PropMinProperties, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.UniqueItems.ValueNode != nil { + lnv = lSchema.UniqueItems.ValueNode + } + if rSchema != nil && rSchema.UniqueItems.ValueNode != nil { + rnv = rSchema.UniqueItems.ValueNode + } + // UniqueItems + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.UniqueItemsLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropUniqueItems), + Component: CompSchema, + Property: PropUniqueItems, + Original: lSchema, + New: rSchema, + }) + + lnv = nil + rnv = nil + + // AdditionalProperties + if lSchema != nil && lSchema.AdditionalProperties.Value != nil && rSchema != nil && rSchema.AdditionalProperties.Value != nil { + if lSchema.AdditionalProperties.Value.IsA() && rSchema.AdditionalProperties.Value.IsA() { + if !low.AreEqual(lSchema.AdditionalProperties.Value.A, rSchema.AdditionalProperties.Value.A) { + sc.AdditionalPropertiesChanges = CompareSchemas(lSchema.AdditionalProperties.Value.A, rSchema.AdditionalProperties.Value.A) + } + } else { + if lSchema.AdditionalProperties.Value.IsB() && rSchema.AdditionalProperties.Value.IsB() { + if lSchema.AdditionalProperties.Value.B != rSchema.AdditionalProperties.Value.B { + CreateChange(changes, Modified, v3.AdditionalPropertiesLabel, + lSchema.AdditionalProperties.ValueNode, rSchema.AdditionalProperties.ValueNode, BreakingModified(CompSchema, PropAdditionalProperties), + lSchema.AdditionalProperties.Value.B, rSchema.AdditionalProperties.Value.B) + } + } else { + CreateChange(changes, Modified, v3.AdditionalPropertiesLabel, + lSchema.AdditionalProperties.ValueNode, rSchema.AdditionalProperties.ValueNode, BreakingModified(CompSchema, PropAdditionalProperties), + lSchema.AdditionalProperties.Value.B, rSchema.AdditionalProperties.Value.B) + } + } + } + + // added AdditionalProperties + if (lSchema == nil || lSchema.AdditionalProperties.Value == nil) && (rSchema != nil && rSchema.AdditionalProperties.Value != nil) { + CreateChange(changes, ObjectAdded, v3.AdditionalPropertiesLabel, + nil, rSchema.AdditionalProperties.ValueNode, BreakingAdded(CompSchema, PropAdditionalProperties), nil, rSchema.AdditionalProperties.Value) + } + // removed AdditionalProperties + if (lSchema != nil && lSchema.AdditionalProperties.Value != nil) && (rSchema == nil || rSchema.AdditionalProperties.Value == nil) { + CreateChange(changes, ObjectRemoved, v3.AdditionalPropertiesLabel, + lSchema.AdditionalProperties.ValueNode, nil, BreakingRemoved(CompSchema, PropAdditionalProperties), lSchema.AdditionalProperties.Value, nil) + } + + if lSchema != nil && lSchema.Description.ValueNode != nil { + lnv = lSchema.Description.ValueNode + } + if rSchema != nil && rSchema.Description.ValueNode != nil { + rnv = rSchema.Description.ValueNode + } + // Description + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.DescriptionLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropDescription), + Component: CompSchema, + Property: PropDescription, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.ContentEncoding.ValueNode != nil { + lnv = lSchema.ContentEncoding.ValueNode + } + if rSchema != nil && rSchema.ContentEncoding.ValueNode != nil { + rnv = rSchema.ContentEncoding.ValueNode + } + // ContentEncoding + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.ContentEncodingLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropContentEncoding), + Component: CompSchema, + Property: PropContentEncoding, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.ContentMediaType.ValueNode != nil { + lnv = lSchema.ContentMediaType.ValueNode + } + if rSchema != nil && rSchema.ContentMediaType.ValueNode != nil { + rnv = rSchema.ContentMediaType.ValueNode + } + // ContentMediaType + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.ContentMediaType, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropContentMediaType), + Component: CompSchema, + Property: PropContentMediaType, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.Default.ValueNode != nil { + lnv = lSchema.Default.ValueNode + } + if rSchema != nil && rSchema.Default.ValueNode != nil { + rnv = rSchema.Default.ValueNode + } + // Default + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.DefaultLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropDefault), + Component: CompSchema, + Property: PropDefault, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.Const.ValueNode != nil { + lnv = lSchema.Const.ValueNode + } + if rSchema != nil && rSchema.Const.ValueNode != nil { + rnv = rSchema.Const.ValueNode + } + // Const + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.ConstLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropConst), + Component: CompSchema, + Property: PropConst, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.Nullable.ValueNode != nil { + lnv = lSchema.Nullable.ValueNode + } + if rSchema != nil && rSchema.Nullable.ValueNode != nil { + rnv = rSchema.Nullable.ValueNode + } + // Nullable + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.NullableLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropNullable), + Component: CompSchema, + Property: PropNullable, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.ReadOnly.ValueNode != nil { + lnv = lSchema.ReadOnly.ValueNode + } + if rSchema != nil && rSchema.ReadOnly.ValueNode != nil { + rnv = rSchema.ReadOnly.ValueNode + } + // ReadOnly + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.ReadOnlyLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropReadOnly), + Component: CompSchema, + Property: PropReadOnly, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.WriteOnly.ValueNode != nil { + lnv = lSchema.WriteOnly.ValueNode + } + if rSchema != nil && rSchema.WriteOnly.ValueNode != nil { + rnv = rSchema.WriteOnly.ValueNode + } + // WriteOnly + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.WriteOnlyLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropWriteOnly), + Component: CompSchema, + Property: PropWriteOnly, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.Example.ValueNode != nil { + lnv = lSchema.Example.ValueNode + } + if rSchema != nil && rSchema.Example.ValueNode != nil { + rnv = rSchema.Example.ValueNode + } + // Example + CheckPropertyAdditionOrRemovalWithEncoding(lnv, rnv, + v3.ExampleLabel, changes, false, lSchema, rSchema) + CheckForModificationWithEncoding(lnv, rnv, + v3.ExampleLabel, changes, false, lSchema, rSchema) + lnv = nil + rnv = nil + + if lSchema != nil && lSchema.Deprecated.ValueNode != nil { + lnv = lSchema.Deprecated.ValueNode + } + if rSchema != nil && rSchema.Deprecated.ValueNode != nil { + rnv = rSchema.Deprecated.ValueNode + } + // Deprecated + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.DeprecatedLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropDeprecated), + Component: CompSchema, + Property: PropDeprecated, + Original: lSchema, + New: rSchema, + }) + + // Required + j := make(map[string]int) + k := make(map[string]int) + if lSchema != nil { + for i := range lSchema.Required.Value { + j[lSchema.Required.Value[i].Value] = i + } + } + if rSchema != nil { + for i := range rSchema.Required.Value { + k[rSchema.Required.Value[i].Value] = i + } + } + for g := range k { + if _, ok := j[g]; !ok { + CreateChange(changes, PropertyAdded, v3.RequiredLabel, + nil, rSchema.Required.Value[k[g]].GetValueNode(), BreakingAdded(CompSchema, PropRequired), nil, + rSchema.Required.Value[k[g]].GetValue) + } + } + for g := range j { + if _, ok := k[g]; !ok { + CreateChange(changes, PropertyRemoved, v3.RequiredLabel, + lSchema.Required.Value[j[g]].GetValueNode(), nil, BreakingRemoved(CompSchema, PropRequired), lSchema.Required.Value[j[g]].GetValue, + nil) + } + } + + // Enums + j = make(map[string]int) + k = make(map[string]int) + if lSchema != nil { + for i := range lSchema.Enum.Value { + j[toString(lSchema.Enum.Value[i].Value.Value)] = i + } + } + if rSchema != nil { + for i := range rSchema.Enum.Value { + k[toString(rSchema.Enum.Value[i].Value.Value)] = i + } + } + for g := range k { + if _, ok := j[g]; !ok { + CreateChange(changes, PropertyAdded, v3.EnumLabel, + nil, rSchema.Enum.Value[k[g]].GetValueNode(), BreakingAdded(CompSchema, PropEnum), nil, + rSchema.Enum.Value[k[g]].GetValue) + } + } + for g := range j { + if _, ok := k[g]; !ok { + CreateChange(changes, PropertyRemoved, v3.EnumLabel, + lSchema.Enum.Value[j[g]].GetValueNode(), nil, BreakingRemoved(CompSchema, PropEnum), lSchema.Enum.Value[j[g]].GetValue, + nil) + } + } + + // Discriminator + if (lSchema != nil && lSchema.Discriminator.Value != nil) && (rSchema != nil && rSchema.Discriminator.Value != nil) { + // check if hash matches, if not then compare. + if lSchema.Discriminator.Value.Hash() != rSchema.Discriminator.Value.Hash() { + sc.DiscriminatorChanges = CompareDiscriminator(lSchema.Discriminator.Value, rSchema.Discriminator.Value) + } + } + // added Discriminator + if (lSchema == nil || lSchema.Discriminator.Value == nil) && (rSchema != nil && rSchema.Discriminator.Value != nil) { + CreateChange(changes, ObjectAdded, v3.DiscriminatorLabel, + nil, rSchema.Discriminator.ValueNode, BreakingAdded(CompSchema, PropDiscriminator), nil, rSchema.Discriminator.Value) + } + // removed Discriminator + if (lSchema != nil && lSchema.Discriminator.Value != nil) && (rSchema == nil || rSchema.Discriminator.Value == nil) { + CreateChange(changes, ObjectRemoved, v3.DiscriminatorLabel, + lSchema.Discriminator.ValueNode, nil, BreakingRemoved(CompSchema, PropDiscriminator), lSchema.Discriminator.Value, nil) + } + + // ExternalDocs + if (lSchema != nil && lSchema.ExternalDocs.Value != nil) && (rSchema != nil && rSchema.ExternalDocs.Value != nil) { + // check if hash matches, if not then compare. + if lSchema.ExternalDocs.Value.Hash() != rSchema.ExternalDocs.Value.Hash() { + sc.ExternalDocChanges = CompareExternalDocs(lSchema.ExternalDocs.Value, rSchema.ExternalDocs.Value) + } + } + // added ExternalDocs + if (lSchema == nil || lSchema.ExternalDocs.Value == nil) && (rSchema != nil && rSchema.ExternalDocs.Value != nil) { + CreateChange(changes, ObjectAdded, v3.ExternalDocsLabel, + nil, rSchema.ExternalDocs.ValueNode, BreakingAdded(CompSchema, PropExternalDocs), nil, rSchema.ExternalDocs.Value) + } + // removed ExternalDocs + if (lSchema != nil && lSchema.ExternalDocs.Value != nil) && (rSchema == nil || rSchema.ExternalDocs.Value == nil) { + CreateChange(changes, ObjectRemoved, v3.ExternalDocsLabel, + lSchema.ExternalDocs.ValueNode, nil, BreakingRemoved(CompSchema, PropExternalDocs), lSchema.ExternalDocs.Value, nil) + } + + // 3.1 properties + // If + if (lSchema != nil && lSchema.If.Value != nil) && (rSchema != nil && rSchema.If.Value != nil) { + if !low.AreEqual(lSchema.If.Value, rSchema.If.Value) { + sc.IfChanges = CompareSchemas(lSchema.If.Value, rSchema.If.Value) + } + } + // added If + if (lSchema == nil || lSchema.If.Value == nil) && (rSchema != nil && rSchema.If.Value != nil) { + CreateChange(changes, ObjectAdded, v3.IfLabel, + nil, rSchema.If.ValueNode, BreakingAdded(CompSchema, PropIf), nil, rSchema.If.Value) + } + // removed If + if (lSchema != nil && lSchema.If.Value != nil) && (rSchema == nil || rSchema.If.Value == nil) { + CreateChange(changes, ObjectRemoved, v3.IfLabel, + lSchema.If.ValueNode, nil, BreakingRemoved(CompSchema, PropIf), lSchema.If.Value, nil) + } + // Else + if (lSchema != nil && lSchema.Else.Value != nil) && (rSchema == nil || rSchema.Else.Value != nil) { + if !low.AreEqual(lSchema.Else.Value, rSchema.Else.Value) { + sc.ElseChanges = CompareSchemas(lSchema.Else.Value, rSchema.Else.Value) + } + } + // added Else + if (lSchema == nil || lSchema.Else.Value == nil) && (rSchema != nil && rSchema.Else.Value != nil) { + CreateChange(changes, ObjectAdded, v3.ElseLabel, + nil, rSchema.Else.ValueNode, BreakingAdded(CompSchema, PropElse), nil, rSchema.Else.Value) + } + // removed Else + if (lSchema != nil && lSchema.Else.Value != nil) && (rSchema == nil || rSchema.Else.Value == nil) { + CreateChange(changes, ObjectRemoved, v3.ElseLabel, + lSchema.Else.ValueNode, nil, BreakingRemoved(CompSchema, PropElse), lSchema.Else.Value, nil) + } + // Then + if (lSchema != nil && lSchema.Then.Value != nil) && (rSchema != nil && rSchema.Then.Value != nil) { + if !low.AreEqual(lSchema.Then.Value, rSchema.Then.Value) { + sc.ThenChanges = CompareSchemas(lSchema.Then.Value, rSchema.Then.Value) + } + } + // added Then + if (lSchema == nil || lSchema.Then.Value == nil) && (rSchema != nil && rSchema.Then.Value != nil) { + CreateChange(changes, ObjectAdded, v3.ThenLabel, + nil, rSchema.Then.ValueNode, BreakingAdded(CompSchema, PropThen), nil, rSchema.Then.Value) + } + // removed Then + if (lSchema != nil && lSchema.Then.Value != nil) && (rSchema == nil || rSchema.Then.Value == nil) { + CreateChange(changes, ObjectRemoved, v3.ThenLabel, + lSchema.Then.ValueNode, nil, BreakingRemoved(CompSchema, PropThen), lSchema.Then.Value, nil) + } + // PropertyNames + if (lSchema != nil && lSchema.PropertyNames.Value != nil) && (rSchema != nil && rSchema.PropertyNames.Value != nil) { + if !low.AreEqual(lSchema.PropertyNames.Value, rSchema.PropertyNames.Value) { + sc.PropertyNamesChanges = CompareSchemas(lSchema.PropertyNames.Value, rSchema.PropertyNames.Value) + } + } + // added PropertyNames + if (lSchema == nil || lSchema.PropertyNames.Value == nil) && (rSchema != nil && rSchema.PropertyNames.Value != nil) { + CreateChange(changes, ObjectAdded, v3.PropertyNamesLabel, + nil, rSchema.PropertyNames.ValueNode, BreakingAdded(CompSchema, PropPropertyNames), nil, rSchema.PropertyNames.Value) + } + // removed PropertyNames + if (lSchema != nil && lSchema.PropertyNames.Value != nil) && (rSchema == nil || rSchema.PropertyNames.Value == nil) { + CreateChange(changes, ObjectRemoved, v3.PropertyNamesLabel, + lSchema.PropertyNames.ValueNode, nil, BreakingRemoved(CompSchema, PropPropertyNames), lSchema.PropertyNames.Value, nil) + } + // Contains + if (lSchema != nil && lSchema.Contains.Value != nil) && (rSchema != nil && rSchema.Contains.Value != nil) { + if !low.AreEqual(lSchema.Contains.Value, rSchema.Contains.Value) { + sc.ContainsChanges = CompareSchemas(lSchema.Contains.Value, rSchema.Contains.Value) + } + } + // added Contains + if (lSchema == nil || lSchema.Contains.Value == nil) && (rSchema != nil && rSchema.Contains.Value != nil) { + CreateChange(changes, ObjectAdded, v3.ContainsLabel, + nil, rSchema.Contains.ValueNode, BreakingAdded(CompSchema, PropContains), nil, rSchema.Contains.Value) + } + // removed Contains + if (lSchema != nil && lSchema.Contains.Value != nil) && (rSchema == nil || rSchema.Contains.Value == nil) { + CreateChange(changes, ObjectRemoved, v3.ContainsLabel, + lSchema.Contains.ValueNode, nil, BreakingRemoved(CompSchema, PropContains), lSchema.Contains.Value, nil) + } + // UnevaluatedItems + if (lSchema != nil && lSchema.UnevaluatedItems.Value != nil) && (rSchema != nil && rSchema.UnevaluatedItems.Value != nil) { + if !low.AreEqual(lSchema.UnevaluatedItems.Value, rSchema.UnevaluatedItems.Value) { + sc.UnevaluatedItemsChanges = CompareSchemas(lSchema.UnevaluatedItems.Value, rSchema.UnevaluatedItems.Value) + } + } + // added UnevaluatedItems + if (lSchema == nil || lSchema.UnevaluatedItems.Value == nil) && (rSchema != nil && rSchema.UnevaluatedItems.Value != nil) { + CreateChange(changes, ObjectAdded, v3.UnevaluatedItemsLabel, + nil, rSchema.UnevaluatedItems.ValueNode, BreakingAdded(CompSchema, PropUnevaluatedItems), nil, rSchema.UnevaluatedItems.Value) + } + // removed UnevaluatedItems + if (lSchema != nil && lSchema.UnevaluatedItems.Value != nil) && (rSchema == nil || rSchema.UnevaluatedItems.Value == nil) { + CreateChange(changes, ObjectRemoved, v3.UnevaluatedItemsLabel, + lSchema.UnevaluatedItems.ValueNode, nil, BreakingRemoved(CompSchema, PropUnevaluatedItems), lSchema.UnevaluatedItems.Value, nil) + } + + // UnevaluatedProperties + if (lSchema != nil && lSchema.UnevaluatedProperties.Value != nil) && (rSchema != nil && rSchema.UnevaluatedProperties.Value != nil) { + if lSchema.UnevaluatedProperties.Value.IsA() && rSchema.UnevaluatedProperties.Value.IsA() { + if !low.AreEqual(lSchema.UnevaluatedProperties.Value.A, rSchema.UnevaluatedProperties.Value.A) { + sc.UnevaluatedPropertiesChanges = CompareSchemas(lSchema.UnevaluatedProperties.Value.A, rSchema.UnevaluatedProperties.Value.A) + } + } else { + if lSchema.UnevaluatedProperties.Value.IsB() && rSchema.UnevaluatedProperties.Value.IsB() { + if lSchema.UnevaluatedProperties.Value.B != rSchema.UnevaluatedProperties.Value.B { + CreateChange(changes, Modified, v3.UnevaluatedPropertiesLabel, + lSchema.UnevaluatedProperties.ValueNode, rSchema.UnevaluatedProperties.ValueNode, BreakingModified(CompSchema, PropUnevaluatedProps), + lSchema.UnevaluatedProperties.Value.B, rSchema.UnevaluatedProperties.Value.B) + } + } else { + CreateChange(changes, Modified, v3.UnevaluatedPropertiesLabel, + lSchema.UnevaluatedProperties.ValueNode, rSchema.UnevaluatedProperties.ValueNode, BreakingModified(CompSchema, PropUnevaluatedProps), + lSchema.UnevaluatedProperties.Value.B, rSchema.UnevaluatedProperties.Value.B) + } + } + } + + // added UnevaluatedProperties + if (lSchema == nil || lSchema.UnevaluatedProperties.Value == nil) && (rSchema != nil && rSchema.UnevaluatedProperties.Value != nil) { + CreateChange(changes, ObjectAdded, v3.UnevaluatedPropertiesLabel, + nil, rSchema.UnevaluatedProperties.ValueNode, BreakingAdded(CompSchema, PropUnevaluatedProps), nil, rSchema.UnevaluatedProperties.Value) + } + // removed UnevaluatedProperties + if (lSchema != nil && lSchema.UnevaluatedProperties.Value != nil) && (rSchema == nil || rSchema.UnevaluatedProperties.Value == nil) { + CreateChange(changes, ObjectRemoved, v3.UnevaluatedPropertiesLabel, + lSchema.UnevaluatedProperties.ValueNode, nil, BreakingRemoved(CompSchema, PropUnevaluatedProps), lSchema.UnevaluatedProperties.Value, nil) + } + + // Not + if (lSchema != nil && lSchema.Not.Value != nil) && (rSchema != nil && rSchema.Not.Value != nil) { + if !low.AreEqual(lSchema.Not.Value, rSchema.Not.Value) { + sc.NotChanges = CompareSchemas(lSchema.Not.Value, rSchema.Not.Value) + } + } + // added Not + if (lSchema == nil || lSchema.Not.Value == nil) && (rSchema != nil && rSchema.Not.Value != nil) { + CreateChange(changes, ObjectAdded, v3.NotLabel, + nil, rSchema.Not.ValueNode, BreakingAdded(CompSchema, PropNot), nil, rSchema.Not.Value) + } + // removed not + if (lSchema != nil && lSchema.Not.Value != nil) && (rSchema == nil || rSchema.Not.Value == nil) { + CreateChange(changes, ObjectRemoved, v3.NotLabel, + lSchema.Not.ValueNode, nil, BreakingRemoved(CompSchema, PropNot), lSchema.Not.Value, nil) + } + + // items + if (lSchema != nil && lSchema.Items.Value != nil) && (rSchema != nil && rSchema.Items.Value != nil) { + if lSchema.Items.Value.IsA() && rSchema.Items.Value.IsA() { + if !low.AreEqual(lSchema.Items.Value.A, rSchema.Items.Value.A) { + sc.ItemsChanges = CompareSchemas(lSchema.Items.Value.A, rSchema.Items.Value.A) + } + } else { + CreateChange(changes, Modified, v3.ItemsLabel, + lSchema.Items.ValueNode, rSchema.Items.ValueNode, BreakingModified(CompSchema, PropItems), lSchema.Items.Value.B, rSchema.Items.Value.B) + } + } + // added Items + if (lSchema == nil || lSchema.Items.Value == nil) && (rSchema != nil && rSchema.Items.Value != nil) { + CreateChange(changes, ObjectAdded, v3.ItemsLabel, + nil, rSchema.Items.ValueNode, BreakingAdded(CompSchema, PropItems), nil, rSchema.Items.Value) + } + // removed Items + if (lSchema != nil && lSchema.Items.Value != nil) && (rSchema == nil || rSchema.Items.Value == nil) { + CreateChange(changes, ObjectRemoved, v3.ItemsLabel, + lSchema.Items.ValueNode, nil, BreakingRemoved(CompSchema, PropItems), lSchema.Items.Value, nil) + } + + // $dynamicAnchor (JSON Schema 2020-12) + lnv = nil + rnv = nil + if lSchema != nil && lSchema.DynamicAnchor.ValueNode != nil { + lnv = lSchema.DynamicAnchor.ValueNode + } + if rSchema != nil && rSchema.DynamicAnchor.ValueNode != nil { + rnv = rSchema.DynamicAnchor.ValueNode + } + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.DynamicAnchorLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropDynamicAnchor), + Component: CompSchema, + Property: PropDynamicAnchor, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + // $dynamicRef (JSON Schema 2020-12) + if lSchema != nil && lSchema.DynamicRef.ValueNode != nil { + lnv = lSchema.DynamicRef.ValueNode + } + if rSchema != nil && rSchema.DynamicRef.ValueNode != nil { + rnv = rSchema.DynamicRef.ValueNode + } + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: v3.DynamicRefLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropDynamicRef), + Component: CompSchema, + Property: PropDynamicRef, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + // $id (JSON Schema 2020-12) + if lSchema != nil && lSchema.Id.ValueNode != nil { + lnv = lSchema.Id.ValueNode + } + if rSchema != nil && rSchema.Id.ValueNode != nil { + rnv = rSchema.Id.ValueNode + } + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: base.IdLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropId), + Component: CompSchema, + Property: PropId, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + // $comment (JSON Schema 2020-12) + if lSchema != nil && lSchema.Comment.ValueNode != nil { + lnv = lSchema.Comment.ValueNode + } + if rSchema != nil && rSchema.Comment.ValueNode != nil { + rnv = rSchema.Comment.ValueNode + } + props = append(props, &PropertyCheck{ + LeftNode: lnv, + RightNode: rnv, + Label: base.CommentLabel, + Changes: changes, + Breaking: BreakingModified(CompSchema, PropComment), + Component: CompSchema, + Property: PropComment, + Original: lSchema, + New: rSchema, + }) + lnv = nil + rnv = nil + + // contentSchema (JSON Schema 2020-12) - recursive schema comparison + if lSchema != nil && !lSchema.ContentSchema.IsEmpty() && rSchema != nil && !rSchema.ContentSchema.IsEmpty() { + sc.ContentSchemaChanges = CompareSchemas(lSchema.ContentSchema.Value, rSchema.ContentSchema.Value) + } + if lSchema != nil && !lSchema.ContentSchema.IsEmpty() && (rSchema == nil || rSchema.ContentSchema.IsEmpty()) { + CreateChange(changes, PropertyRemoved, base.ContentSchemaLabel, + lSchema.ContentSchema.ValueNode, nil, + BreakingRemoved(CompSchema, PropContentSchema), + lSchema.ContentSchema.Value, nil) + } + if (lSchema == nil || lSchema.ContentSchema.IsEmpty()) && rSchema != nil && !rSchema.ContentSchema.IsEmpty() { + CreateChange(changes, PropertyAdded, base.ContentSchemaLabel, + nil, rSchema.ContentSchema.ValueNode, + BreakingAdded(CompSchema, PropContentSchema), + nil, rSchema.ContentSchema.Value) + } + + // $vocabulary (JSON Schema 2020-12) - map comparison + // note: vocabulary changes are stored in VocabularyChanges and counted separately + // in TotalChanges(), so they should NOT be appended to the main changes slice + var lVocab, rVocab *orderedmap.Map[low.KeyReference[string], low.ValueReference[bool]] + if lSchema != nil { + lVocab = lSchema.Vocabulary.Value + } + if rSchema != nil { + rVocab = rSchema.Vocabulary.Value + } + if lVocab != nil || rVocab != nil { + sc.VocabularyChanges = checkVocabularyChanges(lVocab, rVocab) + } + + // check extensions + var lext *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + var rext *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + if lSchema != nil { + lext = lSchema.Extensions + } + if rSchema != nil { + rext = rSchema.Extensions + } + if lext != nil && rext != nil { + sc.ExtensionChanges = CompareExtensions(lext, rext) + } + + // check core properties + CheckProperties(props) + + // Post-process: Update context line numbers for Type changes to use schema KeyNode for better context + // This provides line where "schema:" is defined, not "type: value" + if changes != nil && len(*changes) > 0 { + for _, change := range *changes { + if change.Property == v3.TypeLabel && change.Context != nil { + if lProxy != nil && lProxy.GetKeyNode() != nil { + line := lProxy.GetKeyNode().Line + col := lProxy.GetKeyNode().Column + change.Context.OriginalLine = &line + change.Context.OriginalColumn = &col + } + if rProxy != nil && rProxy.GetKeyNode() != nil { + line := rProxy.GetKeyNode().Line + col := rProxy.GetKeyNode().Column + change.Context.NewLine = &line + change.Context.NewColumn = &col + } + break // found the type change, no need to continue + } + } + } +} + +func checkExamples(lSchema *base.Schema, rSchema *base.Schema, changes *[]*Change) { + if lSchema == nil && rSchema == nil { + return + } + + // check examples (3.1+) + var lExampKey, rExampKey []string + lExampN := make(map[string]*yaml.Node) + rExampN := make(map[string]*yaml.Node) + lExampVal := make(map[string]any) + rExampVal := make(map[string]any) + + // create keys by hashing values + if lSchema != nil && lSchema.Examples.ValueNode != nil { + for i := range lSchema.Examples.ValueNode.Content { + key := low.GenerateHashString(lSchema.Examples.ValueNode.Content[i].Value) + lExampKey = append(lExampKey, key) + lExampVal[key] = lSchema.Examples.ValueNode.Content[i].Value + lExampN[key] = lSchema.Examples.ValueNode.Content[i] + + } + } + if rSchema != nil && rSchema.Examples.ValueNode != nil { + for i := range rSchema.Examples.ValueNode.Content { + key := low.GenerateHashString(rSchema.Examples.ValueNode.Content[i].Value) + rExampKey = append(rExampKey, key) + rExampVal[key] = rSchema.Examples.ValueNode.Content[i].Value + rExampN[key] = rSchema.Examples.ValueNode.Content[i] + } + } + + // if examples equal lengths, check for equality + if len(lExampKey) == len(rExampKey) { + for i := range lExampKey { + if lExampKey[i] != rExampKey[i] { + CreateChangeWithEncoding(changes, Modified, v3.ExamplesLabel, + lExampN[lExampKey[i]], rExampN[rExampKey[i]], BreakingModified(CompSchema, PropExamples), + lExampVal[lExampKey[i]], rExampVal[rExampKey[i]]) + } + } + } + // examples were removed. + if len(lExampKey) > len(rExampKey) { + for i := range lExampKey { + if i < len(rExampKey) && lExampKey[i] != rExampKey[i] { + CreateChangeWithEncoding(changes, Modified, v3.ExamplesLabel, + lExampN[lExampKey[i]], rExampN[rExampKey[i]], BreakingModified(CompSchema, PropExamples), + lExampVal[lExampKey[i]], rExampVal[rExampKey[i]]) + } + if i >= len(rExampKey) { + CreateChangeWithEncoding(changes, ObjectRemoved, v3.ExamplesLabel, + lExampN[lExampKey[i]], nil, BreakingRemoved(CompSchema, PropExamples), + lExampVal[lExampKey[i]], nil) + } + } + } + + // examples were added + if len(lExampKey) < len(rExampKey) { + for i := range rExampKey { + if i < len(lExampKey) && lExampKey[i] != rExampKey[i] { + CreateChangeWithEncoding(changes, Modified, v3.ExamplesLabel, + lExampN[lExampKey[i]], rExampN[rExampKey[i]], BreakingModified(CompSchema, PropExamples), + lExampVal[lExampKey[i]], rExampVal[rExampKey[i]]) + } + if i >= len(lExampKey) { + CreateChangeWithEncoding(changes, ObjectAdded, v3.ExamplesLabel, + nil, rExampN[rExampKey[i]], BreakingAdded(CompSchema, PropExamples), + nil, rExampVal[rExampKey[i]]) + } + } + } +} + +func extractSchemaChanges( + lSchema []low.ValueReference[*base.SchemaProxy], + rSchema []low.ValueReference[*base.SchemaProxy], + label string, + sc *[]*SchemaChanges, + changes *[]*Change, +) { + // if there is nothing here, there is nothing to do. + if lSchema == nil && rSchema == nil { + return + } + + x := "%x" + // create hash key maps to check equality + lKeys := make([]string, 0, len(lSchema)) + rKeys := make([]string, 0, len(rSchema)) + lEntities := make(map[string]*base.SchemaProxy) + rEntities := make(map[string]*base.SchemaProxy) + + for h := range lSchema { + q := lSchema[h].Value + z := fmt.Sprintf(x, q.Hash()) + lKeys = append(lKeys, z) + lEntities[z] = q + } + for h := range rSchema { + q := rSchema[h].Value + z := fmt.Sprintf(x, q.Hash()) + rKeys = append(rKeys, z) + rEntities[z] = q + } + + // check for identical lengths + if len(lKeys) == len(rKeys) { + for w := range lKeys { + // keys are different, which means there are changes. + if lKeys[w] != rKeys[w] { + *sc = append(*sc, CompareSchemas(lEntities[lKeys[w]], rEntities[rKeys[w]])) + } + } + } + + // things were removed + if len(lKeys) > len(rKeys) { + for w := range lKeys { + if w < len(rKeys) && lKeys[w] != rKeys[w] { + *sc = append(*sc, CompareSchemas(lEntities[lKeys[w]], rEntities[rKeys[w]])) + } + if w >= len(rKeys) { + // determine breaking status based on label + breaking := true + switch label { + case v3.AllOfLabel: + breaking = BreakingRemoved(CompSchema, PropAllOf) + case v3.AnyOfLabel: + breaking = BreakingRemoved(CompSchema, PropAnyOf) + case v3.OneOfLabel: + breaking = BreakingRemoved(CompSchema, PropOneOf) + case v3.PrefixItemsLabel: + breaking = BreakingRemoved(CompSchema, PropPrefixItems) + } + CreateChange(changes, ObjectRemoved, label, + lEntities[lKeys[w]].GetValueNode(), nil, breaking, lEntities[lKeys[w]], nil) + } + } + } + + // things were added + if len(rKeys) > len(lKeys) { + for w := range rKeys { + if w < len(lKeys) && rKeys[w] != lKeys[w] { + *sc = append(*sc, CompareSchemas(lEntities[lKeys[w]], rEntities[rKeys[w]])) + } + if w >= len(lKeys) { + // determine breaking status based on label + breaking := false + switch label { + case v3.AllOfLabel: + breaking = BreakingAdded(CompSchema, PropAllOf) + case v3.AnyOfLabel: + breaking = BreakingAdded(CompSchema, PropAnyOf) + case v3.OneOfLabel: + breaking = BreakingAdded(CompSchema, PropOneOf) + case v3.PrefixItemsLabel: + breaking = BreakingAdded(CompSchema, PropPrefixItems) + } + CreateChange(changes, ObjectAdded, label, + nil, rEntities[rKeys[w]].GetValueNode(), breaking, nil, rEntities[rKeys[w]]) + } + } + } +} + +// checkDependentRequiredChanges compares two DependentRequired maps and returns any changes found +func checkDependentRequiredChanges( + left, right *orderedmap.Map[low.KeyReference[string], low.ValueReference[[]string]], +) []*Change { + // If both are nil, no changes + if left == nil && right == nil { + return nil + } + + var changes []*Change + + leftMap := make(map[string][]string) + rightMap := make(map[string][]string) + + // Build left map + if left != nil { + for prop, reqArray := range left.FromOldest() { + leftMap[prop.Value] = reqArray.Value + } + } + + // Build right map + if right != nil { + for prop, reqArray := range right.FromOldest() { + rightMap[prop.Value] = reqArray.Value + } + } + + // Check for property additions and modifications + for prop, rightReqs := range rightMap { + if leftReqs, exists := leftMap[prop]; exists { + // Property exists in both, check if requirements changed + if !slicesEqual(leftReqs, rightReqs) { + CreateChange(&changes, Modified, prop, + getNodeForProperty(left, prop), getNodeForProperty(right, prop), + BreakingModified(CompSchema, PropDependentRequired), leftReqs, rightReqs) + } + } else { + // Property added + CreateChange(&changes, PropertyAdded, prop, + nil, getNodeForProperty(right, prop), + BreakingAdded(CompSchema, PropDependentRequired), nil, rightReqs) + } + } + + // Check for property removals + for prop, leftReqs := range leftMap { + if _, exists := rightMap[prop]; !exists { + CreateChange(&changes, PropertyRemoved, prop, + getNodeForProperty(left, prop), nil, + BreakingRemoved(CompSchema, PropDependentRequired), leftReqs, nil) + } + } + + return changes +} + +// slicesEqual compares two string slices for equality (order matters) +func slicesEqual(a, b []string) bool { + if len(a) != len(b) { + return false + } + for i, v := range a { + if b[i] != v { + return false + } + } + return true +} + +// getNodeForProperty gets the YAML node for a specific property in a DependentRequired map +func getNodeForProperty(depMap *orderedmap.Map[low.KeyReference[string], low.ValueReference[[]string]], prop string) *yaml.Node { + if depMap == nil { + return nil + } + for key, value := range depMap.FromOldest() { + if key.Value == prop { + return value.ValueNode + } + } + return nil +} + +// checkVocabularyChanges compares $vocabulary maps and returns a list of changes. +// the caller is responsible for appending the returned changes to their main changes slice. +func checkVocabularyChanges(lVocab, rVocab *orderedmap.Map[low.KeyReference[string], low.ValueReference[bool]]) []*Change { + if lVocab == nil && rVocab == nil { + return nil + } + + // pre-allocate maps with size hints for better memory efficiency + lSize := orderedmap.Len(lVocab) + rSize := orderedmap.Len(rVocab) + + lVocabMap := make(map[string]bool, lSize) + lVocabNodes := make(map[string]*yaml.Node, lSize) + rVocabMap := make(map[string]bool, rSize) + rVocabNodes := make(map[string]*yaml.Node, rSize) + + if lVocab != nil { + for k, v := range lVocab.FromOldest() { + lVocabMap[k.Value] = v.Value + lVocabNodes[k.Value] = v.ValueNode + } + } + if rVocab != nil { + for k, v := range rVocab.FromOldest() { + rVocabMap[k.Value] = v.Value + rVocabNodes[k.Value] = v.ValueNode + } + } + + // pre-allocate result slice with reasonable capacity + var vocabChanges []*Change + + // check for removed or modified vocabularies + for uri, lVal := range lVocabMap { + if rVal, ok := rVocabMap[uri]; ok { + // vocabulary exists in both - check if value changed + if lVal != rVal { + c := &Change{ + Property: base.VocabularyLabel, + ChangeType: Modified, + Original: fmt.Sprintf("%s=%v", uri, lVal), + New: fmt.Sprintf("%s=%v", uri, rVal), + Breaking: BreakingModified(CompSchema, PropVocabulary), + OriginalObject: lVocabMap, + NewObject: rVocabMap, + } + if lVocabNodes[uri] != nil { + c.Context = CreateContext(lVocabNodes[uri], rVocabNodes[uri]) + } + vocabChanges = append(vocabChanges, c) + } + } else { + // vocabulary was removed + c := &Change{ + Property: base.VocabularyLabel, + ChangeType: PropertyRemoved, + Original: uri, + Breaking: BreakingRemoved(CompSchema, PropVocabulary), + OriginalObject: lVocabMap, + } + if lVocabNodes[uri] != nil { + c.Context = CreateContext(lVocabNodes[uri], nil) + } + vocabChanges = append(vocabChanges, c) + } + } + + // check for added vocabularies + for uri := range rVocabMap { + if _, ok := lVocabMap[uri]; !ok { + // vocabulary was added + c := &Change{ + Property: base.VocabularyLabel, + ChangeType: PropertyAdded, + New: uri, + Breaking: BreakingAdded(CompSchema, PropVocabulary), + NewObject: rVocabMap, + } + if rVocabNodes[uri] != nil { + c.Context = CreateContext(nil, rVocabNodes[uri]) + } + vocabChanges = append(vocabChanges, c) + } + } + + return vocabChanges +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/scopes.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/scopes.go new file mode 100644 index 000000000..4e38c9814 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/scopes.go @@ -0,0 +1,82 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "github.com/pb33f/libopenapi/datamodel/low" + v2 "github.com/pb33f/libopenapi/datamodel/low/v2" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +// ScopesChanges represents changes between two Swagger Scopes Objects +type ScopesChanges struct { + *PropertyChanges + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between Scopes objects +func (s *ScopesChanges) GetAllChanges() []*Change { + if s == nil { + return nil + } + var changes []*Change + changes = append(changes, s.Changes...) + if s.ExtensionChanges != nil { + changes = append(changes, s.ExtensionChanges.GetAllChanges()...) + } + return changes +} + +// TotalChanges returns the total changes found between two Swagger Scopes objects. +func (s *ScopesChanges) TotalChanges() int { + if s == nil { + return 0 + } + c := s.PropertyChanges.TotalChanges() + if s.ExtensionChanges != nil { + c += s.ExtensionChanges.TotalChanges() + } + return c +} + +// TotalBreakingChanges returns the total number of breaking changes between two Swagger Scopes objects. +func (s *ScopesChanges) TotalBreakingChanges() int { + return s.PropertyChanges.TotalBreakingChanges() +} + +// CompareScopes compares a left and right Swagger Scopes objects for changes. If anything is found, returns +// a pointer to ScopesChanges, or returns nil if nothing is found. +func CompareScopes(l, r *v2.Scopes) *ScopesChanges { + if low.AreEqual(l, r) { + return nil + } + var changes []*Change + for k, v := range l.Values.FromOldest() { + if r != nil && r.FindScope(k.Value) == nil { + CreateChange(&changes, ObjectRemoved, v3.Scopes, + v.ValueNode, nil, BreakingRemoved(CompOAuthFlow, PropScopes), + k.Value, nil) + continue + } + if r != nil && r.FindScope(k.Value) != nil { + if v.Value != r.FindScope(k.Value).Value { + CreateChange(&changes, Modified, v3.Scopes, + v.ValueNode, r.FindScope(k.Value).ValueNode, BreakingModified(CompOAuthFlow, PropScopes), + v.Value, r.FindScope(k.Value).Value) + } + } + } + for k, v := range r.Values.FromOldest() { + if l != nil && l.FindScope(k.Value) == nil { + CreateChange(&changes, ObjectAdded, v3.Scopes, + nil, v.ValueNode, BreakingAdded(CompOAuthFlow, PropScopes), + nil, k.Value) + } + } + + sc := new(ScopesChanges) + sc.PropertyChanges = NewPropertyChanges(changes) + sc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) + return sc +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/security_requirement.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/security_requirement.go new file mode 100644 index 000000000..78a5d2e7b --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/security_requirement.go @@ -0,0 +1,192 @@ +// Copyright 2022-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/orderedmap" + "go.yaml.in/yaml/v4" +) + +// SecurityRequirementChanges represents changes found between two SecurityRequirement Objects. +type SecurityRequirementChanges struct { + *PropertyChanges +} + +// GetAllChanges returns a slice of all changes made between SecurityRequirement objects +func (s *SecurityRequirementChanges) GetAllChanges() []*Change { + if s == nil { + return nil + } + return s.Changes +} + +// TotalChanges returns the total number of changes between two SecurityRequirement Objects. +func (s *SecurityRequirementChanges) TotalChanges() int { + if s == nil { + return 0 + } + return s.PropertyChanges.TotalChanges() +} + +// TotalBreakingChanges returns the total number of breaking changes between two SecurityRequirement Objects. +func (s *SecurityRequirementChanges) TotalBreakingChanges() int { + return s.PropertyChanges.TotalBreakingChanges() +} + +// CompareSecurityRequirement compares left and right SecurityRequirement objects for changes. If anything +// is found, then a pointer to SecurityRequirementChanges is returned, otherwise nil. +func CompareSecurityRequirement(l, r *base.SecurityRequirement) *SecurityRequirementChanges { + var changes []*Change + sc := new(SecurityRequirementChanges) + + if low.AreEqual(l, r) { + return nil + } + checkSecurityRequirement(l.Requirements.Value, r.Requirements.Value, &changes) + sc.PropertyChanges = NewPropertyChanges(changes) + return sc +} + +func removedSecurityRequirement(vn *yaml.Node, schemeName, scopeName string, changes *[]*Change) { + property := schemeName + value := scopeName + var node *yaml.Node = vn + breaking := BreakingRemoved(CompSecurityRequirement, PropSchemes) + if scopeName == "" { + // entire scheme was removed, use scheme name as value + value = schemeName + // Don't use the node for entire scheme removal, as it may be an empty array [] + node = nil + } else { + // scope was removed + breaking = BreakingRemoved(CompSecurityRequirement, PropScopes) + } + CreateChange(changes, ObjectRemoved, property, + node, nil, breaking, value, nil) +} + +func addedSecurityRequirement(vn *yaml.Node, schemeName, scopeName string, changes *[]*Change) { + property := schemeName + value := scopeName + var node *yaml.Node = vn + breaking := BreakingAdded(CompSecurityRequirement, PropSchemes) + if scopeName == "" { + // entire scheme was added, use scheme name as value + value = schemeName + // Don't use the node for entire scheme addition, as it may be an empty array [] + node = nil + } else { + // scope was added + breaking = BreakingAdded(CompSecurityRequirement, PropScopes) + } + CreateChange(changes, ObjectAdded, property, + nil, node, breaking, nil, value) +} + +// tricky to do this correctly, this is my solution. +func checkSecurityRequirement(lSec, rSec *orderedmap.Map[low.KeyReference[string], low.ValueReference[[]low.ValueReference[string]]], + changes *[]*Change, +) { + lKeys := make([]string, orderedmap.Len(lSec)) + rKeys := make([]string, orderedmap.Len(rSec)) + lValues := make(map[string]low.ValueReference[[]low.ValueReference[string]]) + rValues := make(map[string]low.ValueReference[[]low.ValueReference[string]]) + var n, z int + for k, v := range lSec.FromOldest() { + lKeys[n] = k.Value + lValues[k.Value] = v + n++ + } + for k, v := range rSec.FromOldest() { + rKeys[z] = k.Value + rValues[k.Value] = v + z++ + } + + for z = range lKeys { + if z < len(rKeys) { + if _, ok := rValues[lKeys[z]]; !ok { + removedSecurityRequirement(lValues[lKeys[z]].ValueNode, lKeys[z], "", changes) + continue + } + + lValue := lValues[lKeys[z]].Value + rValue := rValues[lKeys[z]].Value + + // check if actual values match up + lRoleKeys := make([]string, len(lValue)) + rRoleKeys := make([]string, len(rValue)) + lRoleValues := make(map[string]low.ValueReference[string]) + rRoleValues := make(map[string]low.ValueReference[string]) + var t, k int + for i := range lValue { + if lValue[i].Value == "" { + continue // Skip empty scope values (from malformed YAML) + } + lRoleKeys[t] = lValue[i].Value + lRoleValues[lValue[i].Value] = lValue[i] + t++ + } + lRoleKeys = lRoleKeys[:t] // Trim to actual size + + for i := range rValue { + if rValue[i].Value == "" { + continue // Skip empty scope values (from malformed YAML) + } + rRoleKeys[k] = rValue[i].Value + rRoleValues[rValue[i].Value] = rValue[i] + k++ + } + rRoleKeys = rRoleKeys[:k] // Trim to actual size + + for t = range lRoleKeys { + if t < len(rRoleKeys) { + if _, ok := rRoleValues[lRoleKeys[t]]; !ok { + removedSecurityRequirement(lRoleValues[lRoleKeys[t]].ValueNode, lKeys[z], lRoleKeys[t], changes) + continue + } + } + if t >= len(rRoleKeys) { + if _, ok := rRoleValues[lRoleKeys[t]]; !ok { + removedSecurityRequirement(lRoleValues[lRoleKeys[t]].ValueNode, lKeys[z], lRoleKeys[t], changes) + } + } + } + for t = range rRoleKeys { + if t < len(lRoleKeys) { + if _, ok := lRoleValues[rRoleKeys[t]]; !ok { + addedSecurityRequirement(rRoleValues[rRoleKeys[t]].ValueNode, rKeys[z], rRoleKeys[t], changes) + continue + } + } + if t >= len(lRoleKeys) { + if _, ok := lRoleValues[rRoleKeys[t]]; !ok { + addedSecurityRequirement(rRoleValues[rRoleKeys[t]].ValueNode, rKeys[z], rRoleKeys[t], changes) + } + } + } + + } + if z >= len(rKeys) { + if _, ok := rValues[lKeys[z]]; !ok { + removedSecurityRequirement(lValues[lKeys[z]].ValueNode, lKeys[z], "", changes) + } + } + } + for z = range rKeys { + if z < len(lKeys) { + if _, ok := lValues[rKeys[z]]; !ok { + addedSecurityRequirement(rValues[rKeys[z]].ValueNode, rKeys[z], "", changes) + continue + } + } + if z >= len(lKeys) { + if _, ok := lValues[rKeys[z]]; !ok { + addedSecurityRequirement(rValues[rKeys[z]].ValueNode, rKeys[z], "", changes) + } + } + } +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/security_scheme.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/security_scheme.go new file mode 100644 index 000000000..4b6b5d471 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/security_scheme.go @@ -0,0 +1,203 @@ +// Copyright 2022-2025 Princess Beef Heavy Industries, LLC / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "reflect" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +// SecuritySchemeChanges represents changes made between Swagger or OpenAPI SecurityScheme Objects. +type SecuritySchemeChanges struct { + *PropertyChanges + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` + + // OpenAPI Version + OAuthFlowChanges *OAuthFlowsChanges `json:"oAuthFlow,omitempty" yaml:"oAuthFlow,omitempty"` + + // Swagger Version + ScopesChanges *ScopesChanges `json:"scopes,omitempty" yaml:"scopes,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between SecurityRequirement objects +func (ss *SecuritySchemeChanges) GetAllChanges() []*Change { + if ss == nil { + return nil + } + var changes []*Change + changes = append(changes, ss.Changes...) + if ss.OAuthFlowChanges != nil { + changes = append(changes, ss.OAuthFlowChanges.GetAllChanges()...) + } + if ss.ScopesChanges != nil { + changes = append(changes, ss.ScopesChanges.GetAllChanges()...) + } + if ss.ExtensionChanges != nil { + changes = append(changes, ss.ExtensionChanges.GetAllChanges()...) + } + return changes +} + +// TotalChanges represents total changes found between two Swagger or OpenAPI SecurityScheme instances. +func (ss *SecuritySchemeChanges) TotalChanges() int { + if ss == nil { + return 0 + } + c := ss.PropertyChanges.TotalChanges() + if ss.OAuthFlowChanges != nil { + c += ss.OAuthFlowChanges.TotalChanges() + } + if ss.ScopesChanges != nil { + c += ss.ScopesChanges.TotalChanges() + } + if ss.ExtensionChanges != nil { + c += ss.ExtensionChanges.TotalChanges() + } + return c +} + +// TotalBreakingChanges returns total number of breaking changes between two SecurityScheme Objects. +func (ss *SecuritySchemeChanges) TotalBreakingChanges() int { + c := ss.PropertyChanges.TotalBreakingChanges() + if ss.OAuthFlowChanges != nil { + c += ss.OAuthFlowChanges.TotalBreakingChanges() + } + if ss.ScopesChanges != nil { + c += ss.ScopesChanges.TotalBreakingChanges() + } + return c +} + +// CompareSecuritySchemesV2 is a Swagger type safe proxy for CompareSecuritySchemes +func CompareSecuritySchemesV2(l, r *v2.SecurityScheme) *SecuritySchemeChanges { + return CompareSecuritySchemes(l, r) +} + +// CompareSecuritySchemesV3 is an OpenAPI type safe proxt for CompareSecuritySchemes +func CompareSecuritySchemesV3(l, r *v3.SecurityScheme) *SecuritySchemeChanges { + return CompareSecuritySchemes(l, r) +} + +// CompareSecuritySchemes compares left and right Swagger or OpenAPI Security Scheme objects for changes. +// If anything is found, returns a pointer to *SecuritySchemeChanges or nil if nothing is found. +func CompareSecuritySchemes(l, r any) *SecuritySchemeChanges { + var props []*PropertyCheck + var changes []*Change + + sc := new(SecuritySchemeChanges) + if reflect.TypeOf(&v2.SecurityScheme{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v2.SecurityScheme{}) == reflect.TypeOf(r) { + + lSS := l.(*v2.SecurityScheme) + rSS := r.(*v2.SecurityScheme) + + if low.AreEqual(lSS, rSS) { + return nil + } + addPropertyCheck(&props, lSS.Type.ValueNode, rSS.Type.ValueNode, + lSS.Type.Value, rSS.Type.Value, &changes, v3.TypeLabel, true, CompSecurityScheme, PropType) + + addPropertyCheck(&props, lSS.Description.ValueNode, rSS.Description.ValueNode, + lSS.Description.Value, rSS.Description.Value, &changes, v3.DescriptionLabel, false, CompSecurityScheme, PropDescription) + + addPropertyCheck(&props, lSS.Name.ValueNode, rSS.Name.ValueNode, + lSS.Name.Value, rSS.Name.Value, &changes, v3.NameLabel, true, CompSecurityScheme, PropName) + + addPropertyCheck(&props, lSS.In.ValueNode, rSS.In.ValueNode, + lSS.In.Value, rSS.In.Value, &changes, v3.InLabel, true, CompSecurityScheme, PropIn) + + addPropertyCheck(&props, lSS.Flow.ValueNode, rSS.Flow.ValueNode, + lSS.Flow.Value, rSS.Flow.Value, &changes, v3.FlowLabel, true, CompSecurityScheme, PropFlow) + + addPropertyCheck(&props, lSS.AuthorizationUrl.ValueNode, rSS.AuthorizationUrl.ValueNode, + lSS.AuthorizationUrl.Value, rSS.AuthorizationUrl.Value, &changes, v3.AuthorizationUrlLabel, true, CompSecurityScheme, PropAuthorizationURL) + + addPropertyCheck(&props, lSS.TokenUrl.ValueNode, rSS.TokenUrl.ValueNode, + lSS.TokenUrl.Value, rSS.TokenUrl.Value, &changes, v3.TokenUrlLabel, true, CompSecurityScheme, PropTokenURL) + + if !lSS.Scopes.IsEmpty() && !rSS.Scopes.IsEmpty() { + if !low.AreEqual(lSS.Scopes.Value, rSS.Scopes.Value) { + sc.ScopesChanges = CompareScopes(lSS.Scopes.Value, rSS.Scopes.Value) + } + } + if lSS.Scopes.IsEmpty() && !rSS.Scopes.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.ScopesLabel, + nil, rSS.Scopes.ValueNode, BreakingAdded(CompSecurityScheme, PropScopes), nil, rSS.Scopes.Value) + } + if !lSS.Scopes.IsEmpty() && rSS.Scopes.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.ScopesLabel, + lSS.Scopes.ValueNode, nil, BreakingRemoved(CompSecurityScheme, PropScopes), lSS.Scopes.Value, nil) + } + + sc.ExtensionChanges = CompareExtensions(lSS.Extensions, rSS.Extensions) + } + + if reflect.TypeOf(&v3.SecurityScheme{}) == reflect.TypeOf(l) && + reflect.TypeOf(&v3.SecurityScheme{}) == reflect.TypeOf(r) { + + lSS := l.(*v3.SecurityScheme) + rSS := r.(*v3.SecurityScheme) + + if low.AreEqual(lSS, rSS) { + return nil + } + addPropertyCheck(&props, lSS.Type.ValueNode, rSS.Type.ValueNode, + lSS.Type.Value, rSS.Type.Value, &changes, v3.TypeLabel, + BreakingModified(CompSecurityScheme, PropType), CompSecurityScheme, PropType) + + addPropertyCheck(&props, lSS.Description.ValueNode, rSS.Description.ValueNode, + lSS.Description.Value, rSS.Description.Value, &changes, v3.DescriptionLabel, + BreakingModified(CompSecurityScheme, PropDescription), CompSecurityScheme, PropDescription) + + addPropertyCheck(&props, lSS.Name.ValueNode, rSS.Name.ValueNode, + lSS.Name.Value, rSS.Name.Value, &changes, v3.NameLabel, + BreakingModified(CompSecurityScheme, PropName), CompSecurityScheme, PropName) + + addPropertyCheck(&props, lSS.In.ValueNode, rSS.In.ValueNode, + lSS.In.Value, rSS.In.Value, &changes, v3.InLabel, + BreakingModified(CompSecurityScheme, PropIn), CompSecurityScheme, PropIn) + + addPropertyCheck(&props, lSS.Scheme.ValueNode, rSS.Scheme.ValueNode, + lSS.Scheme.Value, rSS.Scheme.Value, &changes, v3.SchemeLabel, + BreakingModified(CompSecurityScheme, PropScheme), CompSecurityScheme, PropScheme) + + addPropertyCheck(&props, lSS.BearerFormat.ValueNode, rSS.BearerFormat.ValueNode, + lSS.BearerFormat.Value, rSS.BearerFormat.Value, &changes, v3.SchemeLabel, + BreakingModified(CompSecurityScheme, PropBearerFormat), CompSecurityScheme, PropBearerFormat) + + addPropertyCheck(&props, lSS.OpenIdConnectUrl.ValueNode, rSS.OpenIdConnectUrl.ValueNode, + lSS.OpenIdConnectUrl.Value, rSS.OpenIdConnectUrl.Value, &changes, v3.OpenIdConnectUrlLabel, + BreakingModified(CompSecurityScheme, PropOpenIDConnectURL), CompSecurityScheme, PropOpenIDConnectURL) + + // OpenAPI 3.2+ fields + addPropertyCheck(&props, lSS.OAuth2MetadataUrl.ValueNode, rSS.OAuth2MetadataUrl.ValueNode, + lSS.OAuth2MetadataUrl.Value, rSS.OAuth2MetadataUrl.Value, &changes, v3.OAuth2MetadataUrlLabel, + BreakingModified(CompSecurityScheme, PropOAuth2MetadataUrl), CompSecurityScheme, PropOAuth2MetadataUrl) + + addPropertyCheck(&props, lSS.Deprecated.ValueNode, rSS.Deprecated.ValueNode, + lSS.Deprecated.Value, rSS.Deprecated.Value, &changes, v3.DeprecatedLabel, + BreakingModified(CompSecurityScheme, PropDeprecated), CompSecurityScheme, PropDeprecated) + + if !lSS.Flows.IsEmpty() && !rSS.Flows.IsEmpty() { + if !low.AreEqual(lSS.Flows.Value, rSS.Flows.Value) { + sc.OAuthFlowChanges = CompareOAuthFlows(lSS.Flows.Value, rSS.Flows.Value) + } + } + if lSS.Flows.IsEmpty() && !rSS.Flows.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.FlowsLabel, + nil, rSS.Flows.ValueNode, BreakingAdded(CompSecurityScheme, PropFlows), nil, rSS.Flows.Value) + } + if !lSS.Flows.IsEmpty() && rSS.Flows.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.FlowsLabel, + lSS.Flows.ValueNode, nil, BreakingRemoved(CompSecurityScheme, PropFlows), lSS.Flows.Value, nil) + } + sc.ExtensionChanges = CompareExtensions(lSS.Extensions, rSS.Extensions) + } + CheckProperties(props) + sc.PropertyChanges = NewPropertyChanges(changes) + return sc +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/server.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/server.go new file mode 100644 index 000000000..86914a061 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/server.go @@ -0,0 +1,89 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +// ServerChanges represents changes found between two OpenAPI Server Objects +type ServerChanges struct { + *PropertyChanges + Server *v3.Server + ServerVariableChanges map[string]*ServerVariableChanges `json:"serverVariables,omitempty" yaml:"serverVariables,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between SecurityRequirement objects +func (s *ServerChanges) GetAllChanges() []*Change { + if s == nil { + return nil + } + var changes []*Change + changes = append(changes, s.Changes...) + for k := range s.ServerVariableChanges { + changes = append(changes, s.ServerVariableChanges[k].GetAllChanges()...) + } + if s.ExtensionChanges != nil { + changes = append(changes, s.ExtensionChanges.GetAllChanges()...) + } + return changes +} + +// TotalChanges returns total changes found between two OpenAPI Server Objects +func (s *ServerChanges) TotalChanges() int { + if s == nil { + return 0 + } + c := s.PropertyChanges.TotalChanges() + for k := range s.ServerVariableChanges { + c += s.ServerVariableChanges[k].TotalChanges() + } + if s.ExtensionChanges != nil { + c += s.ExtensionChanges.TotalChanges() + } + return c +} + +// TotalBreakingChanges returns the total number of breaking changes found between two OpenAPI Server objects. +func (s *ServerChanges) TotalBreakingChanges() int { + c := s.PropertyChanges.TotalBreakingChanges() + for k := range s.ServerVariableChanges { + c += s.ServerVariableChanges[k].TotalBreakingChanges() + } + return c +} + +// CompareServers compares two OpenAPI Server objects for any changes. If anything is found, returns a pointer +// to a ServerChanges instance, or returns nil if nothing is found. +func CompareServers(l, r *v3.Server) *ServerChanges { + if low.AreEqual(l, r) { + return nil + } + var changes []*Change + props := make([]*PropertyCheck, 0, 3) + + props = append(props, + NewPropertyCheck(CompServer, PropName, + l.Name.ValueNode, r.Name.ValueNode, + v3.NameLabel, &changes, l, r), + NewPropertyCheck(CompServer, PropURL, + l.URL.ValueNode, r.URL.ValueNode, + v3.URLLabel, &changes, l, r), + NewPropertyCheck(CompServer, PropDescription, + l.Description.ValueNode, r.Description.ValueNode, + v3.DescriptionLabel, &changes, l, r), + ) + + CheckProperties(props) + sc := new(ServerChanges) + sc.PropertyChanges = NewPropertyChanges(changes) + sc.ServerVariableChanges = CheckMapForChanges(l.Variables.Value, r.Variables.Value, + &changes, v3.VariablesLabel, CompareServerVariables) + + sc.ExtensionChanges = CompareExtensions(l.Extensions, r.Extensions) + sc.Server = r + return sc +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/server_variable.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/server_variable.go new file mode 100644 index 000000000..a7a96518f --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/server_variable.go @@ -0,0 +1,71 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +// ServerVariableChanges represents changes found between two OpenAPI ServerVariable Objects +type ServerVariableChanges struct { + *PropertyChanges +} + +// GetAllChanges returns a slice of all changes made between SecurityRequirement objects +func (s *ServerVariableChanges) GetAllChanges() []*Change { + if s == nil { + return nil + } + return s.Changes +} + +// CompareServerVariables compares a left and right OpenAPI ServerVariable object for changes. +// If anything is found, returns a pointer to a ServerVariableChanges instance, otherwise returns nil. +func CompareServerVariables(l, r *v3.ServerVariable) *ServerVariableChanges { + if low.AreEqual(l, r) { + return nil + } + + var changes []*Change + + lValues := make(map[string]low.NodeReference[string]) + rValues := make(map[string]low.NodeReference[string]) + for i := range l.Enum { + lValues[l.Enum[i].Value] = l.Enum[i] + } + for i := range r.Enum { + rValues[r.Enum[i].Value] = r.Enum[i] + } + for k := range lValues { + if _, ok := rValues[k]; !ok { + CreateChange(&changes, ObjectRemoved, v3.EnumLabel, + lValues[k].ValueNode, nil, BreakingRemoved(CompServerVariable, PropEnum), + lValues[k].Value, nil) + continue + } + } + for k := range rValues { + if _, ok := lValues[k]; !ok { + CreateChange(&changes, ObjectAdded, v3.EnumLabel, + lValues[k].ValueNode, rValues[k].ValueNode, BreakingAdded(CompServerVariable, PropEnum), + lValues[k].Value, rValues[k].Value) + } + } + + props := make([]*PropertyCheck, 0, 2) + props = append(props, + NewPropertyCheck(CompServerVariable, PropDefault, + l.Default.ValueNode, r.Default.ValueNode, + v3.DefaultLabel, &changes, l, r), + NewPropertyCheck(CompServerVariable, PropDescription, + l.Description.ValueNode, r.Description.ValueNode, + v3.DescriptionLabel, &changes, l, r), + ) + + CheckProperties(props) + sc := new(ServerVariableChanges) + sc.PropertyChanges = NewPropertyChanges(changes) + return sc +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/tags.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/tags.go new file mode 100644 index 000000000..d839c4052 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/tags.go @@ -0,0 +1,152 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +// TagChanges represents changes made to the Tags object of an OpenAPI document. +type TagChanges struct { + *PropertyChanges + ExternalDocs *ExternalDocChanges `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between Tag objects +func (t *TagChanges) GetAllChanges() []*Change { + if t == nil { + return nil + } + var changes []*Change + changes = append(changes, t.Changes...) + if t.ExternalDocs != nil { + changes = append(changes, t.ExternalDocs.GetAllChanges()...) + } + if t.ExtensionChanges != nil { + changes = append(changes, t.ExtensionChanges.GetAllChanges()...) + } + return changes +} + +// TotalChanges returns a count of everything that changed within tags. +func (t *TagChanges) TotalChanges() int { + if t == nil { + return 0 + } + c := t.PropertyChanges.TotalChanges() + if t.ExternalDocs != nil { + c += t.ExternalDocs.TotalChanges() + } + if t.ExtensionChanges != nil { + c += t.ExtensionChanges.TotalChanges() + } + return c +} + +// TotalBreakingChanges returns the number of breaking changes made by Tags +func (t *TagChanges) TotalBreakingChanges() int { + return t.PropertyChanges.TotalBreakingChanges() +} + +// CompareTags will compare a left (original) and a right (new) slice of ValueReference nodes for +// any changes between them. If there are changes, a pointer to TagChanges is returned, if not then +// nil is returned instead. +func CompareTags(l, r []low.ValueReference[*base.Tag]) []*TagChanges { + var tagResults []*TagChanges + + // look at the original and then look through the new. + seenLeft := make(map[string]*low.ValueReference[*base.Tag]) + seenRight := make(map[string]*low.ValueReference[*base.Tag]) + for i := range l { + h := l[i] + seenLeft[l[i].Value.Name.Value] = &h + } + for i := range r { + h := r[i] + seenRight[r[i].Value.Name.Value] = &h + } + + // var changes []*Change + + // check for removals, modifications and moves + for i := range seenLeft { + tc := new(TagChanges) + var changes []*Change + + CheckForObjectAdditionOrRemoval[*base.Tag](seenLeft, seenRight, i, &changes, BreakingAdded(CompTags, ""), BreakingRemoved(CompTags, "")) + + // if the existing tag exists, let's check it. + if seenRight[i] != nil { + lTag := seenLeft[i].Value + rTag := seenRight[i].Value + props := make([]*PropertyCheck, 0, 5) + + props = append(props, + NewPropertyCheck(CompTag, PropName, + lTag.Name.ValueNode, rTag.Name.ValueNode, + v3.NameLabel, &changes, lTag, rTag), + NewPropertyCheck(CompTag, PropSummary, + lTag.Summary.ValueNode, rTag.Summary.ValueNode, + v3.SummaryLabel, &changes, lTag, rTag), + NewPropertyCheck(CompTag, PropDescription, + lTag.Description.ValueNode, rTag.Description.ValueNode, + v3.DescriptionLabel, &changes, lTag, rTag), + NewPropertyCheck(CompTag, PropParent, + lTag.Parent.ValueNode, rTag.Parent.ValueNode, + v3.ParentLabel, &changes, lTag, rTag), + NewPropertyCheck(CompTag, PropKind, + lTag.Kind.ValueNode, rTag.Kind.ValueNode, + v3.KindLabel, &changes, lTag, rTag), + ) + + // check properties + CheckProperties(props) + + // compare external docs + if !seenLeft[i].Value.ExternalDocs.IsEmpty() && !seenRight[i].Value.ExternalDocs.IsEmpty() { + tc.ExternalDocs = CompareExternalDocs(seenLeft[i].Value.ExternalDocs.Value, + seenRight[i].Value.ExternalDocs.Value) + } + if seenLeft[i].Value.ExternalDocs.IsEmpty() && !seenRight[i].Value.ExternalDocs.IsEmpty() { + CreateChange(&changes, ObjectAdded, v3.ExternalDocsLabel, nil, seenRight[i].GetValueNode(), + BreakingAdded(CompTag, PropExternalDocs), nil, seenRight[i].Value.ExternalDocs.Value) + } + if !seenLeft[i].Value.ExternalDocs.IsEmpty() && seenRight[i].Value.ExternalDocs.IsEmpty() { + CreateChange(&changes, ObjectRemoved, v3.ExternalDocsLabel, seenLeft[i].GetValueNode(), nil, + BreakingRemoved(CompTag, PropExternalDocs), seenLeft[i].Value.ExternalDocs.Value, nil) + } + + // check extensions + tc.ExtensionChanges = CompareExtensions(seenLeft[i].Value.Extensions, seenRight[i].Value.Extensions) + tc.PropertyChanges = NewPropertyChanges(changes) + if tc.TotalChanges() > 0 { + tagResults = append(tagResults, tc) + } + continue + } + + if len(changes) > 0 { + tc.PropertyChanges = NewPropertyChanges(changes) + tagResults = append(tagResults, tc) + } + + } + for i := range seenRight { + if seenLeft[i] == nil { + tc := new(TagChanges) + var changes []*Change + + CreateChange(&changes, ObjectAdded, i, nil, seenRight[i].GetValueNode(), + BreakingAdded(CompTags, ""), nil, seenRight[i].GetValue()) + + tc.PropertyChanges = NewPropertyChanges(changes) + tagResults = append(tagResults, tc) + + } + } + return tagResults +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/model/xml.go b/vendor/github.com/pb33f/libopenapi/what-changed/model/xml.go new file mode 100644 index 000000000..5b9c71c6e --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/model/xml.go @@ -0,0 +1,85 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package model + +import ( + "github.com/pb33f/libopenapi/datamodel/low/base" + v3 "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +// XMLChanges represents changes made to the XML object of an OpenAPI document. +type XMLChanges struct { + *PropertyChanges + ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` +} + +// GetAllChanges returns a slice of all changes made between XML objects +func (x *XMLChanges) GetAllChanges() []*Change { + if x == nil { + return nil + } + var changes []*Change + changes = append(changes, x.Changes...) + if x.ExtensionChanges != nil { + changes = append(changes, x.ExtensionChanges.GetAllChanges()...) + } + return changes +} + +// TotalChanges returns a count of everything that was changed within an XML object. +func (x *XMLChanges) TotalChanges() int { + if x == nil { + return 0 + } + c := x.PropertyChanges.TotalChanges() + if x.ExtensionChanges != nil { + c += x.ExtensionChanges.TotalChanges() + } + return c +} + +// TotalBreakingChanges returns the number of breaking changes made by the XML object. +func (x *XMLChanges) TotalBreakingChanges() int { + return x.PropertyChanges.TotalBreakingChanges() +} + +// CompareXML will compare a left (original) and a right (new) XML instance, and check for +// any changes between them. If changes are found, the function returns a pointer to XMLChanges, +// otherwise, if nothing changed - it will return nil +func CompareXML(l, r *base.XML) *XMLChanges { + xc := new(XMLChanges) + var changes []*Change + props := make([]*PropertyCheck, 0, 6) + + props = append(props, + NewPropertyCheck(CompXML, PropName, + l.Name.ValueNode, r.Name.ValueNode, + v3.NameLabel, &changes, l, r), + NewPropertyCheck(CompXML, PropNamespace, + l.Namespace.ValueNode, r.Namespace.ValueNode, + v3.NamespaceLabel, &changes, l, r), + NewPropertyCheck(CompXML, PropPrefix, + l.Prefix.ValueNode, r.Prefix.ValueNode, + v3.PrefixLabel, &changes, l, r), + NewPropertyCheck(CompXML, PropAttribute, + l.Attribute.ValueNode, r.Attribute.ValueNode, + v3.AttributeLabel, &changes, l, r), + NewPropertyCheck(CompXML, PropNodeType, + l.NodeType.ValueNode, r.NodeType.ValueNode, + base.NodeTypeLabel, &changes, l, r), + NewPropertyCheck(CompXML, PropWrapped, + l.Wrapped.ValueNode, r.Wrapped.ValueNode, + v3.WrappedLabel, &changes, l, r), + ) + + CheckProperties(props) + + // check extensions + xc.ExtensionChanges = CheckExtensions(l, r) + xc.PropertyChanges = NewPropertyChanges(changes) + if xc.TotalChanges() <= 0 { + return nil + } + return xc +} diff --git a/vendor/github.com/pb33f/libopenapi/what-changed/what_changed.go b/vendor/github.com/pb33f/libopenapi/what-changed/what_changed.go new file mode 100644 index 000000000..d076a7ca8 --- /dev/null +++ b/vendor/github.com/pb33f/libopenapi/what-changed/what_changed.go @@ -0,0 +1,36 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +// Package what_changed +// +// what changed is a feature that performs an accurate and deep analysis of what has changed between two OpenAPI +// documents. The report generated outlines every single change made between two specifications (left and right) +// rendered in the document hierarchy, so exploring it is the same as exploring the document model. +// +// There are two main functions, one of generating a report for Swagger documents (OpenAPI 2) +// And OpenAPI 3+ documents. +// +// This package uses a combined model for OpenAPI and Swagger changes, it does not break them out into separate +// versions like the datamodel package. The reason for this is to prevent sprawl across versions and to provide +// a single API and model for any application that wants to use this feature. +package what_changed + +import ( + "github.com/pb33f/libopenapi/datamodel/low/v2" + "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/what-changed/model" +) + +// CompareOpenAPIDocuments will compare left (original) and right (updated) OpenAPI 3+ documents and extract every change +// made across the entire specification. The report outlines every property changed, everything that was added, +// or removed and which of those changes were breaking. +func CompareOpenAPIDocuments(original, updated *v3.Document) *model.DocumentChanges { + return model.CompareDocuments(original, updated) +} + +// CompareSwaggerDocuments will compare left (original) and a right (updated) Swagger documents and extract every change +// made across the entire specification. The report outlines every property changes, everything that was added, +// or removed and which of those changes were breaking. +func CompareSwaggerDocuments(original, updated *v2.Swagger) *model.DocumentChanges { + return model.CompareDocuments(original, updated) +} diff --git a/vendor/github.com/pb33f/ordered-map/v2/.gitignore b/vendor/github.com/pb33f/ordered-map/v2/.gitignore new file mode 100644 index 000000000..57872d0f1 --- /dev/null +++ b/vendor/github.com/pb33f/ordered-map/v2/.gitignore @@ -0,0 +1 @@ +/vendor/ diff --git a/vendor/github.com/pb33f/ordered-map/v2/.golangci.yml b/vendor/github.com/pb33f/ordered-map/v2/.golangci.yml new file mode 100644 index 000000000..f305c9db1 --- /dev/null +++ b/vendor/github.com/pb33f/ordered-map/v2/.golangci.yml @@ -0,0 +1,78 @@ +run: + tests: false + +linters: + disable-all: true + enable: + - asciicheck + - bidichk + - bodyclose + - containedctx + - contextcheck + - decorder + # Disabling depguard as there is no guarded list of imports + # - depguard + - dogsled + - dupl + - durationcheck + - errcheck + - errchkjson + - errname + - errorlint + - exportloopref + - forbidigo + - funlen + # Don't need gci and goimports + # - gci + - gochecknoglobals + - gochecknoinits + - gocognit + - goconst + - gocritic + - gocyclo + - godox + - gofmt + - gofumpt + - goheader + - goimports + - mnd + - gomoddirectives + - gomodguard + - goprintffuncname + - gosec + - gosimple + - govet + - grouper + - importas + - ineffassign + - lll + - maintidx + - makezero + - misspell + - nakedret + - nilerr + - nilnil + - noctx + - nolintlint + - paralleltest + - prealloc + - predeclared + - promlinter + - revive + - rowserrcheck + - sqlclosecheck + - staticcheck + - stylecheck + - tagliatelle + - tenv + - testpackage + - thelper + - tparallel + # FIXME: doesn't support 1.23 yet + # - typecheck + - unconvert + - unparam + - unused + - varnamelen + - wastedassign + - whitespace diff --git a/vendor/github.com/pb33f/ordered-map/v2/CHANGELOG.md b/vendor/github.com/pb33f/ordered-map/v2/CHANGELOG.md new file mode 100644 index 000000000..f27126f84 --- /dev/null +++ b/vendor/github.com/pb33f/ordered-map/v2/CHANGELOG.md @@ -0,0 +1,38 @@ +# Changelog + +[comment]: # (Changes since last release go here) + +## 2.1.8 - Jun 27th 2023 + +* Added support for YAML serialization/deserialization + +## 2.1.7 - Apr 13th 2023 + +* Renamed test_utils.go to utils_test.go + +## 2.1.6 - Feb 15th 2023 + +* Added `GetAndMoveToBack()` and `GetAndMoveToFront()` methods + +## 2.1.5 - Dec 13th 2022 + +* Added `Value()` method + +## 2.1.4 - Dec 12th 2022 + +* Fixed a bug with UTF-8 special characters in JSON keys + +## 2.1.3 - Dec 11th 2022 + +* Added support for JSON marshalling/unmarshalling of wrapper of primitive types + +## 2.1.2 - Dec 10th 2022 +* Allowing to pass options to `New`, to give a capacity hint, or initial data +* Allowing to deserialize nested ordered maps from JSON without having to explicitly instantiate them +* Added the `AddPairs` method + +## 2.1.1 - Dec 9th 2022 +* Fixing a bug with JSON marshalling + +## 2.1.0 - Dec 7th 2022 +* Added support for JSON serialization/deserialization diff --git a/vendor/github.com/pb33f/ordered-map/v2/LICENSE b/vendor/github.com/pb33f/ordered-map/v2/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/vendor/github.com/pb33f/ordered-map/v2/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/pb33f/ordered-map/v2/Makefile b/vendor/github.com/pb33f/ordered-map/v2/Makefile new file mode 100644 index 000000000..6e0e18a1b --- /dev/null +++ b/vendor/github.com/pb33f/ordered-map/v2/Makefile @@ -0,0 +1,32 @@ +.DEFAULT_GOAL := all + +.PHONY: all +all: test_with_fuzz lint + +# the TEST_FLAGS env var can be set to eg run only specific tests +TEST_COMMAND = go test -v -count=1 -race -cover $(TEST_FLAGS) + +.PHONY: test +test: + $(TEST_COMMAND) + +.PHONY: bench +bench: + go test -bench=. + +FUZZ_TIME ?= 10s + +# see https://github.com/golang/go/issues/46312 +# and https://stackoverflow.com/a/72673487/4867444 +# if we end up having more fuzz tests +.PHONY: test_with_fuzz +test_with_fuzz: + $(TEST_COMMAND) -fuzz=FuzzRoundTripJSON -fuzztime=$(FUZZ_TIME) + $(TEST_COMMAND) -fuzz=FuzzRoundTripYAML -fuzztime=$(FUZZ_TIME) + +.PHONY: fuzz +fuzz: test_with_fuzz + +.PHONY: lint +lint: + golangci-lint run diff --git a/vendor/github.com/pb33f/ordered-map/v2/README.md b/vendor/github.com/pb33f/ordered-map/v2/README.md new file mode 100644 index 000000000..42d7f1683 --- /dev/null +++ b/vendor/github.com/pb33f/ordered-map/v2/README.md @@ -0,0 +1,223 @@ +# Ordered Maps + +This repo was forked from [wk8/go-ordered-map](https://github.com/wk8/go-ordered-map) because of this: https://github.com/pb33f/libopenapi/issues/446 + +The [easyjson](https://github.com/mailru/easyjson) library which the wk8/go-ordered-map project depends on is now considered a **security risk** to pb33f. + +So we forked it and removed the dependency on easyjson. + +--- + +Same as regular maps, but also remembers the order in which keys were inserted, akin to [Python's `collections.OrderedDict`s](https://docs.python.org/3.7/library/collections.html#ordereddict-objects). + +It offers the following features: +* optimal runtime performance (all operations are constant time) +* optimal memory usage (only one copy of values, no unnecessary memory allocation) +* allows iterating from newest or oldest keys indifferently, without memory copy, allowing to `break` the iteration, and in time linear to the number of keys iterated over rather than the total length of the ordered map +* supports any generic types for both keys and values. If you're running go < 1.18, you can use [version 1](https://github.com/wk8/go-ordered-map/tree/v1) that takes and returns generic `interface{}`s instead of using generics +* idiomatic API, akin to that of [`container/list`](https://golang.org/pkg/container/list) +* support for JSON and YAML marshalling + +## Documentation + +[The full documentation is available on pkg.go.dev](https://pkg.go.dev/github.com/pb33f/ordered-map). + +## Installation +```bash +go get -u github.com/pb33f/ordered-map +``` + +Or use your favorite golang vendoring tool! + +## Supported go versions + +Go >= 1.23 is required to use version >= 2.2.0 of this library, as it uses generics and iterators. + +if you're running go < 1.23, you can use [version 2.1.8](https://github.com/wk8/go-ordered-map/tree/v2.1.8) instead. + +If you're running go < 1.18, you can use [version 1](https://github.com/wk8/go-ordered-map/tree/v1) instead. + +## Example / usage + +```go +package main + +import ( + "fmt" + "strings" + + "github.com/pb33f/ordered-map" +) + +func main() { + om := orderedmap.New[string, string]() + + om.Set("foo", "bar") + om.Set("bar", "baz") + om.Set("coucou", "toi") + + fmt.Println(om.Get("foo")) // => "bar", true + fmt.Println(om.Get("i dont exist")) // => "", false + + // iterating pairs from oldest to newest: + for pair := om.Oldest(); pair != nil; pair = pair.Next() { + fmt.Printf("%s => %s\n", pair.Key, pair.Value) + } // prints: + // foo => bar + // bar => baz + // coucou => toi + + // iterating over the 2 newest pairs: + i := 0 + for pair := om.Newest(); pair != nil; pair = pair.Prev() { + fmt.Printf("%s => %s\n", pair.Key, pair.Value) + i++ + if i >= 2 { + break + } + } // prints: + // coucou => toi + // bar => baz + + // removing all pairs which do not have an "o" in their key + om.Filter(func(key, value string) bool { return strings.Contains(key, "o") }) + + // new iteration syntax + for key, value := range om.FromOldest() { + fmt.Printf("%s => %s\n", key, value) + }// prints: + // foo => bar + // coucou => toi +} +``` + +An `OrderedMap`'s keys must implement `comparable`, and its values can be anything, for example: + +```go +type myStruct struct { + payload string +} + +func main() { + om := orderedmap.New[int, *myStruct]() + + om.Set(12, &myStruct{"foo"}) + om.Set(1, &myStruct{"bar"}) + + value, present := om.Get(12) + if !present { + panic("should be there!") + } + fmt.Println(value.payload) // => foo + + for pair := om.Oldest(); pair != nil; pair = pair.Next() { + fmt.Printf("%d => %s\n", pair.Key, pair.Value.payload) + } // prints: + // 12 => foo + // 1 => bar +} +``` + +Also worth noting that you can provision ordered maps with a capacity hint, as you would do by passing an optional hint to `make(map[K]V, capacity`): +```go +om := orderedmap.New[int, *myStruct](28) +``` + +You can also pass in some initial data to store in the map: +```go +om := orderedmap.New[int, string](orderedmap.WithInitialData[int, string]( + orderedmap.Pair[int, string]{ + Key: 12, + Value: "foo", + }, + orderedmap.Pair[int, string]{ + Key: 28, + Value: "bar", + }, +)) +``` + +`OrderedMap`s also support JSON serialization/deserialization, and preserves order: + +```go +// serialization +data, err := json.Marshal(om) +... + +// deserialization +om := orderedmap.New[string, string]() // or orderedmap.New[int, any](), or any type you expect +err := json.Unmarshal(data, &om) +... +``` + +Similarly, it also supports YAML serialization/deserialization using the yaml.v3 package, which also preserves order: + +```go +// serialization +data, err := yaml.Marshal(om) +... + +// deserialization +om := orderedmap.New[string, string]() // or orderedmap.New[int, any](), or any type you expect +err := yaml.Unmarshal(data, &om) +... +``` + +## Iterator support (go >= 1.23) + +The `FromOldest`, `FromNewest`, `KeysFromOldest`, `KeysFromNewest`, `ValuesFromOldest` and `ValuesFromNewest` methods return iterators over the map's pairs, starting from the oldest or newest pair, respectively. + +For example: + +```go +om := orderedmap.New[int, string]() +om.Set(1, "foo") +om.Set(2, "bar") +om.Set(3, "baz") + +for k, v := range om.FromOldest() { + fmt.Printf("%d => %s\n", k, v) +} + +// prints: +// 1 => foo +// 2 => bar +// 3 => baz + +for k := range om.KeysNewest() { + fmt.Printf("%d\n", k) +} + +// prints: +// 3 +// 2 +// 1 +``` + +`From` is a convenience function that creates a new `OrderedMap` from an iterator over key-value pairs. + +```go +om := orderedmap.New[int, string]() +om.Set(1, "foo") +om.Set(2, "bar") +om.Set(3, "baz") + +om2 := orderedmap.From(om.FromOldest()) + +for k, v := range om2.FromOldest() { + fmt.Printf("%d => %s\n", k, v) +} + +// prints: +// 1 => foo +// 2 => bar +// 3 => baz +``` + +## Alternatives + +There are several other ordered map golang implementations out there, but I believe that at the time of writing none of them offer the same functionality as this library; more specifically: +* [iancoleman/orderedmap](https://github.com/iancoleman/orderedmap) only accepts `string` keys, its `Delete` operations are linear +* [cevaris/ordered_map](https://github.com/cevaris/ordered_map) uses a channel for iterations, and leaks goroutines if the iteration is interrupted before fully traversing the map +* [mantyr/iterator](https://github.com/mantyr/iterator) also uses a channel for iterations, and its `Delete` operations are linear +* [samdolan/go-ordered-map](https://github.com/samdolan/go-ordered-map) adds unnecessary locking (users should add their own locking instead if they need it), its `Delete` and `Get` operations are linear, iterations trigger a linear memory allocation diff --git a/vendor/github.com/pb33f/ordered-map/v2/json.go b/vendor/github.com/pb33f/ordered-map/v2/json.go new file mode 100644 index 000000000..db5ea8975 --- /dev/null +++ b/vendor/github.com/pb33f/ordered-map/v2/json.go @@ -0,0 +1,243 @@ +package orderedmap + +import ( + "bytes" + "encoding" + "encoding/json" + "fmt" + "reflect" + "strconv" + "unicode/utf8" + + "github.com/buger/jsonparser" +) + +var ( + _ json.Marshaler = &OrderedMap[int, any]{} + _ json.Unmarshaler = &OrderedMap[int, any]{} +) + +// MarshalJSON implements the json.Marshaler interface. +func (om *OrderedMap[K, V]) MarshalJSON() ([]byte, error) { //nolint:funlen + if om == nil || om.list == nil { + return []byte("null"), nil + } + + buf := &bytes.Buffer{} + buf.WriteByte('{') + + for pair, firstIteration := om.Oldest(), true; pair != nil; pair = pair.Next() { + if firstIteration { + firstIteration = false + } else { + buf.WriteByte(',') + } + + // Marshal the key + switch key := any(pair.Key).(type) { + case string: + if err := writeJSONString(buf, key, om.disableHTMLEscape); err != nil { + return nil, err + } + case encoding.TextMarshaler: + buf.WriteByte('"') + text, err := key.MarshalText() + if err != nil { + return nil, err + } + buf.Write(text) + buf.WriteByte('"') + case int: + buf.WriteByte('"') + buf.WriteString(strconv.Itoa(key)) + buf.WriteByte('"') + case int8: + buf.WriteByte('"') + buf.WriteString(strconv.FormatInt(int64(key), 10)) + buf.WriteByte('"') + case int16: + buf.WriteByte('"') + buf.WriteString(strconv.FormatInt(int64(key), 10)) + buf.WriteByte('"') + case int32: + buf.WriteByte('"') + buf.WriteString(strconv.FormatInt(int64(key), 10)) + buf.WriteByte('"') + case int64: + buf.WriteByte('"') + buf.WriteString(strconv.FormatInt(key, 10)) + buf.WriteByte('"') + case uint: + buf.WriteByte('"') + buf.WriteString(strconv.FormatUint(uint64(key), 10)) + buf.WriteByte('"') + case uint8: + buf.WriteByte('"') + buf.WriteString(strconv.FormatUint(uint64(key), 10)) + buf.WriteByte('"') + case uint16: + buf.WriteByte('"') + buf.WriteString(strconv.FormatUint(uint64(key), 10)) + buf.WriteByte('"') + case uint32: + buf.WriteByte('"') + buf.WriteString(strconv.FormatUint(uint64(key), 10)) + buf.WriteByte('"') + case uint64: + buf.WriteByte('"') + buf.WriteString(strconv.FormatUint(key, 10)) + buf.WriteByte('"') + default: + // this switch takes care of wrapper types around primitive types, such as + // type myType string + switch keyValue := reflect.ValueOf(key); keyValue.Type().Kind() { + case reflect.String: + if err := writeJSONString(buf, keyValue.String(), om.disableHTMLEscape); err != nil { + return nil, err + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + buf.WriteByte('"') + buf.WriteString(strconv.FormatInt(keyValue.Int(), 10)) + buf.WriteByte('"') + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + buf.WriteByte('"') + buf.WriteString(strconv.FormatUint(keyValue.Uint(), 10)) + buf.WriteByte('"') + default: + return nil, fmt.Errorf("unsupported key type: %T", key) + } + } + + buf.WriteByte(':') + + // Marshal the value + valueBytes, err := jsonMarshal(pair.Value, om.disableHTMLEscape) + if err != nil { + return nil, err + } + buf.Write(valueBytes) + } + + buf.WriteByte('}') + return buf.Bytes(), nil +} + +// writeJSONString writes a JSON-encoded string to the buffer. +// It handles proper escaping according to JSON specification. +func writeJSONString(buf *bytes.Buffer, str string, disableHTMLEscape bool) error { + // Use json.Marshal for proper escaping + var encoded []byte + var err error + if disableHTMLEscape { + // Create a temporary buffer and encoder to handle HTML escaping + tempBuf := &bytes.Buffer{} + encoder := json.NewEncoder(tempBuf) + encoder.SetEscapeHTML(false) + err = encoder.Encode(str) + if err != nil { + return err + } + // Remove the trailing newline that Encode adds + encoded = bytes.TrimRight(tempBuf.Bytes(), "\n") + } else { + encoded, err = json.Marshal(str) + if err != nil { + return err + } + } + buf.Write(encoded) + return nil +} + +func jsonMarshal(value interface{}, disableHTMLEscape bool) ([]byte, error) { + if disableHTMLEscape { + buffer := &bytes.Buffer{} + encoder := json.NewEncoder(buffer) + encoder.SetEscapeHTML(false) + err := encoder.Encode(value) + // Encode() adds an extra newline, strip it off to guarantee same behavior as json.Marshal + return bytes.TrimRight(buffer.Bytes(), "\n"), err + } + return json.Marshal(value) +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (om *OrderedMap[K, V]) UnmarshalJSON(data []byte) error { + if om.list == nil { + om.initialize(0, om.disableHTMLEscape) + } + + return jsonparser.ObjectEach( + data, + func(keyData []byte, valueData []byte, dataType jsonparser.ValueType, offset int) error { + if dataType == jsonparser.String { + // jsonparser removes the enclosing quotes; we need to restore them to make a valid JSON + valueData = data[offset-len(valueData)-2 : offset] + } + + var key K + var value V + + switch typedKey := any(&key).(type) { + case *string: + s, err := decodeUTF8(keyData) + if err != nil { + return err + } + *typedKey = s + case encoding.TextUnmarshaler: + if err := typedKey.UnmarshalText(keyData); err != nil { + return err + } + case *int, *int8, *int16, *int32, *int64, *uint, *uint8, *uint16, *uint32, *uint64: + if err := json.Unmarshal(keyData, typedKey); err != nil { + return err + } + default: + // this switch takes care of wrapper types around primitive types, such as + // type myType string + switch reflect.TypeOf(key).Kind() { + case reflect.String: + s, err := decodeUTF8(keyData) + if err != nil { + return err + } + + convertedKeyData := reflect.ValueOf(s).Convert(reflect.TypeOf(key)) + reflect.ValueOf(&key).Elem().Set(convertedKeyData) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if err := json.Unmarshal(keyData, &key); err != nil { + return err + } + default: + return fmt.Errorf("unsupported key type: %T", key) + } + } + + if err := json.Unmarshal(valueData, &value); err != nil { + return err + } + + om.Set(key, value) + return nil + }) +} + +func decodeUTF8(input []byte) (string, error) { + remaining, offset := input, 0 + runes := make([]rune, 0, len(remaining)) + + for len(remaining) > 0 { + r, size := utf8.DecodeRune(remaining) + if r == utf8.RuneError && size <= 1 { + return "", fmt.Errorf("not a valid UTF-8 string (at position %d): %s", offset, string(input)) + } + + runes = append(runes, r) + remaining = remaining[size:] + offset += size + } + + return string(runes), nil +} diff --git a/vendor/github.com/pb33f/ordered-map/v2/orderedmap.go b/vendor/github.com/pb33f/ordered-map/v2/orderedmap.go new file mode 100644 index 000000000..be56f7471 --- /dev/null +++ b/vendor/github.com/pb33f/ordered-map/v2/orderedmap.go @@ -0,0 +1,398 @@ +// Package orderedmap implements an ordered map, i.e. a map that also keeps track of +// the order in which keys were inserted. +// +// All operations are constant-time. +// +// Github repo: https://github.com/wk8/go-ordered-map +package orderedmap + +import ( + "fmt" + "iter" + + list "github.com/bahlo/generic-list-go" +) + +type Pair[K comparable, V any] struct { + Key K + Value V + + element *list.Element[*Pair[K, V]] +} + +type OrderedMap[K comparable, V any] struct { + pairs map[K]*Pair[K, V] + list *list.List[*Pair[K, V]] + disableHTMLEscape bool +} + +type initConfig[K comparable, V any] struct { + capacity int + initialData []Pair[K, V] + disableHTMLEscape bool +} + +type InitOption[K comparable, V any] func(config *initConfig[K, V]) + +// WithCapacity allows giving a capacity hint for the map, akin to the standard make(map[K]V, capacity). +func WithCapacity[K comparable, V any](capacity int) InitOption[K, V] { + return func(c *initConfig[K, V]) { + c.capacity = capacity + } +} + +// WithInitialData allows passing in initial data for the map. +func WithInitialData[K comparable, V any](initialData ...Pair[K, V]) InitOption[K, V] { + return func(c *initConfig[K, V]) { + c.initialData = initialData + if c.capacity < len(initialData) { + c.capacity = len(initialData) + } + } +} + +// WithDisableHTMLEscape disables HTMl escaping when marshalling to JSON +func WithDisableHTMLEscape[K comparable, V any]() InitOption[K, V] { + return func(c *initConfig[K, V]) { + c.disableHTMLEscape = true + } +} + +// New creates a new OrderedMap. +// options can either be one or several InitOption[K, V], or a single integer, +// which is then interpreted as a capacity hint, à la make(map[K]V, capacity). +func New[K comparable, V any](options ...any) *OrderedMap[K, V] { + orderedMap := &OrderedMap[K, V]{} + + var config initConfig[K, V] + for _, untypedOption := range options { + switch option := untypedOption.(type) { + case int: + if len(options) != 1 { + invalidOption() + } + config.capacity = option + case bool: + if len(options) != 1 { + invalidOption() + } + config.disableHTMLEscape = option + + case InitOption[K, V]: + option(&config) + + default: + invalidOption() + } + } + + orderedMap.initialize(config.capacity, config.disableHTMLEscape) + orderedMap.AddPairs(config.initialData...) + + return orderedMap +} + +const invalidOptionMessage = `when using orderedmap.New[K,V]() with options, either provide one or several InitOption[K, V]; or a single integer which is then interpreted as a capacity hint, à la make(map[K]V, capacity).` //nolint:lll + +func invalidOption() { panic(invalidOptionMessage) } + +func (om *OrderedMap[K, V]) initialize(capacity int, disableHTMLEscape bool) { + om.pairs = make(map[K]*Pair[K, V], capacity) + om.list = list.New[*Pair[K, V]]() + om.disableHTMLEscape = disableHTMLEscape +} + +// Get looks for the given key, and returns the value associated with it, +// or V's nil value if not found. The boolean it returns says whether the key is present in the map. +func (om *OrderedMap[K, V]) Get(key K) (val V, present bool) { + if pair, present := om.pairs[key]; present { + return pair.Value, true + } + + return +} + +// Load is an alias for Get, mostly to present an API similar to `sync.Map`'s. +func (om *OrderedMap[K, V]) Load(key K) (V, bool) { + return om.Get(key) +} + +// Value returns the value associated with the given key or the zero value. +func (om *OrderedMap[K, V]) Value(key K) (val V) { + if pair, present := om.pairs[key]; present { + val = pair.Value + } + return +} + +// GetPair looks for the given key, and returns the pair associated with it, +// or nil if not found. The Pair struct can then be used to iterate over the ordered map +// from that point, either forward or backward. +func (om *OrderedMap[K, V]) GetPair(key K) *Pair[K, V] { + return om.pairs[key] +} + +// Set sets the key-value pair, and returns what `Get` would have returned +// on that key prior to the call to `Set`. +func (om *OrderedMap[K, V]) Set(key K, value V) (val V, present bool) { + if pair, present := om.pairs[key]; present { + oldValue := pair.Value + pair.Value = value + return oldValue, true + } + + pair := &Pair[K, V]{ + Key: key, + Value: value, + } + pair.element = om.list.PushBack(pair) + om.pairs[key] = pair + + return +} + +// AddPairs allows setting multiple pairs at a time. It's equivalent to calling +// Set on each pair sequentially. +func (om *OrderedMap[K, V]) AddPairs(pairs ...Pair[K, V]) { + for _, pair := range pairs { + om.Set(pair.Key, pair.Value) + } +} + +// Store is an alias for Set, mostly to present an API similar to `sync.Map`'s. +func (om *OrderedMap[K, V]) Store(key K, value V) (V, bool) { + return om.Set(key, value) +} + +// Delete removes the key-value pair, and returns what `Get` would have returned +// on that key prior to the call to `Delete`. +func (om *OrderedMap[K, V]) Delete(key K) (val V, present bool) { + if pair, present := om.pairs[key]; present { + om.list.Remove(pair.element) + delete(om.pairs, key) + return pair.Value, true + } + return +} + +// Len returns the length of the ordered map. +func (om *OrderedMap[K, V]) Len() int { + if om == nil || om.pairs == nil { + return 0 + } + return len(om.pairs) +} + +// Oldest returns a pointer to the oldest pair. It's meant to be used to iterate on the ordered map's +// pairs from the oldest to the newest, e.g.: +// for pair := orderedMap.Oldest(); pair != nil; pair = pair.Next() { fmt.Printf("%v => %v\n", pair.Key, pair.Value) } +func (om *OrderedMap[K, V]) Oldest() *Pair[K, V] { + if om == nil || om.list == nil { + return nil + } + return listElementToPair(om.list.Front()) +} + +// Newest returns a pointer to the newest pair. It's meant to be used to iterate on the ordered map's +// pairs from the newest to the oldest, e.g.: +// for pair := orderedMap.Newest(); pair != nil; pair = pair.Prev() { fmt.Printf("%v => %v\n", pair.Key, pair.Value) } +func (om *OrderedMap[K, V]) Newest() *Pair[K, V] { + if om == nil || om.list == nil { + return nil + } + return listElementToPair(om.list.Back()) +} + +// Next returns a pointer to the next pair. +func (p *Pair[K, V]) Next() *Pair[K, V] { + return listElementToPair(p.element.Next()) +} + +// Prev returns a pointer to the previous pair. +func (p *Pair[K, V]) Prev() *Pair[K, V] { + return listElementToPair(p.element.Prev()) +} + +func listElementToPair[K comparable, V any](element *list.Element[*Pair[K, V]]) *Pair[K, V] { + if element == nil { + return nil + } + return element.Value +} + +// KeyNotFoundError may be returned by functions in this package when they're called with keys that are not present +// in the map. +type KeyNotFoundError[K comparable] struct { + MissingKey K +} + +func (e *KeyNotFoundError[K]) Error() string { + return fmt.Sprintf("missing key: %v", e.MissingKey) +} + +// MoveAfter moves the value associated with key to its new position after the one associated with markKey. +// Returns an error iff key or markKey are not present in the map. If an error is returned, +// it will be a KeyNotFoundError. +func (om *OrderedMap[K, V]) MoveAfter(key, markKey K) error { + elements, err := om.getElements(key, markKey) + if err != nil { + return err + } + om.list.MoveAfter(elements[0], elements[1]) + return nil +} + +// MoveBefore moves the value associated with key to its new position before the one associated with markKey. +// Returns an error iff key or markKey are not present in the map. If an error is returned, +// it will be a KeyNotFoundError. +func (om *OrderedMap[K, V]) MoveBefore(key, markKey K) error { + elements, err := om.getElements(key, markKey) + if err != nil { + return err + } + om.list.MoveBefore(elements[0], elements[1]) + return nil +} + +func (om *OrderedMap[K, V]) getElements(keys ...K) ([]*list.Element[*Pair[K, V]], error) { + elements := make([]*list.Element[*Pair[K, V]], len(keys)) + for i, k := range keys { + pair, present := om.pairs[k] + if !present { + return nil, &KeyNotFoundError[K]{k} + } + elements[i] = pair.element + } + return elements, nil +} + +// MoveToBack moves the value associated with key to the back of the ordered map, +// i.e. makes it the newest pair in the map. +// Returns an error iff key is not present in the map. If an error is returned, +// it will be a KeyNotFoundError. +func (om *OrderedMap[K, V]) MoveToBack(key K) error { + _, err := om.GetAndMoveToBack(key) + return err +} + +// MoveToFront moves the value associated with key to the front of the ordered map, +// i.e. makes it the oldest pair in the map. +// Returns an error iff key is not present in the map. If an error is returned, +// it will be a KeyNotFoundError. +func (om *OrderedMap[K, V]) MoveToFront(key K) error { + _, err := om.GetAndMoveToFront(key) + return err +} + +// GetAndMoveToBack combines Get and MoveToBack in the same call. If an error is returned, +// it will be a KeyNotFoundError. +func (om *OrderedMap[K, V]) GetAndMoveToBack(key K) (val V, err error) { + if pair, present := om.pairs[key]; present { + val = pair.Value + om.list.MoveToBack(pair.element) + } else { + err = &KeyNotFoundError[K]{key} + } + + return +} + +// GetAndMoveToFront combines Get and MoveToFront in the same call. If an error is returned, +// it will be a KeyNotFoundError. +func (om *OrderedMap[K, V]) GetAndMoveToFront(key K) (val V, err error) { + if pair, present := om.pairs[key]; present { + val = pair.Value + om.list.MoveToFront(pair.element) + } else { + err = &KeyNotFoundError[K]{key} + } + + return +} + +// FromOldest returns an iterator over all the key-value pairs in the map, starting from the oldest pair. +func (om *OrderedMap[K, V]) FromOldest() iter.Seq2[K, V] { + return func(yield func(K, V) bool) { + for pair := om.Oldest(); pair != nil; pair = pair.Next() { + if !yield(pair.Key, pair.Value) { + return + } + } + } +} + +// FromNewest returns an iterator over all the key-value pairs in the map, starting from the newest pair. +func (om *OrderedMap[K, V]) FromNewest() iter.Seq2[K, V] { + return func(yield func(K, V) bool) { + for pair := om.Newest(); pair != nil; pair = pair.Prev() { + if !yield(pair.Key, pair.Value) { + return + } + } + } +} + +// KeysFromOldest returns an iterator over all the keys in the map, starting from the oldest pair. +func (om *OrderedMap[K, V]) KeysFromOldest() iter.Seq[K] { + return func(yield func(K) bool) { + for pair := om.Oldest(); pair != nil; pair = pair.Next() { + if !yield(pair.Key) { + return + } + } + } +} + +// KeysFromNewest returns an iterator over all the keys in the map, starting from the newest pair. +func (om *OrderedMap[K, V]) KeysFromNewest() iter.Seq[K] { + return func(yield func(K) bool) { + for pair := om.Newest(); pair != nil; pair = pair.Prev() { + if !yield(pair.Key) { + return + } + } + } +} + +// ValuesFromOldest returns an iterator over all the values in the map, starting from the oldest pair. +func (om *OrderedMap[K, V]) ValuesFromOldest() iter.Seq[V] { + return func(yield func(V) bool) { + for pair := om.Oldest(); pair != nil; pair = pair.Next() { + if !yield(pair.Value) { + return + } + } + } +} + +// ValuesFromNewest returns an iterator over all the values in the map, starting from the newest pair. +func (om *OrderedMap[K, V]) ValuesFromNewest() iter.Seq[V] { + return func(yield func(V) bool) { + for pair := om.Newest(); pair != nil; pair = pair.Prev() { + if !yield(pair.Value) { + return + } + } + } +} + +// From creates a new OrderedMap from an iterator over key-value pairs. +func From[K comparable, V any](i iter.Seq2[K, V]) *OrderedMap[K, V] { + oMap := New[K, V]() + + for k, v := range i { + oMap.Set(k, v) + } + + return oMap +} + +func (om *OrderedMap[K, V]) Filter(predicate func(K, V) bool) { + for pair := om.Oldest(); pair != nil; { + key, value := pair.Key, pair.Value + pair = pair.Next() + if !predicate(key, value) { + om.Delete(key) + } + } +} diff --git a/vendor/github.com/pb33f/ordered-map/v2/yaml.go b/vendor/github.com/pb33f/ordered-map/v2/yaml.go new file mode 100644 index 000000000..834490bb3 --- /dev/null +++ b/vendor/github.com/pb33f/ordered-map/v2/yaml.go @@ -0,0 +1,71 @@ +package orderedmap + +import ( + "fmt" + + "go.yaml.in/yaml/v4" +) + +var ( + _ yaml.Marshaler = &OrderedMap[int, any]{} + _ yaml.Unmarshaler = &OrderedMap[int, any]{} +) + +// MarshalYAML implements the yaml.Marshaler interface. +func (om *OrderedMap[K, V]) MarshalYAML() (interface{}, error) { + if om == nil { + return []byte("null"), nil + } + + node := yaml.Node{ + Kind: yaml.MappingNode, + } + + for pair := om.Oldest(); pair != nil; pair = pair.Next() { + key, value := pair.Key, pair.Value + + keyNode := &yaml.Node{} + + // serialize key to yaml, then deserialize it back into the node + // this is a hack to get the correct tag for the key + if err := keyNode.Encode(key); err != nil { + return nil, err + } + + valueNode := &yaml.Node{} + if err := valueNode.Encode(value); err != nil { + return nil, err + } + + node.Content = append(node.Content, keyNode, valueNode) + } + + return &node, nil +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface. +func (om *OrderedMap[K, V]) UnmarshalYAML(value *yaml.Node) error { + if value.Kind != yaml.MappingNode { + return fmt.Errorf("pipeline must contain YAML mapping, has %v", value.Kind) + } + + if om.list == nil { + om.initialize(0, om.disableHTMLEscape) + } + + for index := 0; index < len(value.Content); index += 2 { + var key K + var val V + + if err := value.Content[index].Decode(&key); err != nil { + return err + } + if err := value.Content[index+1].Decode(&val); err != nil { + return err + } + + om.Set(key, val) + } + + return nil +} diff --git a/vendor/github.com/prometheus/prometheus/config/config.go b/vendor/github.com/prometheus/prometheus/config/config.go index 30c8a8ed2..d721d7fb8 100644 --- a/vendor/github.com/prometheus/prometheus/config/config.go +++ b/vendor/github.com/prometheus/prometheus/config/config.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -149,6 +149,10 @@ func LoadFile(filename string, agentMode bool, logger *slog.Logger) (*Config, er return cfg, nil } +func boolPtr(b bool) *bool { + return &b +} + // The defaults applied before parsing the respective config sections. var ( // DefaultConfig is the default top-level configuration. @@ -158,7 +162,6 @@ var ( OTLPConfig: DefaultOTLPConfig, } - f bool // DefaultGlobalConfig is the default global configuration. DefaultGlobalConfig = GlobalConfig{ ScrapeInterval: model.Duration(1 * time.Minute), @@ -173,9 +176,10 @@ var ( ScrapeProtocols: nil, // When the native histogram feature flag is enabled, // ScrapeNativeHistograms default changes to true. - ScrapeNativeHistograms: &f, + ScrapeNativeHistograms: boolPtr(false), ConvertClassicHistogramsToNHCB: false, AlwaysScrapeClassicHistograms: false, + ExtraScrapeMetrics: boolPtr(false), MetricNameValidationScheme: model.UTF8Validation, MetricNameEscapingScheme: model.AllowUTF8, } @@ -513,6 +517,10 @@ type GlobalConfig struct { ConvertClassicHistogramsToNHCB bool `yaml:"convert_classic_histograms_to_nhcb,omitempty"` // Whether to scrape a classic histogram, even if it is also exposed as a native histogram. AlwaysScrapeClassicHistograms bool `yaml:"always_scrape_classic_histograms,omitempty"` + // Whether to enable additional scrape metrics. + // When enabled, Prometheus stores samples for scrape_timeout_seconds, + // scrape_sample_limit, and scrape_body_size_bytes. + ExtraScrapeMetrics *bool `yaml:"extra_scrape_metrics,omitempty"` } // ScrapeProtocol represents supported protocol for scraping metrics. @@ -652,6 +660,9 @@ func (c *GlobalConfig) UnmarshalYAML(unmarshal func(any) error) error { if gc.ScrapeNativeHistograms == nil { gc.ScrapeNativeHistograms = DefaultGlobalConfig.ScrapeNativeHistograms } + if gc.ExtraScrapeMetrics == nil { + gc.ExtraScrapeMetrics = DefaultGlobalConfig.ExtraScrapeMetrics + } if gc.ScrapeProtocols == nil { if DefaultGlobalConfig.ScrapeProtocols != nil { // This is the case where the defaults are set due to a feature flag. @@ -687,7 +698,17 @@ func (c *GlobalConfig) isZero() bool { c.ScrapeProtocols == nil && c.ScrapeNativeHistograms == nil && !c.ConvertClassicHistogramsToNHCB && - !c.AlwaysScrapeClassicHistograms + !c.AlwaysScrapeClassicHistograms && + c.BodySizeLimit == 0 && + c.SampleLimit == 0 && + c.TargetLimit == 0 && + c.LabelLimit == 0 && + c.LabelNameLengthLimit == 0 && + c.LabelValueLengthLimit == 0 && + c.KeepDroppedTargets == 0 && + c.MetricNameValidationScheme == model.UnsetValidation && + c.MetricNameEscapingScheme == "" && + c.ExtraScrapeMetrics == nil } const DefaultGoGCPercentage = 75 @@ -796,6 +817,11 @@ type ScrapeConfig struct { // blank in config files but must have a value if a ScrapeConfig is created // programmatically. MetricNameEscapingScheme string `yaml:"metric_name_escaping_scheme,omitempty"` + // Whether to enable additional scrape metrics. + // When enabled, Prometheus stores samples for scrape_timeout_seconds, + // scrape_sample_limit, and scrape_body_size_bytes. + // If not set (nil), inherits the value from the global configuration. + ExtraScrapeMetrics *bool `yaml:"extra_scrape_metrics,omitempty"` // We cannot do proper Go type embedding below as the parser will then parse // values arbitrarily into the overflow maps of further-down types. @@ -897,6 +923,9 @@ func (c *ScrapeConfig) Validate(globalConfig GlobalConfig) error { if c.ScrapeNativeHistograms == nil { c.ScrapeNativeHistograms = globalConfig.ScrapeNativeHistograms } + if c.ExtraScrapeMetrics == nil { + c.ExtraScrapeMetrics = globalConfig.ExtraScrapeMetrics + } if c.ScrapeProtocols == nil { switch { @@ -1022,7 +1051,7 @@ func ToEscapingScheme(s string, v model.ValidationScheme) (model.EscapingScheme, case model.LegacyValidation: return model.UnderscoreEscaping, nil case model.UnsetValidation: - return model.NoEscaping, fmt.Errorf("v is unset: %s", v) + return model.NoEscaping, fmt.Errorf("ValidationScheme is unset: %s", v) default: panic(fmt.Errorf("unhandled validation scheme: %s", v)) } @@ -1045,6 +1074,11 @@ func (c *ScrapeConfig) AlwaysScrapeClassicHistogramsEnabled() bool { return c.AlwaysScrapeClassicHistograms != nil && *c.AlwaysScrapeClassicHistograms } +// ExtraScrapeMetricsEnabled returns whether to enable extra scrape metrics. +func (c *ScrapeConfig) ExtraScrapeMetricsEnabled() bool { + return c.ExtraScrapeMetrics != nil && *c.ExtraScrapeMetrics +} + // StorageConfig configures runtime reloadable configuration options. type StorageConfig struct { TSDBConfig *TSDBConfig `yaml:"tsdb,omitempty"` @@ -1073,6 +1107,10 @@ type TSDBConfig struct { // This should not be used directly and must be converted into OutOfOrderTimeWindow. OutOfOrderTimeWindowFlag model.Duration `yaml:"out_of_order_time_window,omitempty"` + // StaleSeriesCompactionThreshold is a number between 0.0-1.0 indicating the % of stale series in + // the in-memory Head block. If the % of stale series crosses this threshold, stale series compaction is run immediately. + StaleSeriesCompactionThreshold float64 `yaml:"stale_series_compaction_threshold,omitempty"` + Retention *TSDBRetentionConfig `yaml:"retention,omitempty"` } diff --git a/vendor/github.com/prometheus/prometheus/config/reload.go b/vendor/github.com/prometheus/prometheus/config/reload.go index 07a077a6a..a25069316 100644 --- a/vendor/github.com/prometheus/prometheus/config/reload.go +++ b/vendor/github.com/prometheus/prometheus/config/reload.go @@ -1,4 +1,4 @@ -// Copyright 2024 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/README.md b/vendor/github.com/prometheus/prometheus/discovery/README.md index d5418e7fb..5d1adcf14 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/README.md +++ b/vendor/github.com/prometheus/prometheus/discovery/README.md @@ -50,7 +50,7 @@ file for use with `file_sd`. The general principle with SD is to extract all the potentially useful information we can out of the SD, and let the user choose what they need of it using -[relabelling](https://prometheus.io/docs/operating/configuration/#). +[relabelling](https://prometheus.io/docs/operating/configuration/#relabel_config). This information is generally termed metadata. Metadata is exposed as a set of key/value pairs (labels) per target. The keys diff --git a/vendor/github.com/prometheus/prometheus/discovery/aws/aws.go b/vendor/github.com/prometheus/prometheus/discovery/aws/aws.go index 1ac97b3c9..69b3b41c0 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/aws/aws.go +++ b/vendor/github.com/prometheus/prometheus/discovery/aws/aws.go @@ -14,10 +14,13 @@ package aws import ( + "context" "errors" "fmt" "time" + awsConfig "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/feature/ec2/imds" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/config" "github.com/prometheus/common/model" @@ -43,6 +46,7 @@ const ( RoleEC2 Role = "ec2" RoleECS Role = "ecs" RoleLightsail Role = "lightsail" + RoleMSK Role = "msk" ) // UnmarshalYAML implements the yaml.Unmarshaler interface. @@ -51,7 +55,7 @@ func (c *Role) UnmarshalYAML(unmarshal func(any) error) error { return err } switch *c { - case RoleEC2, RoleECS, RoleLightsail: + case RoleEC2, RoleECS, RoleLightsail, RoleMSK: return nil default: return fmt.Errorf("unknown AWS SD role %q", *c) @@ -78,13 +82,14 @@ type SDConfig struct { // ec2 specific Filters []*EC2Filter `yaml:"filters,omitempty"` - // ecs specific + // ecs, msk specific Clusters []string `yaml:"clusters,omitempty"` // Embedded sub-configs (internal use only, not serialized) *EC2SDConfig `yaml:"-"` *ECSSDConfig `yaml:"-"` *LightsailSDConfig `yaml:"-"` + *MSKSDConfig `yaml:"-"` } // UnmarshalYAML implements the yaml.Unmarshaler interface for SDConfig. @@ -98,15 +103,20 @@ func (c *SDConfig) UnmarshalYAML(unmarshal func(any) error) error { } *c = SDConfig(aux) + var err error + c.Region, err = loadRegion(context.Background(), c.Region) + if err != nil { + return fmt.Errorf("could not determine AWS region: %w", err) + } + switch c.Role { case RoleEC2: if c.EC2SDConfig == nil { - c.EC2SDConfig = &DefaultEC2SDConfig + ec2Config := DefaultEC2SDConfig + c.EC2SDConfig = &ec2Config } c.EC2SDConfig.HTTPClientConfig = c.HTTPClientConfig - if c.Region != "" { - c.EC2SDConfig.Region = c.Region - } + c.EC2SDConfig.Region = c.Region if c.Endpoint != "" { c.EC2SDConfig.Endpoint = c.Endpoint } @@ -133,12 +143,11 @@ func (c *SDConfig) UnmarshalYAML(unmarshal func(any) error) error { } case RoleECS: if c.ECSSDConfig == nil { - c.ECSSDConfig = &DefaultECSSDConfig + ecsConfig := DefaultECSSDConfig + c.ECSSDConfig = &ecsConfig } c.ECSSDConfig.HTTPClientConfig = c.HTTPClientConfig - if c.Region != "" { - c.ECSSDConfig.Region = c.Region - } + c.ECSSDConfig.Region = c.Region if c.Endpoint != "" { c.ECSSDConfig.Endpoint = c.Endpoint } @@ -165,12 +174,11 @@ func (c *SDConfig) UnmarshalYAML(unmarshal func(any) error) error { } case RoleLightsail: if c.LightsailSDConfig == nil { - c.LightsailSDConfig = &DefaultLightsailSDConfig + lightsailConfig := DefaultLightsailSDConfig + c.LightsailSDConfig = &lightsailConfig } c.LightsailSDConfig.HTTPClientConfig = c.HTTPClientConfig - if c.Region != "" { - c.LightsailSDConfig.Region = c.Region - } + c.LightsailSDConfig.Region = c.Region if c.Endpoint != "" { c.LightsailSDConfig.Endpoint = c.Endpoint } @@ -192,6 +200,37 @@ func (c *SDConfig) UnmarshalYAML(unmarshal func(any) error) error { if c.RefreshInterval != 0 { c.LightsailSDConfig.RefreshInterval = c.RefreshInterval } + case RoleMSK: + if c.MSKSDConfig == nil { + mskConfig := DefaultMSKSDConfig + c.MSKSDConfig = &mskConfig + } + c.MSKSDConfig.HTTPClientConfig = c.HTTPClientConfig + c.MSKSDConfig.Region = c.Region + if c.Endpoint != "" { + c.MSKSDConfig.Endpoint = c.Endpoint + } + if c.AccessKey != "" { + c.MSKSDConfig.AccessKey = c.AccessKey + } + if c.SecretKey != "" { + c.MSKSDConfig.SecretKey = c.SecretKey + } + if c.Profile != "" { + c.MSKSDConfig.Profile = c.Profile + } + if c.RoleARN != "" { + c.MSKSDConfig.RoleARN = c.RoleARN + } + if c.Port != 0 { + c.MSKSDConfig.Port = c.Port + } + if c.RefreshInterval != 0 { + c.MSKSDConfig.RefreshInterval = c.RefreshInterval + } + if c.Clusters != nil { + c.MSKSDConfig.Clusters = c.Clusters + } default: return fmt.Errorf("unknown AWS SD role %q", c.Role) } @@ -223,7 +262,39 @@ func (c *SDConfig) NewDiscoverer(opts discovery.DiscovererOptions) (discovery.Di case RoleLightsail: opts.Metrics = &lightsailMetrics{refreshMetrics: awsMetrics.refreshMetrics} return NewLightsailDiscovery(c.LightsailSDConfig, opts) + case RoleMSK: + opts.Metrics = &mskMetrics{refreshMetrics: awsMetrics.refreshMetrics} + return NewMSKDiscovery(c.MSKSDConfig, opts) default: return nil, fmt.Errorf("unknown AWS SD role %q", c.Role) } } + +// loadRegion finds the region in order: AWS config/env vars ->IMDS. +func loadRegion(ctx context.Context, specifiedRegion string) (string, error) { + if specifiedRegion != "" { + return specifiedRegion, nil + } + + cfg, err := awsConfig.LoadDefaultConfig(ctx) + if err != nil { + return "", fmt.Errorf("failed to load AWS config: %w", err) + } + + if cfg.Region != "" { + return cfg.Region, nil + } + + // Fallback (may fail in non-AWS environments) + imdsClient := imds.NewFromConfig(cfg) + region, err := imdsClient.GetRegion(ctx, &imds.GetRegionInput{}) + if err != nil { + return "", fmt.Errorf("failed to get region from IMDS: %w", err) + } + + if region.Region == "" { + return "", errors.New("region not found in AWS config or IMDS") + } + + return region.Region, nil +} diff --git a/vendor/github.com/prometheus/prometheus/discovery/aws/ec2.go b/vendor/github.com/prometheus/prometheus/discovery/aws/ec2.go index 0aae35d75..4daff43ec 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/aws/ec2.go +++ b/vendor/github.com/prometheus/prometheus/discovery/aws/ec2.go @@ -1,4 +1,4 @@ -// Copyright 2021 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -27,7 +27,6 @@ import ( awsConfig "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/credentials/stscreds" - "github.com/aws/aws-sdk-go-v2/feature/ec2/imds" "github.com/aws/aws-sdk-go-v2/service/ec2" ec2Types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/aws/aws-sdk-go-v2/service/sts" @@ -125,31 +124,10 @@ func (c *EC2SDConfig) UnmarshalYAML(unmarshal func(any) error) error { return err } - if c.Region == "" { - cfg, err := awsConfig.LoadDefaultConfig(context.Background()) - if err != nil { - return err - } - - if cfg.Region != "" { - // If the region is already set in the config, use it. - // This can happen if the user has set the region in the AWS config file or environment variables. - c.Region = cfg.Region - } - - if c.Region == "" { - // Try to get the region from the instance metadata service (IMDS). - imdsClient := imds.NewFromConfig(cfg) - region, err := imdsClient.GetRegion(context.Background(), &imds.GetRegionInput{}) - if err != nil { - return err - } - c.Region = region.Region - } - } - - if c.Region == "" { - return errors.New("EC2 SD configuration requires a region") + // Check if the region is set, if not attempt to load it from the AWS SDK. + c.Region, err = loadRegion(context.Background(), c.Region) + if err != nil { + return fmt.Errorf("could not determine AWS region: %w", err) } for _, f := range c.Filters { diff --git a/vendor/github.com/prometheus/prometheus/discovery/aws/ecs.go b/vendor/github.com/prometheus/prometheus/discovery/aws/ecs.go index d6b36a798..18d2746cb 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/aws/ecs.go +++ b/vendor/github.com/prometheus/prometheus/discovery/aws/ecs.go @@ -19,7 +19,9 @@ import ( "fmt" "log/slog" "net" + "slices" "strconv" + "strings" "sync" "time" @@ -27,7 +29,7 @@ import ( awsConfig "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/credentials/stscreds" - "github.com/aws/aws-sdk-go-v2/feature/ec2/imds" + "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/aws/aws-sdk-go-v2/service/ecs" "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/aws/aws-sdk-go-v2/service/sts" @@ -44,31 +46,37 @@ import ( ) const ( - ecsLabel = model.MetaLabelPrefix + "ecs_" - ecsLabelCluster = ecsLabel + "cluster" - ecsLabelClusterARN = ecsLabel + "cluster_arn" - ecsLabelService = ecsLabel + "service" - ecsLabelServiceARN = ecsLabel + "service_arn" - ecsLabelServiceStatus = ecsLabel + "service_status" - ecsLabelTaskGroup = ecsLabel + "task_group" - ecsLabelTaskARN = ecsLabel + "task_arn" - ecsLabelTaskDefinition = ecsLabel + "task_definition" - ecsLabelRegion = ecsLabel + "region" - ecsLabelAvailabilityZone = ecsLabel + "availability_zone" - ecsLabelAZID = ecsLabel + "availability_zone_id" - ecsLabelSubnetID = ecsLabel + "subnet_id" - ecsLabelIPAddress = ecsLabel + "ip_address" - ecsLabelLaunchType = ecsLabel + "launch_type" - ecsLabelDesiredStatus = ecsLabel + "desired_status" - ecsLabelLastStatus = ecsLabel + "last_status" - ecsLabelHealthStatus = ecsLabel + "health_status" - ecsLabelPlatformFamily = ecsLabel + "platform_family" - ecsLabelPlatformVersion = ecsLabel + "platform_version" - ecsLabelTag = ecsLabel + "tag_" - ecsLabelTagCluster = ecsLabelTag + "cluster_" - ecsLabelTagService = ecsLabelTag + "service_" - ecsLabelTagTask = ecsLabelTag + "task_" - ecsLabelSeparator = "," + ecsLabel = model.MetaLabelPrefix + "ecs_" + ecsLabelCluster = ecsLabel + "cluster" + ecsLabelClusterARN = ecsLabel + "cluster_arn" + ecsLabelService = ecsLabel + "service" + ecsLabelServiceARN = ecsLabel + "service_arn" + ecsLabelServiceStatus = ecsLabel + "service_status" + ecsLabelTaskGroup = ecsLabel + "task_group" + ecsLabelTaskARN = ecsLabel + "task_arn" + ecsLabelTaskDefinition = ecsLabel + "task_definition" + ecsLabelRegion = ecsLabel + "region" + ecsLabelAvailabilityZone = ecsLabel + "availability_zone" + ecsLabelSubnetID = ecsLabel + "subnet_id" + ecsLabelIPAddress = ecsLabel + "ip_address" + ecsLabelLaunchType = ecsLabel + "launch_type" + ecsLabelDesiredStatus = ecsLabel + "desired_status" + ecsLabelLastStatus = ecsLabel + "last_status" + ecsLabelHealthStatus = ecsLabel + "health_status" + ecsLabelPlatformFamily = ecsLabel + "platform_family" + ecsLabelPlatformVersion = ecsLabel + "platform_version" + ecsLabelTag = ecsLabel + "tag_" + ecsLabelTagCluster = ecsLabelTag + "cluster_" + ecsLabelTagService = ecsLabelTag + "service_" + ecsLabelTagTask = ecsLabelTag + "task_" + ecsLabelTagEC2 = ecsLabelTag + "ec2_" + ecsLabelNetworkMode = ecsLabel + "network_mode" + ecsLabelContainerInstanceARN = ecsLabel + "container_instance_arn" + ecsLabelEC2InstanceID = ecsLabel + "ec2_instance_id" + ecsLabelEC2InstanceType = ecsLabel + "ec2_instance_type" + ecsLabelEC2InstancePrivateIP = ecsLabel + "ec2_instance_private_ip" + ecsLabelEC2InstancePublicIP = ecsLabel + "ec2_instance_public_ip" + ecsLabelPublicIP = ecsLabel + "public_ip" ) // DefaultECSSDConfig is the default ECS SD configuration. @@ -130,17 +138,9 @@ func (c *ECSSDConfig) UnmarshalYAML(unmarshal func(any) error) error { return err } - if c.Region == "" { - cfg, err := awsConfig.LoadDefaultConfig(context.TODO()) - if err != nil { - return err - } - client := imds.NewFromConfig(cfg) - result, err := client.GetRegion(context.Background(), &imds.GetRegionInput{}) - if err != nil { - return fmt.Errorf("ECS SD configuration requires a region. Tried to fetch it from the instance metadata: %w", err) - } - c.Region = result.Region + c.Region, err = loadRegion(context.Background(), c.Region) + if err != nil { + return fmt.Errorf("could not determine AWS region: %w", err) } return c.HTTPClientConfig.Validate() @@ -153,6 +153,12 @@ type ecsClient interface { DescribeServices(context.Context, *ecs.DescribeServicesInput, ...func(*ecs.Options)) (*ecs.DescribeServicesOutput, error) ListTasks(context.Context, *ecs.ListTasksInput, ...func(*ecs.Options)) (*ecs.ListTasksOutput, error) DescribeTasks(context.Context, *ecs.DescribeTasksInput, ...func(*ecs.Options)) (*ecs.DescribeTasksOutput, error) + DescribeContainerInstances(context.Context, *ecs.DescribeContainerInstancesInput, ...func(*ecs.Options)) (*ecs.DescribeContainerInstancesOutput, error) +} + +type ecsEC2Client interface { + DescribeInstances(context.Context, *ec2.DescribeInstancesInput, ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) + DescribeNetworkInterfaces(context.Context, *ec2.DescribeNetworkInterfacesInput, ...func(*ec2.Options)) (*ec2.DescribeNetworkInterfacesOutput, error) } // ECSDiscovery periodically performs ECS-SD requests. It implements @@ -162,6 +168,7 @@ type ECSDiscovery struct { logger *slog.Logger cfg *ECSSDConfig ecs ecsClient + ec2 ecsEC2Client } // NewECSDiscovery returns a new ECSDiscovery which periodically refreshes its targets. @@ -191,7 +198,7 @@ func NewECSDiscovery(conf *ECSSDConfig, opts discovery.DiscovererOptions) (*ECSD } func (d *ECSDiscovery) initEcsClient(ctx context.Context) error { - if d.ecs != nil { + if d.ecs != nil && d.ec2 != nil { return nil } @@ -240,6 +247,10 @@ func (d *ECSDiscovery) initEcsClient(ctx context.Context) error { options.HTTPClient = client }) + d.ec2 = ec2.NewFromConfig(cfg, func(options *ec2.Options) { + options.HTTPClient = client + }) + // Test credentials by making a simple API call testCtx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() @@ -255,7 +266,6 @@ func (d *ECSDiscovery) initEcsClient(ctx context.Context) error { // listClusterARNs returns a slice of cluster arns. // This method does not use concurrency as it's a simple paginated call. -// AWS ECS Cluster read actions have burst=50, sustained=20 req/sec limits. func (d *ECSDiscovery) listClusterARNs(ctx context.Context) ([]string, error) { var ( clusterARNs []string @@ -263,7 +273,8 @@ func (d *ECSDiscovery) listClusterARNs(ctx context.Context) ([]string, error) { ) for { resp, err := d.ecs.ListClusters(ctx, &ecs.ListClustersInput{ - NextToken: nextToken, + NextToken: nextToken, + MaxResults: aws.Int32(100), }) if err != nil { return nil, fmt.Errorf("could not list clusters: %w", err) @@ -281,56 +292,61 @@ func (d *ECSDiscovery) listClusterARNs(ctx context.Context) ([]string, error) { } // describeClusters returns a map of cluster ARN to a slice of clusters. -// This method processes clusters in batches without concurrency as it's typically -// a single call handling up to 100 clusters. AWS ECS Cluster read actions have -// burst=50, sustained=20 req/sec limits. +// Uses concurrent requests limited by RequestConcurrency to respect AWS API throttling. +// Clusters are described in batches of 100 to respect AWS API limits (DescribeClusters allows up to 100 clusters per call). func (d *ECSDiscovery) describeClusters(ctx context.Context, clusters []string) (map[string]types.Cluster, error) { + mu := sync.Mutex{} clusterMap := make(map[string]types.Cluster) - - // AWS DescribeClusters can handle up to 100 clusters per call - batchSize := 100 - for _, batch := range batchSlice(clusters, batchSize) { - resp, err := d.ecs.DescribeClusters(ctx, &ecs.DescribeClustersInput{ - Clusters: batch, - Include: []types.ClusterField{"TAGS"}, - }) - if err != nil { - d.logger.Error("Failed to describe clusters", "clusters", batch, "error", err) - return nil, fmt.Errorf("could not describe clusters %v: %w", batch, err) - } - - for _, c := range resp.Clusters { - if c.ClusterArn != nil { - clusterMap[*c.ClusterArn] = c + errg, ectx := errgroup.WithContext(ctx) + errg.SetLimit(d.cfg.RequestConcurrency) + for batch := range slices.Chunk(clusters, 100) { + errg.Go(func() error { + resp, err := d.ecs.DescribeClusters(ectx, &ecs.DescribeClustersInput{ + Clusters: batch, + Include: []types.ClusterField{"TAGS"}, + }) + if err != nil { + d.logger.Error("Failed to describe clusters", "clusters", batch, "error", err) + return fmt.Errorf("could not describe clusters %v: %w", batch, err) } - } + + for _, cluster := range resp.Clusters { + if cluster.ClusterArn != nil { + mu.Lock() + clusterMap[*cluster.ClusterArn] = cluster + mu.Unlock() + } + } + return nil + }) } - return clusterMap, nil + return clusterMap, errg.Wait() } // listServiceARNs returns a map of cluster ARN to a slice of service ARNs. // Uses concurrent requests limited by RequestConcurrency to respect AWS API throttling. -// AWS ECS Service read actions have burst=100, sustained=20 req/sec limits. +// Services are listed in batches of 100 to respect AWS API limits (ListServices allows up to 100 services per call). func (d *ECSDiscovery) listServiceARNs(ctx context.Context, clusters []string) (map[string][]string, error) { - serviceARNsMu := sync.Mutex{} - serviceARNs := make(map[string][]string) + mu := sync.Mutex{} + services := make(map[string][]string) errg, ectx := errgroup.WithContext(ctx) errg.SetLimit(d.cfg.RequestConcurrency) for _, clusterARN := range clusters { errg.Go(func() error { var nextToken *string - var clusterServiceARNs []string + var serviceARNs []string for { resp, err := d.ecs.ListServices(ectx, &ecs.ListServicesInput{ - Cluster: aws.String(clusterARN), - NextToken: nextToken, + Cluster: aws.String(clusterARN), + NextToken: nextToken, + MaxResults: aws.Int32(100), }) if err != nil { return fmt.Errorf("could not list services for cluster %q: %w", clusterARN, err) } - clusterServiceARNs = append(clusterServiceARNs, resp.ServiceArns...) + serviceARNs = append(serviceARNs, resp.ServiceArns...) if resp.NextToken == nil { break @@ -338,75 +354,76 @@ func (d *ECSDiscovery) listServiceARNs(ctx context.Context, clusters []string) ( nextToken = resp.NextToken } - serviceARNsMu.Lock() - serviceARNs[clusterARN] = clusterServiceARNs - serviceARNsMu.Unlock() + mu.Lock() + services[clusterARN] = serviceARNs + mu.Unlock() return nil }) } - return serviceARNs, errg.Wait() -} - -// describeServices returns a map of cluster ARN to services. -// Uses concurrent requests with batching (10 services per request) to respect AWS API limits. -// AWS ECS Service read actions have burst=100, sustained=20 req/sec limits. -func (d *ECSDiscovery) describeServices(ctx context.Context, clusterServiceARNsMap map[string][]string) (map[string][]types.Service, error) { - batchSize := 10 // AWS DescribeServices API limit is 10 services per request - serviceMu := sync.Mutex{} - services := make(map[string][]types.Service) - errg, ectx := errgroup.WithContext(ctx) - errg.SetLimit(d.cfg.RequestConcurrency) - for clusterARN, serviceARNs := range clusterServiceARNsMap { - for _, batch := range batchSlice(serviceARNs, batchSize) { - errg.Go(func() error { - resp, err := d.ecs.DescribeServices(ectx, &ecs.DescribeServicesInput{ - Services: batch, - Cluster: aws.String(clusterARN), - Include: []types.ServiceField{"TAGS"}, - }) - if err != nil { - d.logger.Error("Failed to describe services", "cluster", clusterARN, "batch", batch, "error", err) - return fmt.Errorf("could not describe services for cluster %q: %w", clusterARN, err) - } - - serviceMu.Lock() - services[clusterARN] = append(services[clusterARN], resp.Services...) - serviceMu.Unlock() - - return nil - }) - } - } - return services, errg.Wait() } -// listTaskARNs returns a map of service ARN to a slice of task ARNs. +// describeServices returns a map of service name to service. // Uses concurrent requests limited by RequestConcurrency to respect AWS API throttling. -// AWS ECS Cluster resource read actions have burst=100, sustained=20 req/sec limits. -func (d *ECSDiscovery) listTaskARNs(ctx context.Context, services []types.Service) (map[string][]string, error) { - taskARNsMu := sync.Mutex{} - taskARNs := make(map[string][]string) +// Services are described in batches of 10 to respect AWS API limits (DescribeServices allows up to 10 services per call). +func (d *ECSDiscovery) describeServices(ctx context.Context, clusterARN string, serviceARNS []string) (map[string]types.Service, error) { + mu := sync.Mutex{} + services := make(map[string]types.Service) errg, ectx := errgroup.WithContext(ctx) errg.SetLimit(d.cfg.RequestConcurrency) - for _, service := range services { + for batch := range slices.Chunk(serviceARNS, 10) { errg.Go(func() error { - serviceArn := aws.ToString(service.ServiceArn) + resp, err := d.ecs.DescribeServices(ectx, &ecs.DescribeServicesInput{ + Cluster: aws.String(clusterARN), + Services: batch, + Include: []types.ServiceField{"TAGS"}, + }) + if err != nil { + d.logger.Error("Failed to describe services", "cluster", clusterARN, "batch", batch, "error", err) + return fmt.Errorf("could not describe services for cluster %q: batch %v: %w", clusterARN, batch, err) + } - var nextToken *string - var serviceTaskARNs []string + for _, service := range resp.Services { + if service.ServiceArn != nil { + mu.Lock() + services[*service.ServiceName] = service + mu.Unlock() + } + } + return nil + }) + } + + return services, errg.Wait() +} + +// listTaskARNs returns a map of clustersARN to a slice of task ARNs. +// Uses concurrent requests limited by RequestConcurrency to respect AWS API throttling. +// Tasks are listed in batches of 100 to respect AWS API limits (ListTasks allows up to 100 tasks per call). +// This method also uses pagination to handle cases where there are more than 100 tasks in a cluster. +func (d *ECSDiscovery) listTaskARNs(ctx context.Context, clusterARNs []string) (map[string][]string, error) { + mu := sync.Mutex{} + tasks := make(map[string][]string) + errg, ectx := errgroup.WithContext(ctx) + errg.SetLimit(d.cfg.RequestConcurrency) + for _, clusterARN := range clusterARNs { + errg.Go(func() error { + var ( + nextToken *string + taskARNs []string + ) for { resp, err := d.ecs.ListTasks(ectx, &ecs.ListTasksInput{ - Cluster: aws.String(*service.ClusterArn), - ServiceName: aws.String(*service.ServiceName), - NextToken: nextToken, + Cluster: aws.String(clusterARN), + NextToken: nextToken, + MaxResults: aws.Int32(100), }) if err != nil { - return fmt.Errorf("could not list tasks for service %q: %w", serviceArn, err) + return fmt.Errorf("could not list tasks for cluster %q: %w", clusterARN, err) } - serviceTaskARNs = append(serviceTaskARNs, resp.TaskArns...) + taskARNs = append(taskARNs, resp.TaskArns...) if resp.NextToken == nil { break @@ -414,57 +431,203 @@ func (d *ECSDiscovery) listTaskARNs(ctx context.Context, services []types.Servic nextToken = resp.NextToken } - taskARNsMu.Lock() - taskARNs[serviceArn] = serviceTaskARNs - taskARNsMu.Unlock() + mu.Lock() + tasks[clusterARN] = taskARNs + mu.Unlock() return nil }) } - return taskARNs, errg.Wait() -} - -// describeTasks returns a map of task arn to a slice task. -// Uses concurrent requests with batching (100 tasks per request) to respect AWS API limits. -// AWS ECS Cluster resource read actions have burst=100, sustained=20 req/sec limits. -func (d *ECSDiscovery) describeTasks(ctx context.Context, clusterARN string, taskARNsMap map[string][]string) (map[string][]types.Task, error) { - batchSize := 100 // AWS DescribeTasks API limit is 100 tasks per request - taskMu := sync.Mutex{} - tasks := make(map[string][]types.Task) - errg, ectx := errgroup.WithContext(ctx) - errg.SetLimit(d.cfg.RequestConcurrency) - for serviceARN, taskARNs := range taskARNsMap { - for _, batch := range batchSlice(taskARNs, batchSize) { - errg.Go(func() error { - resp, err := d.ecs.DescribeTasks(ectx, &ecs.DescribeTasksInput{ - Cluster: aws.String(clusterARN), - Tasks: batch, - Include: []types.TaskField{"TAGS"}, - }) - if err != nil { - d.logger.Error("Failed to describe tasks", "service", serviceARN, "cluster", clusterARN, "batch", batch, "error", err) - return fmt.Errorf("could not describe tasks for service %q in cluster %q: %w", serviceARN, clusterARN, err) - } - - taskMu.Lock() - tasks[serviceARN] = append(tasks[serviceARN], resp.Tasks...) - taskMu.Unlock() - - return nil - }) - } - } - return tasks, errg.Wait() } -func batchSlice[T any](a []T, size int) [][]T { - batches := make([][]T, 0, len(a)/size+1) - for i := 0; i < len(a); i += size { - end := min(i+size, len(a)) - batches = append(batches, a[i:end]) +// describeTasks returns a slice of tasks. +// Uses concurrent requests limited by RequestConcurrency to respect AWS API throttling. +// Tasks are described in batches of 100 to respect AWS API limits (DescribeTasks allows up to 100 tasks per call). +func (d *ECSDiscovery) describeTasks(ctx context.Context, clusterARN string, taskARNs []string) ([]types.Task, error) { + mu := sync.Mutex{} + var tasks []types.Task + errg, ectx := errgroup.WithContext(ctx) + errg.SetLimit(d.cfg.RequestConcurrency) + for batch := range slices.Chunk(taskARNs, 100) { + errg.Go(func() error { + resp, err := d.ecs.DescribeTasks(ectx, &ecs.DescribeTasksInput{ + Cluster: aws.String(clusterARN), + Tasks: batch, + Include: []types.TaskField{"TAGS"}, + }) + if err != nil { + d.logger.Error("Failed to describe tasks", "cluster", clusterARN, "batch", batch, "error", err) + return fmt.Errorf("could not describe tasks in cluster %q: batch %v: %w", clusterARN, batch, err) + } + + mu.Lock() + tasks = append(tasks, resp.Tasks...) + mu.Unlock() + return nil + }) } - return batches + + return tasks, errg.Wait() +} + +// describeContainerInstances returns a map of container instance ARN to EC2 instance ID +// Uses concurrent requests limited by RequestConcurrency to respect AWS API throttling. +// Container instances are described in batches of 100 to respect AWS API limits (DescribeContainerInstances allows up to 100 container instances per call). +func (d *ECSDiscovery) describeContainerInstances(ctx context.Context, clusterARN string, tasks []types.Task) (map[string]string, error) { + containerInstanceARNs := make([]string, 0, len(tasks)) + for _, task := range tasks { + if task.ContainerInstanceArn != nil { + containerInstanceARNs = append(containerInstanceARNs, *task.ContainerInstanceArn) + } + } + + if len(containerInstanceARNs) == 0 { + return make(map[string]string), nil + } + + mu := sync.Mutex{} + containerInstToEC2 := make(map[string]string) + errg, ectx := errgroup.WithContext(ctx) + errg.SetLimit(d.cfg.RequestConcurrency) + for batch := range slices.Chunk(containerInstanceARNs, 100) { + errg.Go(func() error { + resp, err := d.ecs.DescribeContainerInstances(ectx, &ecs.DescribeContainerInstancesInput{ + Cluster: aws.String(clusterARN), + ContainerInstances: batch, + }) + if err != nil { + return fmt.Errorf("could not describe container instances: %w", err) + } + + for _, ci := range resp.ContainerInstances { + if ci.ContainerInstanceArn != nil && ci.Ec2InstanceId != nil { + mu.Lock() + containerInstToEC2[*ci.ContainerInstanceArn] = *ci.Ec2InstanceId + mu.Unlock() + } + } + return nil + }) + } + + return containerInstToEC2, errg.Wait() +} + +// ec2InstanceInfo holds information retrieved from EC2 DescribeInstances. +type ec2InstanceInfo struct { + privateIP string + publicIP string + subnetID string + instanceType string + tags map[string]string +} + +// describeEC2Instances returns a map of EC2 instance ID to instance information. +// Uses concurrent requests limited by RequestConcurrency to respect AWS API throttling. +// This method does not use concurrency as it's a simple paginated call. +func (d *ECSDiscovery) describeEC2Instances(ctx context.Context, instanceIDs []string) (map[string]ec2InstanceInfo, error) { + if len(instanceIDs) == 0 { + return make(map[string]ec2InstanceInfo), nil + } + + instanceInfo := make(map[string]ec2InstanceInfo) + var nextToken *string + + for { + resp, err := d.ec2.DescribeInstances(ctx, &ec2.DescribeInstancesInput{ + InstanceIds: instanceIDs, + NextToken: nextToken, + }) + if err != nil { + return nil, fmt.Errorf("could not describe EC2 instances: %w", err) + } + + for _, reservation := range resp.Reservations { + for _, instance := range reservation.Instances { + if instance.InstanceId != nil && instance.PrivateIpAddress != nil { + info := ec2InstanceInfo{ + privateIP: *instance.PrivateIpAddress, + tags: make(map[string]string), + } + if instance.PublicIpAddress != nil { + info.publicIP = *instance.PublicIpAddress + } + if instance.SubnetId != nil { + info.subnetID = *instance.SubnetId + } + if instance.InstanceType != "" { + info.instanceType = string(instance.InstanceType) + } + // Collect EC2 instance tags + for _, tag := range instance.Tags { + if tag.Key != nil && tag.Value != nil { + info.tags[*tag.Key] = *tag.Value + } + } + instanceInfo[*instance.InstanceId] = info + } + } + } + + if resp.NextToken == nil { + break + } + nextToken = resp.NextToken + } + + return instanceInfo, nil +} + +// describeNetworkInterfaces returns a map of ENI ID to public IP address. +// This is needed to get the public IP for tasks using awsvpc network mode, as the ENI is what gets the public IP, not the EC2 instance. +// This method does not use concurrency as it's a simple paginated call. +func (d *ECSDiscovery) describeNetworkInterfaces(ctx context.Context, tasks []types.Task) (map[string]string, error) { + eniIDs := make([]string, 0, len(tasks)) + + for _, task := range tasks { + for _, attachment := range task.Attachments { + if attachment.Type != nil && *attachment.Type == "ElasticNetworkInterface" { + for _, detail := range attachment.Details { + if detail.Name != nil && *detail.Name == "networkInterfaceId" && detail.Value != nil { + eniIDs = append(eniIDs, *detail.Value) + break + } + } + break + } + } + } + + if len(eniIDs) == 0 { + return make(map[string]string), nil + } + + eniToPublicIP := make(map[string]string) + var nextToken *string + + for { + resp, err := d.ec2.DescribeNetworkInterfaces(ctx, &ec2.DescribeNetworkInterfacesInput{ + NetworkInterfaceIds: eniIDs, + NextToken: nextToken, + }) + if err != nil { + return nil, fmt.Errorf("could not describe network interfaces: %w", err) + } + + for _, eni := range resp.NetworkInterfaces { + if eni.NetworkInterfaceId != nil && eni.Association != nil && eni.Association.PublicIp != nil { + eniToPublicIP[*eni.NetworkInterfaceId] = *eni.Association.PublicIp + } + } + + if resp.NextToken == nil { + break + } + nextToken = resp.NextToken + } + + return eniToPublicIP, nil } func (d *ECSDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) { @@ -495,166 +658,338 @@ func (d *ECSDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group, error Source: d.cfg.Region, } - clusterARNMap, err := d.describeClusters(ctx, clusters) - if err != nil { - return nil, err - } + // Fetch cluster details, service ARNs, and task ARNs in parallel + var ( + clusterMap map[string]types.Cluster + serviceMap map[string][]string + taskMap map[string][]string + ) - clusterServiceARNMap, err := d.listServiceARNs(ctx, clusters) - if err != nil { - return nil, err - } + clusterErrg, clusterCtx := errgroup.WithContext(ctx) + clusterErrg.Go(func() error { + var err error + clusterMap, err = d.describeClusters(clusterCtx, clusters) + return err + }) + clusterErrg.Go(func() error { + var err error + serviceMap, err = d.listServiceARNs(clusterCtx, clusters) + return err + }) + clusterErrg.Go(func() error { + var err error + taskMap, err = d.listTaskARNs(clusterCtx, clusters) + return err + }) - clusterServicesMap, err := d.describeServices(ctx, clusterServiceARNMap) - if err != nil { + if err := clusterErrg.Wait(); err != nil { return nil, err } // Use goroutines to process clusters in parallel var ( - targetsMu sync.Mutex - wg sync.WaitGroup + clusterWg sync.WaitGroup + clusterMu sync.Mutex + clusterTargets []model.LabelSet ) - for clusterArn, clusterServices := range clusterServicesMap { - if len(clusterServices) == 0 { + for clusterARN, taskARNs := range taskMap { + if len(taskARNs) == 0 { continue } - wg.Add(1) - go func(clusterArn string, clusterServices []types.Service) { - defer wg.Done() + clusterWg.Add(1) - serviceTaskARNMap, err := d.listTaskARNs(ctx, clusterServices) - if err != nil { - d.logger.Error("Failed to list task ARNs for cluster", "cluster", clusterArn, "error", err) - return - } + go func(cluster types.Cluster, serviceARNs, taskARNs []string) { + defer clusterWg.Done() - serviceTaskMap, err := d.describeTasks(ctx, clusterArn, serviceTaskARNMap) - if err != nil { - d.logger.Error("Failed to describe tasks for cluster", "cluster", clusterArn, "error", err) - return - } - - // Process services within this cluster in parallel + // Fetch services and tasks in parallel (they're independent) var ( - serviceWg sync.WaitGroup - localTargets []model.LabelSet - localTargetsMu sync.Mutex + services map[string]types.Service + tasks []types.Task ) - for _, clusterService := range clusterServices { - serviceWg.Add(1) - go func(clusterService types.Service) { - defer serviceWg.Done() + resourceErrg, resourceCtx := errgroup.WithContext(ctx) + resourceErrg.Go(func() error { + var err error + services, err = d.describeServices(resourceCtx, *cluster.ClusterArn, serviceARNs) + if err != nil { + d.logger.Error("Failed to describe services for cluster", "cluster", *cluster.ClusterArn, "error", err) + } + return err + }) + resourceErrg.Go(func() error { + var err error + tasks, err = d.describeTasks(resourceCtx, *cluster.ClusterArn, taskARNs) + if err != nil { + d.logger.Error("Failed to describe tasks for cluster", "cluster", *cluster.ClusterArn, "error", err) + } + return err + }) - serviceArn := *clusterService.ServiceArn - - if tasks, exists := serviceTaskMap[serviceArn]; exists { - var serviceTargets []model.LabelSet - - for _, task := range tasks { - // Find the ENI attachment to get the private IP address - var eniAttachment *types.Attachment - for _, attachment := range task.Attachments { - if attachment.Type != nil && *attachment.Type == "ElasticNetworkInterface" { - eniAttachment = &attachment - break - } - } - if eniAttachment == nil { - continue - } - - var ipAddress, subnetID string - for _, detail := range eniAttachment.Details { - switch *detail.Name { - case "privateIPv4Address": - ipAddress = *detail.Value - case "subnetId": - subnetID = *detail.Value - } - } - if ipAddress == "" { - continue - } - - labels := model.LabelSet{ - ecsLabelClusterARN: model.LabelValue(*clusterService.ClusterArn), - ecsLabelService: model.LabelValue(*clusterService.ServiceName), - ecsLabelServiceARN: model.LabelValue(*clusterService.ServiceArn), - ecsLabelServiceStatus: model.LabelValue(*clusterService.Status), - ecsLabelTaskGroup: model.LabelValue(*task.Group), - ecsLabelTaskARN: model.LabelValue(*task.TaskArn), - ecsLabelTaskDefinition: model.LabelValue(*task.TaskDefinitionArn), - ecsLabelIPAddress: model.LabelValue(ipAddress), - ecsLabelSubnetID: model.LabelValue(subnetID), - ecsLabelRegion: model.LabelValue(d.cfg.Region), - ecsLabelLaunchType: model.LabelValue(task.LaunchType), - ecsLabelAvailabilityZone: model.LabelValue(*task.AvailabilityZone), - ecsLabelDesiredStatus: model.LabelValue(*task.DesiredStatus), - ecsLabelLastStatus: model.LabelValue(*task.LastStatus), - ecsLabelHealthStatus: model.LabelValue(task.HealthStatus), - } - - if task.PlatformFamily != nil { - labels[ecsLabelPlatformFamily] = model.LabelValue(*task.PlatformFamily) - } - if task.PlatformVersion != nil { - labels[ecsLabelPlatformVersion] = model.LabelValue(*task.PlatformVersion) - } - - labels[model.AddressLabel] = model.LabelValue(net.JoinHostPort(ipAddress, strconv.Itoa(d.cfg.Port))) - - // Add cluster tags - if cluster, exists := clusterARNMap[*clusterService.ClusterArn]; exists { - if cluster.ClusterName != nil { - labels[ecsLabelCluster] = model.LabelValue(*cluster.ClusterName) - } - - for _, clusterTag := range cluster.Tags { - if clusterTag.Key != nil && clusterTag.Value != nil { - labels[model.LabelName(ecsLabelTagCluster+strutil.SanitizeLabelName(*clusterTag.Key))] = model.LabelValue(*clusterTag.Value) - } - } - } - - // Add service tags - for _, serviceTag := range clusterService.Tags { - if serviceTag.Key != nil && serviceTag.Value != nil { - labels[model.LabelName(ecsLabelTagService+strutil.SanitizeLabelName(*serviceTag.Key))] = model.LabelValue(*serviceTag.Value) - } - } - - // Add task tags - for _, taskTag := range task.Tags { - if taskTag.Key != nil && taskTag.Value != nil { - labels[model.LabelName(ecsLabelTagTask+strutil.SanitizeLabelName(*taskTag.Key))] = model.LabelValue(*taskTag.Value) - } - } - - serviceTargets = append(serviceTargets, labels) - } - - // Add service targets to local targets with mutex protection - localTargetsMu.Lock() - localTargets = append(localTargets, serviceTargets...) - localTargetsMu.Unlock() - } - }(clusterService) + if err := resourceErrg.Wait(); err != nil { + return } - serviceWg.Wait() + // Fetch container instances and network interfaces in parallel (both depend on tasks) + var ( + containerInstances map[string]string + eniToPublicIP map[string]string + ) - // Add all local targets to main target group with mutex protection - targetsMu.Lock() - tg.Targets = append(tg.Targets, localTargets...) - targetsMu.Unlock() - }(clusterArn, clusterServices) + instanceErrg, instanceCtx := errgroup.WithContext(ctx) + instanceErrg.Go(func() error { + var err error + containerInstances, err = d.describeContainerInstances(instanceCtx, *cluster.ClusterArn, tasks) + if err != nil { + d.logger.Error("Failed to describe container instances for cluster", "cluster", *cluster.ClusterArn, "error", err) + } + return err + }) + instanceErrg.Go(func() error { + var err error + eniToPublicIP, err = d.describeNetworkInterfaces(instanceCtx, tasks) + if err != nil { + d.logger.Error("Failed to describe network interfaces for cluster", "cluster", *cluster.ClusterArn, "error", err) + } + return err + }) + + if err := instanceErrg.Wait(); err != nil { + return + } + + ec2Instances := make(map[string]ec2InstanceInfo) + if len(containerInstances) > 0 { + // Deduplicate EC2 instance IDs (multiple tasks can share the same instance) + ec2InstanceIDSet := make(map[string]struct{}) + for _, ec2ID := range containerInstances { + ec2InstanceIDSet[ec2ID] = struct{}{} + } + ec2InstanceIDs := make([]string, 0, len(ec2InstanceIDSet)) + for ec2ID := range ec2InstanceIDSet { + ec2InstanceIDs = append(ec2InstanceIDs, ec2ID) + } + ec2Instances, err = d.describeEC2Instances(ctx, ec2InstanceIDs) + if err != nil { + d.logger.Error("Failed to describe EC2 instances for cluster", "cluster", *cluster.ClusterArn, "error", err) + return + } + } + + var ( + taskWg sync.WaitGroup + taskMu sync.Mutex + taskTargets []model.LabelSet + ) + + for _, task := range tasks { + taskWg.Add(1) + + go func(cluster types.Cluster, services map[string]types.Service, task types.Task, containerInstances map[string]string, ec2Instances map[string]ec2InstanceInfo, eniToPublicIP map[string]string) { + defer taskWg.Done() + + var ( + ipAddress, subnetID, publicIP string + networkMode string + ec2InstanceID, ec2InstanceType, ec2InstancePrivateIP, ec2InstancePublicIP string + ) + + // Try to get IP from ENI attachment (awsvpc mode) + var eniAttachment *types.Attachment + for _, attachment := range task.Attachments { + if attachment.Type != nil && *attachment.Type == "ElasticNetworkInterface" { + eniAttachment = &attachment + break + } + } + + if eniAttachment != nil { + // awsvpc networking mode - get IP from ENI + networkMode = "awsvpc" + var eniID string + for _, detail := range eniAttachment.Details { + switch *detail.Name { + case "privateIPv4Address": + ipAddress = *detail.Value + case "subnetId": + subnetID = *detail.Value + case "networkInterfaceId": + eniID = *detail.Value + } + } + // Get public IP from ENI if available + if eniID != "" { + if pub, ok := eniToPublicIP[eniID]; ok { + publicIP = pub + } + } + } else if task.ContainerInstanceArn != nil { + // bridge/host networking mode - need to get EC2 instance IP and subnet + networkMode = "bridge" + var ok bool + ec2InstanceID, ok = containerInstances[*task.ContainerInstanceArn] + if ok { + info, ok := ec2Instances[ec2InstanceID] + if ok { + ipAddress = info.privateIP + publicIP = info.publicIP + subnetID = info.subnetID + ec2InstanceType = info.instanceType + ec2InstancePrivateIP = info.privateIP + ec2InstancePublicIP = info.publicIP + } else { + d.logger.Debug("EC2 instance info not found", "instance", ec2InstanceID, "task", *task.TaskArn) + } + } else { + d.logger.Debug("Container instance not found in map", "arn", *task.ContainerInstanceArn, "task", *task.TaskArn) + } + } + + // Get EC2 instance metadata for awsvpc tasks running on EC2 + // We want the instance type and the host IPs for advanced use cases + if networkMode == "awsvpc" && task.ContainerInstanceArn != nil { + var ok bool + ec2InstanceID, ok = containerInstances[*task.ContainerInstanceArn] + if ok { + info, ok := ec2Instances[ec2InstanceID] + if ok { + ec2InstanceType = info.instanceType + ec2InstancePrivateIP = info.privateIP + ec2InstancePublicIP = info.publicIP + } + } + } + + if ipAddress == "" { + return + } + + labels := model.LabelSet{ + ecsLabelClusterARN: model.LabelValue(*cluster.ClusterArn), + ecsLabelCluster: model.LabelValue(*cluster.ClusterName), + ecsLabelTaskGroup: model.LabelValue(*task.Group), + ecsLabelTaskARN: model.LabelValue(*task.TaskArn), + ecsLabelTaskDefinition: model.LabelValue(*task.TaskDefinitionArn), + ecsLabelIPAddress: model.LabelValue(ipAddress), + ecsLabelRegion: model.LabelValue(d.cfg.Region), + ecsLabelLaunchType: model.LabelValue(task.LaunchType), + ecsLabelAvailabilityZone: model.LabelValue(*task.AvailabilityZone), + ecsLabelDesiredStatus: model.LabelValue(*task.DesiredStatus), + ecsLabelLastStatus: model.LabelValue(*task.LastStatus), + ecsLabelHealthStatus: model.LabelValue(task.HealthStatus), + ecsLabelNetworkMode: model.LabelValue(networkMode), + } + + // Add subnet ID when available (awsvpc mode from ENI, bridge/host from EC2 instance) + if subnetID != "" { + labels[ecsLabelSubnetID] = model.LabelValue(subnetID) + } + + // Add container instance and EC2 instance info for EC2 launch type + if task.ContainerInstanceArn != nil { + labels[ecsLabelContainerInstanceARN] = model.LabelValue(*task.ContainerInstanceArn) + } + if ec2InstanceID != "" { + labels[ecsLabelEC2InstanceID] = model.LabelValue(ec2InstanceID) + } + if ec2InstanceType != "" { + labels[ecsLabelEC2InstanceType] = model.LabelValue(ec2InstanceType) + } + if ec2InstancePrivateIP != "" { + labels[ecsLabelEC2InstancePrivateIP] = model.LabelValue(ec2InstancePrivateIP) + } + if ec2InstancePublicIP != "" { + labels[ecsLabelEC2InstancePublicIP] = model.LabelValue(ec2InstancePublicIP) + } + if publicIP != "" { + labels[ecsLabelPublicIP] = model.LabelValue(publicIP) + } + + if task.PlatformFamily != nil { + labels[ecsLabelPlatformFamily] = model.LabelValue(*task.PlatformFamily) + } + if task.PlatformVersion != nil { + labels[ecsLabelPlatformVersion] = model.LabelValue(*task.PlatformVersion) + } + + labels[model.AddressLabel] = model.LabelValue(net.JoinHostPort(ipAddress, strconv.Itoa(d.cfg.Port))) + + // Add cluster tags + for _, clusterTag := range cluster.Tags { + if clusterTag.Key != nil && clusterTag.Value != nil { + labels[model.LabelName(ecsLabelTagCluster+strutil.SanitizeLabelName(*clusterTag.Key))] = model.LabelValue(*clusterTag.Value) + } + } + + // If this is not a standalone task, add service information and tags + if !isStandaloneTask(task) { + service, ok := services[getServiceNameFromTaskGroup(task)] + if !ok { + d.logger.Debug("Service not found for task", "task", *task.TaskArn, "service", getServiceNameFromTaskGroup(task)) + } + if service.ServiceName != nil { + labels[ecsLabelService] = model.LabelValue(*service.ServiceName) + } + if service.ServiceArn != nil { + labels[ecsLabelServiceARN] = model.LabelValue(*service.ServiceArn) + } + if service.Status != nil { + labels[ecsLabelServiceStatus] = model.LabelValue(*service.Status) + } + + // Add service tags + for _, serviceTag := range service.Tags { + if serviceTag.Key != nil && serviceTag.Value != nil { + labels[model.LabelName(ecsLabelTagService+strutil.SanitizeLabelName(*serviceTag.Key))] = model.LabelValue(*serviceTag.Value) + } + } + } + + // Add task tags + for _, taskTag := range task.Tags { + if taskTag.Key != nil && taskTag.Value != nil { + labels[model.LabelName(ecsLabelTagTask+strutil.SanitizeLabelName(*taskTag.Key))] = model.LabelValue(*taskTag.Value) + } + } + + // Add EC2 instance tags (if running on EC2) + if ec2InstanceID != "" { + if info, ok := ec2Instances[ec2InstanceID]; ok { + for tagKey, tagValue := range info.tags { + labels[model.LabelName(ecsLabelTagEC2+strutil.SanitizeLabelName(tagKey))] = model.LabelValue(tagValue) + } + } + } + + taskMu.Lock() + taskTargets = append(taskTargets, labels) + taskMu.Unlock() + }(cluster, services, task, containerInstances, ec2Instances, eniToPublicIP) + } + + taskWg.Wait() + + // Add this cluster's task targets to the overall collection + clusterMu.Lock() + clusterTargets = append(clusterTargets, taskTargets...) + clusterMu.Unlock() + }(clusterMap[clusterARN], serviceMap[clusterARN], taskARNs) } - wg.Wait() + clusterWg.Wait() + + // Set all targets to the target group + tg.Targets = clusterTargets return []*targetgroup.Group{tg}, nil } + +func isStandaloneTask(task types.Task) bool { + // A standalone task will have a group of "family:task-def-name" + return task.Group != nil && strings.HasPrefix(*task.Group, "family:") +} + +func getServiceNameFromTaskGroup(task types.Task) string { + return strings.Split(*task.Group, ":")[1] +} diff --git a/vendor/github.com/prometheus/prometheus/discovery/aws/lightsail.go b/vendor/github.com/prometheus/prometheus/discovery/aws/lightsail.go index c9ca3eaee..69a5b6625 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/aws/lightsail.go +++ b/vendor/github.com/prometheus/prometheus/discovery/aws/lightsail.go @@ -1,4 +1,4 @@ -// Copyright 2021 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -26,7 +26,6 @@ import ( awsConfig "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/credentials/stscreds" - "github.com/aws/aws-sdk-go-v2/feature/ec2/imds" "github.com/aws/aws-sdk-go-v2/service/lightsail" "github.com/aws/aws-sdk-go-v2/service/sts" "github.com/aws/smithy-go" @@ -106,30 +105,9 @@ func (c *LightsailSDConfig) UnmarshalYAML(unmarshal func(any) error) error { return err } - if c.Region == "" { - cfg, err := awsConfig.LoadDefaultConfig(context.Background()) - if err != nil { - return err - } - - if cfg.Region != "" { - // Use the region from the AWS config. It will load environment variables and shared config files. - c.Region = cfg.Region - } - - if c.Region == "" { - // Try to get the region from the instance metadata service (IMDS). - imdsClient := imds.NewFromConfig(cfg) - region, err := imdsClient.GetRegion(context.Background(), &imds.GetRegionInput{}) - if err != nil { - return err - } - c.Region = region.Region - } - } - - if c.Region == "" { - return errors.New("lightsail SD configuration requires a region") + c.Region, err = loadRegion(context.Background(), c.Region) + if err != nil { + return fmt.Errorf("could not determine AWS region: %w", err) } return c.HTTPClientConfig.Validate() diff --git a/vendor/github.com/prometheus/prometheus/discovery/aws/metrics_ec2.go b/vendor/github.com/prometheus/prometheus/discovery/aws/metrics_ec2.go index 45227c353..1a37347b4 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/aws/metrics_ec2.go +++ b/vendor/github.com/prometheus/prometheus/discovery/aws/metrics_ec2.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/aws/metrics_lightsail.go b/vendor/github.com/prometheus/prometheus/discovery/aws/metrics_lightsail.go index 4dfe14c60..40f763945 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/aws/metrics_lightsail.go +++ b/vendor/github.com/prometheus/prometheus/discovery/aws/metrics_lightsail.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/aws/metrics_msk.go b/vendor/github.com/prometheus/prometheus/discovery/aws/metrics_msk.go new file mode 100644 index 000000000..fc69f57aa --- /dev/null +++ b/vendor/github.com/prometheus/prometheus/discovery/aws/metrics_msk.go @@ -0,0 +1,32 @@ +// Copyright 2015 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package aws + +import ( + "github.com/prometheus/prometheus/discovery" +) + +type mskMetrics struct { + refreshMetrics discovery.RefreshMetricsInstantiator +} + +var _ discovery.DiscovererMetrics = (*mskMetrics)(nil) + +// Register implements discovery.DiscovererMetrics. +func (*mskMetrics) Register() error { + return nil +} + +// Unregister implements discovery.DiscovererMetrics. +func (*mskMetrics) Unregister() {} diff --git a/vendor/github.com/prometheus/prometheus/discovery/aws/msk.go b/vendor/github.com/prometheus/prometheus/discovery/aws/msk.go new file mode 100644 index 000000000..3ecc1e623 --- /dev/null +++ b/vendor/github.com/prometheus/prometheus/discovery/aws/msk.go @@ -0,0 +1,451 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package aws + +import ( + "context" + "errors" + "fmt" + "log/slog" + "net" + "strconv" + "sync" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + awsConfig "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/credentials" + "github.com/aws/aws-sdk-go-v2/credentials/stscreds" + "github.com/aws/aws-sdk-go-v2/service/kafka" + "github.com/aws/aws-sdk-go-v2/service/kafka/types" + "github.com/aws/aws-sdk-go-v2/service/sts" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/config" + "github.com/prometheus/common/model" + "github.com/prometheus/common/promslog" + "golang.org/x/sync/errgroup" + + "github.com/prometheus/prometheus/discovery" + "github.com/prometheus/prometheus/discovery/refresh" + "github.com/prometheus/prometheus/discovery/targetgroup" + "github.com/prometheus/prometheus/util/strutil" +) + +type NodeType string + +const ( + NodeTypeBroker NodeType = "BROKER" + NodeTypeController NodeType = "CONTROLLER" +) + +const ( + mskLabel = model.MetaLabelPrefix + "msk_" + + // Cluster labels. + mskLabelCluster = mskLabel + "cluster_" + mskLabelClusterName = mskLabelCluster + "name" + mskLabelClusterARN = mskLabelCluster + "arn" + mskLabelClusterState = mskLabelCluster + "state" + mskLabelClusterType = mskLabelCluster + "type" + mskLabelClusterVersion = mskLabelCluster + "version" + mskLabelClusterJmxExporterEnabled = mskLabelCluster + "jmx_exporter_enabled" + mskLabelClusterConfigurationARN = mskLabelCluster + "configuration_arn" + mskLabelClusterConfigurationRevision = mskLabelCluster + "configuration_revision" + mskLabelClusterKafkaVersion = mskLabelCluster + "kafka_version" + mskLabelClusterTags = mskLabelCluster + "tag_" + + // Node labels. + mskLabelNode = mskLabel + "node_" + mskLabelNodeType = mskLabelNode + "type" + mskLabelNodeARN = mskLabelNode + "arn" + mskLabelNodeAddedTime = mskLabelNode + "added_time" + mskLabelNodeInstanceType = mskLabelNode + "instance_type" + mskLabelNodeAttachedENI = mskLabelNode + "attached_eni" + + // Broker labels. + mskLabelBroker = mskLabel + "broker_" + mskLabelBrokerEndpointIndex = mskLabelBroker + "endpoint_index" + mskLabelBrokerID = mskLabelBroker + "id" + mskLabelBrokerClientSubnet = mskLabelBroker + "client_subnet" + mskLabelBrokerClientVPCIP = mskLabelBroker + "client_vpc_ip" + mskLabelBrokerNodeExporterEnabled = mskLabelBroker + "node_exporter_enabled" + + // Controller labels. + mskLabelController = mskLabel + "controller_" + mskLabelControllerEndpointIndex = mskLabelController + "endpoint_index" +) + +// DefaultMSKSDConfig is the default MSK SD configuration. +var DefaultMSKSDConfig = MSKSDConfig{ + Port: 80, + RefreshInterval: model.Duration(60 * time.Second), + RequestConcurrency: 10, + HTTPClientConfig: config.DefaultHTTPClientConfig, +} + +func init() { + discovery.RegisterConfig(&MSKSDConfig{}) +} + +// MSKSDConfig is the configuration for MSK based service discovery. +type MSKSDConfig struct { + Region string `yaml:"region"` + Endpoint string `yaml:"endpoint"` + AccessKey string `yaml:"access_key,omitempty"` + SecretKey config.Secret `yaml:"secret_key,omitempty"` + Profile string `yaml:"profile,omitempty"` + RoleARN string `yaml:"role_arn,omitempty"` + Clusters []string `yaml:"clusters,omitempty"` + Port int `yaml:"port"` + RefreshInterval model.Duration `yaml:"refresh_interval,omitempty"` + + RequestConcurrency int `yaml:"request_concurrency,omitempty"` + HTTPClientConfig config.HTTPClientConfig `yaml:",inline"` +} + +// NewDiscovererMetrics implements discovery.Config. +func (*MSKSDConfig) NewDiscovererMetrics(_ prometheus.Registerer, rmi discovery.RefreshMetricsInstantiator) discovery.DiscovererMetrics { + return &mskMetrics{ + refreshMetrics: rmi, + } +} + +// Name returns the name of the MSK Config. +func (*MSKSDConfig) Name() string { return "msk" } + +// NewDiscoverer returns a Discoverer for the MSK Config. +func (c *MSKSDConfig) NewDiscoverer(opts discovery.DiscovererOptions) (discovery.Discoverer, error) { + return NewMSKDiscovery(c, opts) +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface for the MSK Config. +func (c *MSKSDConfig) UnmarshalYAML(unmarshal func(any) error) error { + *c = DefaultMSKSDConfig + type plain MSKSDConfig + err := unmarshal((*plain)(c)) + if err != nil { + return err + } + + c.Region, err = loadRegion(context.Background(), c.Region) + if err != nil { + return fmt.Errorf("could not determine AWS region: %w", err) + } + + return c.HTTPClientConfig.Validate() +} + +type mskClient interface { + DescribeClusterV2(context.Context, *kafka.DescribeClusterV2Input, ...func(*kafka.Options)) (*kafka.DescribeClusterV2Output, error) + ListClustersV2(context.Context, *kafka.ListClustersV2Input, ...func(*kafka.Options)) (*kafka.ListClustersV2Output, error) + ListNodes(context.Context, *kafka.ListNodesInput, ...func(*kafka.Options)) (*kafka.ListNodesOutput, error) +} + +// MSKDiscovery periodically performs MSK-SD requests. It implements +// the Discoverer interface. +type MSKDiscovery struct { + *refresh.Discovery + logger *slog.Logger + cfg *MSKSDConfig + msk mskClient +} + +// NewMSKDiscovery returns a new MSKDiscovery which periodically refreshes its targets. +func NewMSKDiscovery(conf *MSKSDConfig, opts discovery.DiscovererOptions) (*MSKDiscovery, error) { + m, ok := opts.Metrics.(*mskMetrics) + if !ok { + return nil, errors.New("invalid discovery metrics type") + } + + if opts.Logger == nil { + opts.Logger = promslog.NewNopLogger() + } + d := &MSKDiscovery{ + logger: opts.Logger, + cfg: conf, + } + d.Discovery = refresh.NewDiscovery( + refresh.Options{ + Logger: opts.Logger, + Mech: "msk", + Interval: time.Duration(d.cfg.RefreshInterval), + RefreshF: d.refresh, + MetricsInstantiator: m.refreshMetrics, + }, + ) + return d, nil +} + +func (d *MSKDiscovery) initMskClient(ctx context.Context) error { + if d.msk != nil { + return nil + } + + if d.cfg.Region == "" { + return errors.New("region must be set for MSK service discovery") + } + + // Build the HTTP client from the provided HTTPClientConfig. + client, err := config.NewClientFromConfig(d.cfg.HTTPClientConfig, "msk_sd") + if err != nil { + return err + } + + // Build the AWS config with the provided region. + var configOptions []func(*awsConfig.LoadOptions) error + configOptions = append(configOptions, awsConfig.WithRegion(d.cfg.Region)) + configOptions = append(configOptions, awsConfig.WithHTTPClient(client)) + + // Only set static credentials if both access key and secret key are provided + // Otherwise, let AWS SDK use its default credential chain + if d.cfg.AccessKey != "" && d.cfg.SecretKey != "" { + credProvider := credentials.NewStaticCredentialsProvider(d.cfg.AccessKey, string(d.cfg.SecretKey), "") + configOptions = append(configOptions, awsConfig.WithCredentialsProvider(credProvider)) + } + + if d.cfg.Profile != "" { + configOptions = append(configOptions, awsConfig.WithSharedConfigProfile(d.cfg.Profile)) + } + + cfg, err := awsConfig.LoadDefaultConfig(ctx, configOptions...) + if err != nil { + d.logger.Error("Failed to create AWS config", "error", err) + return fmt.Errorf("could not create aws config: %w", err) + } + + // If the role ARN is set, assume the role to get credentials and set the credentials provider in the config. + if d.cfg.RoleARN != "" { + assumeProvider := stscreds.NewAssumeRoleProvider(sts.NewFromConfig(cfg), d.cfg.RoleARN) + cfg.Credentials = aws.NewCredentialsCache(assumeProvider) + } + + d.msk = kafka.NewFromConfig(cfg, func(options *kafka.Options) { + if d.cfg.Endpoint != "" { + options.BaseEndpoint = &d.cfg.Endpoint + } + options.HTTPClient = client + }) + + // Test credentials by making a simple API call + testCtx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + + _, err = d.msk.ListClustersV2(testCtx, &kafka.ListClustersV2Input{}) + if err != nil { + d.logger.Error("Failed to test MSK credentials", "error", err) + return fmt.Errorf("MSK credential test failed: %w", err) + } + + return nil +} + +// describeClusters describes the clusters with the given ARNs and returns their details. +func (d *MSKDiscovery) describeClusters(ctx context.Context, clusterARNs []string) ([]types.Cluster, error) { + var ( + clusters []types.Cluster + mu sync.Mutex + ) + errg, ectx := errgroup.WithContext(ctx) + errg.SetLimit(d.cfg.RequestConcurrency) + for _, clusterARN := range clusterARNs { + errg.Go(func() error { + cluster, err := d.msk.DescribeClusterV2(ectx, &kafka.DescribeClusterV2Input{ + ClusterArn: aws.String(clusterARN), + }) + if err != nil { + return fmt.Errorf("could not describe cluster %v: %w", clusterARN, err) + } + mu.Lock() + clusters = append(clusters, *cluster.ClusterInfo) + mu.Unlock() + return nil + }) + } + + return clusters, errg.Wait() +} + +// listClusters lists all MSK clusters in the configured region and returns their details. +func (d *MSKDiscovery) listClusters(ctx context.Context) ([]types.Cluster, error) { + var ( + clusters []types.Cluster + nextToken *string + ) + for { + listClustersInput := kafka.ListClustersV2Input{ + ClusterTypeFilter: aws.String("PROVISIONED"), + MaxResults: aws.Int32(100), + NextToken: nextToken, + } + + resp, err := d.msk.ListClustersV2(ctx, &listClustersInput) + if err != nil { + return nil, fmt.Errorf("could not list clusters: %w", err) + } + + clusters = append(clusters, resp.ClusterInfoList...) + if resp.NextToken == nil { + break + } + nextToken = resp.NextToken + } + + return clusters, nil +} + +// listNodes lists all nodes for the given clusters and returns a map of cluster ARN to its nodes. +func (d *MSKDiscovery) listNodes(ctx context.Context, clusters []types.Cluster) (map[string][]types.NodeInfo, error) { + clusterNodeMap := make(map[string][]types.NodeInfo) + mu := sync.Mutex{} + errg, ectx := errgroup.WithContext(ctx) + errg.SetLimit(d.cfg.RequestConcurrency) + for _, cluster := range clusters { + clusterARN := aws.ToString(cluster.ClusterArn) + errg.Go(func() error { + var clusterNodes []types.NodeInfo + var nextToken *string + for { + resp, err := d.msk.ListNodes(ectx, &kafka.ListNodesInput{ + ClusterArn: aws.String(clusterARN), + MaxResults: aws.Int32(100), + NextToken: nextToken, + }) + if err != nil { + return fmt.Errorf("could not list nodes for cluster %v: %w", clusterARN, err) + } + + clusterNodes = append(clusterNodes, resp.NodeInfoList...) + if resp.NextToken == nil { + break + } + nextToken = resp.NextToken + } + + mu.Lock() + clusterNodeMap[clusterARN] = clusterNodes + mu.Unlock() + return nil + }) + } + + return clusterNodeMap, errg.Wait() +} + +func (d *MSKDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) { + err := d.initMskClient(ctx) + if err != nil { + return nil, err + } + + tg := &targetgroup.Group{ + Source: d.cfg.Region, + } + + var clusters []types.Cluster + if len(d.cfg.Clusters) > 0 { + clusters, err = d.describeClusters(ctx, d.cfg.Clusters) + if err != nil { + return nil, err + } + } else { + clusters, err = d.listClusters(ctx) + if err != nil { + return nil, err + } + } + + clusterNodeMap, err := d.listNodes(ctx, clusters) + if err != nil { + return nil, err + } + + var ( + targetsMu sync.Mutex + wg sync.WaitGroup + ) + for _, cluster := range clusters { + wg.Add(1) + + go func(cluster types.Cluster, nodes []types.NodeInfo) { + defer wg.Done() + for _, node := range nodes { + labels := model.LabelSet{ + mskLabelClusterName: model.LabelValue(aws.ToString(cluster.ClusterName)), + mskLabelClusterARN: model.LabelValue(aws.ToString(cluster.ClusterArn)), + mskLabelClusterState: model.LabelValue(string(cluster.State)), + mskLabelClusterType: model.LabelValue(string(cluster.ClusterType)), + mskLabelClusterVersion: model.LabelValue(aws.ToString(cluster.CurrentVersion)), + mskLabelNodeARN: model.LabelValue(aws.ToString(node.NodeARN)), + mskLabelNodeAddedTime: model.LabelValue(aws.ToString(node.AddedToClusterTime)), + mskLabelNodeInstanceType: model.LabelValue(aws.ToString(node.InstanceType)), + mskLabelClusterJmxExporterEnabled: model.LabelValue(strconv.FormatBool(*cluster.Provisioned.OpenMonitoring.Prometheus.JmxExporter.EnabledInBroker)), + mskLabelClusterConfigurationARN: model.LabelValue(aws.ToString(cluster.Provisioned.CurrentBrokerSoftwareInfo.ConfigurationArn)), + mskLabelClusterConfigurationRevision: model.LabelValue(strconv.FormatInt(*cluster.Provisioned.CurrentBrokerSoftwareInfo.ConfigurationRevision, 10)), + mskLabelClusterKafkaVersion: model.LabelValue(aws.ToString(cluster.Provisioned.CurrentBrokerSoftwareInfo.KafkaVersion)), + } + + for key, value := range cluster.Tags { + labels[model.LabelName(mskLabelClusterTags+strutil.SanitizeLabelName(key))] = model.LabelValue(value) + } + + switch nodeType(node) { + case NodeTypeBroker: + labels[mskLabelNodeType] = model.LabelValue(NodeTypeBroker) + labels[mskLabelNodeAttachedENI] = model.LabelValue(aws.ToString(node.BrokerNodeInfo.AttachedENIId)) + labels[mskLabelBrokerID] = model.LabelValue(fmt.Sprintf("%.0f", aws.ToFloat64(node.BrokerNodeInfo.BrokerId))) + labels[mskLabelBrokerClientSubnet] = model.LabelValue(aws.ToString(node.BrokerNodeInfo.ClientSubnet)) + labels[mskLabelBrokerClientVPCIP] = model.LabelValue(aws.ToString(node.BrokerNodeInfo.ClientVpcIpAddress)) + labels[mskLabelBrokerNodeExporterEnabled] = model.LabelValue(strconv.FormatBool(*cluster.Provisioned.OpenMonitoring.Prometheus.NodeExporter.EnabledInBroker)) + + for idx, endpoint := range node.BrokerNodeInfo.Endpoints { + endpointLabels := labels.Clone() + endpointLabels[mskLabelBrokerEndpointIndex] = model.LabelValue(strconv.Itoa(idx)) + endpointLabels[model.AddressLabel] = model.LabelValue(net.JoinHostPort(endpoint, strconv.Itoa(d.cfg.Port))) + + targetsMu.Lock() + tg.Targets = append(tg.Targets, endpointLabels) + targetsMu.Unlock() + } + + case NodeTypeController: + labels[mskLabelNodeType] = model.LabelValue(NodeTypeController) + + for idx, endpoint := range node.ControllerNodeInfo.Endpoints { + endpointLabels := labels.Clone() + endpointLabels[mskLabelControllerEndpointIndex] = model.LabelValue(strconv.Itoa(idx)) + endpointLabels[model.AddressLabel] = model.LabelValue(net.JoinHostPort(endpoint, strconv.Itoa(d.cfg.Port))) + + targetsMu.Lock() + tg.Targets = append(tg.Targets, endpointLabels) + targetsMu.Unlock() + } + default: + continue + } + } + }(cluster, clusterNodeMap[aws.ToString(cluster.ClusterArn)]) + } + wg.Wait() + + return []*targetgroup.Group{tg}, nil +} + +func nodeType(node types.NodeInfo) NodeType { + if node.BrokerNodeInfo != nil { + return NodeTypeBroker + } else if node.ControllerNodeInfo != nil { + return NodeTypeController + } + return "" +} diff --git a/vendor/github.com/prometheus/prometheus/discovery/azure/azure.go b/vendor/github.com/prometheus/prometheus/discovery/azure/azure.go index 3c38bbf3e..32fc97fdf 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/azure/azure.go +++ b/vendor/github.com/prometheus/prometheus/discovery/azure/azure.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/azure/metrics.go b/vendor/github.com/prometheus/prometheus/discovery/azure/metrics.go index 3e3dbdbfb..dc0291cdb 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/azure/metrics.go +++ b/vendor/github.com/prometheus/prometheus/discovery/azure/metrics.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/consul/consul.go b/vendor/github.com/prometheus/prometheus/discovery/consul/consul.go index 74b5d0724..1004d0941 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/consul/consul.go +++ b/vendor/github.com/prometheus/prometheus/discovery/consul/consul.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/consul/metrics.go b/vendor/github.com/prometheus/prometheus/discovery/consul/metrics.go index b49509bd8..903fba5ce 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/consul/metrics.go +++ b/vendor/github.com/prometheus/prometheus/discovery/consul/metrics.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/digitalocean/digitalocean.go b/vendor/github.com/prometheus/prometheus/discovery/digitalocean/digitalocean.go index d2fbee1d9..0a185c291 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/digitalocean/digitalocean.go +++ b/vendor/github.com/prometheus/prometheus/discovery/digitalocean/digitalocean.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/digitalocean/metrics.go b/vendor/github.com/prometheus/prometheus/discovery/digitalocean/metrics.go index 7f68b39e5..4b11b825e 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/digitalocean/metrics.go +++ b/vendor/github.com/prometheus/prometheus/discovery/digitalocean/metrics.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/discoverer_metrics_noop.go b/vendor/github.com/prometheus/prometheus/discovery/discoverer_metrics_noop.go index 4321204b6..b75474dfe 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/discoverer_metrics_noop.go +++ b/vendor/github.com/prometheus/prometheus/discovery/discoverer_metrics_noop.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/discovery.go b/vendor/github.com/prometheus/prometheus/discovery/discovery.go index e643cb10a..c4f8c8d45 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/discovery.go +++ b/vendor/github.com/prometheus/prometheus/discovery/discovery.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/dns/dns.go b/vendor/github.com/prometheus/prometheus/discovery/dns/dns.go index 1e0a78698..4d9200d73 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/dns/dns.go +++ b/vendor/github.com/prometheus/prometheus/discovery/dns/dns.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/dns/metrics.go b/vendor/github.com/prometheus/prometheus/discovery/dns/metrics.go index 27c96b53e..b65db5e6c 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/dns/metrics.go +++ b/vendor/github.com/prometheus/prometheus/discovery/dns/metrics.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/file/file.go b/vendor/github.com/prometheus/prometheus/discovery/file/file.go index e0225891c..c654297e0 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/file/file.go +++ b/vendor/github.com/prometheus/prometheus/discovery/file/file.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/file/metrics.go b/vendor/github.com/prometheus/prometheus/discovery/file/metrics.go index 3e3df7bbf..0371338d4 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/file/metrics.go +++ b/vendor/github.com/prometheus/prometheus/discovery/file/metrics.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/gce/gce.go b/vendor/github.com/prometheus/prometheus/discovery/gce/gce.go index 106028ff9..96eed2b27 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/gce/gce.go +++ b/vendor/github.com/prometheus/prometheus/discovery/gce/gce.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/gce/metrics.go b/vendor/github.com/prometheus/prometheus/discovery/gce/metrics.go index 7ea69b1a8..c4020f0a5 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/gce/metrics.go +++ b/vendor/github.com/prometheus/prometheus/discovery/gce/metrics.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/kubernetes/endpoints.go b/vendor/github.com/prometheus/prometheus/discovery/kubernetes/endpoints.go index 21c401da2..4edcf9d4f 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/kubernetes/endpoints.go +++ b/vendor/github.com/prometheus/prometheus/discovery/kubernetes/endpoints.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/kubernetes/endpointslice.go b/vendor/github.com/prometheus/prometheus/discovery/kubernetes/endpointslice.go index 85b579438..a6cfb0706 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/kubernetes/endpointslice.go +++ b/vendor/github.com/prometheus/prometheus/discovery/kubernetes/endpointslice.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/kubernetes/ingress.go b/vendor/github.com/prometheus/prometheus/discovery/kubernetes/ingress.go index 551453e51..985cc8f13 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/kubernetes/ingress.go +++ b/vendor/github.com/prometheus/prometheus/discovery/kubernetes/ingress.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/kubernetes/kubernetes.go b/vendor/github.com/prometheus/prometheus/discovery/kubernetes/kubernetes.go index 1a6f965ec..678f287ef 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/kubernetes/kubernetes.go +++ b/vendor/github.com/prometheus/prometheus/discovery/kubernetes/kubernetes.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/kubernetes/metrics.go b/vendor/github.com/prometheus/prometheus/discovery/kubernetes/metrics.go index ba3cb1d32..cdf158a03 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/kubernetes/metrics.go +++ b/vendor/github.com/prometheus/prometheus/discovery/kubernetes/metrics.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/kubernetes/node.go b/vendor/github.com/prometheus/prometheus/discovery/kubernetes/node.go index 131cdcc9e..cbc69dd0c 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/kubernetes/node.go +++ b/vendor/github.com/prometheus/prometheus/discovery/kubernetes/node.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/kubernetes/pod.go b/vendor/github.com/prometheus/prometheus/discovery/kubernetes/pod.go index 03089e39d..1fed78b3a 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/kubernetes/pod.go +++ b/vendor/github.com/prometheus/prometheus/discovery/kubernetes/pod.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/kubernetes/service.go b/vendor/github.com/prometheus/prometheus/discovery/kubernetes/service.go index d676490d6..ac2d42fc7 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/kubernetes/service.go +++ b/vendor/github.com/prometheus/prometheus/discovery/kubernetes/service.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/manager.go b/vendor/github.com/prometheus/prometheus/discovery/manager.go index 431050aa0..3f2b2db65 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/manager.go +++ b/vendor/github.com/prometheus/prometheus/discovery/manager.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/marathon/marathon.go b/vendor/github.com/prometheus/prometheus/discovery/marathon/marathon.go index 438b8915d..878d40437 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/marathon/marathon.go +++ b/vendor/github.com/prometheus/prometheus/discovery/marathon/marathon.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/marathon/metrics.go b/vendor/github.com/prometheus/prometheus/discovery/marathon/metrics.go index 40e2ade55..3d3d57d9a 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/marathon/metrics.go +++ b/vendor/github.com/prometheus/prometheus/discovery/marathon/metrics.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/metrics.go b/vendor/github.com/prometheus/prometheus/discovery/metrics.go index 356be1ddc..2a3734fb2 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/metrics.go +++ b/vendor/github.com/prometheus/prometheus/discovery/metrics.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/metrics_k8s_client.go b/vendor/github.com/prometheus/prometheus/discovery/metrics_k8s_client.go index 19dfd4e24..3642eac56 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/metrics_k8s_client.go +++ b/vendor/github.com/prometheus/prometheus/discovery/metrics_k8s_client.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/metrics_refresh.go b/vendor/github.com/prometheus/prometheus/discovery/metrics_refresh.go index 9f3eb27b4..11092d9f9 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/metrics_refresh.go +++ b/vendor/github.com/prometheus/prometheus/discovery/metrics_refresh.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/moby/docker.go b/vendor/github.com/prometheus/prometheus/discovery/moby/docker.go index ec1187278..aa1cd2eb4 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/moby/docker.go +++ b/vendor/github.com/prometheus/prometheus/discovery/moby/docker.go @@ -1,4 +1,4 @@ -// Copyright 2021 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/moby/dockerswarm.go b/vendor/github.com/prometheus/prometheus/discovery/moby/dockerswarm.go index 2761e891b..5cb12279d 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/moby/dockerswarm.go +++ b/vendor/github.com/prometheus/prometheus/discovery/moby/dockerswarm.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/moby/metrics_docker.go b/vendor/github.com/prometheus/prometheus/discovery/moby/metrics_docker.go index 716f52b60..8c2518a75 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/moby/metrics_docker.go +++ b/vendor/github.com/prometheus/prometheus/discovery/moby/metrics_docker.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/moby/metrics_dockerswarm.go b/vendor/github.com/prometheus/prometheus/discovery/moby/metrics_dockerswarm.go index 17dd30d1b..e4682b032 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/moby/metrics_dockerswarm.go +++ b/vendor/github.com/prometheus/prometheus/discovery/moby/metrics_dockerswarm.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/moby/network.go b/vendor/github.com/prometheus/prometheus/discovery/moby/network.go index ea1ca66bc..02db2b8a1 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/moby/network.go +++ b/vendor/github.com/prometheus/prometheus/discovery/moby/network.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/moby/nodes.go b/vendor/github.com/prometheus/prometheus/discovery/moby/nodes.go index a11afeee2..76e090c80 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/moby/nodes.go +++ b/vendor/github.com/prometheus/prometheus/discovery/moby/nodes.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/moby/services.go b/vendor/github.com/prometheus/prometheus/discovery/moby/services.go index 0698c01e6..558d544e2 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/moby/services.go +++ b/vendor/github.com/prometheus/prometheus/discovery/moby/services.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/moby/tasks.go b/vendor/github.com/prometheus/prometheus/discovery/moby/tasks.go index 8a3dbe810..d4e3678ee 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/moby/tasks.go +++ b/vendor/github.com/prometheus/prometheus/discovery/moby/tasks.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/openstack/hypervisor.go b/vendor/github.com/prometheus/prometheus/discovery/openstack/hypervisor.go index e7a636205..141b77c70 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/openstack/hypervisor.go +++ b/vendor/github.com/prometheus/prometheus/discovery/openstack/hypervisor.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/openstack/instance.go b/vendor/github.com/prometheus/prometheus/discovery/openstack/instance.go index 58bf15455..2a6a777e9 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/openstack/instance.go +++ b/vendor/github.com/prometheus/prometheus/discovery/openstack/instance.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/openstack/loadbalancer.go b/vendor/github.com/prometheus/prometheus/discovery/openstack/loadbalancer.go index 254b713cd..3b2def0d6 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/openstack/loadbalancer.go +++ b/vendor/github.com/prometheus/prometheus/discovery/openstack/loadbalancer.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/openstack/metrics.go b/vendor/github.com/prometheus/prometheus/discovery/openstack/metrics.go index 664f5ea6b..01e7ab3ad 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/openstack/metrics.go +++ b/vendor/github.com/prometheus/prometheus/discovery/openstack/metrics.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/openstack/openstack.go b/vendor/github.com/prometheus/prometheus/discovery/openstack/openstack.go index 61dff847c..ce365e6cd 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/openstack/openstack.go +++ b/vendor/github.com/prometheus/prometheus/discovery/openstack/openstack.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/refresh/refresh.go b/vendor/github.com/prometheus/prometheus/discovery/refresh/refresh.go index 0613fd6c6..3e766d1c8 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/refresh/refresh.go +++ b/vendor/github.com/prometheus/prometheus/discovery/refresh/refresh.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/registry.go b/vendor/github.com/prometheus/prometheus/discovery/registry.go index b3b82cdee..04145e72e 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/registry.go +++ b/vendor/github.com/prometheus/prometheus/discovery/registry.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/targetgroup/targetgroup.go b/vendor/github.com/prometheus/prometheus/discovery/targetgroup/targetgroup.go index 5c3b67d6e..4b1670ae1 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/targetgroup/targetgroup.go +++ b/vendor/github.com/prometheus/prometheus/discovery/targetgroup/targetgroup.go @@ -1,4 +1,4 @@ -// Copyright 2013 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/triton/metrics.go b/vendor/github.com/prometheus/prometheus/discovery/triton/metrics.go index ea98eae45..2d4193ee1 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/triton/metrics.go +++ b/vendor/github.com/prometheus/prometheus/discovery/triton/metrics.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/triton/triton.go b/vendor/github.com/prometheus/prometheus/discovery/triton/triton.go index 209e1c4de..b21beef9d 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/triton/triton.go +++ b/vendor/github.com/prometheus/prometheus/discovery/triton/triton.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/util.go b/vendor/github.com/prometheus/prometheus/discovery/util.go index 4e2a08851..064a5312a 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/util.go +++ b/vendor/github.com/prometheus/prometheus/discovery/util.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/discovery/zookeeper/zookeeper.go b/vendor/github.com/prometheus/prometheus/discovery/zookeeper/zookeeper.go index d5239324c..6ac9b25cd 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/zookeeper/zookeeper.go +++ b/vendor/github.com/prometheus/prometheus/discovery/zookeeper/zookeeper.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/model/exemplar/exemplar.go b/vendor/github.com/prometheus/prometheus/model/exemplar/exemplar.go index d03940f1b..5db7c46a6 100644 --- a/vendor/github.com/prometheus/prometheus/model/exemplar/exemplar.go +++ b/vendor/github.com/prometheus/prometheus/model/exemplar/exemplar.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/model/histogram/float_histogram.go b/vendor/github.com/prometheus/prometheus/model/histogram/float_histogram.go index 91fcac1cf..d457d8ab2 100644 --- a/vendor/github.com/prometheus/prometheus/model/histogram/float_histogram.go +++ b/vendor/github.com/prometheus/prometheus/model/histogram/float_histogram.go @@ -1,4 +1,4 @@ -// Copyright 2021 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -18,6 +18,8 @@ import ( "fmt" "math" "strings" + + "github.com/prometheus/prometheus/util/kahansum" ) // FloatHistogram is similar to Histogram but uses float64 for all @@ -353,7 +355,7 @@ func (h *FloatHistogram) Add(other *FloatHistogram) (res *FloatHistogram, counte } counterResetCollision = h.adjustCounterReset(other) if !h.UsesCustomBuckets() { - otherZeroCount := h.reconcileZeroBuckets(other) + otherZeroCount, _ := h.reconcileZeroBuckets(other, nil) h.ZeroCount += otherZeroCount } h.Count += other.Count @@ -374,11 +376,11 @@ func (h *FloatHistogram) Add(other *FloatHistogram) (res *FloatHistogram, counte intersectedBounds := intersectCustomBucketBounds(h.CustomValues, other.CustomValues) // Add with mapping - maps both histograms to intersected layout. - h.PositiveSpans, h.PositiveBuckets = addCustomBucketsWithMismatches( + h.PositiveSpans, h.PositiveBuckets, _ = addCustomBucketsWithMismatches( false, hPositiveSpans, hPositiveBuckets, h.CustomValues, otherPositiveSpans, otherPositiveBuckets, other.CustomValues, - intersectedBounds) + nil, intersectedBounds) h.CustomValues = intersectedBounds } return h, counterResetCollision, nhcbBoundsReconciled, nil @@ -408,6 +410,121 @@ func (h *FloatHistogram) Add(other *FloatHistogram) (res *FloatHistogram, counte return h, counterResetCollision, nhcbBoundsReconciled, nil } +// KahanAdd works like Add but using the Kahan summation algorithm to minimize numerical errors. +// c is a histogram holding the Kahan compensation term. It is modified in-place if non-nil. +// If c is nil, a new compensation histogram is created inside the function. In this case, +// the caller must use the returned updatedC, because the original c variable is not modified. +func (h *FloatHistogram) KahanAdd(other, c *FloatHistogram) (updatedC *FloatHistogram, counterResetCollision, nhcbBoundsReconciled bool, err error) { + if err := h.checkSchemaAndBounds(other); err != nil { + return nil, false, false, err + } + + counterResetCollision = h.adjustCounterReset(other) + + if c == nil { + c = h.newCompensationHistogram() + } + if !h.UsesCustomBuckets() { + otherZeroCount, otherCZeroCount := h.reconcileZeroBuckets(other, c) + h.ZeroCount, c.ZeroCount = kahansum.Inc(otherZeroCount, h.ZeroCount, c.ZeroCount) + h.ZeroCount, c.ZeroCount = kahansum.Inc(otherCZeroCount, h.ZeroCount, c.ZeroCount) + } + h.Count, c.Count = kahansum.Inc(other.Count, h.Count, c.Count) + h.Sum, c.Sum = kahansum.Inc(other.Sum, h.Sum, c.Sum) + + var ( + hPositiveSpans = h.PositiveSpans + hPositiveBuckets = h.PositiveBuckets + otherPositiveSpans = other.PositiveSpans + otherPositiveBuckets = other.PositiveBuckets + cPositiveBuckets = c.PositiveBuckets + ) + + if h.UsesCustomBuckets() { + if CustomBucketBoundsMatch(h.CustomValues, other.CustomValues) { + h.PositiveSpans, h.PositiveBuckets, c.PositiveBuckets = kahanAddBuckets( + h.Schema, h.ZeroThreshold, false, + hPositiveSpans, hPositiveBuckets, + otherPositiveSpans, otherPositiveBuckets, + cPositiveBuckets, nil, + ) + } else { + nhcbBoundsReconciled = true + intersectedBounds := intersectCustomBucketBounds(h.CustomValues, other.CustomValues) + + // Add with mapping - maps both histograms to intersected layout. + h.PositiveSpans, h.PositiveBuckets, c.PositiveBuckets = addCustomBucketsWithMismatches( + false, + hPositiveSpans, hPositiveBuckets, h.CustomValues, + otherPositiveSpans, otherPositiveBuckets, other.CustomValues, + cPositiveBuckets, intersectedBounds) + h.CustomValues = intersectedBounds + c.CustomValues = intersectedBounds + } + c.PositiveSpans = h.PositiveSpans + return c, counterResetCollision, nhcbBoundsReconciled, nil + } + + otherC := other.newCompensationHistogram() + + var ( + hNegativeSpans = h.NegativeSpans + hNegativeBuckets = h.NegativeBuckets + otherNegativeSpans = other.NegativeSpans + otherNegativeBuckets = other.NegativeBuckets + cNegativeBuckets = c.NegativeBuckets + otherCPositiveBuckets = otherC.PositiveBuckets + otherCNegativeBuckets = otherC.NegativeBuckets + ) + + switch { + case other.Schema < h.Schema: + hPositiveSpans, hPositiveBuckets, cPositiveBuckets = kahanReduceResolution( + hPositiveSpans, hPositiveBuckets, cPositiveBuckets, + h.Schema, other.Schema, + true, + ) + hNegativeSpans, hNegativeBuckets, cNegativeBuckets = kahanReduceResolution( + hNegativeSpans, hNegativeBuckets, cNegativeBuckets, + h.Schema, other.Schema, + true, + ) + h.Schema = other.Schema + + case other.Schema > h.Schema: + otherPositiveSpans, otherPositiveBuckets, otherCPositiveBuckets = kahanReduceResolution( + otherPositiveSpans, otherPositiveBuckets, otherCPositiveBuckets, + other.Schema, h.Schema, + false, + ) + otherNegativeSpans, otherNegativeBuckets, otherCNegativeBuckets = kahanReduceResolution( + otherNegativeSpans, otherNegativeBuckets, otherCNegativeBuckets, + other.Schema, h.Schema, + false, + ) + } + + h.PositiveSpans, h.PositiveBuckets, c.PositiveBuckets = kahanAddBuckets( + h.Schema, h.ZeroThreshold, false, + hPositiveSpans, hPositiveBuckets, + otherPositiveSpans, otherPositiveBuckets, + cPositiveBuckets, otherCPositiveBuckets, + ) + h.NegativeSpans, h.NegativeBuckets, c.NegativeBuckets = kahanAddBuckets( + h.Schema, h.ZeroThreshold, false, + hNegativeSpans, hNegativeBuckets, + otherNegativeSpans, otherNegativeBuckets, + cNegativeBuckets, otherCNegativeBuckets, + ) + + c.Schema = h.Schema + c.ZeroThreshold = h.ZeroThreshold + c.PositiveSpans = h.PositiveSpans + c.NegativeSpans = h.NegativeSpans + + return c, counterResetCollision, nhcbBoundsReconciled, nil +} + // Sub works like Add but subtracts the other histogram. It uses the same logic // to adjust the counter reset hint. This is useful where this method is used // for incremental mean calculation. However, if it is used for the actual "-" @@ -419,7 +536,7 @@ func (h *FloatHistogram) Sub(other *FloatHistogram) (res *FloatHistogram, counte } counterResetCollision = h.adjustCounterReset(other) if !h.UsesCustomBuckets() { - otherZeroCount := h.reconcileZeroBuckets(other) + otherZeroCount, _ := h.reconcileZeroBuckets(other, nil) h.ZeroCount -= otherZeroCount } h.Count -= other.Count @@ -440,11 +557,11 @@ func (h *FloatHistogram) Sub(other *FloatHistogram) (res *FloatHistogram, counte intersectedBounds := intersectCustomBucketBounds(h.CustomValues, other.CustomValues) // Subtract with mapping - maps both histograms to intersected layout. - h.PositiveSpans, h.PositiveBuckets = addCustomBucketsWithMismatches( + h.PositiveSpans, h.PositiveBuckets, _ = addCustomBucketsWithMismatches( true, hPositiveSpans, hPositiveBuckets, h.CustomValues, otherPositiveSpans, otherPositiveBuckets, other.CustomValues, - intersectedBounds) + nil, intersectedBounds) h.CustomValues = intersectedBounds } return h, counterResetCollision, nhcbBoundsReconciled, nil @@ -484,7 +601,7 @@ func (h *FloatHistogram) Sub(other *FloatHistogram) (res *FloatHistogram, counte // supposed to be used according to the schema. func (h *FloatHistogram) Equals(h2 *FloatHistogram) bool { if h2 == nil { - return false + return h == nil } if h.Schema != h2.Schema || @@ -576,15 +693,28 @@ func (h *FloatHistogram) Size() int { // easier to iterate through. Still, the safest bet is to use maxEmptyBuckets==0 // and only use a larger number if you know what you are doing. func (h *FloatHistogram) Compact(maxEmptyBuckets int) *FloatHistogram { - h.PositiveBuckets, h.PositiveSpans = compactBuckets( - h.PositiveBuckets, h.PositiveSpans, maxEmptyBuckets, false, + h.PositiveBuckets, _, h.PositiveSpans = compactBuckets( + h.PositiveBuckets, nil, h.PositiveSpans, maxEmptyBuckets, false, ) - h.NegativeBuckets, h.NegativeSpans = compactBuckets( - h.NegativeBuckets, h.NegativeSpans, maxEmptyBuckets, false, + h.NegativeBuckets, _, h.NegativeSpans = compactBuckets( + h.NegativeBuckets, nil, h.NegativeSpans, maxEmptyBuckets, false, ) return h } +// kahanCompact works like Compact, but it is specialized for FloatHistogram's KahanAdd method. +// c is a histogram holding the Kahan compensation term. +func (h *FloatHistogram) kahanCompact(maxEmptyBuckets int, c *FloatHistogram, +) (updatedH, updatedC *FloatHistogram) { + h.PositiveBuckets, c.PositiveBuckets, h.PositiveSpans = compactBuckets( + h.PositiveBuckets, c.PositiveBuckets, h.PositiveSpans, maxEmptyBuckets, false, + ) + h.NegativeBuckets, c.NegativeBuckets, h.NegativeSpans = compactBuckets( + h.NegativeBuckets, c.NegativeBuckets, h.NegativeSpans, maxEmptyBuckets, false, + ) + return h, c +} + // DetectReset returns true if the receiving histogram is missing any buckets // that have a non-zero population in the provided previous histogram. It also // returns true if any count (in any bucket, in the zero count, or in the count @@ -652,7 +782,7 @@ func (h *FloatHistogram) DetectReset(previous *FloatHistogram) bool { // ZeroThreshold decreased. return true } - previousZeroCount, newThreshold := previous.zeroCountForLargerThreshold(h.ZeroThreshold) + previousZeroCount, newThreshold, _ := previous.zeroCountForLargerThreshold(h.ZeroThreshold, nil) if newThreshold != h.ZeroThreshold { // ZeroThreshold is within a populated bucket in previous // histogram. @@ -847,30 +977,42 @@ func (h *FloatHistogram) Validate() error { } // zeroCountForLargerThreshold returns what the histogram's zero count would be -// if the ZeroThreshold had the provided larger (or equal) value. If the -// provided value is less than the histogram's ZeroThreshold, the method panics. +// if the ZeroThreshold had the provided larger (or equal) value. It also returns the +// zero count of the compensation histogram `c` if provided (used for Kahan summation). +// +// If the provided ZeroThreshold is less than the histogram's ZeroThreshold, the method panics. // If the largerThreshold ends up within a populated bucket of the histogram, it // is adjusted upwards to the lower limit of that bucket (all in terms of // absolute values) and that bucket's count is included in the returned // count. The adjusted threshold is returned, too. -func (h *FloatHistogram) zeroCountForLargerThreshold(largerThreshold float64) (count, threshold float64) { +func (h *FloatHistogram) zeroCountForLargerThreshold( + largerThreshold float64, c *FloatHistogram) (hZeroCount, threshold, cZeroCount float64, +) { + if c != nil { + cZeroCount = c.ZeroCount + } // Fast path. if largerThreshold == h.ZeroThreshold { - return h.ZeroCount, largerThreshold + return h.ZeroCount, largerThreshold, cZeroCount } if largerThreshold < h.ZeroThreshold { panic(fmt.Errorf("new threshold %f is less than old threshold %f", largerThreshold, h.ZeroThreshold)) } outer: for { - count = h.ZeroCount + hZeroCount = h.ZeroCount i := h.PositiveBucketIterator() + bucketsIdx := 0 for i.Next() { b := i.At() if b.Lower >= largerThreshold { break } - count += b.Count // Bucket to be merged into zero bucket. + // Bucket to be merged into zero bucket. + hZeroCount, cZeroCount = kahansum.Inc(b.Count, hZeroCount, cZeroCount) + if c != nil { + hZeroCount, cZeroCount = kahansum.Inc(c.PositiveBuckets[bucketsIdx], hZeroCount, cZeroCount) + } if b.Upper > largerThreshold { // New threshold ended up within a bucket. if it's // populated, we need to adjust largerThreshold before @@ -880,14 +1022,20 @@ outer: } break } + bucketsIdx++ } i = h.NegativeBucketIterator() + bucketsIdx = 0 for i.Next() { b := i.At() if b.Upper <= -largerThreshold { break } - count += b.Count // Bucket to be merged into zero bucket. + // Bucket to be merged into zero bucket. + hZeroCount, cZeroCount = kahansum.Inc(b.Count, hZeroCount, cZeroCount) + if c != nil { + hZeroCount, cZeroCount = kahansum.Inc(c.NegativeBuckets[bucketsIdx], hZeroCount, cZeroCount) + } if b.Lower < -largerThreshold { // New threshold ended up within a bucket. If // it's populated, we need to adjust @@ -900,15 +1048,17 @@ outer: } break } + bucketsIdx++ } - return count, largerThreshold + return hZeroCount, largerThreshold, cZeroCount } } // trimBucketsInZeroBucket removes all buckets that are within the zero // bucket. It assumes that the zero threshold is at a bucket boundary and that // the counts in the buckets to remove are already part of the zero count. -func (h *FloatHistogram) trimBucketsInZeroBucket() { +// c is a histogram holding the Kahan compensation term. +func (h *FloatHistogram) trimBucketsInZeroBucket(c *FloatHistogram) { i := h.PositiveBucketIterator() bucketsIdx := 0 for i.Next() { @@ -917,6 +1067,9 @@ func (h *FloatHistogram) trimBucketsInZeroBucket() { break } h.PositiveBuckets[bucketsIdx] = 0 + if c != nil { + c.PositiveBuckets[bucketsIdx] = 0 + } bucketsIdx++ } i = h.NegativeBucketIterator() @@ -927,34 +1080,46 @@ func (h *FloatHistogram) trimBucketsInZeroBucket() { break } h.NegativeBuckets[bucketsIdx] = 0 + if c != nil { + c.NegativeBuckets[bucketsIdx] = 0 + } bucketsIdx++ } // We are abusing Compact to trim the buckets set to zero // above. Premature compacting could cause additional cost, but this // code path is probably rarely used anyway. - h.Compact(0) + if c != nil { + h.kahanCompact(0, c) + } else { + h.Compact(0) + } } // reconcileZeroBuckets finds a zero bucket large enough to include the zero // buckets of both histograms (the receiving histogram and the other histogram) // with a zero threshold that is not within a populated bucket in either -// histogram. This method modifies the receiving histogram accordingly, but -// leaves the other histogram as is. Instead, it returns the zero count the -// other histogram would have if it were modified. -func (h *FloatHistogram) reconcileZeroBuckets(other *FloatHistogram) float64 { - otherZeroCount := other.ZeroCount +// histogram. This method modifies the receiving histogram accordingly, and +// also modifies the compensation histogram `c` (used for Kahan summation) if provided, +// but leaves the other histogram as is. Instead, it returns the zero count the +// other histogram would have if it were modified, as well as its Kahan compensation term. +func (h *FloatHistogram) reconcileZeroBuckets(other, c *FloatHistogram) (otherZeroCount, otherCZeroCount float64) { + otherZeroCount = other.ZeroCount otherZeroThreshold := other.ZeroThreshold for otherZeroThreshold != h.ZeroThreshold { if h.ZeroThreshold > otherZeroThreshold { - otherZeroCount, otherZeroThreshold = other.zeroCountForLargerThreshold(h.ZeroThreshold) + otherZeroCount, otherZeroThreshold, otherCZeroCount = other.zeroCountForLargerThreshold(h.ZeroThreshold, nil) } if otherZeroThreshold > h.ZeroThreshold { - h.ZeroCount, h.ZeroThreshold = h.zeroCountForLargerThreshold(otherZeroThreshold) - h.trimBucketsInZeroBucket() + var cZeroCount float64 + h.ZeroCount, h.ZeroThreshold, cZeroCount = h.zeroCountForLargerThreshold(otherZeroThreshold, c) + if c != nil { + c.ZeroCount = cZeroCount + } + h.trimBucketsInZeroBucket(c) } } - return otherZeroCount + return otherZeroCount, otherCZeroCount } // floatBucketIterator is a low-level constructor for bucket iterators. @@ -1369,6 +1534,145 @@ func addBuckets( return spansA, bucketsA } +// kahanAddBuckets works like addBuckets but it is used in FloatHistogram's KahanAdd method +// and takes additional arguments, compensationBucketsA and compensationBucketsB, +// which hold the Kahan compensation values associated with histograms A and B. +// It returns the resulting spans/buckets and compensation buckets. +func kahanAddBuckets( + schema int32, threshold float64, negative bool, + spansA []Span, bucketsA []float64, + spansB []Span, bucketsB []float64, + compensationBucketsA, compensationBucketsB []float64, +) (newSpans []Span, newBucketsA, newBucketsC []float64) { + var ( + iSpan = -1 + iBucket = -1 + iInSpan int32 + indexA int32 + indexB int32 + bIdxB int + bucketB float64 + compensationBucketB float64 + deltaIndex int32 + lowerThanThreshold = true + ) + + for _, spanB := range spansB { + indexB += spanB.Offset + for j := 0; j < int(spanB.Length); j++ { + if lowerThanThreshold && IsExponentialSchema(schema) && getBoundExponential(indexB, schema) <= threshold { + goto nextLoop + } + lowerThanThreshold = false + + bucketB = bucketsB[bIdxB] + if compensationBucketsB != nil { + compensationBucketB = compensationBucketsB[bIdxB] + } + if negative { + bucketB *= -1 + compensationBucketB *= -1 + } + + if iSpan == -1 { + if len(spansA) == 0 || spansA[0].Offset > indexB { + // Add bucket before all others. + bucketsA = append(bucketsA, 0) + copy(bucketsA[1:], bucketsA) + bucketsA[0] = bucketB + compensationBucketsA = append(compensationBucketsA, 0) + copy(compensationBucketsA[1:], compensationBucketsA) + compensationBucketsA[0] = compensationBucketB + if len(spansA) > 0 && spansA[0].Offset == indexB+1 { + spansA[0].Length++ + spansA[0].Offset-- + goto nextLoop + } + spansA = append(spansA, Span{}) + copy(spansA[1:], spansA) + spansA[0] = Span{Offset: indexB, Length: 1} + if len(spansA) > 1 { + // Convert the absolute offset in the formerly + // first span to a relative offset. + spansA[1].Offset -= indexB + 1 + } + goto nextLoop + } else if spansA[0].Offset == indexB { + // Just add to first bucket. + bucketsA[0], compensationBucketsA[0] = kahansum.Inc(bucketB, bucketsA[0], compensationBucketsA[0]) + bucketsA[0], compensationBucketsA[0] = kahansum.Inc(compensationBucketB, bucketsA[0], compensationBucketsA[0]) + goto nextLoop + } + iSpan, iBucket, iInSpan = 0, 0, 0 + indexA = spansA[0].Offset + } + deltaIndex = indexB - indexA + for { + remainingInSpan := int32(spansA[iSpan].Length) - iInSpan + if deltaIndex < remainingInSpan { + // Bucket is in current span. + iBucket += int(deltaIndex) + iInSpan += deltaIndex + bucketsA[iBucket], compensationBucketsA[iBucket] = kahansum.Inc(bucketB, bucketsA[iBucket], compensationBucketsA[iBucket]) + bucketsA[iBucket], compensationBucketsA[iBucket] = kahansum.Inc(compensationBucketB, bucketsA[iBucket], compensationBucketsA[iBucket]) + break + } + deltaIndex -= remainingInSpan + iBucket += int(remainingInSpan) + iSpan++ + if iSpan == len(spansA) || deltaIndex < spansA[iSpan].Offset { + // Bucket is in gap behind previous span (or there are no further spans). + bucketsA = append(bucketsA, 0) + copy(bucketsA[iBucket+1:], bucketsA[iBucket:]) + bucketsA[iBucket] = bucketB + compensationBucketsA = append(compensationBucketsA, 0) + copy(compensationBucketsA[iBucket+1:], compensationBucketsA[iBucket:]) + compensationBucketsA[iBucket] = compensationBucketB + switch { + case deltaIndex == 0: + // Directly after previous span, extend previous span. + if iSpan < len(spansA) { + spansA[iSpan].Offset-- + } + iSpan-- + iInSpan = int32(spansA[iSpan].Length) + spansA[iSpan].Length++ + goto nextLoop + case iSpan < len(spansA) && deltaIndex == spansA[iSpan].Offset-1: + // Directly before next span, extend next span. + iInSpan = 0 + spansA[iSpan].Offset-- + spansA[iSpan].Length++ + goto nextLoop + default: + // No next span, or next span is not directly adjacent to new bucket. + // Add new span. + iInSpan = 0 + if iSpan < len(spansA) { + spansA[iSpan].Offset -= deltaIndex + 1 + } + spansA = append(spansA, Span{}) + copy(spansA[iSpan+1:], spansA[iSpan:]) + spansA[iSpan] = Span{Length: 1, Offset: deltaIndex} + goto nextLoop + } + } else { + // Try start of next span. + deltaIndex -= spansA[iSpan].Offset + iInSpan = 0 + } + } + + nextLoop: + indexA = indexB + indexB++ + bIdxB++ + } + } + + return spansA, bucketsA, compensationBucketsA +} + // floatBucketsMatch compares bucket values of two float histograms using binary float comparison // and returns true if all values match. func floatBucketsMatch(b1, b2 []float64) bool { @@ -1496,15 +1800,18 @@ func intersectCustomBucketBounds(boundsA, boundsB []float64) []float64 { // addCustomBucketsWithMismatches handles adding/subtracting custom bucket histograms // with mismatched bucket layouts by mapping both to an intersected layout. +// It also processes the Kahan compensation term if provided. func addCustomBucketsWithMismatches( negative bool, spansA []Span, bucketsA, boundsA []float64, spansB []Span, bucketsB, boundsB []float64, + bucketsC []float64, intersectedBounds []float64, -) ([]Span, []float64) { +) ([]Span, []float64, []float64) { targetBuckets := make([]float64, len(intersectedBounds)+1) + cTargetBuckets := make([]float64, len(intersectedBounds)+1) - mapBuckets := func(spans []Span, buckets, bounds []float64, negative bool) { + mapBuckets := func(spans []Span, buckets, bounds []float64, negative, withCompensation bool) { srcIdx := 0 bucketIdx := 0 intersectIdx := 0 @@ -1530,9 +1837,12 @@ func addCustomBucketsWithMismatches( } if negative { - targetBuckets[targetIdx] -= value + targetBuckets[targetIdx], cTargetBuckets[targetIdx] = kahansum.Dec(value, targetBuckets[targetIdx], cTargetBuckets[targetIdx]) } else { - targetBuckets[targetIdx] += value + targetBuckets[targetIdx], cTargetBuckets[targetIdx] = kahansum.Inc(value, targetBuckets[targetIdx], cTargetBuckets[targetIdx]) + if withCompensation && bucketsC != nil { + targetBuckets[targetIdx], cTargetBuckets[targetIdx] = kahansum.Inc(bucketsC[bucketIdx], targetBuckets[targetIdx], cTargetBuckets[targetIdx]) + } } } srcIdx++ @@ -1541,21 +1851,23 @@ func addCustomBucketsWithMismatches( } } - // Map both histograms to the intersected layout. - mapBuckets(spansA, bucketsA, boundsA, false) - mapBuckets(spansB, bucketsB, boundsB, negative) + // Map histograms to the intersected layout. + mapBuckets(spansA, bucketsA, boundsA, false, true) + mapBuckets(spansB, bucketsB, boundsB, negative, false) // Build spans and buckets, excluding zero-valued buckets from the final result. - destSpans := spansA[:0] // Reuse spansA capacity for destSpans since we don't need it anymore. - destBuckets := targetBuckets[:0] // Reuse targetBuckets capacity for destBuckets since it's guaranteed to be large enough. + destSpans := spansA[:0] // Reuse spansA capacity for destSpans since we don't need it anymore. + destBuckets := targetBuckets[:0] // Reuse targetBuckets capacity for destBuckets since it's guaranteed to be large enough. + cDestBuckets := cTargetBuckets[:0] // Reuse cTargetBuckets capacity for cDestBuckets since it's guaranteed to be large enough. lastIdx := int32(-1) - for i, count := range targetBuckets { - if count == 0 { + for i := range targetBuckets { + if targetBuckets[i] == 0 && cTargetBuckets[i] == 0 { continue } - destBuckets = append(destBuckets, count) + destBuckets = append(destBuckets, targetBuckets[i]) + cDestBuckets = append(cDestBuckets, cTargetBuckets[i]) idx := int32(i) if len(destSpans) > 0 && idx == lastIdx+1 { @@ -1578,7 +1890,7 @@ func addCustomBucketsWithMismatches( lastIdx = idx } - return destSpans, destBuckets + return destSpans, destBuckets, cDestBuckets } // ReduceResolution reduces the float histogram's spans, buckets into target schema. @@ -1618,6 +1930,121 @@ func (h *FloatHistogram) ReduceResolution(targetSchema int32) error { return nil } +// kahanReduceResolution works like reduceResolution, but it is specialized for FloatHistogram's KahanAdd method. +// Unlike reduceResolution, which supports both float and integer buckets, this function only operates on float buckets. +// It also takes an additional argument, originCompensationBuckets, representing the compensation buckets for the origin histogram. +// Modifies both the origin histogram buckets and their associated compensation buckets. +func kahanReduceResolution( + originSpans []Span, + originReceivingBuckets []float64, + originCompensationBuckets []float64, + originSchema, + targetSchema int32, + inplace bool, +) (newSpans []Span, newReceivingBuckets, newCompensationBuckets []float64) { + var ( + targetSpans []Span // The spans in the target schema. + targetReceivingBuckets []float64 // The receiving bucket counts in the target schema. + targetCompensationBuckets []float64 // The compensation bucket counts in the target schema. + bucketIdx int32 // The index of bucket in the origin schema. + bucketCountIdx int // The position of a bucket in origin bucket count slice `originBuckets`. + targetBucketIdx int32 // The index of bucket in the target schema. + lastTargetBucketIdx int32 // The index of the last added target bucket. + ) + + if inplace { + // Slice reuse is safe because when reducing the resolution, + // target slices don't grow faster than origin slices are being read. + targetSpans = originSpans[:0] + targetReceivingBuckets = originReceivingBuckets[:0] + targetCompensationBuckets = originCompensationBuckets[:0] + } + + for _, span := range originSpans { + // Determine the index of the first bucket in this span. + bucketIdx += span.Offset + for j := 0; j < int(span.Length); j++ { + // Determine the index of the bucket in the target schema from the index in the original schema. + targetBucketIdx = targetIdx(bucketIdx, originSchema, targetSchema) + + switch { + case len(targetSpans) == 0: + // This is the first span in the targetSpans. + span := Span{ + Offset: targetBucketIdx, + Length: 1, + } + targetSpans = append(targetSpans, span) + targetReceivingBuckets = append(targetReceivingBuckets, originReceivingBuckets[bucketCountIdx]) + lastTargetBucketIdx = targetBucketIdx + targetCompensationBuckets = append(targetCompensationBuckets, originCompensationBuckets[bucketCountIdx]) + + case lastTargetBucketIdx == targetBucketIdx: + // The current bucket has to be merged into the same target bucket as the previous bucket. + lastBucketIdx := len(targetReceivingBuckets) - 1 + targetReceivingBuckets[lastBucketIdx], targetCompensationBuckets[lastBucketIdx] = kahansum.Inc( + originReceivingBuckets[bucketCountIdx], + targetReceivingBuckets[lastBucketIdx], + targetCompensationBuckets[lastBucketIdx], + ) + targetReceivingBuckets[lastBucketIdx], targetCompensationBuckets[lastBucketIdx] = kahansum.Inc( + originCompensationBuckets[bucketCountIdx], + targetReceivingBuckets[lastBucketIdx], + targetCompensationBuckets[lastBucketIdx], + ) + + case (lastTargetBucketIdx + 1) == targetBucketIdx: + // The current bucket has to go into a new target bucket, + // and that bucket is next to the previous target bucket, + // so we add it to the current target span. + targetSpans[len(targetSpans)-1].Length++ + lastTargetBucketIdx++ + targetReceivingBuckets = append(targetReceivingBuckets, originReceivingBuckets[bucketCountIdx]) + targetCompensationBuckets = append(targetCompensationBuckets, originCompensationBuckets[bucketCountIdx]) + + case (lastTargetBucketIdx + 1) < targetBucketIdx: + // The current bucket has to go into a new target bucket, + // and that bucket is separated by a gap from the previous target bucket, + // so we need to add a new target span. + span := Span{ + Offset: targetBucketIdx - lastTargetBucketIdx - 1, + Length: 1, + } + targetSpans = append(targetSpans, span) + lastTargetBucketIdx = targetBucketIdx + targetReceivingBuckets = append(targetReceivingBuckets, originReceivingBuckets[bucketCountIdx]) + targetCompensationBuckets = append(targetCompensationBuckets, originCompensationBuckets[bucketCountIdx]) + } + + bucketIdx++ + bucketCountIdx++ + } + } + + return targetSpans, targetReceivingBuckets, targetCompensationBuckets +} + +// newCompensationHistogram initializes a new compensation histogram that can be used +// alongside the current FloatHistogram in Kahan summation. +// The compensation histogram is structured to match the receiving histogram's bucket layout +// including its schema, zero threshold and custom values, and it shares spans with the receiving +// histogram. However, the bucket values in the compensation histogram are initialized to zero. +func (h *FloatHistogram) newCompensationHistogram() *FloatHistogram { + c := &FloatHistogram{ + CounterResetHint: h.CounterResetHint, + Schema: h.Schema, + ZeroThreshold: h.ZeroThreshold, + CustomValues: h.CustomValues, + PositiveBuckets: make([]float64, len(h.PositiveBuckets)), + PositiveSpans: h.PositiveSpans, + NegativeSpans: h.NegativeSpans, + } + if !h.UsesCustomBuckets() { + c.NegativeBuckets = make([]float64, len(h.NegativeBuckets)) + } + return c +} + // checkSchemaAndBounds checks if two histograms are compatible because they // both use a standard exponential schema or because they both are NHCBs. func (h *FloatHistogram) checkSchemaAndBounds(other *FloatHistogram) error { @@ -1659,3 +2086,27 @@ func (h *FloatHistogram) adjustCounterReset(other *FloatHistogram) (counterReset } return false } + +// HasOverflow reports whether any of the FloatHistogram's fields contain an infinite value. +// This can happen when aggregating multiple histograms and exceeding float64 capacity. +func (h *FloatHistogram) HasOverflow() bool { + if math.IsInf(h.ZeroCount, 0) || math.IsInf(h.Count, 0) || math.IsInf(h.Sum, 0) { + return true + } + for _, v := range h.PositiveBuckets { + if math.IsInf(v, 0) { + return true + } + } + for _, v := range h.NegativeBuckets { + if math.IsInf(v, 0) { + return true + } + } + for _, v := range h.CustomValues { + if math.IsInf(v, 0) { + return true + } + } + return false +} diff --git a/vendor/github.com/prometheus/prometheus/model/histogram/generic.go b/vendor/github.com/prometheus/prometheus/model/histogram/generic.go index 649db769c..9ec9e9cd4 100644 --- a/vendor/github.com/prometheus/prometheus/model/histogram/generic.go +++ b/vendor/github.com/prometheus/prometheus/model/histogram/generic.go @@ -1,4 +1,4 @@ -// Copyright 2022 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -230,14 +230,29 @@ func (b *baseBucketIterator[BC, IBC]) strippedAt() strippedBucket[BC] { // compactBuckets is a generic function used by both Histogram.Compact and // FloatHistogram.Compact. Set deltaBuckets to true if the provided buckets are // deltas. Set it to false if the buckets contain absolute counts. -func compactBuckets[IBC InternalBucketCount](buckets []IBC, spans []Span, maxEmptyBuckets int, deltaBuckets bool) ([]IBC, []Span) { +// For float histograms, deltaBuckets is always false. +// primaryBuckets hold the main histogram values, while compensationBuckets (if provided) store +// Kahan compensation values. compensationBuckets can only be provided for float histograms +// and are processed in parallel with primaryBuckets to maintain synchronization. +func compactBuckets[IBC InternalBucketCount]( + primaryBuckets []IBC, compensationBuckets []float64, + spans []Span, maxEmptyBuckets int, deltaBuckets bool, +) (updatedPrimaryBuckets []IBC, updatedCompensationBuckets []float64, updatedSpans []Span) { + if deltaBuckets && compensationBuckets != nil { + panic("histogram type mismatch: deltaBuckets cannot be true when compensationBuckets is provided") + } else if compensationBuckets != nil && len(primaryBuckets) != len(compensationBuckets) { + panic(fmt.Errorf( + "primary buckets layout (%v) mismatch against associated compensation buckets layout (%v)", + primaryBuckets, compensationBuckets), + ) + } // Fast path: If there are no empty buckets AND no offset in any span is // <= maxEmptyBuckets AND no span has length 0, there is nothing to do and we can return // immediately. We check that first because it's cheap and presumably // common. nothingToDo := true var currentBucketAbsolute IBC - for _, bucket := range buckets { + for _, bucket := range primaryBuckets { if deltaBuckets { currentBucketAbsolute += bucket } else { @@ -256,7 +271,7 @@ func compactBuckets[IBC InternalBucketCount](buckets []IBC, spans []Span, maxEmp } } if nothingToDo { - return buckets, spans + return primaryBuckets, compensationBuckets, spans } } @@ -268,12 +283,19 @@ func compactBuckets[IBC InternalBucketCount](buckets []IBC, spans []Span, maxEmp emptyBucketsHere := func() int { i := 0 abs := currentBucketAbsolute - for uint32(i)+posInSpan < spans[iSpan].Length && abs == 0 { + comp := float64(0) + if compensationBuckets != nil { + comp = compensationBuckets[iBucket] + } + for uint32(i)+posInSpan < spans[iSpan].Length && abs == 0 && comp == 0 { i++ - if i+iBucket >= len(buckets) { + if i+iBucket >= len(primaryBuckets) { break } - abs = buckets[i+iBucket] + abs = primaryBuckets[i+iBucket] + if compensationBuckets != nil { + comp = compensationBuckets[i+iBucket] + } } return i } @@ -313,11 +335,11 @@ func compactBuckets[IBC InternalBucketCount](buckets []IBC, spans []Span, maxEmp // Cut out empty buckets from start and end of spans, no matter // what. Also cut out empty buckets from the middle of a span but only // if there are more than maxEmptyBuckets consecutive empty buckets. - for iBucket < len(buckets) { + for iBucket < len(primaryBuckets) { if deltaBuckets { - currentBucketAbsolute += buckets[iBucket] + currentBucketAbsolute += primaryBuckets[iBucket] } else { - currentBucketAbsolute = buckets[iBucket] + currentBucketAbsolute = primaryBuckets[iBucket] } if nEmpty := emptyBucketsHere(); nEmpty > 0 { if posInSpan > 0 && @@ -334,11 +356,14 @@ func compactBuckets[IBC InternalBucketCount](buckets []IBC, spans []Span, maxEmp continue } // In all other cases, we cut out the empty buckets. - if deltaBuckets && iBucket+nEmpty < len(buckets) { - currentBucketAbsolute = -buckets[iBucket] - buckets[iBucket+nEmpty] += buckets[iBucket] + if deltaBuckets && iBucket+nEmpty < len(primaryBuckets) { + currentBucketAbsolute = -primaryBuckets[iBucket] + primaryBuckets[iBucket+nEmpty] += primaryBuckets[iBucket] + } + primaryBuckets = append(primaryBuckets[:iBucket], primaryBuckets[iBucket+nEmpty:]...) + if compensationBuckets != nil { + compensationBuckets = append(compensationBuckets[:iBucket], compensationBuckets[iBucket+nEmpty:]...) } - buckets = append(buckets[:iBucket], buckets[iBucket+nEmpty:]...) if posInSpan == 0 { // Start of span. if nEmpty == int(spans[iSpan].Length) { @@ -388,8 +413,8 @@ func compactBuckets[IBC InternalBucketCount](buckets []IBC, spans []Span, maxEmp iSpan++ } } - if maxEmptyBuckets == 0 || len(buckets) == 0 { - return buckets, spans + if maxEmptyBuckets == 0 || len(primaryBuckets) == 0 { + return primaryBuckets, compensationBuckets, spans } // Finally, check if any offsets between spans are small enough to merge @@ -397,7 +422,7 @@ func compactBuckets[IBC InternalBucketCount](buckets []IBC, spans []Span, maxEmp iBucket = int(spans[0].Length) if deltaBuckets { currentBucketAbsolute = 0 - for _, bucket := range buckets[:iBucket] { + for _, bucket := range primaryBuckets[:iBucket] { currentBucketAbsolute += bucket } } @@ -406,7 +431,7 @@ func compactBuckets[IBC InternalBucketCount](buckets []IBC, spans []Span, maxEmp if int(spans[iSpan].Offset) > maxEmptyBuckets { l := int(spans[iSpan].Length) if deltaBuckets { - for _, bucket := range buckets[iBucket : iBucket+l] { + for _, bucket := range primaryBuckets[iBucket : iBucket+l] { currentBucketAbsolute += bucket } } @@ -418,22 +443,28 @@ func compactBuckets[IBC InternalBucketCount](buckets []IBC, spans []Span, maxEmp offset := int(spans[iSpan].Offset) spans[iSpan-1].Length += uint32(offset) + spans[iSpan].Length spans = append(spans[:iSpan], spans[iSpan+1:]...) - newBuckets := make([]IBC, len(buckets)+offset) - copy(newBuckets, buckets[:iBucket]) - copy(newBuckets[iBucket+offset:], buckets[iBucket:]) + newPrimaryBuckets := make([]IBC, len(primaryBuckets)+offset) + copy(newPrimaryBuckets, primaryBuckets[:iBucket]) + copy(newPrimaryBuckets[iBucket+offset:], primaryBuckets[iBucket:]) if deltaBuckets { - newBuckets[iBucket] = -currentBucketAbsolute - newBuckets[iBucket+offset] += currentBucketAbsolute + newPrimaryBuckets[iBucket] = -currentBucketAbsolute + newPrimaryBuckets[iBucket+offset] += currentBucketAbsolute + } + primaryBuckets = newPrimaryBuckets + if compensationBuckets != nil { + newCompensationBuckets := make([]float64, len(compensationBuckets)+offset) + copy(newCompensationBuckets, compensationBuckets[:iBucket]) + copy(newCompensationBuckets[iBucket+offset:], compensationBuckets[iBucket:]) + compensationBuckets = newCompensationBuckets } iBucket += offset - buckets = newBuckets - currentBucketAbsolute = buckets[iBucket] + currentBucketAbsolute = primaryBuckets[iBucket] // Note that with many merges, it would be more efficient to // first record all the chunks of empty buckets to insert and // then do it in one go through all the buckets. } - return buckets, spans + return primaryBuckets, compensationBuckets, spans } func checkHistogramSpans(spans []Span, numBuckets int) error { diff --git a/vendor/github.com/prometheus/prometheus/model/histogram/histogram.go b/vendor/github.com/prometheus/prometheus/model/histogram/histogram.go index 5fc68ef9d..6ed02aed5 100644 --- a/vendor/github.com/prometheus/prometheus/model/histogram/histogram.go +++ b/vendor/github.com/prometheus/prometheus/model/histogram/histogram.go @@ -1,4 +1,4 @@ -// Copyright 2021 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -247,7 +247,7 @@ func (h *Histogram) CumulativeBucketIterator() BucketIterator[uint64] { // supposed to be used according to the schema. func (h *Histogram) Equals(h2 *Histogram) bool { if h2 == nil { - return false + return h == nil } if h.Schema != h2.Schema || h.Count != h2.Count || @@ -349,11 +349,11 @@ func allEmptySpans(s []Span) bool { // Compact works like FloatHistogram.Compact. See there for detailed // explanations. func (h *Histogram) Compact(maxEmptyBuckets int) *Histogram { - h.PositiveBuckets, h.PositiveSpans = compactBuckets( - h.PositiveBuckets, h.PositiveSpans, maxEmptyBuckets, true, + h.PositiveBuckets, _, h.PositiveSpans = compactBuckets( + h.PositiveBuckets, nil, h.PositiveSpans, maxEmptyBuckets, true, ) - h.NegativeBuckets, h.NegativeSpans = compactBuckets( - h.NegativeBuckets, h.NegativeSpans, maxEmptyBuckets, true, + h.NegativeBuckets, _, h.NegativeSpans = compactBuckets( + h.NegativeBuckets, nil, h.NegativeSpans, maxEmptyBuckets, true, ) return h } diff --git a/vendor/github.com/prometheus/prometheus/model/histogram/test_utils.go b/vendor/github.com/prometheus/prometheus/model/histogram/test_utils.go index a4871ada3..c86becdcf 100644 --- a/vendor/github.com/prometheus/prometheus/model/histogram/test_utils.go +++ b/vendor/github.com/prometheus/prometheus/model/histogram/test_utils.go @@ -1,4 +1,4 @@ -// Copyright 2023 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/model/labels/labels_common.go b/vendor/github.com/prometheus/prometheus/model/labels/labels_common.go index ab82ae6a8..571064d6c 100644 --- a/vendor/github.com/prometheus/prometheus/model/labels/labels_common.go +++ b/vendor/github.com/prometheus/prometheus/model/labels/labels_common.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/model/labels/labels_dedupelabels.go b/vendor/github.com/prometheus/prometheus/model/labels/labels_dedupelabels.go index 4518482c9..ae751fe34 100644 --- a/vendor/github.com/prometheus/prometheus/model/labels/labels_dedupelabels.go +++ b/vendor/github.com/prometheus/prometheus/model/labels/labels_dedupelabels.go @@ -1,4 +1,4 @@ -// Copyright 2024 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/model/labels/labels_slicelabels.go b/vendor/github.com/prometheus/prometheus/model/labels/labels_slicelabels.go index 71dbcd004..2a9056e68 100644 --- a/vendor/github.com/prometheus/prometheus/model/labels/labels_slicelabels.go +++ b/vendor/github.com/prometheus/prometheus/model/labels/labels_slicelabels.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/model/labels/labels_stringlabels.go b/vendor/github.com/prometheus/prometheus/model/labels/labels_stringlabels.go index 1460e7db9..c9be42bf7 100644 --- a/vendor/github.com/prometheus/prometheus/model/labels/labels_stringlabels.go +++ b/vendor/github.com/prometheus/prometheus/model/labels/labels_stringlabels.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/model/labels/matcher.go b/vendor/github.com/prometheus/prometheus/model/labels/matcher.go index a09c838e3..6d22b1bf6 100644 --- a/vendor/github.com/prometheus/prometheus/model/labels/matcher.go +++ b/vendor/github.com/prometheus/prometheus/model/labels/matcher.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/model/labels/regexp.go b/vendor/github.com/prometheus/prometheus/model/labels/regexp.go index 47b50e703..5f4f75341 100644 --- a/vendor/github.com/prometheus/prometheus/model/labels/regexp.go +++ b/vendor/github.com/prometheus/prometheus/model/labels/regexp.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -71,13 +71,28 @@ func NewFastRegexMatcher(v string) (*FastRegexMatcher, error) { if err != nil { return nil, err } + + // Remove any capture operations before trying to optimize the remaining operations. + clearCapture(parsed) + if parsed.Op == syntax.OpConcat { m.prefix, m.suffix, m.contains = optimizeConcatRegex(parsed) } if matches, caseSensitive := findSetMatches(parsed); caseSensitive { m.setMatches = matches } - m.stringMatcher = stringMatcherFromRegexp(parsed) + + // Check if we have a pattern like .*-.*-.*. + // If so, then we can rely on the containsInOrder check in compileMatchStringFunction, + // so no further inspection of the string is required. + // We can't do this in stringMatcherFromRegexpInternal as we only want to apply this + // if the top-level pattern satisfies this requirement. + if isSimpleConcatenationPattern(parsed) { + m.stringMatcher = trueMatcher{} + } else { + m.stringMatcher = stringMatcherFromRegexp(parsed) + } + m.matchString = m.compileMatchStringFunction() } @@ -566,6 +581,40 @@ func stringMatcherFromRegexpInternal(re *syntax.Regexp) StringMatcher { return nil } +// isSimpleConcatenationPattern returns true if re contains only literals or wildcard matchers, +// and starts and ends with a wildcard matcher (eg. .*-.*-.*). +func isSimpleConcatenationPattern(re *syntax.Regexp) bool { + if re.Op != syntax.OpConcat { + return false + } + + if len(re.Sub) < 2 { + return false + } + + first := re.Sub[0] + last := re.Sub[len(re.Sub)-1] + if !isMatchAny(first) || !isMatchAny(last) { + return false + } + + for _, re := range re.Sub[1 : len(re.Sub)-1] { + if !isMatchAny(re) && !isCaseSensitiveLiteral(re) { + return false + } + } + + return true +} + +func isMatchAny(re *syntax.Regexp) bool { + return re.Op == syntax.OpStar && re.Sub[0].Op == syntax.OpAnyChar +} + +func isCaseSensitiveLiteral(re *syntax.Regexp) bool { + return re.Op == syntax.OpLiteral && isCaseSensitive(re) +} + // containsStringMatcher matches a string if it contains any of the substrings. // If left and right are not nil, it's a contains operation where left and right must match. // If left is nil, it's a hasPrefix operation and right must match. diff --git a/vendor/github.com/prometheus/prometheus/model/labels/sharding.go b/vendor/github.com/prometheus/prometheus/model/labels/sharding.go index ed05da675..6394d0a01 100644 --- a/vendor/github.com/prometheus/prometheus/model/labels/sharding.go +++ b/vendor/github.com/prometheus/prometheus/model/labels/sharding.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/model/labels/sharding_dedupelabels.go b/vendor/github.com/prometheus/prometheus/model/labels/sharding_dedupelabels.go index 5bf41b05d..11342146a 100644 --- a/vendor/github.com/prometheus/prometheus/model/labels/sharding_dedupelabels.go +++ b/vendor/github.com/prometheus/prometheus/model/labels/sharding_dedupelabels.go @@ -1,4 +1,4 @@ -// Copyright 2024 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/model/labels/sharding_stringlabels.go b/vendor/github.com/prometheus/prometheus/model/labels/sharding_stringlabels.go index 4dcbaa21d..776a58bb5 100644 --- a/vendor/github.com/prometheus/prometheus/model/labels/sharding_stringlabels.go +++ b/vendor/github.com/prometheus/prometheus/model/labels/sharding_stringlabels.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/model/labels/test_utils.go b/vendor/github.com/prometheus/prometheus/model/labels/test_utils.go index 66020799e..21d1d7129 100644 --- a/vendor/github.com/prometheus/prometheus/model/labels/test_utils.go +++ b/vendor/github.com/prometheus/prometheus/model/labels/test_utils.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/model/relabel/relabel.go b/vendor/github.com/prometheus/prometheus/model/relabel/relabel.go index f7085037f..4045cc65d 100644 --- a/vendor/github.com/prometheus/prometheus/model/relabel/relabel.go +++ b/vendor/github.com/prometheus/prometheus/model/relabel/relabel.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -269,22 +269,8 @@ func (re Regexp) String() string { return str[5 : len(str)-2] } -// Process returns a relabeled version of the given label set. The relabel configurations -// are applied in order of input. -// There are circumstances where Process will modify the input label. -// If you want to avoid issues with the input label set being modified, at the cost of -// higher memory usage, you can use lbls.Copy(). -// If a label set is dropped, EmptyLabels and false is returned. -func Process(lbls labels.Labels, cfgs ...*Config) (ret labels.Labels, keep bool) { - lb := labels.NewBuilder(lbls) - if !ProcessBuilder(lb, cfgs...) { - return labels.EmptyLabels(), false - } - return lb.Labels(), true -} - -// ProcessBuilder is like Process, but the caller passes a labels.Builder -// containing the initial set of labels, which is mutated by the rules. +// ProcessBuilder applies relabeling configurations (rules) to the labels in lb. +// The rules are applied in order of input. Returns false if the rule says to drop. func ProcessBuilder(lb *labels.Builder, cfgs ...*Config) (keep bool) { for _, cfg := range cfgs { keep = relabel(cfg, lb) diff --git a/vendor/github.com/prometheus/prometheus/model/rulefmt/rulefmt.go b/vendor/github.com/prometheus/prometheus/model/rulefmt/rulefmt.go index 83203ba76..d284a14c4 100644 --- a/vendor/github.com/prometheus/prometheus/model/rulefmt/rulefmt.go +++ b/vendor/github.com/prometheus/prometheus/model/rulefmt/rulefmt.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -24,7 +24,7 @@ import ( "time" "github.com/prometheus/common/model" - "gopkg.in/yaml.v3" + "go.yaml.in/yaml/v3" "github.com/prometheus/prometheus/model/timestamp" "github.com/prometheus/prometheus/promql" @@ -97,7 +97,7 @@ type ruleGroups struct { } // Validate validates all rules in the rule groups. -func (g *RuleGroups) Validate(node ruleGroups, nameValidationScheme model.ValidationScheme) (errs []error) { +func (g *RuleGroups) Validate(node ruleGroups, nameValidationScheme model.ValidationScheme, p parser.Parser) (errs []error) { if err := namevalidationutil.CheckNameValidationScheme(nameValidationScheme); err != nil { errs = append(errs, err) return errs @@ -134,7 +134,7 @@ func (g *RuleGroups) Validate(node ruleGroups, nameValidationScheme model.Valida set[g.Name] = struct{}{} for i, r := range g.Rules { - for _, node := range r.Validate(node.Groups[j].Rules[i], nameValidationScheme) { + for _, node := range r.Validate(node.Groups[j].Rules[i], nameValidationScheme, p) { var ruleName string if r.Alert != "" { ruleName = r.Alert @@ -198,7 +198,7 @@ type RuleNode struct { } // Validate the rule and return a list of encountered errors. -func (r *Rule) Validate(node RuleNode, nameValidationScheme model.ValidationScheme) (nodes []WrappedError) { +func (r *Rule) Validate(node RuleNode, nameValidationScheme model.ValidationScheme, p parser.Parser) (nodes []WrappedError) { if r.Record != "" && r.Alert != "" { nodes = append(nodes, WrappedError{ err: errors.New("only one of 'record' and 'alert' must be set"), @@ -219,7 +219,7 @@ func (r *Rule) Validate(node RuleNode, nameValidationScheme model.ValidationSche err: errors.New("field 'expr' must be set in rule"), node: &node.Expr, }) - } else if _, err := parser.ParseExpr(r.Expr); err != nil { + } else if _, err := p.ParseExpr(r.Expr); err != nil { nodes = append(nodes, WrappedError{ err: fmt.Errorf("could not parse expression: %w", err), node: &node.Expr, @@ -339,7 +339,7 @@ func testTemplateParsing(rl *Rule) (errs []error) { } // Parse parses and validates a set of rules. -func Parse(content []byte, ignoreUnknownFields bool, nameValidationScheme model.ValidationScheme) (*RuleGroups, []error) { +func Parse(content []byte, ignoreUnknownFields bool, nameValidationScheme model.ValidationScheme, p parser.Parser) (*RuleGroups, []error) { var ( groups RuleGroups node ruleGroups @@ -364,16 +364,16 @@ func Parse(content []byte, ignoreUnknownFields bool, nameValidationScheme model. return nil, errs } - return &groups, groups.Validate(node, nameValidationScheme) + return &groups, groups.Validate(node, nameValidationScheme, p) } // ParseFile reads and parses rules from a file. -func ParseFile(file string, ignoreUnknownFields bool, nameValidationScheme model.ValidationScheme) (*RuleGroups, []error) { +func ParseFile(file string, ignoreUnknownFields bool, nameValidationScheme model.ValidationScheme, p parser.Parser) (*RuleGroups, []error) { b, err := os.ReadFile(file) if err != nil { return nil, []error{fmt.Errorf("%s: %w", file, err)} } - rgs, errs := Parse(b, ignoreUnknownFields, nameValidationScheme) + rgs, errs := Parse(b, ignoreUnknownFields, nameValidationScheme, p) for i := range errs { errs[i] = fmt.Errorf("%s: %w", file, errs[i]) } diff --git a/vendor/github.com/prometheus/prometheus/model/textparse/interface.go b/vendor/github.com/prometheus/prometheus/model/textparse/interface.go index bbc52290a..08d9a080a 100644 --- a/vendor/github.com/prometheus/prometheus/model/textparse/interface.go +++ b/vendor/github.com/prometheus/prometheus/model/textparse/interface.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/model/textparse/nhcbparse.go b/vendor/github.com/prometheus/prometheus/model/textparse/nhcbparse.go index 79441e1f7..13ce3ca98 100644 --- a/vendor/github.com/prometheus/prometheus/model/textparse/nhcbparse.go +++ b/vendor/github.com/prometheus/prometheus/model/textparse/nhcbparse.go @@ -1,4 +1,4 @@ -// Copyright 2024 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/model/textparse/openmetricsparse.go b/vendor/github.com/prometheus/prometheus/model/textparse/openmetricsparse.go index 207ceb457..724c34054 100644 --- a/vendor/github.com/prometheus/prometheus/model/textparse/openmetricsparse.go +++ b/vendor/github.com/prometheus/prometheus/model/textparse/openmetricsparse.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/model/textparse/promparse.go b/vendor/github.com/prometheus/prometheus/model/textparse/promparse.go index 4a75bcd8d..ada1b2901 100644 --- a/vendor/github.com/prometheus/prometheus/model/textparse/promparse.go +++ b/vendor/github.com/prometheus/prometheus/model/textparse/promparse.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/model/textparse/protobufparse.go b/vendor/github.com/prometheus/prometheus/model/textparse/protobufparse.go index a48aa4af6..637ae7b74 100644 --- a/vendor/github.com/prometheus/prometheus/model/textparse/protobufparse.go +++ b/vendor/github.com/prometheus/prometheus/model/textparse/protobufparse.go @@ -1,4 +1,4 @@ -// Copyright 2021 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/model/timestamp/timestamp.go b/vendor/github.com/prometheus/prometheus/model/timestamp/timestamp.go index 93458f644..0f27314e5 100644 --- a/vendor/github.com/prometheus/prometheus/model/timestamp/timestamp.go +++ b/vendor/github.com/prometheus/prometheus/model/timestamp/timestamp.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/model/value/value.go b/vendor/github.com/prometheus/prometheus/model/value/value.go index 655ce852d..fe8f50e00 100644 --- a/vendor/github.com/prometheus/prometheus/model/value/value.go +++ b/vendor/github.com/prometheus/prometheus/model/value/value.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/notifier/alert.go b/vendor/github.com/prometheus/prometheus/notifier/alert.go index 83e7a97fe..5e6df2097 100644 --- a/vendor/github.com/prometheus/prometheus/notifier/alert.go +++ b/vendor/github.com/prometheus/prometheus/notifier/alert.go @@ -1,4 +1,4 @@ -// Copyright 2013 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/notifier/alertmanager.go b/vendor/github.com/prometheus/prometheus/notifier/alertmanager.go index 8bcf7954e..a9c1e8669 100644 --- a/vendor/github.com/prometheus/prometheus/notifier/alertmanager.go +++ b/vendor/github.com/prometheus/prometheus/notifier/alertmanager.go @@ -1,4 +1,4 @@ -// Copyright 2013 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/notifier/alertmanagerset.go b/vendor/github.com/prometheus/prometheus/notifier/alertmanagerset.go index b6d1b8c4a..81565b5cf 100644 --- a/vendor/github.com/prometheus/prometheus/notifier/alertmanagerset.go +++ b/vendor/github.com/prometheus/prometheus/notifier/alertmanagerset.go @@ -1,4 +1,4 @@ -// Copyright 2013 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -16,6 +16,7 @@ package notifier import ( "crypto/md5" "encoding/hex" + "fmt" "log/slog" "net/http" "sync" @@ -26,6 +27,7 @@ import ( "github.com/prometheus/prometheus/config" "github.com/prometheus/prometheus/discovery/targetgroup" + "github.com/prometheus/prometheus/model/labels" ) // alertmanagerSet contains a set of Alertmanagers discovered via a group of service @@ -33,16 +35,19 @@ import ( type alertmanagerSet struct { cfg *config.AlertmanagerConfig client *http.Client + opts *Options metrics *alertMetrics mtx sync.RWMutex ams []alertmanager droppedAms []alertmanager - logger *slog.Logger + sendLoops map[string]*sendLoop + + logger *slog.Logger } -func newAlertmanagerSet(cfg *config.AlertmanagerConfig, logger *slog.Logger, metrics *alertMetrics) (*alertmanagerSet, error) { +func newAlertmanagerSet(cfg *config.AlertmanagerConfig, opts *Options, logger *slog.Logger, metrics *alertMetrics) (*alertmanagerSet, error) { client, err := config_util.NewClientFromConfig(cfg.HTTPClientConfig, "alertmanager") if err != nil { return nil, err @@ -59,10 +64,12 @@ func newAlertmanagerSet(cfg *config.AlertmanagerConfig, logger *slog.Logger, met client.Transport = t s := &alertmanagerSet{ - client: client, - cfg: cfg, - logger: logger, - metrics: metrics, + client: client, + cfg: cfg, + opts: opts, + sendLoops: make(map[string]*sendLoop), + logger: logger, + metrics: metrics, } return s, nil } @@ -86,36 +93,32 @@ func (s *alertmanagerSet) sync(tgs []*targetgroup.Group) { s.mtx.Lock() defer s.mtx.Unlock() previousAms := s.ams - // Set new Alertmanagers and deduplicate them along their unique URL. s.ams = []alertmanager{} s.droppedAms = []alertmanager{} s.droppedAms = append(s.droppedAms, allDroppedAms...) - seen := map[string]struct{}{} + // Deduplicate Alertmanagers and add sendloops for new Alertmanagers. + seen := map[string]struct{}{} for _, am := range allAms { us := am.url().String() if _, ok := seen[us]; ok { continue } - // This will initialize the Counters for the AM to 0. - s.metrics.sent.WithLabelValues(us) - s.metrics.errors.WithLabelValues(us) - seen[us] = struct{}{} s.ams = append(s.ams, am) } - // Now remove counters for any removed Alertmanagers. + s.addSendLoops(s.ams) + + // Populate a list of Alertmanagers to clean up, + // avoid cleaning up what we just added. for _, am := range previousAms { us := am.url().String() if _, ok := seen[us]; ok { continue } - s.metrics.latencySummary.DeleteLabelValues(us) - s.metrics.latencyHistogram.DeleteLabelValues(us) - s.metrics.sent.DeleteLabelValues(us) - s.metrics.errors.DeleteLabelValues(us) seen[us] = struct{}{} + s.cleanSendLoops(am) } } @@ -127,3 +130,62 @@ func (s *alertmanagerSet) configHash() (string, error) { hash := md5.Sum(b) return hex.EncodeToString(hash[:]), nil } + +func (s *alertmanagerSet) send(alerts ...*Alert) { + s.mtx.Lock() + defer s.mtx.Unlock() + + if len(s.cfg.AlertRelabelConfigs) > 0 { + alerts = relabelAlerts(s.cfg.AlertRelabelConfigs, labels.Labels{}, alerts) + if len(alerts) == 0 { + return + } + } + + for _, sendLoop := range s.sendLoops { + sendLoop.add(alerts...) + } +} + +// addSendLoops creates and starts a send loop for newly discovered alertmanager. +// This function expects the caller to acquire needed locks. +func (s *alertmanagerSet) addSendLoops(ams []alertmanager) { + for _, am := range ams { + us := am.url().String() + // Only add if sendloop doesn't already exist + if loop, exists := s.sendLoops[us]; exists { + loop.logger.Debug("Alertmanager already has send loop running, skipping") + continue + } + sendLoop := newSendLoop(us, s.client, s.cfg, s.opts, s.logger.With("alertmanager", us), s.metrics) + go sendLoop.loop() + s.sendLoops[us] = sendLoop + } +} + +// cleanSendLoops stops and cleans the send loops for each removed alertmanager. +// This function expects the caller to acquire needed locks. +func (s *alertmanagerSet) cleanSendLoops(ams ...alertmanager) { + for _, am := range ams { + us := am.url().String() + if sendLoop, ok := s.sendLoops[us]; ok { + sendLoop.stop() + delete(s.sendLoops, us) + } + } +} + +// startSendLoops starts a send loop for newly discovered alertmanager. +// This function expects the caller to acquire needed locks. +// This is mainly needed for testing where the loops are added as part of the test setup. +func (s *alertmanagerSet) startSendLoops(ams []alertmanager) { + for _, am := range ams { + us := am.url().String() + + if l, ok := s.sendLoops[us]; ok { + go l.loop() + continue + } + panic(fmt.Sprintf("send loop not found for %s", us)) + } +} diff --git a/vendor/github.com/prometheus/prometheus/notifier/manager.go b/vendor/github.com/prometheus/prometheus/notifier/manager.go index e37f59a25..7eeed79b7 100644 --- a/vendor/github.com/prometheus/prometheus/notifier/manager.go +++ b/vendor/github.com/prometheus/prometheus/notifier/manager.go @@ -1,4 +1,4 @@ -// Copyright 2013 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -14,16 +14,12 @@ package notifier import ( - "bytes" "context" - "encoding/json" "fmt" - "io" "log/slog" "net/http" "net/url" "sync" - "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/model" @@ -55,13 +51,11 @@ var userAgent = version.PrometheusUserAgent() // Manager is responsible for dispatching alert notifications to an // alert manager service. type Manager struct { - queue []*Alert - opts *Options + opts *Options metrics *alertMetrics - more chan struct{} - mtx sync.RWMutex + mtx sync.RWMutex stopOnce *sync.Once stopRequested chan struct{} @@ -114,23 +108,16 @@ func NewManager(o *Options, nameValidationScheme model.ValidationScheme, logger } n := &Manager{ - queue: make([]*Alert, 0, o.QueueCapacity), - more: make(chan struct{}, 1), stopRequested: make(chan struct{}), stopOnce: &sync.Once{}, opts: o, logger: logger, } - queueLenFunc := func() float64 { return float64(n.queueLen()) } alertmanagersDiscoveredFunc := func() float64 { return float64(len(n.Alertmanagers())) } - n.metrics = newAlertMetrics( - o.Registerer, - o.QueueCapacity, - queueLenFunc, - alertmanagersDiscoveredFunc, - ) + n.metrics = newAlertMetrics(o.Registerer, alertmanagersDiscoveredFunc) + n.metrics.queueCapacity.Set(float64(o.QueueCapacity)) return n } @@ -163,7 +150,7 @@ func (n *Manager) ApplyConfig(conf *config.Config) error { } for k, cfg := range conf.AlertingConfig.AlertmanagerConfigs.ToMap() { - ams, err := newAlertmanagerSet(cfg, n.logger, n.metrics) + ams, err := newAlertmanagerSet(cfg, n.opts, n.logger, n.metrics) if err != nil { return err } @@ -176,86 +163,54 @@ func (n *Manager) ApplyConfig(conf *config.Config) error { if oldAmSet, ok := configToAlertmanagers[hash]; ok { ams.ams = oldAmSet.ams ams.droppedAms = oldAmSet.droppedAms + // Only transfer sendLoops to the first new config with this hash. + // Subsequent configs with the same hash should not share the sendLoops + // map reference, as that would cause shared mutable state between + // alertmanagerSets (cleanup in one would affect the other). + oldAmSet.mtx.Lock() + if oldAmSet.sendLoops != nil { + ams.mtx.Lock() + ams.sendLoops = oldAmSet.sendLoops + oldAmSet.sendLoops = nil + ams.mtx.Unlock() + } + oldAmSet.mtx.Unlock() } amSets[k] = ams } + // Clean up sendLoops that weren't transferred to new config. + // This happens when: (1) key was removed, or (2) key exists but hash changed. + // After the transfer loop above, any oldAmSet with non-nil sendLoops + // had its sendLoops NOT transferred (since we set it to nil on transfer). + for _, oldAmSet := range n.alertmanagers { + oldAmSet.mtx.Lock() + if oldAmSet.sendLoops != nil { + oldAmSet.cleanSendLoops(oldAmSet.ams...) + } + oldAmSet.mtx.Unlock() + } + n.alertmanagers = amSets return nil } -func (n *Manager) queueLen() int { - n.mtx.RLock() - defer n.mtx.RUnlock() - - return len(n.queue) -} - -func (n *Manager) nextBatch() []*Alert { - n.mtx.Lock() - defer n.mtx.Unlock() - - var alerts []*Alert - - if maxBatchSize := n.opts.MaxBatchSize; len(n.queue) > maxBatchSize { - alerts = append(make([]*Alert, 0, maxBatchSize), n.queue[:maxBatchSize]...) - n.queue = n.queue[maxBatchSize:] - } else { - alerts = append(make([]*Alert, 0, len(n.queue)), n.queue...) - n.queue = n.queue[:0] - } - - return alerts -} - // Run dispatches notifications continuously, returning once Stop has been called and all // pending notifications have been drained from the queue (if draining is enabled). // // Dispatching of notifications occurs in parallel to processing target updates to avoid one starving the other. // Refer to https://github.com/prometheus/prometheus/issues/13676 for more details. func (n *Manager) Run(tsets <-chan map[string][]*targetgroup.Group) { - wg := sync.WaitGroup{} - wg.Add(2) + n.targetUpdateLoop(tsets) - go func() { - defer wg.Done() - n.targetUpdateLoop(tsets) - }() - - go func() { - defer wg.Done() - n.sendLoop() - n.drainQueue() - }() - - wg.Wait() - n.logger.Info("Notification manager stopped") -} - -// sendLoop continuously consumes the notifications queue and sends alerts to -// the configured Alertmanagers. -func (n *Manager) sendLoop() { - for { - // If we've been asked to stop, that takes priority over sending any further notifications. - select { - case <-n.stopRequested: - return - default: - select { - case <-n.stopRequested: - return - - case <-n.more: - n.sendOneBatch() - - // If the queue still has items left, kick off the next iteration. - if n.queueLen() > 0 { - n.setMore() - } - } - } + n.mtx.Lock() + defer n.mtx.Unlock() + for _, ams := range n.alertmanagers { + ams.mtx.Lock() + ams.cleanSendLoops(ams.ams...) + ams.mtx.Unlock() } } @@ -280,33 +235,6 @@ func (n *Manager) targetUpdateLoop(tsets <-chan map[string][]*targetgroup.Group) } } -func (n *Manager) sendOneBatch() { - alerts := n.nextBatch() - - if !n.sendAll(alerts...) { - n.metrics.dropped.Add(float64(len(alerts))) - } -} - -func (n *Manager) drainQueue() { - if !n.opts.DrainOnShutdown { - if n.queueLen() > 0 { - n.logger.Warn("Draining remaining notifications on shutdown is disabled, and some notifications have been dropped", "count", n.queueLen()) - n.metrics.dropped.Add(float64(n.queueLen())) - } - - return - } - - n.logger.Info("Draining any remaining notifications...") - - for n.queueLen() > 0 { - n.sendOneBatch() - } - - n.logger.Info("Remaining notifications drained") -} - func (n *Manager) reload(tgs map[string][]*targetgroup.Group) { n.mtx.Lock() defer n.mtx.Unlock() @@ -324,44 +252,23 @@ func (n *Manager) reload(tgs map[string][]*targetgroup.Group) { // Send queues the given notification requests for processing. // Panics if called on a handler that is not running. func (n *Manager) Send(alerts ...*Alert) { - n.mtx.Lock() - defer n.mtx.Unlock() + // If we've been asked to stop, that takes priority over accepting new alerts. + select { + case <-n.stopRequested: + return + default: + } + + n.mtx.RLock() + defer n.mtx.RUnlock() alerts = relabelAlerts(n.opts.RelabelConfigs, n.opts.ExternalLabels, alerts) if len(alerts) == 0 { return } - // Queue capacity should be significantly larger than a single alert - // batch could be. - if d := len(alerts) - n.opts.QueueCapacity; d > 0 { - alerts = alerts[d:] - - n.logger.Warn("Alert batch larger than queue capacity, dropping alerts", "num_dropped", d) - n.metrics.dropped.Add(float64(d)) - } - - // If the queue is full, remove the oldest alerts in favor - // of newer ones. - if d := (len(n.queue) + len(alerts)) - n.opts.QueueCapacity; d > 0 { - n.queue = n.queue[d:] - - n.logger.Warn("Alert notification queue full, dropping alerts", "num_dropped", d) - n.metrics.dropped.Add(float64(d)) - } - n.queue = append(n.queue, alerts...) - - // Notify sending goroutine that there are alerts to be processed. - n.setMore() -} - -// setMore signals that the alert queue has items. -func (n *Manager) setMore() { - // If we cannot send on the channel, it means the signal already exists - // and has not been consumed yet. - select { - case n.more <- struct{}{}: - default: + for _, ams := range n.alertmanagers { + ams.send(alerts...) } } @@ -403,158 +310,11 @@ func (n *Manager) DroppedAlertmanagers() []*url.URL { return res } -// sendAll sends the alerts to all configured Alertmanagers concurrently. -// It returns true if the alerts could be sent successfully to at least one Alertmanager. -func (n *Manager) sendAll(alerts ...*Alert) bool { - if len(alerts) == 0 { - return true - } - - begin := time.Now() - - // cachedPayload represent 'alerts' marshaled for Alertmanager API v2. - // Marshaling happens below. Reference here is for caching between - // for loop iterations. - var cachedPayload []byte - - n.mtx.RLock() - amSets := n.alertmanagers - n.mtx.RUnlock() - - var ( - wg sync.WaitGroup - amSetCovered sync.Map - ) - for k, ams := range amSets { - var ( - payload []byte - err error - amAlerts = alerts - ) - - ams.mtx.RLock() - - if len(ams.ams) == 0 { - ams.mtx.RUnlock() - continue - } - - if len(ams.cfg.AlertRelabelConfigs) > 0 { - amAlerts = relabelAlerts(ams.cfg.AlertRelabelConfigs, labels.Labels{}, alerts) - if len(amAlerts) == 0 { - ams.mtx.RUnlock() - continue - } - // We can't use the cached values from previous iteration. - cachedPayload = nil - } - - switch ams.cfg.APIVersion { - case config.AlertmanagerAPIVersionV2: - { - if cachedPayload == nil { - openAPIAlerts := alertsToOpenAPIAlerts(amAlerts) - - cachedPayload, err = json.Marshal(openAPIAlerts) - if err != nil { - n.logger.Error("Encoding alerts for Alertmanager API v2 failed", "err", err) - ams.mtx.RUnlock() - return false - } - } - - payload = cachedPayload - } - default: - { - n.logger.Error( - fmt.Sprintf("Invalid Alertmanager API version '%v', expected one of '%v'", ams.cfg.APIVersion, config.SupportedAlertmanagerAPIVersions), - "err", err, - ) - ams.mtx.RUnlock() - return false - } - } - - if len(ams.cfg.AlertRelabelConfigs) > 0 { - // We can't use the cached values on the next iteration. - cachedPayload = nil - } - - // Being here means len(ams.ams) > 0 - amSetCovered.Store(k, false) - for _, am := range ams.ams { - wg.Add(1) - - ctx, cancel := context.WithTimeout(context.Background(), time.Duration(ams.cfg.Timeout)) - defer cancel() - - go func(ctx context.Context, k string, client *http.Client, url string, payload []byte, count int) { - err := n.sendOne(ctx, client, url, payload) - if err != nil { - n.logger.Error("Error sending alerts", "alertmanager", url, "count", count, "err", err) - n.metrics.errors.WithLabelValues(url).Add(float64(count)) - } else { - amSetCovered.CompareAndSwap(k, false, true) - } - - durationSeconds := time.Since(begin).Seconds() - n.metrics.latencySummary.WithLabelValues(url).Observe(durationSeconds) - n.metrics.latencyHistogram.WithLabelValues(url).Observe(durationSeconds) - n.metrics.sent.WithLabelValues(url).Add(float64(count)) - - wg.Done() - }(ctx, k, ams.client, am.url().String(), payload, len(amAlerts)) - } - - ams.mtx.RUnlock() - } - - wg.Wait() - - // Return false if there are any sets which were attempted (e.g. not filtered - // out) but have no successes. - allAmSetsCovered := true - amSetCovered.Range(func(_, value any) bool { - if !value.(bool) { - allAmSetsCovered = false - return false - } - return true - }) - - return allAmSetsCovered -} - -func (n *Manager) sendOne(ctx context.Context, c *http.Client, url string, b []byte) error { - req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(b)) - if err != nil { - return err - } - req.Header.Set("User-Agent", userAgent) - req.Header.Set("Content-Type", contentTypeJSON) - resp, err := n.opts.Do(ctx, c, req) - if err != nil { - return err - } - defer func() { - io.Copy(io.Discard, resp.Body) - resp.Body.Close() - }() - - // Any HTTP status 2xx is OK. - if resp.StatusCode/100 != 2 { - return fmt.Errorf("bad response status %s", resp.Status) - } - - return nil -} - // Stop signals the notification manager to shut down and immediately returns. // // Run will return once the notification manager has successfully shut down. // -// The manager will optionally drain any queued notifications before shutting down. +// The manager will optionally drain send loops before shutting down. // // Stop is safe to call multiple times. func (n *Manager) Stop() { diff --git a/vendor/github.com/prometheus/prometheus/notifier/metric.go b/vendor/github.com/prometheus/prometheus/notifier/metric.go index 3f4abdda9..a150331ab 100644 --- a/vendor/github.com/prometheus/prometheus/notifier/metric.go +++ b/vendor/github.com/prometheus/prometheus/notifier/metric.go @@ -1,4 +1,4 @@ -// Copyright 2013 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -24,17 +24,13 @@ type alertMetrics struct { latencyHistogram *prometheus.HistogramVec errors *prometheus.CounterVec sent *prometheus.CounterVec - dropped prometheus.Counter - queueLength prometheus.GaugeFunc + dropped *prometheus.CounterVec + queueLength *prometheus.GaugeVec queueCapacity prometheus.Gauge alertmanagersDiscovered prometheus.GaugeFunc } -func newAlertMetrics( - r prometheus.Registerer, - queueCap int, - queueLen, alertmanagersDiscovered func() float64, -) *alertMetrics { +func newAlertMetrics(r prometheus.Registerer, alertmanagersDiscovered func() float64) *alertMetrics { m := &alertMetrics{ latencySummary: prometheus.NewSummaryVec(prometheus.SummaryOpts{ Namespace: namespace, @@ -74,18 +70,18 @@ func newAlertMetrics( }, []string{alertmanagerLabel}, ), - dropped: prometheus.NewCounter(prometheus.CounterOpts{ + dropped: prometheus.NewCounterVec(prometheus.CounterOpts{ Namespace: namespace, Subsystem: subsystem, Name: "dropped_total", Help: "Total number of alerts dropped due to errors when sending to Alertmanager.", - }), - queueLength: prometheus.NewGaugeFunc(prometheus.GaugeOpts{ + }, []string{alertmanagerLabel}), + queueLength: prometheus.NewGaugeVec(prometheus.GaugeOpts{ Namespace: namespace, Subsystem: subsystem, Name: "queue_length", Help: "The number of alert notifications in the queue.", - }, queueLen), + }, []string{alertmanagerLabel}), queueCapacity: prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: namespace, Subsystem: subsystem, @@ -98,8 +94,6 @@ func newAlertMetrics( }, alertmanagersDiscovered), } - m.queueCapacity.Set(float64(queueCap)) - if r != nil { r.MustRegister( m.latencySummary, diff --git a/vendor/github.com/prometheus/prometheus/notifier/sendloop.go b/vendor/github.com/prometheus/prometheus/notifier/sendloop.go new file mode 100644 index 000000000..041339026 --- /dev/null +++ b/vendor/github.com/prometheus/prometheus/notifier/sendloop.go @@ -0,0 +1,273 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package notifier + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "log/slog" + "net/http" + "sync" + "time" + + "github.com/prometheus/prometheus/config" +) + +type sendLoop struct { + alertmanagerURL string + + cfg *config.AlertmanagerConfig + client *http.Client + opts *Options + + metrics *alertMetrics + + mtx sync.RWMutex + queue []*Alert + hasWork chan struct{} + stopped chan struct{} + stopOnce sync.Once + + logger *slog.Logger +} + +func newSendLoop( + alertmanagerURL string, + client *http.Client, + cfg *config.AlertmanagerConfig, + opts *Options, + logger *slog.Logger, + metrics *alertMetrics, +) *sendLoop { + // This will initialize the Counters for the AM to 0 and set the static queue capacity gauge. + metrics.dropped.WithLabelValues(alertmanagerURL) + metrics.errors.WithLabelValues(alertmanagerURL) + metrics.sent.WithLabelValues(alertmanagerURL) + metrics.queueLength.WithLabelValues(alertmanagerURL) + + return &sendLoop{ + alertmanagerURL: alertmanagerURL, + client: client, + cfg: cfg, + opts: opts, + logger: logger, + metrics: metrics, + queue: make([]*Alert, 0, opts.QueueCapacity), + hasWork: make(chan struct{}, 1), + stopped: make(chan struct{}), + } +} + +func (s *sendLoop) add(alerts ...*Alert) { + select { + case <-s.stopped: + return + default: + } + + s.mtx.Lock() + defer s.mtx.Unlock() + + var dropped int + // Queue capacity should be significantly larger than a single alert + // batch could be. + if d := len(alerts) - s.opts.QueueCapacity; d > 0 { + s.logger.Warn("Alert batch larger than queue capacity, dropping alerts", "count", d) + dropped += d + alerts = alerts[d:] + } + + // If the queue is full, remove the oldest alerts in favor + // of newer ones. + if d := (len(s.queue) + len(alerts)) - s.opts.QueueCapacity; d > 0 { + s.logger.Warn("Alert notification queue full, dropping alerts", "count", d) + dropped += d + s.queue = s.queue[d:] + } + + s.queue = append(s.queue, alerts...) + + // Notify sending goroutine that there are alerts to be processed. + // If we cannot send on the channel, it means the signal already exists + // and has not been consumed yet. + s.notifyWork() + + s.metrics.queueLength.WithLabelValues(s.alertmanagerURL).Set(float64(len(s.queue))) + if dropped > 0 { + s.metrics.dropped.WithLabelValues(s.alertmanagerURL).Add(float64(dropped)) + } +} + +func (s *sendLoop) notifyWork() { + select { + case <-s.stopped: + return + case s.hasWork <- struct{}{}: + default: + } +} + +func (s *sendLoop) stop() { + s.stopOnce.Do(func() { + s.logger.Debug("Stopping send loop") + close(s.stopped) + + if s.opts.DrainOnShutdown { + s.drainQueue() + } else { + ql := s.queueLen() + s.logger.Warn("Alert notification queue not drained on shutdown, dropping alerts", "count", ql) + s.metrics.dropped.WithLabelValues(s.alertmanagerURL).Add(float64(ql)) + } + + s.metrics.latencySummary.DeleteLabelValues(s.alertmanagerURL) + s.metrics.latencyHistogram.DeleteLabelValues(s.alertmanagerURL) + s.metrics.sent.DeleteLabelValues(s.alertmanagerURL) + s.metrics.dropped.DeleteLabelValues(s.alertmanagerURL) + s.metrics.errors.DeleteLabelValues(s.alertmanagerURL) + s.metrics.queueLength.DeleteLabelValues(s.alertmanagerURL) + }) +} + +func (s *sendLoop) drainQueue() { + for s.queueLen() > 0 { + s.sendOneBatch() + } +} + +func (s *sendLoop) queueLen() int { + s.mtx.RLock() + defer s.mtx.RUnlock() + + return len(s.queue) +} + +func (s *sendLoop) nextBatch() []*Alert { + s.mtx.Lock() + defer s.mtx.Unlock() + + var alerts []*Alert + if maxBatchSize := s.opts.MaxBatchSize; len(s.queue) > maxBatchSize { + alerts = append(make([]*Alert, 0, maxBatchSize), s.queue[:maxBatchSize]...) + s.queue = s.queue[maxBatchSize:] + } else { + alerts = append(make([]*Alert, 0, len(s.queue)), s.queue...) + s.queue = s.queue[:0] + } + s.metrics.queueLength.WithLabelValues(s.alertmanagerURL).Set(float64(len(s.queue))) + + return alerts +} + +func (s *sendLoop) sendOneBatch() { + alerts := s.nextBatch() + + if !s.sendAll(alerts) { + s.metrics.dropped.WithLabelValues(s.alertmanagerURL).Add(float64(len(alerts))) + } +} + +// loop continuously consumes the notifications queue and sends alerts to +// the Alertmanager. +func (s *sendLoop) loop() { + s.logger.Debug("Starting send loop") + for { + // If we've been asked to stop, that takes priority over sending any further notifications. + select { + case <-s.stopped: + return + default: + select { + case <-s.stopped: + return + case <-s.hasWork: + s.sendOneBatch() + + // If the queue still has items left, kick off the next iteration. + if s.queueLen() > 0 { + s.notifyWork() + } + } + } + } +} + +func (s *sendLoop) sendAll(alerts []*Alert) bool { + if len(alerts) == 0 { + return true + } + + begin := time.Now() + + var payload []byte + var err error + switch s.cfg.APIVersion { + case config.AlertmanagerAPIVersionV2: + openAPIAlerts := alertsToOpenAPIAlerts(alerts) + payload, err = json.Marshal(openAPIAlerts) + if err != nil { + s.logger.Error("Encoding alerts for Alertmanager API v2 failed", "err", err) + return false + } + + default: + s.logger.Error( + fmt.Sprintf("Invalid Alertmanager API version '%v', expected one of '%v'", s.cfg.APIVersion, config.SupportedAlertmanagerAPIVersions), + "err", err, + ) + return false + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(s.cfg.Timeout)) + defer cancel() + + if err := s.sendOne(ctx, s.client, s.alertmanagerURL, payload); err != nil { + s.logger.Error("Error sending alerts", "count", len(alerts), "err", err) + s.metrics.errors.WithLabelValues(s.alertmanagerURL).Add(float64(len(alerts))) + return false + } + durationSeconds := time.Since(begin).Seconds() + s.metrics.latencySummary.WithLabelValues(s.alertmanagerURL).Observe(durationSeconds) + s.metrics.latencyHistogram.WithLabelValues(s.alertmanagerURL).Observe(durationSeconds) + s.metrics.sent.WithLabelValues(s.alertmanagerURL).Add(float64(len(alerts))) + + return true +} + +func (s *sendLoop) sendOne(ctx context.Context, c *http.Client, url string, b []byte) error { + req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(b)) + if err != nil { + return err + } + req.Header.Set("User-Agent", userAgent) + req.Header.Set("Content-Type", contentTypeJSON) + resp, err := s.opts.Do(ctx, c, req) + if err != nil { + return err + } + defer func() { + io.Copy(io.Discard, resp.Body) + resp.Body.Close() + }() + + // Any HTTP status 2xx is OK. + if resp.StatusCode/100 != 2 { + return fmt.Errorf("bad response status %s", resp.Status) + } + + return nil +} diff --git a/vendor/github.com/prometheus/prometheus/notifier/util.go b/vendor/github.com/prometheus/prometheus/notifier/util.go index c21c33a57..cf9a53eda 100644 --- a/vendor/github.com/prometheus/prometheus/notifier/util.go +++ b/vendor/github.com/prometheus/prometheus/notifier/util.go @@ -1,4 +1,4 @@ -// Copyright 2013 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/prompb/codec.go b/vendor/github.com/prometheus/prometheus/prompb/codec.go index 6cc0cdc86..36490984a 100644 --- a/vendor/github.com/prometheus/prometheus/prompb/codec.go +++ b/vendor/github.com/prometheus/prometheus/prompb/codec.go @@ -1,4 +1,4 @@ -// Copyright 2024 Prometheus Team +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -110,7 +110,7 @@ func (h Histogram) ToFloatHistogram() *histogram.FloatHistogram { PositiveBuckets: h.GetPositiveCounts(), NegativeSpans: spansProtoToSpans(h.GetNegativeSpans()), NegativeBuckets: h.GetNegativeCounts(), - CustomValues: h.CustomValues, + CustomValues: h.CustomValues, // CustomValues are immutable. } } // Conversion from integer histogram. @@ -125,6 +125,7 @@ func (h Histogram) ToFloatHistogram() *histogram.FloatHistogram { PositiveBuckets: deltasToCounts(h.GetPositiveDeltas()), NegativeSpans: spansProtoToSpans(h.GetNegativeSpans()), NegativeBuckets: deltasToCounts(h.GetNegativeDeltas()), + CustomValues: h.CustomValues, // CustomValues are immutable. } } @@ -161,6 +162,7 @@ func FromIntHistogram(timestamp int64, h *histogram.Histogram) Histogram { PositiveDeltas: h.PositiveBuckets, ResetHint: Histogram_ResetHint(h.CounterResetHint), Timestamp: timestamp, + CustomValues: h.CustomValues, // CustomValues are immutable. } } @@ -178,6 +180,7 @@ func FromFloatHistogram(timestamp int64, fh *histogram.FloatHistogram) Histogram PositiveCounts: fh.PositiveBuckets, ResetHint: Histogram_ResetHint(fh.CounterResetHint), Timestamp: timestamp, + CustomValues: fh.CustomValues, // CustomValues are immutable. } } diff --git a/vendor/github.com/prometheus/prometheus/prompb/custom.go b/vendor/github.com/prometheus/prometheus/prompb/custom.go index f73ddd446..65f856a75 100644 --- a/vendor/github.com/prometheus/prometheus/prompb/custom.go +++ b/vendor/github.com/prometheus/prometheus/prompb/custom.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/prompb/io/prometheus/client/decoder.go b/vendor/github.com/prometheus/prometheus/prompb/io/prometheus/client/decoder.go index 6bc9600ab..de7184c4b 100644 --- a/vendor/github.com/prometheus/prometheus/prompb/io/prometheus/client/decoder.go +++ b/vendor/github.com/prometheus/prometheus/prompb/io/prometheus/client/decoder.go @@ -1,4 +1,4 @@ -// Copyright 2025 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/prompb/io/prometheus/write/v2/codec.go b/vendor/github.com/prometheus/prometheus/prompb/io/prometheus/write/v2/codec.go index 71196edb8..ae4d0f635 100644 --- a/vendor/github.com/prometheus/prometheus/prompb/io/prometheus/write/v2/codec.go +++ b/vendor/github.com/prometheus/prometheus/prompb/io/prometheus/write/v2/codec.go @@ -1,4 +1,4 @@ -// Copyright 2024 Prometheus Team +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/prompb/io/prometheus/write/v2/custom.go b/vendor/github.com/prometheus/prometheus/prompb/io/prometheus/write/v2/custom.go index 5721aec53..4063cf32e 100644 --- a/vendor/github.com/prometheus/prometheus/prompb/io/prometheus/write/v2/custom.go +++ b/vendor/github.com/prometheus/prometheus/prompb/io/prometheus/write/v2/custom.go @@ -1,4 +1,4 @@ -// Copyright 2024 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/prompb/io/prometheus/write/v2/symbols.go b/vendor/github.com/prometheus/prometheus/prompb/io/prometheus/write/v2/symbols.go index 7c7feca23..292801a18 100644 --- a/vendor/github.com/prometheus/prometheus/prompb/io/prometheus/write/v2/symbols.go +++ b/vendor/github.com/prometheus/prometheus/prompb/io/prometheus/write/v2/symbols.go @@ -1,4 +1,4 @@ -// Copyright 2024 Prometheus Team +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/promql/durations.go b/vendor/github.com/prometheus/prometheus/promql/durations.go index c882adfbb..c660dbf46 100644 --- a/vendor/github.com/prometheus/prometheus/promql/durations.go +++ b/vendor/github.com/prometheus/prometheus/promql/durations.go @@ -1,4 +1,4 @@ -// Copyright 2025 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -28,7 +28,8 @@ import ( // in OriginalOffsetExpr representing (1h / 2). This visitor evaluates // such duration expression, setting OriginalOffset to 30m. type durationVisitor struct { - step time.Duration + step time.Duration + queryRange time.Duration } // Visit finds any duration expressions in AST Nodes and modifies the Node to @@ -121,6 +122,8 @@ func (v *durationVisitor) evaluateDurationExpr(expr parser.Expr) (float64, error switch n.Op { case parser.STEP: return float64(v.step.Seconds()), nil + case parser.RANGE: + return float64(v.queryRange.Seconds()), nil case parser.MIN: return math.Min(lhs, rhs), nil case parser.MAX: diff --git a/vendor/github.com/prometheus/prometheus/promql/engine.go b/vendor/github.com/prometheus/prometheus/promql/engine.go index 5a08da121..bd7b868d8 100644 --- a/vendor/github.com/prometheus/prometheus/promql/engine.go +++ b/vendor/github.com/prometheus/prometheus/promql/engine.go @@ -1,4 +1,4 @@ -// Copyright 2013 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -50,6 +50,7 @@ import ( "github.com/prometheus/prometheus/tsdb/chunkenc" "github.com/prometheus/prometheus/util/annotations" "github.com/prometheus/prometheus/util/features" + "github.com/prometheus/prometheus/util/kahansum" "github.com/prometheus/prometheus/util/logging" "github.com/prometheus/prometheus/util/stats" "github.com/prometheus/prometheus/util/zeropool" @@ -334,6 +335,9 @@ type EngineOpts struct { // FeatureRegistry is the registry for tracking enabled/disabled features. FeatureRegistry features.Collector + + // Parser is the PromQL parser instance used for parsing expressions. + Parser parser.Parser } // Engine handles the lifetime of queries from beginning to end. @@ -353,6 +357,7 @@ type Engine struct { enablePerStepStats bool enableDelayedNameRemoval bool enableTypeAndUnitLabels bool + parser parser.Parser } // NewEngine returns a new engine. @@ -431,6 +436,10 @@ func NewEngine(opts EngineOpts) *Engine { metrics.maxConcurrentQueries.Set(-1) } + if opts.Parser == nil { + opts.Parser = parser.NewParser(parser.Options{}) + } + if opts.LookbackDelta == 0 { opts.LookbackDelta = defaultLookbackDelta if l := opts.Logger; l != nil { @@ -459,7 +468,9 @@ func NewEngine(opts EngineOpts) *Engine { r.Enable(features.PromQL, "per_query_lookback_delta") r.Enable(features.PromQL, "subqueries") - parser.RegisterFeatures(r) + if opts.Parser != nil { + opts.Parser.RegisterFeatures(r) + } } return &Engine{ @@ -475,6 +486,7 @@ func NewEngine(opts EngineOpts) *Engine { enablePerStepStats: opts.EnablePerStepStats, enableDelayedNameRemoval: opts.EnableDelayedNameRemoval, enableTypeAndUnitLabels: opts.EnableTypeAndUnitLabels, + parser: opts.Parser, } } @@ -523,7 +535,7 @@ func (ng *Engine) NewInstantQuery(ctx context.Context, q storage.Queryable, opts return nil, err } defer finishQueue() - expr, err := parser.ParseExpr(qs) + expr, err := ng.parser.ParseExpr(qs) if err != nil { return nil, err } @@ -544,7 +556,7 @@ func (ng *Engine) NewRangeQuery(ctx context.Context, q storage.Queryable, opts Q return nil, err } defer finishQueue() - expr, err := parser.ParseExpr(qs) + expr, err := ng.parser.ParseExpr(qs) if err != nil { return nil, err } @@ -1202,6 +1214,9 @@ type EvalNodeHelper struct { // funcHistogramQuantile and funcHistogramFraction for classic histograms. signatureToMetricWithBuckets map[string]*metricWithBuckets nativeHistogramSamples []Sample + // funcHistogramQuantiles for histograms. + quantileStrs map[float64]string + signatureToLabelsWithQuantile map[string]map[float64]labels.Labels lb *labels.Builder lblBuf []byte @@ -1293,6 +1308,35 @@ func (enh *EvalNodeHelper) resetHistograms(inVec Vector, arg parser.Expr) annota return annos } +func (enh *EvalNodeHelper) getOrCreateLblsWithQuantile(lbls labels.Labels, quantileLabel string, q float64) labels.Labels { + if enh.signatureToLabelsWithQuantile == nil { + enh.signatureToLabelsWithQuantile = make(map[string]map[float64]labels.Labels) + } + + enh.lblBuf = lbls.Bytes(enh.lblBuf) + cachedLbls, ok := enh.signatureToLabelsWithQuantile[string(enh.lblBuf)] + if !ok { + cachedLbls = make(map[float64]labels.Labels, len(enh.quantileStrs)) + enh.signatureToLabelsWithQuantile[string(enh.lblBuf)] = cachedLbls + } + + cachedLblsWithQuantile, ok := cachedLbls[q] + if !ok { + quantileStr := "NaN" + if !math.IsNaN(q) { + // Cannot do map lookup by NaN key. + quantileStr = enh.quantileStrs[q] + } + cachedLblsWithQuantile = labels.NewBuilder(lbls). + Set(quantileLabel, quantileStr). + Labels() + + cachedLbls[q] = cachedLblsWithQuantile + } + + return cachedLblsWithQuantile +} + // rangeEval evaluates the given expressions, and then for each step calls // the given funcCall with the values computed for each expression at that // step. The return value is the combination into time series of all the @@ -1667,7 +1711,7 @@ func (ev *evaluator) smoothSeries(series []storage.Series, offset time.Duration) // Interpolate between prev and next. // TODO: detect if the sample is a counter, based on __type__ or metadata. prev, next := floats[i-1], floats[i] - val := interpolate(prev, next, ts, false, false) + val := interpolate(prev, next, ts, false) ss.Floats = append(ss.Floats, FPoint{F: val, T: ts}) case i > 0: @@ -2862,7 +2906,8 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching * if matching.Card == parser.CardManyToMany { panic("many-to-many only allowed for set operators") } - if len(lhs) == 0 || len(rhs) == 0 { + if (len(lhs) == 0 && len(rhs) == 0) || + ((len(lhs) == 0 || len(rhs) == 0) && matching.FillValues.RHS == nil && matching.FillValues.LHS == nil) { return nil, nil // Short-circuit: nothing is going to match. } @@ -2910,17 +2955,9 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching * } matchedSigs := enh.matchedSigs - // For all lhs samples find a respective rhs sample and perform - // the binary operation. var lastErr error - for i, ls := range lhs { - sigOrd := lhsh[i].sigOrdinal - - rs, found := rightSigs[sigOrd] // Look for a match in the rhs Vector. - if !found { - continue - } + doBinOp := func(ls, rs Sample, sigOrd int) { // Account for potentially swapped sidedness. fl, fr := ls.F, rs.F hl, hr := ls.H, rs.H @@ -2931,7 +2968,7 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching * floatValue, histogramValue, keep, info, err := vectorElemBinop(op, fl, fr, hl, hr, pos) if err != nil { lastErr = err - continue + return } if info != nil { lastErr = info @@ -2971,7 +3008,7 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching * } if !keep && !returnBool { - continue + return } enh.Out = append(enh.Out, Sample{ @@ -2981,6 +3018,43 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching * DropName: returnBool, }) } + + // For all lhs samples, find a respective rhs sample and perform + // the binary operation. + for i, ls := range lhs { + sigOrd := lhsh[i].sigOrdinal + + rs, found := rightSigs[sigOrd] // Look for a match in the rhs Vector. + if !found { + fill := matching.FillValues.RHS + if fill == nil { + continue + } + rs = Sample{ + Metric: ls.Metric.MatchLabels(matching.On, matching.MatchingLabels...), + F: *fill, + } + } + + doBinOp(ls, rs, sigOrd) + } + + // For any rhs samples which have not been matched, check if we need to + // perform the operation with a fill value from the lhs. + if fill := matching.FillValues.LHS; fill != nil { + for sigOrd, rs := range rightSigs { + if _, matched := matchedSigs[sigOrd]; matched { + continue // Already matched. + } + ls := Sample{ + Metric: rs.Metric.MatchLabels(matching.On, matching.MatchingLabels...), + F: *fill, + } + + doBinOp(ls, rs, sigOrd) + } + } + return enh.Out, lastErr } @@ -3209,23 +3283,26 @@ func vectorElemBinop(op parser.ItemType, lhs, rhs float64, hlhs, hrhs *histogram } type groupedAggregation struct { - floatValue float64 - histogramValue *histogram.FloatHistogram - floatMean float64 - floatKahanC float64 // "Compensating value" for Kahan summation. - groupCount float64 - heap vectorByValueHeap + floatValue float64 + floatMean float64 + floatKahanC float64 // Compensation float for Kahan summation. + histogramValue *histogram.FloatHistogram + histogramMean *histogram.FloatHistogram + histogramKahanC *histogram.FloatHistogram // Compensation histogram for Kahan summation. + groupCount float64 + heap vectorByValueHeap // All bools together for better packing within the struct. - seen bool // Was this output groups seen in the input at this timestamp. - hasFloat bool // Has at least 1 float64 sample aggregated. - hasHistogram bool // Has at least 1 histogram sample aggregated. - incompatibleHistograms bool // If true, group has seen mixed exponential and custom buckets. - groupAggrComplete bool // Used by LIMITK to short-cut series loop when we've reached K elem on every group. - incrementalMean bool // True after reverting to incremental calculation of the mean value. - counterResetSeen bool // Counter reset hint CounterReset seen. Currently only used for histogram samples. - notCounterResetSeen bool // Counter reset hint NotCounterReset seen. Currently only used for histogram samples. - dropName bool // True if any sample in this group has DropName set. + seen bool // Was this output groups seen in the input at this timestamp. + hasFloat bool // Has at least 1 float64 sample aggregated. + hasHistogram bool // Has at least 1 histogram sample aggregated. + incompatibleHistograms bool // If true, group has seen mixed exponential and custom buckets. + groupAggrComplete bool // Used by LIMITK to short-cut series loop when we've reached K elem on every group. + floatIncrementalMean bool // True after reverting to incremental calculation for float-based mean value. + histogramIncrementalMean bool // True after reverting to incremental calculation for histogram-based mean value. + counterResetSeen bool // Counter reset hint CounterReset seen. Currently only used for histogram samples. + notCounterResetSeen bool // Counter reset hint NotCounterReset seen. Currently only used for histogram samples. + dropName bool // True if any sample in this group has DropName set. } // aggregation evaluates sum, avg, count, stdvar, stddev or quantile at one timestep on inputMatrix. @@ -3315,6 +3392,11 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix group.dropName = true } + var ( + nhcbBoundsReconciled bool + err error + ) + switch op { case parser.SUM: if h != nil { @@ -3326,7 +3408,7 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix case histogram.NotCounterReset: group.notCounterResetSeen = true } - _, _, nhcbBoundsReconciled, err := group.histogramValue.Add(h) + group.histogramKahanC, _, nhcbBoundsReconciled, err = group.histogramValue.KahanAdd(h, group.histogramKahanC) if err != nil { handleAggregationError(err, e, inputMatrix[si].Metric.Get(model.MetricNameLabel), &annos) group.incompatibleHistograms = true @@ -3340,18 +3422,13 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix // point in copying the histogram in that case. } else { group.hasFloat = true - group.floatValue, group.floatKahanC = kahanSumInc(f, group.floatValue, group.floatKahanC) + group.floatValue, group.floatKahanC = kahansum.Inc(f, group.floatValue, group.floatKahanC) } case parser.AVG: - // For the average calculation of histograms, we use - // incremental mean calculation without the help of - // Kahan summation (but this should change, see - // https://github.com/prometheus/prometheus/issues/14105 - // ). For floats, we improve the accuracy with the help - // of Kahan summation. For a while, we assumed that - // incremental mean calculation combined with Kahan - // summation (see + // We improve the accuracy with the help of Kahan summation. + // For a while, we assumed that incremental mean calculation + // combined with Kahan summation (see // https://stackoverflow.com/questions/61665473/is-it-beneficial-for-precision-to-calculate-the-incremental-mean-average // for inspiration) is generally the preferred solution. // However, it then turned out that direct mean @@ -3386,20 +3463,37 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix case histogram.NotCounterReset: group.notCounterResetSeen = true } - left := h.Copy().Div(group.groupCount) - right := group.histogramValue.Copy().Div(group.groupCount) - - toAdd, _, nhcbBoundsReconciled, err := left.Sub(right) - if err != nil { - handleAggregationError(err, e, inputMatrix[si].Metric.Get(model.MetricNameLabel), &annos) - group.incompatibleHistograms = true - continue + if !group.histogramIncrementalMean { + v := group.histogramValue.Copy() + var c *histogram.FloatHistogram + if group.histogramKahanC != nil { + c = group.histogramKahanC.Copy() + } + c, _, nhcbBoundsReconciled, err = v.KahanAdd(h, c) + if err != nil { + handleAggregationError(err, e, inputMatrix[si].Metric.Get(model.MetricNameLabel), &annos) + group.incompatibleHistograms = true + continue + } + if nhcbBoundsReconciled { + annos.Add(annotations.NewMismatchedCustomBucketsHistogramsInfo(e.Expr.PositionRange(), annotations.HistogramAgg)) + } + if !v.HasOverflow() { + group.histogramValue, group.histogramKahanC = v, c + break + } + group.histogramIncrementalMean = true + group.histogramMean = group.histogramValue.Copy().Div(group.groupCount - 1) + if group.histogramKahanC != nil { + group.histogramKahanC.Div(group.groupCount - 1) + } } - if nhcbBoundsReconciled { - annos.Add(annotations.NewMismatchedCustomBucketsHistogramsInfo(e.Expr.PositionRange(), annotations.HistogramAgg)) + q := (group.groupCount - 1) / group.groupCount + if group.histogramKahanC != nil { + group.histogramKahanC.Mul(q) } - - _, _, nhcbBoundsReconciled, err = group.histogramValue.Add(toAdd) + toAdd := h.Copy().Div(group.groupCount) + group.histogramKahanC, _, nhcbBoundsReconciled, err = group.histogramMean.Mul(q).KahanAdd(toAdd, group.histogramKahanC) if err != nil { handleAggregationError(err, e, inputMatrix[si].Metric.Get(model.MetricNameLabel), &annos) group.incompatibleHistograms = true @@ -3414,8 +3508,8 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix // point in copying the histogram in that case. } else { group.hasFloat = true - if !group.incrementalMean { - newV, newC := kahanSumInc(f, group.floatValue, group.floatKahanC) + if !group.floatIncrementalMean { + newV, newC := kahansum.Inc(f, group.floatValue, group.floatKahanC) if !math.IsInf(newV, 0) { // The sum doesn't overflow, so we propagate it to the // group struct and continue with the regular @@ -3426,12 +3520,12 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix // If we are here, we know that the sum _would_ overflow. So // instead of continue to sum up, we revert to incremental // calculation of the mean value from here on. - group.incrementalMean = true + group.floatIncrementalMean = true group.floatMean = group.floatValue / (group.groupCount - 1) group.floatKahanC /= group.groupCount - 1 } q := (group.groupCount - 1) / group.groupCount - group.floatMean, group.floatKahanC = kahanSumInc( + group.floatMean, group.floatKahanC = kahansum.Inc( f/group.groupCount, q*group.floatMean, q*group.floatKahanC, @@ -3506,8 +3600,24 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix case aggr.incompatibleHistograms: continue case aggr.hasHistogram: + if aggr.histogramIncrementalMean { + if aggr.histogramKahanC != nil { + aggr.histogramValue, _, _, _ = aggr.histogramMean.Add(aggr.histogramKahanC) + // Add can theoretically return ErrHistogramsIncompatibleSchema, but at + // this stage errors should not occur if earlier KahanAdd calls succeeded. + } else { + aggr.histogramValue = aggr.histogramMean + } + } else { + aggr.histogramValue.Div(aggr.groupCount) + if aggr.histogramKahanC != nil { + aggr.histogramValue, _, _, _ = aggr.histogramValue.Add(aggr.histogramKahanC.Div(aggr.groupCount)) + // Add can theoretically return ErrHistogramsIncompatibleSchema, but at + // this stage errors should not occur if earlier KahanAdd calls succeeded. + } + } aggr.histogramValue = aggr.histogramValue.Compact(0) - case aggr.incrementalMean: + case aggr.floatIncrementalMean: aggr.floatValue = aggr.floatMean + aggr.floatKahanC default: aggr.floatValue = aggr.floatValue/aggr.groupCount + aggr.floatKahanC/aggr.groupCount @@ -3535,6 +3645,11 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix case aggr.incompatibleHistograms: continue case aggr.hasHistogram: + if aggr.histogramKahanC != nil { + aggr.histogramValue, _, _, _ = aggr.histogramValue.Add(aggr.histogramKahanC) + // Add can theoretically return ErrHistogramsIncompatibleSchema, but at + // this stage errors should not occur if earlier KahanAdd calls succeeded. + } aggr.histogramValue.Compact(0) default: aggr.floatValue += aggr.floatKahanC @@ -4057,7 +4172,7 @@ func unwrapStepInvariantExpr(e parser.Expr) parser.Expr { func PreprocessExpr(expr parser.Expr, start, end time.Time, step time.Duration) (parser.Expr, error) { detectHistogramStatsDecoding(expr) - if err := parser.Walk(&durationVisitor{step: step}, expr, nil); err != nil { + if err := parser.Walk(&durationVisitor{step: step, queryRange: end.Sub(start)}, expr, nil); err != nil { return nil, err } @@ -4237,7 +4352,7 @@ func detectHistogramStatsDecoding(expr parser.Expr) { // further up (the latter wouldn't make sense, // but no harm in detecting it). n.SkipHistogramBuckets = true - case "histogram_quantile", "histogram_fraction": + case "histogram_quantile", "histogram_quantiles", "histogram_fraction": // If we ever see a function that needs the // whole histogram, we will not skip the // buckets. @@ -4418,9 +4533,9 @@ func extendFloats(floats []FPoint, mint, maxt int64, smoothed bool) []FPoint { lastSampleIndex-- } - // TODO: Preallocate the length of the new list. - out := make([]FPoint, 0) - // Create the new floats list with the boundary samples and the inner samples. + count := max(lastSampleIndex-firstSampleIndex+1, 0) + out := make([]FPoint, 0, count+2) + out = append(out, FPoint{T: mint, F: left}) out = append(out, floats[firstSampleIndex:lastSampleIndex+1]...) out = append(out, FPoint{T: maxt, F: right}) diff --git a/vendor/github.com/prometheus/prometheus/promql/functions.go b/vendor/github.com/prometheus/prometheus/promql/functions.go index f844bf5ad..546f94df1 100644 --- a/vendor/github.com/prometheus/prometheus/promql/functions.go +++ b/vendor/github.com/prometheus/prometheus/promql/functions.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -33,6 +33,7 @@ import ( "github.com/prometheus/prometheus/promql/parser/posrange" "github.com/prometheus/prometheus/schema" "github.com/prometheus/prometheus/util/annotations" + "github.com/prometheus/prometheus/util/kahansum" ) // FunctionCall is the type of a PromQL function implementation @@ -70,7 +71,7 @@ func funcTime(_ []Vector, _ Matrix, _ parser.Expressions, enh *EvalNodeHelper) ( // it returns the interpolated value at the left boundary; otherwise, it returns the first sample's value. func pickOrInterpolateLeft(floats []FPoint, first int, rangeStart int64, smoothed, isCounter bool) float64 { if smoothed && floats[first].T < rangeStart { - return interpolate(floats[first], floats[first+1], rangeStart, isCounter, true) + return interpolate(floats[first], floats[first+1], rangeStart, isCounter) } return floats[first].F } @@ -80,25 +81,20 @@ func pickOrInterpolateLeft(floats []FPoint, first int, rangeStart int64, smoothe // it returns the interpolated value at the right boundary; otherwise, it returns the last sample's value. func pickOrInterpolateRight(floats []FPoint, last int, rangeEnd int64, smoothed, isCounter bool) float64 { if smoothed && last > 0 && floats[last].T > rangeEnd { - return interpolate(floats[last-1], floats[last], rangeEnd, isCounter, false) + return interpolate(floats[last-1], floats[last], rangeEnd, isCounter) } return floats[last].F } // interpolate performs linear interpolation between two points. -// If isCounter is true and there is a counter reset: -// - on the left edge, it sets the value to 0. -// - on the right edge, it adds the left value to the right value. +// If isCounter is true and there is a counter reset, it models the counter +// as starting from 0 (post-reset) by setting y1 to 0. // It then calculates the interpolated value at the given timestamp. -func interpolate(p1, p2 FPoint, t int64, isCounter, leftEdge bool) float64 { +func interpolate(p1, p2 FPoint, t int64, isCounter bool) float64 { y1 := p1.F y2 := p2.F if isCounter && y2 < y1 { - if leftEdge { - y1 = 0 - } else { - y2 += y1 - } + y1 = 0 } return y1 + (y2-y1)*float64(t-p1.T)/float64(p2.T-p1.T) @@ -200,9 +196,8 @@ func extrapolatedRate(vals Matrix, args parser.Expressions, enh *EvalNodeHelper, // We need either at least two Histograms and no Floats, or at least two // Floats and no Histograms to calculate a rate. Otherwise, drop this // Vector element. - metricName := samples.Metric.Get(labels.MetricName) if len(samples.Histograms) > 0 && len(samples.Floats) > 0 { - return enh.Out, annos.Add(annotations.NewMixedFloatsHistogramsWarning(metricName, args[0].PositionRange())) + return enh.Out, annos.Add(annotations.NewMixedFloatsHistogramsWarning(getMetricName(samples.Metric), args[0].PositionRange())) } switch { @@ -211,7 +206,7 @@ func extrapolatedRate(vals Matrix, args parser.Expressions, enh *EvalNodeHelper, firstT = samples.Histograms[0].T lastT = samples.Histograms[numSamplesMinusOne].T var newAnnos annotations.Annotations - resultHistogram, newAnnos = histogramRate(samples.Histograms, isCounter, metricName, args[0].PositionRange()) + resultHistogram, newAnnos = histogramRate(samples.Histograms, isCounter, samples.Metric, args[0].PositionRange()) annos.Merge(newAnnos) if resultHistogram == nil { // The histograms are not compatible with each other. @@ -305,7 +300,7 @@ func extrapolatedRate(vals Matrix, args parser.Expressions, enh *EvalNodeHelper, // points[0] to be a histogram. It returns nil if any other Point in points is // not a histogram, and a warning wrapped in an annotation in that case. // Otherwise, it returns the calculated histogram and an empty annotation. -func histogramRate(points []HPoint, isCounter bool, metricName string, pos posrange.PositionRange) (*histogram.FloatHistogram, annotations.Annotations) { +func histogramRate(points []HPoint, isCounter bool, labels labels.Labels, pos posrange.PositionRange) (*histogram.FloatHistogram, annotations.Annotations) { var ( prev = points[0].H usingCustomBuckets = prev.UsesCustomBuckets() @@ -314,14 +309,14 @@ func histogramRate(points []HPoint, isCounter bool, metricName string, pos posra ) if last == nil { - return nil, annos.Add(annotations.NewMixedFloatsHistogramsWarning(metricName, pos)) + return nil, annos.Add(annotations.NewMixedFloatsHistogramsWarning(getMetricName(labels), pos)) } // We check for gauge type histograms in the loop below, but the loop // below does not run on the first and last point, so check the first // and last point now. if isCounter && (prev.CounterResetHint == histogram.GaugeType || last.CounterResetHint == histogram.GaugeType) { - annos.Add(annotations.NewNativeHistogramNotCounterWarning(metricName, pos)) + annos.Add(annotations.NewNativeHistogramNotCounterWarning(getMetricName(labels), pos)) } // Null out the 1st sample if there is a counter reset between the 1st @@ -338,7 +333,7 @@ func histogramRate(points []HPoint, isCounter bool, metricName string, pos posra } if last.UsesCustomBuckets() != usingCustomBuckets { - return nil, annos.Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, pos)) + return nil, annos.Add(annotations.NewMixedExponentialCustomHistogramsWarning(getMetricName(labels), pos)) } // First iteration to find out two things: @@ -348,19 +343,19 @@ func histogramRate(points []HPoint, isCounter bool, metricName string, pos posra for _, currPoint := range points[1 : len(points)-1] { curr := currPoint.H if curr == nil { - return nil, annotations.New().Add(annotations.NewMixedFloatsHistogramsWarning(metricName, pos)) + return nil, annotations.New().Add(annotations.NewMixedFloatsHistogramsWarning(getMetricName(labels), pos)) } if !isCounter { continue } if curr.CounterResetHint == histogram.GaugeType { - annos.Add(annotations.NewNativeHistogramNotCounterWarning(metricName, pos)) + annos.Add(annotations.NewNativeHistogramNotCounterWarning(getMetricName(labels), pos)) } if curr.Schema < minSchema { minSchema = curr.Schema } if curr.UsesCustomBuckets() != usingCustomBuckets { - return nil, annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, pos)) + return nil, annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(getMetricName(labels), pos)) } } @@ -371,7 +366,7 @@ func histogramRate(points []HPoint, isCounter bool, metricName string, pos posra _, _, nhcbBoundsReconciled, err := h.Sub(prev) if err != nil { if errors.Is(err, histogram.ErrHistogramsIncompatibleSchema) { - return nil, annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, pos)) + return nil, annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(getMetricName(labels), pos)) } } if nhcbBoundsReconciled { @@ -387,7 +382,7 @@ func histogramRate(points []HPoint, isCounter bool, metricName string, pos posra _, _, nhcbBoundsReconciled, err := h.Add(prev) if err != nil { if errors.Is(err, histogram.ErrHistogramsIncompatibleSchema) { - return nil, annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, pos)) + return nil, annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(getMetricName(labels), pos)) } } if nhcbBoundsReconciled { @@ -397,7 +392,7 @@ func histogramRate(points []HPoint, isCounter bool, metricName string, pos posra prev = curr } } else if points[0].H.CounterResetHint != histogram.GaugeType || points[len(points)-1].H.CounterResetHint != histogram.GaugeType { - annos.Add(annotations.NewNativeHistogramNotGaugeWarning(metricName, pos)) + annos.Add(annotations.NewNativeHistogramNotGaugeWarning(getMetricName(labels), pos)) } h.CounterResetHint = histogram.GaugeType @@ -431,10 +426,9 @@ func funcIdelta(_ []Vector, matrixVals Matrix, args parser.Expressions, enh *Eva func instantValue(vals Matrix, args parser.Expressions, out Vector, isRate bool) (Vector, annotations.Annotations) { var ( - samples = vals[0] - metricName = samples.Metric.Get(labels.MetricName) - ss = make([]Sample, 0, 2) - annos annotations.Annotations + samples = vals[0] + ss = make([]Sample, 0, 2) + annos annotations.Annotations ) // No sense in trying to compute a rate without at least two points. Drop @@ -500,11 +494,11 @@ func instantValue(vals Matrix, args parser.Expressions, out Vector, isRate bool) resultSample.H = ss[1].H.Copy() // irate should only be applied to counters. if isRate && (ss[1].H.CounterResetHint == histogram.GaugeType || ss[0].H.CounterResetHint == histogram.GaugeType) { - annos.Add(annotations.NewNativeHistogramNotCounterWarning(metricName, args.PositionRange())) + annos.Add(annotations.NewNativeHistogramNotCounterWarning(getMetricName(samples.Metric), args.PositionRange())) } // idelta should only be applied to gauges. if !isRate && (ss[1].H.CounterResetHint != histogram.GaugeType || ss[0].H.CounterResetHint != histogram.GaugeType) { - annos.Add(annotations.NewNativeHistogramNotGaugeWarning(metricName, args.PositionRange())) + annos.Add(annotations.NewNativeHistogramNotGaugeWarning(getMetricName(samples.Metric), args.PositionRange())) } if !isRate || !ss[1].H.DetectReset(ss[0].H) { // This subtraction may deliberately include conflicting @@ -513,7 +507,7 @@ func instantValue(vals Matrix, args parser.Expressions, out Vector, isRate bool) // conflicting counter resets is ignored here. _, _, nhcbBoundsReconciled, err := resultSample.H.Sub(ss[0].H) if errors.Is(err, histogram.ErrHistogramsIncompatibleSchema) { - return out, annos.Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, args.PositionRange())) + return out, annos.Add(annotations.NewMixedExponentialCustomHistogramsWarning(getMetricName(samples.Metric), args.PositionRange())) } if nhcbBoundsReconciled { annos.Add(annotations.NewMismatchedCustomBucketsHistogramsInfo(args.PositionRange(), annotations.HistogramSub)) @@ -523,7 +517,7 @@ func instantValue(vals Matrix, args parser.Expressions, out Vector, isRate bool) resultSample.H.Compact(0) default: // Mix of a float and a histogram. - return out, annos.Add(annotations.NewMixedFloatsHistogramsWarning(metricName, args.PositionRange())) + return out, annos.Add(annotations.NewMixedFloatsHistogramsWarning(getMetricName(samples.Metric), args.PositionRange())) } if isRate { @@ -564,8 +558,10 @@ func calcTrendValue(i int, tf, s0, s1, b float64) float64 { // trend factor increases the influence. of trends. Algorithm taken from // https://en.wikipedia.org/wiki/Exponential_smoothing . func funcDoubleExponentialSmoothing(vectorVals []Vector, matrixVal Matrix, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + if len(vectorVals) < 2 || len(vectorVals[0]) == 0 || len(vectorVals[1]) == 0 || len(matrixVal) == 0 { + return enh.Out, nil + } samples := matrixVal[0] - metricName := samples.Metric.Get(labels.MetricName) // The smoothing factor argument. sf := vectorVals[0][0].F @@ -586,7 +582,7 @@ func funcDoubleExponentialSmoothing(vectorVals []Vector, matrixVal Matrix, args if l < 2 { // Annotate mix of float and histogram. if l == 1 && len(samples.Histograms) > 0 { - return enh.Out, annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange())) + return enh.Out, annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(getMetricName(samples.Metric), args[0].PositionRange())) } return enh.Out, nil } @@ -609,7 +605,7 @@ func funcDoubleExponentialSmoothing(vectorVals []Vector, matrixVal Matrix, args s0, s1 = s1, x+y } if len(samples.Histograms) > 0 { - return append(enh.Out, Sample{F: s1}), annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange())) + return append(enh.Out, Sample{F: s1}), annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(getMetricName(samples.Metric), args[0].PositionRange())) } return append(enh.Out, Sample{F: s1}), nil } @@ -779,12 +775,18 @@ func funcScalar(vectorVals []Vector, _ Matrix, _ parser.Expressions, enh *EvalNo } func aggrOverTime(matrixVal Matrix, enh *EvalNodeHelper, aggrFn func(Series) float64) Vector { + if len(matrixVal) == 0 { + return enh.Out + } el := matrixVal[0] return append(enh.Out, Sample{F: aggrFn(el)}) } func aggrHistOverTime(matrixVal Matrix, enh *EvalNodeHelper, aggrFn func(Series) (*histogram.FloatHistogram, error)) (Vector, error) { + if len(matrixVal) == 0 { + return enh.Out, nil + } el := matrixVal[0] res, err := aggrFn(el) @@ -793,15 +795,14 @@ func aggrHistOverTime(matrixVal Matrix, enh *EvalNodeHelper, aggrFn func(Series) // === avg_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) === func funcAvgOverTime(_ []Vector, matrixVal Matrix, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + if len(matrixVal) == 0 { + return enh.Out, nil + } firstSeries := matrixVal[0] if len(firstSeries.Floats) > 0 && len(firstSeries.Histograms) > 0 { - metricName := firstSeries.Metric.Get(labels.MetricName) - return enh.Out, annotations.New().Add(annotations.NewMixedFloatsHistogramsWarning(metricName, args[0].PositionRange())) + return enh.Out, annotations.New().Add(annotations.NewMixedFloatsHistogramsWarning(getMetricName(firstSeries.Metric), args[0].PositionRange())) } - // For the average calculation of histograms, we use incremental mean - // calculation without the help of Kahan summation (but this should - // change, see https://github.com/prometheus/prometheus/issues/14105 ). - // For floats, we improve the accuracy with the help of Kahan summation. + // We improve the accuracy with the help of Kahan summation. // For a while, we assumed that incremental mean calculation combined // with Kahan summation (see // https://stackoverflow.com/questions/61665473/is-it-beneficial-for-precision-to-calculate-the-incremental-mean-average @@ -844,23 +845,47 @@ func funcAvgOverTime(_ []Vector, matrixVal Matrix, args parser.Expressions, enh } }() - mean := s.Histograms[0].H.Copy() - trackCounterReset(mean) + var ( + sum = s.Histograms[0].H.Copy() + mean, kahanC *histogram.FloatHistogram + count = 1. + incrementalMean bool + nhcbBoundsReconciled bool + err error + ) + trackCounterReset(sum) for i, h := range s.Histograms[1:] { trackCounterReset(h.H) - count := float64(i + 2) - left := h.H.Copy().Div(count) - right := mean.Copy().Div(count) - - toAdd, _, nhcbBoundsReconciled, err := left.Sub(right) - if err != nil { - return mean, err + count = float64(i + 2) + if !incrementalMean { + sumCopy := sum.Copy() + var cCopy *histogram.FloatHistogram + if kahanC != nil { + cCopy = kahanC.Copy() + } + cCopy, _, nhcbBoundsReconciled, err = sumCopy.KahanAdd(h.H, cCopy) + if err != nil { + return sumCopy.Div(count), err + } + if nhcbBoundsReconciled { + nhcbBoundsReconciledSeen = true + } + if !sumCopy.HasOverflow() { + sum, kahanC = sumCopy, cCopy + continue + } + incrementalMean = true + mean = sum.Copy().Div(count - 1) + if kahanC != nil { + kahanC.Div(count - 1) + } } - if nhcbBoundsReconciled { - nhcbBoundsReconciledSeen = true + q := (count - 1) / count + if kahanC != nil { + kahanC.Mul(q) } - - _, _, nhcbBoundsReconciled, err = mean.Add(toAdd) + toAdd := h.H.Copy().Div(count) + kahanC, _, nhcbBoundsReconciled, err = mean.Mul(q).KahanAdd(toAdd, kahanC) if err != nil { return mean, err } @@ -868,12 +893,22 @@ func funcAvgOverTime(_ []Vector, matrixVal Matrix, args parser.Expressions, enh nhcbBoundsReconciledSeen = true } } - return mean, nil + if incrementalMean { + if kahanC != nil { + _, _, _, err := mean.Add(kahanC) + return mean, err + } + return mean, nil + } + if kahanC != nil { + _, _, _, err := sum.Div(count).Add(kahanC.Div(count)) + return sum, err + } + return sum.Div(count), nil }) if err != nil { - metricName := firstSeries.Metric.Get(labels.MetricName) if errors.Is(err, histogram.ErrHistogramsIncompatibleSchema) { - return enh.Out, annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, args[0].PositionRange())) + return enh.Out, annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(getMetricName(firstSeries.Metric), args[0].PositionRange())) } } return vec, annos @@ -888,7 +923,7 @@ func funcAvgOverTime(_ []Vector, matrixVal Matrix, args parser.Expressions, enh for i, f := range s.Floats[1:] { count = float64(i + 2) if !incrementalMean { - newSum, newC := kahanSumInc(f.F, sum, kahanC) + newSum, newC := kahansum.Inc(f.F, sum, kahanC) // Perform regular mean calculation as long as // the sum doesn't overflow. if !math.IsInf(newSum, 0) { @@ -902,7 +937,7 @@ func funcAvgOverTime(_ []Vector, matrixVal Matrix, args parser.Expressions, enh kahanC /= (count - 1) } q := (count - 1) / count - mean, kahanC = kahanSumInc(f.F/count, q*mean, q*kahanC) + mean, kahanC = kahansum.Inc(f.F/count, q*mean, q*kahanC) } if incrementalMean { return mean + kahanC @@ -920,6 +955,9 @@ func funcCountOverTime(_ []Vector, matrixVals Matrix, _ parser.Expressions, enh // === first_over_time(Matrix parser.ValueTypeMatrix) (Vector, Notes) === func funcFirstOverTime(_ []Vector, matrixVal Matrix, _ parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + if len(matrixVal) == 0 { + return enh.Out, nil + } el := matrixVal[0] var f FPoint @@ -948,6 +986,9 @@ func funcFirstOverTime(_ []Vector, matrixVal Matrix, _ parser.Expressions, enh * // === last_over_time(Matrix parser.ValueTypeMatrix) (Vector, Notes) === func funcLastOverTime(_ []Vector, matrixVal Matrix, _ parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + if len(matrixVal) == 0 { + return enh.Out, nil + } el := matrixVal[0] var f FPoint @@ -974,14 +1015,16 @@ func funcLastOverTime(_ []Vector, matrixVal Matrix, _ parser.Expressions, enh *E // === mad_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) === func funcMadOverTime(_ []Vector, matrixVal Matrix, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + if len(matrixVal) == 0 { + return enh.Out, nil + } samples := matrixVal[0] var annos annotations.Annotations if len(samples.Floats) == 0 { return enh.Out, nil } if len(samples.Histograms) > 0 { - metricName := samples.Metric.Get(labels.MetricName) - annos.Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange())) + annos.Add(annotations.NewHistogramIgnoredInMixedRangeInfo(getMetricName(samples.Metric), args[0].PositionRange())) } return aggrOverTime(matrixVal, enh, func(s Series) float64 { values := make(vectorByValueHeap, 0, len(s.Floats)) @@ -999,6 +1042,9 @@ func funcMadOverTime(_ []Vector, matrixVal Matrix, args parser.Expressions, enh // === ts_of_first_over_time(Matrix parser.ValueTypeMatrix) (Vector, Notes) === func funcTsOfFirstOverTime(_ []Vector, matrixVal Matrix, _ parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + if len(matrixVal) == 0 { + return enh.Out, nil + } el := matrixVal[0] var tf int64 = math.MaxInt64 @@ -1019,6 +1065,9 @@ func funcTsOfFirstOverTime(_ []Vector, matrixVal Matrix, _ parser.Expressions, e // === ts_of_last_over_time(Matrix parser.ValueTypeMatrix) (Vector, Notes) === func funcTsOfLastOverTime(_ []Vector, matrixVal Matrix, _ parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + if len(matrixVal) == 0 { + return enh.Out, nil + } el := matrixVal[0] var tf int64 @@ -1053,14 +1102,16 @@ func funcTsOfMinOverTime(_ []Vector, matrixVals Matrix, args parser.Expressions, // compareOverTime is a helper used by funcMaxOverTime and funcMinOverTime. func compareOverTime(matrixVal Matrix, args parser.Expressions, enh *EvalNodeHelper, compareFn func(float64, float64) bool, returnTimestamp bool) (Vector, annotations.Annotations) { + if len(matrixVal) == 0 { + return enh.Out, nil + } samples := matrixVal[0] var annos annotations.Annotations if len(samples.Floats) == 0 { return enh.Out, nil } if len(samples.Histograms) > 0 { - metricName := samples.Metric.Get(labels.MetricName) - annos.Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange())) + annos.Add(annotations.NewHistogramIgnoredInMixedRangeInfo(getMetricName(samples.Metric), args[0].PositionRange())) } return aggrOverTime(matrixVal, enh, func(s Series) float64 { maxVal := s.Floats[0].F @@ -1094,10 +1145,12 @@ func funcMinOverTime(_ []Vector, matrixVals Matrix, args parser.Expressions, enh // === sum_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) === func funcSumOverTime(_ []Vector, matrixVal Matrix, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + if len(matrixVal) == 0 { + return enh.Out, nil + } firstSeries := matrixVal[0] if len(firstSeries.Floats) > 0 && len(firstSeries.Histograms) > 0 { - metricName := firstSeries.Metric.Get(labels.MetricName) - return enh.Out, annotations.New().Add(annotations.NewMixedFloatsHistogramsWarning(metricName, args[0].PositionRange())) + return enh.Out, annotations.New().Add(annotations.NewMixedFloatsHistogramsWarning(getMetricName(firstSeries.Metric), args[0].PositionRange())) } if len(firstSeries.Floats) == 0 { // The passed values only contain histograms. @@ -1125,9 +1178,14 @@ func funcSumOverTime(_ []Vector, matrixVal Matrix, args parser.Expressions, enh sum := s.Histograms[0].H.Copy() trackCounterReset(sum) + var ( + comp *histogram.FloatHistogram + nhcbBoundsReconciled bool + err error + ) for _, h := range s.Histograms[1:] { trackCounterReset(h.H) - _, _, nhcbBoundsReconciled, err := sum.Add(h.H) + comp, _, nhcbBoundsReconciled, err = sum.KahanAdd(h.H, comp) if err != nil { return sum, err } @@ -1135,12 +1193,20 @@ func funcSumOverTime(_ []Vector, matrixVal Matrix, args parser.Expressions, enh nhcbBoundsReconciledSeen = true } } - return sum, nil + if comp != nil { + sum, _, nhcbBoundsReconciled, err = sum.Add(comp) + if err != nil { + return sum, err + } + if nhcbBoundsReconciled { + nhcbBoundsReconciledSeen = true + } + } + return sum, err }) if err != nil { - metricName := firstSeries.Metric.Get(labels.MetricName) if errors.Is(err, histogram.ErrHistogramsIncompatibleSchema) { - return enh.Out, annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, args[0].PositionRange())) + return enh.Out, annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(getMetricName(firstSeries.Metric), args[0].PositionRange())) } } return vec, annos @@ -1148,7 +1214,7 @@ func funcSumOverTime(_ []Vector, matrixVal Matrix, args parser.Expressions, enh return aggrOverTime(matrixVal, enh, func(s Series) float64 { var sum, c float64 for _, f := range s.Floats { - sum, c = kahanSumInc(f.F, sum, c) + sum, c = kahansum.Inc(f.F, sum, c) } if math.IsInf(sum, 0) { return sum @@ -1159,6 +1225,9 @@ func funcSumOverTime(_ []Vector, matrixVal Matrix, args parser.Expressions, enh // === quantile_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) === func funcQuantileOverTime(vectorVals []Vector, matrixVal Matrix, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + if len(vectorVals) == 0 || len(vectorVals[0]) == 0 || len(matrixVal) == 0 { + return enh.Out, nil + } q := vectorVals[0][0].F el := matrixVal[0] if len(el.Floats) == 0 { @@ -1170,8 +1239,7 @@ func funcQuantileOverTime(vectorVals []Vector, matrixVal Matrix, args parser.Exp annos.Add(annotations.NewInvalidQuantileWarning(q, args[0].PositionRange())) } if len(el.Histograms) > 0 { - metricName := el.Metric.Get(labels.MetricName) - annos.Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange())) + annos.Add(annotations.NewHistogramIgnoredInMixedRangeInfo(getMetricName(el.Metric), args[0].PositionRange())) } values := make(vectorByValueHeap, 0, len(el.Floats)) for _, f := range el.Floats { @@ -1181,14 +1249,16 @@ func funcQuantileOverTime(vectorVals []Vector, matrixVal Matrix, args parser.Exp } func varianceOverTime(matrixVal Matrix, args parser.Expressions, enh *EvalNodeHelper, varianceToResult func(float64) float64) (Vector, annotations.Annotations) { + if len(matrixVal) == 0 { + return enh.Out, nil + } samples := matrixVal[0] var annos annotations.Annotations if len(samples.Floats) == 0 { return enh.Out, nil } if len(samples.Histograms) > 0 { - metricName := samples.Metric.Get(labels.MetricName) - annos.Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange())) + annos.Add(annotations.NewHistogramIgnoredInMixedRangeInfo(getMetricName(samples.Metric), args[0].PositionRange())) } return aggrOverTime(matrixVal, enh, func(s Series) float64 { var count float64 @@ -1197,8 +1267,8 @@ func varianceOverTime(matrixVal Matrix, args parser.Expressions, enh *EvalNodeHe for _, f := range s.Floats { count++ delta := f.F - (mean + cMean) - mean, cMean = kahanSumInc(delta/count, mean, cMean) - aux, cAux = kahanSumInc(delta*(f.F-(mean+cMean)), aux, cAux) + mean, cMean = kahansum.Inc(delta/count, mean, cMean) + aux, cAux = kahansum.Inc(delta*(f.F-(mean+cMean)), aux, cAux) } variance := (aux + cAux) / count if varianceToResult == nil { @@ -1411,24 +1481,6 @@ func funcTimestamp(vectorVals []Vector, _ Matrix, _ parser.Expressions, enh *Eva return enh.Out, nil } -// We get incorrect results if this function is inlined; see https://github.com/prometheus/prometheus/issues/16714. -// -//go:noinline -func kahanSumInc(inc, sum, c float64) (newSum, newC float64) { - t := sum + inc - switch { - case math.IsInf(t, 0): - c = 0 - - // Using Neumaier improvement, swap if next term larger than sum. - case math.Abs(sum) >= math.Abs(inc): - c += (sum - t) + inc - default: - c += (inc - t) + sum - } - return t, c -} - // linearRegression performs a least-square linear regression analysis on the // provided SamplePairs. It returns the slope, and the intercept value at the // provided time. @@ -1451,10 +1503,10 @@ func linearRegression(samples []FPoint, interceptTime int64) (slope, intercept f } n += 1.0 x := float64(sample.T-interceptTime) / 1e3 - sumX, cX = kahanSumInc(x, sumX, cX) - sumY, cY = kahanSumInc(sample.F, sumY, cY) - sumXY, cXY = kahanSumInc(x*sample.F, sumXY, cXY) - sumX2, cX2 = kahanSumInc(x*x, sumX2, cX2) + sumX, cX = kahansum.Inc(x, sumX, cX) + sumY, cY = kahansum.Inc(sample.F, sumY, cY) + sumXY, cXY = kahansum.Inc(x*sample.F, sumXY, cXY) + sumX2, cX2 = kahansum.Inc(x*x, sumX2, cX2) } if constY { if math.IsInf(initY, 0) { @@ -1477,15 +1529,17 @@ func linearRegression(samples []FPoint, interceptTime int64) (slope, intercept f // === deriv(node parser.ValueTypeMatrix) (Vector, Annotations) === func funcDeriv(_ []Vector, matrixVal Matrix, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + if len(matrixVal) == 0 { + return enh.Out, nil + } samples := matrixVal[0] - metricName := samples.Metric.Get(labels.MetricName) // No sense in trying to compute a derivative without at least two float points. // Drop this Vector element. if len(samples.Floats) < 2 { // Annotate mix of float and histogram. if len(samples.Floats) == 1 && len(samples.Histograms) > 0 { - return enh.Out, annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange())) + return enh.Out, annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(getMetricName(samples.Metric), args[0].PositionRange())) } return enh.Out, nil } @@ -1495,30 +1549,32 @@ func funcDeriv(_ []Vector, matrixVal Matrix, args parser.Expressions, enh *EvalN // https://github.com/prometheus/prometheus/issues/2674 slope, _ := linearRegression(samples.Floats, samples.Floats[0].T) if len(samples.Histograms) > 0 { - return append(enh.Out, Sample{F: slope}), annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange())) + return append(enh.Out, Sample{F: slope}), annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(getMetricName(samples.Metric), args[0].PositionRange())) } return append(enh.Out, Sample{F: slope}), nil } // === predict_linear(node parser.ValueTypeMatrix, k parser.ValueTypeScalar) (Vector, Annotations) === func funcPredictLinear(vectorVals []Vector, matrixVal Matrix, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + if len(vectorVals) == 0 || len(vectorVals[0]) == 0 || len(matrixVal) == 0 { + return enh.Out, nil + } samples := matrixVal[0] duration := vectorVals[0][0].F - metricName := samples.Metric.Get(labels.MetricName) // No sense in trying to predict anything without at least two float points. // Drop this Vector element. if len(samples.Floats) < 2 { // Annotate mix of float and histogram. if len(samples.Floats) == 1 && len(samples.Histograms) > 0 { - return enh.Out, annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange())) + return enh.Out, annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(getMetricName(samples.Metric), args[0].PositionRange())) } return enh.Out, nil } slope, intercept := linearRegression(samples.Floats, enh.Ts) if len(samples.Histograms) > 0 { - return append(enh.Out, Sample{F: slope*duration + intercept}), annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange())) + return append(enh.Out, Sample{F: slope*duration + intercept}), annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(getMetricName(samples.Metric), args[0].PositionRange())) } return append(enh.Out, Sample{F: slope*duration + intercept}), nil } @@ -1586,7 +1642,7 @@ func histogramVariance(vectorVals []Vector, enh *EvalNodeHelper, varianceToResul } } delta := val - mean - variance, cVariance = kahanSumInc(bucket.Count*delta*delta, variance, cVariance) + variance, cVariance = kahansum.Inc(bucket.Count*delta*delta, variance, cVariance) } variance += cVariance variance /= h.Count @@ -1609,6 +1665,9 @@ func funcHistogramStdVar(vectorVals []Vector, _ Matrix, _ parser.Expressions, en // === histogram_fraction(lower, upper parser.ValueTypeScalar, Vector parser.ValueTypeVector) (Vector, Annotations) === func funcHistogramFraction(vectorVals []Vector, _ Matrix, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + if len(vectorVals) < 3 || len(vectorVals[0]) == 0 || len(vectorVals[1]) == 0 { + return enh.Out, nil + } lower := vectorVals[0][0].F upper := vectorVals[1][0].F inVec := vectorVals[2] @@ -1624,7 +1683,7 @@ func funcHistogramFraction(vectorVals []Vector, _ Matrix, args parser.Expression if !enh.enableDelayedNameRemoval { sample.Metric = sample.Metric.DropReserved(schema.IsMetadataLabel) } - hf, hfAnnos := HistogramFraction(lower, upper, sample.H, sample.Metric.Get(model.MetricNameLabel), args[0].PositionRange()) + hf, hfAnnos := HistogramFraction(lower, upper, sample.H, getMetricName(sample.Metric), args[0].PositionRange()) annos.Merge(hfAnnos) enh.Out = append(enh.Out, Sample{ Metric: sample.Metric, @@ -1654,12 +1713,15 @@ func funcHistogramFraction(vectorVals []Vector, _ Matrix, args parser.Expression // === histogram_quantile(k parser.ValueTypeScalar, Vector parser.ValueTypeVector) (Vector, Annotations) === func funcHistogramQuantile(vectorVals []Vector, _ Matrix, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + if len(vectorVals) < 2 || len(vectorVals[0]) == 0 { + return enh.Out, nil + } q := vectorVals[0][0].F inVec := vectorVals[1] var annos annotations.Annotations - if math.IsNaN(q) || q < 0 || q > 1 { - annos.Add(annotations.NewInvalidQuantileWarning(q, args[0].PositionRange())) + if err := validateQuantile(q, args[0]); err != nil { + annos.Add(err) } annos.Merge(enh.resetHistograms(inVec, args[1])) @@ -1672,7 +1734,7 @@ func funcHistogramQuantile(vectorVals []Vector, _ Matrix, args parser.Expression if !enh.enableDelayedNameRemoval { sample.Metric = sample.Metric.DropReserved(schema.IsMetadataLabel) } - hq, hqAnnos := HistogramQuantile(q, sample.H, sample.Metric.Get(model.MetricNameLabel), args[0].PositionRange()) + hq, hqAnnos := HistogramQuantile(q, sample.H, getMetricName(sample.Metric), args[0].PositionRange()) annos.Merge(hqAnnos) enh.Out = append(enh.Out, Sample{ Metric: sample.Metric, @@ -1684,13 +1746,13 @@ func funcHistogramQuantile(vectorVals []Vector, _ Matrix, args parser.Expression // Deal with classic histograms that have already been filtered for conflicting native histograms. for _, mb := range enh.signatureToMetricWithBuckets { if len(mb.buckets) > 0 { - res, forcedMonotonicity, _ := BucketQuantile(q, mb.buckets) + quantile, forcedMonotonicity, _, minBucket, maxBucket, maxDiff := BucketQuantile(q, mb.buckets) if forcedMonotonicity { + metricName := "" if enh.enableDelayedNameRemoval { - annos.Add(annotations.NewHistogramQuantileForcedMonotonicityInfo(mb.metric.Get(labels.MetricName), args[1].PositionRange())) - } else { - annos.Add(annotations.NewHistogramQuantileForcedMonotonicityInfo("", args[1].PositionRange())) + metricName = getMetricName(mb.metric) } + annos.Add(annotations.NewHistogramQuantileForcedMonotonicityInfo(metricName, args[1].PositionRange(), enh.Ts, minBucket, maxBucket, maxDiff)) } if !enh.enableDelayedNameRemoval { @@ -1699,7 +1761,7 @@ func funcHistogramQuantile(vectorVals []Vector, _ Matrix, args parser.Expression enh.Out = append(enh.Out, Sample{ Metric: mb.metric, - F: res, + F: quantile, DropName: true, }) } @@ -1708,6 +1770,89 @@ func funcHistogramQuantile(vectorVals []Vector, _ Matrix, args parser.Expression return enh.Out, annos } +func validateQuantile(q float64, arg parser.Expr) error { + if math.IsNaN(q) || q < 0 || q > 1 { + return annotations.NewInvalidQuantileWarning(q, arg.PositionRange()) + } + return nil +} + +// === histogram_quantiles(Vector parser.ValueTypeVector, label parser.ValueTypeString, q0 parser.ValueTypeScalar, qs parser.ValueTypeScalar...) (Vector, Annotations) === +func funcHistogramQuantiles(vectorVals []Vector, _ Matrix, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + var ( + inVec = vectorVals[0] + quantileLabel = args[1].(*parser.StringLiteral).Val + numQuantiles = len(vectorVals[2:]) + qs = make([]float64, 0, numQuantiles) + + annos annotations.Annotations + ) + + if enh.quantileStrs == nil { + enh.quantileStrs = make(map[float64]string, numQuantiles) + } + for i := 2; i < len(vectorVals); i++ { + q := vectorVals[i][0].F + + if err := validateQuantile(q, args[i]); err != nil { + annos.Add(err) + } + + if _, ok := enh.quantileStrs[q]; !ok { + enh.quantileStrs[q] = labels.FormatOpenMetricsFloat(q) + } + qs = append(qs, q) + } + + annos.Merge(enh.resetHistograms(inVec, args[0])) + + for _, q := range qs { + // Deal with the native histograms. + for _, sample := range enh.nativeHistogramSamples { + if sample.H == nil { + // Native histogram conflicts with classic histogram at the same timestamp, ignore. + continue + } + if !enh.enableDelayedNameRemoval { + sample.Metric = sample.Metric.DropReserved(schema.IsMetadataLabel) + } + hq, hqAnnos := HistogramQuantile(q, sample.H, sample.Metric.Get(model.MetricNameLabel), args[0].PositionRange()) + annos.Merge(hqAnnos) + enh.Out = append(enh.Out, Sample{ + Metric: enh.getOrCreateLblsWithQuantile(sample.Metric, quantileLabel, q), + F: hq, + DropName: true, + }) + } + + // Deal with classic histograms that have already been filtered for conflicting native histograms. + for _, mb := range enh.signatureToMetricWithBuckets { + if len(mb.buckets) > 0 { + hq, forcedMonotonicity, _, minBucket, maxBucket, maxDiff := BucketQuantile(q, mb.buckets) + if forcedMonotonicity { + metricName := "" + if enh.enableDelayedNameRemoval { + metricName = getMetricName(mb.metric) + } + annos.Add(annotations.NewHistogramQuantileForcedMonotonicityInfo(metricName, args[1].PositionRange(), enh.Ts, minBucket, maxBucket, maxDiff)) + } + + if !enh.enableDelayedNameRemoval { + mb.metric = mb.metric.DropReserved(schema.IsMetadataLabel) + } + + enh.Out = append(enh.Out, Sample{ + Metric: enh.getOrCreateLblsWithQuantile(mb.metric, quantileLabel, q), + F: hq, + DropName: true, + }) + } + } + } + + return enh.Out, annos +} + // pickFirstSampleIndex returns the index of the last sample before // or at the range start, or 0 if none exist before the range start. // If the vector selector is not anchored, it always returns 0, true. @@ -1727,6 +1872,9 @@ func pickFirstSampleIndex(floats []FPoint, args parser.Expressions, enh *EvalNod // === resets(Matrix parser.ValueTypeMatrix) (Vector, Annotations) === func funcResets(_ []Vector, matrixVal Matrix, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + if len(matrixVal) == 0 { + return enh.Out, nil + } floats := matrixVal[0].Floats histograms := matrixVal[0].Histograms resets := 0 @@ -1776,6 +1924,9 @@ func funcResets(_ []Vector, matrixVal Matrix, args parser.Expressions, enh *Eval // === changes(Matrix parser.ValueTypeMatrix) (Vector, Annotations) === func funcChanges(_ []Vector, matrixVal Matrix, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + if len(matrixVal) == 0 { + return enh.Out, nil + } floats := matrixVal[0].Floats histograms := matrixVal[0].Histograms changes := 0 @@ -2032,6 +2183,7 @@ var FunctionCalls = map[string]FunctionCall{ "histogram_count": funcHistogramCount, "histogram_fraction": funcHistogramFraction, "histogram_quantile": funcHistogramQuantile, + "histogram_quantiles": funcHistogramQuantiles, "histogram_sum": funcHistogramSum, "histogram_stddev": funcHistogramStdDev, "histogram_stdvar": funcHistogramStdVar, @@ -2224,3 +2376,7 @@ func stringSliceFromArgs(args parser.Expressions) []string { } return tmp } + +func getMetricName(metric labels.Labels) string { + return metric.Get(model.MetricNameLabel) +} diff --git a/vendor/github.com/prometheus/prometheus/promql/fuzz.go b/vendor/github.com/prometheus/prometheus/promql/fuzz.go index a71a63f8e..3fa28abe4 100644 --- a/vendor/github.com/prometheus/prometheus/promql/fuzz.go +++ b/vendor/github.com/prometheus/prometheus/promql/fuzz.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -60,6 +60,8 @@ const ( // Use package-scope symbol table to avoid memory allocation on every fuzzing operation. var symbolTable = labels.NewSymbolTable() +var fuzzParser = parser.NewParser(parser.Options{}) + func fuzzParseMetricWithContentType(in []byte, contentType string) int { p, warning := textparse.New(in, contentType, symbolTable, textparse.ParserOptions{}) if p == nil || warning != nil { @@ -103,7 +105,7 @@ func FuzzParseMetricSelector(in []byte) int { if len(in) > maxInputSize { return fuzzMeh } - _, err := parser.ParseMetricSelector(string(in)) + _, err := fuzzParser.ParseMetricSelector(string(in)) if err == nil { return fuzzInteresting } @@ -116,7 +118,7 @@ func FuzzParseExpr(in []byte) int { if len(in) > maxInputSize { return fuzzMeh } - _, err := parser.ParseExpr(string(in)) + _, err := fuzzParser.ParseExpr(string(in)) if err == nil { return fuzzInteresting } diff --git a/vendor/github.com/prometheus/prometheus/promql/histogram_stats_iterator.go b/vendor/github.com/prometheus/prometheus/promql/histogram_stats_iterator.go index e58cc7d84..87cc5acfb 100644 --- a/vendor/github.com/prometheus/prometheus/promql/histogram_stats_iterator.go +++ b/vendor/github.com/prometheus/prometheus/promql/histogram_stats_iterator.go @@ -1,4 +1,4 @@ -// Copyright 2024 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/promql/info.go b/vendor/github.com/prometheus/prometheus/promql/info.go index d5ffda6af..97a79cd0f 100644 --- a/vendor/github.com/prometheus/prometheus/promql/info.go +++ b/vendor/github.com/prometheus/prometheus/promql/info.go @@ -1,4 +1,4 @@ -// Copyright 2024 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -21,6 +21,7 @@ import ( "strings" "github.com/grafana/regexp" + "github.com/prometheus/common/model" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/promql/parser" @@ -46,24 +47,20 @@ func (ev *evaluator) evalInfo(ctx context.Context, args parser.Expressions) (par labelSelector := args[1].(*parser.VectorSelector) for _, m := range labelSelector.LabelMatchers { dataLabelMatchers[m.Name] = append(dataLabelMatchers[m.Name], m) - if m.Name == labels.MetricName { + if m.Name == model.MetricNameLabel { infoNameMatchers = append(infoNameMatchers, m) } } } else { - infoNameMatchers = []*labels.Matcher{labels.MustNewMatcher(labels.MatchEqual, labels.MetricName, targetInfo)} + infoNameMatchers = []*labels.Matcher{labels.MustNewMatcher(labels.MatchEqual, model.MetricNameLabel, targetInfo)} } // Don't try to enrich info series. ignoreSeries := map[uint64]struct{}{} -loop: for _, s := range mat { - name := s.Metric.Get(labels.MetricName) - for _, m := range infoNameMatchers { - if m.Matches(name) { - ignoreSeries[s.Metric.Hash()] = struct{}{} - continue loop - } + name := s.Metric.Get(model.MetricNameLabel) + if len(infoNameMatchers) > 0 && matchersMatch(infoNameMatchers, name) { + ignoreSeries[s.Metric.Hash()] = struct{}{} } } @@ -79,6 +76,15 @@ loop: return res, annots } +func matchersMatch(matchers []*labels.Matcher, value string) bool { + for _, m := range matchers { + if !m.Matches(value) { + return false + } + } + return true +} + // infoSelectHints calculates the storage.SelectHints for selecting info series, given expr (first argument to info call). func (ev *evaluator) infoSelectHints(expr parser.Expr) storage.SelectHints { var nodeTimestamp *int64 @@ -122,6 +128,19 @@ func (ev *evaluator) infoSelectHints(expr parser.Expr) storage.SelectHints { // Series in ignoreSeries are not fetched. // dataLabelMatchers may be mutated. func (ev *evaluator) fetchInfoSeries(ctx context.Context, mat Matrix, ignoreSeries map[uint64]struct{}, dataLabelMatchers map[string][]*labels.Matcher, selectHints storage.SelectHints) (Matrix, annotations.Annotations, error) { + removeNameFromDataLabelMatchers := func() { + for name, ms := range dataLabelMatchers { + ms = slices.DeleteFunc(ms, func(m *labels.Matcher) bool { + return m.Name == model.MetricNameLabel + }) + if len(ms) > 0 { + dataLabelMatchers[name] = ms + } else { + delete(dataLabelMatchers, name) + } + } + } + // A map of values for all identifying labels we are interested in. idLblValues := map[string]map[string]struct{}{} for _, s := range mat { @@ -143,6 +162,11 @@ func (ev *evaluator) fetchInfoSeries(ctx context.Context, mat Matrix, ignoreSeri } } if len(idLblValues) == 0 { + // Even when returning early, we need to remove __name__ from dataLabelMatchers + // since it's not a data label selector (it's used to select which info metrics + // to consider). Without this, combineWithInfoVector would incorrectly exclude + // series when only __name__ is specified in the selector. + removeNameFromDataLabelMatchers() return nil, nil, nil } @@ -166,24 +190,19 @@ func (ev *evaluator) fetchInfoSeries(ctx context.Context, mat Matrix, ignoreSeri for name, re := range idLblRegexps { infoLabelMatchers = append(infoLabelMatchers, labels.MustNewMatcher(labels.MatchRegexp, name, re)) } - var nameMatcher *labels.Matcher - for name, ms := range dataLabelMatchers { - for i, m := range ms { - if m.Name == labels.MetricName { - nameMatcher = m - ms = slices.Delete(ms, i, i+1) + hasNameMatcher := false + for _, ms := range dataLabelMatchers { + for _, m := range ms { + if m.Name == model.MetricNameLabel { + hasNameMatcher = true } infoLabelMatchers = append(infoLabelMatchers, m) } - if len(ms) > 0 { - dataLabelMatchers[name] = ms - } else { - delete(dataLabelMatchers, name) - } } - if nameMatcher == nil { + removeNameFromDataLabelMatchers() + if !hasNameMatcher { // Default to using the target_info metric. - infoLabelMatchers = append([]*labels.Matcher{labels.MustNewMatcher(labels.MatchEqual, labels.MetricName, targetInfo)}, infoLabelMatchers...) + infoLabelMatchers = append([]*labels.Matcher{labels.MustNewMatcher(labels.MatchEqual, model.MetricNameLabel, targetInfo)}, infoLabelMatchers...) } infoIt := ev.querier.Select(ctx, false, &selectHints, infoLabelMatchers...) @@ -203,7 +222,7 @@ func (ev *evaluator) combineWithInfoSeries(ctx context.Context, mat, infoMat Mat sigFunction := func(name string) func(labels.Labels) string { return func(lset labels.Labels) string { lb.Reset() - lb.Add(labels.MetricName, name) + lb.Add(model.MetricNameLabel, name) lset.MatchLabels(true, identifyingLabels...).Range(func(l labels.Label) { lb.Add(l.Name, l.Value) }) @@ -215,7 +234,7 @@ func (ev *evaluator) combineWithInfoSeries(ctx context.Context, mat, infoMat Mat infoMetrics := map[string]struct{}{} for _, is := range infoMat { lblMap := is.Metric.Map() - infoMetrics[lblMap[labels.MetricName]] = struct{}{} + infoMetrics[lblMap[model.MetricNameLabel]] = struct{}{} } sigfs := make(map[string]func(labels.Labels) string, len(infoMetrics)) for name := range infoMetrics { @@ -260,7 +279,7 @@ func (ev *evaluator) combineWithInfoSeries(ctx context.Context, mat, infoMat Mat infoSigs := make(map[uint64]string, len(infoMat)) for _, s := range infoMat { - name := s.Metric.Map()[labels.MetricName] + name := s.Metric.Map()[model.MetricNameLabel] infoSigs[s.Metric.Hash()] = sigfs[name](s.Metric) } @@ -398,7 +417,7 @@ func (ev *evaluator) combineWithInfoVector(base, info Vector, ignoreSeries map[u } err := is.Metric.Validate(func(l labels.Label) error { - if l.Name == labels.MetricName { + if l.Name == model.MetricNameLabel { return nil } if _, exists := dataLabelMatchers[l.Name]; len(dataLabelMatchers) > 0 && !exists { @@ -424,9 +443,10 @@ func (ev *evaluator) combineWithInfoVector(base, info Vector, ignoreSeries map[u } infoLbls := enh.lb.Labels() - if infoLbls.Len() == 0 { - // If there's at least one data label matcher not matching the empty string, - // we have to ignore this series as there are no matching info series. + if len(seenInfoMetrics) == 0 { + // No info series matched this base series. If there's at least one data + // label matcher not matching the empty string, we have to ignore this + // series as there are no matching info series. allMatchersMatchEmpty := true for _, ms := range dataLabelMatchers { for _, m := range ms { diff --git a/vendor/github.com/prometheus/prometheus/promql/parser/ast.go b/vendor/github.com/prometheus/prometheus/promql/parser/ast.go index 67ecb190f..649609528 100644 --- a/vendor/github.com/prometheus/prometheus/promql/parser/ast.go +++ b/vendor/github.com/prometheus/prometheus/promql/parser/ast.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -116,8 +116,8 @@ type DurationExpr struct { LHS, RHS Expr // The operands on the respective sides of the operator. Wrapped bool // Set when the duration is wrapped in parentheses. - StartPos posrange.Pos // For unary operations and step(), the start position of the operator. - EndPos posrange.Pos // For step(), the end position of the operator. + StartPos posrange.Pos // For unary operations, step(), and range(), the start position of the operator. + EndPos posrange.Pos // For step() and range(), the end position of the operator. } // Call represents a function call. @@ -318,6 +318,19 @@ type VectorMatching struct { // Include contains additional labels that should be included in // the result from the side with the lower cardinality. Include []string + // Fill-in values to use when a series from one side does not find a match on the other side. + FillValues VectorMatchFillValues +} + +// VectorMatchFillValues contains the fill values to use for Vector matching +// when one side does not find a match on the other side. +// When a fill value is nil, no fill is applied for that side, and there +// is no output for the match group if there is no match. +type VectorMatchFillValues struct { + // RHS is the fill value to use for the right-hand side. + RHS *float64 + // LHS is the fill value to use for the left-hand side. + LHS *float64 } // Visitor allows visiting a Node and its child nodes. The Visit method is @@ -474,7 +487,7 @@ func (e *BinaryExpr) PositionRange() posrange.PositionRange { } func (e *DurationExpr) PositionRange() posrange.PositionRange { - if e.Op == STEP { + if e.Op == STEP || e.Op == RANGE { return posrange.PositionRange{ Start: e.StartPos, End: e.EndPos, diff --git a/vendor/github.com/prometheus/prometheus/promql/parser/features.go b/vendor/github.com/prometheus/prometheus/promql/parser/features.go index ec6467823..3bd3c493f 100644 --- a/vendor/github.com/prometheus/prometheus/promql/parser/features.go +++ b/vendor/github.com/prometheus/prometheus/promql/parser/features.go @@ -18,14 +18,15 @@ import "github.com/prometheus/prometheus/util/features" // RegisterFeatures registers all PromQL features with the feature registry. // This includes operators (arithmetic and comparison/set), aggregators (standard // and experimental), and functions. -func RegisterFeatures(r features.Collector) { +func (pql *promQLParser) RegisterFeatures(r features.Collector) { // Register core PromQL language keywords. for keyword, itemType := range key { if itemType.IsKeyword() { - // Handle experimental keywords separately. switch keyword { case "anchored", "smoothed": - r.Set(features.PromQL, keyword, EnableExtendedRangeSelectors) + r.Set(features.PromQL, keyword, pql.options.EnableExtendedRangeSelectors) + case "fill", "fill_left", "fill_right": + r.Set(features.PromQL, keyword, pql.options.EnableBinopFillModifiers) default: r.Enable(features.PromQL, keyword) } @@ -42,16 +43,16 @@ func RegisterFeatures(r features.Collector) { // Register aggregators. for a := ItemType(aggregatorsStart + 1); a < aggregatorsEnd; a++ { if a.IsAggregator() { - experimental := a.IsExperimentalAggregator() && !EnableExperimentalFunctions + experimental := a.IsExperimentalAggregator() && !pql.options.EnableExperimentalFunctions r.Set(features.PromQLOperators, a.String(), !experimental) } } // Register functions. for f, fc := range Functions { - r.Set(features.PromQLFunctions, f, !fc.Experimental || EnableExperimentalFunctions) + r.Set(features.PromQLFunctions, f, !fc.Experimental || pql.options.EnableExperimentalFunctions) } // Register experimental parser features. - r.Set(features.PromQL, "duration_expr", ExperimentalDurationExpr) + r.Set(features.PromQL, "duration_expr", pql.options.ExperimentalDurationExpr) } diff --git a/vendor/github.com/prometheus/prometheus/promql/parser/functions.go b/vendor/github.com/prometheus/prometheus/promql/parser/functions.go index a471cb3a6..180a255ab 100644 --- a/vendor/github.com/prometheus/prometheus/promql/parser/functions.go +++ b/vendor/github.com/prometheus/prometheus/promql/parser/functions.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -23,9 +23,6 @@ type Function struct { Experimental bool } -// EnableExperimentalFunctions controls whether experimentalFunctions are enabled. -var EnableExperimentalFunctions bool - // Functions is a list of all functions supported by PromQL, including their types. var Functions = map[string]*Function{ "abs": { @@ -208,6 +205,13 @@ var Functions = map[string]*Function{ ArgTypes: []ValueType{ValueTypeScalar, ValueTypeVector}, ReturnType: ValueTypeVector, }, + "histogram_quantiles": { + Name: "histogram_quantiles", + ArgTypes: []ValueType{ValueTypeVector, ValueTypeString, ValueTypeScalar, ValueTypeScalar}, + Variadic: 9, + ReturnType: ValueTypeVector, + Experimental: true, + }, "double_exponential_smoothing": { Name: "double_exponential_smoothing", ArgTypes: []ValueType{ValueTypeMatrix, ValueTypeScalar, ValueTypeScalar}, diff --git a/vendor/github.com/prometheus/prometheus/promql/parser/generated_parser.y b/vendor/github.com/prometheus/prometheus/promql/parser/generated_parser.y index d9bbb10b2..1196002b7 100644 --- a/vendor/github.com/prometheus/prometheus/promql/parser/generated_parser.y +++ b/vendor/github.com/prometheus/prometheus/promql/parser/generated_parser.y @@ -139,6 +139,9 @@ BOOL BY GROUP_LEFT GROUP_RIGHT +FILL +FILL_LEFT +FILL_RIGHT IGNORING OFFSET SMOOTHED @@ -153,6 +156,7 @@ WITHOUT START END STEP +RANGE %token preprocessorEnd // Counter reset hints. @@ -189,7 +193,7 @@ START_METRIC_SELECTOR %type int %type uint %type number series_value signed_number signed_or_unsigned_number -%type step_invariant_expr aggregate_expr aggregate_modifier bin_modifier binary_expr bool_modifier expr function_call function_call_args function_call_body group_modifiers label_matchers matrix_selector number_duration_literal offset_expr anchored_expr smoothed_expr on_or_ignoring paren_expr string_literal subquery_expr unary_expr vector_selector duration_expr paren_duration_expr positive_duration_expr offset_duration_expr +%type step_invariant_expr aggregate_expr aggregate_modifier bin_modifier fill_modifiers binary_expr bool_modifier expr function_call function_call_args function_call_body group_modifiers fill_value label_matchers matrix_selector number_duration_literal offset_expr anchored_expr smoothed_expr on_or_ignoring paren_expr string_literal subquery_expr unary_expr vector_selector duration_expr paren_duration_expr positive_duration_expr offset_duration_expr %start start @@ -301,7 +305,7 @@ binary_expr : expr ADD bin_modifier expr { $$ = yylex.(*parser).newBinar // Using left recursion for the modifier rules, helps to keep the parser stack small and // reduces allocations. -bin_modifier : group_modifiers; +bin_modifier : fill_modifiers; bool_modifier : /* empty */ { $$ = &BinaryExpr{ @@ -345,6 +349,47 @@ group_modifiers: bool_modifier /* empty */ } ; +fill_modifiers: group_modifiers /* empty */ + /* Only fill() */ + | group_modifiers FILL fill_value + { + $$ = $1 + fill := $3.(*NumberLiteral).Val + $$.(*BinaryExpr).VectorMatching.FillValues.LHS = &fill + $$.(*BinaryExpr).VectorMatching.FillValues.RHS = &fill + } + /* Only fill_left() */ + | group_modifiers FILL_LEFT fill_value + { + $$ = $1 + fill := $3.(*NumberLiteral).Val + $$.(*BinaryExpr).VectorMatching.FillValues.LHS = &fill + } + /* Only fill_right() */ + | group_modifiers FILL_RIGHT fill_value + { + $$ = $1 + fill := $3.(*NumberLiteral).Val + $$.(*BinaryExpr).VectorMatching.FillValues.RHS = &fill + } + /* fill_left() fill_right() */ + | group_modifiers FILL_LEFT fill_value FILL_RIGHT fill_value + { + $$ = $1 + fill_left := $3.(*NumberLiteral).Val + fill_right := $5.(*NumberLiteral).Val + $$.(*BinaryExpr).VectorMatching.FillValues.LHS = &fill_left + $$.(*BinaryExpr).VectorMatching.FillValues.RHS = &fill_right + } + /* fill_right() fill_left() */ + | group_modifiers FILL_RIGHT fill_value FILL_LEFT fill_value + { + fill_right := $3.(*NumberLiteral).Val + fill_left := $5.(*NumberLiteral).Val + $$.(*BinaryExpr).VectorMatching.FillValues.LHS = &fill_left + $$.(*BinaryExpr).VectorMatching.FillValues.RHS = &fill_right + } + ; grouping_labels : LEFT_PAREN grouping_label_list RIGHT_PAREN { $$ = $2 } @@ -386,6 +431,21 @@ grouping_label : maybe_label { yylex.(*parser).unexpected("grouping opts", "label"); $$ = Item{} } ; +fill_value : LEFT_PAREN number_duration_literal RIGHT_PAREN + { + $$ = $2.(*NumberLiteral) + } + | LEFT_PAREN unary_op number_duration_literal RIGHT_PAREN + { + nl := $3.(*NumberLiteral) + if $2.Typ == SUB { + nl.Val *= -1 + } + nl.PosRange.Start = $2.Pos + $$ = nl + } + ; + /* * Function calls. */ @@ -396,7 +456,7 @@ function_call : IDENTIFIER function_call_body if !exist{ yylex.(*parser).addParseErrf($1.PositionRange(),"unknown function with name %q", $1.Val) } - if fn != nil && fn.Experimental && !EnableExperimentalFunctions { + if fn != nil && fn.Experimental && !yylex.(*parser).options.EnableExperimentalFunctions { yylex.(*parser).addParseErrf($1.PositionRange(),"function %q is not enabled", $1.Val) } $$ = &Call{ @@ -465,7 +525,7 @@ offset_expr: expr OFFSET offset_duration_expr $$ = $1 } | expr OFFSET error - { yylex.(*parser).unexpected("offset", "number, duration, or step()"); $$ = $1 } + { yylex.(*parser).unexpected("offset", "number, duration, step(), or range()"); $$ = $1 } ; /* @@ -575,11 +635,11 @@ subquery_expr : expr LEFT_BRACKET positive_duration_expr COLON positive_durati | expr LEFT_BRACKET positive_duration_expr COLON positive_duration_expr error { yylex.(*parser).unexpected("subquery selector", "\"]\""); $$ = $1 } | expr LEFT_BRACKET positive_duration_expr COLON error - { yylex.(*parser).unexpected("subquery selector", "number, duration, or step() or \"]\""); $$ = $1 } + { yylex.(*parser).unexpected("subquery selector", "number, duration, step(), range(), or \"]\""); $$ = $1 } | expr LEFT_BRACKET positive_duration_expr error { yylex.(*parser).unexpected("subquery or range", "\":\" or \"]\""); $$ = $1 } | expr LEFT_BRACKET error - { yylex.(*parser).unexpected("subquery or range selector", "number, duration, or step()"); $$ = $1 } + { yylex.(*parser).unexpected("subquery or range selector", "number, duration, step(), or range()"); $$ = $1 } ; /* @@ -696,7 +756,7 @@ metric : metric_identifier label_set ; -metric_identifier: AVG | BOTTOMK | BY | COUNT | COUNT_VALUES | GROUP | IDENTIFIER | LAND | LOR | LUNLESS | MAX | METRIC_IDENTIFIER | MIN | OFFSET | QUANTILE | STDDEV | STDVAR | SUM | TOPK | WITHOUT | START | END | LIMITK | LIMIT_RATIO | STEP | ANCHORED | SMOOTHED; +metric_identifier: AVG | BOTTOMK | BY | COUNT | COUNT_VALUES | FILL | FILL_LEFT | FILL_RIGHT | GROUP | IDENTIFIER | LAND | LOR | LUNLESS | MAX | METRIC_IDENTIFIER | MIN | OFFSET | QUANTILE | STDDEV | STDVAR | SUM | TOPK | WITHOUT | START | END | LIMITK | LIMIT_RATIO | STEP | RANGE | ANCHORED | SMOOTHED; label_set : LEFT_BRACE label_set_list RIGHT_BRACE { $$ = labels.New($2...) } @@ -790,14 +850,15 @@ series_item : BLANK // Histogram descriptions (part of unit testing). | histogram_series_value { - $$ = []SequenceValue{{Histogram:$1}} + $$ = []SequenceValue{yylex.(*parser).newHistogramSequenceValue($1)} } | histogram_series_value TIMES uint { $$ = []SequenceValue{} // Add an additional value for time 0, which we ignore in tests. + sv := yylex.(*parser).newHistogramSequenceValue($1) for i:=uint64(0); i <= $3; i++{ - $$ = append($$, SequenceValue{Histogram:$1}) + $$ = append($$, sv) //$1 += $2 } } @@ -953,7 +1014,7 @@ counter_reset_hint : UNKNOWN_COUNTER_RESET | COUNTER_RESET | NOT_COUNTER_RESET | aggregate_op : AVG | BOTTOMK | COUNT | COUNT_VALUES | GROUP | MAX | MIN | QUANTILE | STDDEV | STDVAR | SUM | TOPK | LIMITK | LIMIT_RATIO; // Inside of grouping options label names can be recognized as keywords by the lexer. This is a list of keywords that could also be a label name. -maybe_label : AVG | BOOL | BOTTOMK | BY | COUNT | COUNT_VALUES | GROUP | GROUP_LEFT | GROUP_RIGHT | IDENTIFIER | IGNORING | LAND | LOR | LUNLESS | MAX | METRIC_IDENTIFIER | MIN | OFFSET | ON | QUANTILE | STDDEV | STDVAR | SUM | TOPK | START | END | ATAN2 | LIMITK | LIMIT_RATIO | STEP | ANCHORED | SMOOTHED; +maybe_label : AVG | BOOL | BOTTOMK | BY | COUNT | COUNT_VALUES | GROUP | GROUP_LEFT | GROUP_RIGHT | FILL | FILL_LEFT | FILL_RIGHT | IDENTIFIER | IGNORING | LAND | LOR | LUNLESS | MAX | METRIC_IDENTIFIER | MIN | OFFSET | ON | QUANTILE | STDDEV | STDVAR | SUM | TOPK | START | END | ATAN2 | LIMITK | LIMIT_RATIO | STEP | RANGE | ANCHORED | SMOOTHED; unary_op : ADD | SUB; @@ -1088,6 +1149,14 @@ offset_duration_expr : number_duration_literal EndPos: $3.PositionRange().End, } } + | RANGE LEFT_PAREN RIGHT_PAREN + { + $$ = &DurationExpr{ + Op: RANGE, + StartPos: $1.PositionRange().Start, + EndPos: $3.PositionRange().End, + } + } | unary_op STEP LEFT_PAREN RIGHT_PAREN { $$ = &DurationExpr{ @@ -1100,6 +1169,18 @@ offset_duration_expr : number_duration_literal StartPos: $1.Pos, } } + | unary_op RANGE LEFT_PAREN RIGHT_PAREN + { + $$ = &DurationExpr{ + Op: $1.Typ, + RHS: &DurationExpr{ + Op: RANGE, + StartPos: $2.PositionRange().Start, + EndPos: $4.PositionRange().End, + }, + StartPos: $1.Pos, + } + } | min_max LEFT_PAREN duration_expr COMMA duration_expr RIGHT_PAREN { $$ = &DurationExpr{ @@ -1141,7 +1222,7 @@ offset_duration_expr : number_duration_literal } | duration_expr ; - + min_max: MIN | MAX ; duration_expr : number_duration_literal @@ -1234,6 +1315,14 @@ duration_expr : number_duration_literal EndPos: $3.PositionRange().End, } } + | RANGE LEFT_PAREN RIGHT_PAREN + { + $$ = &DurationExpr{ + Op: RANGE, + StartPos: $1.PositionRange().Start, + EndPos: $3.PositionRange().End, + } + } | min_max LEFT_PAREN duration_expr COMMA duration_expr RIGHT_PAREN { $$ = &DurationExpr{ @@ -1248,14 +1337,14 @@ duration_expr : number_duration_literal ; paren_duration_expr : LEFT_PAREN duration_expr RIGHT_PAREN - { + { yylex.(*parser).experimentalDurationExpr($2.(Expr)) if durationExpr, ok := $2.(*DurationExpr); ok { durationExpr.Wrapped = true $$ = durationExpr break } - $$ = $2 + $$ = $2 } ; diff --git a/vendor/github.com/prometheus/prometheus/promql/parser/generated_parser.y.go b/vendor/github.com/prometheus/prometheus/promql/parser/generated_parser.y.go index eb4b32129..3a69f5551 100644 --- a/vendor/github.com/prometheus/prometheus/promql/parser/generated_parser.y.go +++ b/vendor/github.com/prometheus/prometheus/promql/parser/generated_parser.y.go @@ -113,30 +113,34 @@ const BOOL = 57420 const BY = 57421 const GROUP_LEFT = 57422 const GROUP_RIGHT = 57423 -const IGNORING = 57424 -const OFFSET = 57425 -const SMOOTHED = 57426 -const ANCHORED = 57427 -const ON = 57428 -const WITHOUT = 57429 -const keywordsEnd = 57430 -const preprocessorStart = 57431 -const START = 57432 -const END = 57433 -const STEP = 57434 -const preprocessorEnd = 57435 -const counterResetHintsStart = 57436 -const UNKNOWN_COUNTER_RESET = 57437 -const COUNTER_RESET = 57438 -const NOT_COUNTER_RESET = 57439 -const GAUGE_TYPE = 57440 -const counterResetHintsEnd = 57441 -const startSymbolsStart = 57442 -const START_METRIC = 57443 -const START_SERIES_DESCRIPTION = 57444 -const START_EXPRESSION = 57445 -const START_METRIC_SELECTOR = 57446 -const startSymbolsEnd = 57447 +const FILL = 57424 +const FILL_LEFT = 57425 +const FILL_RIGHT = 57426 +const IGNORING = 57427 +const OFFSET = 57428 +const SMOOTHED = 57429 +const ANCHORED = 57430 +const ON = 57431 +const WITHOUT = 57432 +const keywordsEnd = 57433 +const preprocessorStart = 57434 +const START = 57435 +const END = 57436 +const STEP = 57437 +const RANGE = 57438 +const preprocessorEnd = 57439 +const counterResetHintsStart = 57440 +const UNKNOWN_COUNTER_RESET = 57441 +const COUNTER_RESET = 57442 +const NOT_COUNTER_RESET = 57443 +const GAUGE_TYPE = 57444 +const counterResetHintsEnd = 57445 +const startSymbolsStart = 57446 +const START_METRIC = 57447 +const START_SERIES_DESCRIPTION = 57448 +const START_EXPRESSION = 57449 +const START_METRIC_SELECTOR = 57450 +const startSymbolsEnd = 57451 var yyToknames = [...]string{ "$end", @@ -220,6 +224,9 @@ var yyToknames = [...]string{ "BY", "GROUP_LEFT", "GROUP_RIGHT", + "FILL", + "FILL_LEFT", + "FILL_RIGHT", "IGNORING", "OFFSET", "SMOOTHED", @@ -231,6 +238,7 @@ var yyToknames = [...]string{ "START", "END", "STEP", + "RANGE", "preprocessorEnd", "counterResetHintsStart", "UNKNOWN_COUNTER_RESET", @@ -256,376 +264,403 @@ var yyExca = [...]int16{ -1, 1, 1, -1, -2, 0, - -1, 40, - 1, 149, - 10, 149, - 24, 149, + -1, 44, + 1, 161, + 10, 161, + 24, 161, -2, 0, - -1, 70, - 2, 192, - 15, 192, - 79, 192, - 87, 192, - -2, 107, - -1, 71, - 2, 193, - 15, 193, - 79, 193, - 87, 193, - -2, 108, - -1, 72, - 2, 194, - 15, 194, - 79, 194, - 87, 194, - -2, 110, - -1, 73, - 2, 195, - 15, 195, - 79, 195, - 87, 195, - -2, 111, - -1, 74, - 2, 196, - 15, 196, - 79, 196, - 87, 196, - -2, 112, -1, 75, - 2, 197, - 15, 197, - 79, 197, - 87, 197, - -2, 117, - -1, 76, - 2, 198, - 15, 198, - 79, 198, - 87, 198, - -2, 119, - -1, 77, - 2, 199, - 15, 199, - 79, 199, - 87, 199, - -2, 121, - -1, 78, - 2, 200, - 15, 200, - 79, 200, - 87, 200, - -2, 122, - -1, 79, - 2, 201, - 15, 201, - 79, 201, - 87, 201, - -2, 123, - -1, 80, - 2, 202, - 15, 202, - 79, 202, - 87, 202, - -2, 124, - -1, 81, - 2, 203, - 15, 203, - 79, 203, - 87, 203, - -2, 125, - -1, 82, 2, 204, 15, 204, 79, 204, - 87, 204, - -2, 129, - -1, 83, + 90, 204, + -2, 115, + -1, 76, 2, 205, 15, 205, 79, 205, - 87, 205, + 90, 205, + -2, 116, + -1, 77, + 2, 206, + 15, 206, + 79, 206, + 90, 206, + -2, 118, + -1, 78, + 2, 207, + 15, 207, + 79, 207, + 90, 207, + -2, 119, + -1, 79, + 2, 208, + 15, 208, + 79, 208, + 90, 208, + -2, 123, + -1, 80, + 2, 209, + 15, 209, + 79, 209, + 90, 209, + -2, 128, + -1, 81, + 2, 210, + 15, 210, + 79, 210, + 90, 210, -2, 130, - -1, 135, - 41, 270, - 42, 270, - 52, 270, - 53, 270, - 57, 270, + -1, 82, + 2, 211, + 15, 211, + 79, 211, + 90, 211, + -2, 132, + -1, 83, + 2, 212, + 15, 212, + 79, 212, + 90, 212, + -2, 133, + -1, 84, + 2, 213, + 15, 213, + 79, 213, + 90, 213, + -2, 134, + -1, 85, + 2, 214, + 15, 214, + 79, 214, + 90, 214, + -2, 135, + -1, 86, + 2, 215, + 15, 215, + 79, 215, + 90, 215, + -2, 136, + -1, 87, + 2, 216, + 15, 216, + 79, 216, + 90, 216, + -2, 140, + -1, 88, + 2, 217, + 15, 217, + 79, 217, + 90, 217, + -2, 141, + -1, 140, + 41, 288, + 42, 288, + 52, 288, + 53, 288, + 57, 288, -2, 22, - -1, 245, - 9, 257, - 12, 257, - 13, 257, - 18, 257, - 19, 257, - 25, 257, - 41, 257, - 47, 257, - 48, 257, - 51, 257, - 57, 257, - 62, 257, - 63, 257, - 64, 257, - 65, 257, - 66, 257, - 67, 257, - 68, 257, - 69, 257, - 70, 257, - 71, 257, - 72, 257, - 73, 257, - 74, 257, - 75, 257, - 79, 257, - 83, 257, - 84, 257, - 85, 257, - 87, 257, - 90, 257, - 91, 257, - 92, 257, + -1, 258, + 9, 273, + 12, 273, + 13, 273, + 18, 273, + 19, 273, + 25, 273, + 41, 273, + 47, 273, + 48, 273, + 51, 273, + 57, 273, + 62, 273, + 63, 273, + 64, 273, + 65, 273, + 66, 273, + 67, 273, + 68, 273, + 69, 273, + 70, 273, + 71, 273, + 72, 273, + 73, 273, + 74, 273, + 75, 273, + 79, 273, + 82, 273, + 83, 273, + 84, 273, + 86, 273, + 87, 273, + 88, 273, + 90, 273, + 93, 273, + 94, 273, + 95, 273, + 96, 273, -2, 0, - -1, 246, - 9, 257, - 12, 257, - 13, 257, - 18, 257, - 19, 257, - 25, 257, - 41, 257, - 47, 257, - 48, 257, - 51, 257, - 57, 257, - 62, 257, - 63, 257, - 64, 257, - 65, 257, - 66, 257, - 67, 257, - 68, 257, - 69, 257, - 70, 257, - 71, 257, - 72, 257, - 73, 257, - 74, 257, - 75, 257, - 79, 257, - 83, 257, - 84, 257, - 85, 257, - 87, 257, - 90, 257, - 91, 257, - 92, 257, + -1, 259, + 9, 273, + 12, 273, + 13, 273, + 18, 273, + 19, 273, + 25, 273, + 41, 273, + 47, 273, + 48, 273, + 51, 273, + 57, 273, + 62, 273, + 63, 273, + 64, 273, + 65, 273, + 66, 273, + 67, 273, + 68, 273, + 69, 273, + 70, 273, + 71, 273, + 72, 273, + 73, 273, + 74, 273, + 75, 273, + 79, 273, + 82, 273, + 83, 273, + 84, 273, + 86, 273, + 87, 273, + 88, 273, + 90, 273, + 93, 273, + 94, 273, + 95, 273, + 96, 273, -2, 0, } const yyPrivate = 57344 -const yyLast = 1071 +const yyLast = 1224 var yyAct = [...]int16{ - 57, 182, 401, 399, 185, 406, 278, 237, 193, 332, - 93, 47, 346, 141, 68, 221, 91, 413, 414, 415, - 416, 127, 128, 64, 156, 186, 66, 126, 347, 326, - 129, 243, 122, 125, 130, 244, 245, 246, 119, 122, - 118, 124, 123, 121, 327, 151, 124, 118, 214, 123, - 121, 396, 373, 124, 120, 364, 395, 366, 323, 385, - 328, 354, 352, 133, 216, 135, 6, 98, 100, 101, - 364, 102, 103, 104, 105, 106, 107, 108, 109, 110, - 111, 324, 112, 113, 117, 99, 42, 131, 315, 112, - 144, 117, 136, 400, 241, 350, 191, 143, 128, 349, - 142, 137, 270, 314, 322, 320, 129, 268, 317, 114, - 116, 115, 192, 95, 233, 178, 114, 116, 115, 195, - 199, 200, 201, 202, 203, 204, 174, 321, 319, 177, - 196, 196, 196, 196, 196, 196, 196, 232, 175, 217, - 267, 130, 197, 197, 197, 197, 197, 197, 197, 132, - 196, 134, 138, 205, 390, 407, 239, 207, 210, 227, - 206, 223, 197, 229, 428, 2, 3, 4, 5, 360, - 190, 194, 429, 389, 359, 7, 266, 240, 61, 86, - 189, 231, 269, 427, 181, 150, 426, 262, 60, 358, - 264, 119, 122, 196, 425, 209, 271, 272, 266, 197, - 152, 225, 123, 121, 230, 197, 124, 120, 208, 196, - 84, 224, 226, 119, 122, 38, 384, 213, 222, 383, - 223, 197, 10, 382, 123, 121, 85, 235, 124, 120, - 143, 190, 88, 318, 238, 381, 180, 179, 241, 242, - 380, 189, 379, 378, 247, 248, 249, 250, 251, 252, - 253, 254, 255, 256, 257, 258, 259, 260, 261, 348, - 225, 198, 325, 191, 94, 377, 351, 376, 97, 353, - 224, 226, 344, 345, 92, 195, 375, 196, 374, 192, - 196, 39, 228, 355, 61, 55, 196, 95, 1, 197, - 181, 87, 197, 149, 60, 148, 172, 69, 197, 54, - 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, - 167, 168, 169, 170, 171, 417, 84, 362, 65, 53, - 190, 9, 9, 144, 52, 51, 363, 365, 196, 367, - 189, 155, 85, 142, 275, 368, 369, 184, 274, 50, - 197, 140, 180, 179, 190, 49, 95, 48, 372, 119, - 122, 386, 191, 273, 189, 8, 46, 153, 211, 40, - 123, 121, 196, 371, 124, 120, 392, 198, 192, 394, - 370, 388, 94, 45, 197, 154, 191, 402, 403, 404, - 398, 44, 92, 405, 43, 409, 408, 411, 410, 418, - 90, 281, 192, 56, 236, 95, 422, 316, 419, 420, - 196, 291, 361, 421, 393, 119, 122, 297, 329, 423, - 96, 391, 197, 234, 280, 276, 123, 121, 424, 89, - 124, 120, 412, 119, 122, 187, 188, 183, 431, 196, - 279, 119, 122, 58, 123, 121, 293, 294, 124, 120, - 295, 197, 123, 121, 139, 0, 124, 120, 308, 0, - 0, 282, 284, 286, 287, 288, 296, 298, 301, 302, - 303, 304, 305, 309, 310, 0, 281, 283, 285, 289, - 290, 292, 299, 313, 312, 300, 291, 0, 220, 306, - 307, 311, 297, 219, 0, 0, 277, 387, 0, 280, - 147, 0, 190, 61, 0, 146, 218, 0, 0, 265, - 0, 0, 189, 60, 430, 0, 119, 122, 145, 0, - 0, 293, 294, 0, 0, 295, 0, 123, 121, 0, - 0, 124, 120, 308, 191, 84, 282, 284, 286, 287, - 288, 296, 298, 301, 302, 303, 304, 305, 309, 310, - 192, 85, 283, 285, 289, 290, 292, 299, 313, 312, - 300, 180, 179, 0, 306, 307, 311, 61, 0, 118, - 59, 86, 0, 62, 0, 0, 22, 60, 0, 0, - 212, 0, 0, 63, 0, 0, 263, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 98, 100, 0, 84, - 0, 0, 0, 0, 0, 18, 19, 109, 110, 20, - 0, 112, 113, 117, 99, 85, 0, 0, 0, 0, - 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 0, 0, 0, 13, 114, 116, - 115, 24, 37, 36, 215, 30, 0, 0, 31, 32, - 67, 61, 41, 0, 59, 86, 0, 62, 0, 0, - 22, 60, 0, 119, 122, 0, 0, 63, 0, 0, - 0, 0, 0, 0, 123, 121, 0, 0, 124, 120, - 0, 357, 0, 84, 0, 0, 0, 0, 61, 18, - 19, 0, 0, 20, 181, 0, 0, 0, 60, 85, - 356, 0, 0, 0, 70, 71, 72, 73, 74, 75, - 76, 77, 78, 79, 80, 81, 82, 83, 0, 0, - 84, 13, 0, 0, 0, 24, 37, 36, 0, 30, - 0, 0, 31, 32, 67, 61, 85, 0, 59, 86, - 0, 62, 331, 0, 22, 60, 180, 179, 0, 330, - 0, 63, 0, 334, 335, 333, 340, 342, 339, 341, - 336, 337, 338, 343, 0, 0, 0, 84, 0, 0, - 0, 198, 0, 18, 19, 0, 0, 20, 0, 0, - 0, 0, 0, 85, 0, 0, 0, 0, 70, 71, - 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, - 82, 83, 17, 86, 0, 13, 0, 0, 22, 24, - 37, 36, 397, 30, 0, 0, 31, 32, 67, 0, - 0, 0, 0, 334, 335, 333, 340, 342, 339, 341, - 336, 337, 338, 343, 0, 0, 0, 18, 19, 0, - 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 11, 12, 14, 15, 16, 21, 23, 25, - 26, 27, 28, 29, 33, 34, 17, 38, 0, 13, - 0, 0, 22, 24, 37, 36, 0, 30, 0, 0, - 31, 32, 35, 0, 0, 0, 0, 0, 0, 0, + 61, 363, 190, 429, 351, 436, 431, 293, 247, 201, + 98, 51, 147, 193, 369, 96, 231, 412, 413, 370, + 132, 133, 68, 130, 73, 163, 194, 131, 443, 444, + 445, 446, 134, 135, 256, 253, 254, 255, 257, 258, + 259, 129, 70, 426, 123, 425, 124, 127, 391, 342, + 157, 458, 223, 198, 447, 389, 415, 128, 126, 345, + 451, 129, 125, 197, 414, 465, 398, 138, 379, 140, + 6, 103, 105, 106, 346, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 199, 117, 118, 122, 104, + 347, 136, 343, 46, 124, 127, 389, 133, 334, 251, + 397, 200, 149, 377, 192, 128, 126, 199, 134, 129, + 125, 198, 141, 333, 420, 396, 119, 121, 120, 123, + 186, 197, 395, 200, 203, 208, 209, 210, 211, 212, + 213, 181, 376, 419, 430, 204, 204, 204, 204, 204, + 204, 204, 182, 199, 185, 227, 205, 205, 205, 205, + 205, 205, 205, 216, 219, 215, 204, 341, 214, 200, + 137, 117, 139, 122, 339, 385, 237, 205, 239, 464, + 384, 249, 226, 2, 3, 4, 5, 91, 290, 225, + 340, 123, 289, 280, 250, 383, 364, 338, 124, 127, + 284, 119, 121, 120, 275, 195, 196, 288, 218, 128, + 126, 204, 460, 129, 125, 205, 280, 278, 158, 105, + 374, 217, 205, 286, 287, 423, 243, 204, 241, 114, + 115, 124, 127, 117, 373, 122, 104, 372, 205, 222, + 143, 437, 128, 126, 124, 127, 129, 125, 65, 242, + 149, 240, 337, 142, 42, 128, 126, 418, 64, 129, + 125, 285, 252, 119, 121, 120, 365, 366, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 344, 371, 127, 367, 368, 198, 283, + 375, 124, 127, 282, 378, 128, 126, 281, 197, 129, + 203, 204, 128, 126, 135, 204, 129, 125, 198, 380, + 65, 204, 205, 144, 7, 409, 205, 408, 197, 407, + 64, 406, 205, 164, 165, 166, 167, 168, 169, 170, + 171, 172, 173, 174, 175, 176, 177, 178, 202, 232, + 199, 233, 89, 156, 417, 65, 387, 405, 463, 233, + 404, 189, 102, 224, 403, 64, 200, 204, 90, 388, + 390, 10, 392, 124, 127, 393, 394, 462, 205, 402, + 461, 93, 124, 127, 128, 126, 401, 89, 129, 125, + 400, 235, 399, 128, 126, 416, 410, 129, 125, 235, + 8, 234, 236, 90, 44, 59, 204, 411, 43, 234, + 236, 92, 422, 188, 187, 1, 179, 205, 424, 155, + 428, 154, 230, 432, 433, 434, 150, 229, 74, 335, + 439, 438, 441, 440, 449, 450, 148, 435, 58, 452, + 228, 206, 207, 448, 336, 57, 296, 56, 386, 100, + 204, 69, 453, 454, 9, 9, 309, 455, 99, 55, + 457, 205, 315, 124, 127, 162, 421, 150, 97, 295, + 99, 54, 459, 53, 128, 126, 238, 148, 129, 125, + 97, 100, 153, 204, 466, 146, 52, 152, 95, 50, + 100, 311, 312, 100, 205, 313, 160, 220, 49, 161, + 151, 48, 159, 326, 47, 60, 297, 299, 301, 302, + 303, 314, 316, 319, 320, 321, 322, 323, 327, 328, + 246, 456, 298, 300, 304, 305, 306, 307, 308, 310, + 317, 332, 331, 318, 296, 348, 101, 324, 325, 329, + 330, 245, 244, 291, 309, 198, 94, 442, 248, 191, + 315, 350, 251, 294, 292, 197, 62, 295, 349, 145, + 0, 0, 353, 354, 352, 359, 361, 358, 360, 355, + 356, 357, 362, 0, 0, 0, 0, 199, 0, 311, + 312, 0, 0, 313, 0, 0, 0, 0, 0, 0, + 0, 326, 0, 200, 297, 299, 301, 302, 303, 314, + 316, 319, 320, 321, 322, 323, 327, 328, 0, 0, + 298, 300, 304, 305, 306, 307, 308, 310, 317, 332, + 331, 318, 0, 0, 0, 324, 325, 329, 330, 65, + 0, 0, 63, 91, 0, 66, 427, 0, 25, 64, + 0, 0, 221, 0, 0, 67, 0, 353, 354, 352, + 359, 361, 358, 360, 355, 356, 357, 362, 0, 0, + 0, 89, 0, 0, 0, 0, 0, 21, 22, 0, + 0, 23, 0, 0, 0, 0, 0, 90, 0, 0, + 0, 0, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 0, 0, 0, 13, + 0, 0, 16, 17, 18, 0, 27, 41, 40, 0, + 33, 0, 0, 34, 35, 71, 72, 65, 45, 0, + 63, 91, 0, 66, 0, 0, 25, 64, 0, 0, + 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, + 0, 0, 0, 0, 0, 21, 22, 0, 0, 23, + 0, 0, 0, 0, 0, 90, 0, 0, 0, 0, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 0, 0, 0, 13, 0, 0, + 16, 17, 18, 0, 27, 41, 40, 0, 33, 0, + 0, 34, 35, 71, 72, 65, 0, 0, 63, 91, + 0, 66, 0, 0, 25, 64, 0, 0, 0, 0, + 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 89, 0, 0, + 0, 0, 0, 21, 22, 0, 0, 23, 0, 0, + 0, 0, 0, 90, 0, 0, 0, 0, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 0, 0, 0, 13, 0, 0, 16, 17, + 18, 0, 27, 41, 40, 0, 33, 20, 91, 34, + 35, 71, 72, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 18, 19, 0, 0, 20, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 21, 22, 0, 0, 23, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 11, 12, 14, + 15, 19, 24, 26, 28, 29, 30, 31, 32, 36, + 37, 0, 0, 0, 13, 0, 0, 16, 17, 18, + 0, 27, 41, 40, 0, 33, 20, 42, 34, 35, + 38, 39, 25, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 21, 22, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 14, 15, - 16, 21, 23, 25, 26, 27, 28, 29, 33, 34, - 118, 0, 0, 13, 0, 0, 0, 24, 37, 36, - 0, 30, 0, 0, 31, 32, 35, 0, 0, 118, - 0, 0, 0, 0, 0, 0, 0, 98, 100, 101, - 0, 102, 103, 104, 105, 106, 107, 108, 109, 110, - 111, 0, 112, 113, 117, 99, 98, 100, 101, 0, - 102, 103, 104, 0, 106, 107, 108, 109, 110, 111, - 173, 112, 113, 117, 99, 118, 0, 61, 0, 114, - 116, 115, 0, 181, 118, 0, 0, 60, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 114, 116, - 115, 0, 98, 100, 101, 0, 102, 103, 0, 84, - 106, 107, 100, 109, 110, 111, 0, 112, 113, 117, - 99, 0, 109, 110, 0, 85, 112, 0, 117, 99, - 0, 0, 0, 0, 0, 180, 179, 0, 0, 0, - 0, 0, 0, 0, 114, 116, 115, 0, 0, 0, - 0, 0, 0, 114, 116, 115, 0, 0, 0, 0, - 176, + 19, 24, 26, 28, 29, 30, 31, 32, 36, 37, + 123, 0, 0, 13, 0, 0, 16, 17, 18, 0, + 27, 41, 40, 0, 33, 0, 0, 34, 35, 38, + 39, 123, 0, 0, 0, 0, 0, 103, 105, 106, + 0, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 0, 117, 118, 122, 104, 0, 0, 103, 105, + 106, 0, 107, 108, 109, 0, 111, 112, 113, 114, + 115, 116, 382, 117, 118, 122, 104, 0, 0, 65, + 0, 123, 119, 121, 120, 189, 65, 0, 0, 64, + 0, 381, 189, 0, 0, 0, 64, 0, 0, 0, + 0, 0, 0, 119, 121, 120, 0, 0, 103, 105, + 106, 89, 107, 108, 0, 0, 111, 112, 89, 114, + 115, 116, 180, 117, 118, 122, 104, 90, 0, 65, + 0, 0, 0, 0, 90, 189, 65, 188, 187, 64, + 0, 0, 279, 0, 188, 187, 64, 123, 0, 0, + 0, 0, 0, 119, 121, 120, 0, 0, 0, 0, + 0, 89, 0, 0, 0, 206, 207, 0, 89, 0, + 0, 0, 206, 207, 103, 105, 0, 90, 0, 0, + 0, 0, 0, 0, 90, 114, 115, 188, 187, 117, + 118, 122, 104, 0, 188, 187, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 183, 184, 0, 0, 119, + 121, 120, 276, 277, } var yyPact = [...]int16{ - 64, 165, 844, 844, 632, 780, -1000, -1000, -1000, 202, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 370, -1000, - 266, -1000, 906, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -3, 19, 126, - -1000, -1000, 716, -1000, 716, 166, -1000, 86, 137, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 321, -1000, -1000, 488, - -1000, -1000, 291, 181, -1000, -1000, 21, -1000, -54, -54, - -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, - -54, -54, -54, -54, 978, -1000, -1000, 335, 169, 275, - 275, 275, 275, 275, 275, 126, -57, -1000, 193, 193, - 548, -1000, 26, 612, 33, -15, -1000, 42, 275, 476, - -1000, -1000, 216, 157, -1000, -1000, 262, -1000, 179, -1000, - 112, 222, 716, -1000, -51, -44, -1000, 716, 716, 716, - 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, - 716, 716, -1000, -1000, -1000, 484, 125, 92, -3, -1000, - -1000, 275, -1000, 87, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 161, 161, 332, -1000, -3, -1000, 275, 86, -10, - -10, -15, -15, -15, -15, -1000, -1000, -1000, 464, -1000, - -1000, 81, -1000, 906, -1000, -1000, -1000, 390, -1000, 88, - -1000, 103, -1000, -1000, -1000, -1000, -1000, 102, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 32, 55, 3, -1000, -1000, - -1000, 715, 980, 193, 193, 193, 193, 33, 33, 545, - 545, 545, 971, 925, 545, 545, 971, 33, 33, 545, - 33, 980, -1000, 84, 80, 275, -15, 40, 275, 612, - 39, -1000, -1000, -1000, 669, -1000, 167, -1000, -1000, -1000, + 68, 294, 934, 934, 688, 855, -1000, -1000, -1000, 231, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 716, 275, -1000, -1000, -1000, - -1000, -1000, -1000, 51, 51, 31, 51, 78, 78, 346, - 35, -1000, -1000, 272, 270, 261, 259, 237, 236, 234, - 229, 217, 213, 210, -1000, -1000, -1000, -1000, -1000, 37, - 275, 465, -1000, 364, -1000, 152, -1000, -1000, -1000, 389, - -1000, 906, 382, -1000, -1000, -1000, 51, -1000, 30, 25, - 785, -1000, -1000, -1000, 36, 311, 311, 311, 161, 141, - 141, 36, 141, 36, -78, -1000, 308, -1000, 275, -1000, - -1000, -1000, -1000, -1000, -1000, 51, 51, -1000, -1000, -1000, - 51, -1000, -1000, -1000, -1000, -1000, -1000, 311, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 275, 172, -1000, - -1000, -1000, 162, -1000, 150, -1000, 483, -1000, -1000, -1000, - -1000, -1000, + -1000, -1000, 448, -1000, 340, -1000, 996, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 5, 18, 279, -1000, -1000, 776, -1000, 776, 164, + -1000, 228, 215, 288, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 445, -1000, -1000, 460, -1000, -1000, 397, 329, -1000, + -1000, 26, -1000, -53, -53, -53, -53, -53, -53, -53, + -53, -53, -53, -53, -53, -53, -53, -53, -53, 1120, + -1000, -1000, 102, 326, 1077, 1077, 1077, 1077, 1077, 1077, + 279, -58, -1000, 196, 196, 600, -1000, 30, 321, 105, + -15, -1000, 157, 150, 1077, 400, -1000, -1000, 327, 335, + -1000, -1000, 436, -1000, 216, -1000, 214, 516, 776, -1000, + -47, -51, -41, -1000, 776, 776, 776, 776, 776, 776, + 776, 776, 776, 776, 776, 776, 776, 776, 776, -1000, + -1000, -1000, 1127, 272, 268, 264, 5, -1000, -1000, 1077, + -1000, 236, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 269, + 269, 176, -1000, 5, -1000, 1077, 228, 215, 233, 233, + -15, -15, -15, -15, -1000, -1000, -1000, 512, -1000, -1000, + 91, -1000, 996, -1000, -1000, -1000, -1000, 402, -1000, 404, + -1000, 162, -1000, -1000, -1000, -1000, -1000, 155, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 23, 66, 33, -1000, -1000, + -1000, 514, 167, 171, 171, 171, 196, 196, 196, 196, + 105, 105, 1133, 1133, 1133, 1067, 1017, 1133, 1133, 1067, + 105, 105, 1133, 105, 167, -1000, 212, 209, 195, 1077, + -15, 110, 81, 1077, 321, 46, -1000, -1000, -1000, 1070, + -1000, 163, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 776, 1077, -1000, -1000, -1000, -1000, + -1000, -1000, 36, 36, 22, 36, 83, 83, 98, 49, + -1000, -1000, 366, 364, 360, 353, 338, 334, 331, 305, + 303, 301, 299, -1000, 291, -67, -65, -1000, -1000, -1000, + -1000, -1000, 42, 34, 1077, 312, -1000, -1000, 240, -1000, + 112, -1000, -1000, -1000, 424, -1000, 996, 193, -1000, -1000, + -1000, 36, -1000, 19, 17, 599, -1000, -1000, -1000, 77, + 289, 289, 289, 269, 217, 217, 77, 217, 77, -71, + 32, 229, 171, 171, -1000, -1000, 53, -1000, 1077, -1000, + -1000, -1000, -1000, -1000, -1000, 36, 36, -1000, -1000, -1000, + 36, -1000, -1000, -1000, -1000, -1000, -1000, 289, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 29, -1000, + -1000, 1077, 180, -1000, -1000, -1000, 336, -1000, -1000, 147, + -1000, 44, -1000, -1000, -1000, -1000, -1000, } var yyPgo = [...]int16{ - 0, 444, 13, 433, 6, 15, 430, 318, 23, 427, - 10, 422, 14, 222, 355, 419, 16, 415, 28, 12, - 413, 410, 7, 408, 9, 5, 396, 3, 2, 4, - 394, 25, 1, 393, 384, 33, 200, 381, 375, 86, - 373, 358, 27, 357, 26, 356, 11, 347, 345, 339, - 331, 325, 324, 319, 299, 285, 0, 297, 8, 296, - 288, 281, + 0, 539, 12, 536, 7, 16, 533, 431, 22, 529, + 10, 527, 24, 351, 380, 526, 15, 523, 19, 14, + 522, 516, 8, 515, 4, 5, 501, 3, 6, 13, + 500, 26, 2, 485, 484, 23, 208, 482, 481, 479, + 93, 478, 477, 27, 476, 1, 42, 469, 11, 466, + 453, 451, 445, 439, 427, 425, 418, 385, 0, 408, + 9, 396, 395, 388, } var yyR1 = [...]int8{ - 0, 60, 60, 60, 60, 60, 60, 60, 39, 39, - 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 39, 34, 34, 34, 34, 35, 35, 37, - 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 37, 37, 36, 38, 38, 50, 50, - 43, 43, 43, 43, 18, 18, 18, 18, 17, 17, - 17, 4, 4, 4, 40, 42, 42, 41, 41, 41, - 51, 58, 47, 47, 48, 49, 33, 33, 33, 9, - 9, 45, 53, 53, 53, 53, 53, 53, 54, 55, - 55, 55, 44, 44, 44, 1, 1, 1, 2, 2, - 2, 2, 2, 2, 2, 14, 14, 7, 7, 7, + 0, 62, 62, 62, 62, 62, 62, 62, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 34, 34, 34, 34, 35, 35, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 36, 39, 39, 52, 52, + 44, 44, 44, 44, 37, 37, 37, 37, 37, 37, + 18, 18, 18, 18, 17, 17, 17, 4, 4, 4, + 45, 45, 41, 43, 43, 42, 42, 42, 53, 60, + 49, 49, 50, 51, 33, 33, 33, 9, 9, 47, + 55, 55, 55, 55, 55, 55, 56, 57, 57, 57, + 46, 46, 46, 1, 1, 1, 2, 2, 2, 2, + 2, 2, 2, 14, 14, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 13, 13, 13, 13, 15, 15, - 15, 16, 16, 16, 16, 16, 16, 16, 61, 21, - 21, 21, 21, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 30, 30, 30, 22, 22, 22, 22, 23, - 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 25, 25, 26, 26, 26, 11, 11, - 11, 11, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 13, 13, 13, 13, + 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, + 63, 21, 21, 21, 21, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 30, 30, 30, 22, 22, 22, + 22, 23, 23, 23, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 25, 25, 26, 26, 26, + 11, 11, 11, 11, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 8, 8, - 5, 5, 5, 5, 46, 46, 29, 29, 31, 31, - 32, 32, 28, 27, 27, 52, 10, 19, 19, 59, - 59, 59, 59, 59, 59, 59, 59, 12, 12, 56, - 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, - 57, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 8, 8, 5, 5, 5, 5, + 48, 48, 29, 29, 31, 31, 32, 32, 28, 27, + 27, 54, 10, 19, 19, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 12, 12, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 59, } var yyR2 = [...]int8{ @@ -634,124 +669,131 @@ var yyR2 = [...]int8{ 1, 1, 1, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 0, 1, 3, 3, - 1, 1, 3, 3, 3, 4, 2, 1, 3, 1, - 2, 1, 1, 1, 2, 3, 2, 3, 1, 2, - 3, 1, 3, 3, 2, 2, 3, 5, 3, 1, - 1, 4, 6, 5, 6, 5, 4, 3, 2, 2, - 1, 1, 3, 4, 2, 3, 1, 2, 3, 3, - 1, 3, 3, 2, 1, 2, 1, 1, 1, 1, + 1, 1, 3, 3, 1, 3, 3, 3, 5, 5, + 3, 4, 2, 1, 3, 1, 2, 1, 1, 1, + 3, 4, 2, 3, 2, 3, 1, 2, 3, 1, + 3, 3, 2, 2, 3, 5, 3, 1, 1, 4, + 6, 5, 6, 5, 4, 3, 2, 2, 1, 1, + 3, 4, 2, 3, 1, 2, 3, 3, 1, 3, + 3, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 3, 4, 2, 0, 3, 1, - 2, 3, 3, 1, 3, 3, 2, 1, 2, 0, - 3, 2, 1, 1, 3, 1, 3, 4, 1, 3, - 5, 5, 1, 1, 1, 4, 3, 3, 2, 3, - 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 4, 3, 3, 1, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 3, 4, 2, 0, + 3, 1, 2, 3, 3, 1, 3, 3, 2, 1, + 2, 0, 3, 2, 1, 1, 3, 1, 3, 4, + 1, 3, 5, 5, 1, 1, 1, 4, 3, 3, + 2, 3, 1, 2, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 4, 3, 3, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, - 1, 1, 1, 2, 1, 1, 1, 0, 1, 1, - 2, 3, 4, 6, 7, 4, 1, 1, 1, 1, - 2, 3, 3, 3, 3, 3, 3, 3, 6, 1, - 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 2, 1, 1, 1, 2, + 1, 1, 1, 0, 1, 1, 2, 3, 3, 4, + 4, 6, 7, 4, 1, 1, 1, 1, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 6, 1, 3, } var yyChk = [...]int16{ - -1000, -60, 101, 102, 103, 104, 2, 10, -14, -7, - -13, 62, 63, 79, 64, 65, 66, 12, 47, 48, - 51, 67, 18, 68, 83, 69, 70, 71, 72, 73, - 87, 90, 91, 74, 75, 92, 85, 84, 13, -61, - -14, 10, -39, -34, -37, -40, -45, -46, -47, -48, - -49, -51, -52, -53, -54, -55, -33, -56, -3, 12, - 19, 9, 15, 25, -8, -7, -44, 92, -12, -57, - 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 41, 57, 13, -55, -13, -15, - 20, -16, 12, -10, 2, 25, -21, 2, 41, 59, - 42, 43, 45, 46, 47, 48, 49, 50, 51, 52, - 53, 54, 56, 57, 83, 85, 84, 58, 14, 41, - 57, 53, 42, 52, 56, -35, -42, 2, 79, 87, - 15, -42, -39, -56, -39, -56, -44, 15, 15, -1, - 20, -2, 12, -10, 2, 20, 7, 2, 4, 2, - 4, 24, -36, -43, -38, -50, 78, -36, -36, -36, - -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, - -36, -36, -59, 2, -46, -8, 92, -12, -56, 68, - 67, 15, -32, -9, 2, -29, -31, 90, 91, 19, - 9, 41, 57, -58, 2, -56, -46, -8, 92, -56, - -56, -56, -56, -56, -56, -42, -35, -18, 15, 2, - -18, -41, 22, -39, 22, 22, 22, -56, 20, 7, + -1000, -62, 105, 106, 107, 108, 2, 10, -14, -7, + -13, 62, 63, 79, 64, 65, 82, 83, 84, 66, + 12, 47, 48, 51, 67, 18, 68, 86, 69, 70, + 71, 72, 73, 90, 93, 94, 74, 75, 95, 96, + 88, 87, 13, -63, -14, 10, -40, -34, -38, -41, + -47, -48, -49, -50, -51, -53, -54, -55, -56, -57, + -33, -58, -3, 12, 19, 9, 15, 25, -8, -7, + -46, 95, 96, -12, -59, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 41, + 57, 13, -57, -13, -15, 20, -16, 12, -10, 2, + 25, -21, 2, 41, 59, 42, 43, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 56, 57, 86, + 88, 87, 58, 14, 41, 57, 53, 42, 52, 56, + -35, -43, 2, 79, 90, 15, -43, -40, -58, -40, + -58, -46, 15, 15, 15, -1, 20, -2, 12, -10, + 2, 20, 7, 2, 4, 2, 4, 24, -36, -37, + -44, -39, -52, 78, -36, -36, -36, -36, -36, -36, + -36, -36, -36, -36, -36, -36, -36, -36, -36, -61, + 2, -48, -8, 95, 96, -12, -58, 68, 67, 15, + -32, -9, 2, -29, -31, 93, 94, 19, 9, 41, + 57, -60, 2, -58, -48, -8, 95, 96, -58, -58, + -58, -58, -58, -58, -43, -35, -18, 15, 2, -18, + -42, 22, -40, 22, 22, 22, 22, -58, 20, 7, 2, -5, 2, 4, 54, 44, 55, -5, 20, -16, 25, 2, 25, 2, -20, 5, -30, -22, 12, -29, - -31, 16, -39, 82, 86, 80, 81, -39, -39, -39, - -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, - -39, -39, -46, 92, -12, 15, -56, 15, 15, -56, - 15, -29, -29, 21, 6, 2, -17, 22, -4, -6, - 25, 2, 62, 78, 63, 79, 64, 65, 66, 80, - 81, 12, 82, 47, 48, 51, 67, 18, 68, 83, - 86, 69, 70, 71, 72, 73, 90, 91, 59, 74, - 75, 92, 85, 84, 22, 7, 7, 20, -2, 25, - 2, 25, 2, 26, 26, -31, 26, 41, 57, -23, - 24, 17, -24, 30, 28, 29, 35, 36, 37, 33, - 31, 34, 32, 38, -18, -18, -19, -18, -19, 15, - 15, -56, 22, -56, 22, -58, 21, 2, 22, 7, - 2, -39, -56, -28, 19, -28, 26, -28, -22, -22, - 24, 17, 2, 17, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 22, -56, 22, 7, 21, + -31, 16, -40, 82, 83, 84, 85, 89, 80, 81, + -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, + -40, -40, -40, -40, -40, -48, 95, 96, -12, 15, + -58, 15, 15, 15, -58, 15, -29, -29, 21, 6, + 2, -17, 22, -4, -6, 25, 2, 62, 78, 63, + 79, 64, 65, 66, 80, 81, 82, 83, 84, 12, + 85, 47, 48, 51, 67, 18, 68, 86, 89, 69, + 70, 71, 72, 73, 93, 94, 59, 74, 75, 95, + 96, 88, 87, 22, 7, 7, 20, -2, 25, 2, + 25, 2, 26, 26, -31, 26, 41, 57, -23, 24, + 17, -24, 30, 28, 29, 35, 36, 37, 33, 31, + 34, 32, 38, -45, 15, -45, -45, -18, -18, -19, + -18, -19, 15, 15, 15, -58, 22, 22, -58, 22, + -60, 21, 2, 22, 7, 2, -40, -58, -28, 19, + -28, 26, -28, -22, -22, 24, 17, 2, 17, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + -48, -8, 84, 83, 22, 22, -58, 22, 7, 21, 2, 22, -4, 22, -28, 26, 26, 17, -24, -27, 57, -28, -32, -32, -32, -29, -25, 14, -25, -27, - -25, -27, -11, 95, 96, 97, 98, 7, -56, -28, - -28, -28, -26, -32, -56, 22, 24, 21, 2, 22, - 21, -32, + -25, -27, -11, 99, 100, 101, 102, 22, -48, -45, + -45, 7, -58, -28, -28, -28, -26, -32, 22, -58, + 22, 24, 21, 2, 22, 21, -32, } var yyDef = [...]int16{ - 0, -2, 137, 137, 0, 0, 7, 6, 1, 137, - 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, - 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, - 126, 127, 128, 129, 130, 131, 132, 133, 0, 2, - -2, 3, 4, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 0, 113, - 244, 245, 0, 255, 0, 90, 91, 131, 0, 279, - -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, - -2, -2, -2, -2, 238, 239, 0, 5, 105, 0, - 136, 139, 0, 143, 147, 256, 148, 152, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 0, 74, 75, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 25, 26, 0, 0, - 0, 64, 0, 22, 88, -2, 89, 0, 0, 0, - 94, 96, 0, 100, 104, 134, 0, 140, 0, 146, - 0, 151, 0, 45, 50, 51, 47, 0, 0, 0, + 0, -2, 149, 149, 0, 0, 7, 6, 1, 149, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 0, 2, -2, 3, 4, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 0, 124, 260, 261, 0, 271, 0, 98, + 99, 142, 143, 0, 298, -2, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, 254, + 255, 0, 5, 113, 0, 148, 151, 0, 155, 159, + 272, 160, 164, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 0, + 82, 83, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 25, 26, 0, 0, 0, 72, 0, 22, 96, + -2, 97, 0, 0, 0, 0, 102, 104, 0, 108, + 112, 146, 0, 152, 0, 158, 0, 163, 0, 45, + 54, 50, 51, 47, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, + 81, 275, 0, 0, 0, 0, 284, 285, 286, 0, + 84, 0, 86, 266, 267, 87, 88, 262, 263, 0, + 0, 0, 95, 79, 287, 0, 0, 0, 289, 290, + 291, 292, 293, 294, 23, 24, 27, 0, 63, 28, + 0, 74, 76, 78, 299, 295, 296, 0, 100, 0, + 105, 0, 111, 256, 257, 258, 259, 0, 147, 150, + 153, 156, 154, 157, 162, 165, 167, 170, 174, 175, + 176, 0, 29, 0, 0, 0, 0, 0, -2, -2, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 276, 0, 0, 0, 0, + 288, 0, 0, 0, 0, 0, 264, 265, 89, 0, + 94, 0, 62, 65, 67, 68, 69, 218, 219, 220, + 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, + 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, + 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, + 251, 252, 253, 73, 77, 0, 101, 103, 106, 110, + 107, 109, 0, 0, 0, 0, 0, 0, 0, 0, + 180, 182, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 55, 0, 56, 57, 48, 49, 52, + 274, 53, 0, 0, 0, 0, 277, 278, 0, 85, + 0, 91, 93, 60, 0, 66, 75, 0, 166, 268, + 168, 0, 171, 0, 0, 0, 178, 183, 179, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 72, 73, 259, 0, 0, 0, 266, 267, - 268, 0, 76, 0, 78, 250, 251, 79, 80, 246, - 247, 0, 0, 0, 87, 71, 269, 0, 0, 271, - 272, 273, 274, 275, 276, 23, 24, 27, 0, 57, - 28, 0, 66, 68, 70, 280, 277, 0, 92, 0, - 97, 0, 103, 240, 241, 242, 243, 0, 135, 138, - 141, 144, 142, 145, 150, 153, 155, 158, 162, 163, - 164, 0, 29, 0, 0, -2, -2, 30, 31, 32, - 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, - 43, 44, 260, 0, 0, 0, 270, 0, 0, 0, - 0, 248, 249, 81, 0, 86, 0, 56, 59, 61, - 62, 63, 206, 207, 208, 209, 210, 211, 212, 213, - 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, - 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, - 234, 235, 236, 237, 65, 69, 0, 93, 95, 98, - 102, 99, 101, 0, 0, 0, 0, 0, 0, 0, - 0, 168, 170, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 48, 49, 52, 258, 53, 0, - 0, 0, 261, 0, 77, 0, 83, 85, 54, 0, - 60, 67, 0, 154, 252, 156, 0, 159, 0, 0, - 0, 166, 171, 167, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 262, 0, 265, 0, 82, - 84, 55, 58, 278, 157, 0, 0, 165, 169, 172, - 0, 254, 173, 174, 175, 176, 177, 0, 178, 179, - 180, 181, 182, 188, 189, 190, 191, 0, 0, 160, - 161, 253, 0, 186, 0, 263, 0, 184, 187, 264, - 183, 185, + 0, 0, 0, 0, 279, 280, 0, 283, 0, 90, + 92, 61, 64, 297, 169, 0, 0, 177, 181, 184, + 0, 270, 185, 186, 187, 188, 189, 0, 190, 191, + 192, 193, 194, 200, 201, 202, 203, 70, 0, 58, + 59, 0, 0, 172, 173, 269, 0, 198, 71, 0, + 281, 0, 196, 199, 282, 195, 197, } var yyTok1 = [...]int8{ @@ -769,7 +811,7 @@ var yyTok2 = [...]int8{ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, - 102, 103, 104, 105, + 102, 103, 104, 105, 106, 107, 108, 109, } var yyTok3 = [...]int8{ @@ -1294,44 +1336,83 @@ yydefault: yyVAL.node.(*BinaryExpr).VectorMatching.Card = CardOneToMany yyVAL.node.(*BinaryExpr).VectorMatching.Include = yyDollar[3].strings } - case 54: + case 55: + yyDollar = yyS[yypt-3 : yypt+1] + { + yyVAL.node = yyDollar[1].node + fill := yyDollar[3].node.(*NumberLiteral).Val + yyVAL.node.(*BinaryExpr).VectorMatching.FillValues.LHS = &fill + yyVAL.node.(*BinaryExpr).VectorMatching.FillValues.RHS = &fill + } + case 56: + yyDollar = yyS[yypt-3 : yypt+1] + { + yyVAL.node = yyDollar[1].node + fill := yyDollar[3].node.(*NumberLiteral).Val + yyVAL.node.(*BinaryExpr).VectorMatching.FillValues.LHS = &fill + } + case 57: + yyDollar = yyS[yypt-3 : yypt+1] + { + yyVAL.node = yyDollar[1].node + fill := yyDollar[3].node.(*NumberLiteral).Val + yyVAL.node.(*BinaryExpr).VectorMatching.FillValues.RHS = &fill + } + case 58: + yyDollar = yyS[yypt-5 : yypt+1] + { + yyVAL.node = yyDollar[1].node + fill_left := yyDollar[3].node.(*NumberLiteral).Val + fill_right := yyDollar[5].node.(*NumberLiteral).Val + yyVAL.node.(*BinaryExpr).VectorMatching.FillValues.LHS = &fill_left + yyVAL.node.(*BinaryExpr).VectorMatching.FillValues.RHS = &fill_right + } + case 59: + yyDollar = yyS[yypt-5 : yypt+1] + { + fill_right := yyDollar[3].node.(*NumberLiteral).Val + fill_left := yyDollar[5].node.(*NumberLiteral).Val + yyVAL.node.(*BinaryExpr).VectorMatching.FillValues.LHS = &fill_left + yyVAL.node.(*BinaryExpr).VectorMatching.FillValues.RHS = &fill_right + } + case 60: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.strings = yyDollar[2].strings } - case 55: + case 61: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.strings = yyDollar[2].strings } - case 56: + case 62: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.strings = []string{} } - case 57: + case 63: yyDollar = yyS[yypt-1 : yypt+1] { yylex.(*parser).unexpected("grouping opts", "\"(\"") yyVAL.strings = nil } - case 58: + case 64: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.strings = append(yyDollar[1].strings, yyDollar[3].item.Val) } - case 59: + case 65: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.strings = []string{yyDollar[1].item.Val} } - case 60: + case 66: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).unexpected("grouping opts", "\",\" or \")\"") yyVAL.strings = yyDollar[1].strings } - case 61: + case 67: yyDollar = yyS[yypt-1 : yypt+1] { if !model.UTF8Validation.IsValidLabelName(yyDollar[1].item.Val) { @@ -1339,7 +1420,7 @@ yydefault: } yyVAL.item = yyDollar[1].item } - case 62: + case 68: yyDollar = yyS[yypt-1 : yypt+1] { unquoted := yylex.(*parser).unquoteString(yyDollar[1].item.Val) @@ -1350,20 +1431,35 @@ yydefault: yyVAL.item.Pos++ yyVAL.item.Val = unquoted } - case 63: + case 69: yyDollar = yyS[yypt-1 : yypt+1] { yylex.(*parser).unexpected("grouping opts", "label") yyVAL.item = Item{} } - case 64: + case 70: + yyDollar = yyS[yypt-3 : yypt+1] + { + yyVAL.node = yyDollar[2].node.(*NumberLiteral) + } + case 71: + yyDollar = yyS[yypt-4 : yypt+1] + { + nl := yyDollar[3].node.(*NumberLiteral) + if yyDollar[2].item.Typ == SUB { + nl.Val *= -1 + } + nl.PosRange.Start = yyDollar[2].item.Pos + yyVAL.node = nl + } + case 72: yyDollar = yyS[yypt-2 : yypt+1] { fn, exist := getFunction(yyDollar[1].item.Val, yylex.(*parser).functions) if !exist { yylex.(*parser).addParseErrf(yyDollar[1].item.PositionRange(), "unknown function with name %q", yyDollar[1].item.Val) } - if fn != nil && fn.Experimental && !EnableExperimentalFunctions { + if fn != nil && fn.Experimental && !yylex.(*parser).options.EnableExperimentalFunctions { yylex.(*parser).addParseErrf(yyDollar[1].item.PositionRange(), "function %q is not enabled", yyDollar[1].item.Val) } yyVAL.node = &Call{ @@ -1375,38 +1471,38 @@ yydefault: }, } } - case 65: + case 73: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.node = yyDollar[2].node } - case 66: + case 74: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.node = Expressions{} } - case 67: + case 75: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.node = append(yyDollar[1].node.(Expressions), yyDollar[3].node.(Expr)) } - case 68: + case 76: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.node = Expressions{yyDollar[1].node.(Expr)} } - case 69: + case 77: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).addParseErrf(yyDollar[2].item.PositionRange(), "trailing commas not allowed in function call args") yyVAL.node = yyDollar[1].node } - case 70: + case 78: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.node = &ParenExpr{Expr: yyDollar[2].node.(Expr), PosRange: mergeRanges(&yyDollar[1].item, &yyDollar[3].item)} } - case 71: + case 79: yyDollar = yyS[yypt-1 : yypt+1] { if numLit, ok := yyDollar[1].node.(*NumberLiteral); ok { @@ -1420,7 +1516,7 @@ yydefault: } yyVAL.node = yyDollar[1].node } - case 72: + case 80: yyDollar = yyS[yypt-3 : yypt+1] { if numLit, ok := yyDollar[3].node.(*NumberLiteral); ok { @@ -1431,41 +1527,41 @@ yydefault: yylex.(*parser).addOffsetExpr(yyDollar[1].node, yyDollar[3].node.(*DurationExpr)) yyVAL.node = yyDollar[1].node } - case 73: + case 81: yyDollar = yyS[yypt-3 : yypt+1] { - yylex.(*parser).unexpected("offset", "number, duration, or step()") + yylex.(*parser).unexpected("offset", "number, duration, step(), or range()") yyVAL.node = yyDollar[1].node } - case 74: + case 82: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).setAnchored(yyDollar[1].node) } - case 75: + case 83: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).setSmoothed(yyDollar[1].node) } - case 76: + case 84: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).setTimestamp(yyDollar[1].node, yyDollar[3].float) yyVAL.node = yyDollar[1].node } - case 77: + case 85: yyDollar = yyS[yypt-5 : yypt+1] { yylex.(*parser).setAtModifierPreprocessor(yyDollar[1].node, yyDollar[3].item) yyVAL.node = yyDollar[1].node } - case 78: + case 86: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).unexpected("@", "timestamp") yyVAL.node = yyDollar[1].node } - case 81: + case 89: yyDollar = yyS[yypt-4 : yypt+1] { var errMsg string @@ -1495,7 +1591,7 @@ yydefault: EndPos: yylex.(*parser).lastClosing, } } - case 82: + case 90: yyDollar = yyS[yypt-6 : yypt+1] { var rangeNl time.Duration @@ -1517,7 +1613,7 @@ yydefault: EndPos: yyDollar[6].item.Pos + 1, } } - case 83: + case 91: yyDollar = yyS[yypt-5 : yypt+1] { var rangeNl time.Duration @@ -1532,31 +1628,31 @@ yydefault: EndPos: yyDollar[5].item.Pos + 1, } } - case 84: + case 92: yyDollar = yyS[yypt-6 : yypt+1] { yylex.(*parser).unexpected("subquery selector", "\"]\"") yyVAL.node = yyDollar[1].node } - case 85: + case 93: yyDollar = yyS[yypt-5 : yypt+1] { - yylex.(*parser).unexpected("subquery selector", "number, duration, or step() or \"]\"") + yylex.(*parser).unexpected("subquery selector", "number, duration, step(), range(), or \"]\"") yyVAL.node = yyDollar[1].node } - case 86: + case 94: yyDollar = yyS[yypt-4 : yypt+1] { yylex.(*parser).unexpected("subquery or range", "\":\" or \"]\"") yyVAL.node = yyDollar[1].node } - case 87: + case 95: yyDollar = yyS[yypt-3 : yypt+1] { - yylex.(*parser).unexpected("subquery or range selector", "number, duration, or step()") + yylex.(*parser).unexpected("subquery or range selector", "number, duration, step(), or range()") yyVAL.node = yyDollar[1].node } - case 88: + case 96: yyDollar = yyS[yypt-2 : yypt+1] { if nl, ok := yyDollar[2].node.(*NumberLiteral); ok { @@ -1569,7 +1665,7 @@ yydefault: yyVAL.node = &UnaryExpr{Op: yyDollar[1].item.Typ, Expr: yyDollar[2].node.(Expr), StartPos: yyDollar[1].item.Pos} } } - case 89: + case 97: yyDollar = yyS[yypt-2 : yypt+1] { vs := yyDollar[2].node.(*VectorSelector) @@ -1578,7 +1674,7 @@ yydefault: yylex.(*parser).assembleVectorSelector(vs) yyVAL.node = vs } - case 90: + case 98: yyDollar = yyS[yypt-1 : yypt+1] { vs := &VectorSelector{ @@ -1589,14 +1685,14 @@ yydefault: yylex.(*parser).assembleVectorSelector(vs) yyVAL.node = vs } - case 91: + case 99: yyDollar = yyS[yypt-1 : yypt+1] { vs := yyDollar[1].node.(*VectorSelector) yylex.(*parser).assembleVectorSelector(vs) yyVAL.node = vs } - case 92: + case 100: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.node = &VectorSelector{ @@ -1604,7 +1700,7 @@ yydefault: PosRange: mergeRanges(&yyDollar[1].item, &yyDollar[3].item), } } - case 93: + case 101: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.node = &VectorSelector{ @@ -1612,7 +1708,7 @@ yydefault: PosRange: mergeRanges(&yyDollar[1].item, &yyDollar[4].item), } } - case 94: + case 102: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.node = &VectorSelector{ @@ -1620,7 +1716,7 @@ yydefault: PosRange: mergeRanges(&yyDollar[1].item, &yyDollar[2].item), } } - case 95: + case 103: yyDollar = yyS[yypt-3 : yypt+1] { if yyDollar[1].matchers != nil { @@ -1629,144 +1725,144 @@ yydefault: yyVAL.matchers = yyDollar[1].matchers } } - case 96: + case 104: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.matchers = []*labels.Matcher{yyDollar[1].matcher} } - case 97: + case 105: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).unexpected("label matching", "\",\" or \"}\"") yyVAL.matchers = yyDollar[1].matchers } - case 98: + case 106: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.matcher = yylex.(*parser).newLabelMatcher(yyDollar[1].item, yyDollar[2].item, yyDollar[3].item) } - case 99: + case 107: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.matcher = yylex.(*parser).newLabelMatcher(yyDollar[1].item, yyDollar[2].item, yyDollar[3].item) } - case 100: + case 108: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.matcher = yylex.(*parser).newMetricNameMatcher(yyDollar[1].item) } - case 101: + case 109: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).unexpected("label matching", "string") yyVAL.matcher = nil } - case 102: + case 110: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).unexpected("label matching", "string") yyVAL.matcher = nil } - case 103: + case 111: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).unexpected("label matching", "label matching operator") yyVAL.matcher = nil } - case 104: + case 112: yyDollar = yyS[yypt-1 : yypt+1] { yylex.(*parser).unexpected("label matching", "identifier or \"}\"") yyVAL.matcher = nil } - case 105: + case 113: yyDollar = yyS[yypt-2 : yypt+1] { b := labels.NewBuilder(yyDollar[2].labels) b.Set(labels.MetricName, yyDollar[1].item.Val) yyVAL.labels = b.Labels() } - case 106: + case 114: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.labels = yyDollar[1].labels } - case 134: + case 146: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.labels = labels.New(yyDollar[2].lblList...) } - case 135: + case 147: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.labels = labels.New(yyDollar[2].lblList...) } - case 136: + case 148: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.labels = labels.New() } - case 137: + case 149: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.labels = labels.New() } - case 138: + case 150: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.lblList = append(yyDollar[1].lblList, yyDollar[3].label) } - case 139: + case 151: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.lblList = []labels.Label{yyDollar[1].label} } - case 140: + case 152: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).unexpected("label set", "\",\" or \"}\"") yyVAL.lblList = yyDollar[1].lblList } - case 141: + case 153: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.label = labels.Label{Name: yyDollar[1].item.Val, Value: yylex.(*parser).unquoteString(yyDollar[3].item.Val)} } - case 142: + case 154: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.label = labels.Label{Name: yyDollar[1].item.Val, Value: yylex.(*parser).unquoteString(yyDollar[3].item.Val)} } - case 143: + case 155: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.label = labels.Label{Name: labels.MetricName, Value: yyDollar[1].item.Val} } - case 144: + case 156: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).unexpected("label set", "string") yyVAL.label = labels.Label{} } - case 145: + case 157: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).unexpected("label set", "string") yyVAL.label = labels.Label{} } - case 146: + case 158: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).unexpected("label set", "\"=\"") yyVAL.label = labels.Label{} } - case 147: + case 159: yyDollar = yyS[yypt-1 : yypt+1] { yylex.(*parser).unexpected("label set", "identifier or \"}\"") yyVAL.label = labels.Label{} } - case 148: + case 160: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).generatedParserResult = &seriesDescription{ @@ -1774,33 +1870,33 @@ yydefault: values: yyDollar[2].series, } } - case 149: + case 161: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.series = []SequenceValue{} } - case 150: + case 162: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.series = append(yyDollar[1].series, yyDollar[3].series...) } - case 151: + case 163: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.series = yyDollar[1].series } - case 152: + case 164: yyDollar = yyS[yypt-1 : yypt+1] { yylex.(*parser).unexpected("series values", "") yyVAL.series = nil } - case 153: + case 165: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.series = []SequenceValue{{Omitted: true}} } - case 154: + case 166: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.series = []SequenceValue{} @@ -1808,12 +1904,12 @@ yydefault: yyVAL.series = append(yyVAL.series, SequenceValue{Omitted: true}) } } - case 155: + case 167: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.series = []SequenceValue{{Value: yyDollar[1].float}} } - case 156: + case 168: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.series = []SequenceValue{} @@ -1822,7 +1918,7 @@ yydefault: yyVAL.series = append(yyVAL.series, SequenceValue{Value: yyDollar[1].float}) } } - case 157: + case 169: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.series = []SequenceValue{} @@ -1832,22 +1928,23 @@ yydefault: yyDollar[1].float += yyDollar[2].float } } - case 158: + case 170: yyDollar = yyS[yypt-1 : yypt+1] { - yyVAL.series = []SequenceValue{{Histogram: yyDollar[1].histogram}} + yyVAL.series = []SequenceValue{yylex.(*parser).newHistogramSequenceValue(yyDollar[1].histogram)} } - case 159: + case 171: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.series = []SequenceValue{} // Add an additional value for time 0, which we ignore in tests. + sv := yylex.(*parser).newHistogramSequenceValue(yyDollar[1].histogram) for i := uint64(0); i <= yyDollar[3].uint; i++ { - yyVAL.series = append(yyVAL.series, SequenceValue{Histogram: yyDollar[1].histogram}) + yyVAL.series = append(yyVAL.series, sv) //$1 += $2 } } - case 160: + case 172: yyDollar = yyS[yypt-5 : yypt+1] { val, err := yylex.(*parser).histogramsIncreaseSeries(yyDollar[1].histogram, yyDollar[3].histogram, yyDollar[5].uint) @@ -1856,7 +1953,7 @@ yydefault: } yyVAL.series = val } - case 161: + case 173: yyDollar = yyS[yypt-5 : yypt+1] { val, err := yylex.(*parser).histogramsDecreaseSeries(yyDollar[1].histogram, yyDollar[3].histogram, yyDollar[5].uint) @@ -1865,7 +1962,7 @@ yydefault: } yyVAL.series = val } - case 162: + case 174: yyDollar = yyS[yypt-1 : yypt+1] { if yyDollar[1].item.Val != "stale" { @@ -1873,130 +1970,130 @@ yydefault: } yyVAL.float = math.Float64frombits(value.StaleNaN) } - case 165: + case 177: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.histogram = yylex.(*parser).buildHistogramFromMap(&yyDollar[2].descriptors) } - case 166: + case 178: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.histogram = yylex.(*parser).buildHistogramFromMap(&yyDollar[2].descriptors) } - case 167: + case 179: yyDollar = yyS[yypt-3 : yypt+1] { m := yylex.(*parser).newMap() yyVAL.histogram = yylex.(*parser).buildHistogramFromMap(&m) } - case 168: + case 180: yyDollar = yyS[yypt-2 : yypt+1] { m := yylex.(*parser).newMap() yyVAL.histogram = yylex.(*parser).buildHistogramFromMap(&m) } - case 169: + case 181: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = *(yylex.(*parser).mergeMaps(&yyDollar[1].descriptors, &yyDollar[3].descriptors)) } - case 170: + case 182: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.descriptors = yyDollar[1].descriptors } - case 171: + case 183: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).unexpected("histogram description", "histogram description key, e.g. buckets:[5 10 7]") } - case 172: + case 184: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["schema"] = yyDollar[3].int } - case 173: + case 185: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["sum"] = yyDollar[3].float } - case 174: + case 186: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["count"] = yyDollar[3].float } - case 175: + case 187: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["z_bucket"] = yyDollar[3].float } - case 176: + case 188: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["z_bucket_w"] = yyDollar[3].float } - case 177: + case 189: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["custom_values"] = yyDollar[3].bucket_set } - case 178: + case 190: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["buckets"] = yyDollar[3].bucket_set } - case 179: + case 191: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["offset"] = yyDollar[3].int } - case 180: + case 192: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["n_buckets"] = yyDollar[3].bucket_set } - case 181: + case 193: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["n_offset"] = yyDollar[3].int } - case 182: + case 194: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["counter_reset_hint"] = yyDollar[3].item } - case 183: + case 195: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.bucket_set = yyDollar[2].bucket_set } - case 184: + case 196: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.bucket_set = yyDollar[2].bucket_set } - case 185: + case 197: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.bucket_set = append(yyDollar[1].bucket_set, yyDollar[3].float) } - case 186: + case 198: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.bucket_set = []float64{yyDollar[1].float} } - case 244: + case 260: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.node = &NumberLiteral{ @@ -2004,7 +2101,7 @@ yydefault: PosRange: yyDollar[1].item.PositionRange(), } } - case 245: + case 261: yyDollar = yyS[yypt-1 : yypt+1] { var err error @@ -2019,12 +2116,12 @@ yydefault: Duration: true, } } - case 246: + case 262: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.float = yylex.(*parser).number(yyDollar[1].item.Val) } - case 247: + case 263: yyDollar = yyS[yypt-1 : yypt+1] { var err error @@ -2035,17 +2132,17 @@ yydefault: } yyVAL.float = dur.Seconds() } - case 248: + case 264: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.float = yyDollar[2].float } - case 249: + case 265: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.float = -yyDollar[2].float } - case 252: + case 268: yyDollar = yyS[yypt-1 : yypt+1] { var err error @@ -2054,17 +2151,17 @@ yydefault: yylex.(*parser).addParseErrf(yyDollar[1].item.PositionRange(), "invalid repetition in series values: %s", err) } } - case 253: + case 269: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.int = -int64(yyDollar[2].uint) } - case 254: + case 270: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.int = int64(yyDollar[1].uint) } - case 255: + case 271: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.node = &StringLiteral{ @@ -2072,7 +2169,7 @@ yydefault: PosRange: yyDollar[1].item.PositionRange(), } } - case 256: + case 272: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.item = Item{ @@ -2081,12 +2178,12 @@ yydefault: Val: yylex.(*parser).unquoteString(yyDollar[1].item.Val), } } - case 257: + case 273: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.strings = nil } - case 259: + case 275: yyDollar = yyS[yypt-1 : yypt+1] { nl := yyDollar[1].node.(*NumberLiteral) @@ -2097,7 +2194,7 @@ yydefault: } yyVAL.node = nl } - case 260: + case 276: yyDollar = yyS[yypt-2 : yypt+1] { nl := yyDollar[2].node.(*NumberLiteral) @@ -2112,7 +2209,7 @@ yydefault: nl.PosRange.Start = yyDollar[1].item.Pos yyVAL.node = nl } - case 261: + case 277: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.node = &DurationExpr{ @@ -2121,7 +2218,16 @@ yydefault: EndPos: yyDollar[3].item.PositionRange().End, } } - case 262: + case 278: + yyDollar = yyS[yypt-3 : yypt+1] + { + yyVAL.node = &DurationExpr{ + Op: RANGE, + StartPos: yyDollar[1].item.PositionRange().Start, + EndPos: yyDollar[3].item.PositionRange().End, + } + } + case 279: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.node = &DurationExpr{ @@ -2134,7 +2240,20 @@ yydefault: StartPos: yyDollar[1].item.Pos, } } - case 263: + case 280: + yyDollar = yyS[yypt-4 : yypt+1] + { + yyVAL.node = &DurationExpr{ + Op: yyDollar[1].item.Typ, + RHS: &DurationExpr{ + Op: RANGE, + StartPos: yyDollar[2].item.PositionRange().Start, + EndPos: yyDollar[4].item.PositionRange().End, + }, + StartPos: yyDollar[1].item.Pos, + } + } + case 281: yyDollar = yyS[yypt-6 : yypt+1] { yyVAL.node = &DurationExpr{ @@ -2145,7 +2264,7 @@ yydefault: RHS: yyDollar[5].node.(Expr), } } - case 264: + case 282: yyDollar = yyS[yypt-7 : yypt+1] { yyVAL.node = &DurationExpr{ @@ -2161,7 +2280,7 @@ yydefault: }, } } - case 265: + case 283: yyDollar = yyS[yypt-4 : yypt+1] { de := yyDollar[3].node.(*DurationExpr) @@ -2176,7 +2295,7 @@ yydefault: } yyVAL.node = yyDollar[3].node } - case 269: + case 287: yyDollar = yyS[yypt-1 : yypt+1] { nl := yyDollar[1].node.(*NumberLiteral) @@ -2187,7 +2306,7 @@ yydefault: } yyVAL.node = nl } - case 270: + case 288: yyDollar = yyS[yypt-2 : yypt+1] { switch expr := yyDollar[2].node.(type) { @@ -2220,25 +2339,25 @@ yydefault: break } } - case 271: + case 289: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).experimentalDurationExpr(yyDollar[1].node.(Expr)) yyVAL.node = &DurationExpr{Op: ADD, LHS: yyDollar[1].node.(Expr), RHS: yyDollar[3].node.(Expr)} } - case 272: + case 290: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).experimentalDurationExpr(yyDollar[1].node.(Expr)) yyVAL.node = &DurationExpr{Op: SUB, LHS: yyDollar[1].node.(Expr), RHS: yyDollar[3].node.(Expr)} } - case 273: + case 291: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).experimentalDurationExpr(yyDollar[1].node.(Expr)) yyVAL.node = &DurationExpr{Op: MUL, LHS: yyDollar[1].node.(Expr), RHS: yyDollar[3].node.(Expr)} } - case 274: + case 292: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).experimentalDurationExpr(yyDollar[1].node.(Expr)) @@ -2249,7 +2368,7 @@ yydefault: } yyVAL.node = &DurationExpr{Op: DIV, LHS: yyDollar[1].node.(Expr), RHS: yyDollar[3].node.(Expr)} } - case 275: + case 293: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).experimentalDurationExpr(yyDollar[1].node.(Expr)) @@ -2260,13 +2379,13 @@ yydefault: } yyVAL.node = &DurationExpr{Op: MOD, LHS: yyDollar[1].node.(Expr), RHS: yyDollar[3].node.(Expr)} } - case 276: + case 294: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).experimentalDurationExpr(yyDollar[1].node.(Expr)) yyVAL.node = &DurationExpr{Op: POW, LHS: yyDollar[1].node.(Expr), RHS: yyDollar[3].node.(Expr)} } - case 277: + case 295: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.node = &DurationExpr{ @@ -2275,7 +2394,16 @@ yydefault: EndPos: yyDollar[3].item.PositionRange().End, } } - case 278: + case 296: + yyDollar = yyS[yypt-3 : yypt+1] + { + yyVAL.node = &DurationExpr{ + Op: RANGE, + StartPos: yyDollar[1].item.PositionRange().Start, + EndPos: yyDollar[3].item.PositionRange().End, + } + } + case 297: yyDollar = yyS[yypt-6 : yypt+1] { yyVAL.node = &DurationExpr{ @@ -2286,7 +2414,7 @@ yydefault: RHS: yyDollar[5].node.(Expr), } } - case 280: + case 299: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).experimentalDurationExpr(yyDollar[2].node.(Expr)) diff --git a/vendor/github.com/prometheus/prometheus/promql/parser/lex.go b/vendor/github.com/prometheus/prometheus/promql/parser/lex.go index 296b91d1a..714998576 100644 --- a/vendor/github.com/prometheus/prometheus/promql/parser/lex.go +++ b/vendor/github.com/prometheus/prometheus/promql/parser/lex.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -137,12 +137,16 @@ var key = map[string]ItemType{ "ignoring": IGNORING, "group_left": GROUP_LEFT, "group_right": GROUP_RIGHT, + "fill": FILL, + "fill_left": FILL_LEFT, + "fill_right": FILL_RIGHT, "bool": BOOL, // Preprocessors. "start": START, "end": END, "step": STEP, + "range": RANGE, } var histogramDesc = map[string]ItemType{ @@ -915,6 +919,9 @@ func (l *Lexer) scanDurationKeyword() bool { case "step": l.emit(STEP) return true + case "range": + l.emit(RANGE) + return true case "min": l.emit(MIN) return true @@ -1079,6 +1086,17 @@ Loop: word := l.input[l.start:l.pos] switch kw, ok := key[strings.ToLower(word)]; { case ok: + // For fill/fill_left/fill_right, only treat as keyword if followed by '(' + // This allows using these as metric names (e.g., "fill + fill"). + // This could be done for other keywords as well, but for the new fill + // modifiers this is especially important so we don't break any existing + // queries. + if kw == FILL || kw == FILL_LEFT || kw == FILL_RIGHT { + if !l.peekFollowedByLeftParen() { + l.emit(IDENTIFIER) + break Loop + } + } l.emit(kw) case !strings.Contains(word, ":"): l.emit(IDENTIFIER) @@ -1094,6 +1112,23 @@ Loop: return lexStatements } +// peekFollowedByLeftParen checks if the next non-whitespace character is '('. +// This is used for context-sensitive keywords like fill/fill_left/fill_right +// that should only be treated as keywords when followed by '('. +func (l *Lexer) peekFollowedByLeftParen() bool { + pos := l.pos + for { + if int(pos) >= len(l.input) { + return false + } + r, w := utf8.DecodeRuneInString(l.input[pos:]) + if !isSpace(r) { + return r == '(' + } + pos += posrange.Pos(w) + } +} + func isSpace(r rune) bool { return r == ' ' || r == '\t' || r == '\n' || r == '\r' } @@ -1175,7 +1210,7 @@ func lexDurationExpr(l *Lexer) stateFn { case r == ',': l.emit(COMMA) return lexDurationExpr - case r == 's' || r == 'S' || r == 'm' || r == 'M': + case r == 's' || r == 'S' || r == 'm' || r == 'M' || r == 'r' || r == 'R': if l.scanDurationKeyword() { return lexDurationExpr } diff --git a/vendor/github.com/prometheus/prometheus/promql/parser/parse.go b/vendor/github.com/prometheus/prometheus/promql/parser/parse.go index bcd511f46..ec3e1001d 100644 --- a/vendor/github.com/prometheus/prometheus/promql/parser/parse.go +++ b/vendor/github.com/prometheus/prometheus/promql/parser/parse.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -30,6 +30,7 @@ import ( "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/model/timestamp" "github.com/prometheus/prometheus/promql/parser/posrange" + "github.com/prometheus/prometheus/util/features" "github.com/prometheus/prometheus/util/strutil" ) @@ -39,15 +40,104 @@ var parserPool = sync.Pool{ }, } -// ExperimentalDurationExpr is a flag to enable experimental duration expression parsing. -var ExperimentalDurationExpr bool - -// EnableExtendedRangeSelectors is a flag to enable experimental extended range selectors. -var EnableExtendedRangeSelectors bool +// Options holds the configuration for the PromQL parser. +type Options struct { + EnableExperimentalFunctions bool + ExperimentalDurationExpr bool + EnableExtendedRangeSelectors bool + EnableBinopFillModifiers bool +} +// Parser provides PromQL parsing methods. Create one with NewParser. type Parser interface { - ParseExpr() (Expr, error) - Close() + ParseExpr(input string) (Expr, error) + ParseMetric(input string) (labels.Labels, error) + ParseMetricSelector(input string) ([]*labels.Matcher, error) + ParseMetricSelectors(matchers []string) ([][]*labels.Matcher, error) + ParseSeriesDesc(input string) (labels.Labels, []SequenceValue, error) + RegisterFeatures(r features.Collector) +} + +type promQLParser struct { + options Options +} + +// NewParser returns a new PromQL Parser configured with the given options. +func NewParser(opts Options) Parser { + return &promQLParser{options: opts} +} + +func (pql *promQLParser) ParseExpr(input string) (Expr, error) { + p := newParser(input, pql.options) + defer p.Close() + return p.parseExpr() +} + +func (pql *promQLParser) ParseMetric(input string) (m labels.Labels, err error) { + p := newParser(input, pql.options) + defer p.Close() + defer p.recover(&err) + + parseResult := p.parseGenerated(START_METRIC) + if parseResult != nil { + m = parseResult.(labels.Labels) + } + + if len(p.parseErrors) != 0 { + err = p.parseErrors + } + + return m, err +} + +func (pql *promQLParser) ParseMetricSelector(input string) (m []*labels.Matcher, err error) { + p := newParser(input, pql.options) + defer p.Close() + defer p.recover(&err) + + parseResult := p.parseGenerated(START_METRIC_SELECTOR) + if parseResult != nil { + m = parseResult.(*VectorSelector).LabelMatchers + } + + if len(p.parseErrors) != 0 { + err = p.parseErrors + } + + return m, err +} + +func (pql *promQLParser) ParseMetricSelectors(matchers []string) ([][]*labels.Matcher, error) { + var matcherSets [][]*labels.Matcher + for _, s := range matchers { + ms, err := pql.ParseMetricSelector(s) + if err != nil { + return nil, err + } + matcherSets = append(matcherSets, ms) + } + return matcherSets, nil +} + +func (pql *promQLParser) ParseSeriesDesc(input string) (lbls labels.Labels, values []SequenceValue, err error) { + p := newParser(input, pql.options) + p.lex.seriesDesc = true + + defer p.Close() + defer p.recover(&err) + + parseResult := p.parseGenerated(START_SERIES_DESCRIPTION) + if parseResult != nil { + result := parseResult.(*seriesDescription) + lbls = result.labels + values = result.values + } + + if len(p.parseErrors) != 0 { + err = p.parseErrors + } + + return lbls, values, err } type parser struct { @@ -67,18 +157,17 @@ type parser struct { generatedParserResult any parseErrors ParseErrors + + // lastHistogramCounterResetHintSet is set to true when the most recently + // built histogram had a counter_reset_hint explicitly specified. + // This is used to populate CounterResetHintSet in SequenceValue. + lastHistogramCounterResetHintSet bool + + options Options } -type Opt func(p *parser) - -func WithFunctions(functions map[string]*Function) Opt { - return func(p *parser) { - p.functions = functions - } -} - -// NewParser returns a new parser. -func NewParser(input string, opts ...Opt) *parser { //nolint:revive // unexported-return +// newParser returns a new low-level parser instance from the pool. +func newParser(input string, opts Options) *parser { p := parserPool.Get().(*parser) p.functions = Functions @@ -86,6 +175,7 @@ func NewParser(input string, opts ...Opt) *parser { //nolint:revive // unexporte p.parseErrors = nil p.generatedParserResult = nil p.lastClosing = posrange.Pos(0) + p.options = opts // Clear lexer struct before reusing. p.lex = Lexer{ @@ -93,15 +183,17 @@ func NewParser(input string, opts ...Opt) *parser { //nolint:revive // unexporte state: lexStatements, } - // Apply user define options. - for _, opt := range opts { - opt(p) - } - return p } -func (p *parser) ParseExpr() (expr Expr, err error) { +// newParserWithFunctions returns a new low-level parser instance with custom functions. +func newParserWithFunctions(input string, opts Options, functions map[string]*Function) *parser { + p := newParser(input, opts) + p.functions = functions + return p +} + +func (p *parser) parseExpr() (expr Expr, err error) { defer p.recover(&err) parseResult := p.parseGenerated(START_EXPRESSION) @@ -171,69 +263,16 @@ func EnrichParseError(err error, enrich func(parseErr *ParseErr)) { } } -// ParseExpr returns the expression parsed from the input. -func ParseExpr(input string) (expr Expr, err error) { - p := NewParser(input) - defer p.Close() - return p.ParseExpr() -} - -// ParseMetric parses the input into a metric. -func ParseMetric(input string) (m labels.Labels, err error) { - p := NewParser(input) - defer p.Close() - defer p.recover(&err) - - parseResult := p.parseGenerated(START_METRIC) - if parseResult != nil { - m = parseResult.(labels.Labels) - } - - if len(p.parseErrors) != 0 { - err = p.parseErrors - } - - return m, err -} - -// ParseMetricSelector parses the provided textual metric selector into a list of -// label matchers. -func ParseMetricSelector(input string) (m []*labels.Matcher, err error) { - p := NewParser(input) - defer p.Close() - defer p.recover(&err) - - parseResult := p.parseGenerated(START_METRIC_SELECTOR) - if parseResult != nil { - m = parseResult.(*VectorSelector).LabelMatchers - } - - if len(p.parseErrors) != 0 { - err = p.parseErrors - } - - return m, err -} - -// ParseMetricSelectors parses a list of provided textual metric selectors into lists of -// label matchers. -func ParseMetricSelectors(matchers []string) (m [][]*labels.Matcher, err error) { - var matcherSets [][]*labels.Matcher - for _, s := range matchers { - matchers, err := ParseMetricSelector(s) - if err != nil { - return nil, err - } - matcherSets = append(matcherSets, matchers) - } - return matcherSets, nil -} - // SequenceValue is an omittable value in a sequence of time series values. type SequenceValue struct { Value float64 Omitted bool Histogram *histogram.FloatHistogram + // CounterResetHintSet is true if the counter reset hint was explicitly + // specified in the test file using counter_reset_hint:... syntax. + // This allows distinguishing between "no hint specified" (don't care) + // vs "counter_reset_hint:unknown" (verify it's unknown). + CounterResetHintSet bool } func (v SequenceValue) String() string { @@ -251,30 +290,6 @@ type seriesDescription struct { values []SequenceValue } -// ParseSeriesDesc parses the description of a time series. It is only used in -// the PromQL testing framework code. -func ParseSeriesDesc(input string) (labels labels.Labels, values []SequenceValue, err error) { - p := NewParser(input) - p.lex.seriesDesc = true - - defer p.Close() - defer p.recover(&err) - - parseResult := p.parseGenerated(START_SERIES_DESCRIPTION) - if parseResult != nil { - result := parseResult.(*seriesDescription) - - labels = result.labels - values = result.values - } - - if len(p.parseErrors) != 0 { - err = p.parseErrors - } - - return labels, values, err -} - // addParseErrf formats the error and appends it to the list of parsing errors. func (p *parser) addParseErrf(positionRange posrange.PositionRange, format string, args ...any) { p.addParseErr(positionRange, fmt.Errorf(format, args...)) @@ -413,13 +428,18 @@ func (p *parser) InjectItem(typ ItemType) { p.injecting = true } -func (*parser) newBinaryExpression(lhs Node, op Item, modifiers, rhs Node) *BinaryExpr { +func (p *parser) newBinaryExpression(lhs Node, op Item, modifiers, rhs Node) *BinaryExpr { ret := modifiers.(*BinaryExpr) ret.LHS = lhs.(Expr) ret.RHS = rhs.(Expr) ret.Op = op.Typ + if !p.options.EnableBinopFillModifiers && (ret.VectorMatching.FillValues.LHS != nil || ret.VectorMatching.FillValues.RHS != nil) { + p.addParseErrf(ret.PositionRange(), "binop fill modifiers are experimental and not enabled") + return ret + } + return ret } @@ -458,7 +478,7 @@ func (p *parser) newAggregateExpr(op Item, modifier, args Node, overread bool) ( desiredArgs := 1 if ret.Op.IsAggregatorWithParam() { - if !EnableExperimentalFunctions && ret.Op.IsExperimentalAggregator() { + if !p.options.EnableExperimentalFunctions && ret.Op.IsExperimentalAggregator() { p.addParseErrf(ret.PositionRange(), "%s() is experimental and must be enabled with --enable-feature=promql-experimental-functions", ret.Op) return ret } @@ -496,25 +516,30 @@ func (p *parser) mergeMaps(left, right *map[string]any) (ret *map[string]any) { } func (p *parser) histogramsIncreaseSeries(base, inc *histogram.FloatHistogram, times uint64) ([]SequenceValue, error) { - return p.histogramsSeries(base, inc, times, func(a, b *histogram.FloatHistogram) (*histogram.FloatHistogram, error) { + // Capture the hint set flag immediately after inc histogram is built. + // The base histogram's hint set flag was already captured. + hintSet := p.lastHistogramCounterResetHintSet + return p.histogramsSeries(base, inc, times, hintSet, func(a, b *histogram.FloatHistogram) (*histogram.FloatHistogram, error) { res, _, _, err := a.Add(b) return res, err }) } func (p *parser) histogramsDecreaseSeries(base, inc *histogram.FloatHistogram, times uint64) ([]SequenceValue, error) { - return p.histogramsSeries(base, inc, times, func(a, b *histogram.FloatHistogram) (*histogram.FloatHistogram, error) { + // Capture the hint set flag immediately after inc histogram is built. + hintSet := p.lastHistogramCounterResetHintSet + return p.histogramsSeries(base, inc, times, hintSet, func(a, b *histogram.FloatHistogram) (*histogram.FloatHistogram, error) { res, _, _, err := a.Sub(b) return res, err }) } -func (*parser) histogramsSeries(base, inc *histogram.FloatHistogram, times uint64, +func (*parser) histogramsSeries(base, inc *histogram.FloatHistogram, times uint64, counterResetHintSet bool, combine func(*histogram.FloatHistogram, *histogram.FloatHistogram) (*histogram.FloatHistogram, error), ) ([]SequenceValue, error) { ret := make([]SequenceValue, times+1) // Add an additional value (the base) for time 0, which we ignore in tests. - ret[0] = SequenceValue{Histogram: base} + ret[0] = SequenceValue{Histogram: base, CounterResetHintSet: counterResetHintSet} cur := base for i := uint64(1); i <= times; i++ { if cur.Schema > inc.Schema { @@ -526,7 +551,7 @@ func (*parser) histogramsSeries(base, inc *histogram.FloatHistogram, times uint6 if err != nil { return ret, err } - ret[i] = SequenceValue{Histogram: cur} + ret[i] = SequenceValue{Histogram: cur, CounterResetHintSet: counterResetHintSet} } return ret, nil @@ -535,6 +560,8 @@ func (*parser) histogramsSeries(base, inc *histogram.FloatHistogram, times uint6 // buildHistogramFromMap is used in the grammar to take then individual parts of the histogram and complete it. func (p *parser) buildHistogramFromMap(desc *map[string]any) *histogram.FloatHistogram { output := &histogram.FloatHistogram{} + // Reset the flag for each new histogram being built. + p.lastHistogramCounterResetHintSet = false val, ok := (*desc)["schema"] if ok { @@ -595,6 +622,8 @@ func (p *parser) buildHistogramFromMap(desc *map[string]any) *histogram.FloatHis val, ok = (*desc)["counter_reset_hint"] if ok { + // Mark that the counter reset hint was explicitly specified. + p.lastHistogramCounterResetHintSet = true resetHint, ok := val.(Item) if ok { @@ -626,6 +655,16 @@ func (p *parser) buildHistogramFromMap(desc *map[string]any) *histogram.FloatHis return output } +// newHistogramSequenceValue creates a SequenceValue for a histogram, +// setting CounterResetHintSet based on whether counter_reset_hint was +// explicitly specified in the histogram description. +func (p *parser) newHistogramSequenceValue(h *histogram.FloatHistogram) SequenceValue { + return SequenceValue{ + Histogram: h, + CounterResetHintSet: p.lastHistogramCounterResetHintSet, + } +} + func (p *parser) buildHistogramBucketsAndSpans(desc *map[string]any, bucketsKey, offsetKey string, ) (buckets []float64, spans []histogram.Span) { bucketCount := 0 @@ -768,6 +807,9 @@ func (p *parser) checkAST(node Node) (typ ValueType) { if len(n.VectorMatching.MatchingLabels) > 0 { p.addParseErrf(n.PositionRange(), "vector matching only allowed between instant vectors") } + if n.VectorMatching.FillValues.LHS != nil || n.VectorMatching.FillValues.RHS != nil { + p.addParseErrf(n.PositionRange(), "filling in missing series only allowed between instant vectors") + } n.VectorMatching = nil case n.Op.IsSetOperator(): // Both operands are Vectors. if n.VectorMatching.Card == CardOneToMany || n.VectorMatching.Card == CardManyToOne { @@ -776,6 +818,9 @@ func (p *parser) checkAST(node Node) (typ ValueType) { if n.VectorMatching.Card != CardManyToMany { p.addParseErrf(n.PositionRange(), "set operations must always be many-to-many") } + if n.VectorMatching.FillValues.LHS != nil || n.VectorMatching.FillValues.RHS != nil { + p.addParseErrf(n.PositionRange(), "filling in missing series not allowed for set operators") + } } if (lt == ValueTypeScalar || rt == ValueTypeScalar) && n.Op.IsSetOperator() { @@ -1030,7 +1075,7 @@ func (p *parser) addOffsetExpr(e Node, expr *DurationExpr) { } func (p *parser) setAnchored(e Node) { - if !EnableExtendedRangeSelectors { + if !p.options.EnableExtendedRangeSelectors { p.addParseErrf(e.PositionRange(), "anchored modifier is experimental and not enabled") return } @@ -1053,7 +1098,7 @@ func (p *parser) setAnchored(e Node) { } func (p *parser) setSmoothed(e Node) { - if !EnableExtendedRangeSelectors { + if !p.options.EnableExtendedRangeSelectors { p.addParseErrf(e.PositionRange(), "smoothed modifier is experimental and not enabled") return } @@ -1149,7 +1194,7 @@ func (p *parser) getAtModifierVars(e Node) (**int64, *ItemType, *posrange.Pos, b } func (p *parser) experimentalDurationExpr(e Expr) { - if !ExperimentalDurationExpr { + if !p.options.ExperimentalDurationExpr { p.addParseErrf(e.PositionRange(), "experimental duration expression is not enabled") } } diff --git a/vendor/github.com/prometheus/prometheus/promql/parser/posrange/posrange.go b/vendor/github.com/prometheus/prometheus/promql/parser/posrange/posrange.go index f883a91bb..c5cdc4b91 100644 --- a/vendor/github.com/prometheus/prometheus/promql/parser/posrange/posrange.go +++ b/vendor/github.com/prometheus/prometheus/promql/parser/posrange/posrange.go @@ -1,4 +1,4 @@ -// Copyright 2023 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/promql/parser/prettier.go b/vendor/github.com/prometheus/prometheus/promql/parser/prettier.go index 90fb7a0cf..a0ab9e121 100644 --- a/vendor/github.com/prometheus/prometheus/promql/parser/prettier.go +++ b/vendor/github.com/prometheus/prometheus/promql/parser/prettier.go @@ -1,4 +1,4 @@ -// Copyright 2022 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/promql/parser/printer.go b/vendor/github.com/prometheus/prometheus/promql/parser/printer.go index 961167428..cc5c93197 100644 --- a/vendor/github.com/prometheus/prometheus/promql/parser/printer.go +++ b/vendor/github.com/prometheus/prometheus/promql/parser/printer.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -38,7 +38,7 @@ func tree(node Node, level string) string { typs := strings.Split(fmt.Sprintf("%T", node), ".")[1] var t strings.Builder - t.WriteString(fmt.Sprintf("%s |---- %s :: %s\n", level, typs, node)) + fmt.Fprintf(&t, "%s |---- %s :: %s\n", level, typs, node) level += " · · ·" @@ -109,7 +109,7 @@ func writeLabels(b *bytes.Buffer, ss []string) { if i > 0 { b.WriteString(", ") } - if !model.LegacyValidation.IsValidMetricName(s) { + if !model.LegacyValidation.IsValidLabelName(s) { b.Write(strconv.AppendQuote(b.AvailableBuffer(), s)) } else { b.WriteString(s) @@ -147,6 +147,7 @@ func (node *BinaryExpr) ShortString() string { func (node *BinaryExpr) getMatchingStr() string { matching := "" + var b bytes.Buffer vm := node.VectorMatching if vm != nil { if len(vm.MatchingLabels) > 0 || vm.On || vm.Card == CardManyToOne || vm.Card == CardOneToMany { @@ -154,7 +155,10 @@ func (node *BinaryExpr) getMatchingStr() string { if vm.On { vmTag = "on" } - matching = fmt.Sprintf(" %s (%s)", vmTag, strings.Join(vm.MatchingLabels, ", ")) + b.WriteString(" " + vmTag + " (") + writeLabels(&b, vm.MatchingLabels) + b.WriteString(")") + matching = b.String() } if vm.Card == CardManyToOne || vm.Card == CardOneToMany { @@ -162,7 +166,24 @@ func (node *BinaryExpr) getMatchingStr() string { if vm.Card == CardManyToOne { vmCard = "left" } - matching += fmt.Sprintf(" group_%s (%s)", vmCard, strings.Join(vm.Include, ", ")) + b.Reset() + b.WriteString(" group_" + vmCard + " (") + writeLabels(&b, vm.Include) + b.WriteString(")") + matching += b.String() + } + + if vm.FillValues.LHS != nil || vm.FillValues.RHS != nil { + if vm.FillValues.LHS == vm.FillValues.RHS { + matching += fmt.Sprintf(" fill (%v)", *vm.FillValues.LHS) + } else { + if vm.FillValues.LHS != nil { + matching += fmt.Sprintf(" fill_left (%v)", *vm.FillValues.LHS) + } + if vm.FillValues.RHS != nil { + matching += fmt.Sprintf(" fill_right (%v)", *vm.FillValues.RHS) + } + } } } return matching @@ -182,6 +203,8 @@ func (node *DurationExpr) writeTo(b *bytes.Buffer) { switch { case node.Op == STEP: b.WriteString("step()") + case node.Op == RANGE: + b.WriteString("range()") case node.Op == MIN: b.WriteString("min(") b.WriteString(node.LHS.String()) diff --git a/vendor/github.com/prometheus/prometheus/promql/parser/value.go b/vendor/github.com/prometheus/prometheus/promql/parser/value.go index f882f9f0b..3c1c8571d 100644 --- a/vendor/github.com/prometheus/prometheus/promql/parser/value.go +++ b/vendor/github.com/prometheus/prometheus/promql/parser/value.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/prometheus/promql/promqltest/README.md b/vendor/github.com/prometheus/prometheus/promql/promqltest/README.md new file mode 100644 index 000000000..b4efd9c12 --- /dev/null +++ b/vendor/github.com/prometheus/prometheus/promql/promqltest/README.md @@ -0,0 +1,238 @@ +# The PromQL test scripting language + +This package contains two things: + +* an implementation of a test scripting language for PromQL engines +* a predefined set of tests written in that scripting language + +The predefined set of tests can be run against any PromQL engine implementation by calling `promqltest.RunBuiltinTests()`. +Any other test script can be run with `promqltest.RunTest()`. + +The rest of this document explains the test scripting language. + +Each test script is written in plain text. + +Comments can be given by prefixing the comment with a `#`, for example: + +``` +# This is a comment. +``` + +Each test file contains a series of commands. There are three kinds of commands: + +* `load` +* `clear` +* `eval` + +> **Note:** The `eval` command variants (`eval_fail`, `eval_warn`, `eval_info`, and `eval_ordered`) are deprecated. Use the new `expect` lines instead (explained in the [`eval` command](#eval-command) section). Additionally, `expected_fail_message` and `expected_fail_regexp` are also deprecated. + +Each command is executed in the order given in the file. + +## `load` command + +`load` adds some data to the test environment. + +The syntax is as follows: + +``` +load + + ... + +``` + +* `` is the step between points (eg. `1m` or `30s`) +* `` is a Prometheus series name in the usual `metric{label="value"}` syntax +* `` is a specification of the points to add for that series, following the same expanding syntax as for `promtool unittest` documented [here](../../docs/configuration/unit_testing_rules.md#series) + +For example: + +``` +load 1m + my_metric{env="prod"} 5 2+3x2 _ stale {{schema:1 sum:3 count:22 buckets:[5 10 7]}} +``` + +… will create a single series with labels `my_metric{env="prod"}`, with the following points: + +* t=0: value is 5 +* t=1m: value is 2 +* t=2m: value is 5 +* t=3m: value is 8 +* t=4m: no point +* t=5m: stale marker +* t=6m: native histogram with schema 1, sum -3, count 22 and bucket counts 5, 10 and 7 + +Each `load` command is additive - it does not replace any data loaded in a previous `load` command. +Use `clear` to remove all loaded data. + +### Native histograms with custom buckets (NHCB) + +When loading a batch of classic histogram float series, you can optionally append the suffix `_with_nhcb` to convert them to native histograms with custom buckets and load both the original float series and the new histogram series. + +## `clear` command + +`clear` removes all data previously loaded with `load` commands. + +## `eval` command + +`eval` runs a query against the test environment and asserts that the result is as expected. +It requires the query to succeed without any failures unless an `expect fail` line is provided. Previously `eval` expected no `info` or `warn` annotation, but now `expect no_info` and `expect no_warn` lines must be explicitly provided. + +Both instant and range queries are supported. + +The syntax is as follows: + +``` +# Instant query +eval instant at