How to add links to root resource in Spring Data REST? - spring-data-rest

How to expose an external resource (not managed through a repository) in the root listing of resources of Spring Data REST? I defined a controller following the pattern in Restbucks

This can be done by implementing ResourceProcessor<RepositoryLinksResource>.
Following code snippet adds "/others" to the root listing
#Controller
#ExposesResourceFor(Other.class)
#RequestMapping("/others")
public class CustomRootController implements
ResourceProcessor<RepositoryLinksResource> {
#ResponseBody
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<Resources<Resource<Other>>> listEntities(
Pageable pageable) throws ResourceNotFoundException {
//... do what needs to be done
}
#Override
public RepositoryLinksResource process(RepositoryLinksResource resource) {
resource.add(ControllerLinkBuilder.linkTo(CustomRootController.class).withRel("others"));
return resource;
}
}
should add
{
"rel": "others",
"href": "http://localhost:8080/api/others"
}
to your root listing links

I have been searching for an answer to the same issue, but the key is: I don't have a controller. My url points to something created in an auth filter. What worked for me is to create a RootController that doesn't have any methods, and use it for building links in the ResourceProcessor implementation.
#RestController
#RequestMapping("/")
public class RootController {}
Then the link is inserted using the empty controller.
#Component
public class AuthLinkProcessor implements ResourceProcessor<RepositoryLinksResource> {
#Override
public RepositoryLinksResource process(RepositoryLinksResource resource) {
resource.add(
linkTo(RootController.class)
.slash("auth/login")
.withRel("auth-login"));
return resource;
}
}

In 2022, API has changed. This reply might be relevant: Migrating ResourceProcessor to HATEOAS 1.0.0 M1.
Here's my piece of code with the new API:
#Component
class AuthLinkProcessor implements RepresentationModelProcessor<RepositoryLinksResource> {
#Override
public RepositoryLinksResource process(RepositoryLinksResource model) {
model.add(
linkTo(AuthenticationController.class)
.slash("/authenticate")
.withRel("authenticate"));
return model;
}
}

Related

Dependency injection in Hotchocolate GraphQL, ASP.NET Core

Is it possible to do something like this?
Query.cs
class Query<T> : ObjectType<MyQuery<T>> where T : class
{
protected override void configure(IObjectTypeDescriptor<MyQuery<T>> descriptor)
{
descriptor
.Field(f => f.GetItems)
.Description("Return List");
}
}
public partial class MyQuery<T> where T : class
{
private readonly IGenericRepositorty _repo
public MyQuery(IGenericRepositorty repo)
{
_repo = repo;
}
public IEnumerable<T> GetItems()
{
return _repo.GetAll(); // GetAll in generic repo
}
}
Now if I am adding my service in Startup.cs as
services.AddQueryType<MyQuery<Entity>>();
It works.
But I want to add it as
services.AddQueryType<MyQuery<>>(); or kind of services.AddQueryType(typeOf(MyQuery<>));
The way we inject generic repo like this
services.AddScoped(typef(IGenericRepository<>),typeofGenericRepository<>)
So, here at run time it creates an instance.
The same way for query at run time I am trying whether it will be possible to create instance

I want to place Automapper profile in my Business Layer

I've created a web api core 2.0 application.
I've got my main app and the Business Layer.
I want to place the automapper profile in the business layer so that all the mappings are made in the business layer. My business layer is just a class library project.
Is this possible? or do I need to place all my mapping in a Profile class in the main app?
Just a theoretical explanation can help.
Yes, it's possible but it depends on where the model classes reside.
You can give each layer or project a Profile where you map the appropriate model classes. Then in the project where you want to use the mapper, create the ObjectMapper class to load the Profiles.
namespace BL.Config
{
public class MapperProfile : Profile
{
public MapperProfile()
{
CreateMap<Entity, Dto>();
...
}
}
public class ObjectMapper
{
public static IMapper Mapper
{
get { return mapper.Value; }
}
public static IConfigurationProvider Configuration
{
get { return config.Value; }
}
public static Lazy<IMapper> mapper = new Lazy<IMapper>(() =>
{
var mapper = new Mapper(Configuration);
return mapper;
});
public static Lazy<IConfigurationProvider> config = new Lazy<IConfigurationProvider>(() =>
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<BL.Config.MapperProfile>();
cfg.AddProfile<AppCore.Config.MapperProfile>(); // any other profiles you need to use
});
return config;
});
}
}
When I need to use AutoMapper, I use the ObjectMapper.Mapper to get my mapper instance. I like to add this to an abstract service.
public interface IAutoMapperService
{
IMapper Mapper { get; }
}
public abstract class AutoMapperService : IAutoMapperService
{
public IMapper Mapper
{
get { return BAL.Config.ObjectMapper.Mapper; }
}
}
And usage: The service has the Mapper member.
public class SomeService : AutoMapperService, ISomeService
{
public Foo GetFoo()
{
var foo = Mapper.Map<Foo>(bar);
return foo;
}
}
Or just implement the IAutoMapperService if you can't inherit another base class.
The downside is BL requires the AutoMapper dependency. But using this way I find I can hide many models from the other layers.

