How to define model specification file? - jhipster

Kindly help on below error, I am getting some model specification, I am not sure on how to define one.
JHipster JDL file content
application {
config {
baseName myApp,
applicationType monolith,
packageName com.myapp,
authenticationType jwt,
prodDatabaseType postgresql,
clientFramework angular
enableSwaggerCodegen true
}
entities *
}
// One to One
entity Product { name String }
entity Origin { name String}
relationship OneToOne { Product to Origin }
// Many to One
entity Rating { name String }
entity Movie { name String }
relationship ManyToOne { Rating to Movie }
// One to Many
entity Customer { name String }
entity Address { name String }
relationship OneToMany { Customer to Address }
// Many to Many
entity Car { name String }
entity Owner { name String }
relationship ManyToMany { Car to Owner }
Error
After build is success, I tried below ./mvnw, and got below error even though app is running fine...
2021-06-02 14:50:24.334 DEBUG 574 --- [ restartedMain] Validator : ERROR [ModelSpecification.spec] At least one type of specification is required
2021-06-02 14:50:24.337 DEBUG 574 --- [ restartedMain] Validator : ERROR [ModelSpecification.spec] At least one type of specification is required
2021-06-02 14:50:24.342 DEBUG 574 --- [ restartedMain] Validator : ERROR [ModelSpecification.spec] At least one type of specification is required

It's not really an error as it is logged at DEBUG level. Although it's confusing, you can ignore it.
It's a known issue that will be fixed in next release 7.1.0, see https://github.com/jhipster/generator-jhipster/issues/13835

Related

DDD : Business Logic which need infra layer access should be in application service layer, domain service or domain objects?

