domaindrivendev / Swashbuckle.WebApi

Seamlessly adds a swagger to WebApi projects!
BSD 3-Clause "New" or "Revised" License
3.07k stars 678 forks source link

(Rel #142) Support for hashing duplicate items #901

Open dcbroad3 opened 8 years ago

dcbroad3 commented 8 years ago

I've been working on overcoming the issue in #142 using KarunaGovind's hashing suggestion. Our API is fairly inflexible at this point, and we have two actions like "/things" that returns a list and "/things?name={name}" that returns a single item, so it's not really possible to represent this in a single swagger path. (We also have "/things/id", but we allow lookup by three different fields)

Basically, I'd like to request that Swashbuckle let me specify an option that either A) allows me to alter ApiDescriptions before it checks for conflicts, or B) gives me an option like ResolveConflictingActions that allows me to actually modify the actions so that they do not conflict, then return the full collection rather than one item.

What I've found as a workaround is to use ResolveConflictingActions with a method that returns the first conflicting action, but also logs the other conflicting actions to a list. Then, I've implemented a DocumentFilter that takes the list of conflicting actions that weren't included, grabs the name of the first query parameter (this won't work for everyone but works for me), and adds "#paramName" to the end of the RelativePathSansQueryString(). So where I had two "/thingy" actions, I now have "/thingy" processed automatically and "/thingy#paramName" that is processed at the end.

The messy part of this is since CreatePathItem is internal to Swashbuckle I have to re-implement it, add the cleaned up conflicting items to the paths dictionary, then re-sort the dictionary. The main thing I want is to not have to include implementations of CreatePathItem, CreateOperation, GetParameterLocation, and CreateParameter in my code, so the bare minimum request would be to expose CreatePathItem, but I'd prefer A or B above.

bfcamara commented 8 years ago

I think another option could be to allow to specify the path grouping key function which by default uses RelativePathSansQueryString.

Here is an example to add an hash to the path if a custom attribute is applied on the action

                .GroupBy(apiDesc => {
                    string relativePath = apiDesc.RelativePathSansQueryString();
                    string operationSuffix = apiDesc.GetControllerAndActionAttributes<SwaggerVerbOperationSuffixAttribute>().FirstOrDefault()?.OperationSuffix;
                    if (!string.IsNullOrEmpty(operationSuffix))
                        relativePath += "#" + operationSuffix;

                    return relativePath;
                })

So, one option could be to allow in configuration to do something like this

c.PathProvider(apiDesc =>  {
                    string relativePath = apiDesc.RelativePathSansQueryString();
                    string operationSuffix = apiDesc.GetControllerAndActionAttributes<SwaggerVerbOperationSuffixAttribute>().FirstOrDefault()?.OperationSuffix;
                    if (!string.IsNullOrEmpty(operationSuffix))
                        relativePath += "#" + operationSuffix;

                    return relativePath;
                }))

Makes sense?

bfcamara commented 8 years ago

I really would like to make a PR to add support to use a custom path grouping key by providing a config option (probably PathGoupingKeyProvider is its name). I just want to make sure that it makes sense before proposing the PR.