question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Swagger is broken.

See original GitHub issue

All of the swagger files downloadable from the site and found at https://github.com/apache/pulsar/tree/master/site2/website/static/swagger/2.7.0 are invalid according to several of the tools I’ve attempted to use. The following outputs are from interaction with the validatation API at swagger.io like so:

swagger.json

curl -X GET "https://validator.swagger.io/validator/debug?url=https%3A%2F%2Fraw.githubusercontent.com%2Fapache%2Fpulsar%2Fmaster%2Fsite2%2Fwebsite%2Fstatic%2Fswagger%2F2.7.0%2Fswagger.json" -H "accept: application/yaml"
---
messages:
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/backlogQuota'(post).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/backlogQuota'(delete).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/backlogQuotaMap'(get).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/compactionThreshold'(get).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/compactionThreshold'(post).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/deduplicationSnapshotInterval'(get).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/deduplicationSnapshotInterval'(post).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/delayedDelivery'(get).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/delayedDelivery'(post).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/dispatchRate'(get).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/dispatchRate'(post).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/inactiveTopicPolicies'(get).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/inactiveTopicPolicies'(post).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/maxConsumersPerSubscription'(get).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/maxConsumersPerSubscription'(post).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/offloadPolicies'(get).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/offloadPolicies'(post).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/offloadPolicies'(delete).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/persistence'(get).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/persistence'(post).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/retention'(get).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/retention'(post).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/subscribeRate'(get).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/subscribeRate'(post).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/subscriptionDispatchRate'(get).operationId\
  \ is repeated"
- "attribute paths.'/non-persistent/{tenant}/{namespace}/{topic}/subscriptionDispatchRate'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}'(get).operationId is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/partitioned'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}'(put).operationId is\
  \ repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/all_subscription/expireMessages/{expireTimeInSeconds}'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/backlog'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/backlogQuota'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/backlogQuota'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/backlogQuotaMap'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/compaction'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/compaction'(put).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/compactionThreshold'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/compactionThreshold'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/compactionThreshold'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/createMissedPartitions'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/deduplicationEnabled'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/deduplicationEnabled'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/deduplicationEnabled'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/deduplicationSnapshotInterval'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/deduplicationSnapshotInterval'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/deduplicationSnapshotInterval'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/delayedDelivery'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/delayedDelivery'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/delayedDelivery'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/dispatchRate'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/dispatchRate'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/dispatchRate'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/examinemessage'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/inactiveTopicPolicies'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/inactiveTopicPolicies'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/inactiveTopicPolicies'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/internal-info'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/internalStats'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/lastMessageId'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/ledger/{ledgerId}/entry/{entryId}'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/maxConsumers'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/maxConsumers'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/maxConsumers'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/maxConsumersPerSubscription'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/maxConsumersPerSubscription'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/maxConsumersPerSubscription'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/maxProducers'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/maxProducers'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/maxProducers'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/maxUnackedMessagesOnConsumer'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/maxUnackedMessagesOnConsumer'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/maxUnackedMessagesOnConsumer'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/maxUnackedMessagesOnSubscription'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/maxUnackedMessagesOnSubscription'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/maxUnackedMessagesOnSubscription'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/messageTTL'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/messageTTL'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/messageTTL'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/offload'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/offload'(put).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/offloadPolicies'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/offloadPolicies'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/offloadPolicies'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/partitioned-stats'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/partitions'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/partitions'(put).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/partitions'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/partitions'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/permissions'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/permissions/{role}'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/permissions/{role}'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/persistence'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/persistence'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/persistence'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/publishRate'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/publishRate'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/publishRate'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/retention'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/retention'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/retention'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/stats'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/subscribeRate'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/subscribeRate'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/subscribeRate'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/subscription/{subName}'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/subscription/{subName}/expireMessages/{expireTimeInSeconds}'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/subscription/{subName}/position/{messagePosition}'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/subscription/{subName}/resetcursor'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/subscription/{subName}/resetcursor/{timestamp}'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/subscription/{subName}/skip/{numMessages}'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/subscription/{subName}/skip_all'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/subscription/{subscriptionName}'(put).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/subscriptionDispatchRate'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/subscriptionDispatchRate'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/subscriptionDispatchRate'(delete).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/subscriptions'(get).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/terminate'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/terminate/partitions'(post).operationId\
  \ is repeated"
- "attribute paths.'/persistent/{tenant}/{namespace}/{topic}/unload'(put).operationId\
  \ is repeated"
