Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AppService missing in Swagger after upgrading from 2.7 to 3.3.2 #7698

Closed
Ultre00 opened this issue Feb 11, 2021 · 6 comments · Fixed by #7766
Closed

AppService missing in Swagger after upgrading from 2.7 to 3.3.2 #7698

Ultre00 opened this issue Feb 11, 2021 · 6 comments · Fixed by #7766

Comments

@Ultre00
Copy link

Ultre00 commented Feb 11, 2021

I just upgraded to versio 3.3.2 because this is the last version before 5.0. I want to do the 5.0 upgrade later. However after upgrading from 2.7 the Swagger seemed to be missing some AppServices. All the AppServices that were missing do inherit from a base class.

For example:

    [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(IIdentityUserAppService), typeof(IdentityUserAppService), typeof(MydentityUserAppService))]
    public class MyIdentityUserAppService : IdentityUserAppService

In 2.7 there were 2 end-points. One for users and one for myIdentityUsers. This wasn't ideal either because I would like to hide the initial users. However when upgrading to 3.3.2 I am missing the myIdentityUsers endpoint in Swagger at all. This prevents openapi from generating that service as well. Did I miss something here?

Also how would I hide the original users service in Swagger, or better keep the users name but use my own implementation that has an extra endpoint for creating a user without a password.

@Ultre00
Copy link
Author

Ultre00 commented Feb 11, 2021

I found out that there is also an IdentityUserController. I also created a custom class implementation for this:

 [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(IdentityUserController))]
    public class MyIdentityUserController : IdentityUserController, IMyIdentityUserAppService
    {
        public MyIdentityUserController(IMyIdentityUserAppService userAppService) : base(userAppService)
        {
        }

        [HttpPost]
        public Task<IdentityUserDto> CreateWithoutPasswordAsync(IdentityUserCreateWithoutPasswordDto input)
        {
            return ((IMyIdentityUserAppService)UserAppService).CreateWithoutPasswordAsync(input);
        }

        public override Task<PagedResultDto<IdentityUserDto>> GetListAsync(GetIdentityUsersInput input)
        {
            return base.GetListAsync(input);
        }
    }

However I still don't see my own custom extended functions like CreateWithoutPassword. When I debug my code my custom service does get hit and it is actually being used. How can I get swagger to add my extended functions ?

@olicooper
Copy link
Contributor

olicooper commented Feb 12, 2021

Firstly, you might be missing [Route(Order = 0, "api/identity/...", Name = "Identity")] attribute above your methods. Order=0 is important.

To hide endpoints I created a custom attribute which I can use to decide which things appear on the Swagger UI. This is restrictive by default so it may be more than you need. You can adapt it to work how you need.

public class MyWebModule...
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        // ...

        context.Services.AddSwaggerGen(options =>
        {
            // ...
            if (!environment.IsDevelopment())
            {
                options.DocumentFilter<ShowInSwaggerAttributeFilter>();

                // you might also be able to override this instead:
                // options.DocInclusionPredicate((docName, description) => true);
            }
        });
    }

    class ShowInSwaggerAttributeFilter : IDocumentFilter
    {
        public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
        {
            var filteredApis = context.ApiDescriptions.Where(a => a.CustomAttributes().Any(x =>
                x.GetType() == typeof(ShowInSwaggerAttribute))
                || a.RelativePath.Equals("api/identity/my-profile"));

            foreach (var path in swaggerDoc.Paths.ToList())
            {
                if (filteredApis.All(x => ("/" + x.RelativePath) != path.Key))
                    swaggerDoc.Paths.Remove(path.Key);
            }
        }
    }
}

Then create the Attribute:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class ShowInSwaggerAttribute : Attribute { }

Then in your controllers you can use this attribute above the class:

[ShowInSwagger]
public class MyController { }

@Ultre00
Copy link
Author

Ultre00 commented Feb 14, 2021

@olicooper Thanks for the reply. I also tried adding the route attribute, but that didn't do anything. Also it is not a Swagger only issue. When I try to connect with postman I get a 404 anyway. This is only an issue with extending existing controllers. If I create a whole new endpoint that inherits directly from AbpController then it is fine but then i can't use:

[RemoteService(Name = IdentityRemoteServiceConsts.RemoteServiceName)]
    [Area("identity")]
    [ControllerName("User")]
    [Route("api/identity/users")]

Because the original IdentityUserController already has these names making it ambiguous. So then I would have to duplicate the users for something like users_v2 that I don't intend to do because then the original users endpoint is still accessible as well.

I also created a stack overflow issue here: https://stackoverflow.com/questions/66157218/extending-existing-abp-controllers

@olicooper
Copy link
Contributor

olicooper commented Feb 14, 2021

I had a similar issue before, maybe this will help: #5269
Also, did you try the Order=0 trick on the methods?

@Ultre00
Copy link
Author

Ultre00 commented Feb 14, 2021

Yes I did also try to add the Order = 0 trick. But that didn't solve the issue. The only thing that comes to mind is some older version code that is still in effect. Maybe 2.7 or before has some different ConfigureServices defaults in the HttpApi.Host module after running the cli. I will try a clean project later and see if I do have the same issues there.

@olicooper
Copy link
Contributor

olicooper commented Feb 14, 2021

Sorry for not reading your previous comment properly. I see you already tried some of my suggestions.

I have posted an answer on stack overflow.. I managed to get it working by creating a new controller that doesn't inherit from the base controller. You'd have one to override existing methods, and one to add new ones if that makes sense? I feel like I managed to solve it in a nicer way before though. I will see if I can find the code.

Update:
I've looked through the revision history for the project I had this issue on and I inherited directly from AbpController to extend functionality. It seems you will need two controllers to extend and override IdentityUserController methods unless the ABP team know of a better way.
I don't feel like this is a bad thing to do though. If you need access to the IdentityAppService, you can still access it through DI and the controller won't look any different to the users or swagger. I believe it is only really an aesthetic issue. You can use this approach until a nicer solution appears?

I believe the issue will be due to how controllers are exposed. I think the controller 'footprint' is set with the base
IdentityUserController controller, when you use ExposeServices it replaces the controller with your own, but the footprint remains the same. Similar to writing interfaces - you don't expect to see your new methods unless you cast to the concrete type and swagger doesn't use reflection. This is why I think you have to create a new controller for new methods.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants