domaindrivendev / Swashbuckle.AspNetCore

Swagger tools for documenting API's built on ASP.NET Core
MIT License
5.25k stars 1.31k forks source link

Support for jsonpatch #199

Closed JanEggers closed 8 years ago

JanEggers commented 8 years ago

Hi

im triing to get my patch operations working but the generated swagger is/seems wrong.

i have seen https://github.com/domaindrivendev/Swashbuckle/issues/595 but that is related to asp.net classic

in asp.net core JsonPatch is part of the framework but the generated swagger.json is wrong:

i get:

"JsonPatchDocument[T]": { "type": "object", "properties": { "operations": { "type": "array", "items": { "$ref": "#/definitions/Operation[T]" }, "readOnly": true } } }

but it should be:

"JsonPatchDocument": { "type": "array", "items": { "$ref": "#/definitions/Operation" }, } }

can someone please point me in the right direction?

JanEggers commented 8 years ago

i found a workaround:

i changed the methodsignature from JsonPatchDocument to Operation[], that way swagger is generated correctly.

i dont like this approach because i need JsonPatchDocument to apply the patch. Its constructor contains IContractResolver as parameter that now needs to be obtained somehow.

and its simply not the way its ment to be according to: http://benfoster.io/blog/aspnet-core-json-patch-partial-api-updates

RehanSaeed commented 8 years ago

I wrote the following ISchemaFilter which fixes the example JSON for any parameters which are JsonPatchDocument<T>. However, Ahoy is wrapping my example with { "operations": [ MYCONTENT ] }. @JanEggers Did you figure this out.

public class JsonPatchDocumentSchemaFilter : ISchemaFilter
{
    public void Apply(Schema model, SchemaFilterContext context)
    {
        if ((context.SystemType.GenericTypeArguments.Length > 0) &&
            (context.SystemType.GetGenericTypeDefinition() == typeof(List<>)) &&
            (context.SystemType.GenericTypeArguments.First().GetGenericTypeDefinition() ==
            typeof(Microsoft.AspNetCore.JsonPatch.Operations.Operation<>)))
        {
            model.Default = new List<Microsoft.AspNetCore.JsonPatch.Operations.Operation>()
            {
                new Microsoft.AspNetCore.JsonPatch.Operations.Operation()
                {
                    op = "replace",
                    path = "/property",
                    value = "New Value"
                },
                new Microsoft.AspNetCore.JsonPatch.Operations.Operation()
                {
                    op = "remove",
                    path = "/property"
                },
                new Microsoft.AspNetCore.JsonPatch.Operations.Operation()
                {
                    op = "replace",
                    path = "/arrayProperty/0",
                    value = "New Array Item"
                },
                new Microsoft.AspNetCore.JsonPatch.Operations.Operation()
                {
                    op = "add",
                    path = "/arrayProperty/-",
                    value = "New Array Item"
                },
                new Microsoft.AspNetCore.JsonPatch.Operations.Operation()
                {
                    op = "remove",
                    path = "/arrayProperty/0"
                },
                new Microsoft.AspNetCore.JsonPatch.Operations.Operation()
                {
                    op = "move",
                    from = "/arrayProperty/0",
                    path = "/arrayProperty/2"
                },
            };
        }
    }
}
JanEggers commented 8 years ago

in short no i didnt figure it out.

thats why i changed the contract of my methods to be operation[].

but there must be some kind of serialization feature that makes shure the document is deserialized correctly but the swagger is wrong

RehanSaeed commented 8 years ago

This does the trick:

namespace Bridge.FeatureToggle.Api.ViewModelSchemaFilters
{
    using Microsoft.AspNetCore.JsonPatch;
    using Swashbuckle.Swagger.Model;
    using Swashbuckle.SwaggerGen.Generator;
    using Operation = Microsoft.AspNetCore.JsonPatch.Operations.Operation;

    public class JsonPatchDocumentSchemaFilter : ISchemaFilter
    {
        public void Apply(Schema model, SchemaFilterContext context)
        {
            if (context.SystemType.GenericTypeArguments.Length > 0 &&
                context.SystemType.GetGenericTypeDefinition() == typeof(JsonPatchDocument<>))
            {
                model.Default = GetExample();
                model.ExternalDocs = new ExternalDocs()
                {
                    Description = "JSON Patch Documentation",
                    Url = "http://jsonpatch.com/"
                };
            }
        }

        private static Operation[] GetExample()
        {
            return new Operation[]
            {
                new Operation()
                {
                    op = "replace",
                    path = "/property",
                    value = "New Value"
                },
                new Operation()
                {
                    op = "add",
                    path = "/property",
                    value = "New Value"
                },
                new Operation()
                {
                    op = "remove",
                    path = "/property"
                },
                new Operation()
                {
                    op = "copy",
                    from = "/fromProperty",
                    path = "/toProperty"
                },
                new Operation()
                {
                    op = "move",
                    from = "/fromProperty",
                    path = "/toProperty"
                },
                new Operation()
                {
                    op = "test",
                    path = "/property",
                    value = "Has Value"
                },
                new Operation()
                {
                    op = "replace",
                    path = "/arrayProperty/0",
                    value = "Replace First Array Item"
                },
                new Operation()
                {
                    op = "replace",
                    path = "/arrayProperty/-",
                    value = "Replace Last Array Item"
                }
            };
        }
    }
}
JanEggers commented 8 years ago

that doesnt do anything it just fixed the example data but the type in the swagger.json remains the same.

@domaindrivendev can u please elaborate why the type isnt detected correctly

Eilon commented 8 years ago

Hi folks, we logged https://github.com/aspnet/Mvc/issues/5464 for MVC to describe this case more accurately in MVC's ApiExplorer. But for the meantime the workarounds here should be fine. Thanks!

JanEggers commented 8 years ago

okay if the error is in mvc api explorer ill close this issue