- "attribute paths.'/schemas/{tenant}/{namespace}/{topic}/schema/{version}'(get).operationId\
  \ is repeated"
schemaValidationMessages: null

swaggerfunctions.json

curl -X GET "https://validator.swagger.io/validator/debug?url=https%3A%2F%2Fraw.githubusercontent.com%2Fapache%2Fpulsar%2Fmaster%2Fsite2%2Fwebsite%2Fstatic%2Fswagger%2F2.7.0%2Fswaggerfunctions.json" -H "accept: application/yaml"
---
messages:
- "attribute paths.'/functions/{tenant}/{namespace}/{functionName}/{instanceId}/restart'(post).operationId\
  \ is repeated"
- "attribute paths.'/functions/{tenant}/{namespace}/{functionName}/{instanceId}/start'(post).operationId\
  \ is repeated"
- "attribute paths.'/functions/{tenant}/{namespace}/{functionName}/{instanceId}/stop'(post).operationId\
  \ is repeated"
schemaValidationMessages: null

swaggersink.json

curl -X GET "https://validator.swagger.io/validator/debug?url=https%3A%2F%2Fraw.githubusercontent.com%2Fapache%2Fpulsar%2Fmaster%2Fsite2%2Fwebsite%2Fstatic%2Fswagger%2F2.7.0%2Fswaggersink.json" -H "accept: application/yaml"
---
messages:
- "attribute paths.'/sinks/{tenant}/{namespace}/{sinkName}/{instanceId}/restart'(post).operationId\
  \ is repeated"
- "attribute paths.'/sinks/{tenant}/{namespace}/{sinkName}/{instanceId}/start'(post).operationId\
  \ is repeated"
- "attribute paths.'/sinks/{tenant}/{namespace}/{sinkName}/{instanceId}/stop'(post).operationId\
  \ is repeated"
schemaValidationMessages: null

swaggersource.json

curl -X GET "https://validator.swagger.io/validator/debug?url=https%3A%2F%2Fraw.githubusercontent.com%2Fapache%2Fpulsar%2Fmaster%2Fsite2%2Fwebsite%2Fstatic%2Fswagger%2F2.7.0%2Fswaggersource.json" -H "accept: application/yaml"
---
messages:
- "attribute paths.'/sources/{tenant}/{namespace}/{sourceName}/{instanceId}/restart'(post).operationId\
  \ is repeated"
- "attribute paths.'/sources/{tenant}/{namespace}/{sourceName}/{instanceId}/start'(post).operationId\
  \ is repeated"
- "attribute paths.'/sources/{tenant}/{namespace}/{sourceName}/{instanceId}/stop'(post).operationId\
  \ is repeated"
schemaValidationMessages: null

In addition, when these duplicate opreationId errors are resolved through heuristics, several routes from the combined swagger violate the spec by including two or more body parameters, which is not allowed:

The swagger spec at "/Users/awalker/devel/pulsaradmin/clienttest/combined.json" is invalid against swagger specification 2.0. see errors :
- duplicate parameter name "body" for "body" in operation "triggerFunction"
- duplicate parameter name "body" for "body" in operation "updateFunction"
- duplicate parameter name "body" for "body" in operation "updateSink"
- duplicate parameter name "body" for "body" in operation "updateSource"

As it stands, it is not possible to build administrative clients for pulsar from the swagger using any tool that respects the spec.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:3
  • Comments:8 (8 by maintainers)

github_iconTop GitHub Comments

1reaction
flowchartsmancommented, Jan 13, 2021

Attached is the code (in Go) I’ve attempted to use to resolve the duplicated operationIds through heuristics. It seems to work rather well, but I am unsure how to resolve the multiple body parameters:

package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"github.com/fatih/camelcase"
	"github.com/go-openapi/analysis"
	"github.com/go-openapi/loads"
	"github.com/go-openapi/spec"
)

const pulsarVersion = `2.7.0`

var specFiles = []string{
	"swagger.json",
	"swaggerfunctions.json",
	"swaggersource.json",
	"swaggersink.json",
}

type opConflict struct {
	op     *spec.Operation
	route  string
	method string
}

type conflictList struct {
	conflicts []*opConflict
	unchanged []*opConflict
}

type opConflicts map[string]*conflictList

