given that application.yml contains:
values: "WEB", "MOBILE"
Can I create such class:
enum class Channel {
WEB,
MOBILE
}
and not to list WEB, MOBILE literally in the code, bu instead, take them from the properties?
Related
I wrote the following object type class.
public class ResponseType<T> : ObjectType<ResponseEntry<T>>
{
protected override void Configure(IObjectTypeDescriptor<ResponseEntry<T>> descriptor)
{
descriptor.Name("Response");
}
}
I want to use it like this as the outermost type in the resolver definition.
descriptor.Field<SharedResolvers>(r => r.GetObject1(default, default, default, default))
.Type<ResponseType<ListType<Object1>>>()
.Name("object1");
descriptor.Field<SharedResolvers>(r => r.GetObject2(default, default, default, default))
.Type<ResponseType<ListType<Object2>>>()
.Name("object2");
This code works if I only implement object1 however as soon as I add object2 I get the following error.
System.Collections.Generic.KeyNotFoundException: 'The given key 'HotChocolate.Configuration.RegisteredType' was not present in the dictionary.'
It seems as though there may be some issue with declaring two resolvers of the same class type. Is that the case? And if so, what are my options?
I was able to resolve the issue by setting the descriptor.Name to a unique value based on T.
descriptor.Name($"Response_{typeof(T).GetHashCode()}");
Then I realized my real issue was that I was defining the name at all. If you don't override the name it automatically comes up with a unique name/key based on the type definition.
I am using Spring Boot (2.1.1) to automatically create an HAL REST API of my JpaRepository interfaces.
For most cases these interfaces are empty, for example:
public interface LevelRepository extends JpaRepository<Level, Long> {}
When I open my REST base path, the following link is generated for levels:
"levels": {
"href": "http://localhost:8080/admin/levels{?page,size,sort}",
"templated": true
}
When I follow http://localhost:8080/admin/levels?size=10 I get the expected page 0 and its 10 elements.
But the given self link is:
"self": {
"href": "http://localhost:8080/admin/levels{&sort}",
"templated": true
}
I would have expected:
http://localhost:8080/admin/levels{?page,size,sort} or
http://localhost:8080/admin/levels?page=0&size=10{&sort}
I am not sure if this is a bug or a feature? Is it possible to get the expected behavior?
I have found the following question on the topic: Error on generating self link on pageable resource
But the given solution does not help, as I am already using a newer version.
Further informations (Why do I need it?)
This behavior breaks the flow of following links on the client side.
If the user wants to jump directly to page X or if he wants to change the page size, the original link must be reused.
Not a big workaround, but it is not as nice as templating and following the given self link directly.
After posting this question I found the following Spring JIRA ticket describing the same behavior.
According to a comment in the ticket the self link should not be templated.
Although the generated self link is templated, it is probably better to ignore the given parameters.
You could re-implement the self link by yourself. First of all, I think you use HATEOAS library:
<dependency>
<groupId>org.springframework.hateoas</groupId>
<artifactId>spring-hateoas</artifactId>
</dependency>
Let's consider some of your entity - ExampleEntity:
public class ExampleEntity extends ResourceSupport {
private String id;
private String name;
private String surname;
// standard getters and setters
}
You extended the ResourceSupport class, and it has the link property, which we could use using add method:
ExampleEntity exampleEntity = new ExampleEntity();
Link selfLink = ControllerLinkBuilder.linkTo(YourController.class).slash(id).withSelfRel();
exampleEntity.add(selfLink);
So, you can change behaviour of your self link
I understand that when documenting an API with Swagger in Spring, I can change the description for the API by adding #Api annotation, but when I add it as follows
#Api(value= "NEW_NAME", description="NEW_DESCRIPTION")
Only the description is changed, not the name.
as seen here
Further, I'm not sure where the default name and description are coming from, before adding the API, the name seems to be derived from the controller name, but the description; which to me looks natural and human like almost like hard coded String with capitalization and all.
I ran a search on the code, and I wasn't able to find those Strings. Where's Swagger getting those values from?
thanks
The attribute you are looking for is: tags. So you can avoid grouping by controller name.
From Javadoc of #Api tags:
Tags can be used for logical grouping of operations by resources or any other qualifier.
For example:
#Api(value = "/customers", tags = "customers", description = "Manage Customer")
By default Springfox creates API with name as {controller-name}-controller and description as {Controller Name} Controller (cf. How to change the default Controller Name in Swagger Spring ).
It seems that the current way to do this is:
#Api(description = "Manage cars", tags = { "Cars" })
Default -
{controller-name}-controller
For Custom Name Add -
#Tag(name="YOUR CUSTOM NAME HERE")
on the Controller Class
Example -
#RestController
#Tag(name="1. Project Resource")
public class ProjectResource {...}
Result -
When the class Dog inherits from the class Animal, we say that 'Dog is an Animal'.
When the class Dog has a property Name, we say that 'Dog has a Name'.
When the class Dog has a method void Sleep(), we say that 'Dog can Sleep'.
What do we say when a class has an enum? In case of the following:
enum UserType {
Admin,
Guest,
Registered
}
And where is it common to put enums? Is it usual to put all enums in one big class, like it's done with extension methods?
Enum is list of the constants. That being said, you as developer determine where to use it. Usually months, days of the week, access types are common enums. For instance your example about dog. The one thing came to my mind is paw as enum, when you ask dog for its paw,and you reward him. For instance in your Dog class:
public enum Paw{ RIGHTFRONT,LEFTFRONT,RIGHTRARE,LEFTRARE }
public Dog(Paw paw){
this.paw=paw;
}
public void rewardDog(){
switch(paw){
case RIGHTFRONT: giveFood();
break;
case LEFTFRONT: giveWater();
break;
};
etc.
Then in your activity:
Dog mDog=new Dog(Paw.RIGHTFRONT);
mDog.rewardDog();
In case of UserType, consider it's as some kind of restrictions toward different user type. For instance when you load icons for the users, you can define that if usertype==Admin, then on icon there will be a crown, if usertype==Registered, there will be a star overlain on the main pic. Or admin has access to edit posts, comments; but guests and registered ones don't have that access.
I am implementing ACL security using the spring-security-acl plugin. I have the following domain classes:
package test
class Subitem {
String name
static belongsTo = [employer: Employer]
static constraints = {
name blank: false
}
}
package test
class Employer {
String name
static hasMany = [users: User, items: Subitem]
static belongsTo = User
static constraints = {
name blank: false, unique: true
}
String toString() {
name
}
}
In the create.gsp file which is used to create a Subitem, there is the following statement:
<g:select id="employer" name="employer.id" from="${test.Employer.list()}" optionKey="id" required="" value="${subitemInstance?.employer?.id}" class="many-to-one"/>
From the EmployerController:
def list = {
params.max = Math.min(params.max ? params.int('max') : 10, 100)
[employerInstanceList: employerService.list(params),
employerInstanceTotal: employerService.count()]
}
Following the tutorial given here, I have moved some of the functionality with dealing with Employer to a service called EmployerService:
#PreAuthorize("hasRole('ROLE_USER')")
#PostFilter("hasPermission(filterObject, read)")
List<Employer> list(Map params) {
Employer.list params
}
int count() {
Employer.count()
}
Access to information in any given Employer class instance is restricted using ACL. At present, I can see ALL instances of Employer in the database in the drop down, and I assume that is because I am using the controller list(), not the service list() - however, I only want to see the filtered list of Employer domain classes. However, if I replace the g:select with:
<g:select id="employer" name="employer.id" from="${test.EmployerService.list()}" optionKey="id" required="" value="${subitemInstance?.employer?.id}" class="many-to-one"/>
then I get an internal server error because I haven't passed a Map parameter to the service list() function (and I don't know how to do this within the tag):
URI /security/subitem/create
Class groovy.lang.MissingMethodException
Message No signature of method: static test.EmployerService.list() is applicable for argument types: () values: [] Possible solutions: list(java.util.Map), is(java.lang.Object), wait(), find(), wait(long), get(long)
I only want to see the information that comes from the EmployerService list() function - how do I do this please? How do I reference the correct function from within the gap?
Edit 16 Mar 0835: Thanks #OverZealous, that's really helpful, I hadn't realised that. However, I've tried that and still get the same problem. I've put a println() statement in both the Employer and EmployerService list() functions, and can see that neither actually seems to get called when the g:select tag is parsed (even if I leave the g:select to refer to Employer). Is there another version of the list() function that is being called perhaps? Or how else to get the g:select to take account of the ACL?
Just change your method signature in the Service to look like this:
List<Employer> list(Map params = [:]) {
Employer.list params
}
The change is adding this: = [:]. This provides a default value for params, in this case, an empty map.
(This is a Groovy feature, BTW. You can use it on any method or closure where the arguments are optional, and you want to provide a default.)
OK, I worked it out, and here is the solution to anyone else who comes up against the same problem.
The create Subitem page is rendered by means of the Subitem's create.gsp file and the SubitemController. The trick is to amend the SubitemController create() closure:
class SubitemController {
def employerService
def create() {
// this line was the default supplied method:
// [subitemInstance: new Subitem(params)]
// so replace with the following:
params.max = Math.min(params.max ? params.int('max') : 10, 100)
[subitemInstance: new Subitem(params), employerInstanceList: employerService.list(params),
employerInstanceTotal: employerService.count()]
}
}
So now when the SubitemController is asked by the g:select within the Subitem view for the list of Employers, it calls the EmployerService, which supplies the correct answer. We have simply added 2 further variables that are returned to the view, and which can be referenced anywhere within the view (such as by the g:select tag).
The lesson for me is that the View interacts with the Controller, which can refer to a Service: the Service doesn't play nicely with a View, it seems.