diff --git a/amps/ags/pom.xml b/amps/ags/pom.xml index cf4a3386a4..237a98066c 100644 --- a/amps/ags/pom.xml +++ b/amps/ags/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo-amps - 20.38-SNAPSHOT + 20.50-SNAPSHOT diff --git a/amps/ags/rm-automation/pom.xml b/amps/ags/rm-automation/pom.xml index 7318a2532d..2466106d64 100644 --- a/amps/ags/rm-automation/pom.xml +++ b/amps/ags/rm-automation/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-governance-services-community-parent - 20.38-SNAPSHOT + 20.50-SNAPSHOT diff --git a/amps/ags/rm-automation/rm-automation-community-rest-api/pom.xml b/amps/ags/rm-automation/rm-automation-community-rest-api/pom.xml index 609655602e..7f93589e86 100644 --- a/amps/ags/rm-automation/rm-automation-community-rest-api/pom.xml +++ b/amps/ags/rm-automation/rm-automation-community-rest-api/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-governance-services-automation-community-repo - 20.38-SNAPSHOT + 20.50-SNAPSHOT @@ -42,18 +42,6 @@ - - org.apache.logging.log4j - log4j-slf4j2-impl - ${dependency.log4j.version} - test - - - org.apache.logging.log4j - log4j-core - ${dependency.log4j.version} - test - org.alfresco.tas restapi @@ -69,17 +57,6 @@ org.alfresco.tas utility ${dependency.tas-utility.version} - - - - ch.qos.reload4j - reload4j - - - org.slf4j - slf4j-reload4j - - org.projectlombok diff --git a/amps/ags/rm-community/pom.xml b/amps/ags/rm-community/pom.xml index e9192e384f..9e977acdb1 100644 --- a/amps/ags/rm-community/pom.xml +++ b/amps/ags/rm-community/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-governance-services-community-parent - 20.38-SNAPSHOT + 20.50-SNAPSHOT diff --git a/amps/ags/rm-community/rm-community-repo/.env b/amps/ags/rm-community/rm-community-repo/.env index fc37bc79cc..9c612ce1fb 100644 --- a/amps/ags/rm-community/rm-community-repo/.env +++ b/amps/ags/rm-community/rm-community-repo/.env @@ -1,3 +1,3 @@ -SOLR6_TAG=2.0.6-A4 +SOLR6_TAG=2.0.6 POSTGRES_TAG=14.4 ACTIVEMQ_TAG=5.17.1-jre11-rockylinux8 diff --git a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/security/rm-method-security.properties b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/security/rm-method-security.properties index 8c9bd9a7e8..60fe652775 100644 --- a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/security/rm-method-security.properties +++ b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/security/rm-method-security.properties @@ -47,6 +47,7 @@ rm.methodsecurity.org.alfresco.service.cmr.repository.NodeService.getPaths=RM.Re rm.methodsecurity.org.alfresco.service.cmr.repository.NodeService.getStoreArchiveNode=RM_ABSTAIN rm.methodsecurity.org.alfresco.service.cmr.repository.NodeService.restoreNode=RM_ABSTAIN rm.methodsecurity.org.alfresco.service.cmr.repository.NodeService.getChildAssocsWithoutParentAssocsOfType=RM_ABSTAIN +rm.methodsecurity.org.alfresco.service.cmr.repository.NodeService.findAssocsNotLinkedByTwoOtherAssocs=RM_ABSTAIN rm.methodsecurity.org.alfresco.service.cmr.repository.NodeService.getNodeRef=RM.Read.0 rm.methodsecurity.org.alfresco.service.cmr.repository.NodeService.getChildAssocsByPropertyValue=RM.Read.0,AFTER_RM.FilterNode rm.methodsecurity.org.alfresco.service.cmr.repository.NodeService.countChildAssocs=RM.Read.0 diff --git a/amps/ags/rm-community/rm-community-repo/pom.xml b/amps/ags/rm-community/rm-community-repo/pom.xml index ebf65c7b3f..526e69f26c 100644 --- a/amps/ags/rm-community/rm-community-repo/pom.xml +++ b/amps/ags/rm-community/rm-community-repo/pom.xml @@ -8,7 +8,7 @@ org.alfresco alfresco-governance-services-community-repo-parent - 20.38-SNAPSHOT + 20.50-SNAPSHOT diff --git a/amps/ags/rm-community/rm-community-rest-api-explorer/pom.xml b/amps/ags/rm-community/rm-community-rest-api-explorer/pom.xml index d70eac8adc..75f33cfa4d 100644 --- a/amps/ags/rm-community/rm-community-rest-api-explorer/pom.xml +++ b/amps/ags/rm-community/rm-community-rest-api-explorer/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-governance-services-community-repo-parent - 20.38-SNAPSHOT + 20.50-SNAPSHOT diff --git a/amps/pom.xml b/amps/pom.xml index 8a04186766..ecddb5031a 100644 --- a/amps/pom.xml +++ b/amps/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo - 20.38-SNAPSHOT + 20.50-SNAPSHOT diff --git a/amps/share-services/pom.xml b/amps/share-services/pom.xml index 9af36da4ba..102b74653a 100644 --- a/amps/share-services/pom.xml +++ b/amps/share-services/pom.xml @@ -8,7 +8,7 @@ org.alfresco alfresco-community-repo-amps - 20.38-SNAPSHOT + 20.50-SNAPSHOT diff --git a/core/pom.xml b/core/pom.xml index c3f86f82be..bfb5beb7d2 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo - 20.38-SNAPSHOT + 20.50-SNAPSHOT diff --git a/data-model/pom.xml b/data-model/pom.xml index d1ab881e19..6021f15d93 100644 --- a/data-model/pom.xml +++ b/data-model/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo - 20.38-SNAPSHOT + 20.50-SNAPSHOT diff --git a/data-model/src/main/java/org/alfresco/service/cmr/repository/NodeService.java b/data-model/src/main/java/org/alfresco/service/cmr/repository/NodeService.java index 449b4dad9c..b2d59e4935 100644 --- a/data-model/src/main/java/org/alfresco/service/cmr/repository/NodeService.java +++ b/data-model/src/main/java/org/alfresco/service/cmr/repository/NodeService.java @@ -2,7 +2,7 @@ * #%L * Alfresco Data model classes * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2022 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -714,6 +714,18 @@ public interface NodeService final NodeRef parent, final QName assocTypeQName); + /** + * Gets the list of the localnames of the child associations without parent node + * + * @param parent + * the parent node reference + * @return a list of the local names of the child associations + */ + @Auditable(parameters = {"parent"}) + public List findAssocsNotLinkedByTwoOtherAssocs( + final NodeRef parent); + + /** * Create a peer association between two nodes. *