func main() {
	log.SetFlags(0)
	specFile := flag.String("specfile", "", "an individual spec file to download and fix (no merge")
	flag.Parse()
	var (
		specs []*spec.Swagger
		err   error
	)
	if *specFile == "" {
		specs, err = downloadSpecs(specFiles)
		if err != nil {
			log.Fatal(err)
		}
		for _, spec := range specs {
			if err := resolveDuplicatOpIDs(spec); err != nil {
				log.Fatalf("error resolving duplicate OpIds: %v", err)
			}
			if err := fixPaths(spec); err != nil {
				log.Fatalf("error performing fixPaths on %s", spec.ID)
			}
		}
		if err := ensureNoDuplicateDefinitions(specs); err != nil {
			log.Fatalf("error resolving duplicate definitions: %v", err)
		}
		collisions := analysis.Mixin(specs[0], specs[1:]...)
		for _, collision := range collisions {
			log.Println(collision)
		}
	} else {
		// otherwise download and fix a single spec file
		specs, err = downloadSpecs([]string{*specFile})
		if err != nil {
			log.Fatal(err)
		}
		if err := resolveDuplicatOpIDs(specs[0]); err != nil {
			log.Fatalf("error resolving duplicate OpIds: %v", err)
		}
	}
	enc := json.NewEncoder(os.Stdout)
	enc.SetIndent("", "  ")
	enc.Encode(specs[0])
}

// download all specs
func downloadSpecs(specFiles []string) ([]*spec.Swagger, error) {
	tmpDir, err := ioutil.TempDir("", "pulsar-spec")
	if err != nil {
		return nil, err
	}
	defer os.RemoveAll(tmpDir)
	specs := make([]*spec.Swagger, len(specFiles))
	for i, specFile := range specFiles {
		url := fmt.Sprintf(`https://pulsar.apache.org/swagger/%s/%s`, pulsarVersion, specFile)
		log.Printf("downloading %s", url)
		fileName, err := getURLToFile(tmpDir, url)
		if err != nil {
			return nil, fmt.Errorf("error retrieving %s: %w", url, err)
		}
		doc, err := loads.Spec(fileName)
		if err != nil {
			return nil, fmt.Errorf("error loading tempfile: %w", err)
		}
		specs[i] = doc.Spec()
	}
	return specs, nil
}

// map of meaningful operation names for storage in case we want to base a heuristic off of it (unused)
var opNames = []string{"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"}

func getOpConflicts(s *spec.Swagger) ([]*conflictList, error) {
	conflicts := make(opConflicts)
	for pathName, path := range s.Paths.Paths {
		ops := []*spec.Operation{path.Get, path.Post, path.Put, path.Delete, path.Patch, path.Options, path.Head}
		for i, op := range ops {
			if op != nil {
				if op.ID == "" {
					return nil, fmt.Errorf("%s method for route %s has empty opID", opNames[i], pathName)
				}
				if conflicts[op.ID] == nil {
					conflicts[op.ID] = new(conflictList)
				}
				conflicts[op.ID].conflicts = append(conflicts[op.ID].conflicts, &opConflict{
					op:     op,
					route:  pathName,
					method: opNames[i],
				})
			}
		}
	}
	for opName, clist := range conflicts {
		if len(clist.conflicts) < 2 {
			delete(conflicts, opName)
		}
	}
	cl := make([]*conflictList, 0, len(conflicts))
	for _, clist := range conflicts {
		cl = append(cl, clist)
	}
	return cl, nil
}

// apply heuristics to resolve duplicate operation names. usually, one operation name doesn't need changed, so retain one, but if we find more than one we don't know how to rename, return an error
func resolveDuplicatOpIDs(s *spec.Swagger) error {
	opConflicts, err := getOpConflicts(s)
	if err != nil {
		return err
	}
	for _, clist := range opConflicts {
		for _, c := range clist.conflicts {
			// attempt to apply resolution rules
			routeParts := strings.Split(c.route, "/")
			switch {
			case strings.HasPrefix(c.route, "/non-persistent"):
				c.op.ID += "NP"
			case strings.HasPrefix(c.route, "/namespace"):
				parts := camelcase.Split(c.op.ID)
				parts[0] += "NameSpace"
				c.op.ID = strings.Join(parts, "")
			case strings.HasSuffix(c.route, "{version}"):
				c.op.ID += "Version"
			case routeParts[len(routeParts)-2] == "{instanceId}":
				c.op.ID += "Instance"
			default:
				clist.unchanged = append(clist.unchanged, c)
				if len(clist.unchanged) > 1 {
					var tooMany strings.Builder
					tooMany.WriteString(fmt.Sprintf("Found more than one OP conflict I couldn't handle for opID [%s]: ", c.op.ID))
					for _, uc := range clist.unchanged {
						tooMany.WriteString(
							fmt.Sprintf("%s-%s", uc.method, uc.route),
						)
					}
					return fmt.Errorf(tooMany.String())
				}
			}
		}
	}
	return nil
}