Autofac - Registering instance type that takes interface as parameter

I am very new to Autofac and not able to understand the syntax for registration. I have following calsses/interfaces :
//Interface
public interface ISender
{
void Send();
}
//implementations
public class Post : ISender
{
public void Send()
{
//Post implementation
}
}
public class Email : ISender
{
public void Send()
{
//Email implementation
}
}
And a class that calls these implementations
public class Consumer
{
ISender Sender;
public Consumer(ISender sender)
{
Sender = sender
}
public void Act()
{
Sender.Send();
}
}
Now, which implementation to call is to be decided in a controller, so I tried using IIndex from this page like:
public calss PostController
{
Consumer ConsumerObject;
public PostController(IIndex<string, Consumer> consumer)
{
ConsumerObject = consumer["post"];
}
}
public class EmailController
{
Consumer ConsumerObject;
public PostController(IIndex<string, Consumer> consumer)
{
ConsumerObject = consumer["email"];
}
}
Firstly, is it correct or doable? Now the problem is I don't understand how to register in Autofac. So, How can we register Consumer and ISender in Autofac ?? Please suggest if there is any better/alternative way.
The way of registering components in Autofac is described widely in the documentation here. And how to use keyed services is described in the documentation you linked.
Basically you have to create ContainerBuilder, register all your components and build the container itself, based on the project type you have.
In your situation you need to use the following registrations:
var builder = new ContainerBuilder();
builder.RegisterType<Post>().Keyed<ISender>("post");
builder.RegisterType<Email>().Keyed<ISender>("email");
builder.RegisterType<Consumer>();
If you use ASP.NET WebApi (I assume that based on the fact you are using Controllers), you need to register your controllers
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
or for MVC
builder.RegisterControllers(typeof(MvcApplication).Assembly);
Now, there are various way to pick the Consumer with proper ISender implementation (I assume you want to pick proper implementation in controller).
One would be to inject IIndex<string, ISender> to Consumer class and pass the proper key string to it as well:
public class Consumer
{
ISender Sender;
public Consumer(string type, IIndex<string, ISender> sender)
{
Sender = sender[type];
}
public void Act()
{
Sender.Send();
}
}
Then, in controller you could use Func injection:
public class PostController
{
Consumer ConsumerObject;
public PostController(Func<string, Consumer> consumer)
{
ConsumerObject = consumer("post");
}
}
public class EmailController
{
Consumer ConsumerObject;
public EmailController(Func<string, Consumer> consumer)
{
ConsumerObject = consumer("email");
}
}
Another could be registering Consumer twice with Register method and resolving ISender in registration time.

Resteasy and Google Guice: how to use multiple #ApplicationPath and resource with #Injection?

