diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/builder/PublicUserBuilder.java b/dmp-backend/core/src/main/java/eu/eudat/model/builder/PublicUserBuilder.java new file mode 100644 index 000000000..cbfa0f136 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/builder/PublicUserBuilder.java @@ -0,0 +1,149 @@ +package eu.eudat.model.builder; + +import eu.eudat.authorization.AuthorizationFlags; +import eu.eudat.commons.JsonHandlingService; +import eu.eudat.commons.types.user.AdditionalInfoEntity; +import eu.eudat.convention.ConventionService; +import eu.eudat.data.UserEntity; +import eu.eudat.model.*; +import eu.eudat.query.UserContactInfoQuery; +import eu.eudat.query.UserCredentialQuery; +import eu.eudat.query.UserRoleQuery; +import gr.cite.tools.data.builder.BuilderFactory; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.exception.MyApplicationException; +import gr.cite.tools.fieldset.BaseFieldSet; +import gr.cite.tools.fieldset.FieldSet; +import gr.cite.tools.logging.DataLogEntry; +import gr.cite.tools.logging.LoggerService; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.stream.Collectors; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class UserBuilder extends BaseBuilder { + + private final QueryFactory queryFactory; + + private final BuilderFactory builderFactory; + private final JsonHandlingService jsonHandlingService; + + private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); + + @Autowired + public UserBuilder(ConventionService conventionService, + QueryFactory queryFactory, + BuilderFactory builderFactory, JsonHandlingService jsonHandlingService) { + super(conventionService, new LoggerService(LoggerFactory.getLogger(UserBuilder.class))); + this.queryFactory = queryFactory; + this.builderFactory = builderFactory; + this.jsonHandlingService = jsonHandlingService; + } + + public UserBuilder authorize(EnumSet values) { + this.authorize = values; + return this; + } + + @Override + public List build(FieldSet fields, List data) throws MyApplicationException { + this.logger.debug("building for {} items requesting {} fields", Optional.ofNullable(data).map(List::size).orElse(0), Optional.ofNullable(fields).map(FieldSet::getFields).map(Set::size).orElse(0)); + this.logger.trace(new DataLogEntry("requested fields", fields)); + if (fields == null || data == null || fields.isEmpty()) + return new ArrayList<>(); + + List models = new ArrayList<>(); + + FieldSet contactsFields = fields.extractPrefixed(this.asPrefix(User._contacts)); + Map> contactsMap = this.collectUserContactInfos(contactsFields, data); + + FieldSet rolesFields = fields.extractPrefixed(this.asPrefix(User._roles)); + Map> rolesMap = this.collectUserRoles(rolesFields, data); + + FieldSet credentialsFields = fields.extractPrefixed(this.asPrefix(User._credentials)); + Map> credentialsMap = this.collectUserCredentials(credentialsFields, data); + + FieldSet additionalInfoFields = fields.extractPrefixed(this.asPrefix(User._additionalInfo)); + for (UserEntity d : data) { + User m = new User(); + if (fields.hasField(this.asIndexer(User._id))) m.setId(d.getId()); + if (fields.hasField(this.asIndexer(User._name))) m.setName(d.getName()); + if (fields.hasField(this.asIndexer(User._createdAt))) m.setCreatedAt(d.getCreatedAt()); + if (fields.hasField(this.asIndexer(User._updatedAt))) m.setUpdatedAt(d.getUpdatedAt()); + if (fields.hasField(this.asIndexer(User._isActive))) m.setIsActive(d.getIsActive()); + if (fields.hasField(this.asIndexer(User._hash))) m.setHash(this.hashValue(d.getUpdatedAt())); + if (contactsMap != null && !contactsFields.isEmpty() && contactsMap.containsKey(d.getId())) m.setContacts(contactsMap.get(d.getId())); + if (rolesMap != null && !rolesFields.isEmpty() && rolesMap.containsKey(d.getId())) m.setRoles(rolesMap.get(d.getId())); + if (credentialsMap != null && !credentialsFields.isEmpty() && credentialsMap.containsKey(d.getId())) m.setCredentials(credentialsMap.get(d.getId())); + if (!additionalInfoFields.isEmpty() && d.getAdditionalInfo() != null){ + AdditionalInfoEntity definition = this.jsonHandlingService.fromJsonSafe(AdditionalInfoEntity.class, d.getAdditionalInfo()); + m.setAdditionalInfo(this.builderFactory.builder(UserAdditionalInfoBuilder.class).authorize(this.authorize).build(additionalInfoFields, definition)); + } + models.add(m); + } + this.logger.debug("build {} items", Optional.of(models).map(List::size).orElse(0)); + + return models; + } + + private Map> collectUserContactInfos(FieldSet fields, List data) throws MyApplicationException { + if (fields.isEmpty() || data.isEmpty()) return null; + this.logger.debug("checking related - {}", UserContactInfo.class.getSimpleName()); + + Map> itemMap; + FieldSet clone = new BaseFieldSet(fields.getFields()).ensure(this.asIndexer(UserContactInfo._user, User._id)); + UserContactInfoQuery query = this.queryFactory.query(UserContactInfoQuery.class).authorize(this.authorize).userIds(data.stream().map(UserEntity::getId).distinct().collect(Collectors.toList())); + itemMap = this.builderFactory.builder(UserContactInfoBuilder.class).authorize(this.authorize).asMasterKey(query, clone, x -> x.getUser().getId()); + + if (!fields.hasField(this.asIndexer(UserContactInfo._user, User._id))) { + itemMap.values().stream().flatMap(List::stream).filter(x -> x != null && x.getUser() != null).peek(x -> { + x.getUser().setId(null); + }); + } + + return itemMap; + } + + private Map> collectUserRoles(FieldSet fields, List data) throws MyApplicationException { + if (fields.isEmpty() || data.isEmpty()) return null; + this.logger.debug("checking related - {}", UserRole.class.getSimpleName()); + + Map> itemMap; + FieldSet clone = new BaseFieldSet(fields.getFields()).ensure(this.asIndexer(UserRole._user, User._id)); + UserRoleQuery query = this.queryFactory.query(UserRoleQuery.class).authorize(this.authorize).userIds(data.stream().map(UserEntity::getId).distinct().collect(Collectors.toList())); + itemMap = this.builderFactory.builder(UserRoleBuilder.class).authorize(this.authorize).asMasterKey(query, clone, x -> x.getUser().getId()); + + if (!fields.hasField(this.asIndexer(UserRole._user, User._id))) { + itemMap.values().stream().flatMap(List::stream).filter(x -> x != null && x.getUser() != null).peek(x -> { + x.getUser().setId(null); + }); + } + + return itemMap; + } + + private Map> collectUserCredentials(FieldSet fields, List data) throws MyApplicationException { + if (fields.isEmpty() || data.isEmpty()) return null; + this.logger.debug("checking related - {}", UserCredential.class.getSimpleName()); + + Map> itemMap; + FieldSet clone = new BaseFieldSet(fields.getFields()).ensure(this.asIndexer(UserCredential._user, User._id)); + UserCredentialQuery query = this.queryFactory.query(UserCredentialQuery.class).authorize(this.authorize).userIds(data.stream().map(UserEntity::getId).distinct().collect(Collectors.toList())); + itemMap = this.builderFactory.builder(UserCredentialBuilder.class).authorize(this.authorize).asMasterKey(query, clone, x -> x.getUser().getId()); + + if (!fields.hasField(this.asIndexer(UserCredential._user, User._id))) { + itemMap.values().stream().flatMap(List::stream).filter(x -> x != null && x.getUser() != null).peek(x -> { + x.getUser().setId(null); + }); + } + + return itemMap; + } + +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/builder/UserAdditionalInfoBuilder.java b/dmp-backend/core/src/main/java/eu/eudat/model/builder/UserAdditionalInfoBuilder.java new file mode 100644 index 000000000..c32a85c0b --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/builder/UserAdditionalInfoBuilder.java @@ -0,0 +1,106 @@ +package eu.eudat.model.builder; + +import eu.eudat.authorization.AuthorizationFlags; +import eu.eudat.convention.ConventionService; +import eu.eudat.data.UserCredentialEntity; +import eu.eudat.model.User; +import eu.eudat.model.UserCredential; +import eu.eudat.query.UserQuery; +import gr.cite.tools.data.builder.BuilderFactory; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.exception.MyApplicationException; +import gr.cite.tools.fieldset.BaseFieldSet; +import gr.cite.tools.fieldset.FieldSet; +import gr.cite.tools.logging.DataLogEntry; +import gr.cite.tools.logging.LoggerService; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.stream.Collectors; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class UserCredentialBuilder extends BaseBuilder { + + private final BuilderFactory builderFactory; + + private final QueryFactory queryFactory; + + private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); + + @Autowired + public UserCredentialBuilder( + ConventionService conventionService, + BuilderFactory builderFactory, QueryFactory queryFactory) { + super(conventionService, new LoggerService(LoggerFactory.getLogger(UserCredentialBuilder.class))); + this.builderFactory = builderFactory; + this.queryFactory = queryFactory; + } + + public UserCredentialBuilder authorize(EnumSet values) { + this.authorize = values; + return this; + } + + @Override + public List build(FieldSet fields, List data) throws MyApplicationException { + this.logger.debug("building for {} items requesting {} fields", Optional.ofNullable(data).map(List::size).orElse(0), Optional.ofNullable(fields).map(FieldSet::getFields).map(Set::size).orElse(0)); + this.logger.trace(new DataLogEntry("requested fields", fields)); + if (fields == null || data == null || fields.isEmpty()) + return new ArrayList<>(); + + FieldSet userFields = fields.extractPrefixed(this.asPrefix(UserCredential._user)); + Map userItemsMap = this.collectUsers(userFields, data); + + List models = new ArrayList<>(); + + for (UserCredentialEntity d : data) { + UserCredential m = new UserCredential(); + if (fields.hasField(this.asIndexer(UserCredential._id))) m.setId(d.getId()); + if (fields.hasField(this.asIndexer(UserCredential._createdAt))) m.setCreatedAt(d.getCreatedAt()); + if (fields.hasField(this.asIndexer(UserCredential._externalId))) m.setExternalId(d.getExternalId()); + if (!userFields.isEmpty() && userItemsMap != null && userItemsMap.containsKey(d.getUserId())) m.setUser(userItemsMap.get(d.getUserId())); + + models.add(m); + } + + this.logger.debug("build {} items", Optional.of(models).map(List::size).orElse(0)); + + return models; + } + + private Map collectUsers(FieldSet fields, List data) throws MyApplicationException { + if (fields.isEmpty() || data.isEmpty()) + return null; + this.logger.debug("checking related - {}", User.class.getSimpleName()); + + Map itemMap; + if (!fields.hasOtherField(this.asIndexer(User._id))) { + itemMap = this.asEmpty( + data.stream().map(UserCredentialEntity::getUserId).distinct().collect(Collectors.toList()), + x -> { + User item = new User(); + item.setId(x); + return item; + }, + User::getId); + } else { + FieldSet clone = new BaseFieldSet(fields.getFields()).ensure(User._id); + UserQuery q = this.queryFactory.query(UserQuery.class).authorize(this.authorize).ids(data.stream().map(UserCredentialEntity::getUserId).distinct().collect(Collectors.toList())); + itemMap = this.builderFactory.builder(UserBuilder.class).authorize(this.authorize).asForeignKey(q, clone, User::getId); + } + if (!fields.hasField(User._id)) { + itemMap.forEach((id, item) -> { + if (item != null) + item.setId(null); + }); + } + + return itemMap; + } + +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/builder/UserBuilder.java b/dmp-backend/core/src/main/java/eu/eudat/model/builder/UserBuilder.java new file mode 100644 index 000000000..7cf5a08a9 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/builder/UserBuilder.java @@ -0,0 +1,131 @@ +package eu.eudat.model.builder; + +import eu.eudat.authorization.AuthorizationFlags; +import eu.eudat.convention.ConventionService; +import eu.eudat.data.DmpEntity; +import eu.eudat.model.*; +import eu.eudat.query.DescriptionQuery; +import eu.eudat.query.DmpReferenceQuery; +import eu.eudat.query.DmpUserQuery; +import gr.cite.tools.data.builder.BuilderFactory; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.exception.MyApplicationException; +import gr.cite.tools.fieldset.BaseFieldSet; +import gr.cite.tools.fieldset.FieldSet; +import gr.cite.tools.logging.DataLogEntry; +import gr.cite.tools.logging.LoggerService; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.stream.Collectors; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class DmpBuilder extends BaseBuilder { + + private final QueryFactory queryFactory; + + private final BuilderFactory builderFactory; + + private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); + + @Autowired + public DmpBuilder(ConventionService conventionService, + QueryFactory queryFactory, + BuilderFactory builderFactory) { + super(conventionService, new LoggerService(LoggerFactory.getLogger(DmpBuilder.class))); + this.queryFactory = queryFactory; + this.builderFactory = builderFactory; + } + + public DmpBuilder authorize(EnumSet values) { + this.authorize = values; + return this; + } + + @Override + public List build(FieldSet fields, List data) throws MyApplicationException { + this.logger.debug("building for {} items requesting {} fields", Optional.ofNullable(data).map(List::size).orElse(0), Optional.ofNullable(fields).map(FieldSet::getFields).map(Set::size).orElse(0)); + this.logger.trace(new DataLogEntry("requested fields", fields)); + if (fields == null || data == null || fields.isEmpty()) + return new ArrayList<>(); + + List models = new ArrayList<>(); + + FieldSet dmpReferencesFields = fields.extractPrefixed(this.asPrefix(Dmp._dmpReferences)); + Map> dmpReferencesMap = this.collectDmpReferences(dmpReferencesFields, data); + + FieldSet dmpUsersFields = fields.extractPrefixed(this.asPrefix(Dmp._dmpUsers)); + Map> dmpUsersMap = this.collectDmpUsers(dmpUsersFields, data); + + for (DmpEntity d : data) { + Dmp m = new Dmp(); + if (fields.hasField(this.asIndexer(Dmp._id))) m.setId(d.getId()); + if (fields.hasField(this.asIndexer(Dmp._label))) m.setLabel(d.getLabel()); + if (fields.hasField(this.asIndexer(Dmp._version))) m.setVersion(d.getVersion()); + if (fields.hasField(this.asIndexer(Dmp._status))) m.setStatus(d.getStatus()); + if (fields.hasField(this.asIndexer(Dmp._properties))) m.setProperties(d.getProperties()); + if (fields.hasField(this.asIndexer(Dmp._groupId))) m.setGroupId(d.getGroupId()); + if (fields.hasField(this.asIndexer(Dmp._description))) m.setDescription(d.getDescription()); + if (fields.hasField(this.asIndexer(Dmp._createdAt))) m.setCreatedAt(d.getCreatedAt()); + if (fields.hasField(this.asIndexer(Dmp._updatedAt))) m.setUpdatedAt(d.getUpdatedAt()); + if (fields.hasField(this.asIndexer(Dmp._isActive))) m.setIsActive(d.getIsActive()); + if (fields.hasField(this.asIndexer(Dmp._finalizedAt))) m.setFinalizedAt(d.getFinalizedAt()); + if (fields.hasField(this.asIndexer(Dmp._accessType))) m.setAccessType(d.getAccessType()); + if (fields.hasField(this.asIndexer(Dmp._blueprint))) m.setBlueprint(d.getBlueprint()); + if (fields.hasField(this.asIndexer(Dmp._language))) m.setLanguage(d.getLanguage()); + if (fields.hasField(this.asIndexer(Dmp._versionStatus))) m.setVersionStatus(d.getVersionStatus()); + if (fields.hasField(this.asIndexer(Dmp._publicAfter))) m.setPublicAfter(d.getPublicAfter()); + if (fields.hasField(this.asIndexer(Dmp._hash))) m.setHash(this.hashValue(d.getUpdatedAt())); + if (dmpReferencesMap != null && !dmpReferencesMap.isEmpty() && dmpReferencesMap.containsKey(d.getId())) m.setDmpReferences(dmpReferencesMap.get(d.getId())); + if (dmpUsersMap != null && !dmpUsersMap.isEmpty() && dmpUsersMap.containsKey(d.getId())) m.setDmpUsers(dmpUsersMap.get(d.getId())); + + models.add(m); + } + this.logger.debug("build {} items", Optional.of(models).map(List::size).orElse(0)); + + return models; + } + + private Map> collectDmpReferences(FieldSet fields, List data) throws MyApplicationException { + if (fields.isEmpty() || data.isEmpty()) return null; + this.logger.debug("checking related - {}", DmpReference.class.getSimpleName()); + + Map> itemMap; + FieldSet clone = new BaseFieldSet(fields.getFields()).ensure(this.asIndexer(DmpReference._dmp, Dmp._id)); + DmpReferenceQuery query = this.queryFactory.query(DmpReferenceQuery.class).authorize(this.authorize).dmpIds(data.stream().map(DmpEntity::getId).distinct().collect(Collectors.toList())); + itemMap = this.builderFactory.builder(DmpReferenceBuilder.class).authorize(this.authorize).asMasterKey(query, clone, x -> x.getDmp().getId()); + + if (!fields.hasField(this.asIndexer(DmpReference._dmp, Dmp._id))) { + itemMap.values().stream().flatMap(List::stream).filter(x -> x != null && x.getDmp() != null).peek(x -> { + x.getDmp().setId(null); + }); + } + + return itemMap; + } + + private Map> collectDmpUsers(FieldSet fields, List data) throws MyApplicationException { + if (fields.isEmpty() || data.isEmpty()) + return null; + this.logger.debug("checking related - {}", DmpUser.class.getSimpleName()); + + Map> itemMap; + FieldSet clone = new BaseFieldSet(fields.getFields()).ensure(this.asIndexer(DmpUser._dmp, Dmp._id)); + DmpUserQuery query = this.queryFactory.query(DmpUserQuery.class).authorize(this.authorize).dmpIds(data.stream().map(DmpEntity::getId).distinct().collect(Collectors.toList())); + itemMap = this.builderFactory.builder(DmpUserBuilder.class).authorize(this.authorize).asMasterKey(query, clone, x -> x.getDmp().getId()); + + if (!fields.hasField(this.asIndexer(DmpUser._dmp, Dmp._id))) { + itemMap.values().stream().flatMap(List::stream).filter(x -> x != null && x.getDmp() != null).peek(x -> { + x.getDmp().setId(null); + }); + } + + return itemMap; + } + +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/builder/UserContactInfoBuilder.java b/dmp-backend/core/src/main/java/eu/eudat/model/builder/UserContactInfoBuilder.java new file mode 100644 index 000000000..5b36794a2 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/builder/UserContactInfoBuilder.java @@ -0,0 +1,143 @@ +package eu.eudat.model.builder; + +import eu.eudat.authorization.AuthorizationFlags; +import eu.eudat.convention.ConventionService; +import eu.eudat.data.DescriptionTagEntity; +import eu.eudat.model.Description; +import eu.eudat.model.DescriptionTag; +import eu.eudat.model.Tag; +import eu.eudat.query.DescriptionQuery; +import eu.eudat.query.TagQuery; +import gr.cite.tools.data.builder.BuilderFactory; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.exception.MyApplicationException; +import gr.cite.tools.fieldset.BaseFieldSet; +import gr.cite.tools.fieldset.FieldSet; +import gr.cite.tools.logging.DataLogEntry; +import gr.cite.tools.logging.LoggerService; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.stream.Collectors; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class DescriptionTagBuilder extends BaseBuilder { + + private final BuilderFactory builderFactory; + + private final QueryFactory queryFactory; + + private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); + + @Autowired + public DescriptionTagBuilder( + ConventionService conventionService, + BuilderFactory builderFactory, QueryFactory queryFactory) { + super(conventionService, new LoggerService(LoggerFactory.getLogger(DescriptionTagBuilder.class))); + this.builderFactory = builderFactory; + this.queryFactory = queryFactory; + } + + public DescriptionTagBuilder authorize(EnumSet values) { + this.authorize = values; + return this; + } + + @Override + public List build(FieldSet fields, List data) throws MyApplicationException { + this.logger.debug("building for {} items requesting {} fields", Optional.ofNullable(data).map(List::size).orElse(0), Optional.ofNullable(fields).map(FieldSet::getFields).map(Set::size).orElse(0)); + this.logger.trace(new DataLogEntry("requested fields", fields)); + if (fields == null || data == null || fields.isEmpty()) + return new ArrayList<>(); + + FieldSet referenceFields = fields.extractPrefixed(this.asPrefix(DescriptionTag._tag)); + Map referenceItemsMap = this.collectTags(referenceFields, data); + + FieldSet descriptionFields = fields.extractPrefixed(this.asPrefix(DescriptionTag._description)); + Map descriptionItemsMap = this.collectDescriptions(descriptionFields, data); + + List models = new ArrayList<>(); + + for (DescriptionTagEntity d : data) { + DescriptionTag m = new DescriptionTag(); + if (fields.hasField(this.asIndexer(DescriptionTag._id))) m.setId(d.getId()); + if (fields.hasField(this.asIndexer(DescriptionTag._createdAt))) m.setCreatedAt(d.getCreatedAt()); + if (fields.hasField(this.asIndexer(DescriptionTag._updatedAt))) m.setUpdatedAt(d.getUpdatedAt()); + if (fields.hasField(this.asIndexer(DescriptionTag._isActive))) m.setIsActive(d.getIsActive()); + if (!referenceFields.isEmpty() && referenceItemsMap != null && referenceItemsMap.containsKey(d.getTagId())) m.setTag(referenceItemsMap.get(d.getTagId())); + if (!descriptionFields.isEmpty() && descriptionItemsMap != null && descriptionItemsMap.containsKey(d.getDescriptionId())) m.setDescription(descriptionItemsMap.get(d.getDescriptionId())); + + models.add(m); + } + + this.logger.debug("build {} items", Optional.of(models).map(List::size).orElse(0)); + + return models; + } + + private Map collectDescriptions(FieldSet fields, List data) throws MyApplicationException { + if (fields.isEmpty() || data.isEmpty()) + return null; + this.logger.debug("checking related - {}", Description.class.getSimpleName()); + + Map itemMap; + if (!fields.hasOtherField(this.asIndexer(Tag._id))) { + itemMap = this.asEmpty( + data.stream().map(DescriptionTagEntity::getDescriptionId).distinct().collect(Collectors.toList()), + x -> { + Description item = new Description(); + item.setId(x); + return item; + }, + Description::getId); + } else { + FieldSet clone = new BaseFieldSet(fields.getFields()).ensure(Tag._id); + DescriptionQuery q = this.queryFactory.query(DescriptionQuery.class).authorize(this.authorize).ids(data.stream().map(DescriptionTagEntity::getDescriptionId).distinct().collect(Collectors.toList())); + itemMap = this.builderFactory.builder(DescriptionBuilder.class).authorize(this.authorize).asForeignKey(q, clone, Description::getId); + } + if (!fields.hasField(Description._id)) { + itemMap.forEach((id, item) -> { + if (item != null) + item.setId(null); + }); + } + + return itemMap; + } + + private Map collectTags(FieldSet fields, List data) throws MyApplicationException { + if (fields.isEmpty() || data.isEmpty()) + return null; + this.logger.debug("checking related - {}", Tag.class.getSimpleName()); + + Map itemMap; + if (!fields.hasOtherField(this.asIndexer(Tag._id))) { + itemMap = this.asEmpty( + data.stream().map(DescriptionTagEntity::getTagId).distinct().collect(Collectors.toList()), + x -> { + Tag item = new Tag(); + item.setId(x); + return item; + }, + Tag::getId); + } else { + FieldSet clone = new BaseFieldSet(fields.getFields()).ensure(Tag._id); + TagQuery q = this.queryFactory.query(TagQuery.class).authorize(this.authorize).ids(data.stream().map(DescriptionTagEntity::getTagId).distinct().collect(Collectors.toList())); + itemMap = this.builderFactory.builder(TagBuilder.class).authorize(this.authorize).asForeignKey(q, clone, Tag::getId); + } + if (!fields.hasField(Tag._id)) { + itemMap.forEach((id, item) -> { + if (item != null) + item.setId(null); + }); + } + + return itemMap; + } + +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/builder/UserCredentialBuilder.java b/dmp-backend/core/src/main/java/eu/eudat/model/builder/UserCredentialBuilder.java new file mode 100644 index 000000000..b6aab0515 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/builder/UserCredentialBuilder.java @@ -0,0 +1,107 @@ +package eu.eudat.model.builder; + +import eu.eudat.authorization.AuthorizationFlags; +import eu.eudat.convention.ConventionService; +import eu.eudat.data.UserCredentialEntity; +import eu.eudat.model.User; +import eu.eudat.model.UserCredential; +import eu.eudat.model.UserCredential; +import eu.eudat.query.UserQuery; +import gr.cite.tools.data.builder.BuilderFactory; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.exception.MyApplicationException; +import gr.cite.tools.fieldset.BaseFieldSet; +import gr.cite.tools.fieldset.FieldSet; +import gr.cite.tools.logging.DataLogEntry; +import gr.cite.tools.logging.LoggerService; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.stream.Collectors; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class UserCredentialBuilder extends BaseBuilder { + + private final BuilderFactory builderFactory; + + private final QueryFactory queryFactory; + + private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); + + @Autowired + public UserCredentialBuilder( + ConventionService conventionService, + BuilderFactory builderFactory, QueryFactory queryFactory) { + super(conventionService, new LoggerService(LoggerFactory.getLogger(UserCredentialBuilder.class))); + this.builderFactory = builderFactory; + this.queryFactory = queryFactory; + } + + public UserCredentialBuilder authorize(EnumSet values) { + this.authorize = values; + return this; + } + + @Override + public List build(FieldSet fields, List data) throws MyApplicationException { + this.logger.debug("building for {} items requesting {} fields", Optional.ofNullable(data).map(List::size).orElse(0), Optional.ofNullable(fields).map(FieldSet::getFields).map(Set::size).orElse(0)); + this.logger.trace(new DataLogEntry("requested fields", fields)); + if (fields == null || data == null || fields.isEmpty()) + return new ArrayList<>(); + + FieldSet userFields = fields.extractPrefixed(this.asPrefix(UserCredential._user)); + Map userItemsMap = this.collectUsers(userFields, data); + + List models = new ArrayList<>(); + + for (UserCredentialEntity d : data) { + UserCredential m = new UserCredential(); + if (fields.hasField(this.asIndexer(UserCredential._id))) m.setId(d.getId()); + if (fields.hasField(this.asIndexer(UserCredential._createdAt))) m.setCreatedAt(d.getCreatedAt()); + if (fields.hasField(this.asIndexer(UserCredential._externalId))) m.setExternalId(d.getExternalId()); + if (!userFields.isEmpty() && userItemsMap != null && userItemsMap.containsKey(d.getUserId())) m.setUser(userItemsMap.get(d.getUserId())); + + models.add(m); + } + + this.logger.debug("build {} items", Optional.of(models).map(List::size).orElse(0)); + + return models; + } + + private Map collectUsers(FieldSet fields, List data) throws MyApplicationException { + if (fields.isEmpty() || data.isEmpty()) + return null; + this.logger.debug("checking related - {}", User.class.getSimpleName()); + + Map itemMap; + if (!fields.hasOtherField(this.asIndexer(User._id))) { + itemMap = this.asEmpty( + data.stream().map(UserCredentialEntity::getUserId).distinct().collect(Collectors.toList()), + x -> { + User item = new User(); + item.setId(x); + return item; + }, + User::getId); + } else { + FieldSet clone = new BaseFieldSet(fields.getFields()).ensure(User._id); + UserQuery q = this.queryFactory.query(UserQuery.class).authorize(this.authorize).ids(data.stream().map(UserCredentialEntity::getUserId).distinct().collect(Collectors.toList())); + itemMap = this.builderFactory.builder(UserBuilder.class).authorize(this.authorize).asForeignKey(q, clone, User::getId); + } + if (!fields.hasField(User._id)) { + itemMap.forEach((id, item) -> { + if (item != null) + item.setId(null); + }); + } + + return itemMap; + } + +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/builder/UserRoleBuilder.java b/dmp-backend/core/src/main/java/eu/eudat/model/builder/UserRoleBuilder.java new file mode 100644 index 000000000..deff56581 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/builder/UserRoleBuilder.java @@ -0,0 +1,111 @@ +package eu.eudat.model.builder; + +import eu.eudat.authorization.AuthorizationFlags; +import eu.eudat.convention.ConventionService; +import eu.eudat.data.UserContactInfoEntity; +import eu.eudat.model.Description; +import eu.eudat.model.User; +import eu.eudat.model.UserContactInfo; +import eu.eudat.model.User; +import eu.eudat.query.DescriptionQuery; +import eu.eudat.query.UserQuery; +import gr.cite.tools.data.builder.BuilderFactory; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.exception.MyApplicationException; +import gr.cite.tools.fieldset.BaseFieldSet; +import gr.cite.tools.fieldset.FieldSet; +import gr.cite.tools.logging.DataLogEntry; +import gr.cite.tools.logging.LoggerService; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.stream.Collectors; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class UserContactInfoBuilder extends BaseBuilder { + + private final BuilderFactory builderFactory; + + private final QueryFactory queryFactory; + + private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); + + @Autowired + public UserContactInfoBuilder( + ConventionService conventionService, + BuilderFactory builderFactory, QueryFactory queryFactory) { + super(conventionService, new LoggerService(LoggerFactory.getLogger(UserContactInfoBuilder.class))); + this.builderFactory = builderFactory; + this.queryFactory = queryFactory; + } + + public UserContactInfoBuilder authorize(EnumSet values) { + this.authorize = values; + return this; + } + + @Override + public List build(FieldSet fields, List data) throws MyApplicationException { + this.logger.debug("building for {} items requesting {} fields", Optional.ofNullable(data).map(List::size).orElse(0), Optional.ofNullable(fields).map(FieldSet::getFields).map(Set::size).orElse(0)); + this.logger.trace(new DataLogEntry("requested fields", fields)); + if (fields == null || data == null || fields.isEmpty()) + return new ArrayList<>(); + + FieldSet userFields = fields.extractPrefixed(this.asPrefix(UserContactInfo._user)); + Map userItemsMap = this.collectUsers(userFields, data); + + List models = new ArrayList<>(); + + for (UserContactInfoEntity d : data) { + UserContactInfo m = new UserContactInfo(); + if (fields.hasField(this.asIndexer(UserContactInfo._id))) m.setId(d.getId()); + if (fields.hasField(this.asIndexer(UserContactInfo._createdAt))) m.setCreatedAt(d.getCreatedAt()); + if (fields.hasField(this.asIndexer(UserContactInfo._ordinal))) m.setOrdinal(d.getOrdinal()); + if (fields.hasField(this.asIndexer(UserContactInfo._value))) m.setValue(d.getValue()); + if (fields.hasField(this.asIndexer(UserContactInfo._type))) m.setType(d.getType()); + if (!userFields.isEmpty() && userItemsMap != null && userItemsMap.containsKey(d.getUserId())) m.setUser(userItemsMap.get(d.getUserId())); + + models.add(m); + } + + this.logger.debug("build {} items", Optional.of(models).map(List::size).orElse(0)); + + return models; + } + + private Map collectUsers(FieldSet fields, List data) throws MyApplicationException { + if (fields.isEmpty() || data.isEmpty()) + return null; + this.logger.debug("checking related - {}", User.class.getSimpleName()); + + Map itemMap; + if (!fields.hasOtherField(this.asIndexer(User._id))) { + itemMap = this.asEmpty( + data.stream().map(UserContactInfoEntity::getUserId).distinct().collect(Collectors.toList()), + x -> { + User item = new User(); + item.setId(x); + return item; + }, + User::getId); + } else { + FieldSet clone = new BaseFieldSet(fields.getFields()).ensure(User._id); + UserQuery q = this.queryFactory.query(UserQuery.class).authorize(this.authorize).ids(data.stream().map(UserContactInfoEntity::getUserId).distinct().collect(Collectors.toList())); + itemMap = this.builderFactory.builder(UserBuilder.class).authorize(this.authorize).asForeignKey(q, clone, User::getId); + } + if (!fields.hasField(User._id)) { + itemMap.forEach((id, item) -> { + if (item != null) + item.setId(null); + }); + } + + return itemMap; + } + +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/deleter/UserCredentialDeleter.java b/dmp-backend/core/src/main/java/eu/eudat/model/deleter/UserCredentialDeleter.java new file mode 100644 index 000000000..ff11615d7 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/deleter/UserCredentialDeleter.java @@ -0,0 +1,68 @@ +package eu.eudat.model.deleter; + +import eu.eudat.data.UserContactInfoEntity; +import eu.eudat.query.UserContactInfoQuery; +import gr.cite.tools.data.deleter.Deleter; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.logging.LoggerService; +import gr.cite.tools.logging.MapLogEntry; +import jakarta.persistence.EntityManager; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import javax.management.InvalidApplicationException; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class UserContactInfoDeleter implements Deleter { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserContactInfoDeleter.class)); + private final EntityManager entityManager; + + protected final QueryFactory queryFactory; + + + @Autowired + public UserContactInfoDeleter( + EntityManager entityManager, + QueryFactory queryFactory + ) { + this.entityManager = entityManager; + this.queryFactory = queryFactory; + } + + public void deleteAndSaveByIds(List ids) throws InvalidApplicationException { + logger.debug(new MapLogEntry("collecting to delete").And("count", Optional.ofNullable(ids).map(List::size).orElse(0)).And("ids", ids)); + List data = this.queryFactory.query(UserContactInfoQuery.class).ids(ids).collect(); + logger.trace("retrieved {} items", Optional.ofNullable(data).map(List::size).orElse(0)); + this.deleteAndSave(data); + } + + public void deleteAndSave(List data) throws InvalidApplicationException { + logger.debug("will delete {} items", Optional.ofNullable(data).map(List::size).orElse(0)); + this.delete(data); + logger.trace("saving changes"); + this.entityManager.flush(); + logger.trace("changes saved"); + } + + public void delete(List data) throws InvalidApplicationException { + logger.debug("will delete {} items", Optional.ofNullable(data).map(List::size).orElse(0)); + if (data == null || data.isEmpty()) + return; + + for (UserContactInfoEntity item : data) { + logger.trace("deleting item {}", item.getId()); + logger.trace("deleting item"); + this.entityManager.remove(item); + logger.trace("deleted item"); + } + } + +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/deleter/UserDeleter.java b/dmp-backend/core/src/main/java/eu/eudat/model/deleter/UserDeleter.java new file mode 100644 index 000000000..55aa92e01 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/deleter/UserDeleter.java @@ -0,0 +1,87 @@ +package eu.eudat.model.deleter; + +import eu.eudat.commons.enums.IsActive; +import eu.eudat.data.DescriptionTagEntity; +import eu.eudat.data.TagEntity; +import eu.eudat.query.DescriptionTagQuery; +import eu.eudat.query.TagQuery; +import gr.cite.tools.data.deleter.Deleter; +import gr.cite.tools.data.deleter.DeleterFactory; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.logging.LoggerService; +import gr.cite.tools.logging.MapLogEntry; +import jakarta.persistence.EntityManager; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import javax.management.InvalidApplicationException; +import java.time.Instant; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class TagDeleter implements Deleter { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TagDeleter.class)); + private final EntityManager entityManager; + + protected final QueryFactory queryFactory; + + protected final DeleterFactory deleterFactory; + + @Autowired + public TagDeleter( + EntityManager entityManager, + QueryFactory queryFactory, + DeleterFactory deleterFactory + ) { + this.entityManager = entityManager; + this.queryFactory = queryFactory; + this.deleterFactory = deleterFactory; + } + + public void deleteAndSaveByIds(List ids) throws InvalidApplicationException { + logger.debug(new MapLogEntry("collecting to delete").And("count", Optional.ofNullable(ids).map(List::size).orElse(0)).And("ids", ids)); + List data = this.queryFactory.query(TagQuery.class).ids(ids).collect(); + logger.trace("retrieved {} items", Optional.ofNullable(data).map(List::size).orElse(0)); + this.deleteAndSave(data); + } + + public void deleteAndSave(List data) throws InvalidApplicationException { + logger.debug("will delete {} items", Optional.ofNullable(data).map(List::size).orElse(0)); + this.delete(data); + logger.trace("saving changes"); + this.entityManager.flush(); + logger.trace("changes saved"); + } + + public void delete(List data) throws InvalidApplicationException { + logger.debug("will delete {} items", Optional.ofNullable(data).map(List::size).orElse(0)); + if (data == null || data.isEmpty()) + return; + List ids = data.stream().map(TagEntity::getId).distinct().collect(Collectors.toList()); + { + logger.debug("checking related - {}", DescriptionTagEntity.class.getSimpleName()); + List items = this.queryFactory.query(DescriptionTagQuery.class).tagIds(ids).collect(); + DescriptionTagDeleter deleter = this.deleterFactory.deleter(DescriptionTagDeleter.class); + deleter.delete(items); + } + Instant now = Instant.now(); + + for (TagEntity item : data) { + logger.trace("deleting item {}", item.getId()); + item.setIsActive(IsActive.Inactive); + item.setUpdatedAt(now); + logger.trace("updating item"); + this.entityManager.merge(item); + logger.trace("updated item"); + } + } + +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/deleter/UserRoleDeleter.java b/dmp-backend/core/src/main/java/eu/eudat/model/deleter/UserRoleDeleter.java new file mode 100644 index 000000000..f3d48b989 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/deleter/UserRoleDeleter.java @@ -0,0 +1,89 @@ +package eu.eudat.model.deleter; + +import eu.eudat.commons.enums.IsActive; +import eu.eudat.data.DescriptionUserEntity; +import eu.eudat.data.UserEntity; +import eu.eudat.data.UserRoleEntity; +import eu.eudat.query.DescriptionUserQuery; +import eu.eudat.query.UserQuery; +import eu.eudat.query.UserRoleQuery; +import gr.cite.tools.data.deleter.Deleter; +import gr.cite.tools.data.deleter.DeleterFactory; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.logging.LoggerService; +import gr.cite.tools.logging.MapLogEntry; +import jakarta.persistence.EntityManager; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import javax.management.InvalidApplicationException; +import java.time.Instant; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class UserDeleter implements Deleter { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserDeleter.class)); + private final EntityManager entityManager; + + protected final QueryFactory queryFactory; + + protected final DeleterFactory deleterFactory; + + @Autowired + public UserDeleter( + EntityManager entityManager, + QueryFactory queryFactory, + DeleterFactory deleterFactory + ) { + this.entityManager = entityManager; + this.queryFactory = queryFactory; + this.deleterFactory = deleterFactory; + } + + public void deleteAndSaveByIds(List ids) throws InvalidApplicationException { + logger.debug(new MapLogEntry("collecting to delete").And("count", Optional.ofNullable(ids).map(List::size).orElse(0)).And("ids", ids)); + List data = this.queryFactory.query(UserQuery.class).ids(ids).collect(); + logger.trace("retrieved {} items", Optional.ofNullable(data).map(List::size).orElse(0)); + this.deleteAndSave(data); + } + + public void deleteAndSave(List data) throws InvalidApplicationException { + logger.debug("will delete {} items", Optional.ofNullable(data).map(List::size).orElse(0)); + this.delete(data); + logger.trace("saving changes"); + this.entityManager.flush(); + logger.trace("changes saved"); + } + + public void delete(List data) throws InvalidApplicationException { + logger.debug("will delete {} items", Optional.ofNullable(data).map(List::size).orElse(0)); + if (data == null || data.isEmpty()) + return; + List ids = data.stream().map(UserEntity::getId).distinct().collect(Collectors.toList()); + { + logger.debug("checking related - {}", UserRoleEntity.class.getSimpleName()); + List items = this.queryFactory.query(UserRoleQuery.class).userIds(ids).collect(); + UserRoleDeleter deleter = this.deleterFactory.deleter(UserRoleDeleter.class); + deleter.delete(items); + } + Instant now = Instant.now(); + + for (UserEntity item : data) { + logger.trace("deleting item {}", item.getId()); + item.setIsActive(IsActive.Inactive); + item.setUpdatedAt(now); + logger.trace("updating item"); + this.entityManager.merge(item); + logger.trace("updated item"); + } + } + +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/persist/UserAdditionalInfoPersist.java b/dmp-backend/core/src/main/java/eu/eudat/model/persist/UserAdditionalInfoPersist.java new file mode 100644 index 000000000..246057a16 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/UserAdditionalInfoPersist.java @@ -0,0 +1,74 @@ +package eu.eudat.model; + +import eu.eudat.commons.enums.ContactInfoType; + +import java.time.Instant; +import java.util.UUID; + +public class UserAdditionalInfo { + private String avatarUrl; + public static final String _avatarUrl = "avatarUrl"; + + private String timezone; + public static final String _timezone = "timezone"; + + private String culture; + public static final String _culture = "culture"; + + private String language; + public static final String _language = "language"; + + private String roleOrganization; + public static final String _roleOrganization = "roleOrganization"; + + private String organization; + public static final String _organization = "organization"; + + public String getAvatarUrl() { + return avatarUrl; + } + + public void setAvatarUrl(String avatarUrl) { + this.avatarUrl = avatarUrl; + } + + public String getTimezone() { + return timezone; + } + + public void setTimezone(String timezone) { + this.timezone = timezone; + } + + public String getCulture() { + return culture; + } + + public void setCulture(String culture) { + this.culture = culture; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + public String getRoleOrganization() { + return roleOrganization; + } + + public void setRoleOrganization(String roleOrganization) { + this.roleOrganization = roleOrganization; + } + + public String getOrganization() { + return organization; + } + + public void setOrganization(String organization) { + this.organization = organization; + } +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/persist/UserPersist.java b/dmp-backend/core/src/main/java/eu/eudat/model/persist/UserPersist.java new file mode 100644 index 000000000..52aabbac7 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/UserPersist.java @@ -0,0 +1,63 @@ +package eu.eudat.model.persist; + + +import eu.eudat.commons.enums.DescriptionTemplateTypeStatus; +import eu.eudat.commons.validation.FieldNotNullIfOtherSet; +import eu.eudat.commons.validation.ValidEnum; +import eu.eudat.commons.validation.ValidId; + +import eu.eudat.data.DescriptionTemplateTypeEntity; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import java.util.UUID; + +@FieldNotNullIfOtherSet(message = "{validation.hashempty}") +public class DescriptionTemplateTypePersist { + + @ValidId(message = "{validation.invalidid}") + private UUID id; + + @NotNull(message = "{validation.empty}") + @NotEmpty(message = "{validation.empty}") + @Size(max = DescriptionTemplateTypeEntity._nameLength, message = "{validation.largerthanmax}") + private String name = null; + + private String hash; + + @ValidEnum(message = "{validation.empty}") + private DescriptionTemplateTypeStatus status; + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getHash() { + return hash; + } + + public void setHash(String hash) { + this.hash = hash; + } + + public DescriptionTemplateTypeStatus getStatus() { + return status; + } + + public void setStatus(DescriptionTemplateTypeStatus status) { + this.status = status; + } +} + diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/persist/UserRolePatchPersist.java b/dmp-backend/core/src/main/java/eu/eudat/model/persist/UserRolePatchPersist.java new file mode 100644 index 000000000..7f37c9865 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/UserRolePatchPersist.java @@ -0,0 +1,74 @@ +package eu.eudat.model.persist; + + +import eu.eudat.commons.enums.DescriptionTemplateTypeStatus; +import eu.eudat.commons.enums.IsActive; +import eu.eudat.commons.validation.FieldNotNullIfOtherSet; +import eu.eudat.commons.validation.ValidEnum; +import eu.eudat.commons.validation.ValidId; +import eu.eudat.data.DescriptionEntity; +import eu.eudat.data.DescriptionTemplateTypeEntity; +import eu.eudat.data.UserEntity; +import eu.eudat.model.UserAdditionalInfo; +import eu.eudat.model.UserContactInfo; +import eu.eudat.model.UserCredential; +import eu.eudat.model.UserRole; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +import java.time.Instant; +import java.util.List; +import java.util.UUID; + +@FieldNotNullIfOtherSet(message = "{validation.hashempty}") +public class UserPersist { + + @ValidId(message = "{validation.invalidid}") + private UUID id; + + + @NotNull(message = "{validation.empty}") + @NotEmpty(message = "{validation.empty}") + @Size(max = UserEntity._nameLength, message = "{validation.largerthanmax}") + private String name; + + private String hash; + + @Valid + private UserAdditionalInfoPersist additionalInfo; + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getHash() { + return hash; + } + + public void setHash(String hash) { + this.hash = hash; + } + + public UserAdditionalInfoPersist getAdditionalInfo() { + return additionalInfo; + } + + public void setAdditionalInfo(UserAdditionalInfoPersist additionalInfo) { + this.additionalInfo = additionalInfo; + } +} + diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/user/UserService.java b/dmp-backend/core/src/main/java/eu/eudat/service/user/UserService.java new file mode 100644 index 000000000..6746ff861 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/service/user/UserService.java @@ -0,0 +1,20 @@ +package eu.eudat.service.descriptiontemplatetype; + +import eu.eudat.model.DescriptionTemplateType; +import eu.eudat.model.persist.DescriptionTemplateTypePersist; +import gr.cite.tools.exception.MyApplicationException; +import gr.cite.tools.exception.MyForbiddenException; +import gr.cite.tools.exception.MyNotFoundException; +import gr.cite.tools.exception.MyValidationException; +import gr.cite.tools.fieldset.FieldSet; + +import javax.management.InvalidApplicationException; +import java.util.UUID; + +public interface DescriptionTemplateTypeService { + + DescriptionTemplateType persist(DescriptionTemplateTypePersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException; + + void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException; + +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/user/UserServiceImpl.java b/dmp-backend/core/src/main/java/eu/eudat/service/user/UserServiceImpl.java new file mode 100644 index 000000000..64b798b42 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/service/user/UserServiceImpl.java @@ -0,0 +1,121 @@ +package eu.eudat.service.user; + +import eu.eudat.authorization.AuthorizationFlags; +import eu.eudat.authorization.Permission; +import eu.eudat.commons.enums.IsActive; +import eu.eudat.convention.ConventionService; +import eu.eudat.data.DescriptionTemplateTypeEntity; +import eu.eudat.errorcode.ErrorThesaurusProperties; +import eu.eudat.event.DescriptionTemplateTypeTouchedEvent; +import eu.eudat.event.EventBroker; +import eu.eudat.model.DescriptionTemplateType; +import eu.eudat.model.builder.DescriptionTemplateTypeBuilder; +import eu.eudat.model.deleter.DescriptionTemplateTypeDeleter; +import eu.eudat.model.persist.DescriptionTemplateTypePersist; +import gr.cite.commons.web.authz.service.AuthorizationService; +import gr.cite.tools.data.builder.BuilderFactory; +import gr.cite.tools.data.deleter.DeleterFactory; +import gr.cite.tools.exception.MyApplicationException; +import gr.cite.tools.exception.MyForbiddenException; +import gr.cite.tools.exception.MyNotFoundException; +import gr.cite.tools.exception.MyValidationException; +import gr.cite.tools.fieldset.BaseFieldSet; +import gr.cite.tools.fieldset.FieldSet; +import gr.cite.tools.logging.LoggerService; +import gr.cite.tools.logging.MapLogEntry; +import jakarta.persistence.EntityManager; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.stereotype.Service; + +import javax.management.InvalidApplicationException; +import java.time.Instant; +import java.util.List; +import java.util.UUID; + +@Service +public class DescriptionTemplateTypeServiceImpl implements UserService { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(DescriptionTemplateTypeServiceImpl.class)); + + private final EntityManager entityManager; + + private final AuthorizationService authorizationService; + + private final DeleterFactory deleterFactory; + + private final BuilderFactory builderFactory; + + private final ConventionService conventionService; + + private final ErrorThesaurusProperties errors; + + private final MessageSource messageSource; + + private final EventBroker eventBroker; + + @Autowired + public DescriptionTemplateTypeServiceImpl( + EntityManager entityManager, + AuthorizationService authorizationService, + DeleterFactory deleterFactory, + BuilderFactory builderFactory, + ConventionService conventionService, + ErrorThesaurusProperties errors, + MessageSource messageSource, + EventBroker eventBroker) { + this.entityManager = entityManager; + this.authorizationService = authorizationService; + this.deleterFactory = deleterFactory; + this.builderFactory = builderFactory; + this.conventionService = conventionService; + this.errors = errors; + this.messageSource = messageSource; + this.eventBroker = eventBroker; + } + + public DescriptionTemplateType persist(DescriptionTemplateTypePersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException { + logger.debug(new MapLogEntry("persisting data descriptionTemplateType").And("model", model).And("fields", fields)); + + this.authorizationService.authorizeForce(Permission.EditDescriptionTemplateType); + + Boolean isUpdate = this.conventionService.isValidGuid(model.getId()); + + DescriptionTemplateTypeEntity data; + if (isUpdate) { + data = this.entityManager.find(DescriptionTemplateTypeEntity.class, model.getId()); + if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), DescriptionTemplateType.class.getSimpleName()}, LocaleContextHolder.getLocale())); + if (!this.conventionService.hashValue(data.getUpdatedAt()).equals(model.getHash())) throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage()); + } else { + data = new DescriptionTemplateTypeEntity(); + data.setId(UUID.randomUUID()); + data.setIsActive(IsActive.Active); + data.setCreatedAt(Instant.now()); + } + + data.setName(model.getName()); + data.setStatus(model.getStatus()); + data.setUpdatedAt(Instant.now()); + if (isUpdate) + this.entityManager.merge(data); + else + this.entityManager.persist(data); + + this.entityManager.flush(); + + this.eventBroker.emit(new DescriptionTemplateTypeTouchedEvent(data.getId())); + return this.builderFactory.builder(DescriptionTemplateTypeBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fields, DescriptionTemplateType._id), data); + } + + public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException { + logger.debug("deleting descriptionTemplateType: {}", id); + + this.authorizationService.authorizeForce(Permission.DeleteDescriptionTemplateType); + + this.deleterFactory.deleter(DescriptionTemplateTypeDeleter.class).deleteAndSaveByIds(List.of(id)); + } + +} + diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/UserController.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/UserController.java new file mode 100644 index 000000000..ad2576339 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/UserController.java @@ -0,0 +1,217 @@ +package eu.eudat.controllers.v2; + +import com.fasterxml.jackson.core.JsonProcessingException; +import eu.eudat.audit.AuditableAction; +import eu.eudat.authorization.AuthorizationFlags; +import eu.eudat.data.DescriptionTemplateEntity; +import eu.eudat.model.DescriptionTemplate; +import eu.eudat.model.DmpBlueprint; +import eu.eudat.model.builder.DescriptionTemplateBuilder; +import eu.eudat.model.censorship.DescriptionTemplateCensor; +import eu.eudat.model.censorship.DmpBlueprintCensor; +import eu.eudat.model.persist.DescriptionTemplatePersist; +import eu.eudat.model.persist.NewVersionDescriptionTemplatePersist; +import eu.eudat.model.result.QueryResult; +import eu.eudat.query.DescriptionTemplateQuery; +import eu.eudat.query.lookup.DescriptionTemplateLookup; +import eu.eudat.service.descriptiontemplate.DescriptionTemplateService; +import gr.cite.tools.auditing.AuditService; +import gr.cite.tools.data.builder.BuilderFactory; +import gr.cite.tools.data.censor.CensorFactory; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.exception.MyApplicationException; +import gr.cite.tools.exception.MyForbiddenException; +import gr.cite.tools.exception.MyNotFoundException; +import gr.cite.tools.fieldset.FieldSet; +import gr.cite.tools.logging.LoggerService; +import gr.cite.tools.logging.MapLogEntry; +import gr.cite.tools.validation.MyValidate; +import jakarta.xml.bind.JAXBException; +import org.slf4j.LoggerFactory; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; +import org.xml.sax.SAXException; + +import javax.management.InvalidApplicationException; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; +import java.io.IOException; +import java.util.*; + +@RestController +@RequestMapping(path = "api/description-template") +public class DescriptionTemplateController { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(DescriptionTemplateController.class)); + + private final BuilderFactory builderFactory; + + private final AuditService auditService; + + private final DescriptionTemplateService descriptionTemplateTypeService; + + private final CensorFactory censorFactory; + + private final QueryFactory queryFactory; + + private final MessageSource messageSource; + + public DescriptionTemplateController( + BuilderFactory builderFactory, + AuditService auditService, + DescriptionTemplateService descriptionTemplateTypeService, + CensorFactory censorFactory, + QueryFactory queryFactory, + MessageSource messageSource) { + this.builderFactory = builderFactory; + this.auditService = auditService; + this.descriptionTemplateTypeService = descriptionTemplateTypeService; + this.censorFactory = censorFactory; + this.queryFactory = queryFactory; + this.messageSource = messageSource; + } + + @PostMapping("query") + public QueryResult query(@RequestBody DescriptionTemplateLookup lookup) throws MyApplicationException, MyForbiddenException { + logger.debug("querying {}", DescriptionTemplate.class.getSimpleName()); + + this.censorFactory.censor(DescriptionTemplateCensor.class).censor(lookup.getProject(), null); + + DescriptionTemplateQuery query = lookup.enrich(this.queryFactory).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic); + + List data = query.collectAs(lookup.getProject()); + List models = this.builderFactory.builder(DescriptionTemplateBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(lookup.getProject(), data); + long count = (lookup.getMetadata() != null && lookup.getMetadata().getCountAll()) ? query.count() : models.size(); + + this.auditService.track(AuditableAction.DescriptionTemplate_Query, "lookup", lookup); + //this.auditService.trackIdentity(AuditableAction.IdentityTracking_Action); + + return new QueryResult<>(models, count); + } + + @GetMapping("{id}") + public DescriptionTemplate get(@PathVariable("id") UUID id, FieldSet fieldSet, Locale locale) throws MyApplicationException, MyForbiddenException, MyNotFoundException { + logger.debug(new MapLogEntry("retrieving" + DescriptionTemplate.class.getSimpleName()).And("id", id).And("fields", fieldSet)); + + this.censorFactory.censor(DescriptionTemplateCensor.class).censor(fieldSet, null); + + DescriptionTemplateQuery query = this.queryFactory.query(DescriptionTemplateQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).ids(id); + DescriptionTemplate model = this.builderFactory.builder(DescriptionTemplateBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(fieldSet, query.firstAs(fieldSet)); + if (model == null) + throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, DescriptionTemplate.class.getSimpleName()}, LocaleContextHolder.getLocale())); + + this.auditService.track(AuditableAction.DescriptionTemplate_Lookup, Map.ofEntries( + new AbstractMap.SimpleEntry("id", id), + new AbstractMap.SimpleEntry("fields", fieldSet) + )); + //this.auditService.trackIdentity(AuditableAction.IdentityTracking_Action); + + return model; + } + + @PostMapping("persist") + @Transactional + public DescriptionTemplate persist(@MyValidate @RequestBody DescriptionTemplatePersist model, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException, JAXBException, ParserConfigurationException, JsonProcessingException, TransformerException { + logger.debug(new MapLogEntry("persisting" + DescriptionTemplate.class.getSimpleName()).And("model", model).And("fieldSet", fieldSet)); + DescriptionTemplate persisted = this.descriptionTemplateTypeService.persist(model, fieldSet); + + this.auditService.track(AuditableAction.DescriptionTemplate_Persist, Map.ofEntries( + new AbstractMap.SimpleEntry("model", model), + new AbstractMap.SimpleEntry("fields", fieldSet) + )); + //this.auditService.trackIdentity(AuditableAction.IdentityTracking_Action); + return persisted; + } + + @DeleteMapping("{id}") + @Transactional + public void delete(@PathVariable("id") UUID id) throws MyForbiddenException, InvalidApplicationException { + logger.debug(new MapLogEntry("retrieving" + DescriptionTemplate.class.getSimpleName()).And("id", id)); + + this.descriptionTemplateTypeService.deleteAndSave(id); + + this.auditService.track(AuditableAction.DescriptionTemplate_Delete, "id", id); + //this.auditService.trackIdentity(AuditableAction.IdentityTracking_Action); + } + + @GetMapping("clone/{id}") + public DescriptionTemplate buildClone(@PathVariable("id") UUID id, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException { + logger.debug(new MapLogEntry("clone" + DmpBlueprint.class.getSimpleName()).And("id", id).And("fields", fieldSet)); + + this.censorFactory.censor(DmpBlueprintCensor.class).censor(fieldSet, null); + + DescriptionTemplate model = this.descriptionTemplateTypeService.buildClone(id, fieldSet); + + this.auditService.track(AuditableAction.DescriptionTemplate_Clone, Map.ofEntries( + new AbstractMap.SimpleEntry("id", id), + new AbstractMap.SimpleEntry("fields", fieldSet) + )); + //this.auditService.trackIdentity(AuditableAction.IdentityTracking_Action); + + return model; + } + + @PostMapping("new-version") + @Transactional + public DescriptionTemplate createNewVersion(@MyValidate @RequestBody NewVersionDescriptionTemplatePersist model, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException, JAXBException, ParserConfigurationException, JsonProcessingException, TransformerException { + logger.debug(new MapLogEntry("persisting" + NewVersionDescriptionTemplatePersist.class.getSimpleName()).And("model", model).And("fieldSet", fieldSet)); + DescriptionTemplate persisted = this.descriptionTemplateTypeService.createNewVersion(model, fieldSet); + + this.auditService.track(AuditableAction.DescriptionTemplate_PersistNewVersion, Map.ofEntries( + new AbstractMap.SimpleEntry("model", model), + new AbstractMap.SimpleEntry("fields", fieldSet) + )); + //this.auditService.trackIdentity(AuditableAction.IdentityTracking_Action); + return persisted; + } + + + + @RequestMapping(method = RequestMethod.GET, value = {"/xml/export/{id}"}, produces = "application/xml") + public @ResponseBody ResponseEntity getXml(@PathVariable UUID id) throws JAXBException, ParserConfigurationException, IOException, TransformerException, InstantiationException, IllegalAccessException, SAXException, InvalidApplicationException { + logger.debug(new MapLogEntry("export" + DescriptionTemplate.class.getSimpleName()).And("id", id)); + + ResponseEntity response = this.descriptionTemplateTypeService.exportXml(id); + + this.auditService.track(AuditableAction.DescriptionTemplate_GetXml, Map.ofEntries( + new AbstractMap.SimpleEntry("id", id) + )); + return response; + } + + @RequestMapping(method = RequestMethod.POST, value = {"/xml/import/{id}", "/xml/import"}) + public DescriptionTemplate importXml(@RequestParam("file") MultipartFile file, @PathVariable(value = "id", required = false) UUID id, FieldSet fieldSet) throws IOException, JAXBException, InvalidApplicationException, ParserConfigurationException, TransformerException, InstantiationException, IllegalAccessException, SAXException { + logger.debug(new MapLogEntry("import" + DescriptionTemplate.class.getSimpleName()).And("file", file).And("id", id)); + + this.censorFactory.censor(DescriptionTemplateCensor.class).censor(fieldSet, null); + + DescriptionTemplate model = this.descriptionTemplateTypeService.importXml(file.getBytes(), id, file.getOriginalFilename(), fieldSet); + + this.auditService.track(AuditableAction.DescriptionTemplate_Import, Map.ofEntries( + new AbstractMap.SimpleEntry("file", file), + new AbstractMap.SimpleEntry("id", id), + new AbstractMap.SimpleEntry("fields", fieldSet) + )); + //this.auditService.trackIdentity(AuditableAction.IdentityTracking_Action); + + return model; + } + + @RequestMapping(method = RequestMethod.GET, value = {"/get-semantics"}, produces = "application/json") + public List getSemantics(@RequestParam(value = "query", required = false) String query) throws IOException { + logger.debug(new MapLogEntry("import" + DescriptionTemplate.class.getSimpleName()).And("query", query)); + + List semantics = this.descriptionTemplateTypeService.getSemantics(query); + + this.auditService.track(AuditableAction.DescriptionTemplate_GetSemantics, Map.ofEntries( + new AbstractMap.SimpleEntry("query", query) + )); + //this.auditService.trackIdentity(AuditableAction.IdentityTracking_Action); + + return semantics; + } +} diff --git a/dmp-db-scema/updates/00.01.014_sync_Description.sql b/dmp-db-scema/updates/00.01.014_sync_Description.sql new file mode 100644 index 000000000..1a6519e2a --- /dev/null +++ b/dmp-db-scema/updates/00.01.014_sync_Description.sql @@ -0,0 +1,43 @@ +DO $$DECLARE + this_version CONSTANT varchar := '00.01.014'; +BEGIN + PERFORM * FROM "DBVersion" WHERE version = this_version; + IF FOUND THEN RETURN; END IF; + + ALTER TABLE public."Description" DROP CONSTRAINT "Description_profile_fkey"; + + ALTER TABLE public."Description" DROP COLUMN uri; + + ALTER TABLE public."Description" DROP COLUMN profile; + + ALTER TABLE public."Description" DROP COLUMN reference; + + ALTER TABLE public."Description" DROP COLUMN dmp_section_index; + + ALTER TABLE public."Description" + ALTER COLUMN dmp SET NOT NULL; + + ALTER TABLE public."Description" + ADD COLUMN created_by uuid NOT NULL; + + ALTER TABLE public."Description" + ADD COLUMN dmp_description_template uuid NOT NULL; + + ALTER TABLE public."Description" + ADD FOREIGN KEY (created_by) + REFERENCES public."UserInfo" (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION + NOT VALID; + + ALTER TABLE public."Description" + ADD FOREIGN KEY (dmp_description_template) + REFERENCES public."DmpDescriptionTemplate" (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION + NOT VALID; + + +INSERT INTO public."DBVersion" VALUES ('DMPDB', '00.01.014', '2023-11-20 12:00:00.000000+02', now(), 'Sync table Description.'); + +END$$; \ No newline at end of file diff --git a/dmp-db-scema/updates/00.01.015_addTag.sql b/dmp-db-scema/updates/00.01.015_addTag.sql new file mode 100644 index 000000000..59aeeaa3f --- /dev/null +++ b/dmp-db-scema/updates/00.01.015_addTag.sql @@ -0,0 +1,29 @@ + +DO $$DECLARE + this_version CONSTANT varchar := '00.01.015'; +BEGIN + PERFORM * FROM "DBVersion" WHERE version = this_version; + IF FOUND THEN RETURN; END IF; + CREATE TABLE public."Tag" + ( + id uuid NOT NULL, + label character varying(250) COLLATE pg_catalog."default" NOT NULL, + created_by uuid, + is_active smallint NOT NULL, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL, + CONSTRAINT "Tag_pkey" PRIMARY KEY (id), + CONSTRAINT "Tag_created_by_fkey" FOREIGN KEY (created_by) + REFERENCES public."User" (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION + NOT VALID + ) + WITH ( + OIDS = FALSE + ) + TABLESPACE pg_default; + +INSERT INTO public."DBVersion" VALUES ('DMPDB', '00.01.015', '2023-11-20 12:00:00.000000+02', now(), 'Add table tag.'); + +END$$; \ No newline at end of file diff --git a/dmp-db-scema/updates/00.01.016_addDescriptionTag.sql b/dmp-db-scema/updates/00.01.016_addDescriptionTag.sql new file mode 100644 index 000000000..91079b027 --- /dev/null +++ b/dmp-db-scema/updates/00.01.016_addDescriptionTag.sql @@ -0,0 +1,35 @@ + +DO $$DECLARE + this_version CONSTANT varchar := '00.01.016'; +BEGIN + PERFORM * FROM "DBVersion" WHERE version = this_version; + IF FOUND THEN RETURN; END IF; + + CREATE TABLE IF NOT EXISTS public."DescriptionTag" + ( + id uuid NOT NULL, + description uuid NOT NULL, + tag uuid NOT NULL, + is_active smallint NOT NULL, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL, + CONSTRAINT "DescriptionTag_pkey" PRIMARY KEY (id), + CONSTRAINT "DescriptionTag_description_fkey" FOREIGN KEY (description) + REFERENCES public."Description" (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION + NOT VALID, + CONSTRAINT "DescriptionTag_tag_fkey" FOREIGN KEY (tag) + REFERENCES public."Tag" (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION + NOT VALID + ) + WITH ( + OIDS = FALSE + ) + TABLESPACE pg_default; + +INSERT INTO public."DBVersion" VALUES ('DMPDB', '00.01.016', '2023-11-20 12:00:00.000000+02', now(), 'Add table DescriptionTag.'); + +END$$; \ No newline at end of file