For an attribute which need to be validated, lets say for an entity we have country field as VO
This country field needs to be validated to be alpha-3 code as per some business logic required by domain expert.
NOTE:
*We need to persist this country data as it can have other values also and possible in future there can be addition, updating and deleting of the country persisted data.
This is just one example using country code which may rarely change, there can be other fields which needs to be validated from persistence like validating some quantity with wrt data in persistence and it won't be efficient to store them in memory or prefetching them all.
Another valid example can be user creation with unique and valid domain email check, which will need uniqueness check from persistence
*
Case 1.
Doing validation in application layer:
If we call repository countryRepo.getCountryByCountryAlpha3Code() in application layer and then if the value is correct and valid part of system we can then pass the createValidEntity() and if not then can throw the error directly in application layer use-case.
Issue:
This validation will be repeated in multiple use-case if same validation need to be checked in other use-cases if its application layer concern
Here the business logic is now a part of application service layer
Case 2
Validating the country code in its value object class or domain service in Domain Layer
Doing this will keep business logic inside domain layer and also won't violate DRY principle.
import { ValueObject } from '#shared/core/domain/ValueObject';
import { Result } from '#shared/core/Result';
import { Utils } from '#shared/utils/Utils';
interface CountryAlpha3CodeProps {
value: string;
}
export class CountryAlpha3Code extends ValueObject<CountryAlpha3CodeProps> {
// Case Insensitive String. Only printable ASCII allowed. (Non-printable characters like: Carriage returns, Tabs, Line breaks, etc are not allowed)
get value(): string {
return this.props.value;
}
private constructor(props: CountryAlpha3CodeProps) {
super(props);
}
public static create(value: string): Result<CountryAlpha3Code> {
return Result.ok<CountryAlpha3Code>(new CountryAlpha3Code({ value: value }));
}
}
Is it good to call the repository from inside domain layer (Service
or VO (not recommended) ) then dependency flow will change?
If we trigger event how to make it synchronous?
What are some better ways to solve this?
export default class UseCaseClass implements IUseCaseInterface {
constructor(private readonly _repo: IRepo, private readonly countryCodeRepo: ICountryCodeRepo) {}
async execute(request: dto): Promise<dtoResponse> {
const someOtherKeyorError = KeyEntity.create(request.someOtherDtoKey);
const countryOrError = CountryAlpha3Code.create(request.country);
const dtoResult = Result.combine([
someOtherKeyorError, countryOrError
]);
if (dtoResult.isFailure) {
return left(Result.fail<void>(dtoResult.error)) as dtoResponse;
}
try {
// -> Here we are just calling the repo
const isValidCountryCode = await this.countryCodeRepo.getCountryCodeByAlpha2Code(countryOrError.getValue()); // return boolean value
if (!isValidCountryCode) {
return left(new ValidCountryCodeError.CountryCodeNotValid(countryOrError.getValue())) as dtoResponse;
}
const dataOrError = MyEntity.create({...request,
key: someOtherKeyorError.city.getValue(),
country: countryOrError.getValue(),
});
const commandResult = await this._repo.save(dataOrError.getValue());
return right(Result.ok<any>(commandResult));
} catch (err: any) {
return left(new AppError.UnexpectedError(err)) as dtoResponse;
}
}
}
In above application layer,
this part of code :
const isValidCountryCode = await this.countryCodeRepo.getCountryCodeByAlpha2Code(countryOrError.getValue()); // return boolean value
if (!isValidCountryCode) {
return left(new ValidCountryCodeError.CountryCodeNotValid(countryOrError.getValue())) as dtoResponse;
}
it it right to call the countryCodeRepo and fetch result or this part should be moved to domain service and then check the validity of the countryCode VO?
UPDATE:
After exploring I found this article by Vladimir Khorikov which seems close to what I was looking, he is following
As per his thoughts some domain logic leakage is fine, but I feel it will still keep the value object validation in invalid state if some other use case call without knowing that persistence check is necessary for that particular VO/entity creation.
I am still confused for the right approach
In my opinion, the conversion from String to ValueObject does not belong to the Business Logic at all. The Business Logic has a public contract that is invoked from the outside (API layer or presentation layer maybe). The contract should already expect Value Objects, not raw strings. Therefore, whoever is calling the business logic has to figure out how to obtain those Value Objects.
Regarding the implementation of the Country Code value object, I would question if it is really necessary to load the country codes from the database. The list of country codes very rarely changes. The way I've solved this in the past is simply hardcoding the list of country codes inside the value object itself.
Sample code in pseudo-C#, but you should get the point:
public class CountryCode : ValueObject
{
// Static definitions to be used in code like:
// var myCountry = CountryCode.France;
public static readonly CountryCode France = new CountryCode("FRA");
public static readonly CountryCode China = new CountryCode("CHN");
[...]
public static AllCountries = new [] {
France, China, ...
}
public string ThreeLetterCode { get; }
private CountryCode(string threeLetterCountryCode)
{
ThreeLetterCode = threeLetterCountryCode;
}
public static CountryCode Parse(string code)
{
[...] handle nulls, empties, etc
var exists = AllCountries.FirstOrDefault(c=>c.ThreeLetterCode==code);
if(exists == null)
// throw error
return exists;
}
}
Following this approach, you can make a very useful and developer-friendly CountryCode value object. In my actual solution, I had both the 2 and 3-letter codes and display names in English only for logging purposes (for presentation purposes, the presentation layer can look up the translation based on the code).
If loading the country codes from the DB is valuable for your scenario, it's still very likely that the list changes very rarely, so you could for example load a static list in the value object itself at application start up and then refresh it periodically if the application runs for very long.

Can't get CollectionField to work in EasyAdmin 3.0