I created a project to test the dependency injection offered by Google Guice in my Jax-rs resources, using Resteasy.
My intentions are:
Use multiple #ApplicationPath for the versions of my API. In each class annotated with #ApplicationPath I load a set of classes for the specific version.
Each resource have a #Inject (from Google Guice) in his constructor to inject some services.
I created two classes annotated with #ApplicationPath: ApplicationV1RS and ApplicationV2RS. In both I added the same resources classes (UserResource and HelloResource), only for my test.
My Module is configured like this:
public class HelloModule implements Module
{
public void configure(final Binder binder)
{
binder.bind(IGreeterService.class).to(GreeterService.class);
binder.bind(IUserService.class).to(UserService.class);
}
}
When I call http://localhost:9095/v1/hello/world or http://localhost:9095/v2/hello/world, I receive the same error:
java.lang.RuntimeException: RESTEASY003190: Could not find constructor
for class: org.jboss.resteasy.examples.guice.hello.HelloResource
Well, as I expected, this not works. The Google Guice is not "smart" to instantiate the resource classes using the construtor for me.
But I can't find a way to work. To be really honest, I'm really confuse about how the Google Guice, Jetty and Resteasy play with each other in this scenario.
If I abandon the idea of use #ApplicationPath, my resources work with Google Guice configuring my HelloModule like this:
public class HelloModule implements Module
{
public void configure(final Binder binder)
{
binder.bind(HelloResource.class);
binder.bind(IGreeterService.class).to(GreeterService.class);
binder.bind(UserResource.class);
binder.bind(IUserService.class).to(UserService.class);
}
}
But in this case, I'm passing the control to register my resources (HelloResource and UserResource) to Guice. It's not flexible for me, I can't setup my multiple #ApplicationPath.
So, what I'm missing or not understanding?
I created a project with the problemetic code. Is very easy to setup and test: https://github.com/dherik/resteasy-guice-hello/tree/so-question/README.md
Thanks!
When you have getClasses method in your Application then it tries to create instance for all the registered resources using the default constructor which is missing in our Resources class. One way is to create a default constructor and Inject the dependencies through setter Injection.
And then instead of overriding getClasses in ApplicationV1RS and ApplicationV2RS you override getSingletons. Since Resources can be Singleton.
Below are the changes that I made to make it work the way you want.
ApplicationV1RS.java
#ApplicationPath("v1")
public class ApplicationV1RS extends Application {
private Set<Object> singletons = new HashSet<Object>();
public ApplicationV1RS(#Context ServletContext servletContext) {
}
#Override
public Set<Object> getSingletons() {
Injector injector = Guice.createInjector(new HelloModule());
HelloResource helloResource = injector.getInstance(HelloResource.class);
UserResource userResource = injector.getInstance(UserResource.class);
singletons.add(helloResource);
singletons.add(userResource);
return singletons;
}
}
ApplicationV2RS.java
#ApplicationPath("v2")
public class ApplicationV2RS extends Application {
private Set<Object> singletons = new HashSet<Object>();
public ApplicationV2RS(#Context ServletContext servletContext) {
}
#Override
public Set<Object> getSingletons() {
Injector injector = Guice.createInjector(new HelloModule());
HelloResource helloResource = injector.getInstance(HelloResource.class);
UserResource userResource = injector.getInstance(UserResource.class);
singletons.add(helloResource);
singletons.add(userResource);
return singletons;
}
}
HelloResource.java
#Path("hello")
public class HelloResource {
#Inject
private IGreeterService greeter;
public HelloResource() {
}
#GET
#Path("{name}")
public String hello(#PathParam("name") final String name) {
return greeter.greet(name);
}
}
UserResource.java
#Path("user")
public class UserResource {
#Inject
private IUserService userService;
public UserResource() {
}
#GET
#Path("{name}")
public String hello(#PathParam("name") final String name) {
return userService.getUser(name);
}
}
Add #Singleton to your Service Classes.
Hope it helps.
I have also pushed the code to forked repo. check it out

Does StructureMap have scoping corresponding to NInject's DefinesNamedScope/InNamedScope?

The problem I'd like to solve is sharing an ISessionProvider between IXyzRepositories (where ISessionProvider holds the current NHibernate ISession).
I'm tweaking the "Setting up session per presenter" recipe from NHibernate 3 Cookbook, and would like to keep StructureMap (brownfield project).
I think you would have to create a custom Lifecyle to do that, although I am not sure what exactly you are trying to accomplish...
To create a custom Lifecycle, you just have to implement the ILifecycle interface and the use it in your registration. Here is an example you can look at: http://blog.mikeobrien.net/2010/01/creating-structuremap-lifecycle-for-wcf.html.
In a web application I use Singleton for the sessionFactory and HybridHttpOrThreadLocalScoped for the session:
This is my structuremap registry:
public class NhibernateRegistry: Registry
{
public NhibernateRegistry()
{
For<ISessionFactory>()
.Singleton()
.Use(new NHibernateSessionFactory(connectionString).SessionFactory);
For<ISession>()
.HybridHttpOrThreadLocalScoped()
.Use(o => o.GetInstance<ISessionFactory>().CurrentSession);
}
}
My NHibernateSessionFactory is similar to SessionProvider class used in the book.
Everything is disposed at the end of the request (web app):
protected void Application_EndRequest(object sender, EventArgs e)
{
ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
}
I use a generic repository:
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
private readonly ISession _session;
public GenericRepository(ISession session)
{
_session = session;
}
public T Load(Guid Code)
{
return (_session.Load<T>(Code));
}
}
but you can easily change it with your own implementation.
I register the repository here:
public class RepositoriesRegistry : Registry
{
public RepositoriesRegistry()
{
For <Data.IRepository<Domain.Reminder, Guid>>()
.HybridHttpOrThreadLocalScoped()
.Use<Data.NH.Repository<Domain.Reminder, Guid>>();
}
}