Custom validation without annotation in spring - bean-validation

I have validations in my application which are built based on spring validation annotations like NotNull, NotEmpty.
public class AccountCreateRequest {
#NotNull
private String accountName;
#NotNull
#Size(max = ACCOUNT_NUMBER_CHAR_LIMIT, message = "AccountNumber should not exceed {max} characters",groups = {SqlCharLimitValidatorGroup.class})
private String accountNumber;
private String departmentType;
#NotNull
private AccountType accountTypeId;
private String accountSubTypeId;
}
It works perfectly, I can also use groups based validation for different flows.
I have different clients who want to create entities differently and want to have different validations. I do not want to create multiple groups for them. I want to store the client level config in databases and drive the validation from that config.
For example:
for above request
{
asset: "Account",
client: "123",
fields: [
{
name: "accountName",
validations: ["NotNull", "NotBlank"]
},
...
]
}
For any asset, we would read the field configs from db and validate. How can I achieve it in my spring application?

Related

Ardalis CleanArchitecture - BaseEntity & Composite Keys for AggregateRoots

What is the recommendation for extending BaseEntity as an AggregateRoot is I need to have a composite Key?
Essentially I need to be able to manage an Entity that would normally be configured in EFCore like:
builder.HasKey(z => new { z.PartA, z.PartB });
Currently the template gives us the following definition of BaseEntity, but it's unclear to me how I might need to setup the EntityTypeCuilder config, and how the Aggregates might handle this scenario under the hood.
// This can be modified to BaseEntity<TId> to support multiple key types (e.g. Guid)
public abstract class BaseEntity
{
public int Id { get; set; }
public List<BaseDomainEvent> Events = new List<BaseDomainEvent>();
}
Paul

Querying tags with Cosmos DB with EF Core

I have a domain model that is persisted with EF Core and the Cosmos DB provider. The problem is, I would like to add to it tags, which I would like to query, e.g., return all entries that have this tag. Unfortunately, Cosmos DB does not support string arrays (among others), so I can only imagine using a single string property, which is far from ideal, because if I need multiple tags I would need to combine them on the same property, probably separated by spaces, and this would make querying of a single one very difficult. What are the best options for achieving this?
You can create a POCO:
public class Tag
{
public string Name { get; set; }
}
Then your model class looks like this:
public class MyEntity
{
public string Id { get; set; }
public string Name { get; set; }
public List<Tag> Tags { get; set; }
}
Instruct EF how to handle the model either by overriding OnModelCreating() or using separate config files:
// Configure the one-to-many/owns many relationship:
builder
.OwnsMany(
ownedType: typeof(Tag),
navigationName: nameof(MyEntity.Tags))
.WithOwner();
This will produce something like the following:
{
"Id": "some-unique-id",
"Name": "The name of the entity",
"Tags": [
{
"Name": "Developer"
},
{
"Name": "Hacker"
},
{
"Name": "Guru"
}
]
}
Alternatively, you could define a simple conversion like this:
builder.Property(c => c.Tags)
.HasConversion(
v => string.Join(',', v),
v => v.Split(',', StringSplitOptions.RemoveEmptyEntries).ToList()
);
Which would allow querying against the list of strings as desired, but gets persisted as a comma-delimited string value in the database. While this could be okay, you would lose some querying ability within Cosmos Db.
Note: you may also need to implement a comparer in order for EF to track changes properly.
This is a limitation of EF Core, not Cosmos DB - to disallow entities with properties of string arrays.
To go around this, I just used the Cosmos DB API and dropped EF Core.

EF Core annotation for client side generated values

I'd like to set my code-first class (EF Core 3.1) to have generated values with value generators (Microsoft.EntityFrameworkCore.ValueGeneration.ValueGenerator). It's important to me that the values are generated on the client side and not on the database side. Also, I would like to use an annotation instead of the fluent API because I need to use derived types; something like this:
public class typeX
{
[Key]
[some-annotation?] // This is where I want to add my annotation
public string MyKey { get; set; }
[some-other-annotation?] // This is where I want to add my annotation
public string OtherData { get; set; }
}
public class derivedA : typeX
{
}
public class derivedB : typeX
{
}
I'd like to annotate MyKey and OtherData so they will use generators (e.g. GuidValueGenerator, MyCustomValueGenerator). I think I know how to use the fluent API for that (although most examples are for database side generation), but I can't find a way to use annotations. Is this possible? I'd like to generate values on creation in some cases and on creation+update in others.
Thanks

How does breeze detemine API action method based on entity name?