// retrieve the swagger stored at URL to a temporary file. Necessary because loads.Spec requires a file
func getURLToFile(tmpDir string, url string) (filename string, err error) {
	file, err := os.Create(filepath.Join(tmpDir, url[strings.LastIndex(url, `/`)+1:]))
	if err != nil {
		return "", err
	}

	defer func() {
		if err != nil {
			os.Remove(file.Name())
		}
	}()

	var resp *http.Response
	resp, err = http.Get(url)
	if err != nil {
		return "", err
	}
	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("code %d when getting %s", resp.StatusCode, url)
	}
	if _, err := io.Copy(file, resp.Body); err != nil {
		return "", err
	}
	return file.Name(), file.Close()
}

const adminPrefix = `/admin`

// remove the version suffix from the admin path and prepend to all routes so all routes can live together in the combined spec under /admin
func fixPaths(s *spec.Swagger) error {
	s.Info.Version = pulsarVersion
	if !strings.HasPrefix(s.BasePath, adminPrefix) {
		return fmt.Errorf("basePath does not have required %q prefix: %q", adminPrefix, s.BasePath)
	}
	newPrefix := s.BasePath[len(adminPrefix):]
	s.BasePath = adminPrefix
	for pathName, path := range s.Paths.Paths {
		newPathName := newPrefix + pathName
		if _, ok := s.Paths.Paths[newPathName]; ok {
			return fmt.Errorf("new path %q already exists", newPathName)
		}
		s.Paths.Paths[newPathName] = path
		delete(s.Paths.Paths, pathName)
	}
	return nil
}

func ensureNoDuplicateDefinitions(specs []*spec.Swagger) error {
	// create a map of serialized definitions to ensure that definitions of the same name are in fact the same and will preserve refs
	serializedDefs := map[string][]byte{}
	for _, s := range specs {
		for defName, defSchema := range s.Definitions {
			b, err := json.Marshal(defSchema)
			if err != nil {
				return fmt.Errorf("error serializing defininition schema [%s]: %w", defName, err)
			}
			if schemaBytes, ok := serializedDefs[defName]; ok {
				if !bytes.Equal(schemaBytes, b) {
					return fmt.Errorf("definitions of %q are different", defName)
				}
				// remove the duplicate from the spec
				delete(s.Definitions, defName)
				continue
			}
			// otherwise store the serialized definition
			serializedDefs[defName] = b
		}
	}
	return nil
}
0reactions
tisonkuncommented, Dec 7, 2022

Closed as stale. Please open a new issue if it’s still relevant to the maintained versions.

Now the swagger files are hosted under https://pulsar.apache.org/swagger/ and I can read https://pulsar.apache.org/admin-rest-api pages that generated from the swagger file properly.

Please create a new issue and provide a reproduce if those files still don’t work for your concrete use case.

Read more comments on GitHub >

github_iconTop Results From Across the Web

[BUG] Swagger-UI is broken · Issue #7015 - GitHub
One of your dependencies has a bug. Is there a way to workaround it until they fix it? Maybe a way to use...
Read more >
SWAGGER IS A BROKEN MOVE : r/nuzlocke - Reddit
SWAGGER IS A BROKEN MOVE. I'm doing a Gold randomized run. Well, I was. Random Trainer mons, random moves, the whole shebang.
Read more >
Swagger is not Working Asp.net Core how to open swagger ui
1. "its not working"... · spender The application is working fine one localhost/Acquirer/Dashboard i just want to go to swagger ui. – Asad...
Read more >
Broken Swagger - Album by P-Ro - Spotify
Broken Swagger. P-Ro. 202112 songs, 35 min 36 sec. 1. The Getup (Intro)P-Ro, CloaqxDagger. 2. Fight BackP-Ro, CloaqxDagger. 3. Salma HayekP-Ro, CloaqxDagger.
Read more >
Swagger Inspector not working - SmartBear Community
I'm trying to use Swagger Inspector to generate some OpenAPI files. The tool is throwing a number of errors in the console, and...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found