diff --git a/mmt/pom.xml b/mmt/pom.xml index 2db4e39189..0729623636 100644 --- a/mmt/pom.xml +++ b/mmt/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo - 20.38-SNAPSHOT + 20.50-SNAPSHOT diff --git a/packaging/distribution/pom.xml b/packaging/distribution/pom.xml index 2ff114b42c..f6feafbdd2 100644 --- a/packaging/distribution/pom.xml +++ b/packaging/distribution/pom.xml @@ -9,6 +9,6 @@ org.alfresco alfresco-community-repo-packaging - 20.38-SNAPSHOT + 20.50-SNAPSHOT diff --git a/packaging/docker-alfresco/pom.xml b/packaging/docker-alfresco/pom.xml index 3b738c9057..6a12975b46 100644 --- a/packaging/docker-alfresco/pom.xml +++ b/packaging/docker-alfresco/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo-packaging - 20.38-SNAPSHOT + 20.50-SNAPSHOT diff --git a/packaging/pom.xml b/packaging/pom.xml index 6bc332b0fd..f154ed8aea 100644 --- a/packaging/pom.xml +++ b/packaging/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo - 20.38-SNAPSHOT + 20.50-SNAPSHOT diff --git a/packaging/tests/environment/.env b/packaging/tests/environment/.env index fc37bc79cc..9c612ce1fb 100644 --- a/packaging/tests/environment/.env +++ b/packaging/tests/environment/.env @@ -1,3 +1,3 @@ -SOLR6_TAG=2.0.6-A4 +SOLR6_TAG=2.0.6 POSTGRES_TAG=14.4 ACTIVEMQ_TAG=5.17.1-jre11-rockylinux8 diff --git a/packaging/tests/pom.xml b/packaging/tests/pom.xml index 4069327bff..72108b5fd4 100644 --- a/packaging/tests/pom.xml +++ b/packaging/tests/pom.xml @@ -6,7 +6,7 @@ org.alfresco alfresco-community-repo-packaging - 20.38-SNAPSHOT + 20.50-SNAPSHOT diff --git a/packaging/tests/scripts/output_tests_run.sh b/packaging/tests/scripts/output_tests_run.sh index ca1c9355c4..cd7c630cbf 100755 --- a/packaging/tests/scripts/output_tests_run.sh +++ b/packaging/tests/scripts/output_tests_run.sh @@ -4,4 +4,4 @@ TAS_DIRECTORY=$1 cd ${TAS_DIRECTORY} -cat target/reports/alfresco-tas.log | grep "*** STARTING" +cat target/reports/alfresco-tas.log | grep -a "*** STARTING" diff --git a/packaging/tests/tas-cmis/pom.xml b/packaging/tests/tas-cmis/pom.xml index 113072ab83..e649f22bca 100644 --- a/packaging/tests/tas-cmis/pom.xml +++ b/packaging/tests/tas-cmis/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo-tests - 20.38-SNAPSHOT + 20.50-SNAPSHOT diff --git a/packaging/tests/tas-cmis/src/main/resources/log4j.properties b/packaging/tests/tas-cmis/src/main/resources/log4j.properties deleted file mode 100644 index 00e9b5a114..0000000000 --- a/packaging/tests/tas-cmis/src/main/resources/log4j.properties +++ /dev/null @@ -1,26 +0,0 @@ -# Root logger option -log4j.rootLogger=INFO, file, stdout - -# Direct log messages to a log file -log4j.appender.file=org.apache.log4j.RollingFileAppender -log4j.appender.file.File=./target/reports/alfresco-tas.log -log4j.appender.file.MaxBackupIndex=10 -log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n - -# Direct log messages to stdout -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target=System.out -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n - -# TestRail particular log file -# Direct log messages to a log file -log4j.appender.testrailLog=org.apache.log4j.RollingFileAppender -log4j.appender.testrailLog.File=./target/reports/alfresco-testrail.log -log4j.appender.testrailLog.MaxBackupIndex=10 -log4j.appender.testrailLog.layout=org.apache.log4j.PatternLayout -log4j.appender.testrailLog.layout.ConversionPattern=%d{HH:mm:ss} %-5p %c{1}:%L - %m%n - -log4j.category.testrail=INFO, testrailLog -log4j.additivity.testrail=false \ No newline at end of file diff --git a/packaging/tests/tas-cmis/src/main/resources/log4j2.properties b/packaging/tests/tas-cmis/src/main/resources/log4j2.properties new file mode 100644 index 0000000000..5e34814028 --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/resources/log4j2.properties @@ -0,0 +1,42 @@ +# Root logger option +rootLogger.level=info +rootLogger.appenderRef.stdout.ref=ConsoleAppender +rootLogger.appenderRef.rolling.ref=RollingAppender + +###### File appender definition ####### +appender.rolling.type=RollingFile +appender.rolling.name=RollingAppender +appender.rolling.fileName=./target/reports/alfresco-tas.log +appender.rolling.filePattern=./target/reports/alfresco-tas.log.%i +appender.rolling.layout.type=PatternLayout +appender.rolling.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n +appender.rolling.policies.type = Policies +appender.rolling.policies.size.type=SizeBasedTriggeringPolicy +appender.rolling.policies.size.size=10MB +appender.rolling.strategy.type=DefaultRolloverStrategy +appender.rolling.strategy.max=10 + +###### Console appender definition ####### +appender.console.type=Console +appender.console.name=ConsoleAppender +appender.console.layout.type=PatternLayout +appender.console.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n + +# TestRail particular log file +# Direct log messages to a log file +logger.testrail.name=testrail +logger.testrail.level=info +logger.testrail.additivity=false +logger.testrail.appenderRef.testrail.ref=TestrailAppender + +appender.testrail.name=TestrailAppender +appender.testrail.type=RollingFile +appender.testrail.fileName=./target/reports/alfresco-testrail.log +appender.testrail.filePattern=./target/reports/alfresco-testrail.log.%i +appender.testrail.layout.type=PatternLayout +appender.testrail.layout.pattern=%d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n +appender.testrail.policies.type=Policies +appender.testrail.policies.size.type=SizeBasedTriggeringPolicy +appender.testrail.policies.size.size=10MB +appender.testrail.strategy.type=DefaultRolloverStrategy +appender.testrail.strategy.max=10 \ No newline at end of file diff --git a/packaging/tests/tas-cmis/src/test/resources/log4j.properties b/packaging/tests/tas-cmis/src/test/resources/log4j.properties deleted file mode 100644 index 00e9b5a114..0000000000 --- a/packaging/tests/tas-cmis/src/test/resources/log4j.properties +++ /dev/null @@ -1,26 +0,0 @@ -# Root logger option -log4j.rootLogger=INFO, file, stdout - -# Direct log messages to a log file -log4j.appender.file=org.apache.log4j.RollingFileAppender -log4j.appender.file.File=./target/reports/alfresco-tas.log -log4j.appender.file.MaxBackupIndex=10 -log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n - -# Direct log messages to stdout -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target=System.out -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n - -# TestRail particular log file -# Direct log messages to a log file -log4j.appender.testrailLog=org.apache.log4j.RollingFileAppender -log4j.appender.testrailLog.File=./target/reports/alfresco-testrail.log -log4j.appender.testrailLog.MaxBackupIndex=10 -log4j.appender.testrailLog.layout=org.apache.log4j.PatternLayout -log4j.appender.testrailLog.layout.ConversionPattern=%d{HH:mm:ss} %-5p %c{1}:%L - %m%n - -log4j.category.testrail=INFO, testrailLog -log4j.additivity.testrail=false \ No newline at end of file diff --git a/packaging/tests/tas-cmis/src/test/resources/log4j2.properties b/packaging/tests/tas-cmis/src/test/resources/log4j2.properties new file mode 100644 index 0000000000..5e34814028 --- /dev/null +++ b/packaging/tests/tas-cmis/src/test/resources/log4j2.properties @@ -0,0 +1,42 @@ +# Root logger option +rootLogger.level=info +rootLogger.appenderRef.stdout.ref=ConsoleAppender +rootLogger.appenderRef.rolling.ref=RollingAppender + +###### File appender definition ####### +appender.rolling.type=RollingFile +appender.rolling.name=RollingAppender +appender.rolling.fileName=./target/reports/alfresco-tas.log +appender.rolling.filePattern=./target/reports/alfresco-tas.log.%i +appender.rolling.layout.type=PatternLayout +appender.rolling.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n +appender.rolling.policies.type = Policies +appender.rolling.policies.size.type=SizeBasedTriggeringPolicy +appender.rolling.policies.size.size=10MB +appender.rolling.strategy.type=DefaultRolloverStrategy +appender.rolling.strategy.max=10 + +###### Console appender definition ####### +appender.console.type=Console +appender.console.name=ConsoleAppender +appender.console.layout.type=PatternLayout +appender.console.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n + +# TestRail particular log file +# Direct log messages to a log file +logger.testrail.name=testrail +logger.testrail.level=info +logger.testrail.additivity=false +logger.testrail.appenderRef.testrail.ref=TestrailAppender + +appender.testrail.name=TestrailAppender +appender.testrail.type=RollingFile +appender.testrail.fileName=./target/reports/alfresco-testrail.log +appender.testrail.filePattern=./target/reports/alfresco-testrail.log.%i +appender.testrail.layout.type=PatternLayout +appender.testrail.layout.pattern=%d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n +appender.testrail.policies.type=Policies +appender.testrail.policies.size.type=SizeBasedTriggeringPolicy +appender.testrail.policies.size.size=10MB +appender.testrail.strategy.type=DefaultRolloverStrategy +appender.testrail.strategy.max=10 \ No newline at end of file diff --git a/packaging/tests/tas-email/pom.xml b/packaging/tests/tas-email/pom.xml index c0a373d1c6..b12506d2db 100644 --- a/packaging/tests/tas-email/pom.xml +++ b/packaging/tests/tas-email/pom.xml @@ -9,7 +9,7 @@ org.alfresco alfresco-community-repo-tests - 20.38-SNAPSHOT + 20.50-SNAPSHOT diff --git a/packaging/tests/tas-email/src/test/resources/log4j.properties b/packaging/tests/tas-email/src/test/resources/log4j.properties deleted file mode 100644 index 00e9b5a114..0000000000 --- a/packaging/tests/tas-email/src/test/resources/log4j.properties +++ /dev/null @@ -1,26 +0,0 @@ -# Root logger option -log4j.rootLogger=INFO, file, stdout - -# Direct log messages to a log file -log4j.appender.file=org.apache.log4j.RollingFileAppender -log4j.appender.file.File=./target/reports/alfresco-tas.log -log4j.appender.file.MaxBackupIndex=10 -log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n - -# Direct log messages to stdout -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target=System.out -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n - -# TestRail particular log file -# Direct log messages to a log file -log4j.appender.testrailLog=org.apache.log4j.RollingFileAppender -log4j.appender.testrailLog.File=./target/reports/alfresco-testrail.log -log4j.appender.testrailLog.MaxBackupIndex=10 -log4j.appender.testrailLog.layout=org.apache.log4j.PatternLayout -log4j.appender.testrailLog.layout.ConversionPattern=%d{HH:mm:ss} %-5p %c{1}:%L - %m%n - -log4j.category.testrail=INFO, testrailLog -log4j.additivity.testrail=false \ No newline at end of file diff --git a/packaging/tests/tas-email/src/test/resources/log4j2.properties b/packaging/tests/tas-email/src/test/resources/log4j2.properties new file mode 100644 index 0000000000..5e34814028 --- /dev/null +++ b/packaging/tests/tas-email/src/test/resources/log4j2.properties @@ -0,0 +1,42 @@ +# Root logger option +rootLogger.level=info +rootLogger.appenderRef.stdout.ref=ConsoleAppender +rootLogger.appenderRef.rolling.ref=RollingAppender + +###### File appender definition ####### +appender.rolling.type=RollingFile +appender.rolling.name=RollingAppender +appender.rolling.fileName=./target/reports/alfresco-tas.log +appender.rolling.filePattern=./target/reports/alfresco-tas.log.%i +appender.rolling.layout.type=PatternLayout +appender.rolling.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n +appender.rolling.policies.type = Policies +appender.rolling.policies.size.type=SizeBasedTriggeringPolicy +appender.rolling.policies.size.size=10MB +appender.rolling.strategy.type=DefaultRolloverStrategy +appender.rolling.strategy.max=10 + +###### Console appender definition ####### +appender.console.type=Console +appender.console.name=ConsoleAppender +appender.console.layout.type=PatternLayout +appender.console.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n + +# TestRail particular log file +# Direct log messages to a log file +logger.testrail.name=testrail +logger.testrail.level=info +logger.testrail.additivity=false +logger.testrail.appenderRef.testrail.ref=TestrailAppender + +appender.testrail.name=TestrailAppender +appender.testrail.type=RollingFile +appender.testrail.fileName=./target/reports/alfresco-testrail.log +appender.testrail.filePattern=./target/reports/alfresco-testrail.log.%i +appender.testrail.layout.type=PatternLayout +appender.testrail.layout.pattern=%d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n +appender.testrail.policies.type=Policies +appender.testrail.policies.size.type=SizeBasedTriggeringPolicy +appender.testrail.policies.size.size=10MB +appender.testrail.strategy.type=DefaultRolloverStrategy +appender.testrail.strategy.max=10 \ No newline at end of file diff --git a/packaging/tests/tas-integration/pom.xml b/packaging/tests/tas-integration/pom.xml index d6620f0da1..f4e05be8b2 100644 --- a/packaging/tests/tas-integration/pom.xml +++ b/packaging/tests/tas-integration/pom.xml @@ -9,7 +9,7 @@ org.alfresco alfresco-community-repo-tests - 20.38-SNAPSHOT + 20.50-SNAPSHOT diff --git a/packaging/tests/tas-integration/src/test/resources/log4j.properties b/packaging/tests/tas-integration/src/test/resources/log4j.properties deleted file mode 100644 index 00e9b5a114..0000000000 --- a/packaging/tests/tas-integration/src/test/resources/log4j.properties +++ /dev/null @@ -1,26 +0,0 @@ -# Root logger option -log4j.rootLogger=INFO, file, stdout - -# Direct log messages to a log file -log4j.appender.file=org.apache.log4j.RollingFileAppender -log4j.appender.file.File=./target/reports/alfresco-tas.log -log4j.appender.file.MaxBackupIndex=10 -log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n - -# Direct log messages to stdout -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target=System.out -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n - -# TestRail particular log file -# Direct log messages to a log file -log4j.appender.testrailLog=org.apache.log4j.RollingFileAppender -log4j.appender.testrailLog.File=./target/reports/alfresco-testrail.log -log4j.appender.testrailLog.MaxBackupIndex=10 -log4j.appender.testrailLog.layout=org.apache.log4j.PatternLayout -log4j.appender.testrailLog.layout.ConversionPattern=%d{HH:mm:ss} %-5p %c{1}:%L - %m%n - -log4j.category.testrail=INFO, testrailLog -log4j.additivity.testrail=false \ No newline at end of file diff --git a/packaging/tests/tas-integration/src/test/resources/log4j2.properties b/packaging/tests/tas-integration/src/test/resources/log4j2.properties new file mode 100644 index 0000000000..5e34814028 --- /dev/null +++ b/packaging/tests/tas-integration/src/test/resources/log4j2.properties @@ -0,0 +1,42 @@ +# Root logger option +rootLogger.level=info +rootLogger.appenderRef.stdout.ref=ConsoleAppender +rootLogger.appenderRef.rolling.ref=RollingAppender + +###### File appender definition ####### +appender.rolling.type=RollingFile +appender.rolling.name=RollingAppender +appender.rolling.fileName=./target/reports/alfresco-tas.log +appender.rolling.filePattern=./target/reports/alfresco-tas.log.%i +appender.rolling.layout.type=PatternLayout +appender.rolling.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n +appender.rolling.policies.type = Policies +appender.rolling.policies.size.type=SizeBasedTriggeringPolicy +appender.rolling.policies.size.size=10MB +appender.rolling.strategy.type=DefaultRolloverStrategy +appender.rolling.strategy.max=10 + +###### Console appender definition ####### +appender.console.type=Console +appender.console.name=ConsoleAppender +appender.console.layout.type=PatternLayout +appender.console.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n + +# TestRail particular log file +# Direct log messages to a log file +logger.testrail.name=testrail +logger.testrail.level=info +logger.testrail.additivity=false +logger.testrail.appenderRef.testrail.ref=TestrailAppender + +appender.testrail.name=TestrailAppender +appender.testrail.type=RollingFile +appender.testrail.fileName=./target/reports/alfresco-testrail.log +appender.testrail.filePattern=./target/reports/alfresco-testrail.log.%i +appender.testrail.layout.type=PatternLayout +appender.testrail.layout.pattern=%d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n +appender.testrail.policies.type=Policies +appender.testrail.policies.size.type=SizeBasedTriggeringPolicy +appender.testrail.policies.size.size=10MB +appender.testrail.strategy.type=DefaultRolloverStrategy +appender.testrail.strategy.max=10 \ No newline at end of file diff --git a/packaging/tests/tas-restapi/pom.xml b/packaging/tests/tas-restapi/pom.xml index 701ab667dc..c44c0ce6fd 100644 --- a/packaging/tests/tas-restapi/pom.xml +++ b/packaging/tests/tas-restapi/pom.xml @@ -8,7 +8,7 @@ org.alfresco alfresco-community-repo-tests - 20.38-SNAPSHOT + 20.50-SNAPSHOT diff --git a/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestCategoryModel.java b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestCategoryModel.java index 5c94132075..e25c56e22a 100644 --- a/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestCategoryModel.java +++ b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestCategoryModel.java @@ -128,5 +128,52 @@ This must be unique within the parent category. ", count=" + count + '}'; } + + public static Builder builder() + { + return new Builder(); + } + + public static class Builder + { + private String id; + private String name; + private String parentId; + private boolean hasChildren; + + public Builder id(String id) + { + this.id = id; + return this; + } + + public Builder name(String name) + { + this.name = name; + return this; + } + + public Builder parentId(String parentId) + { + this.parentId = parentId; + return this; + } + + public Builder hasChildren(boolean hasChildren) + { + this.hasChildren = hasChildren; + return this; + } + + public RestCategoryModel create() + { + final RestCategoryModel category = new RestCategoryModel(); + category.setId(id); + category.setName(name); + category.setParentId(parentId); + category.setHasChildren(hasChildren); + return category; + } + } } diff --git a/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/Categories.java b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/Categories.java index 460ae87a3f..9dbdb38ab1 100644 --- a/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/Categories.java +++ b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/Categories.java @@ -33,7 +33,6 @@ import org.alfresco.rest.core.RestRequest; import org.alfresco.rest.core.RestWrapper; import org.alfresco.rest.model.RestCategoryModel; import org.alfresco.rest.model.RestCategoryModelsCollection; -import org.alfresco.rest.model.RestRuleModelsCollection; import org.springframework.http.HttpMethod; public class Categories extends ModelRequest @@ -47,7 +46,7 @@ public class Categories extends ModelRequest } /** - * Retrieves a category with ID using GET call on using GET call on "/tags/{tagId}" + * Retrieves a category with ID using GET call on "/categories/{categoryId}" * * @return RestCategoryModel */ @@ -72,12 +71,48 @@ public class Categories extends ModelRequest /** * Create single category. * - * @param restCategoryModel The categories to create. + * @param restCategoryModel The category to create. * @return Created category with additional data populated by the repository. */ - public RestCategoryModel createSingleCategory(RestCategoryModel restCategoryModel) { - RestRequest request = RestRequest.requestWithBody(HttpMethod.POST, restCategoryModel.toJson(), "categories/{categoryId}/subcategories", category.getId()); + public RestCategoryModel createSingleCategory(RestCategoryModel restCategoryModel) + { + RestRequest request = RestRequest + .requestWithBody(HttpMethod.POST, restCategoryModel.toJson(), "categories/{categoryId}/subcategories", category.getId()); return restWrapper.processModel(RestCategoryModel.class, request); } + /** + * Get parent category children. + * + * @return The list of child categories. + */ + public RestCategoryModelsCollection getCategoryChildren() + { + RestRequest request = RestRequest.simpleRequest(HttpMethod.GET, "categories/{categoryId}/subcategories", category.getId()); + return restWrapper.processModels(RestCategoryModelsCollection.class, request); + } + + /** + * Update single category. + * - PUT /categories/{categoryId} + * + * @param restCategoryModel The categories to update. + * @return Created category with additional data populated by the repository. + */ + public RestCategoryModel updateCategory(RestCategoryModel restCategoryModel) + { + RestRequest request = RestRequest.requestWithBody(HttpMethod.PUT, restCategoryModel.toJson(), "categories/{categoryId}", category.getId()); + return restWrapper.processModel(RestCategoryModel.class, request); + } + /** + * Delete category. + * - DELETE /categories/{categoryId} + */ + public void deleteCategory() + { + RestRequest request = RestRequest. + simpleRequest(HttpMethod.DELETE, "/categories/{categoryId}", category.getId()); + restWrapper.processEmptyModel(request); + } + } diff --git a/packaging/tests/tas-restapi/src/main/resources/log4j.properties b/packaging/tests/tas-restapi/src/main/resources/log4j.properties deleted file mode 100644 index 00e9b5a114..0000000000 --- a/packaging/tests/tas-restapi/src/main/resources/log4j.properties +++ /dev/null @@ -1,26 +0,0 @@ -# Root logger option -log4j.rootLogger=INFO, file, stdout - -# Direct log messages to a log file -log4j.appender.file=org.apache.log4j.RollingFileAppender -log4j.appender.file.File=./target/reports/alfresco-tas.log -log4j.appender.file.MaxBackupIndex=10 -log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n - -# Direct log messages to stdout -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target=System.out -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n - -# TestRail particular log file -# Direct log messages to a log file -log4j.appender.testrailLog=org.apache.log4j.RollingFileAppender -log4j.appender.testrailLog.File=./target/reports/alfresco-testrail.log -log4j.appender.testrailLog.MaxBackupIndex=10 -log4j.appender.testrailLog.layout=org.apache.log4j.PatternLayout -log4j.appender.testrailLog.layout.ConversionPattern=%d{HH:mm:ss} %-5p %c{1}:%L - %m%n - -log4j.category.testrail=INFO, testrailLog -log4j.additivity.testrail=false \ No newline at end of file diff --git a/packaging/tests/tas-restapi/src/main/resources/log4j2.properties b/packaging/tests/tas-restapi/src/main/resources/log4j2.properties new file mode 100644 index 0000000000..5e34814028 --- /dev/null +++ b/packaging/tests/tas-restapi/src/main/resources/log4j2.properties @@ -0,0 +1,42 @@ +# Root logger option +rootLogger.level=info +rootLogger.appenderRef.stdout.ref=ConsoleAppender +rootLogger.appenderRef.rolling.ref=RollingAppender + +###### File appender definition ####### +appender.rolling.type=RollingFile +appender.rolling.name=RollingAppender +appender.rolling.fileName=./target/reports/alfresco-tas.log +appender.rolling.filePattern=./target/reports/alfresco-tas.log.%i +appender.rolling.layout.type=PatternLayout +appender.rolling.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n +appender.rolling.policies.type = Policies +appender.rolling.policies.size.type=SizeBasedTriggeringPolicy +appender.rolling.policies.size.size=10MB +appender.rolling.strategy.type=DefaultRolloverStrategy +appender.rolling.strategy.max=10 + +###### Console appender definition ####### +appender.console.type=Console +appender.console.name=ConsoleAppender +appender.console.layout.type=PatternLayout +appender.console.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n + +# TestRail particular log file +# Direct log messages to a log file +logger.testrail.name=testrail +logger.testrail.level=info +logger.testrail.additivity=false +logger.testrail.appenderRef.testrail.ref=TestrailAppender + +appender.testrail.name=TestrailAppender +appender.testrail.type=RollingFile +appender.testrail.fileName=./target/reports/alfresco-testrail.log +appender.testrail.filePattern=./target/reports/alfresco-testrail.log.%i +appender.testrail.layout.type=PatternLayout +appender.testrail.layout.pattern=%d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n +appender.testrail.policies.type=Policies +appender.testrail.policies.size.type=SizeBasedTriggeringPolicy +appender.testrail.policies.size.size=10MB +appender.testrail.strategy.type=DefaultRolloverStrategy +appender.testrail.strategy.max=10 \ No newline at end of file diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CategoriesRestTest.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CategoriesRestTest.java new file mode 100644 index 0000000000..660729bfb6 --- /dev/null +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CategoriesRestTest.java @@ -0,0 +1,91 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2022 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.categories; + +import static org.alfresco.utility.data.RandomData.getRandomName; +import static org.alfresco.utility.report.log.Step.STEP; +import static org.springframework.http.HttpStatus.CREATED; + +import org.alfresco.rest.RestTest; +import org.alfresco.rest.model.RestCategoryModel; +import org.alfresco.utility.model.UserModel; +import org.testng.annotations.BeforeClass; + +abstract class CategoriesRestTest extends RestTest +{ + protected static final String ROOT_CATEGORY_ID = "-root-"; + protected static final String CATEGORY_NAME_PREFIX = "CategoryName"; + protected static final String FIELD_NAME = "name"; + protected static final String FIELD_ID = "id"; + protected static final String FIELD_PARENT_ID = "parentId"; + protected static final String FIELD_HAS_CHILDREN = "hasChildren"; + + protected UserModel user; + + @BeforeClass(alwaysRun = true) + public void dataPreparation() throws Exception + { + STEP("Create a user"); + user = dataUser.createRandomTestUser(); + } + + protected RestCategoryModel prepareCategoryUnderRoot() + { + return prepareCategoryUnder(ROOT_CATEGORY_ID); + } + + protected RestCategoryModel prepareCategoryUnder(final String parentId) + { + final RestCategoryModel parentCategory = createCategoryModelWithId(parentId); + final RestCategoryModel categoryModel = createCategoryModelWithName(getRandomName(CATEGORY_NAME_PREFIX)); + final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(parentCategory) + .createSingleCategory(categoryModel); + restClient.assertStatusCodeIs(CREATED); + + return createdCategory; + } + + protected RestCategoryModel createCategoryModelWithId(final String id) + { + return createCategoryModelWithIdAndName(id, null); + } + + protected RestCategoryModel createCategoryModelWithName(final String name) + { + return createCategoryModelWithIdAndName(null, name); + } + + protected RestCategoryModel createCategoryModelWithIdAndName(final String id, final String name) + { + return RestCategoryModel.builder() + .id(id) + .name(name) + .create(); + } +} diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CreateCategoriesTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CreateCategoriesTests.java index edfc95d36f..92ccb56d44 100644 --- a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CreateCategoriesTests.java +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/CreateCategoriesTests.java @@ -36,31 +36,17 @@ import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; -import org.alfresco.rest.RestTest; import org.alfresco.rest.model.RestCategoryModel; import org.alfresco.rest.model.RestCategoryModelsCollection; import org.alfresco.utility.data.RandomData; import org.alfresco.utility.model.FolderModel; import org.alfresco.utility.model.SiteModel; import org.alfresco.utility.model.TestGroup; -import org.alfresco.utility.model.UserModel; -import org.testng.annotations.BeforeClass; +import org.apache.commons.lang3.StringUtils; import org.testng.annotations.Test; -public class CreateCategoriesTests extends RestTest +public class CreateCategoriesTests extends CategoriesRestTest { - private static final String FIELD_NAME = "name"; - private static final String FIELD_PARENT_ID = "parentId"; - private static final String FIELD_HAS_CHILDREN = "hasChildren"; - private static final String FIELD_ID = "id"; - private UserModel user; - - @BeforeClass(alwaysRun = true) - public void dataPreparation() throws Exception - { - STEP("Create a user"); - user = dataUser.createRandomTestUser(); - } /** * Check we can create a category as direct child of root category @@ -69,10 +55,8 @@ public class CreateCategoriesTests extends RestTest public void testCreateCategoryUnderRoot() { STEP("Create a category under root category (as admin)"); - final RestCategoryModel rootCategory = new RestCategoryModel(); - rootCategory.setId("-root-"); - final RestCategoryModel aCategory = new RestCategoryModel(); - aCategory.setName(RandomData.getRandomName("Category")); + final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); + final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category")); final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser()) .withCoreAPI() .usingCategory(rootCategory) @@ -84,6 +68,22 @@ public class CreateCategoriesTests extends RestTest createdCategory.assertThat().field(FIELD_HAS_CHILDREN).is(false); } + /** + * Check we get 400 error when attempting to create a category with empty name + */ + @Test(groups = {TestGroup.REST_API}) + public void testCreateCategoryWithoutName_andFail() + { + STEP("Create a category under root category (as admin)"); + final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); + final RestCategoryModel aCategory = createCategoryModelWithName(StringUtils.EMPTY); + restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(rootCategory) + .createSingleCategory(aCategory); + restClient.assertStatusCodeIs(BAD_REQUEST).assertLastError().containsSummary("Category name must not be null or empty"); + } + /** * Check we can create several categories as children of a created category */ @@ -91,10 +91,8 @@ public class CreateCategoriesTests extends RestTest public void testCreateSeveralSubCategories() { STEP("Create a category under root category (as admin)"); - final RestCategoryModel rootCategory = new RestCategoryModel(); - rootCategory.setId("-root-"); - final RestCategoryModel aCategory = new RestCategoryModel(); - aCategory.setName(RandomData.getRandomName("Category")); + final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); + final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category")); final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser()) .withCoreAPI() .usingCategory(rootCategory) @@ -134,6 +132,52 @@ public class CreateCategoriesTests extends RestTest parentCategoryFromGet.assertThat().field(FIELD_HAS_CHILDREN).is(true); } + /** + * Check we can create over 100 categories as children of a created category and pagination information is proper. + */ + @Test(groups = {TestGroup.REST_API}) + public void testCreateOver100SubCategories() + { + STEP("Create a category under root category (as admin)"); + final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); + final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category")); + final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(rootCategory) + .createSingleCategory(aCategory); + restClient.assertStatusCodeIs(CREATED); + + createdCategory.assertThat().field(FIELD_NAME).is(aCategory.getName()) + .assertThat().field(FIELD_PARENT_ID).is(rootCategory.getId()) + .assertThat().field(FIELD_HAS_CHILDREN).is(false) + .assertThat().field(FIELD_ID).isNotEmpty(); + + STEP("Create more than a hundred categories under the previously created (as admin)"); + final int categoriesNumber = 120; + final List categoriesToCreate = getCategoriesToCreate(categoriesNumber); + final RestCategoryModelsCollection createdSubCategories = restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(createdCategory) + .createCategoriesList(categoriesToCreate); + restClient.assertStatusCodeIs(CREATED); + + createdSubCategories.assertThat() + .entriesListCountIs(categoriesToCreate.size()); + IntStream.range(0, categoriesNumber) + .forEach(i -> createdSubCategories.getEntries().get(i).onModel() + .assertThat().field(FIELD_NAME).is(categoriesToCreate.get(i).getName()) + .assertThat().field(FIELD_PARENT_ID).is(createdCategory.getId()) + .assertThat().field(FIELD_HAS_CHILDREN).is(false) + .assertThat().field(FIELD_ID).isNotEmpty() + ); + createdSubCategories.getPagination().assertThat().field("count").is(categoriesNumber) + .assertThat().field("totalItems").is(categoriesNumber) + .assertThat().field("maxItems").is(categoriesNumber) + .assertThat().field("skipCount").is(0) + .assertThat().field("hasMoreItems").is(false); + + } + /** * Check we cannot create a category as direct child of root category as non-admin user */ @@ -141,15 +185,13 @@ public class CreateCategoriesTests extends RestTest public void testCreateCategoryUnderRootAsRegularUser_andFail() { STEP("Create a category under root category (as user)"); - final RestCategoryModel rootCategory = new RestCategoryModel(); - rootCategory.setId("-root-"); - final RestCategoryModel aCategory = new RestCategoryModel(); - aCategory.setName(RandomData.getRandomName("Category")); + final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); + final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category")); restClient.authenticateUser(user) .withCoreAPI() .usingCategory(rootCategory) .createSingleCategory(aCategory); - restClient.assertStatusCodeIs(FORBIDDEN).assertLastError().containsSummary("Current user does not have permission to create a category"); + restClient.assertStatusCodeIs(FORBIDDEN).assertLastError().containsSummary("Current user does not have permission to manage a category"); } /** @@ -159,11 +201,9 @@ public class CreateCategoriesTests extends RestTest public void testCreateCategoryUnderNonExistingParent_andFail() { STEP("Create a category under non existing category node (as admin)"); - final RestCategoryModel rootCategory = new RestCategoryModel(); final String id = "non-existing-node-id"; - rootCategory.setId(id); - final RestCategoryModel aCategory = new RestCategoryModel(); - aCategory.setName(RandomData.getRandomName("Category")); + final RestCategoryModel rootCategory = createCategoryModelWithId(id); + final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category")); restClient.authenticateUser(dataUser.getAdminUser()) .withCoreAPI() .usingCategory(rootCategory) @@ -182,10 +222,8 @@ public class CreateCategoriesTests extends RestTest final FolderModel folder = dataContent.usingUser(user).usingSite(site).createFolder(); STEP("Create a category under folder node (as admin)"); - final RestCategoryModel rootCategory = new RestCategoryModel(); - rootCategory.setId(folder.getNodeRef()); - final RestCategoryModel aCategory = new RestCategoryModel(); - aCategory.setName(RandomData.getRandomName("Category")); + final RestCategoryModel rootCategory = createCategoryModelWithId(folder.getNodeRef()); + final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category")); restClient.authenticateUser(dataUser.getAdminUser()) .withCoreAPI() .usingCategory(rootCategory) @@ -193,14 +231,10 @@ public class CreateCategoriesTests extends RestTest restClient.assertStatusCodeIs(BAD_REQUEST).assertLastError().containsSummary("Node id does not refer to a valid category"); } - private List getCategoriesToCreate(final int count) + static List getCategoriesToCreate(final int count) { return IntStream.range(0, count) - .mapToObj(i -> { - final RestCategoryModel aSubCategory = new RestCategoryModel(); - aSubCategory.setName((RandomData.getRandomName("SubCategory"))); - return aSubCategory; - }) - .collect(Collectors.toList()); + .mapToObj(i -> RestCategoryModel.builder().name(RandomData.getRandomName("SubCategory")).create()) + .collect(Collectors.toList()); } } diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/DeleteCategoriesTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/DeleteCategoriesTests.java new file mode 100644 index 0000000000..779876017b --- /dev/null +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/DeleteCategoriesTests.java @@ -0,0 +1,102 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2022 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.categories; + +import static org.alfresco.utility.report.log.Step.STEP; +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.FORBIDDEN; +import static org.springframework.http.HttpStatus.NOT_FOUND; +import static org.springframework.http.HttpStatus.NO_CONTENT; + +import org.alfresco.rest.model.RestCategoryModel; +import org.alfresco.utility.model.FolderModel; +import org.alfresco.utility.model.SiteModel; +import org.alfresco.utility.model.TestGroup; +import org.testng.annotations.Test; + +public class DeleteCategoriesTests extends CategoriesRestTest +{ + + /** + * Check we can delete a category. + */ + @Test(groups = {TestGroup.REST_API}) + public void testDeleteCategory() + { + STEP("Create a category and send a request to delete it."); + RestCategoryModel aCategory = prepareCategoryUnderRoot(); + restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI().usingCategory(aCategory).deleteCategory(); + restClient.assertStatusCodeIs(NO_CONTENT); + + STEP("Ensure that the category has been deleted by sending a GET request and receiving 404."); + restClient.authenticateUser(user).withCoreAPI().usingCategory(aCategory).getCategory(); + restClient.assertStatusCodeIs(NOT_FOUND); + } + + /** + * Check we get an error when trying to delete a category as a non-admin user. + */ + @Test(groups = {TestGroup.REST_API}) + public void testDeleteCategoryAsRegularUser_andFail() + { + RestCategoryModel aCategory = prepareCategoryUnderRoot(); + restClient.authenticateUser(user).withCoreAPI().usingCategory(aCategory).deleteCategory(); + restClient.assertStatusCodeIs(FORBIDDEN).assertLastError().containsSummary("Current user does not have permission to manage a category"); + } + + /** + * Check we receive 404 error when trying to delete a category with a non-existent node id. + */ + @Test(groups = {TestGroup.REST_API}) + public void testDeleteNonExistentCategory() + { + STEP("Get category with non-existent id"); + final String id = "non-existing-dummy-id"; + final RestCategoryModel rootCategory = createCategoryModelWithId(id); + + STEP("Attempt to delete category with non-existent id and receive 404"); + restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI().usingCategory(rootCategory).deleteCategory(); + restClient.assertStatusCodeIs(NOT_FOUND); + } + + /** + * Attempt to delete a category when providing a node id that doesn't belong to a category + */ + @Test(groups = {TestGroup.REST_API}) + public void testDeleteCategory_givenNonCategoryNodeId() + { + STEP("Create a site and a folder inside it"); + final SiteModel site = dataSite.usingUser(user).createPublicRandomSite(); + final FolderModel folder = dataContent.usingUser(user).usingSite(site).createFolder(); + String id = folder.getNodeRef(); + + STEP("Create a category, set its id to the folder id and attempt to delete it"); + final RestCategoryModel aCategory = createCategoryModelWithId(id); + restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI().usingCategory(aCategory).deleteCategory(); + restClient.assertStatusCodeIs(BAD_REQUEST).assertLastError().containsSummary("Node id does not refer to a valid category"); + } +} diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/GetCategoriesTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/GetCategoriesTests.java index d02312442b..ac1e7c666e 100644 --- a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/GetCategoriesTests.java +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/GetCategoriesTests.java @@ -28,28 +28,26 @@ package org.alfresco.rest.categories; import static org.alfresco.utility.report.log.Step.STEP; import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.NOT_FOUND; import static org.springframework.http.HttpStatus.OK; +import static org.testng.Assert.assertTrue; + +import java.util.List; +import java.util.stream.Collectors; -import org.alfresco.rest.RestTest; import org.alfresco.rest.model.RestCategoryModel; +import org.alfresco.rest.model.RestCategoryModelsCollection; +import org.alfresco.utility.data.RandomData; import org.alfresco.utility.model.FolderModel; import org.alfresco.utility.model.SiteModel; import org.alfresco.utility.model.TestGroup; -import org.alfresco.utility.model.UserModel; -import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -public class GetCategoriesTests extends RestTest +public class GetCategoriesTests extends CategoriesRestTest { - private UserModel user; - - @BeforeClass(alwaysRun = true) - public void dataPreparation() throws Exception - { - STEP("Create a user"); - user = dataUser.createRandomTestUser(); - } + private static final List DEFAULT_ROOT_CATEGORIES = List.of("Software Document Classification", "Languages", "Regions", "Tags"); + private static final String NON_EXISTING_CATEGORY_ID = "non-existing-category-id"; /** * Check we can get a category which we just created in as direct child of root category @@ -57,11 +55,24 @@ public class GetCategoriesTests extends RestTest @Test(groups = {TestGroup.REST_API}) public void testGetCategoryById() { - STEP("Get category with -root- as id (which does not exist)"); - final RestCategoryModel rootCategory = new RestCategoryModel(); - rootCategory.setId("-root-"); - restClient.authenticateUser(user).withCoreAPI().usingCategory(rootCategory).getCategory(); - restClient.assertStatusCodeIs(NOT_FOUND); + STEP("Create a category under root category (as admin)"); + final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); + final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category")); + final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(rootCategory) + .createSingleCategory(aCategory); + restClient.assertStatusCodeIs(CREATED); + + createdCategory.assertThat().field("name").is(aCategory.getName()); + createdCategory.assertThat().field("parentId").is(rootCategory.getId()); + createdCategory.assertThat().field("hasChildren").is(false); + + STEP("Get the created category (as regular user)"); + final RestCategoryModel categoryFromGet = + restClient.authenticateUser(user).withCoreAPI().usingCategory(createdCategory).getCategory(); + restClient.assertStatusCodeIs(OK); + categoryFromGet.assertThat().isEqualTo(createdCategory); } /** @@ -70,15 +81,14 @@ public class GetCategoriesTests extends RestTest @Test(groups = {TestGroup.REST_API}) public void testGetCategoryByIdProvidingRootAsId() { - STEP("Get category with -root- as id (which does not exist)"); - final RestCategoryModel rootCategory = new RestCategoryModel(); - rootCategory.setId("-root-"); + STEP("Get category with -root- as id"); + final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); restClient.authenticateUser(user).withCoreAPI().usingCategory(rootCategory).getCategory(); - restClient.assertStatusCodeIs(NOT_FOUND); + restClient.assertStatusCodeIs(BAD_REQUEST).assertLastError().containsSummary("Node id does not refer to a valid category"); } /** - * Check we get an error when passing as category id + * Check we get an error when passing folder node id as category id */ @Test(groups = {TestGroup.REST_API}) public void testGetCategoryByIdProvidingFolderAsId() @@ -88,10 +98,98 @@ public class GetCategoriesTests extends RestTest final FolderModel folder = dataContent.usingUser(user).usingSite(site).createFolder(); STEP("Get category with folder id passed as id"); - final RestCategoryModel rootCategory = new RestCategoryModel(); - rootCategory.setId(folder.getNodeRef()); + final RestCategoryModel rootCategory = createCategoryModelWithId(folder.getNodeRef()); restClient.authenticateUser(user).withCoreAPI().usingCategory(rootCategory).getCategory(); - restClient.assertStatusCodeIs(BAD_REQUEST); + restClient.assertStatusCodeIs(BAD_REQUEST).assertLastError().containsSummary("Node id does not refer to a valid category"); } + /** + * Check we get an error when passing non existing as category id + */ + @Test(groups = {TestGroup.REST_API}) + public void testGetCategoryByIdProvidingNonExistingId() + { + STEP("Get category with id which does not exist"); + final String id = NON_EXISTING_CATEGORY_ID; + final RestCategoryModel rootCategory = createCategoryModelWithId(id); + restClient.authenticateUser(user).withCoreAPI().usingCategory(rootCategory).getCategory(); + restClient.assertStatusCodeIs(NOT_FOUND).assertLastError().containsSummary(id); + } + + /** + * Check we can get children category of a root category + */ + @Test(groups = {TestGroup.REST_API}) + public void testGetCategoryChildren() + { + STEP("Get category children with -root- as parent id"); + final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); + RestCategoryModelsCollection childCategoriesList = + restClient.authenticateUser(user).withCoreAPI().usingCategory(rootCategory).getCategoryChildren(); + restClient.assertStatusCodeIs(OK); + + childCategoriesList.assertThat().entriesListIsNotEmpty(); + assertTrue(childCategoriesList.getEntries().stream() + .map(RestCategoryModel::onModel) + .map(RestCategoryModel::getName) + .collect(Collectors.toList()) + .containsAll(DEFAULT_ROOT_CATEGORIES)); + STEP("Create a new category under root and make sure it is returned as one of root's children"); + final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("newCategoryUnderRoot")); + final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(rootCategory) + .createSingleCategory(aCategory); + restClient.assertStatusCodeIs(CREATED); + + childCategoriesList = restClient.authenticateUser(user).withCoreAPI().usingCategory(rootCategory).getCategoryChildren(); + restClient.assertStatusCodeIs(OK); + assertTrue(childCategoriesList.getEntries().stream() + .map(RestCategoryModel::onModel) + .map(RestCategoryModel::getId) + .collect(Collectors.toList()) + .contains(createdCategory.getId())); + + STEP("Create 2 more categories under newCategoryUnderRoot and make sure they are returned as children"); + final int categoriesCount = 2; + final List categoriesToCreate = CreateCategoriesTests.getCategoriesToCreate(categoriesCount); + final RestCategoryModelsCollection createdSubCategories = restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(createdCategory) + .createCategoriesList(categoriesToCreate); + restClient.assertStatusCodeIs(CREATED); + childCategoriesList = restClient.authenticateUser(user).withCoreAPI().usingCategory(rootCategory).getCategoryChildren(); + restClient.assertStatusCodeIs(OK); + childCategoriesList.getEntries().containsAll(createdSubCategories.getEntries()); + } + + /** + * Check we get an error when passing folder node id as parent category id when getting children. + */ + @Test(groups = {TestGroup.REST_API}) + public void testGetCategoryChildrenProvidingFolderAsId() + { + STEP("Create a site and a folder inside it"); + final SiteModel site = dataSite.usingUser(user).createPublicRandomSite(); + final FolderModel folder = dataContent.usingUser(user).usingSite(site).createFolder(); + + STEP("Get category children with folder id passed as parent id"); + final RestCategoryModel parentCategory = createCategoryModelWithId(folder.getNodeRef()); + restClient.authenticateUser(user).withCoreAPI().usingCategory(parentCategory).getCategoryChildren(); + restClient.assertStatusCodeIs(BAD_REQUEST).assertLastError().containsSummary("Node id does not refer to a valid category"); + } + + /** + * Check we get an error when passing a non-existent node id as parent category id when getting children. + */ + @Test(groups = {TestGroup.REST_API}) + public void testGetCategoryChildrenProvidingNonExistingParent() + { + + STEP("Get category with folder id passed as id"); + final String parentId = NON_EXISTING_CATEGORY_ID; + final RestCategoryModel parentCategory = createCategoryModelWithId(parentId); + restClient.authenticateUser(user).withCoreAPI().usingCategory(parentCategory).getCategoryChildren(); + restClient.assertStatusCodeIs(NOT_FOUND).assertLastError().containsSummary(parentId); + } } diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/UpdateCategoriesTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/UpdateCategoriesTests.java new file mode 100644 index 0000000000..f439c07b6a --- /dev/null +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/categories/UpdateCategoriesTests.java @@ -0,0 +1,214 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2022 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.categories; + +import static org.alfresco.utility.data.RandomData.getRandomName; +import static org.alfresco.utility.report.log.Step.STEP; +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.FORBIDDEN; +import static org.springframework.http.HttpStatus.NOT_FOUND; +import static org.springframework.http.HttpStatus.OK; + +import org.alfresco.rest.model.RestCategoryModel; +import org.alfresco.utility.model.FolderModel; +import org.alfresco.utility.model.SiteModel; +import org.alfresco.utility.model.TestGroup; +import org.alfresco.utility.model.UserModel; +import org.testng.annotations.Test; + +public class UpdateCategoriesTests extends CategoriesRestTest +{ + private static final String CATEGORY_NEW_NAME_PREFIX = "NewCategoryName"; + private static final String IGNORE_FIELD_NAME = FIELD_NAME; + + /** + * Update a category (direct child of root category) + */ + @Test(groups = { TestGroup.REST_API}) + public void testUpdateCategory_asAdmin() + { + STEP("Prepare as admin a category under root category"); + final RestCategoryModel createdCategory = prepareCategoryUnderRoot(); + + STEP("Update as admin newly created category"); + final String categoryNewName = getRandomName(CATEGORY_NEW_NAME_PREFIX); + final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(categoryNewName); + final RestCategoryModel updatedCategory = restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(createdCategory) + .updateCategory(fixedCategoryModel); + + restClient.assertStatusCodeIs(OK); + updatedCategory.assertThat().isEqualTo(createdCategory, IGNORE_FIELD_NAME); + updatedCategory.assertThat().field(FIELD_NAME).isNot(createdCategory.getName()); + updatedCategory.assertThat().field(FIELD_NAME).is(categoryNewName); + } + + /** + * Update a subcategory of root's child category + */ + @Test(groups = { TestGroup.REST_API}) + public void testUpdateSubcategory_asAdmin() + { + STEP("Prepare as admin a category under root category"); + final RestCategoryModel createdCategory = prepareCategoryUnderRoot(); + + STEP("Prepare as admin a subcategory of root's child category"); + final RestCategoryModel createdSubcategory = prepareCategoryUnder(createdCategory.getId()); + + STEP("Update as admin newly created subcategory"); + final String categoryNewName = getRandomName(CATEGORY_NEW_NAME_PREFIX); + final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(categoryNewName); + final RestCategoryModel updatedCategory = restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(createdSubcategory) + .updateCategory(fixedCategoryModel); + + restClient.assertStatusCodeIs(OK); + updatedCategory.assertThat().isEqualTo(createdSubcategory, IGNORE_FIELD_NAME); + updatedCategory.assertThat().field(FIELD_NAME).is(categoryNewName); + } + + /** + * Try to update a category as a user and expect 403 (Forbidden) in response + */ + @Test(groups = { TestGroup.REST_API}) + public void testUpdateCategory_asUserAndExpect403() + { + STEP("Prepare as admin a category under root category"); + final RestCategoryModel createdCategory = prepareCategoryUnderRoot(); + + STEP("Try to update as user newly created category"); + final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(getRandomName(CATEGORY_NEW_NAME_PREFIX)); + restClient.authenticateUser(user) + .withCoreAPI() + .usingCategory(createdCategory) + .updateCategory(fixedCategoryModel); + + restClient.assertStatusCodeIs(FORBIDDEN); + } + + /** + * Try to update a non-existing category and receive 404 (Not Found) + */ + @Test(groups = { TestGroup.REST_API}) + public void testUpdateCategory_usingNonExistingCategoryAndExpect404() + { + STEP("Create a fake parent category"); + final RestCategoryModel nonExistingCategory = createCategoryModelWithIdAndName("non-existing-dummy-id", getRandomName(CATEGORY_NAME_PREFIX)); + + STEP("Try to update as admin fake category"); + final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(getRandomName(CATEGORY_NEW_NAME_PREFIX)); + restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(nonExistingCategory) + .updateCategory(fixedCategoryModel); + + restClient.assertStatusCodeIs(NOT_FOUND); + } + + /** + * Try to update a non-category (folder) node and receive 400 (Bad Request) + */ + @Test(groups = { TestGroup.REST_API}) + public void testUpdateCategory_usingFolderNodeAndExpect400() + { + STEP("Prepare a site and a folder inside it"); + final UserModel user = dataUser.createRandomTestUser(); + final SiteModel site = dataSite.usingUser(user).createPublicRandomSite(); + final FolderModel folder = dataContent.usingUser(user).usingSite(site).createFolder(); + final RestCategoryModel categoryWithFolderId = createCategoryModelWithIdAndName(folder.getNodeRef(), getRandomName(CATEGORY_NAME_PREFIX)); + + STEP("Try to update as admin folder node as category"); + final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(getRandomName(CATEGORY_NEW_NAME_PREFIX)); + restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(categoryWithFolderId) + .updateCategory(fixedCategoryModel); + + restClient.assertStatusCodeIs(BAD_REQUEST); + } + + /** + * Try to update a root category and receive 400 (Bad Request) + */ + @Test(groups = { TestGroup.REST_API}) + public void testUpdateCategory_usingRootCategoryAndExpect400() + { + STEP("Create root category model"); + final RestCategoryModel rootCategoryModel = createCategoryModelWithId(ROOT_CATEGORY_ID); + + STEP("Try to update as admin root category"); + final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(getRandomName(CATEGORY_NEW_NAME_PREFIX)); + restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(rootCategoryModel) + .updateCategory(fixedCategoryModel); + + restClient.assertStatusCodeIs(BAD_REQUEST); + } + + /** + * Try to update a category with an empty name and receive 400 (Bad Request) + */ + @Test(groups = { TestGroup.REST_API}) + public void testUpdateCategory_withEmptyNameAndExpect400() + { + STEP("Prepare as admin a category under root category"); + final RestCategoryModel createdCategory = prepareCategoryUnderRoot(); + + STEP("Try to update as admin newly created category with a category without name"); + final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(null); + restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(createdCategory) + .updateCategory(fixedCategoryModel); + + restClient.assertStatusCodeIs(BAD_REQUEST); + } + + /** + * Try to update a category with an invalid, but not important ID in body and receive 200 (OK) + */ + @Test(groups = { TestGroup.REST_API}) + public void testUpdateCategory_withIgnoredInvalidIdInBodyAndExpect200() + { + STEP("Prepare as admin a category under root category"); + final RestCategoryModel createdCategory = prepareCategoryUnderRoot(); + + STEP("Try to update as admin newly created category with a category with invalid ID and receive 200"); + final String categoryNewName = getRandomName(CATEGORY_NEW_NAME_PREFIX); + final RestCategoryModel fixedCategoryModel = createCategoryModelWithIdAndName("non-existing-dummy-id", categoryNewName); + final RestCategoryModel updatedCategory = restClient.authenticateUser(dataUser.getAdminUser()) + .withCoreAPI() + .usingCategory(createdCategory) + .updateCategory(fixedCategoryModel); + + restClient.assertStatusCodeIs(OK); + updatedCategory.assertThat().field(FIELD_NAME).is(categoryNewName); + } +} diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/CreateRulesTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/CreateRulesTests.java index cf4888952b..8b195e3ebd 100644 --- a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/CreateRulesTests.java +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/rules/CreateRulesTests.java @@ -56,6 +56,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import java.util.stream.IntStream; import javax.json.Json; import javax.json.JsonObject; @@ -63,6 +64,7 @@ import javax.json.JsonObject; import org.alfresco.rest.model.RestActionBodyExecTemplateModel; import org.alfresco.rest.model.RestActionConstraintModel; import org.alfresco.rest.model.RestCompositeConditionDefinitionModel; +import org.alfresco.rest.model.RestPaginationModel; import org.alfresco.rest.model.RestRuleModel; import org.alfresco.rest.model.RestRuleModelsCollection; import org.alfresco.utility.constants.UserRole; @@ -267,6 +269,38 @@ public class CreateRulesTests extends RulesRestTest .assertThat().field("name").is(ruleNames.get(i))); } + /** Check we can create over 100 rules and get them all back in response. */ + @Test (groups = { TestGroup.REST_API, TestGroup.RULES }) + public void createOver100Rules() + { + STEP("Create a list of 120 rules in one POST request"); + final int ruleCount = 120; + final String ruleNamePrefix = "multiRule"; + final List ruleModels = IntStream.rangeClosed(1, ruleCount) + .mapToObj(i -> rulesUtils.createRuleModel(ruleNamePrefix + i)) + .collect(Collectors.toList()); + + final FolderModel aFolder = dataContent.usingUser(user).usingSite(site).createFolder(); + final RestRuleModelsCollection rules = restClient.authenticateUser(user).withPrivateAPI().usingNode(aFolder).usingDefaultRuleSet() + .createListOfRules(ruleModels); + + restClient.assertStatusCodeIs(CREATED); + + assertEquals("Unexpected number of rules received in response.", ruleCount, rules.getEntries().size()); + IntStream.range(0, ruleModels.size()).forEach(i -> + rules.getEntries().get(i).onModel() + .assertThat().field("id").isNotNull() + .assertThat().field("name").is(ruleNamePrefix + (i + 1))); + + rules.getPagination() + .assertThat().field("count").is(ruleCount) + .assertThat().field("totalItems").is(ruleCount) + .assertThat().field("maxItems").is(ruleCount) + .assertThat().field("skipCount").is(0) + .assertThat().field("hasMoreItems").is(false); + + } + /** Try to create several rules with an error in one of them. */ @Test (groups = { TestGroup.REST_API, TestGroup.RULES }) public void createRulesWithOneError() diff --git a/packaging/tests/tas-restapi/src/test/resources/log4j.properties b/packaging/tests/tas-restapi/src/test/resources/log4j.properties deleted file mode 100644 index 00e9b5a114..0000000000 --- a/packaging/tests/tas-restapi/src/test/resources/log4j.properties +++ /dev/null @@ -1,26 +0,0 @@ -# Root logger option -log4j.rootLogger=INFO, file, stdout - -# Direct log messages to a log file -log4j.appender.file=org.apache.log4j.RollingFileAppender -log4j.appender.file.File=./target/reports/alfresco-tas.log -log4j.appender.file.MaxBackupIndex=10 -log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n - -# Direct log messages to stdout -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target=System.out -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n - -# TestRail particular log file -# Direct log messages to a log file -log4j.appender.testrailLog=org.apache.log4j.RollingFileAppender -log4j.appender.testrailLog.File=./target/reports/alfresco-testrail.log -log4j.appender.testrailLog.MaxBackupIndex=10 -log4j.appender.testrailLog.layout=org.apache.log4j.PatternLayout -log4j.appender.testrailLog.layout.ConversionPattern=%d{HH:mm:ss} %-5p %c{1}:%L - %m%n - -log4j.category.testrail=INFO, testrailLog -log4j.additivity.testrail=false \ No newline at end of file diff --git a/packaging/tests/tas-restapi/src/test/resources/log4j2.properties b/packaging/tests/tas-restapi/src/test/resources/log4j2.properties new file mode 100644 index 0000000000..5e34814028 --- /dev/null +++ b/packaging/tests/tas-restapi/src/test/resources/log4j2.properties @@ -0,0 +1,42 @@ +# Root logger option +rootLogger.level=info +rootLogger.appenderRef.stdout.ref=ConsoleAppender +rootLogger.appenderRef.rolling.ref=RollingAppender + +###### File appender definition ####### +appender.rolling.type=RollingFile +appender.rolling.name=RollingAppender +appender.rolling.fileName=./target/reports/alfresco-tas.log +appender.rolling.filePattern=./target/reports/alfresco-tas.log.%i +appender.rolling.layout.type=PatternLayout +appender.rolling.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n +appender.rolling.policies.type = Policies +appender.rolling.policies.size.type=SizeBasedTriggeringPolicy +appender.rolling.policies.size.size=10MB +appender.rolling.strategy.type=DefaultRolloverStrategy +appender.rolling.strategy.max=10 + +###### Console appender definition ####### +appender.console.type=Console +appender.console.name=ConsoleAppender +appender.console.layout.type=PatternLayout +appender.console.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n + +# TestRail particular log file +# Direct log messages to a log file +logger.testrail.name=testrail +logger.testrail.level=info +logger.testrail.additivity=false +logger.testrail.appenderRef.testrail.ref=TestrailAppender + +appender.testrail.name=TestrailAppender +appender.testrail.type=RollingFile +appender.testrail.fileName=./target/reports/alfresco-testrail.log +appender.testrail.filePattern=./target/reports/alfresco-testrail.log.%i +appender.testrail.layout.type=PatternLayout +appender.testrail.layout.pattern=%d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n +appender.testrail.policies.type=Policies +appender.testrail.policies.size.type=SizeBasedTriggeringPolicy +appender.testrail.policies.size.size=10MB +appender.testrail.strategy.type=DefaultRolloverStrategy +appender.testrail.strategy.max=10 \ No newline at end of file diff --git a/packaging/tests/tas-webdav/pom.xml b/packaging/tests/tas-webdav/pom.xml index 6087a467c0..a3405e5827 100644 --- a/packaging/tests/tas-webdav/pom.xml +++ b/packaging/tests/tas-webdav/pom.xml @@ -9,7 +9,7 @@ org.alfresco alfresco-community-repo-tests - 20.38-SNAPSHOT + 20.50-SNAPSHOT diff --git a/packaging/tests/tas-webdav/src/test/java/org/alfresco/webdav/WebDavTest.java b/packaging/tests/tas-webdav/src/test/java/org/alfresco/webdav/WebDavTest.java index f75eed0f92..ca71c0ab91 100644 --- a/packaging/tests/tas-webdav/src/test/java/org/alfresco/webdav/WebDavTest.java +++ b/packaging/tests/tas-webdav/src/test/java/org/alfresco/webdav/WebDavTest.java @@ -1,17 +1,25 @@ package org.alfresco.webdav; +import java.lang.reflect.Method; + import org.alfresco.utility.data.DataContent; import org.alfresco.utility.data.DataSite; import org.alfresco.utility.data.DataUser; +import org.alfresco.utility.LogFactory; import org.alfresco.utility.network.ServerHealth; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; +import org.slf4j.Logger; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeSuite; @ContextConfiguration("classpath:alfresco-webdav-context.xml") public abstract class WebDavTest extends AbstractTestNGSpringContextTests { + private static final Logger LOG = LogFactory.getLogger(); + @Autowired protected DataSite dataSite; @@ -36,4 +44,16 @@ public abstract class WebDavTest extends AbstractTestNGSpringContextTests // The webdav protocol is enabled by default. //webDavProtocol.assertThat().protocolIsEnabled(); } -} + + @BeforeMethod(alwaysRun=true) + public void showStartTestInfo(Method method) + { + LOG.info(String.format("*** STARTING Test: [%s] ***", method.getName())); + } + + @AfterMethod(alwaysRun=true) + public void showEndTestInfo(Method method) + { + LOG.info(String.format("*** ENDING Test: [%s] ***", method.getName())); + } +} \ No newline at end of file diff --git a/packaging/tests/tas-webdav/src/test/resources/log4j.properties b/packaging/tests/tas-webdav/src/test/resources/log4j.properties deleted file mode 100644 index 00e9b5a114..0000000000 --- a/packaging/tests/tas-webdav/src/test/resources/log4j.properties +++ /dev/null @@ -1,26 +0,0 @@ -# Root logger option -log4j.rootLogger=INFO, file, stdout - -# Direct log messages to a log file -log4j.appender.file=org.apache.log4j.RollingFileAppender -log4j.appender.file.File=./target/reports/alfresco-tas.log -log4j.appender.file.MaxBackupIndex=10 -log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n - -# Direct log messages to stdout -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target=System.out -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n - -# TestRail particular log file -# Direct log messages to a log file -log4j.appender.testrailLog=org.apache.log4j.RollingFileAppender -log4j.appender.testrailLog.File=./target/reports/alfresco-testrail.log -log4j.appender.testrailLog.MaxBackupIndex=10 -log4j.appender.testrailLog.layout=org.apache.log4j.PatternLayout -log4j.appender.testrailLog.layout.ConversionPattern=%d{HH:mm:ss} %-5p %c{1}:%L - %m%n - -log4j.category.testrail=INFO, testrailLog -log4j.additivity.testrail=false \ No newline at end of file diff --git a/packaging/tests/tas-webdav/src/test/resources/log4j2.properties b/packaging/tests/tas-webdav/src/test/resources/log4j2.properties new file mode 100644 index 0000000000..5e34814028 --- /dev/null +++ b/packaging/tests/tas-webdav/src/test/resources/log4j2.properties @@ -0,0 +1,42 @@ +# Root logger option +rootLogger.level=info +rootLogger.appenderRef.stdout.ref=ConsoleAppender +rootLogger.appenderRef.rolling.ref=RollingAppender + +###### File appender definition ####### +appender.rolling.type=RollingFile +appender.rolling.name=RollingAppender +appender.rolling.fileName=./target/reports/alfresco-tas.log +appender.rolling.filePattern=./target/reports/alfresco-tas.log.%i +appender.rolling.layout.type=PatternLayout +appender.rolling.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n +appender.rolling.policies.type = Policies +appender.rolling.policies.size.type=SizeBasedTriggeringPolicy +appender.rolling.policies.size.size=10MB +appender.rolling.strategy.type=DefaultRolloverStrategy +appender.rolling.strategy.max=10 + +###### Console appender definition ####### +appender.console.type=Console +appender.console.name=ConsoleAppender +appender.console.layout.type=PatternLayout +appender.console.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n + +# TestRail particular log file +# Direct log messages to a log file +logger.testrail.name=testrail +logger.testrail.level=info +logger.testrail.additivity=false +logger.testrail.appenderRef.testrail.ref=TestrailAppender + +appender.testrail.name=TestrailAppender +appender.testrail.type=RollingFile +appender.testrail.fileName=./target/reports/alfresco-testrail.log +appender.testrail.filePattern=./target/reports/alfresco-testrail.log.%i +appender.testrail.layout.type=PatternLayout +appender.testrail.layout.pattern=%d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n +appender.testrail.policies.type=Policies +appender.testrail.policies.size.type=SizeBasedTriggeringPolicy +appender.testrail.policies.size.size=10MB +appender.testrail.strategy.type=DefaultRolloverStrategy +appender.testrail.strategy.max=10 \ No newline at end of file diff --git a/packaging/war/pom.xml b/packaging/war/pom.xml index 6c9ef0a9ce..f5d91b55b0 100644 --- a/packaging/war/pom.xml +++ b/packaging/war/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo-packaging - 20.38-SNAPSHOT + 20.50-SNAPSHOT diff --git a/packaging/war/src/main/resources/log4j2.properties b/packaging/war/src/main/resources/log4j2.properties index bc37e2bd7c..39b84a34b7 100644 --- a/packaging/war/src/main/resources/log4j2.properties +++ b/packaging/war/src/main/resources/log4j2.properties @@ -29,7 +29,6 @@ appender.rolling.policies.time.interval = 1 # Log4j addLoggerMBean as long as the logger exists and has been loaded. # Hibernate - logger.hibernate.name=org.hibernate logger.hibernate.level=error @@ -45,42 +44,29 @@ logger.hibernate-type.level=warn logger.hibernate-cfg-SettingsFactory.name=org.hibernate.cfg.SettingsFactory logger.hibernate-cfg-SettingsFactory.level=warn - - # Spring - logger.springframework.name=org.springframework logger.springframework.level=warn # Turn off Spring remoting warnings that should really be info or debug. - logger.springframework-remoting-support.name=org.springframework.remoting.support logger.springframework-remoting-support.level=error logger.springframework-util.name=org.springframework.util logger.springframework-util.level=error - - # Axis/WSS4J - logger.apache-axis.name=org.apache.axis logger.apache-axis.level=info logger.apache-ws.name=org.apache.ws logger.apache-ws.level=info - - # CXF - logger.apache-cxf.name=org.apache.cxf logger.apache-cxf.level=error - - # MyFaces - logger.apache-myfaces-util-DebugUtils.name=org.apache.myfaces.util.DebugUtils logger.apache-myfaces-util-DebugUtils.level=info @@ -93,17 +79,11 @@ logger.apache-myfaces-application-jsp-JspViewHandlerImpl.level=error logger.apache-myfaces-taglib.name=org.apache.myfaces.taglib logger.apache-myfaces-taglib.level=error - - # log prepared statement cache activity ### - logger.hibernate-ps-PreparedStatementCache.name=org.hibernate.ps.PreparedStatementCache logger.hibernate-ps-PreparedStatementCache.level=info - - # Alfresco - logger.alfresco.name=org.alfresco logger.alfresco.level=error @@ -146,8 +126,6 @@ logger.alfresco-repo-security-sync.level=info logger.alfresco-repo-security-person.name=org.alfresco.repo.security.person logger.alfresco-repo-security-person.level=info - - logger.alfresco-sample.name=org.alfresco.sample logger.alfresco-sample.level=info @@ -172,34 +150,25 @@ logger.alfresco-service-descriptor-DescriptorService.level=info #logger.alfresco-web-page.name=org.alfresco.web.page #logger.alfresco-web-page.level=debug - - logger.alfresco-repo-importer-ImporterBootstrap.name=org.alfresco.repo.importer.ImporterBootstrap logger.alfresco-repo-importer-ImporterBootstrap.level=error #logger.alfresco-repo-importer-ImporterBootstrap.name=org.alfresco.repo.importer.ImporterBootstrap #logger.alfresco-repo-importer-ImporterBootstrap.level=info - - logger.alfresco-repo-admin-patch-PatchExecuter.name=org.alfresco.repo.admin.patch.PatchExecuter logger.alfresco-repo-admin-patch-PatchExecuter.level=info logger.alfresco-repo-domain-patch-ibatis-PatchDAOImpl.name=org.alfresco.repo.domain.patch.ibatis.PatchDAOImpl logger.alfresco-repo-domain-patch-ibatis-PatchDAOImpl.level=info - - # Specific patches - logger.alfresco-repo-admin-patch-impl-DeploymentMigrationPatch.name=org.alfresco.repo.admin.patch.impl.DeploymentMigrationPatch logger.alfresco-repo-admin-patch-impl-DeploymentMigrationPatch.level=info logger.alfresco-repo-version-VersionMigrator.name=org.alfresco.repo.version.VersionMigrator logger.alfresco-repo-version-VersionMigrator.level=info - - logger.alfresco-repo-module-ModuleServiceImpl.name=org.alfresco.repo.module.ModuleServiceImpl logger.alfresco-repo-module-ModuleServiceImpl.level=info @@ -239,74 +208,49 @@ logger.alfresco-enterprise-repo-cluster.level=info logger.alfresco-repo-version-Version2ServiceImpl.name=org.alfresco.repo.version.Version2ServiceImpl logger.alfresco-repo-version-Version2ServiceImpl.level=warn - - #logger.alfresco-web-app-DebugPhaseListener.name=org.alfresco.web.app.DebugPhaseListener #logger.alfresco-web-app-DebugPhaseListener.level=debug logger.alfresco-repo-node-db-NodeStringLengthWorker.name=org.alfresco.repo.node.db.NodeStringLengthWorker logger.alfresco-repo-node-db-NodeStringLengthWorker.level=info - - logger.alfresco-repo-workflow.name=org.alfresco.repo.workflow logger.alfresco-repo-workflow.level=info - - # FTP server debugging - logger.alfresco-ftp-protocol.name=org.alfresco.ftp.protocol logger.alfresco-ftp-protocol.level=error #logger.alfresco-ftp-server.name=org.alfresco.ftp.server #logger.alfresco-ftp-server.level=debug - - # WebDAV debugging - #logger.alfresco-webdav-protocol.name=org.alfresco.webdav.protocol #logger.alfresco-webdav-protocol.level=debug logger.alfresco-webdav-protocol.name=org.alfresco.webdav.protocol logger.alfresco-webdav-protocol.level=info - - # Kerberos servlet filters - #logger.alfresco-web-app-servlet-KerberosAuthenticationFilter.name=org.alfresco.web.app.servlet.KerberosAuthenticationFilter #logger.alfresco-web-app-servlet-KerberosAuthenticationFilter.level=debug #logger.alfresco-repo-webdav-auth-KerberosAuthenticationFilter.name=org.alfresco.repo.webdav.auth.KerberosAuthenticationFilter #logger.alfresco-repo-webdav-auth-KerberosAuthenticationFilter.level=debug - - # File servers - logger.alfresco-fileserver.name=org.alfresco.fileserver logger.alfresco-fileserver.level=warn - - # Repo filesystem debug logging - #logger.alfresco-filesys-repo-ContentDiskDriver.name=org.alfresco.filesys.repo.ContentDiskDriver #logger.alfresco-filesys-repo-ContentDiskDriver.level=debug - - # Integrity message threshold - if 'failOnViolation' is off, then WARNINGS are generated - logger.alfresco-repo-node-integrity.name=org.alfresco.repo.node.integrity logger.alfresco-repo-node-integrity.level=error - - # Authentication - logger.alfresco-filesys-auth-ftp.name=org.alfresco.filesys.auth.ftp logger.alfresco-filesys-auth-ftp.level=warn @@ -323,22 +267,16 @@ logger.alfresco-web-app-servlet.name=org.alfresco.web.app.servlet logger.alfresco-web-app-servlet.level=warn # Used also for brute force attack detection - logger.alfresco-repo-security-authentication.name=org.alfresco.repo.security.authentication logger.alfresco-repo-security-authentication.level=warn - - # Indexer debugging - logger.alfresco-repo-search-Indexer.name=org.alfresco.repo.search.Indexer logger.alfresco-repo-search-Indexer.level=error #logger.alfresco-repo-search-Indexer.name=org.alfresco.repo.search.Indexer #logger.alfresco-repo-search-Indexer.level=debug - - logger.alfresco-repo-search-impl-lucene-index.name=org.alfresco.repo.search.impl.lucene.index logger.alfresco-repo-search-impl-lucene-index.level=error @@ -348,97 +286,62 @@ logger.alfresco-repo-search-impl-lucene-fts-FullTextSearchIndexerImpl.level=warn #logger.alfresco-repo-search-impl-lucene-index.name=org.alfresco.repo.search.impl.lucene.index #logger.alfresco-repo-search-impl-lucene-index.level=debug - - # Audit debugging - #logger.alfresco-repo-audit.name=org.alfresco.repo.audit #logger.alfresco-repo-audit.level=debug #logger.alfresco-repo-audit-model.name=org.alfresco.repo.audit.model #logger.alfresco-repo-audit-model.level=debug - # Property sheet and modelling debugging - # change to error to hide the warnings about missing properties and associations - logger.missingProperties.name=alfresco.missingProperties logger.missingProperties.level=warn - - # Dictionary/Model debugging - logger.alfresco-repo-dictionary.name=org.alfresco.repo.dictionary logger.alfresco-repo-dictionary.level=warn logger.alfresco-repo-dictionary-types-period.name=org.alfresco.repo.dictionary.types.period logger.alfresco-repo-dictionary-types-period.level=warn - - # Virtualization Server Registry - logger.alfresco-mbeans-VirtServerRegistry.name=org.alfresco.mbeans.VirtServerRegistry logger.alfresco-mbeans-VirtServerRegistry.level=error - - # Spring context runtime property setter - logger.alfresco-util-RuntimeSystemPropertiesSetter.name=org.alfresco.util.RuntimeSystemPropertiesSetter logger.alfresco-util-RuntimeSystemPropertiesSetter.level=info - - # Debugging options for clustering - logger.alfresco-repo-content-ReplicatingContentStore.name=org.alfresco.repo.content.ReplicatingContentStore logger.alfresco-repo-content-ReplicatingContentStore.level=error logger.alfresco-repo-content-replication.name=org.alfresco.repo.content.replication logger.alfresco-repo-content-replication.level=error - - #logger.alfresco-repo-deploy-DeploymentServiceImpl.name=org.alfresco.repo.deploy.DeploymentServiceImpl #logger.alfresco-repo-deploy-DeploymentServiceImpl.level=debug - - # Activity service - logger.alfresco-repo-activities.name=org.alfresco.repo.activities logger.alfresco-repo-activities.level=warn - - # User usage tracking - logger.alfresco-repo-usage.name=org.alfresco.repo.usage logger.alfresco-repo-usage.level=info - - # Sharepoint - logger.alfresco-module-vti.name=org.alfresco.module.vti logger.alfresco-module-vti.level=info - - # Forms Engine - logger.alfresco-web-config-forms.name=org.alfresco.web.config.forms logger.alfresco-web-config-forms.level=info logger.alfresco-web-scripts-forms.name=org.alfresco.web.scripts.forms logger.alfresco-web-scripts-forms.level=info - - # CMIS - logger.alfresco-opencmis.name=org.alfresco.opencmis logger.alfresco-opencmis.level=error @@ -460,35 +363,20 @@ logger.apache-chemistry-opencmis-server-impl-browser-CmisBrowserBindingServlet.l logger.apache-chemistry-opencmis-server-impl-atompub-CmisAtomPubServlet.name=org.apache.chemistry.opencmis.server.impl.atompub.CmisAtomPubServlet logger.apache-chemistry-opencmis-server-impl-atompub-CmisAtomPubServlet.level=off - - # IMAP - logger.alfresco-repo-imap.name=org.alfresco.repo.imap logger.alfresco-repo-imap.level=info - - # JBPM - # Note: non-fatal errors (eg. logged during job execution) should be handled by Alfresco's retrying transaction handler - logger.jbpm-graph-def-GraphElement.name=org.jbpm.graph.def.GraphElement logger.jbpm-graph-def-GraphElement.level=fatal - - #logger.alfresco-repo-googledocs.name=org.alfresco.repo.googledocs #logger.alfresco-repo-googledocs.level=debug - - ###### Scripting ####### - - - # Web Framework - logger.springframework-extensions-webscripts.name=org.springframework.extensions.webscripts logger.springframework-extensions-webscripts.level=info @@ -498,10 +386,7 @@ logger.springframework-extensions-webscripts-ScriptLogger.level=warn logger.springframework-extensions-webscripts-ScriptDebugger.name=org.springframework.extensions.webscripts.ScriptDebugger logger.springframework-extensions-webscripts-ScriptDebugger.level=off - - # Repository - logger.alfresco-repo-web-scripts.name=org.alfresco.repo.web.scripts logger.alfresco-repo-web-scripts.level=warn @@ -520,8 +405,6 @@ logger.alfresco-repo-jscript-ScriptLogger.level=warn logger.alfresco-repo-cmis-rest-CMISTest.name=org.alfresco.repo.cmis.rest.CMISTest logger.alfresco-repo-cmis-rest-CMISTest.level=info - - logger.alfresco-repo-domain-schema-script-ScriptBundleExecutorImpl.name=org.alfresco.repo.domain.schema.script.ScriptBundleExecutorImpl logger.alfresco-repo-domain-schema-script-ScriptBundleExecutorImpl.level=off @@ -531,103 +414,63 @@ logger.alfresco-repo-domain-schema-script-ScriptExecutorImpl.level=info logger.alfresco-repo-domain-schema-script-DeleteNotExistsExecutor.name=org.alfresco.repo.domain.schema.script.DeleteNotExistsExecutor logger.alfresco-repo-domain-schema-script-DeleteNotExistsExecutor.level=off - - logger.alfresco-repo-search-impl-solr-facet-SolrFacetServiceImpl.name=org.alfresco.repo.search.impl.solr.facet.SolrFacetServiceImpl logger.alfresco-repo-search-impl-solr-facet-SolrFacetServiceImpl.level=info - - # Bulk Filesystem Import Tool - logger.alfresco-repo-bulkimport.name=org.alfresco.repo.bulkimport logger.alfresco-repo-bulkimport.level=warn - - # Freemarker - # Note the freemarker.runtime logger is used to log non-fatal errors that are handled by Alfresco's retrying transaction handler - logger.runtime.name=freemarker.runtime logger.runtime.level= - - # Metadata extraction - logger.alfresco-repo-content-metadata-AbstractMappingMetadataExtracter.name=org.alfresco.repo.content.metadata.AbstractMappingMetadataExtracter logger.alfresco-repo-content-metadata-AbstractMappingMetadataExtracter.level=warn - - # no index support - logger.alfresco-repo-search-impl-noindex-NoIndexIndexer.name=org.alfresco.repo.search.impl.noindex.NoIndexIndexer logger.alfresco-repo-search-impl-noindex-NoIndexIndexer.level=fatal logger.alfresco-repo-search-impl-noindex-NoIndexSearchService.name=org.alfresco.repo.search.impl.noindex.NoIndexSearchService logger.alfresco-repo-search-impl-noindex-NoIndexSearchService.level=fatal - - # lucene index warnings - logger.alfresco-repo-search-impl-lucene-index-IndexInfo.name=org.alfresco.repo.search.impl.lucene.index.IndexInfo logger.alfresco-repo-search-impl-lucene-index-IndexInfo.level=warn - - # Warn about RMI socket bind retries. - logger.alfresco-util-remote-server-socket-HostConfigurableSocketFactory.name=org.alfresco.util.remote.server.socket.HostConfigurableSocketFactory logger.alfresco-util-remote-server-socket-HostConfigurableSocketFactory.level=warn - - logger.alfresco-repo-usage-RepoUsageMonitor.name=org.alfresco.repo.usage.RepoUsageMonitor logger.alfresco-repo-usage-RepoUsageMonitor.level=info - - # Authorization - logger.alfresco-enterprise-repo-authorization-AuthorizationService.name=org.alfresco.enterprise.repo.authorization.AuthorizationService logger.alfresco-enterprise-repo-authorization-AuthorizationService.level=info logger.alfresco-enterprise-repo-authorization-AuthorizationsConsistencyMonitor.name=org.alfresco.enterprise.repo.authorization.AuthorizationsConsistencyMonitor logger.alfresco-enterprise-repo-authorization-AuthorizationsConsistencyMonitor.level=warn - - # HeartBeat - logger.alfresco-heartbeat.name=org.alfresco.heartbeat logger.alfresco-heartbeat.level=info - - # Transformations - #logger.alfresco-repo-content-transform-TransformerDebug.name=org.alfresco.repo.content.transform.TransformerDebug #logger.alfresco-repo-content-transform-TransformerDebug.level=debug - - logger.alfresco-repo-content-transform.name=org.alfresco.repo.content.transform logger.alfresco-repo-content-transform.level=info - - # Repository probes - logger.alfresco-rest-api-probes-ProbeEntityResource.name=org.alfresco.rest.api.probes.ProbeEntityResource logger.alfresco-rest-api-probes-ProbeEntityResource.level=info - - # ActiveMQ logger.apache-activemq-transport-failover.name=org.apache.activemq.transport.failover -logger.apache-activemq-transport-failover.level=warn - +logger.apache-activemq-transport-failover.level=warn \ No newline at end of file diff --git a/pom.xml b/pom.xml index ec13bb64e9..bb6924343e 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 alfresco-community-repo - 20.38-SNAPSHOT + 20.50-SNAPSHOT pom Alfresco Community Repo Parent @@ -109,7 +109,7 @@ 2.7.0 1.1.4 - 3.3.1-DEV-LOG4J2 + 3.4.0-A1 1.6.0-A1 7.3.0 @@ -120,7 +120,7 @@ 8.0.30 8 2.7.4 - 3.0.56 + 3.0.57 5.2.0 1.11 1.7 diff --git a/remote-api/pom.xml b/remote-api/pom.xml index 8dbe2825b2..49a4777626 100644 --- a/remote-api/pom.xml +++ b/remote-api/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo - 20.38-SNAPSHOT + 20.50-SNAPSHOT diff --git a/remote-api/src/main/java/org/alfresco/rest/api/Categories.java b/remote-api/src/main/java/org/alfresco/rest/api/Categories.java index 6c18d06fd2..c5714f7acb 100644 --- a/remote-api/src/main/java/org/alfresco/rest/api/Categories.java +++ b/remote-api/src/main/java/org/alfresco/rest/api/Categories.java @@ -29,14 +29,28 @@ package org.alfresco.rest.api; import java.util.List; import org.alfresco.rest.api.model.Category; +import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; import org.alfresco.rest.framework.resource.parameters.Parameters; import org.alfresco.service.Experimental; -import org.alfresco.service.cmr.repository.NodeRef; @Experimental public interface Categories { - Category getCategoryById(String id, Parameters params); + Category getCategoryById(String id, Parameters parameters); List createSubcategories(String parentCategoryId, List categories, Parameters parameters); + + CollectionWithPagingInfo getCategoryChildren(String parentCategoryId, Parameters parameters); + + /** + * Update category by ID. Currently, it's possible only to update the name of category. + * + * @param id Category ID. + * @param fixedCategoryModel Fixed category model. + * @return Updated category. + */ + Category updateCategoryById(String id, Category fixedCategoryModel); + + void deleteCategoryById(String id, Parameters parameters); + } diff --git a/remote-api/src/main/java/org/alfresco/rest/api/categories/CategoriesEntityResource.java b/remote-api/src/main/java/org/alfresco/rest/api/categories/CategoriesEntityResource.java index 3bde251ca0..23b79067ac 100644 --- a/remote-api/src/main/java/org/alfresco/rest/api/categories/CategoriesEntityResource.java +++ b/remote-api/src/main/java/org/alfresco/rest/api/categories/CategoriesEntityResource.java @@ -41,7 +41,9 @@ import org.alfresco.rest.framework.resource.parameters.Parameters; * @author mpichura */ @EntityResource(name = "categories", title = "Categories") -public class CategoriesEntityResource implements EntityResourceAction.ReadById +public class CategoriesEntityResource implements EntityResourceAction.ReadById, + EntityResourceAction.Update, + EntityResourceAction.Delete { private final Categories categories; @@ -50,12 +52,45 @@ public class CategoriesEntityResource implements EntityResourceAction.ReadById +public class SubcategoriesRelation implements RelationshipResourceAction.Create, + RelationshipResourceAction.Read { private final Categories categories; @@ -48,6 +50,9 @@ public class SubcategoriesRelation implements RelationshipResourceAction.Create< this.categories = categories; } + /** + * POST /categories/{categoryId}/subcategories + */ @WebApiDescription(title = "Create a category", description = "Creates one or more categories under a parent category", successStatus = HttpServletResponse.SC_CREATED) @@ -56,4 +61,16 @@ public class SubcategoriesRelation implements RelationshipResourceAction.Create< { return categories.createSubcategories(parentCategoryId, categoryList, parameters); } + + /** + * GET /categories/{categoryId}/subcategories + */ + @WebApiDescription(title = "List category direct children", + description = "Lists direct children of a parent category", + successStatus = HttpServletResponse.SC_OK) + @Override + public CollectionWithPagingInfo readAll(String parentCategoryId, Parameters params) + { + return categories.getCategoryChildren(parentCategoryId, params); + } } diff --git a/remote-api/src/main/java/org/alfresco/rest/api/impl/CategoriesImpl.java b/remote-api/src/main/java/org/alfresco/rest/api/impl/CategoriesImpl.java index 28f452ea31..515e2263e4 100644 --- a/remote-api/src/main/java/org/alfresco/rest/api/impl/CategoriesImpl.java +++ b/remote-api/src/main/java/org/alfresco/rest/api/impl/CategoriesImpl.java @@ -39,6 +39,8 @@ import org.alfresco.rest.api.model.Node; import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException; +import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; +import org.alfresco.rest.framework.resource.parameters.ListPage; import org.alfresco.rest.framework.resource.parameters.Parameters; import org.alfresco.service.Experimental; import org.alfresco.service.cmr.repository.ChildAssociationRef; @@ -47,14 +49,18 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.CategoryService; import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; @Experimental public class CategoriesImpl implements Categories { static final String NOT_A_VALID_CATEGORY = "Node id does not refer to a valid category"; - static final String NO_PERMISSION_TO_CREATE_A_CATEGORY = "Current user does not have permission to create a category"; + static final String NO_PERMISSION_TO_MANAGE_A_CATEGORY = "Current user does not have permission to manage a category"; + static final String NOT_NULL_OR_EMPTY = "Category name must not be null or empty"; + static final String FIELD_NOT_MATCH = "Category field: %s does not match the original one"; private final AuthorityService authorityService; private final CategoryService categoryService; @@ -72,8 +78,8 @@ public class CategoriesImpl implements Categories @Override public Category getCategoryById(final String id, final Parameters params) { - final NodeRef nodeRef = nodes.validateNode(id); - if (isNotACategory(nodeRef) || isRootCategory(nodeRef)) + final NodeRef nodeRef = getCategoryNodeRef(id); + if (isRootCategory(nodeRef)) { throw new InvalidArgumentException(NOT_A_VALID_CATEGORY, new String[]{id}); } @@ -84,29 +90,99 @@ public class CategoriesImpl implements Categories @Override public List createSubcategories(String parentCategoryId, List categories, Parameters parameters) { - if (!authorityService.hasAdminAuthority()) - { - throw new PermissionDeniedException(NO_PERMISSION_TO_CREATE_A_CATEGORY); - } - final NodeRef parentNodeRef = PATH_ROOT.equals(parentCategoryId) ? - categoryService.getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE) - .orElseThrow(() -> new EntityNotFoundException(parentCategoryId)) : - nodes.validateNode(parentCategoryId); - if (isNotACategory(parentNodeRef)) - { - throw new InvalidArgumentException(NOT_A_VALID_CATEGORY, new String[]{parentCategoryId}); - } + verifyAdminAuthority(); + final NodeRef parentNodeRef = getCategoryNodeRef(parentCategoryId); final List categoryNodeRefs = categories.stream() - .map(c -> categoryService.createCategory(parentNodeRef, c.getName())) + .map(c -> createCategoryNodeRef(parentNodeRef, c)) .collect(Collectors.toList()); return categoryNodeRefs.stream() .map(this::mapToCategory) .collect(Collectors.toList()); } - private boolean isNotACategory(NodeRef nodeRef) + @Override + public CollectionWithPagingInfo getCategoryChildren(String parentCategoryId, Parameters params) { - return !nodes.isSubClass(nodeRef, ContentModel.TYPE_CATEGORY, false); + final NodeRef parentNodeRef = getCategoryNodeRef(parentCategoryId); + final List childCategoriesAssocs = nodeService.getChildAssocs(parentNodeRef).stream() + .filter(ca -> ContentModel.ASSOC_SUBCATEGORIES.equals(ca.getTypeQName())) + .collect(Collectors.toList()); + final List categories = childCategoriesAssocs.stream() + .map(c -> mapToCategory(c.getChildRef())) + .collect(Collectors.toList()); + return ListPage.of(categories, params.getPaging()); + } + + @Override + public Category updateCategoryById(final String id, final Category fixedCategoryModel) + { + verifyAdminAuthority(); + final NodeRef categoryNodeRef = getCategoryNodeRef(id); + if (isRootCategory(categoryNodeRef)) + { + throw new InvalidArgumentException(NOT_A_VALID_CATEGORY, new String[]{id}); + } + + verifyCategoryFields(fixedCategoryModel); + + return mapToCategory(changeCategoryName(categoryNodeRef, fixedCategoryModel.getName())); + } + + @Override + public void deleteCategoryById(String id, Parameters parameters) + { + verifyAdminAuthority(); + final NodeRef nodeRef = getCategoryNodeRef(id); + if (isRootCategory(nodeRef)) + { + throw new InvalidArgumentException(NOT_A_VALID_CATEGORY, new String[]{id}); + } + + nodeService.deleteNode(nodeRef); + } + + private void verifyAdminAuthority() + { + if (!authorityService.hasAdminAuthority()) + { + throw new PermissionDeniedException(NO_PERMISSION_TO_MANAGE_A_CATEGORY); + } + } + + /** + * This method gets category NodeRef for a given category id. + * If '-root-' is passed as category id, then it's retrieved as a call to {@link org.alfresco.service.cmr.search.CategoryService#getRootCategoryNodeRef} + * In all other cases it's retrieved as a node of a category type {@link #validateCategoryNode(String)} + * @param nodeId category node id + * @return NodRef of category node + */ + private NodeRef getCategoryNodeRef(String nodeId) + { + return PATH_ROOT.equals(nodeId) ? + categoryService.getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE) + .orElseThrow(() -> new EntityNotFoundException(nodeId)) : + validateCategoryNode(nodeId); + } + + /** + * Validates if the node exists and is a category. + * @param nodeId (presumably) category node id + * @return category NodeRef + */ + private NodeRef validateCategoryNode(String nodeId) + { + final NodeRef nodeRef = nodes.validateNode(nodeId); + if (isNotACategory(nodeRef)) + { + throw new InvalidArgumentException(NOT_A_VALID_CATEGORY, new String[]{nodeId}); + } + return nodeRef; + } + + private NodeRef createCategoryNodeRef(NodeRef parentNodeRef, Category c) + { + verifyCategoryFields(c); + return categoryService.createCategory(parentNodeRef, c.getName()); } private Category mapToCategory(NodeRef nodeRef) @@ -122,6 +198,11 @@ public class CategoriesImpl implements Categories .create(); } + private boolean isNotACategory(NodeRef nodeRef) + { + return !nodes.isSubClass(nodeRef, ContentModel.TYPE_CATEGORY, false); + } + private boolean isRootCategory(final NodeRef nodeRef) { final List parentAssocs = nodeService.getParentAssocs(nodeRef); @@ -133,4 +214,37 @@ public class CategoriesImpl implements Categories final NodeRef parentRef = nodeService.getPrimaryParent(nodeRef).getParentRef(); return isRootCategory(parentRef) ? PATH_ROOT : parentRef.getId(); } + + /** + * Change category qualified name. + * + * @param categoryNodeRef Category node reference. + * @param newName New name. + * @return Updated category. + */ + private NodeRef changeCategoryName(final NodeRef categoryNodeRef, final String newName) + { + final ChildAssociationRef parentAssociation = nodeService.getPrimaryParent(categoryNodeRef); + if (parentAssociation == null) + { + throw new InvalidArgumentException(NOT_A_VALID_CATEGORY, new String[]{categoryNodeRef.getId()}); + } + + nodeService.setProperty(categoryNodeRef, ContentModel.PROP_NAME, newName); + final QName newQName = QName.createQName(parentAssociation.getQName().getNamespaceURI(), QName.createValidLocalName(newName)); + return nodeService.moveNode(parentAssociation.getChildRef(), parentAssociation.getParentRef(), parentAssociation.getTypeQName(), newQName).getChildRef(); + } + + /** + * Verify if fixed category name is not empty. + * + * @param fixedCategoryModel Fixed category model. + */ + private void verifyCategoryFields(final Category fixedCategoryModel) + { + if (StringUtils.isEmpty(fixedCategoryModel.getName())) + { + throw new InvalidArgumentException(NOT_NULL_OR_EMPTY); + } + } } diff --git a/remote-api/src/main/java/org/alfresco/rest/api/model/Category.java b/remote-api/src/main/java/org/alfresco/rest/api/model/Category.java index d9b3b6d959..b2f6eeb851 100644 --- a/remote-api/src/main/java/org/alfresco/rest/api/model/Category.java +++ b/remote-api/src/main/java/org/alfresco/rest/api/model/Category.java @@ -137,5 +137,4 @@ public class Category return category; } } - } diff --git a/remote-api/src/main/java/org/alfresco/rest/framework/webscripts/ResourceWebScriptPost.java b/remote-api/src/main/java/org/alfresco/rest/framework/webscripts/ResourceWebScriptPost.java index 5fcd23b332..1d3500a596 100644 --- a/remote-api/src/main/java/org/alfresco/rest/framework/webscripts/ResourceWebScriptPost.java +++ b/remote-api/src/main/java/org/alfresco/rest/framework/webscripts/ResourceWebScriptPost.java @@ -44,6 +44,7 @@ import org.alfresco.rest.framework.resource.actions.interfaces.MultiPartResource import org.alfresco.rest.framework.resource.actions.interfaces.MultiPartRelationshipResourceAction; import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction; import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; +import org.alfresco.rest.framework.resource.parameters.Paging; import org.alfresco.rest.framework.resource.parameters.Params; import org.alfresco.rest.framework.resource.parameters.Params.RecognizedParams; import org.alfresco.rest.framework.tools.RecognizedParamsExtractor; @@ -377,7 +378,8 @@ public class ResourceWebScriptPost extends AbstractResourceWebScript implements { if (created !=null && created.size() > 1) { - return CollectionWithPagingInfo.asPagedCollection(created.toArray()); + final Paging pagingAll = Paging.valueOf(0, created.size()); + return CollectionWithPagingInfo.asPaged(pagingAll, created); } else { diff --git a/remote-api/src/test/java/org/alfresco/rest/api/categories/CategoriesEntityResourceTest.java b/remote-api/src/test/java/org/alfresco/rest/api/categories/CategoriesEntityResourceTest.java index 4988ae4f32..56d2e72eca 100644 --- a/remote-api/src/test/java/org/alfresco/rest/api/categories/CategoriesEntityResourceTest.java +++ b/remote-api/src/test/java/org/alfresco/rest/api/categories/CategoriesEntityResourceTest.java @@ -26,7 +26,9 @@ package org.alfresco.rest.api.categories; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; @@ -65,4 +67,17 @@ public class CategoriesEntityResourceTest then(categoriesMock).shouldHaveNoMoreInteractions(); assertEquals(categoryMock, category); } + + @Test + public void testUpdateCategoryById() + { + given(categoriesMock.updateCategoryById(any(), any())).willReturn(categoryMock); + + // when + final Category actualCategory = objectUnderTest.update(CATEGORY_ID, categoryMock, parametersMock); + + then(categoriesMock).should().updateCategoryById(CATEGORY_ID, categoryMock); + then(categoriesMock).shouldHaveNoMoreInteractions(); + assertThat(actualCategory).isNotNull(); + } } diff --git a/remote-api/src/test/java/org/alfresco/rest/api/categories/SubcategoriesRelationTest.java b/remote-api/src/test/java/org/alfresco/rest/api/categories/SubcategoriesRelationTest.java new file mode 100644 index 0000000000..7b1948f7d2 --- /dev/null +++ b/remote-api/src/test/java/org/alfresco/rest/api/categories/SubcategoriesRelationTest.java @@ -0,0 +1,105 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2022 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.api.categories; + +import static org.junit.Assert.assertEquals; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.alfresco.rest.api.Categories; +import org.alfresco.rest.api.model.Category; +import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; +import org.alfresco.rest.framework.resource.parameters.Paging; +import org.alfresco.rest.framework.resource.parameters.Parameters; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class SubcategoriesRelationTest +{ + private static final String PARENT_CATEGORY_ID = "parent-category-node-id"; + private static final String CATEGORY_ID = "category-node-id"; + private static final String CATEGORY_NAME = "categoryName"; + private static final String SUBCATEGORY_NAME_PREFIX = "childCategoryName"; + + @Mock + private Categories categoriesMock; + @Mock + private Parameters parametersMock; + + @InjectMocks + private SubcategoriesRelation objectUnderTest; + + @Test + public void testCreateSubcategory() + { + final Category categoryToCreate = Category.builder().name(CATEGORY_NAME).create(); + final Category category = Category.builder().name(CATEGORY_NAME).parentId(PARENT_CATEGORY_ID).hasChildren(false).id(CATEGORY_ID).create(); + final List categoriesToCreate = List.of(categoryToCreate); + given(categoriesMock.createSubcategories(PARENT_CATEGORY_ID, categoriesToCreate, parametersMock)).willReturn(List.of(category)); + + //when + List categories = objectUnderTest.create(PARENT_CATEGORY_ID, categoriesToCreate, parametersMock); + + then(categoriesMock).should().createSubcategories(PARENT_CATEGORY_ID, categoriesToCreate, parametersMock); + then(categoriesMock).shouldHaveNoMoreInteractions(); + assertEquals(List.of(category), categories); + } + + @Test + public void testGetCategoryChildren() { + final CollectionWithPagingInfo categoryChildren = getCategories(3); + given(categoriesMock.getCategoryChildren(PARENT_CATEGORY_ID, parametersMock)).willReturn(categoryChildren); + + //when + final CollectionWithPagingInfo returnedChildren = objectUnderTest.readAll(PARENT_CATEGORY_ID, parametersMock); + + then(categoriesMock).should().getCategoryChildren(PARENT_CATEGORY_ID, parametersMock); + then(categoriesMock).shouldHaveNoMoreInteractions(); + assertEquals(categoryChildren, returnedChildren); + } + + private CollectionWithPagingInfo getCategories(final int count) + { + return CollectionWithPagingInfo.asPaged(Paging.DEFAULT, + IntStream.range(0, count) + .mapToObj(i -> Category.builder().name(SUBCATEGORY_NAME_PREFIX + "-" + i) + .parentId(PARENT_CATEGORY_ID) + .hasChildren(false) + .id(CATEGORY_ID + "-" + i) + .create()) + .collect(Collectors.toList()) + ); + } +} diff --git a/remote-api/src/test/java/org/alfresco/rest/api/impl/CategoriesImplTest.java b/remote-api/src/test/java/org/alfresco/rest/api/impl/CategoriesImplTest.java index 7787e19812..ccd35b5b17 100644 --- a/remote-api/src/test/java/org/alfresco/rest/api/impl/CategoriesImplTest.java +++ b/remote-api/src/test/java/org/alfresco/rest/api/impl/CategoriesImplTest.java @@ -27,14 +27,26 @@ package org.alfresco.rest.api.impl; import static org.alfresco.rest.api.Nodes.PATH_ROOT; +import static org.alfresco.rest.api.impl.CategoriesImpl.NOT_A_VALID_CATEGORY; +import static org.alfresco.rest.api.impl.CategoriesImpl.NOT_NULL_OR_EMPTY; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import org.alfresco.model.ContentModel; import org.alfresco.rest.api.Nodes; @@ -43,6 +55,7 @@ import org.alfresco.rest.api.model.Node; import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException; +import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; import org.alfresco.rest.framework.resource.parameters.Parameters; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; @@ -50,7 +63,9 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.CategoryService; import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; @@ -64,6 +79,8 @@ public class CategoriesImplTest private static final String CATEGORY_NAME = "categoryName"; private static final String PARENT_ID = "parent-node-id"; private static final String CAT_ROOT_NODE_ID = "cat-root-node-id"; + private static final NodeRef CATEGORY_NODE_REF = createNodeRefWithId(CATEGORY_ID); + private static final Category CATEGORY = createDefaultCategoryWithName(CATEGORY_NAME); @Mock private Nodes nodesMock; @@ -83,12 +100,19 @@ public class CategoriesImplTest @InjectMocks private CategoriesImpl objectUnderTest; + @Before + public void setUp() throws Exception + { + given(authorityServiceMock.hasAdminAuthority()).willReturn(true); + given(nodesMock.validateNode(eq(CATEGORY_ID))).willReturn(CATEGORY_NODE_REF); + given(nodesMock.isSubClass(any(), any(), anyBoolean())).willReturn(true); + } + @Test public void shouldNotGetRootCategoryById() { final NodeRef categoryRootNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CAT_ROOT_NODE_ID); given(nodesMock.validateNode(CAT_ROOT_NODE_ID)).willReturn(categoryRootNodeRef); - given(nodesMock.isSubClass(categoryRootNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(true); given(categoryChildAssociationRefMock.getQName()).willReturn(ContentModel.ASPECT_GEN_CLASSIFIABLE); given(nodeServiceMock.getParentAssocs(categoryRootNodeRef)).willReturn(List.of(categoryChildAssociationRefMock)); @@ -101,7 +125,7 @@ public class CategoriesImplTest then(nodeServiceMock).should().getParentAssocs(categoryRootNodeRef); then(nodeServiceMock).shouldHaveNoMoreInteractions(); then(categoryServiceMock).shouldHaveNoInteractions(); - then(authorityServiceMock).shouldHaveNoMoreInteractions(); + then(authorityServiceMock).shouldHaveNoInteractions(); } @Test @@ -109,7 +133,6 @@ public class CategoriesImplTest { final NodeRef categoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID); given(nodesMock.validateNode(CATEGORY_ID)).willReturn(categoryNodeRef); - given(nodesMock.isSubClass(categoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(true); final Node categoryNode = new Node(); categoryNode.setName(CATEGORY_NAME); categoryNode.setNodeId(CATEGORY_ID); @@ -152,7 +175,6 @@ public class CategoriesImplTest { final NodeRef categoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID); given(nodesMock.validateNode(CATEGORY_ID)).willReturn(categoryNodeRef); - given(nodesMock.isSubClass(categoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(true); final Node categoryNode = new Node(); categoryNode.setName(CATEGORY_NAME); categoryNode.setNodeId(CATEGORY_ID); @@ -225,14 +247,98 @@ public class CategoriesImplTest } @Test - public void testCreateCategoryUnderRoot() + public void testDeleteCategoryById_asAdmin() { given(authorityServiceMock.hasAdminAuthority()).willReturn(true); + final NodeRef categoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID); + given(nodesMock.validateNode(CATEGORY_ID)).willReturn(categoryNodeRef); + given(nodesMock.isSubClass(categoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(true); + final Node categoryNode = new Node(); + categoryNode.setName(CATEGORY_NAME); + categoryNode.setNodeId(CATEGORY_ID); + final NodeRef parentNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID); + categoryNode.setParentId(parentNodeRef); + + //when + objectUnderTest.deleteCategoryById(CATEGORY_ID, parametersMock); + + then(authorityServiceMock).should().hasAdminAuthority(); + then(authorityServiceMock).shouldHaveNoMoreInteractions(); + then(nodesMock).should().validateNode(CATEGORY_ID); + + then(nodesMock).should().isSubClass(categoryNodeRef, ContentModel.TYPE_CATEGORY, false); + then(nodesMock).shouldHaveNoMoreInteractions(); + + then(nodeServiceMock).should().getParentAssocs(categoryNodeRef); + then(nodeServiceMock).should().deleteNode(categoryNodeRef); + then(nodeServiceMock).shouldHaveNoMoreInteractions(); + } + + @Test + public void testDeleteCategoryById_asNonAdminUser() + { + given(authorityServiceMock.hasAdminAuthority()).willReturn(false); + + //when + assertThrows(PermissionDeniedException.class, () -> objectUnderTest.deleteCategoryById(CATEGORY_ID, parametersMock)); + + then(authorityServiceMock).should().hasAdminAuthority(); + then(authorityServiceMock).shouldHaveNoMoreInteractions(); + + then(nodesMock).shouldHaveNoInteractions(); + then(nodeServiceMock).shouldHaveNoInteractions(); + } + + @Test + public void testDeleteCategoryById_nonCategoryId() + { + given(authorityServiceMock.hasAdminAuthority()).willReturn(true); + final NodeRef categoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID); + given(nodesMock.validateNode(CATEGORY_ID)).willReturn(categoryNodeRef); + given(nodesMock.isSubClass(categoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(false); + + //when + assertThrows(InvalidArgumentException.class, () -> objectUnderTest.deleteCategoryById(CATEGORY_ID, parametersMock)); + + then(authorityServiceMock).should().hasAdminAuthority(); + then(authorityServiceMock).shouldHaveNoMoreInteractions(); + + then(nodesMock).should().validateNode(CATEGORY_ID); + then(nodesMock).should().isSubClass(categoryNodeRef, ContentModel.TYPE_CATEGORY, false); + then(nodesMock).shouldHaveNoMoreInteractions(); + } + + @Test + public void testDeleteCategoryById_rootCategory() + { + given(authorityServiceMock.hasAdminAuthority()).willReturn(true); + final NodeRef categoryRootNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CAT_ROOT_NODE_ID); + given(nodesMock.validateNode(CAT_ROOT_NODE_ID)).willReturn(categoryRootNodeRef); + given(nodesMock.isSubClass(categoryRootNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(true); + given(categoryChildAssociationRefMock.getQName()).willReturn(ContentModel.ASPECT_GEN_CLASSIFIABLE); + given(nodeServiceMock.getParentAssocs(categoryRootNodeRef)).willReturn(List.of(categoryChildAssociationRefMock)); + + //when + assertThrows(InvalidArgumentException.class, () -> objectUnderTest.deleteCategoryById(CAT_ROOT_NODE_ID, parametersMock)); + + then(authorityServiceMock).should().hasAdminAuthority(); + then(authorityServiceMock).shouldHaveNoMoreInteractions(); + + then(nodesMock).should().validateNode(CAT_ROOT_NODE_ID); + then(nodesMock).should().isSubClass(categoryRootNodeRef, ContentModel.TYPE_CATEGORY, false); + then(nodesMock).shouldHaveNoMoreInteractions(); + + then(nodeServiceMock).should().getParentAssocs(categoryRootNodeRef); + then(nodeServiceMock).shouldHaveNoMoreInteractions(); + } + + @Test + public void testCreateCategoryUnderRoot() + { final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PATH_ROOT); given(categoryServiceMock.getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE)) .willReturn(Optional.of(parentCategoryNodeRef)); - given(nodesMock.isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(true); - final NodeRef categoryNodeRef = prepareCategoryNodeRef(); + final NodeRef categoryNodeRef = createNodeRefWithId(CATEGORY_ID); given(categoryServiceMock.createCategory(parentCategoryNodeRef, CATEGORY_NAME)).willReturn(categoryNodeRef); given(nodesMock.getNode(CATEGORY_ID)).willReturn(prepareCategoryNode()); final ChildAssociationRef parentAssoc = new ChildAssociationRef(null, parentCategoryNodeRef, null, categoryNodeRef); @@ -246,7 +352,6 @@ public class CategoriesImplTest then(authorityServiceMock).should().hasAdminAuthority(); then(authorityServiceMock).shouldHaveNoMoreInteractions(); - then(nodesMock).should().isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false); then(nodesMock).should().getNode(CATEGORY_ID); then(nodesMock).shouldHaveNoMoreInteractions(); then(nodeServiceMock).should().getPrimaryParent(categoryNodeRef); @@ -271,11 +376,9 @@ public class CategoriesImplTest @Test public void testCreateCategory() { - given(authorityServiceMock.hasAdminAuthority()).willReturn(true); final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID); given(nodesMock.validateNode(PARENT_ID)).willReturn(parentCategoryNodeRef); - given(nodesMock.isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(true); - final NodeRef categoryNodeRef = prepareCategoryNodeRef(); + final NodeRef categoryNodeRef = createNodeRefWithId(CATEGORY_ID); given(categoryServiceMock.createCategory(parentCategoryNodeRef, CATEGORY_NAME)).willReturn(categoryNodeRef); given(nodesMock.getNode(CATEGORY_ID)).willReturn(prepareCategoryNode()); final ChildAssociationRef parentAssoc = new ChildAssociationRef(null, parentCategoryNodeRef, null, categoryNodeRef); @@ -330,7 +433,6 @@ public class CategoriesImplTest @Test public void testCreateCategories_wrongParentNodeType() { - given(authorityServiceMock.hasAdminAuthority()).willReturn(true); final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID); given(nodesMock.validateNode(PARENT_ID)).willReturn(parentCategoryNodeRef); given(nodesMock.isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(false); @@ -351,7 +453,6 @@ public class CategoriesImplTest @Test public void testCreateCategories_nonExistingParentNode() { - given(authorityServiceMock.hasAdminAuthority()).willReturn(true); given(nodesMock.validateNode(PARENT_ID)).willThrow(EntityNotFoundException.class); //when @@ -366,19 +467,331 @@ public class CategoriesImplTest then(categoryServiceMock).shouldHaveNoInteractions(); } - private Node prepareCategoryNode() + @Test + public void testGetRootCategoryChildren() + { + final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PATH_ROOT); + given(categoryServiceMock.getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE)) + .willReturn(Optional.of(parentCategoryNodeRef)); + final int childrenCount = 3; + final List childAssociationRefMocks = prepareChildAssocMocks(childrenCount, parentCategoryNodeRef); + given(nodeServiceMock.getChildAssocs(parentCategoryNodeRef)).willReturn(childAssociationRefMocks); + childAssociationRefMocks.forEach(this::prepareCategoryNodeMocks); + + //when + final CollectionWithPagingInfo categoryChildren = objectUnderTest.getCategoryChildren(PATH_ROOT, parametersMock); + + then(categoryServiceMock).should().getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + then(categoryServiceMock).shouldHaveNoMoreInteractions(); + then(nodeServiceMock).should().getChildAssocs(parentCategoryNodeRef); + childAssociationRefMocks.forEach(ca -> { + then(nodesMock).should().getNode(ca.getChildRef().getId()); + then(nodeServiceMock).should() + .getChildAssocs(ca.getChildRef(), RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false); + then(nodeServiceMock).should().getPrimaryParent(ca.getChildRef()); + }); + then(nodeServiceMock).should(times(childrenCount)).getParentAssocs(parentCategoryNodeRef); + + then(nodesMock).shouldHaveNoMoreInteractions(); + then(nodeServiceMock).shouldHaveNoMoreInteractions(); + + then(authorityServiceMock).shouldHaveNoInteractions(); + + assertEquals(childAssociationRefMocks.size(), categoryChildren.getTotalItems().intValue()); + final List categoryChildrenList = new ArrayList<>(categoryChildren.getCollection()); + assertEquals(childAssociationRefMocks.size(), categoryChildrenList.size()); + IntStream.range(0, childrenCount).forEach(i -> doCategoryAssertions(categoryChildrenList.get(i), i, PATH_ROOT)); + } + + @Test + public void testGetCategoryChildren() + { + final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID); + given(nodesMock.validateNode(PARENT_ID)).willReturn(parentCategoryNodeRef); + given(nodesMock.isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(true); + final int childrenCount = 3; + final List childAssociationRefMocks = prepareChildAssocMocks(childrenCount, parentCategoryNodeRef); + given(nodeServiceMock.getChildAssocs(parentCategoryNodeRef)).willReturn(childAssociationRefMocks); + childAssociationRefMocks.forEach(this::prepareCategoryNodeMocks); + + //when + final CollectionWithPagingInfo categoryChildren = objectUnderTest.getCategoryChildren(PARENT_ID, parametersMock); + + then(nodesMock).should().validateNode(PARENT_ID); + then(nodesMock).should().isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false); + then(nodeServiceMock).should().getChildAssocs(parentCategoryNodeRef); + childAssociationRefMocks.forEach(ca -> { + then(nodesMock).should().getNode(ca.getChildRef().getId()); + then(nodeServiceMock).should() + .getChildAssocs(ca.getChildRef(), RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false); + then(nodeServiceMock).should().getPrimaryParent(ca.getChildRef()); + }); + then(nodeServiceMock).should(times(childrenCount)).getParentAssocs(parentCategoryNodeRef); + + then(nodesMock).shouldHaveNoMoreInteractions(); + then(nodeServiceMock).shouldHaveNoMoreInteractions(); + + then(authorityServiceMock).shouldHaveNoInteractions(); + then(categoryServiceMock).shouldHaveNoInteractions(); + + assertEquals(childAssociationRefMocks.size(), categoryChildren.getTotalItems().intValue()); + final List categoryChildrenList = new ArrayList<>(categoryChildren.getCollection()); + assertEquals(childAssociationRefMocks.size(), categoryChildrenList.size()); + IntStream.range(0, childrenCount).forEach(i -> doCategoryAssertions(categoryChildrenList.get(i), i, PARENT_ID)); + } + + @Test + public void testGetCategoryChildren_noChildren() + { + final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID); + given(nodesMock.validateNode(PARENT_ID)).willReturn(parentCategoryNodeRef); + given(nodesMock.isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(true); + + given(nodeServiceMock.getChildAssocs(parentCategoryNodeRef)).willReturn(Collections.emptyList()); + + + //when + final CollectionWithPagingInfo categoryChildren = objectUnderTest.getCategoryChildren(PARENT_ID, parametersMock); + + then(nodesMock).should().validateNode(PARENT_ID); + then(nodesMock).should().isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false); + then(nodesMock).shouldHaveNoMoreInteractions(); + then(nodeServiceMock).should().getChildAssocs(parentCategoryNodeRef); + then(nodeServiceMock).shouldHaveNoMoreInteractions(); + + then(authorityServiceMock).shouldHaveNoInteractions(); + then(categoryServiceMock).shouldHaveNoInteractions(); + + assertEquals(0, categoryChildren.getTotalItems().intValue()); + } + + @Test + public void testGetCategoryChildren_wrongParentNodeType() + { + final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID); + given(nodesMock.validateNode(PARENT_ID)).willReturn(parentCategoryNodeRef); + given(nodesMock.isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(false); + + //when + assertThrows(InvalidArgumentException.class, () -> objectUnderTest.getCategoryChildren(PARENT_ID, parametersMock)); + + then(nodesMock).should().validateNode(PARENT_ID); + then(nodesMock).should().isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false); + then(nodesMock).shouldHaveNoMoreInteractions(); + + then(nodeServiceMock).shouldHaveNoInteractions(); + then(categoryServiceMock).shouldHaveNoInteractions(); + then(authorityServiceMock).shouldHaveNoInteractions(); + } + + @Test + public void testGetCategoryChildren_nonExistingParentNode() + { + given(nodesMock.validateNode(PARENT_ID)).willThrow(EntityNotFoundException.class); + + //when + assertThrows(EntityNotFoundException.class, () -> objectUnderTest.getCategoryChildren(PARENT_ID, parametersMock)); + + + then(nodesMock).should().validateNode(PARENT_ID); + then(nodesMock).shouldHaveNoMoreInteractions(); + + then(nodeServiceMock).shouldHaveNoInteractions(); + then(categoryServiceMock).shouldHaveNoInteractions(); + then(authorityServiceMock).shouldHaveNoInteractions(); + } + + @Test + public void testUpdateCategoryById() + { + final String categoryNewName = "categoryNewName"; + final Category fixedCategory = createCategoryOnlyWithName(categoryNewName); + final QName categoryQName = createCmQNameOf(CATEGORY_NAME); + final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID); + final ChildAssociationRef parentAssociation = createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, categoryQName); + given(nodesMock.getNode(any())).willReturn(prepareCategoryNode(categoryNewName)); + given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation); + given(nodeServiceMock.moveNode(any(), any(), any(), any())).willReturn(createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, createCmQNameOf(categoryNewName))); + + // when + final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, fixedCategory); + + then(authorityServiceMock).should().hasAdminAuthority(); + then(authorityServiceMock).shouldHaveNoMoreInteractions(); + then(nodesMock).should().validateNode(CATEGORY_ID); + then(nodesMock).should().isSubClass(CATEGORY_NODE_REF, ContentModel.TYPE_CATEGORY, false); + then(nodesMock).should().getNode(CATEGORY_ID); + then(nodesMock).shouldHaveNoMoreInteractions(); + then(nodeServiceMock).should().getParentAssocs(CATEGORY_NODE_REF); + then(nodeServiceMock).should().getChildAssocs(CATEGORY_NODE_REF, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false); + then(nodeServiceMock).should().setProperty(CATEGORY_NODE_REF, ContentModel.PROP_NAME, categoryNewName); + then(nodeServiceMock).should(times(2)).getPrimaryParent(CATEGORY_NODE_REF); + final QName expectedNewQName = createCmQNameOf(categoryNewName); + then(nodeServiceMock).should().moveNode(CATEGORY_NODE_REF, parentCategoryNodeRef, ContentModel.ASSOC_SUBCATEGORIES, expectedNewQName); + then(nodeServiceMock).should().getParentAssocs(parentCategoryNodeRef); + then(nodeServiceMock).shouldHaveNoMoreInteractions(); + then(categoryServiceMock).shouldHaveNoInteractions(); + final Category expectedCategory = createDefaultCategoryWithName(categoryNewName); + assertThat(actualCategory) + .isNotNull().usingRecursiveComparison() + .isEqualTo(expectedCategory); + } + + @Test + public void testUpdateCategoryById_noPermission() + { + given(authorityServiceMock.hasAdminAuthority()).willReturn(false); + + // when + assertThatExceptionOfType(PermissionDeniedException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, CATEGORY)); + + then(nodesMock).shouldHaveNoInteractions(); + then(nodeServiceMock).shouldHaveNoInteractions(); + } + + @Test + public void testUpdateCategoryById_categoryNodeNotFound() + { + given(nodesMock.validateNode(any(String.class))).willThrow(EntityNotFoundException.class); + + // when + assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, CATEGORY)); + + then(nodeServiceMock).shouldHaveNoInteractions(); + } + + @Test + public void testUpdateCategoryById_notACategory() + { + given(nodesMock.isSubClass(any(), any(), eq(false))).willReturn(false); + + // when + assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, CATEGORY)) + .withMessageContaining(NOT_A_VALID_CATEGORY); + + then(nodeServiceMock).shouldHaveNoInteractions(); + } + + @Test + public void testUpdateCategoryById_isRootCategory() + { + given(categoryServiceMock.getRootCategoryNodeRef(any())).willReturn(Optional.of(createNodeRefWithId(PATH_ROOT))); + given(nodeServiceMock.getParentAssocs(any())).willReturn(List.of(categoryChildAssociationRefMock)); + given(categoryChildAssociationRefMock.getQName()).willReturn(ContentModel.ASPECT_GEN_CLASSIFIABLE); + + // when + assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(PATH_ROOT, CATEGORY)) + .withMessageContaining(NOT_A_VALID_CATEGORY); + + then(categoryServiceMock).should().getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + then(categoryServiceMock).shouldHaveNoMoreInteractions(); + } + + private List getInvalidCategoryNames() + { + final List invalidNames = new ArrayList<>(); + invalidNames.add(null); + invalidNames.add(""); + return invalidNames; + } + + @Test + public void testUpdateCategoryById_emptyName() + { + for (String invalidName : getInvalidCategoryNames()) + { + final Category categoryWithoutName = createCategoryOnlyWithName(invalidName); + + // when + assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, categoryWithoutName)) + .withMessageContaining(NOT_NULL_OR_EMPTY); + } + } + + @Test + public void testUpdateCategoryById_notMatchingIdField() + { + final String categoryNewName = "categoryNewName"; + final Category categoryWithInvalidId = createCategoryOnlyWithName(categoryNewName); + categoryWithInvalidId.setId("different-" + CATEGORY_ID); + final QName categoryQName = createCmQNameOf(CATEGORY_NAME); + final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID); + final ChildAssociationRef parentAssociation = createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, categoryQName); + given(nodesMock.getNode(any())).willReturn(prepareCategoryNode(categoryNewName)); + given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation); + given(nodeServiceMock.moveNode(any(), any(), any(), any())).willReturn(createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, createCmQNameOf(categoryNewName))); + + // when + final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, categoryWithInvalidId); + + final Category expectedCategory = createDefaultCategoryWithName(categoryNewName); + assertThat(actualCategory) + .isNotNull().usingRecursiveComparison() + .isEqualTo(expectedCategory); + } + + @Test + public void testUpdateCategoryById_notMatchingParentIdField() + { + final String categoryNewName = "categoryNewName"; + final Category categoryWithInvalidParentId = createCategoryOnlyWithName(categoryNewName); + categoryWithInvalidParentId.setParentId("different-" + PARENT_ID); + final QName categoryQName = createCmQNameOf(CATEGORY_NAME); + final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID); + final ChildAssociationRef parentAssociation = createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, categoryQName); + given(nodesMock.getNode(any())).willReturn(prepareCategoryNode(categoryNewName)); + given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation); + given(nodeServiceMock.moveNode(any(), any(), any(), any())).willReturn(createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, createCmQNameOf(categoryNewName))); + + // when + final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, categoryWithInvalidParentId); + + final Category expectedCategory = createDefaultCategoryWithName(categoryNewName); + assertThat(actualCategory) + .isNotNull().usingRecursiveComparison() + .isEqualTo(expectedCategory); + } + + @Test + public void testUpdateCategoryById_notMatchingHasChildrenField() + { + final String categoryNewName = "categoryNewName"; + final Category categoryWithInvalidHasChildren = createCategoryOnlyWithName(categoryNewName); + categoryWithInvalidHasChildren.setHasChildren(true); + final QName categoryQName = createCmQNameOf(CATEGORY_NAME); + final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID); + final ChildAssociationRef parentAssociation = createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, categoryQName); + given(nodesMock.getNode(any())).willReturn(prepareCategoryNode(categoryNewName)); + given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation); + given(nodeServiceMock.moveNode(any(), any(), any(), any())).willReturn(createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, createCmQNameOf(categoryNewName))); + + // when + final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, categoryWithInvalidHasChildren); + + final Category expectedCategory = createDefaultCategoryWithName(categoryNewName); + assertThat(actualCategory) + .isNotNull().usingRecursiveComparison() + .isEqualTo(expectedCategory); + } + + private Node prepareCategoryNode(final String name, final String id, final NodeRef parentNodeRef) { final Node categoryNode = new Node(); - categoryNode.setName(CATEGORY_NAME); - categoryNode.setNodeId(CATEGORY_ID); - final NodeRef parentNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID); + categoryNode.setName(name); + categoryNode.setNodeId(id); categoryNode.setParentId(parentNodeRef); return categoryNode; } - private NodeRef prepareCategoryNodeRef() + private Node prepareCategoryNode(final String name) { - return new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID); + final NodeRef parentNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID); + return prepareCategoryNode(name, CATEGORY_ID, parentNodeRef); + } + + private Node prepareCategoryNode() + { + return prepareCategoryNode(CATEGORY_NAME); } private List prepareCategories() @@ -387,4 +800,69 @@ public class CategoriesImplTest .name(CATEGORY_NAME) .create()); } + + private List prepareChildAssocMocks(final int count, NodeRef parentCategoryNodeRef) + { + return IntStream.range(0, count).mapToObj(i -> { + ChildAssociationRef dummyChildAssocMock = mock(ChildAssociationRef.class); + given(dummyChildAssocMock.getTypeQName()).willReturn(ContentModel.ASSOC_SUBCATEGORIES); + given(dummyChildAssocMock.getChildRef()) + .willReturn(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID + "-" + i)); + given(dummyChildAssocMock.getParentRef()).willReturn(parentCategoryNodeRef); + return dummyChildAssocMock; + }).collect(Collectors.toList()); + } + + private void prepareCategoryNodeMocks(ChildAssociationRef childAssociationRef) + { + final NodeRef childRef = childAssociationRef.getChildRef(); + final String id = childRef.getId(); + final String name = id.replace(CATEGORY_ID, CATEGORY_NAME); + final NodeRef parentRef = childAssociationRef.getParentRef(); + given(nodesMock.getNode(id)).willReturn(prepareCategoryNode(name, id, parentRef)); + final ChildAssociationRef parentAssoc = new ChildAssociationRef(null, parentRef, null, childRef); + given(nodeServiceMock.getPrimaryParent(childRef)).willReturn(parentAssoc); + given(nodeServiceMock.getParentAssocs(parentRef)).willReturn(List.of(parentAssoc)); + } + + private void doCategoryAssertions(final Category category, final int index, final String parentId) + { + final Category expectedCategory = Category.builder() + .id(CATEGORY_ID + "-" + index) + .name(CATEGORY_NAME + "-" + index) + .parentId(parentId) + .hasChildren(false) + .create(); + assertEquals(expectedCategory, category); + } + + private static NodeRef createNodeRefWithId(final String id) + { + return new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, id); + } + + private static Category createCategoryOnlyWithName(final String name) + { + return Category.builder().name(name).create(); + } + + private static Category createDefaultCategoryWithName(final String name) + { + return Category.builder() + .id(CATEGORY_ID) + .name(name) + .parentId(PARENT_ID) + .hasChildren(false) + .create(); + } + + private static QName createCmQNameOf(final String name) + { + return QName.createQName(ContentModel.TYPE_CATEGORY.getNamespaceURI(), QName.createValidLocalName(name)); + } + + private static ChildAssociationRef createAssociationOf(final NodeRef parentNode, final NodeRef childNode, final QName childNodeName) + { + return new ChildAssociationRef(ContentModel.ASSOC_SUBCATEGORIES, parentNode, childNodeName, childNode); + } } diff --git a/remote-api/src/test/resources/log4j.properties b/remote-api/src/test/resources/log4j.properties deleted file mode 100644 index e8b1b86823..0000000000 --- a/remote-api/src/test/resources/log4j.properties +++ /dev/null @@ -1,25 +0,0 @@ -log4j.rootLogger=error, Console - -log4j.appender.Console=org.apache.log4j.ConsoleAppender -log4j.appender.Console.layout=org.apache.log4j.PatternLayout -log4j.appender.Console.layout.ConversionPattern=%d{ISO8601} %x %-5p [%c{3}] [%t] %m%n - -log4j.logger.org.alfresco=WARN -log4j.logger.org.alfresco.rest.api=DEBUG -log4j.logger.org.eclipse.jetty.util.log=INFO - -# Renditions and Transforms -log4j.logger.org.alfresco.repo.content.transform.TransformerDebug=debug - -log4j.logger.org.alfresco.repo.rendition2=debug -#log4j.logger.org.alfresco.repo.rendition2.LocalTransformClient=debug -#log4j.logger.org.alfresco.repo.rendition.RenditionServiceImpl=debug -#log4j.logger.org.alfresco.enterprise.repo.rendition2.RemoteTransformClient=debug -log4j.logger.org.alfresco.repo.thumbnail.ThumbnailServiceImplTest=DEBUG -log4j.logger.org.alfresco.repo.rendition2.RenditionService2Impl=DEBUG - -#log4j.logger.org.alfresco.repo.content.transform.LocalTransformServiceRegistry=debug -#log4j.logger.org.alfresco.enterprise.repo.rendition2.RemoteTransformServiceRegistry=debug -#log4j.logger.org.alfresco.repo.rendition2.RenditionDefinitionRegistry2Impl=debug -#log4j.logger.org.alfresco.repo.content.MimetypeMap=debug -#log4j.logger.org.alfresco.repo.content.transform.LocalTransform=trace diff --git a/remote-api/src/test/resources/log4j2.properties b/remote-api/src/test/resources/log4j2.properties new file mode 100644 index 0000000000..fc0d8a01c2 --- /dev/null +++ b/remote-api/src/test/resources/log4j2.properties @@ -0,0 +1,56 @@ +# Set root logger level to error +rootLogger.level=error +rootLogger.appenderRef.stdout.ref=ConsoleAppender + +###### Console appender definition ####### +# All outputs currently set to be a ConsoleAppender. +appender.console.type=Console +appender.console.name=ConsoleAppender +appender.console.layout.type=PatternLayout +appender.console.layout.pattern=%d{ISO8601} %x %-5p [%c{3}] [%t] %replace{%m}{[\r\n]+}{}%n + +logger.alfresco.name=org.alfresco +logger.alfresco.level=warn + +logger.alfresco-rest-api.name=org.alfresco.rest.api +logger.alfresco-rest-api.level=debug + +logger.eclipse-jetty-util-log.name=org.eclipse.jetty.util.log +logger.eclipse-jetty-util-log.level=info + +# Renditions and Transforms +logger.alfresco-repo-content-transform-TransformerDebug.name=org.alfresco.repo.content.transform.TransformerDebug +logger.alfresco-repo-content-transform-TransformerDebug.level=debug + +logger.alfresco-repo-rendition2.name=org.alfresco.repo.rendition2 +logger.alfresco-repo-rendition2.level=debug + +#logger.alfresco-repo-rendition2-LocalTransformClient.name=org.alfresco.repo.rendition2.LocalTransformClient +#logger.alfresco-repo-rendition2-LocalTransformClient.level=debug + +#logger.alfresco-repo-rendition-RenditionServiceImpl.name=org.alfresco.repo.rendition.RenditionServiceImpl +#logger.alfresco-repo-rendition-RenditionServiceImpl.level=debug + +#logger.alfresco-enterprise-repo-rendition2-RemoteTransformClient.name=org.alfresco.enterprise.repo.rendition2.RemoteTransformClient +#logger.alfresco-enterprise-repo-rendition2-RemoteTransformClient.level=debug + +logger.alfresco-repo-thumbnail-ThumbnailServiceImplTest.name=org.alfresco.repo.thumbnail.ThumbnailServiceImplTest +logger.alfresco-repo-thumbnail-ThumbnailServiceImplTest.level=debug + +logger.alfresco-repo-rendition2-RenditionService2Impl.name=org.alfresco.repo.rendition2.RenditionService2Impl +logger.alfresco-repo-rendition2-RenditionService2Impl.level=debug + +#logger.alfresco-repo-content-transform-LocalTransformServiceRegistry.name=org.alfresco.repo.content.transform.LocalTransformServiceRegistry +#logger.alfresco-repo-content-transform-LocalTransformServiceRegistry.level=debug + +#logger.alfresco-enterprise-repo-rendition2-RemoteTransformServiceRegistry.name=org.alfresco.enterprise.repo.rendition2.RemoteTransformServiceRegistry +#logger.alfresco-enterprise-repo-rendition2-RemoteTransformServiceRegistry.level=debug + +#logger.alfresco-repo-rendition2-RenditionDefinitionRegistry2Impl.name=org.alfresco.repo.rendition2.RenditionDefinitionRegistry2Impl +#logger.alfresco-repo-rendition2-RenditionDefinitionRegistry2Impl.level=debug + +#logger.alfresco-repo-content-MimetypeMap.name=org.alfresco.repo.content.MimetypeMap +#logger.alfresco-repo-content-MimetypeMap.level=debug + +#logger.alfresco-repo-content-transform-LocalTransform.name=org.alfresco.repo.content.transform.LocalTransform +#logger.alfresco-repo-content-transform-LocalTransform.level=trace \ No newline at end of file diff --git a/repository/pom.xml b/repository/pom.xml index 61c8d1f079..3f1dddf34f 100644 --- a/repository/pom.xml +++ b/repository/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo - 20.38-SNAPSHOT + 20.50-SNAPSHOT diff --git a/repository/src/main/java/org/alfresco/repo/domain/node/NodeDAO.java b/repository/src/main/java/org/alfresco/repo/domain/node/NodeDAO.java index 787587761e..3c1bb2675d 100644 --- a/repository/src/main/java/org/alfresco/repo/domain/node/NodeDAO.java +++ b/repository/src/main/java/org/alfresco/repo/domain/node/NodeDAO.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2022 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -894,6 +894,8 @@ public interface NodeDAO extends NodeBulkLoader Serializable nodeValue, ChildAssocRefQueryCallback resultsCallback); + public abstract List selectAssocsNotLinkedByTwoOtherAssocs( + Long parentNodeId); /** * Used by the re-encryptor to re-encrypt encryptable properties with a new encryption key. */ diff --git a/repository/src/main/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java b/repository/src/main/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java index e1e9e546f9..a387385de0 100644 --- a/repository/src/main/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java +++ b/repository/src/main/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2022 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -139,6 +139,8 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl private static final String SELECT_CHILD_ASSOC_OF_PARENT_BY_NAME = "alfresco.node.select_ChildAssocOfParentByName"; private static final String SELECT_CHILD_ASSOCS_OF_PARENT_WITHOUT_PARENT_ASSOCS_OF_TYPE = "alfresco.node.select_ChildAssocsOfParentWithoutParentAssocsOfType"; + + private static final String SELECT_ASSOCS_NOT_LINKED_BY_TWO_OTHER_ASSOCS = "alfresco.node.select_AssocsNotLinkedByTwoOtherAssocs"; private static final String SELECT_CHILD_ASSOCS_OF_PARENT_WITHOUT_NODE_ASSOCS_OF_TYPE = "alfresco.node.select_ChildAssocsOfParentWithoutNodeAssocsOfType"; private static final String SELECT_PARENT_ASSOCS_OF_CHILD = "alfresco.node.select_ParentAssocsOfChild"; @@ -1415,11 +1417,23 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl assoc.setOrdered(resultsCallback.orderResults()); ChildAssocResultHandler resultHandler = new ChildAssocResultHandler(resultsCallback); - + template.select(SELECT_CHILD_ASSOCS_OF_PARENT_WITHOUT_PARENT_ASSOCS_OF_TYPE, assoc, resultHandler); resultsCallback.done(); } + public List selectAssocsNotLinkedByTwoOtherAssocs( + Long parentNodeId) + { + ChildAssocEntity assoc = new ChildAssocEntity(); + // Parent + NodeEntity parentNode = new NodeEntity(); + parentNode.setId(parentNodeId); + assoc.setParentNode(parentNode); + // Type QName + return template.selectList(SELECT_ASSOCS_NOT_LINKED_BY_TWO_OTHER_ASSOCS, assoc); + } + @Override public List selectChildAssocsWithoutNodeAssocsOfTypes(Long parentNodeId, Long minNodeId, Long maxNodeId, Set assocTypeQNames) { diff --git a/repository/src/main/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/repository/src/main/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java index cd6e33f032..e0c19856b9 100644 --- a/repository/src/main/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java +++ b/repository/src/main/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java @@ -2133,7 +2133,16 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl implements Extens // done return results; } - + + @Extend(traitAPI=NodeServiceTrait.class,extensionAPI=NodeServiceExtension.class) + public List findAssocsNotLinkedByTwoOtherAssocs(NodeRef parent) + { + // Get the parent node + Pair nodePair = getNodePairNotNull(parent); + Long parentNodeId = nodePair.getFirst(); + + return nodeDAO.selectAssocsNotLinkedByTwoOtherAssocs(parentNodeId); + } /** * Specific properties not supported by {@link #getChildAssocsByPropertyValue(NodeRef, QName, Serializable)} */ diff --git a/repository/src/main/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java b/repository/src/main/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java index 8e930b3497..f72f4b5177 100644 --- a/repository/src/main/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java +++ b/repository/src/main/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2022 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -48,7 +48,6 @@ import org.alfresco.query.CannedQueryFactory; import org.alfresco.query.CannedQueryResults; import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingResults; -import org.alfresco.repo.cache.AsynchronouslyRefreshedCache; import org.alfresco.util.cache.RefreshableCacheEvent; import org.alfresco.util.cache.RefreshableCacheListener; import org.alfresco.repo.cache.SimpleCache; @@ -1566,11 +1565,11 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor { return Collections. emptySet(); } - Collection childRefs = nodeService.getChildAssocsWithoutParentAssocsOfType(container, ContentModel.ASSOC_MEMBER); + List rootGroupsNames = nodeService.findAssocsNotLinkedByTwoOtherAssocs(container); Set authorities = new TreeSet(); - for (ChildAssociationRef childRef : childRefs) + for (String rootGroupName : rootGroupsNames) { - addAuthorityNameIfMatches(authorities, childRef.getQName().getLocalName(), type); + addAuthorityNameIfMatches(authorities, rootGroupName, type); } return authorities; } diff --git a/repository/src/main/java/org/alfresco/repo/version/NodeServiceImpl.java b/repository/src/main/java/org/alfresco/repo/version/NodeServiceImpl.java index aa3d2514f8..b8c40cc21b 100644 --- a/repository/src/main/java/org/alfresco/repo/version/NodeServiceImpl.java +++ b/repository/src/main/java/org/alfresco/repo/version/NodeServiceImpl.java @@ -1,28 +1,28 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2022 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ package org.alfresco.repo.version; import java.io.Serializable; @@ -656,13 +656,13 @@ public class NodeServiceImpl implements NodeService, VersionModel throw new UnsupportedOperationException(MSG_UNSUPPORTED); } - /** - * Gets an association by ID. - * - * @param id - * the association id - * @return the association, or null if it does not exist - */ + /** + * Gets an association by ID. + * + * @param id + * the association id + * @return the association, or null if it does not exist + */ public AssociationRef getAssoc(Long id) { return null; @@ -762,7 +762,15 @@ public class NodeServiceImpl implements NodeService, VersionModel public Collection getChildAssocsWithoutParentAssocsOfType(NodeRef parent, QName assocTypeQName) { throw new UnsupportedOperationException(MSG_UNSUPPORTED); - } + } + + /** + * @throws UnsupportedOperationException always + */ + public List findAssocsNotLinkedByTwoOtherAssocs(NodeRef parent) + { + throw new UnsupportedOperationException(MSG_UNSUPPORTED); + } /** * Gets, converts and adds the intrinsic properties to the current node's properties diff --git a/repository/src/main/java/org/alfresco/repo/virtual/bundle/VirtualNodeServiceExtension.java b/repository/src/main/java/org/alfresco/repo/virtual/bundle/VirtualNodeServiceExtension.java index 7c752236e8..1cf368d1f1 100644 --- a/repository/src/main/java/org/alfresco/repo/virtual/bundle/VirtualNodeServiceExtension.java +++ b/repository/src/main/java/org/alfresco/repo/virtual/bundle/VirtualNodeServiceExtension.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2022 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -1467,6 +1467,11 @@ public class VirtualNodeServiceExtension extends VirtualSpringBeanExtension findAssocsNotLinkedByTwoOtherAssocs(NodeRef nodeRef){ + return getTrait().findAssocsNotLinkedByTwoOtherAssocs(nodeRef); + } + @Override public void removeAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName) throws InvalidNodeRefException diff --git a/repository/src/main/resources/alfresco/extension/custom-log4j.properties.sample b/repository/src/main/resources/alfresco/extension/custom-log4j.properties.sample deleted file mode 100644 index 7746d6f5dd..0000000000 --- a/repository/src/main/resources/alfresco/extension/custom-log4j.properties.sample +++ /dev/null @@ -1,3 +0,0 @@ -log4j.logger.org.alfresco.repo.content.transform.TransformerDebug=debug -log4j.logger.org.alfresco.util.exec.RuntimeExecBootstrapBean=debug -log4j.logger.org.alfresco.util.exec.RuntimeExec=debug \ No newline at end of file diff --git a/repository/src/main/resources/alfresco/extension/custom-log4j2.properties.sample b/repository/src/main/resources/alfresco/extension/custom-log4j2.properties.sample new file mode 100644 index 0000000000..5e36cae2cd --- /dev/null +++ b/repository/src/main/resources/alfresco/extension/custom-log4j2.properties.sample @@ -0,0 +1,8 @@ +logger.alfresco-repo-content-transform-TransformerDebug.name=org.alfresco.repo.content.transform.TransformerDebug +logger.alfresco-repo-content-transform-TransformerDebug.level=debug + +logger.alfresco-util-exec-RuntimeExecBootstrapBean.name=org.alfresco.util.exec.RuntimeExecBootstrapBean +logger.alfresco-util-exec-RuntimeExecBootstrapBean.level=debug + +logger.alfresco-util-exec-RuntimeExec.name=org.alfresco.util.exec.RuntimeExec +logger.alfresco-util-exec-RuntimeExec.level=debug \ No newline at end of file diff --git a/repository/src/main/resources/alfresco/ibatis/org.alfresco.repo.domain.dialect.Dialect/node-common-SqlMap.xml b/repository/src/main/resources/alfresco/ibatis/org.alfresco.repo.domain.dialect.Dialect/node-common-SqlMap.xml index 2c1ba5bf32..f208f3d222 100644 --- a/repository/src/main/resources/alfresco/ibatis/org.alfresco.repo.domain.dialect.Dialect/node-common-SqlMap.xml +++ b/repository/src/main/resources/alfresco/ibatis/org.alfresco.repo.domain.dialect.Dialect/node-common-SqlMap.xml @@ -1147,6 +1147,20 @@ parentNode.id = #{parentNode.id} and a.child_node_id IS NULL + +