I am trying to use "Tags" in my Account Entity.
So
I have Entity "Account"
I have Entity "Tag"
In "Account" Entity, I have
/**
* #ORM\ManyToMany(targetEntity=Tag::class, inversedBy="accounts")
* #ORM\JoinTable(name="account_tag")
*/
private $tags;
In "Tag" entity I have
/**
* #ORM\ManyToMany(targetEntity=Account::class, mappedBy="tags")
*/
private $accounts;
In my AccountCrudController => ConfigureFields, I use "CollectionField" for my "tags" property
public function configureFields(string $pageName): iterable
{
return [
TextField::new('name'),
AssociationField::new('owner'),
AssociationField::new('parent'),
CollectionField::new('tags'),
];
}
I am getting below
[Expected value of type "App\Entity\Tag" for association field "App\Entity\Account#$tags", got "string" instead.1
You should be able to use an AssociationField here as well, which would fit your purpose.
AssociationField::new('tags') will allow you to reference existing Tags.
If you wish to create all new Tags together, you could use something like this as there is no way to add Tags on the fly in the AssociationField at the moment.
Have you tried setting your CollectionField like this:
CollectionField::new('tags')
->allowAdd()
->allowDelete()
->setEntryType(TagType::class)
;
The important part is the TagType where you define your own FormType. I am also trying to implement this feature, so if you have a fully working example, let us know!
public function configureFields(string $pageName): iterable
{
return [
TextField::new('name'),
AssociationField::new('owner'),
AssociationField::new('parent'),
CollectionField::new('tags')
->SetEntryType(Tag::class)
];
}

DDD: separate model for domain and persistence

Assume we have separate models for Domain and Persistence, one domain model is stored as two persistence model, as given below.
class DomainEntity {
property1;
property2;
}
class PersistenceEntity1 {
domainProperty1;
appProperty1;
}
class PersistenceEntity2 {
domainPproperty2;
appProperty2;
}
If you see the models there are some extra application properties in the persistence model which doesn't belong in the domain model, e.g. modifiedOn, modifiedBy etc...
Now my question is how to pass these values to the infrastructure layer, since the Repository interface also belongs to the Domain layer, we can't add these properties to its signature.
The additional attributes that do not belong to the Domain can be added from the Infrastructure, for example from the Repository implementation. In this way, the Domain remain agnostic of infrastructure concerns.
The Repository implementation could get that data from the services that get injected. For example, if the Persistence model needs the current Authenticated user ID to be stored in the modifiedBy then the AuthenticatedUserService should be injected into the Repository implementation.
One simpler example is the modifiedOn that can be set to the Current system date, without any service injection.
As a pseudocode:
// Domain layer
class DomainEntity {
property1;
property2;
}
// Infrastructure layer
class PersistenceEntity1 {
domainProperty1;
Date modifiedOn;
}
class PersistenceEntity2 {
domainPproperty2;
UserId modifiedBy;
}
class Repository {
// dependency injection
constructor(AuthenticatedUserService auth){
this.auth = auth;
}
function save(DomainEntity d) {
PersistenceEntity1 p1 = new PersistenceEntity1(d.property1, Date.current() );
PersistenceEntity2 p21 = new PersistenceEntity1(d.property1, this.auth.getAuthenticatedUserId() );
db1.save(p1);
db2.save(p2);
}
}

asp.net core identity simpleinjector register applicationusermanager

i'm try isolation ASP.NET Core Identity version 1.1.2, architecture DDD and creating a CrossCutting layer to create a classlib for aspnet core identity , and i using SimpleInjector 4.0.8 for my IoC, so i create a class ApplicationUserManager and ApplicationSignInManager, but i'm cannot register this class in the simlpleinjector container
ApplicationUserManager
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IUserStore<ApplicationUser> store,
IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<ApplicationUser> passwordHasher,
IEnumerable<IUserValidator<ApplicationUser>> userValidators,
IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators,
ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors,
IServiceProvider services,
ILogger<UserManager<ApplicationUser>> logger)
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
{
// My configurations stuffs were...
}
}
and the class ApplicationSignInManager
public class ApplicationSignInManager : SignInManager<ApplicationUser>
{
public ApplicationSignInManager(UserManager<ApplicationUser> userManager,
IHttpContextAccessor contextAccessor,
IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory,
IOptions<IdentityOptions> optionsAccessor,
ILogger<SignInManager<ApplicationUser>> logger)
: base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger)
{ }
// TODO: bug com tipo de retorno IdentityResult para ClaimsPrincipal
//public override Task<ClaimsPrincipal> CreateUserPrincipalAsync(ApplicationUser user)
//{
// return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
//}
}
and i try register this class in BootStrapper.cs like this
public static void RegisterServices(Container container)
{
// Infra.Data App Context
// IdentityAppDbContext
container.RegisterSingleton(() =>
{
var options = new DbContextOptions<IdentityAppDbContext>();
return new IdentityAppDbContext(options);
});
// NetCore Identity
container.RegisterSingleton<ApplicationUserManager>();
container.RegisterSingleton<ApplicationSignInManager>();
container.RegisterSingleton<IUserStore<ApplicationUser>>(() =>
{
var options = new DbContextOptions<IdentityAppDbContext>();
return new UserStore<ApplicationUser>(new IdentityAppDbContext(options));
});
container.Register(() => (IOptions<IdentityOptions>)new IdentityOptions());
container.RegisterSingleton<IPasswordHasher<ApplicationUser>>(() => new PasswordHasher<ApplicationUser>());
}
but when i run the aaplication return erros says i need registre IOptions, IPasswordHasher and other params in contructors class, the queston is, how can'i register this class?
I had a similar requirement: my application needs to interact with identity (it's basically about human resources, that might have a user account on their own or not), and after struggling a long time with with various issues having identity in the framework container, I was trying to configure simple injector to provide all identity services. Call it crazy, but this is how it works (SimpleInjector 4.0 and ASP.Net Identity Core 2.0.1):
This is the "ConfigureServices" part:
// identity options are provided from outside, allowing configuration of the framework
container.RegisterSingleton<IOptions<IdentityOptions>>(new OptionsWrapper<IdentityOptions>(identityOptions));
// we rely on BCrypt instead of the default PBKDF2 hashing algorithm
container.Register<IPasswordHasher<MepUser>>(()=>new BCryptPasswordHasher(bcryptOptions));
// forwarding the framework logger to our own logging framework
container.Register<Microsoft.Extensions.Logging.ILoggerFactory, FrameworkToBackendFxLoggerFactory>();
container.Register(typeof(Microsoft.Extensions.Logging.ILogger<>), typeof(Microsoft.Extensions.Logging.Logger<>));
// identity store = a specific Entity Framework Core DbContext, getting mapped into a specific db scheme
container.RegisterSingleton(identityDbContextOptions);
container.Register<MepIdentityDbContext>();
// UserStore<T> und RoleStore<T> both require a DbContext (no IdentityDbContext, neither a generic TDbContext)
// via constructor, but the container only knows about MepIdentityDbContext so we have to wire it manually
container.Register<IUserStore<MepUser>>(() => new UserStore<MepUser>(
container.GetInstance<MepIdentityDbContext>(),
container.GetInstance<IdentityErrorDescriber>()));
container.Register<IRoleStore<IdentityRole>>(() => new RoleStore<IdentityRole>(
container.GetInstance<MepIdentityDbContext>(),
container.GetInstance<IdentityErrorDescriber>()));
// framework internal services
container.Register<IdentityErrorDescriber>();
container.Register<ILookupNormalizer, UpperInvariantLookupNormalizer>();
container.Register<IPasswordValidator<MepUser>, PasswordValidator<MepUser>>();
container.Register<IUserValidator<MepUser>, UserValidator<MepUser>>();
container.Register<IUserClaimsPrincipalFactory<MepUser>, UserClaimsPrincipalFactory<MepUser>>();
container.Register<IRoleValidator<IdentityRole>, RoleValidator<IdentityRole>>();
// ASP.Net Core Identity violates a design decision of SimpleInjector: The API clearly differentiates the registration of collections
// https://simpleinjector.readthedocs.io/en/latest/decisions.html#the-api-clearly-differentiates-the-registration-of-collections
// By registering IEnumerables of those violating services with a simple wrapping single item array, Identity is happy
container.Register<IEnumerable<IUserValidator<MepUser>>>(() => new[] { container.GetInstance<IUserValidator<MepUser>>() });
container.Register<IEnumerable<IRoleValidator<IdentityRole>>>(() => new[] { container.GetInstance<IRoleValidator<IdentityRole>>() });
container.Register<IEnumerable<IPasswordValidator<MepUser>>>(() => new[] { container.GetInstance<IPasswordValidator<MepUser>>() });
// Role and UserManager reflect the API surface of the whole ASP.Net Core Identity framework
container.Register<RoleManager<IdentityRole>>();
// UserManagerShim is omitting (=nulling) the IServiceProvider parameter of UserManager<T>
container.Register<UserManager<MepUser>, UserManagerShim>();
and this needs to be done during "Configure", otherwise password reset and other two factor token stuff won't work (runtime error, although the container was verified)
// if you eagerly instantiate a provider instance that is considered being a singleton and
// put it into the respective field in the TokenProviderDescriptor and list it in the option's
// provider map, ASP.Net Core Identity will use this one instead of asking the IServiceProvider
// instance injected into UserManager<T> (that we do not do, because it is bad design. Instead,
// we just stuff null in there)
identityOptions.Tokens.ProviderMap[TokenOptions.DefaultProvider] =
new TokenProviderDescriptor(typeof(DataProtectorTokenProvider<MepUser>))
{
ProviderInstance = new DataProtectorTokenProvider<MepUser>(
dataProtectionProvider,
new OptionsWrapper<DataProtectionTokenProviderOptions>(new DataProtectionTokenProviderOptions()))
};
identityOptions.Tokens.ProviderMap[TokenOptions.DefaultEmailProvider] =
new TokenProviderDescriptor(typeof(EmailTokenProvider<MepUser>)) {
ProviderInstance = new EmailTokenProvider<MepUser>()
};
Although there appears AspNetCore in the namespace definition, nothing is dependent on ASP.Net Core hosting, actually. The only dependency to the "outside world" are the options (POCOs) and an implementation of IDataProtectionProvider, that will use the KeyRing in ASP.Net scenarios, but can also be satisfied using the EphemeralDataProtectionProvider in tests.
Caveat: SignInManager<T> is not being injected. This class is a total mess, depending on the whole world, so I rewrote it basically matching my requirements. You also loose lots of the configuration flexibility regarding token providers. But however, in my case it's something you decide once at design time, so I am fine with it.

Eloquent: Scope for getting related models?

I have 3 models: User, Project, Task
Each user has many projects, each project has many tasks.
I want to do something like:
$user->projects()->tasks() to get the tasks of the projects of a user. projects() is a hasMany() relation and I believe tasks() should be a Scope, but have no idea how to implement it.
Well, suppose you have 3 models defined in this way:
User
class User extends Eloquent
{
public function projects()
{
return $this->hasMany('Project');
}
}
Project
class Project extends Eloquent
{
public function tasks()
{
return $this->hasMany('Task');
}
public function user()
{
return $this->belongsTo('User');
}
}
Task
class Task extends Eloquent
{
public function project()
{
return $this->belongsTo('Project');
}
}
I believe this would be enough to query all the user projects and tasks.
If you already have an instance of User class you can just load the required relations:
$user->load('projects.tasks');
dd($user);
Having belongsTo relations defined allows you to query all the tasks with projects and users they belong to:
Task::with('project.user')->get();
I am not sure about getting just a list of tasks. This is the first solution that comes to my mind:
$tasks = Task::whereHas('project', function($query) use ($user){
$query->whereHas('user', function($query) use ($user){
$query->where('id', $user->id);
});
})->get()
I believe there are other ways to achieve it using query builder and join() method.

Resources