I have a simple entity: Contact
DBContext line:
public DbSet<Contact> Contacts { get; set; }
API Controller:
[HttpGet]
public IQueryable<Contact> Contacts()
{
return _contextProvider.Context.Contacts;
}
In my data retrieval in breeze client I do this:
var query = EntityQuery.from("Contacts")
.orderBy(orderBy.obContact)
That works great, and I can understand that the from parameter "Contacts" must match the API action method.
In my getByID I do this:
return manager.fetchEntityByKey("Contact", contactId, true)
That's working great, and it also makes a call to the "Contacts" API method. But I would like to know how breeze took the parameter "Contact" and knew to call the "Contacts" method.
Is this in the metadata?
Did it come from my DBSet line in my dbcontext? (I'm thinking it did, but would like confirmation). If that's the case then these two names must be equal right?
[HttpGet]
public IQueryable<Contact>Contacts() // The HTTP action method name
public DbSet<Contact> Contacts { get; set; } // The DbSet variable
I tried these changes:
public DbSet<Contact> DBSetContacts { get; set; }
and
[HttpGet]
public IQueryable<Contact> Contacts()
{
return _contextProvider.Context.DBSetContacts;
}
My first query above that returns an array ran fine.
My fetch by ID failed, it was trying to find URI resource "DBSetContacts".
My conclusion is that the DbSet variable has to have the same name as the URI method for the fetchByID to work. Is that correct?
Breeze internally keeps an EntityType/ResourceName map. ResourceNames are the names of the server side methods.
The EntityType/ResourceName map is one of the items in the Breeze MetadataStore. The map of a new MetadataStore starts empty. Breeze populates it from server metadata if those metadata contain EntityType/Resource mappings.
As you guessed, the Breeze EFContextProvider generates metadata with mappings derived from Entity Framework DbSet names. When you define a Person class and exposed it from a DbContext as a DbSet named "Persons", the EFContextProvider metadata generator adds a mapping from the "Persons" resource name to the Person entity type.
For more information see the 'EntityType/ResourceName" mapping subtopic on this page.
http://www.getbreezenow.com/documentation/querying-locally
You can also update the EntityType/ResourceMap explicitly via the method below:
http://www.getbreezenow.com/sites/all/apidocs/classes/MetadataStore.html#method_setEntityTypeForResourceName

map subset of request params to an object in spring mvc

In our web app, using Spring MVC 3.2 we display many paginated lists of different objects, and the links to other pages in the list are constructed like this:
/servlet/path?pageNum=4&resultsPerPage=10&sortOrder=ASC&sortBy=name
although there might be additional request parameters in the URL as well (e.g., search filters).
So we have controller methods like this:
#RequestMapping(method = RequestMethod.GET, value="/ajax/admin/list")
public String ajaxlistGroups(Model model,
#RequestParam(value="pageNumber",required=false,defaultValue="0") Long pageNumber,
#RequestParam(value="resultsPerPage",required=false,defaultValue="10") int resultsPerPage,
#RequestParam(value="sortOrder",required=false,defaultValue="DESC") String sortOrder,
#RequestParam(value="orderBy",required=false,defaultValue="modificationDate")String orderBy) {
// create a PaginationCriteria object to hold this information for passing to Service layer
// do Database search
// return a JSP view name
}
so we end up with this clumsy method signature, repeated several times in the app, and each method needs to create a PaginationCriteria object to hold the pagination information, and validate the input.
Is there a way to create our PaginationCriteria object automatically, if these request params are present? E.g., replace the above with:
#RequestMapping(method = RequestMethod.GET, value="/ajax/admin/list")
public String ajaxlistGroups(Model model, #SomeAnnotation? PaginationCriteria criteria,
) {
...
}
I.e., is there a way in Spring to take a defined subset of requestParams from a regular GET request, and convert them to an object automatically, so it's available for use in the Controller handler method? I've only used #ModelAttribute before, and that doesn't seem the right thing here.
Thanks!
Spring 3.2 should automatically map request parameters to a custom java bean.
#RequestMapping(method = RequestMethod.GET, value="/ajax/admin/list")
public String ajaxlistGroups(Model model, PaginationCriteriaBean criteriaBean,
) {
//if PaginationCriteriaBean should be populated as long as the field name is same as
//request parameter names.
}
I'm not sure how Spring magically achieve this(without #ModelAttribute), but the code above works for me.
There is another way to achieve the same goal, you can actually achieve more, that is spring AOP.
<bean id="aspectBean" class="au.net.test.aspect.MyAspect"></bean>
<aop:config>
<aop:aspect id="myAspect" ref="aspectBean">
<aop:pointcut id="myPointcut"
expression="execution(* au.net.test.web.*.*(..)) and args(request,bean,..)" />
<aop:before pointcut-ref="myPointcut" method="monitor" />
</aop:aspect>
</aop:config>
in application context, we declare Aspect bean as well as Pointcut along with advice, which in your case is before advice
the following is source code
public class PaginationCriteriaBean {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//custom Aspect
public class MyAspect {
public void monitor( HttpServletRequest request,PaginationCriteriaBean bean){
//populate your pagination bean
bean.setId(request.getParameter("id"));
bean.setName("my new name");
}
}
#RequestMapping(value="/app")
public String appRoot(HttpServletRequest request,PaginationCriteriaBean bean){
System.out.println(bean.getId());
System.out.println(bean.getName());
return "app";
}
by doing so, the aspect will intercept spring controller and populate PaginationCriteriaBean based on request parameters, and you can even change the original value in request. With this AOP implementation you are empowered to apply more logic against Pagination, such as logging and validation and etc.

Resources