Compare commits

...

55 Commits

Author SHA1 Message Date
alfresco-build
6351782c1d [maven-release-plugin][skip ci] prepare release 23.4.0.52 2024-10-27 00:07:38 +00:00
Alfresco CI User
4c92868efb [force] Force release for 2024-10-27. 2024-10-27 00:04:27 +00:00
alfresco-build
2cca9ea11b [maven-release-plugin][skip ci] prepare for next development iteration 2024-10-25 11:06:31 +00:00
alfresco-build
e12001e4d1 [maven-release-plugin][skip ci] prepare release 23.4.0.51 2024-10-25 11:06:29 +00:00
dependabot[bot]
a57607f728 Bump org.springframework:spring-context from 6.1.13 to 6.1.14 (#3002)
Bumps [org.springframework:spring-context](https://github.com/spring-projects/spring-framework) from 6.1.13 to 6.1.14.
- [Release notes](https://github.com/spring-projects/spring-framework/releases)
- [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.13...v6.1.14)

---
updated-dependencies:
- dependency-name: org.springframework:spring-context
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-25 10:49:56 +02:00
dependabot[bot]
a79af2cac0 Bump org.springframework.security:spring-security-bom (#3004)
Bumps [org.springframework.security:spring-security-bom](https://github.com/spring-projects/spring-security) from 6.3.3 to 6.3.4.
- [Release notes](https://github.com/spring-projects/spring-security/releases)
- [Changelog](https://github.com/spring-projects/spring-security/blob/main/RELEASE.adoc)
- [Commits](https://github.com/spring-projects/spring-security/compare/6.3.3...6.3.4)

---
updated-dependencies:
- dependency-name: org.springframework.security:spring-security-bom
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-25 10:14:37 +02:00
alfresco-build
be807c5b19 [maven-release-plugin][skip ci] prepare for next development iteration 2024-10-24 11:58:56 +00:00
alfresco-build
ae03e7076e [maven-release-plugin][skip ci] prepare release 23.4.0.50 2024-10-24 11:58:54 +00:00
Damian Ujma
26e394c398 ACS-6670 Change nodesSharedCache to fully-distributed (#3007) 2024-10-24 13:00:04 +02:00
alfresco-build
200aa95784 [maven-release-plugin][skip ci] prepare for next development iteration 2024-10-20 00:07:31 +00:00
alfresco-build
4eeabb3dbd [maven-release-plugin][skip ci] prepare release 23.4.0.49 2024-10-20 00:07:29 +00:00
Alfresco CI User
227bbe4fd8 [force] Force release for 2024-10-20. 2024-10-20 00:04:38 +00:00
alfresco-build
1461a04a3d [maven-release-plugin][skip ci] prepare for next development iteration 2024-10-16 13:08:10 +00:00
alfresco-build
52008dc139 [maven-release-plugin][skip ci] prepare release 23.4.0.48 2024-10-16 13:08:09 +00:00
rrajoria
f2a10052e4 Bump aos version 3.2.0-A1 (#2999) 2024-10-16 17:57:11 +05:30
alfresco-build
add64e0cb6 [maven-release-plugin][skip ci] prepare for next development iteration 2024-10-13 00:07:36 +00:00
alfresco-build
14511e2621 [maven-release-plugin][skip ci] prepare release 23.4.0.47 2024-10-13 00:07:34 +00:00
Alfresco CI User
42e0c93121 [force] Force release for 2024-10-13. 2024-10-13 00:04:33 +00:00
alfresco-build
715bc273ee [maven-release-plugin][skip ci] prepare for next development iteration 2024-10-11 12:42:44 +00:00
alfresco-build
812541870e [maven-release-plugin][skip ci] prepare release 23.4.0.46 2024-10-11 12:42:39 +00:00
dependabot[bot]
9aa5051826 Bump commons-io:commons-io from 2.16.1 to 2.17.0 (#2928)
Bumps commons-io:commons-io from 2.16.1 to 2.17.0.

---
updated-dependencies:
- dependency-name: commons-io:commons-io
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-11 14:00:02 +02:00
alfresco-build
54580b4aeb [maven-release-plugin][skip ci] prepare for next development iteration 2024-10-10 09:55:40 +00:00
alfresco-build
2b1b6091a3 [maven-release-plugin][skip ci] prepare release 23.4.0.45 2024-10-10 09:55:37 +00:00
Aleksandra Onych
74a147ab3f [ACS-8862] Bump Keycloak to 25.0.6 (#2983) 2024-10-10 11:13:08 +02:00
alfresco-build
07f0595f5a [maven-release-plugin][skip ci] prepare for next development iteration 2024-10-09 16:57:03 +00:00
alfresco-build
e3422ea6a5 [maven-release-plugin][skip ci] prepare release 23.4.0.44 2024-10-09 16:57:00 +00:00
Eva Vasques
f4103c242f MNT-24641 Avoid duplicate key error on content upload (#2984)
MNT-24641
* On createOrGetByValue in EntityLookupCache, also cache by value
* Created getCachedEntityByValue that attempt to retrieve the value only from cache
* On attempt to create content URL, first check cache before attempting to create in the database avoiding a duplicate key
2024-10-09 17:07:10 +01:00
alfresco-build
34fb5e9dd9 [maven-release-plugin][skip ci] prepare for next development iteration 2024-10-09 07:03:55 +00:00
alfresco-build
f6cf0670c1 [maven-release-plugin][skip ci] prepare release 23.4.0.43 2024-10-09 07:03:53 +00:00
rrajoria
c7bd036030 Update aos version (#2982)
Update aos version to 4.0.0-A1 with Spring 6.1 upgrade
2024-10-09 11:53:51 +05:30
alfresco-build
b20c573040 [maven-release-plugin][skip ci] prepare for next development iteration 2024-10-08 15:11:21 +00:00
alfresco-build
6568885c10 [maven-release-plugin][skip ci] prepare release 23.4.0.42 2024-10-08 15:11:17 +00:00
dependabot[bot]
31237135c5 Bump org.apache.maven.plugins:maven-failsafe-plugin from 3.5.0 to 3.5.1 (#2979)
Bumps [org.apache.maven.plugins:maven-failsafe-plugin](https://github.com/apache/maven-surefire) from 3.5.0 to 3.5.1.
- [Release notes](https://github.com/apache/maven-surefire/releases)
- [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.5.0...surefire-3.5.1)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-failsafe-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-08 16:30:28 +02:00
alfresco-build
d528ed1e97 [maven-release-plugin][skip ci] prepare for next development iteration 2024-10-06 00:07:53 +00:00
alfresco-build
bb207340fd [maven-release-plugin][skip ci] prepare release 23.4.0.41 2024-10-06 00:07:51 +00:00
Alfresco CI User
314e1aeb64 [force] Force release for 2024-10-06. 2024-10-06 00:04:26 +00:00
alfresco-build
9846f7b04f [maven-release-plugin][skip ci] prepare for next development iteration 2024-10-04 18:40:05 +00:00
alfresco-build
6e442e93b8 [maven-release-plugin][skip ci] prepare release 23.4.0.40 2024-10-04 18:40:03 +00:00
Tom Page
fb3c57aab4 Merge pull request #2969 from Alfresco/feature/MNT-24637_IncludeAspectNames
MNT-24637 Add include=aspectNames to favourites API.
2024-10-04 18:59:35 +01:00
Tom Page
093b3281fb MNT-24637 PMD fixes. 2024-10-04 15:54:57 +01:00
Tom Page
3b027c6c36 MNT-24637 Include aspectNames in TAS model. 2024-10-04 15:45:54 +01:00
Tom Page
f193309e4c MNT-24637 Add include=aspectNames to favourites API. 2024-10-04 15:22:38 +01:00
Tom Page
7668849a59 MNT-24637 Pre-commit formatting. 2024-10-04 15:21:53 +01:00
alfresco-build
1350e68c29 [maven-release-plugin][skip ci] prepare for next development iteration 2024-10-04 13:13:41 +00:00
alfresco-build
ea63cf76e5 [maven-release-plugin][skip ci] prepare release 23.4.0.39 2024-10-04 13:13:39 +00:00
Piotr Żurek
674fa8d7e0 ACS-8843 Investigate Java 21 compatibility (#2960)
* [ACS-8843] Delegate creation of proxy to one place

Co-authored-by: Kacper Magdziarz <kacper.magdziarz@hyland.com>
2024-10-04 14:31:56 +02:00
alfresco-build
60a31112ea [maven-release-plugin][skip ci] prepare for next development iteration 2024-10-04 06:24:17 +00:00
alfresco-build
67d8807529 [maven-release-plugin][skip ci] prepare release 23.4.0.38 2024-10-04 06:24:15 +00:00
MohinishSah
dda1fd6ea3 Merge pull request #2968 from Alfresco/fix/APPS-3046
Update surf-webscript version to 9.4
2024-10-04 11:11:30 +05:30
rrajoria
7a937f1e51 Update surf-webscript version to 9.4 2024-10-03 18:10:40 +05:30
alfresco-build
187e9138da [maven-release-plugin][skip ci] prepare for next development iteration 2024-10-03 11:33:20 +00:00
alfresco-build
b8c9605ae6 [maven-release-plugin][skip ci] prepare release 23.4.0.37 2024-10-03 11:33:18 +00:00
Piotr Żurek
8a1d8dba94 ACS-8870 Propagate latest ATS/AIS (#2967) 2024-10-03 12:53:17 +02:00
mikolajbrzezinski
b2c87aa22d ACS-8867 Add time-out to "Set up the environment" steps (#2955)
* ACS-8867 Add time-out to set up steps
2024-10-03 12:05:59 +02:00
alfresco-build
3748482f51 [maven-release-plugin][skip ci] prepare for next development iteration 2024-10-03 08:34:12 +00:00
41 changed files with 3362 additions and 3008 deletions

View File

@@ -266,6 +266,7 @@ jobs:
- name: "Set transformers tag"
run: echo "TRANSFORMERS_TAG=$(mvn help:evaluate -Dexpression=dependency.alfresco-transform-core.version -q -DforceStdout)" >> $GITHUB_ENV
- name: "Set up the environment"
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile ${{ matrix.compose-profile }} up -d
- name: "Prepare Report Portal"
if: github.ref_name == 'master'
@@ -874,6 +875,7 @@ jobs:
echo "HOSTNAME_VERIFICATION_DISABLED=false" >> "$GITHUB_ENV"
fi
- name: "Set up the environment"
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile ${{ matrix.compose-profile }} up -d
- name: "Prepare Report Portal"
if: github.ref_name == 'master'
@@ -975,6 +977,7 @@ jobs:
bash ./scripts/ci/init.sh
bash ./scripts/ci/build.sh
- name: "Set up the environment"
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
run: |
${{ env.TAS_SCRIPTS }}/start-compose.sh ${{ env.TAS_ENVIRONMENT }}/docker-compose-minimal+transforms.yml
${{ env.TAS_SCRIPTS }}/wait-for-alfresco-start.sh "http://localhost:8082/alfresco"

View File

@@ -133,21 +133,21 @@
"filename": ".github/workflows/ci.yml",
"hashed_secret": "b86dc2f033a63f2b7b9e7d270ab806d2910d7572",
"is_verified": false,
"line_number": 292
"line_number": 293
},
{
"type": "Secret Keyword",
"filename": ".github/workflows/ci.yml",
"hashed_secret": "1bfb0e20f886150ba59b853bcd49dea893e00966",
"is_verified": false,
"line_number": 367
"line_number": 368
},
{
"type": "Secret Keyword",
"filename": ".github/workflows/ci.yml",
"hashed_secret": "128f14373ccfaff49e3664045d3a11b50cbb7b39",
"is_verified": false,
"line_number": 900
"line_number": 902
}
],
".github/workflows/master_release.yml": [
@@ -1888,5 +1888,5 @@
}
]
},
"generated_at": "2024-09-25T10:33:16Z"
"generated_at": "2024-10-02T10:18:47Z"
}

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-amps</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
<modules>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-parent</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
<modules>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-automation-community-repo</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
<build>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-parent</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
<modules>

View File

@@ -8,7 +8,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
<properties>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
<build>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
<modules>

View File

@@ -8,7 +8,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-amps</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
<properties>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
<dependencies>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
<properties>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
<dependencies>

View File

@@ -9,6 +9,6 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
</project>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
<properties>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
<modules>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
<modules>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
<organization>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
<developers>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
<developers>

View File

@@ -8,7 +8,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
<properties>

View File

@@ -36,22 +36,22 @@ public class RestPersonFavoritesModel extends TestModel implements IRestModel<Re
{
@JsonProperty(value = "entry")
RestPersonFavoritesModel model;
@Override
public RestPersonFavoritesModel onModel()
{
return model;
public RestPersonFavoritesModel onModel()
{
return model;
}
private String targetGuid;
private String createdAt;
private List<String> aspectNames;
private List<String> allowableOperations;
private RestTargetModel target;
public RestPersonFavoritesModel()
{
}
{}
public RestPersonFavoritesModel(String targetGuid, String createdAt)
{
@@ -69,7 +69,7 @@ public class RestPersonFavoritesModel extends TestModel implements IRestModel<Re
{
this.targetGuid = targetGuid;
}
public RestTargetModel getTarget()
{
return target;
@@ -90,11 +90,23 @@ public class RestPersonFavoritesModel extends TestModel implements IRestModel<Re
this.createdAt = createdAt;
}
public List<String> getAllowableOperations() {
public List<String> getAspectNames()
{
return aspectNames;
}
public void setAspectNames(List<String> aspectNames)
{
this.aspectNames = aspectNames;
}
public List<String> getAllowableOperations()
{
return allowableOperations;
}
public void setAllowableOperations(List<String> allowableOperations) {
public void setAllowableOperations(List<String> allowableOperations)
{
this.allowableOperations = allowableOperations;
}
}

View File

@@ -2,6 +2,11 @@ package org.alfresco.rest.favorites;
import java.util.List;
import org.hamcrest.Matchers;
import org.springframework.http.HttpStatus;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.alfresco.dataprep.CMISUtil.DocumentType;
import org.alfresco.rest.RestTest;
import org.alfresco.rest.model.RestErrorModel;
@@ -20,14 +25,11 @@ import org.alfresco.utility.model.TestGroup;
import org.alfresco.utility.model.UserModel;
import org.alfresco.utility.testrail.ExecutionType;
import org.alfresco.utility.testrail.annotation.TestRail;
import org.hamcrest.Matchers;
import org.springframework.http.HttpStatus;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class GetFavoritesTests extends RestTest
{
private static final String ALLOWABLE_OPERATIONS = "allowableOperations";
private static final String ASPECT_NAMES = "aspectNames";
private UserModel adminUserModel, userModel;
private SiteModel firstSiteModel;
private SiteModel secondSiteModel;
@@ -56,7 +58,7 @@ public class GetFavoritesTests extends RestTest
firstSiteUsers = dataUser.addUsersWithRolesToSite(firstSiteModel, UserRole.SiteManager, UserRole.SiteCollaborator, UserRole.SiteConsumer,
UserRole.SiteContributor);
secondSiteUsers = dataUser.addUsersWithRolesToSite(secondSiteModel, UserRole.SiteManager, UserRole.SiteCollaborator, UserRole.SiteConsumer,
UserRole.SiteContributor);
UserRole.SiteContributor);
restClient.authenticateUser(userModel);
restClient.withCoreAPI().usingUser(userModel).addSiteToFavorites(firstSiteModel);
@@ -64,9 +66,9 @@ public class GetFavoritesTests extends RestTest
restClient.withCoreAPI().usingUser(userModel).addSiteToFavorites(thirdSiteModel);
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.SANITY,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.SANITY,
description = "Verify Manager user gets favorites with Rest API and status code is 200")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.SANITY })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.SANITY})
public void managerIsAbleToRetrieveFavorites()
{
restClient.authenticateUser(firstSiteUsers.getOneUserWithRole(UserRole.SiteManager));
@@ -79,10 +81,10 @@ public class GetFavoritesTests extends RestTest
.assertThat().entriesListContains("targetGuid", secondSiteModel.getGuid());
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.SANITY,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.SANITY,
description = "Verify user gets status code 401 if authentication call fails")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.SANITY })
// @Bug(id = "MNT-16904", description = "It fails only on environment with tenants")
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.SANITY})
// @Bug(id = "MNT-16904", description = "It fails only on environment with tenants")
public void userIsNotAbleToRetrieveFavoritesIfAuthenticationFails()
{
UserModel siteManager = firstSiteUsers.getOneUserWithRole(UserRole.SiteManager);
@@ -91,44 +93,44 @@ public class GetFavoritesTests extends RestTest
restClient.assertStatusCodeIs(HttpStatus.UNAUTHORIZED);
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify Admin user gets favorites sites with Rest API and status code is 200")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void adminIsAbleToRetrieveFavoritesSites()
{
restClient.authenticateUser(adminUserModel).withCoreAPI().usingUser(adminUserModel).addSiteToFavorites(firstSiteModel);
restClient.withCoreAPI().usingUser(adminUserModel).addSiteToFavorites(secondSiteModel);
userFavorites = restClient.withCoreAPI()
.usingAuthUser().where().targetSiteExist().getFavorites();
restClient.assertStatusCodeIs(HttpStatus.OK);
userFavorites.assertThat().entriesListContains("targetGuid", firstSiteModel.getGuid())
.and().entriesListContains("targetGuid", secondSiteModel.getGuid());
.and().entriesListContains("targetGuid", secondSiteModel.getGuid());
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify Admin user gets favorites folders with Rest API and status code is 200")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void adminIsAbleToRetrieveFavoritesFolders()
{
restClient.authenticateUser(adminUserModel).withCoreAPI().usingUser(adminUserModel).addFolderToFavorites(firstFolderModel);
restClient.withCoreAPI().usingUser(adminUserModel).addFolderToFavorites(secondFolderModel);
userFavorites = restClient.withCoreAPI()
.usingAuthUser().where().targetFolderExist().getFavorites();
restClient.assertStatusCodeIs(HttpStatus.OK);
userFavorites.assertThat().entriesListContains("targetGuid", firstFolderModel.getNodeRef())
.and().entriesListContains("targetGuid", secondFolderModel.getNodeRef());
.and().entriesListContains("targetGuid", secondFolderModel.getNodeRef());
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify Admin user gets favorites files with Rest API and status code is 200")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void adminIsAbleToRetrieveFavoritesFiles()
{
restClient.authenticateUser(adminUserModel).withCoreAPI().usingUser(adminUserModel).addFileToFavorites(firstFileModel);
restClient.withCoreAPI().usingUser(adminUserModel).addFileToFavorites(secondFileModel);
userFavorites = restClient.withCoreAPI()
.usingAuthUser().where().targetFileExist().getFavorites();
restClient.assertStatusCodeIs(HttpStatus.OK);
@@ -136,87 +138,87 @@ public class GetFavoritesTests extends RestTest
.and().entriesListContains("targetGuid", secondFileModel.getNodeRefWithoutVersion());
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify Collaborator user gets favorites with Rest API and status code is 200")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void collaboratorIsAbleToRetrieveFavorites()
{
restClient.authenticateUser(firstSiteUsers.getOneUserWithRole(UserRole.SiteCollaborator))
.withCoreAPI().usingAuthUser().addSiteToFavorites(firstSiteModel);
.withCoreAPI().usingAuthUser().addSiteToFavorites(firstSiteModel);
restClient.withCoreAPI().usingAuthUser().addSiteToFavorites(secondSiteModel);
userFavorites = restClient.withCoreAPI().usingAuthUser().getFavorites();
restClient.assertStatusCodeIs(HttpStatus.OK);
userFavorites.assertThat().entriesListContains("targetGuid", firstSiteModel.getGuid())
.and().entriesListContains("targetGuid", secondSiteModel.getGuid());
.and().entriesListContains("targetGuid", secondSiteModel.getGuid());
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify Contributor user gets favorites with Rest API and status code is 200")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void contributorIsAbleToRetrieveFavorites()
{
restClient.authenticateUser(firstSiteUsers.getOneUserWithRole(UserRole.SiteContributor))
.withCoreAPI().usingAuthUser().addSiteToFavorites(firstSiteModel);
.withCoreAPI().usingAuthUser().addSiteToFavorites(firstSiteModel);
restClient.withCoreAPI().usingAuthUser().addSiteToFavorites(secondSiteModel);
userFavorites = restClient.withCoreAPI().usingAuthUser().getFavorites();
restClient.assertStatusCodeIs(HttpStatus.OK);
userFavorites.assertThat().entriesListContains("targetGuid", firstSiteModel.getGuid()).and()
.entriesListContains("targetGuid", secondSiteModel.getGuid());
.entriesListContains("targetGuid", secondSiteModel.getGuid());
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify Consumer user gets favorites with Rest API and status code is 200")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void consumerIsAbleToRetrieveFavorites()
{
restClient.authenticateUser(firstSiteUsers.getOneUserWithRole(UserRole.SiteConsumer))
.withCoreAPI().usingAuthUser().addSiteToFavorites(firstSiteModel);
.withCoreAPI().usingAuthUser().addSiteToFavorites(firstSiteModel);
restClient.withCoreAPI().usingAuthUser().addSiteToFavorites(secondSiteModel);
userFavorites = restClient.withCoreAPI().usingAuthUser().getFavorites();
restClient.assertStatusCodeIs(HttpStatus.OK);
userFavorites.assertThat().entriesListContains("targetGuid", firstSiteModel.getGuid())
.assertThat().entriesListContains("targetGuid", secondSiteModel.getGuid());
.assertThat().entriesListContains("targetGuid", secondSiteModel.getGuid());
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify user doesn't have permission to get favorites of another user with Rest API and status code is 404")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void userIsNotAbleToRetrieveFavoritesOfAnotherUser()
{
restClient.authenticateUser(firstSiteUsers.getOneUserWithRole(UserRole.SiteConsumer))
.withCoreAPI().usingUser(firstSiteUsers.getOneUserWithRole(UserRole.SiteCollaborator)).getFavorites();
.withCoreAPI().usingUser(firstSiteUsers.getOneUserWithRole(UserRole.SiteCollaborator)).getFavorites();
restClient.assertStatusCodeIs(HttpStatus.NOT_FOUND)
.assertLastError().containsSummary(String.format(RestErrorModel.ENTITY_NOT_FOUND, firstSiteUsers.getOneUserWithRole(UserRole.SiteCollaborator).getUsername()));
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify user doesn't have permission to get favorites of admin user with Rest API and status code is 200")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void userIsNotAbleToRetrieveFavoritesOfAdminUser()
{
restClient.authenticateUser(firstSiteUsers.getOneUserWithRole(UserRole.SiteConsumer)).withCoreAPI()
.usingUser(adminUserModel).getFavorites();
.usingUser(adminUserModel).getFavorites();
restClient.assertStatusCodeIs(HttpStatus.NOT_FOUND)
.assertLastError().containsSummary(String.format(RestErrorModel.ENTITY_NOT_FOUND, adminUserModel.getUsername()));
.assertLastError().containsSummary(String.format(RestErrorModel.ENTITY_NOT_FOUND, adminUserModel.getUsername()));
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify admin user doesn't have permission to get favorites of another user with Rest API and status code is 200")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void adminIsNotAbleToRetrieveFavoritesOfAnotherUser()
{
restClient.authenticateUser(adminUserModel).withCoreAPI().usingUser(firstSiteUsers.getOneUserWithRole(UserRole.SiteCollaborator))
.getFavorites();
.getFavorites();
restClient.assertStatusCodeIs(HttpStatus.NOT_FOUND)
.assertLastError().containsSummary(String.format(RestErrorModel.ENTITY_NOT_FOUND, firstSiteUsers.getOneUserWithRole(UserRole.SiteCollaborator).getUsername()));
.assertLastError().containsSummary(String.format(RestErrorModel.ENTITY_NOT_FOUND, firstSiteUsers.getOneUserWithRole(UserRole.SiteCollaborator).getUsername()));
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify User gets only favorites sites with Rest API and status code is 200")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void userIsAbleToRetrieveOnlyFavoritesSites()
{
restClient.authenticateUser(secondSiteUsers.getOneUserWithRole(UserRole.SiteManager));
@@ -232,9 +234,9 @@ public class GetFavoritesTests extends RestTest
.and().entriesListDoesNotContain("targetGuid", firstFolderModel.getNodeRef());
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify User gets only favorites files with Rest API and status code is 200")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void userIsAbleToRetrieveOnlyFavoritesFiles()
{
restClient.authenticateUser(firstSiteUsers.getOneUserWithRole(UserRole.SiteCollaborator));
@@ -250,9 +252,9 @@ public class GetFavoritesTests extends RestTest
.and().entriesListDoesNotContain("targetGuid", firstFolderModel.getNodeRef());
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify User gets only favorites folders with Rest API and status code is 200")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void userIsAbleToRetrieveOnlyFavoritesFolders()
{
restClient.authenticateUser(firstSiteUsers.getOneUserWithRole(UserRole.SiteCollaborator));
@@ -268,9 +270,9 @@ public class GetFavoritesTests extends RestTest
.and().entriesListDoesNotContain("targetGuid", firstFileModel.getNodeRefWithoutVersion());
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify User gets only favorites files or folders with Rest API and status code is 200")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void userIsAbleToRetrieveFavoritesFilesOrFolders()
{
restClient.authenticateUser(firstSiteUsers.getOneUserWithRole(UserRole.SiteConsumer));
@@ -289,9 +291,9 @@ public class GetFavoritesTests extends RestTest
.and().entriesListDoesNotContain("targetGuid", firstSiteModel.getGuid());
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify User gets only favorites files or sites with Rest API and status code is 200")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void userIsAbleToRetrieveFavoritesFilesOrSites()
{
restClient.authenticateUser(secondSiteUsers.getOneUserWithRole(UserRole.SiteManager));
@@ -310,9 +312,9 @@ public class GetFavoritesTests extends RestTest
.and().entriesListDoesNotContain("targetGuid", firstFolderModel.getNodeRef());
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify User gets only favorites folders or sites with Rest API and status code is 200")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void userIsAbleToRetrieveFavoritesFoldersOrSites()
{
restClient.authenticateUser(secondSiteUsers.getOneUserWithRole(UserRole.SiteManager));
@@ -331,9 +333,9 @@ public class GetFavoritesTests extends RestTest
.and().entriesListDoesNotContain("targetGuid", firstFileModel.getNodeRefWithoutVersion());
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify User gets all favorites with Rest API and status code is 200")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void userIsAbleToRetrieveAllFavorites()
{
restClient.authenticateUser(secondSiteUsers.getOneUserWithRole(UserRole.SiteCollaborator));
@@ -362,9 +364,9 @@ public class GetFavoritesTests extends RestTest
.and().entriesListDoesNotContain("targetGuid", secondFileModel.getNodeRefWithoutVersion());
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify request for a user with no favorites returns status 200")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void userHasNoFavorites()
{
restClient.authenticateUser(secondSiteUsers.getOneUserWithRole(UserRole.SiteContributor));
@@ -374,9 +376,9 @@ public class GetFavoritesTests extends RestTest
userFavorites.assertThat().entriesListIsEmpty().and().paginationField("totalItems").is("0");
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify request using invalid where parameter returns status 400")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void getFavoritesUsingInvalidWhereParameter()
{
restClient.authenticateUser(firstSiteUsers.getOneUserWithRole(UserRole.SiteConsumer));
@@ -390,9 +392,9 @@ public class GetFavoritesTests extends RestTest
.assertLastError().containsSummary(String.format(RestErrorModel.INVALID_ARGUMENT, "WHERE query"));
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify User gets correct favorites after deleting a favorite")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void checkFavoriteFolderIsRemoved()
{
restClient.authenticateUser(firstSiteUsers.getOneUserWithRole(UserRole.SiteManager));
@@ -408,9 +410,9 @@ public class GetFavoritesTests extends RestTest
.and().paginationField("totalItems").is("2");
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify get favorites specifying -me- string in place of <personid> for request")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void userIsAbleToGetFavoritesWhenUsingMeAsUsername()
{
userFavorites = restClient.authenticateUser(userModel).withCoreAPI().usingMe().getFavorites();
@@ -418,9 +420,9 @@ public class GetFavoritesTests extends RestTest
userFavorites.assertThat().entriesListContains("targetGuid", firstSiteModel.getGuid()).and().entriesListContains("targetGuid", secondSiteModel.getGuid());
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify get favorites using empty for where parameter for request")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void userIsAbleToGetFavoritesWhenUsingEmptyWhereParameter()
{
userFavorites = restClient.authenticateUser(adminUserModel).withCoreAPI().usingAuthUser().where().getFavorites();
@@ -428,27 +430,27 @@ public class GetFavoritesTests extends RestTest
.assertLastError().containsSummary(String.format(RestErrorModel.INVALID_ARGUMENT, "WHERE query"));
}
@TestRail(section = { TestGroup.REST_API,TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify that for invalid maxItems parameter status code returned is 400.")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void checkInvalidMaxItemsStatusCode()
{
restClient.authenticateUser(adminUserModel).withParams("maxItems=AB").withCoreAPI().usingUser(adminUserModel).getFavorites();
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST).assertLastError().containsSummary("Invalid paging parameter");
}
@TestRail(section = { TestGroup.REST_API,TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify that for invalid skipCount parameter status code returned is 400.")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void checkInvalidSkipCountStatusCode()
{
restClient.authenticateUser(adminUserModel).withParams("skipCount=AB").withCoreAPI().usingUser(adminUserModel).getFavorites();
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST).assertLastError().containsSummary("Invalid paging parameter");
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify get favorites when using invalid network id for non-tenant user")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void getFavoritesWhenNetworkIdIsInvalid()
{
UserModel networkUserModel = dataUser.createRandomTestUser();
@@ -457,9 +459,9 @@ public class GetFavoritesTests extends RestTest
restClient.assertStatusCodeIs(HttpStatus.NOT_FOUND).assertLastError().containsSummary(String.format(RestErrorModel.ENTITY_NOT_FOUND, networkUserModel.getUsername()));
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify get favorites using AND instead of OR in where parameter for request")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void userIsNotAbleToGetFavoritesWhenUsingANDInWhereParameter()
{
userFavorites = restClient.withCoreAPI().usingAuthUser().where().targetFolderExist().invalidWhereParameter("AND").targetFileExist().getFavorites();
@@ -467,9 +469,9 @@ public class GetFavoritesTests extends RestTest
.assertLastError().containsSummary(String.format(RestErrorModel.INVALID_ARGUMENT, "WHERE query"));
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify get favorites using wrong name instead of EXISTS in where parameter for request")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void userIsNotAbleToGetFavoritesWhenUsingWrongWhereParameter()
{
userFavorites = restClient.withCoreAPI().usingAuthUser().where().invalidWhereParameter("EXIST((target/site))").targetFileExist().getFavorites();
@@ -477,9 +479,9 @@ public class GetFavoritesTests extends RestTest
.assertLastError().containsSummary(String.format(RestErrorModel.INVALID_ARGUMENT, "WHERE query"));
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify get favorites except the first one for request")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void userIsAbleToGetFavoritesExceptTheFirstOne()
{
userFavorites = restClient.authenticateUser(userModel).withParams("skipCount=1").withCoreAPI().usingUser(userModel).getFavorites();
@@ -490,9 +492,9 @@ public class GetFavoritesTests extends RestTest
.and().entriesListDoesNotContain("targetGuid", thirdSiteModel.getGuid());
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify get first two favorites sites")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void userIsAbleToGetFirstTwoFavorites()
{
userFavorites = restClient.authenticateUser(userModel).withParams("maxItems=2").withCoreAPI().usingUser(userModel).getFavorites();
@@ -506,9 +508,9 @@ public class GetFavoritesTests extends RestTest
.and().field("count").is("2");
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify get favorites sites when using empty values for skipCount and maxItems")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void userIsAbleToGetFavoritesWhenSkipCountAndMaxItemsAreEmpty()
{
restClient.authenticateUser(userModel).withParams("skipCount= ").withCoreAPI().usingUser(userModel).getFavorites();
@@ -518,9 +520,9 @@ public class GetFavoritesTests extends RestTest
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST).assertLastError().containsSummary(String.format(RestErrorModel.INVALID_MAXITEMS, " "));
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify the get favorites request for a high value for skipCount parameter")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void userIsAbleToGetFavoritesWithHighSkipCount()
{
userFavorites = restClient.authenticateUser(userModel).withParams("skipCount=999999999").withCoreAPI().usingUser(userModel).getFavorites();
@@ -529,9 +531,9 @@ public class GetFavoritesTests extends RestTest
userFavorites.assertThat().entriesListIsEmpty().assertThat().paginationField("skipCount").is("999999999");
}
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify the get favorites request with properties parameter applied")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void userIsAbleToGetFavoritesWithPropertiesParamApplied()
{
userFavorites = restClient.authenticateUser(userModel).withParams("properties=targetGuid").withCoreAPI().usingUser(userModel).getFavorites();
@@ -541,8 +543,8 @@ public class GetFavoritesTests extends RestTest
restPersonFavoritesModel.assertThat().field("targetGuid").is(thirdSiteModel.getGuid()).and().field("createdAt").isNull();
}
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION, description = "Verify entry details for get favorites response with Rest API")
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION, description = "Verify entry details for get favorites response with Rest API")
public void checkResponseSchemaForGetFavorites()
{
userFavorites = restClient.authenticateUser(userModel).withCoreAPI().usingAuthUser().getFavorites();
@@ -564,8 +566,7 @@ public class GetFavoritesTests extends RestTest
description = "Verify if get favorites response returns allowableOperations object when requested")
public void checkResponsesForGetFavoritesWithAllowableOperations()
{
final RestPersonFavoritesModelsCollection adminFavorites =
restClient.authenticateUser(adminUserModel).withCoreAPI().usingAuthUser().include(ALLOWABLE_OPERATIONS).getFavorites();
final RestPersonFavoritesModelsCollection adminFavorites = restClient.authenticateUser(adminUserModel).withCoreAPI().usingAuthUser().include(ALLOWABLE_OPERATIONS).getFavorites();
restClient.assertStatusCodeIs(HttpStatus.OK);
adminFavorites.getEntries().stream()
@@ -576,18 +577,30 @@ public class GetFavoritesTests extends RestTest
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify the get favorites request with properties parameter applied")
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
public void checkSearchResponseContainsIsFavoriteWhenRequested() throws InterruptedException {
public void checkSearchResponseContainsIsFavoriteWhenRequested() throws InterruptedException
{
final SearchRequest query = new SearchRequest();
final RestRequestQueryModel queryReq = new RestRequestQueryModel();
queryReq.setQuery(firstFileModel.getName());
query.setQuery(queryReq);
query.setInclude(List.of("isFavorite"));
Utility.sleep(500, 60000, () ->
{
restClient.authenticateUser(adminUserModel).withSearchAPI().search(query);
restClient.onResponse().assertThat().body("list.entries.entry[0].isFavorite", Matchers.notNullValue());
}
);
Utility.sleep(500, 60000, () -> {
restClient.authenticateUser(adminUserModel).withSearchAPI().search(query);
restClient.onResponse().assertThat().body("list.entries.entry[0].isFavorite", Matchers.notNullValue());
});
}
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
description = "Verify if get favorites response returns aspectNames when requested")
public void checkResponsesForGetFavoritesWithAspectNames()
{
final RestPersonFavoritesModelsCollection adminFavorites = restClient.authenticateUser(adminUserModel).withCoreAPI().usingAuthUser().include(ASPECT_NAMES).getFavorites();
restClient.assertStatusCodeIs(HttpStatus.OK);
adminFavorites.getEntries().stream()
.map(RestPersonFavoritesModel::onModel)
.forEach(m -> m.assertThat().field(ASPECT_NAMES).isNotEmpty());
}
}

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
<developers>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
<properties>

20
pom.xml
View File

@@ -2,7 +2,7 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>alfresco-community-repo</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
<packaging>pom</packaging>
<name>Alfresco Community Repo Parent</name>
@@ -51,25 +51,25 @@
<dependency.alfresco-server-root.version>7.0.1</dependency.alfresco-server-root.version>
<dependency.activiti-engine.version>5.23.0</dependency.activiti-engine.version>
<dependency.activiti.version>5.23.0</dependency.activiti.version>
<dependency.alfresco-transform-core.version>5.1.4</dependency.alfresco-transform-core.version>
<dependency.alfresco-transform-service.version>4.1.4</dependency.alfresco-transform-service.version>
<dependency.alfresco-transform-core.version>5.1.5-A1</dependency.alfresco-transform-core.version>
<dependency.alfresco-transform-service.version>4.1.5-A1</dependency.alfresco-transform-service.version>
<dependency.alfresco-greenmail.version>7.0</dependency.alfresco-greenmail.version>
<dependency.acs-event-model.version>0.0.33</dependency.acs-event-model.version>
<dependency.aspectj.version>1.9.22.1</dependency.aspectj.version>
<dependency.spring.version>6.1.13</dependency.spring.version>
<dependency.spring-security.version>6.3.3</dependency.spring-security.version>
<dependency.spring.version>6.1.14</dependency.spring.version>
<dependency.spring-security.version>6.3.4</dependency.spring-security.version>
<dependency.antlr.version>3.5.3</dependency.antlr.version>
<dependency.jackson.version>2.17.2</dependency.jackson.version>
<dependency.cxf.version>4.0.5</dependency.cxf.version>
<dependency.opencmis.version>1.0.0-jakarta-1</dependency.opencmis.version>
<dependency.webscripts.version>9.3</dependency.webscripts.version>
<dependency.webscripts.version>9.4</dependency.webscripts.version>
<dependency.bouncycastle.version>1.78.1</dependency.bouncycastle.version>
<dependency.mockito-core.version>5.14.1</dependency.mockito-core.version>
<dependency.assertj.version>3.26.3</dependency.assertj.version>
<dependency.org-json.version>20240303</dependency.org-json.version>
<dependency.commons-dbcp.version>2.12.0</dependency.commons-dbcp.version>
<dependency.commons-io.version>2.16.1</dependency.commons-io.version>
<dependency.commons-io.version>2.17.0</dependency.commons-io.version>
<dependency.gson.version>2.11.0</dependency.gson.version>
<dependency.guava.version>33.3.1-jre</dependency.guava.version>
<dependency.httpclient.version>4.5.14</dependency.httpclient.version>
@@ -113,7 +113,7 @@
<dependency.jakarta-json-path.version>2.9.0</dependency.jakarta-json-path.version>
<dependency.json-smart.version>2.5.1</dependency.json-smart.version>
<alfresco.googledrive.version>4.1.0</alfresco.googledrive.version>
<alfresco.aos-module.version>3.1.0</alfresco.aos-module.version>
<alfresco.aos-module.version>3.2.0-A1</alfresco.aos-module.version>
<alfresco.api-explorer.version>23.3.0</alfresco.api-explorer.version> <!-- Also in alfresco-enterprise-share -->
<alfresco.maven-plugin.version>2.2.0</alfresco.maven-plugin.version>
@@ -154,7 +154,7 @@
<connection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</connection>
<developerConnection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</developerConnection>
<url>https://github.com/Alfresco/alfresco-community-repo</url>
<tag>23.4.0.36</tag>
<tag>23.4.0.52</tag>
</scm>
<distributionManagement>
@@ -1015,7 +1015,7 @@
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.5.0</version>
<version>3.5.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
<dependencies>

View File

@@ -1,460 +1,460 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2019 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api.impl;
import static org.alfresco.rest.api.Nodes.PARAM_INCLUDE_ALLOWABLEOPERATIONS;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.query.PagingResults;
import org.alfresco.repo.favourites.PersonFavourite;
import org.alfresco.repo.site.SiteDoesNotExistException;
import org.alfresco.repo.site.SiteModel;
import org.alfresco.rest.api.Favourites;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.People;
import org.alfresco.rest.api.Sites;
import org.alfresco.rest.api.model.Document;
import org.alfresco.rest.api.model.DocumentTarget;
import org.alfresco.rest.api.model.Favourite;
import org.alfresco.rest.api.model.Folder;
import org.alfresco.rest.api.model.FolderTarget;
import org.alfresco.rest.api.model.Node;
import org.alfresco.rest.api.model.PathInfo;
import org.alfresco.rest.api.model.Site;
import org.alfresco.rest.api.model.SiteTarget;
import org.alfresco.rest.api.model.Target;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.core.exceptions.RelationshipResourceNotFoundException;
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.alfresco.rest.framework.resource.parameters.Params;
import org.alfresco.rest.framework.resource.parameters.SortColumn;
import org.alfresco.rest.framework.resource.parameters.where.QueryHelper;
import org.alfresco.rest.framework.resource.parameters.where.QueryHelper.WalkerCallbackAdapter;
import org.alfresco.service.cmr.favourites.FavouritesService;
import org.alfresco.service.cmr.favourites.FavouritesService.SortFields;
import org.alfresco.service.cmr.favourites.FavouritesService.Type;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.site.SiteInfo;
import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Public REST API: Centralises access to favourites functionality and maps between representations repository and api representations.
*
* @author steveglover
* @since publicapi1.0
*/
public class FavouritesImpl implements Favourites
{
private static final Log logger = LogFactory.getLog(FavouritesImpl.class);
private static final List<String> ALLOWED_INCLUDES = List.of(PARAM_INCLUDE_PROPERTIES, PARAM_INCLUDE_ALLOWABLEOPERATIONS);
private People people;
private Sites sites;
private Nodes nodes;
private FavouritesService favouritesService;
private SiteService siteService;
private NamespaceService namespaceService;
// additional exclude properties for favourites as these can be already top-level properties
private static final List<QName> EXCLUDED_PROPS = Arrays.asList(
ContentModel.PROP_TITLE,
ContentModel.PROP_DESCRIPTION,
SiteModel.PROP_SITE_VISIBILITY,
SiteModel.PROP_SITE_PRESET
);
public void setPeople(People people)
{
this.people = people;
}
public void setSites(Sites sites)
{
this.sites = sites;
}
public void setNodes(Nodes nodes)
{
this.nodes = nodes;
}
public void setFavouritesService(FavouritesService favouritesService)
{
this.favouritesService = favouritesService;
}
public void setSiteService(SiteService siteService)
{
this.siteService = siteService;
}
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
private Target getTarget(PersonFavourite personFavourite, Parameters parameters)
{
Target target = null;
NodeRef nodeRef = personFavourite.getNodeRef();
Type type = personFavourite.getType();
if(type.equals(Type.FILE))
{
Document document = nodes.getDocument(nodeRef);
setPathInfo(document, parameters.getInclude());
target = new DocumentTarget(document);
}
else if(type.equals(Type.FOLDER))
{
Folder folder = nodes.getFolder(nodeRef);
setPathInfo(folder, parameters.getInclude());
target = new FolderTarget(folder);
}
else if(type.equals(Type.SITE))
{
SiteInfo siteInfo = siteService.getSite(nodeRef);
String role = sites.getSiteRole(siteInfo.getShortName());
Site site = new Site(siteInfo, role);
target = new SiteTarget(site);
}
else
{
throw new AlfrescoRuntimeException("Unexpected favourite target type: " + type);
}
return target;
}
private Favourite getFavourite(PersonFavourite personFavourite, Parameters parameters)
{
Favourite fav = new Favourite();
fav.setTargetGuid(personFavourite.getNodeRef().getId());
fav.setCreatedAt(personFavourite.getCreatedAt());
Target target = getTarget(personFavourite, parameters);
fav.setTarget(target);
// REPO-1147 allow retrieving additional properties
final List<String> paramsInclude = parameters.getInclude();
if (!Collections.disjoint(paramsInclude, ALLOWED_INCLUDES))
{
final List<String> includes = ALLOWED_INCLUDES.stream().filter(a -> paramsInclude.contains(a)).collect(Collectors.toList());
// get node representation with only properties included
Node node = nodes.getFolderOrDocument(personFavourite.getNodeRef(), null, null, includes, null);
// Create a map from node properties excluding properties already in this Favorite
Map<String, Object> filteredNodeProperties = filterProps(node.getProperties(), EXCLUDED_PROPS);
if(filteredNodeProperties.size() > 0 && paramsInclude.contains(PARAM_INCLUDE_PROPERTIES))
{
fav.setProperties(filteredNodeProperties);
}
final List<String> allowableOperations = node.getAllowableOperations();
if (CollectionUtils.isNotEmpty(allowableOperations) && paramsInclude.contains(PARAM_INCLUDE_ALLOWABLEOPERATIONS))
{
fav.setAllowableOperations(allowableOperations);
}
}
return fav;
}
private Map<String, Object> filterProps(Map<String, Object> properties, List<QName> toRemove)
{
Map<String, Object> filteredProps = properties == null ? new HashMap<>() : new HashMap<>(properties);
List<String> propsToRemove = toRemove.stream().map(e -> e.toPrefixString(namespaceService)).collect(Collectors.toList());
filteredProps.keySet().removeAll(propsToRemove);
return filteredProps;
}
private CollectionWithPagingInfo<Favourite> wrap(Paging paging, PagingResults<PersonFavourite> personFavourites, Parameters parameters)
{
final List<PersonFavourite> page = personFavourites.getPage();
final List<Favourite> list = new AbstractList<Favourite>()
{
@Override
public Favourite get(int index)
{
PersonFavourite personFavourite = page.get(index);
Favourite fav = getFavourite(personFavourite, parameters);
return fav;
}
@Override
public int size()
{
return page.size();
}
};
Pair<Integer, Integer> pair = personFavourites.getTotalResultCount();
Integer total = null;
if(pair.getFirst().equals(pair.getSecond()))
{
total = pair.getFirst();
}
return CollectionWithPagingInfo.asPaged(paging, list, personFavourites.hasMoreItems(), total);
}
@Override
public Favourite addFavourite(String personId, Favourite favourite)
{
Parameters parameters = getDefaultParameters(personId, null);
return addFavourite(personId, favourite, parameters);
}
@Override
public Favourite addFavourite(String personId, Favourite favourite, Parameters parameters)
{
Favourite ret = null;
personId = people.validatePerson(personId, true);
Target target = favourite.getTarget();
if(target == null)
{
throw new InvalidArgumentException("target is missing");
}
else if(target instanceof SiteTarget)
{
SiteTarget siteTarget = (SiteTarget)target;
String guid = siteTarget.getSite().getGuid();
SiteInfo siteInfo = sites.validateSite(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, guid));
NodeRef siteNodeRef = siteInfo.getNodeRef();
String siteId = siteInfo.getShortName();
try
{
PersonFavourite personFavourite = favouritesService.addFavourite(personId, siteNodeRef);
ret = getFavourite(personFavourite, parameters);
}
catch(SiteDoesNotExistException e)
{
throw new RelationshipResourceNotFoundException(personId, siteId);
}
}
else if(target instanceof DocumentTarget)
{
DocumentTarget documentTarget = (DocumentTarget)target;
NodeRef nodeRef = documentTarget.getFile().getGuid();
if(!nodes.nodeMatches(nodeRef, Collections.singleton(ContentModel.TYPE_CONTENT), null))
{
throw new RelationshipResourceNotFoundException(personId, nodeRef.getId());
}
PersonFavourite personFavourite = favouritesService.addFavourite(personId, nodeRef);
ret = getFavourite(personFavourite, parameters);
}
else if(target instanceof FolderTarget)
{
FolderTarget folderTarget = (FolderTarget)target;
NodeRef nodeRef = folderTarget.getFolder().getGuid();
if(!nodes.nodeMatches(nodeRef, Collections.singleton(ContentModel.TYPE_FOLDER), Collections.singleton(SiteModel.TYPE_SITE)))
{
throw new RelationshipResourceNotFoundException(personId, nodeRef.getId());
}
PersonFavourite personFavourite = favouritesService.addFavourite(personId, nodeRef);
ret = getFavourite(personFavourite, parameters);
}
return ret;
}
@Override
public void removeFavourite(String personId, String id)
{
personId = people.validatePerson(personId, true);
NodeRef nodeRef = nodes.validateNode(id);
boolean exists = false;
Type type = favouritesService.getType(nodeRef);
if(type.equals(Type.SITE))
{
SiteInfo siteInfo = siteService.getSite(nodeRef);
if(siteInfo == null)
{
// shouldn't happen because the type implies it's a site
throw new AlfrescoRuntimeException("Unable to find site with nodeRef " + nodeRef);
}
exists = favouritesService.removeFavourite(personId, siteInfo.getNodeRef());
}
else if(type.equals(Type.FILE))
{
exists = favouritesService.removeFavourite(personId, nodeRef);
}
else if(type.equals(Type.FOLDER))
{
exists = favouritesService.removeFavourite(personId, nodeRef);
}
if(!exists)
{
throw new RelationshipResourceNotFoundException(personId, id);
}
}
@Override
public Favourite getFavourite(String personId, String favouriteId)
{
Parameters parameters = getDefaultParameters(personId, favouriteId);
return getFavourite(personId, favouriteId, parameters);
}
@Override
public Favourite getFavourite(String personId, String favouriteId, Parameters parameters)
{
NodeRef nodeRef = nodes.validateNode(favouriteId);
personId = people.validatePerson(personId, true);
PersonFavourite personFavourite = favouritesService.getFavourite(personId, nodeRef);
if(personFavourite != null)
{
Favourite favourite = getFavourite(personFavourite, parameters);
return favourite;
}
else
{
throw new RelationshipResourceNotFoundException(personId, favouriteId);
}
}
@Override
public CollectionWithPagingInfo<Favourite> getFavourites(String personId, final Parameters parameters)
{
personId = people.validatePerson(personId, true);
Paging paging = parameters.getPaging();
List<Pair<FavouritesService.SortFields, Boolean>> sortProps = getSortProps(parameters);
final Set<Type> filteredByClientQuery = new HashSet<Type>();
Set<Type> filterTypes = FavouritesService.Type.ALL_FILTER_TYPES; //Default all
// filterType is of the form 'target.<site|file|folder>'
QueryHelper.walk(parameters.getQuery(), new WalkerCallbackAdapter()
{
@Override
public void or() {
//OR is supported but exists() will be called for each EXISTS so we don't
//need to do anything here. If we don't override it then it will be assumed
//that OR in the grammar is not supported.
}
@Override
public void exists(String filteredByClient, boolean negated) {
if(filteredByClient != null)
{
int idx = filteredByClient.lastIndexOf("/");
if(idx == -1 || idx == filteredByClient.length())
{
throw new InvalidArgumentException();
}
else
{
String filtertype = filteredByClient.substring(idx + 1).toUpperCase();
filteredByClientQuery.add(Type.valueOf(filtertype));
}
}
}
});
if (filteredByClientQuery.size() > 0)
{
filterTypes = filteredByClientQuery;
}
final PagingResults<PersonFavourite> favourites = favouritesService.getPagedFavourites(personId, filterTypes, sortProps, Util.getPagingRequest(paging));
return wrap(paging, favourites, parameters);
}
private void setPathInfo(Node node, List<String> includeParam)
{
if (includeParam.contains(PARAM_INCLUDE_PATH))
{
PathInfo pathInfo = nodes.lookupPathInfo(node.getNodeRef(), null);
node.setPath(pathInfo);
}
}
/**
* Returns a {@code {@link Parameters} object where almost all of its values are null.
* the non-null value is the {@literal include} and whatever value is passed for {@code personId} and {@code favouriteId}
*/
private Parameters getDefaultParameters(String personId, String favouriteId)
{
Params.RecognizedParams recognizedParams = new Params.RecognizedParams(null, null, null, null, Collections.emptyList(), null, null, null,
false);
Parameters parameters = Params.valueOf(recognizedParams, personId, favouriteId, null);
return parameters;
}
private List<Pair<FavouritesService.SortFields, Boolean>> getSortProps(Parameters parameters)
{
List<Pair<FavouritesService.SortFields, Boolean>> sortProps = new ArrayList<>();
List<SortColumn> sortCols = parameters.getSorting();
if ((sortCols != null) && (sortCols.size() > 0))
{
for (SortColumn sortCol : sortCols)
{
SortFields sortField;
try
{
sortField = SortFields.valueOf(sortCol.column);
}
catch (Exception e)
{
throw new InvalidArgumentException("Invalid sort field: " + sortCol.column);
}
sortProps.add(new Pair<>(sortField, (sortCol.asc ? Boolean.TRUE : Boolean.FALSE)));
}
}
else
{
// default sort order
sortProps = FavouritesService.DEFAULT_SORT_PROPS;
}
return sortProps;
}
}
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2019 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api.impl;
import static org.alfresco.rest.api.Nodes.PARAM_INCLUDE_ALLOWABLEOPERATIONS;
import static org.alfresco.rest.api.Nodes.PARAM_INCLUDE_ASPECTNAMES;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.query.PagingResults;
import org.alfresco.repo.favourites.PersonFavourite;
import org.alfresco.repo.site.SiteDoesNotExistException;
import org.alfresco.repo.site.SiteModel;
import org.alfresco.rest.api.Favourites;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.People;
import org.alfresco.rest.api.Sites;
import org.alfresco.rest.api.model.Document;
import org.alfresco.rest.api.model.DocumentTarget;
import org.alfresco.rest.api.model.Favourite;
import org.alfresco.rest.api.model.Folder;
import org.alfresco.rest.api.model.FolderTarget;
import org.alfresco.rest.api.model.Node;
import org.alfresco.rest.api.model.PathInfo;
import org.alfresco.rest.api.model.Site;
import org.alfresco.rest.api.model.SiteTarget;
import org.alfresco.rest.api.model.Target;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.core.exceptions.RelationshipResourceNotFoundException;
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.alfresco.rest.framework.resource.parameters.Params;
import org.alfresco.rest.framework.resource.parameters.SortColumn;
import org.alfresco.rest.framework.resource.parameters.where.QueryHelper;
import org.alfresco.rest.framework.resource.parameters.where.QueryHelper.WalkerCallbackAdapter;
import org.alfresco.service.cmr.favourites.FavouritesService;
import org.alfresco.service.cmr.favourites.FavouritesService.SortFields;
import org.alfresco.service.cmr.favourites.FavouritesService.Type;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.site.SiteInfo;
import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
/**
* Public REST API: Centralises access to favourites functionality and maps between representations repository and api representations.
*
* @author steveglover
* @since publicapi1.0
*/
public class FavouritesImpl implements Favourites
{
private static final List<String> ALLOWED_INCLUDES = List.of(PARAM_INCLUDE_PROPERTIES, PARAM_INCLUDE_ASPECTNAMES, PARAM_INCLUDE_ALLOWABLEOPERATIONS);
private People people;
private Sites sites;
private Nodes nodes;
private FavouritesService favouritesService;
private SiteService siteService;
private NamespaceService namespaceService;
// additional exclude properties for favourites as these can be already top-level properties
private static final List<QName> EXCLUDED_PROPS = Arrays.asList(
ContentModel.PROP_TITLE,
ContentModel.PROP_DESCRIPTION,
SiteModel.PROP_SITE_VISIBILITY,
SiteModel.PROP_SITE_PRESET);
public void setPeople(People people)
{
this.people = people;
}
public void setSites(Sites sites)
{
this.sites = sites;
}
public void setNodes(Nodes nodes)
{
this.nodes = nodes;
}
public void setFavouritesService(FavouritesService favouritesService)
{
this.favouritesService = favouritesService;
}
public void setSiteService(SiteService siteService)
{
this.siteService = siteService;
}
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
private Target getTarget(PersonFavourite personFavourite, Parameters parameters)
{
Target target;
NodeRef nodeRef = personFavourite.getNodeRef();
Type type = personFavourite.getType();
if (type.equals(Type.FILE))
{
Document document = nodes.getDocument(nodeRef);
setPathInfo(document, parameters.getInclude());
target = new DocumentTarget(document);
}
else if (type.equals(Type.FOLDER))
{
Folder folder = nodes.getFolder(nodeRef);
setPathInfo(folder, parameters.getInclude());
target = new FolderTarget(folder);
}
else if (type.equals(Type.SITE))
{
SiteInfo siteInfo = siteService.getSite(nodeRef);
String role = sites.getSiteRole(siteInfo.getShortName());
Site site = new Site(siteInfo, role);
target = new SiteTarget(site);
}
else
{
throw new AlfrescoRuntimeException("Unexpected favourite target type: " + type);
}
return target;
}
private Favourite getFavourite(PersonFavourite personFavourite, Parameters parameters)
{
Favourite fav = new Favourite();
fav.setTargetGuid(personFavourite.getNodeRef().getId());
fav.setCreatedAt(personFavourite.getCreatedAt());
Target target = getTarget(personFavourite, parameters);
fav.setTarget(target);
// REPO-1147 allow retrieving additional properties
final List<String> paramsInclude = parameters.getInclude();
if (!Collections.disjoint(paramsInclude, ALLOWED_INCLUDES))
{
final List<String> includes = ALLOWED_INCLUDES.stream().filter(paramsInclude::contains).collect(Collectors.toList());
// get node representation with only properties included
Node node = nodes.getFolderOrDocument(personFavourite.getNodeRef(), null, null, includes, null);
// Create a map from node properties excluding properties already in this Favorite
Map<String, Object> filteredNodeProperties = filterProps(node.getProperties(), EXCLUDED_PROPS);
if (!filteredNodeProperties.isEmpty() && paramsInclude.contains(PARAM_INCLUDE_PROPERTIES))
{
fav.setProperties(filteredNodeProperties);
}
if (paramsInclude.contains(PARAM_INCLUDE_ASPECTNAMES))
{
fav.setAspectNames(node.getAspectNames());
}
final List<String> allowableOperations = node.getAllowableOperations();
if (CollectionUtils.isNotEmpty(allowableOperations) && paramsInclude.contains(PARAM_INCLUDE_ALLOWABLEOPERATIONS))
{
fav.setAllowableOperations(allowableOperations);
}
}
return fav;
}
private Map<String, Object> filterProps(Map<String, Object> properties, List<QName> toRemove)
{
Map<String, Object> filteredProps = properties == null ? new HashMap<>() : new HashMap<>(properties);
List<String> propsToRemove = toRemove.stream().map(e -> e.toPrefixString(namespaceService)).collect(Collectors.toList());
filteredProps.keySet().removeAll(propsToRemove);
return filteredProps;
}
private CollectionWithPagingInfo<Favourite> wrap(Paging paging, PagingResults<PersonFavourite> personFavourites, Parameters parameters)
{
final List<PersonFavourite> page = personFavourites.getPage();
final List<Favourite> list = new AbstractList<>() {
@Override
public Favourite get(int index)
{
PersonFavourite personFavourite = page.get(index);
Favourite fav = getFavourite(personFavourite, parameters);
return fav;
}
@Override
public int size()
{
return page.size();
}
};
Pair<Integer, Integer> pair = personFavourites.getTotalResultCount();
Integer total = null;
if (pair.getFirst().equals(pair.getSecond()))
{
total = pair.getFirst();
}
return CollectionWithPagingInfo.asPaged(paging, list, personFavourites.hasMoreItems(), total);
}
@Override
public Favourite addFavourite(String personId, Favourite favourite)
{
Parameters parameters = getDefaultParameters(personId, null);
return addFavourite(personId, favourite, parameters);
}
@Override
public Favourite addFavourite(String personId, Favourite favourite, Parameters parameters)
{
Favourite ret = null;
personId = people.validatePerson(personId, true);
Target target = favourite.getTarget();
if (target == null)
{
throw new InvalidArgumentException("target is missing");
}
else if (target instanceof SiteTarget)
{
SiteTarget siteTarget = (SiteTarget) target;
String guid = siteTarget.getSite().getGuid();
SiteInfo siteInfo = sites.validateSite(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, guid));
NodeRef siteNodeRef = siteInfo.getNodeRef();
String siteId = siteInfo.getShortName();
try
{
PersonFavourite personFavourite = favouritesService.addFavourite(personId, siteNodeRef);
ret = getFavourite(personFavourite, parameters);
}
catch (SiteDoesNotExistException e)
{
throw new RelationshipResourceNotFoundException(personId, siteId);
}
}
else if (target instanceof DocumentTarget)
{
DocumentTarget documentTarget = (DocumentTarget) target;
NodeRef nodeRef = documentTarget.getFile().getGuid();
if (!nodes.nodeMatches(nodeRef, Collections.singleton(ContentModel.TYPE_CONTENT), null))
{
throw new RelationshipResourceNotFoundException(personId, nodeRef.getId());
}
PersonFavourite personFavourite = favouritesService.addFavourite(personId, nodeRef);
ret = getFavourite(personFavourite, parameters);
}
else if (target instanceof FolderTarget)
{
FolderTarget folderTarget = (FolderTarget) target;
NodeRef nodeRef = folderTarget.getFolder().getGuid();
if (!nodes.nodeMatches(nodeRef, Collections.singleton(ContentModel.TYPE_FOLDER), Collections.singleton(SiteModel.TYPE_SITE)))
{
throw new RelationshipResourceNotFoundException(personId, nodeRef.getId());
}
PersonFavourite personFavourite = favouritesService.addFavourite(personId, nodeRef);
ret = getFavourite(personFavourite, parameters);
}
return ret;
}
@Override
public void removeFavourite(String personId, String id)
{
personId = people.validatePerson(personId, true);
NodeRef nodeRef = nodes.validateNode(id);
boolean exists = false;
Type type = favouritesService.getType(nodeRef);
if (type.equals(Type.SITE))
{
SiteInfo siteInfo = siteService.getSite(nodeRef);
if (siteInfo == null)
{
// shouldn't happen because the type implies it's a site
throw new AlfrescoRuntimeException("Unable to find site with nodeRef " + nodeRef);
}
exists = favouritesService.removeFavourite(personId, siteInfo.getNodeRef());
}
else if (type.equals(Type.FILE))
{
exists = favouritesService.removeFavourite(personId, nodeRef);
}
else if (type.equals(Type.FOLDER))
{
exists = favouritesService.removeFavourite(personId, nodeRef);
}
if (!exists)
{
throw new RelationshipResourceNotFoundException(personId, id);
}
}
@Override
public Favourite getFavourite(String personId, String favouriteId)
{
Parameters parameters = getDefaultParameters(personId, favouriteId);
return getFavourite(personId, favouriteId, parameters);
}
@Override
public Favourite getFavourite(String personId, String favouriteId, Parameters parameters)
{
NodeRef nodeRef = nodes.validateNode(favouriteId);
personId = people.validatePerson(personId, true);
PersonFavourite personFavourite = favouritesService.getFavourite(personId, nodeRef);
if (personFavourite != null)
{
Favourite favourite = getFavourite(personFavourite, parameters);
return favourite;
}
else
{
throw new RelationshipResourceNotFoundException(personId, favouriteId);
}
}
@Override
public CollectionWithPagingInfo<Favourite> getFavourites(String personId, final Parameters parameters)
{
personId = people.validatePerson(personId, true);
Paging paging = parameters.getPaging();
List<Pair<FavouritesService.SortFields, Boolean>> sortProps = getSortProps(parameters);
final Set<Type> filteredByClientQuery = new HashSet<>();
Set<Type> filterTypes = FavouritesService.Type.ALL_FILTER_TYPES; // Default all
// filterType is of the form 'target.<site|file|folder>'
QueryHelper.walk(parameters.getQuery(), new WalkerCallbackAdapter() {
@Override
public void or()
{
// OR is supported but exists() will be called for each EXISTS so we don't
// need to do anything here. If we don't override it then it will be assumed
// that OR in the grammar is not supported.
}
@Override
public void exists(String filteredByClient, boolean negated)
{
if (filteredByClient != null)
{
int idx = filteredByClient.lastIndexOf('/');
if (idx == -1 || idx == filteredByClient.length())
{
throw new InvalidArgumentException();
}
else
{
String filtertype = filteredByClient.substring(idx + 1).toUpperCase();
filteredByClientQuery.add(Type.valueOf(filtertype));
}
}
}
});
if (!filteredByClientQuery.isEmpty())
{
filterTypes = filteredByClientQuery;
}
final PagingResults<PersonFavourite> favourites = favouritesService.getPagedFavourites(personId, filterTypes, sortProps, Util.getPagingRequest(paging));
return wrap(paging, favourites, parameters);
}
private void setPathInfo(Node node, List<String> includeParam)
{
if (includeParam.contains(PARAM_INCLUDE_PATH))
{
PathInfo pathInfo = nodes.lookupPathInfo(node.getNodeRef(), null);
node.setPath(pathInfo);
}
}
/**
* Returns a {@code {@link Parameters} object where almost all of its values are null. the non-null value is the {@literal include} and whatever value is passed for {@code personId} and {@code favouriteId}
*/
private Parameters getDefaultParameters(String personId, String favouriteId)
{
Params.RecognizedParams recognizedParams = new Params.RecognizedParams(null, null, null, null, Collections.emptyList(), null, null, null,
false);
Parameters parameters = Params.valueOf(recognizedParams, personId, favouriteId, null);
return parameters;
}
private List<Pair<FavouritesService.SortFields, Boolean>> getSortProps(Parameters parameters)
{
List<Pair<FavouritesService.SortFields, Boolean>> sortProps = new ArrayList<>();
List<SortColumn> sortCols = parameters.getSorting();
if (sortCols != null && !sortCols.isEmpty())
{
for (SortColumn sortCol : sortCols)
{
SortFields sortField;
try
{
sortField = SortFields.valueOf(sortCol.column);
}
catch (Exception e)
{
throw new InvalidArgumentException("Invalid sort field: " + sortCol.column);
}
sortProps.add(new Pair<>(sortField, sortCol.asc ? Boolean.TRUE : Boolean.FALSE));
}
}
else
{
// default sort order
sortProps = FavouritesService.DEFAULT_SORT_PROPS;
}
return sortProps;
}
}

View File

@@ -23,43 +23,65 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api.model;
/**
* A document target favourite.
*
* @author steveglover
*
*/
public class DocumentTarget extends Target
{
private Document file;
public DocumentTarget()
{
super();
}
public DocumentTarget(Document file)
{
super();
this.file = file;
}
public void setDocument(Document file)
{
this.file = file;
}
public Document getFile()
{
return file;
}
@Override
public String toString()
{
return "DocumentTarget [file=" + file + "]";
}
}
package org.alfresco.rest.api.model;
import java.util.Objects;
/**
* A document target favourite.
*
* @author steveglover
*
*/
public class DocumentTarget extends Target
{
private Document file;
public DocumentTarget()
{
super();
}
public DocumentTarget(Document file)
{
super();
this.file = file;
}
public void setDocument(Document file)
{
this.file = file;
}
public Document getFile()
{
return file;
}
@Override
public String toString()
{
return "DocumentTarget [file=" + file + "]";
}
@Override
public boolean equals(Object o)
{
if (this == o)
{
return true;
}
if (o == null || getClass() != o.getClass())
{
return false;
}
DocumentTarget that = (DocumentTarget) o;
return Objects.equals(file, that.file);
}
@Override
public int hashCode()
{
return Objects.hashCode(file);
}
}

View File

@@ -1,103 +1,144 @@
/*
* #%L
* Alfresco Remote API
* %%
* 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api.model;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.alfresco.rest.framework.resource.UniqueId;
/**
* Representation of a favourite (document, folder, site, ...).
*
* @author steveglover
*
*/
public class Favourite
{
private String targetGuid;
private Date createdAt;
private Target target;
private Map<String, Object> properties;
private List<String> allowableOperations;
public Date getCreatedAt()
{
return createdAt;
}
public void setCreatedAt(Date createdAt)
{
this.createdAt = createdAt;
}
@UniqueId(name="targetGuid")
public String getTargetGuid()
{
return targetGuid;
}
public void setTargetGuid(String targetGuid)
{
this.targetGuid = targetGuid;
}
public Target getTarget()
{
return target;
}
public void setTarget(Target target)
{
this.target = target;
}
public Map<String, Object> getProperties()
{
return properties;
}
public void setProperties(Map<String, Object> properties)
{
this.properties = properties;
}
public List<String> getAllowableOperations() {
return allowableOperations;
}
public void setAllowableOperations(List<String> allowableOperations) {
this.allowableOperations = allowableOperations;
}
@Override
public String toString()
{
return "Favourite [targetGuid=" + targetGuid
+ ", createdAt=" + createdAt + ", target=" + target + ", properties=" + properties + "]";
}
}
/*
* #%L
* Alfresco Remote API
* %%
* 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api.model;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.alfresco.rest.framework.resource.UniqueId;
/**
* Representation of a favourite (document, folder, site, ...).
*
* @author steveglover
*
*/
public class Favourite
{
private String targetGuid;
private Date createdAt;
private Target target;
private Map<String, Object> properties;
private List<String> aspectNames;
private List<String> allowableOperations;
public Date getCreatedAt()
{
return createdAt;
}
public void setCreatedAt(Date createdAt)
{
this.createdAt = createdAt;
}
@UniqueId(name = "targetGuid")
public String getTargetGuid()
{
return targetGuid;
}
public void setTargetGuid(String targetGuid)
{
this.targetGuid = targetGuid;
}
public Target getTarget()
{
return target;
}
public void setTarget(Target target)
{
this.target = target;
}
public Map<String, Object> getProperties()
{
return properties;
}
public void setProperties(Map<String, Object> properties)
{
this.properties = properties;
}
public List<String> getAspectNames()
{
return aspectNames;
}
public void setAspectNames(List<String> aspectNames)
{
this.aspectNames = aspectNames;
}
public List<String> getAllowableOperations()
{
return allowableOperations;
}
public void setAllowableOperations(List<String> allowableOperations)
{
this.allowableOperations = allowableOperations;
}
@Override
public String toString()
{
return "Favourite{" +
"targetGuid='" + targetGuid + '\'' +
", createdAt=" + createdAt +
", target=" + target +
", properties=" + properties +
", aspectNames=" + aspectNames +
", allowableOperations=" + allowableOperations +
'}';
}
@Override
public boolean equals(Object o)
{
if (this == o)
{
return true;
}
if (o == null || getClass() != o.getClass())
{
return false;
}
Favourite favourite = (Favourite) o;
return Objects.equals(targetGuid, favourite.targetGuid) && Objects.equals(createdAt, favourite.createdAt) && Objects.equals(target, favourite.target) && Objects.equals(properties, favourite.properties) && Objects.equals(aspectNames, favourite.aspectNames) && Objects.equals(allowableOperations, favourite.allowableOperations);
}
@Override
public int hashCode()
{
return Objects.hash(targetGuid, createdAt, target, properties, aspectNames, allowableOperations);
}
}

View File

@@ -25,59 +25,59 @@
*/
package org.alfresco;
import org.alfresco.repo.web.scripts.TestWebScriptRepoServer;
import org.alfresco.util.testing.category.DBTests;
import org.alfresco.util.testing.category.NonBuildTests;
import org.junit.experimental.categories.Categories;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.alfresco.repo.web.scripts.TestWebScriptRepoServer;
import org.alfresco.util.testing.category.DBTests;
import org.alfresco.util.testing.category.NonBuildTests;
@RunWith(Categories.class)
@Categories.ExcludeCategory({DBTests.class, NonBuildTests.class})
@Suite.SuiteClasses({
// [classpath:alfresco/application-context.xml, classpath:alfresco/web-scripts-application-context-test.xml,
// classpath:alfresco/web-scripts-application-context.xml]
org.alfresco.repo.web.scripts.quickshare.QuickShareRestApiTest.class,
org.alfresco.repo.web.scripts.admin.AdminWebScriptTest.class,
org.alfresco.repo.web.scripts.audit.AuditWebScriptTest.class,
org.alfresco.repo.web.scripts.blogs.BlogServiceTest.class,
org.alfresco.repo.web.scripts.dictionary.DictionaryRestApiTest.class,
org.alfresco.repo.web.scripts.discussion.DiscussionRestApiTest.class,
org.alfresco.repo.web.scripts.activities.feed.control.FeedControlTest.class,
org.alfresco.repo.web.scripts.forms.FormRestApiGet_Test.class,
org.alfresco.repo.web.scripts.forms.FormRestApiJsonPost_Test.class,
org.alfresco.repo.web.scripts.groups.GroupsTest.class,
org.alfresco.repo.web.scripts.invitation.InvitationWebScriptTest.class,
org.alfresco.repo.web.scripts.invite.InviteServiceTest.class,
org.alfresco.repo.web.scripts.LoginTest.class,
org.alfresco.repo.web.scripts.search.PersonSearchTest.class,
org.alfresco.repo.web.scripts.person.PersonServiceTest.class,
org.alfresco.repo.web.scripts.preference.PreferenceServiceTest.class,
org.alfresco.repo.web.scripts.rating.RatingRestApiTest.class,
org.alfresco.repo.web.scripts.replication.ReplicationRestApiTest.class,
org.alfresco.repo.web.scripts.RepositoryContainerTest.class,
org.alfresco.repo.web.scripts.rule.RuleServiceTest.class,
org.alfresco.repo.web.scripts.action.RunningActionRestApiTest.class,
org.alfresco.repo.web.scripts.site.SiteServiceTest.class,
org.alfresco.repo.web.scripts.tagging.TaggingServiceTest.class,
org.alfresco.repo.web.scripts.thumbnail.ThumbnailServiceTest.class,
org.alfresco.repo.web.scripts.transfer.TransferWebScriptTest.class,
org.alfresco.repo.web.scripts.workflow.ActivitiWorkflowRestApiTest.class,
org.alfresco.repo.web.scripts.solr.SOLRWebScriptTest.class,
org.alfresco.repo.web.scripts.subscriptions.SubscriptionServiceRestApiTest.class,
org.alfresco.repo.web.scripts.facet.FacetRestApiTest.class,
org.alfresco.repo.web.scripts.comment.CommentsApiTest.class,
org.alfresco.repo.web.scripts.content.ContentGetTest.class,
org.alfresco.repo.web.scripts.XssVulnerabilityTest.class,
org.alfresco.repo.web.scripts.links.LinksRestApiTest.class,
org.alfresco.repo.model.filefolder.RemoteFileFolderLoaderTest.class,
org.alfresco.repo.web.scripts.ReadOnlyTransactionInGetRestApiTest.class,
org.alfresco.repo.web.scripts.custommodel.CustomModelImportTest.class,
org.alfresco.repo.web.scripts.site.SurfConfigTest.class,
org.alfresco.repo.web.scripts.node.NodeWebScripTest.class,
org.alfresco.rest.api.impl.CommentsImplUnitTest.class,
org.alfresco.rest.api.impl.DownloadsImplCheckArchiveStatusUnitTest.class,
org.alfresco.rest.api.impl.RestApiDirectUrlConfigUnitTest.class
org.alfresco.repo.web.scripts.quickshare.QuickShareRestApiTest.class,
org.alfresco.repo.web.scripts.admin.AdminWebScriptTest.class,
org.alfresco.repo.web.scripts.audit.AuditWebScriptTest.class,
org.alfresco.repo.web.scripts.blogs.BlogServiceTest.class,
org.alfresco.repo.web.scripts.dictionary.DictionaryRestApiTest.class,
org.alfresco.repo.web.scripts.discussion.DiscussionRestApiTest.class,
org.alfresco.repo.web.scripts.activities.feed.control.FeedControlTest.class,
org.alfresco.repo.web.scripts.forms.FormRestApiGet_Test.class,
org.alfresco.repo.web.scripts.forms.FormRestApiJsonPost_Test.class,
org.alfresco.repo.web.scripts.groups.GroupsTest.class,
org.alfresco.repo.web.scripts.invitation.InvitationWebScriptTest.class,
org.alfresco.repo.web.scripts.invite.InviteServiceTest.class,
org.alfresco.repo.web.scripts.LoginTest.class,
org.alfresco.repo.web.scripts.search.PersonSearchTest.class,
org.alfresco.repo.web.scripts.person.PersonServiceTest.class,
org.alfresco.repo.web.scripts.preference.PreferenceServiceTest.class,
org.alfresco.repo.web.scripts.rating.RatingRestApiTest.class,
org.alfresco.repo.web.scripts.replication.ReplicationRestApiTest.class,
org.alfresco.repo.web.scripts.RepositoryContainerTest.class,
org.alfresco.repo.web.scripts.rule.RuleServiceTest.class,
org.alfresco.repo.web.scripts.action.RunningActionRestApiTest.class,
org.alfresco.repo.web.scripts.site.SiteServiceTest.class,
org.alfresco.repo.web.scripts.tagging.TaggingServiceTest.class,
org.alfresco.repo.web.scripts.thumbnail.ThumbnailServiceTest.class,
org.alfresco.repo.web.scripts.transfer.TransferWebScriptTest.class,
org.alfresco.repo.web.scripts.workflow.ActivitiWorkflowRestApiTest.class,
org.alfresco.repo.web.scripts.solr.SOLRWebScriptTest.class,
org.alfresco.repo.web.scripts.subscriptions.SubscriptionServiceRestApiTest.class,
org.alfresco.repo.web.scripts.facet.FacetRestApiTest.class,
org.alfresco.repo.web.scripts.comment.CommentsApiTest.class,
org.alfresco.repo.web.scripts.content.ContentGetTest.class,
org.alfresco.repo.web.scripts.XssVulnerabilityTest.class,
org.alfresco.repo.web.scripts.links.LinksRestApiTest.class,
org.alfresco.repo.model.filefolder.RemoteFileFolderLoaderTest.class,
org.alfresco.repo.web.scripts.ReadOnlyTransactionInGetRestApiTest.class,
org.alfresco.repo.web.scripts.custommodel.CustomModelImportTest.class,
org.alfresco.repo.web.scripts.site.SurfConfigTest.class,
org.alfresco.repo.web.scripts.node.NodeWebScripTest.class,
org.alfresco.rest.api.impl.CommentsImplUnitTest.class,
org.alfresco.rest.api.impl.DownloadsImplCheckArchiveStatusUnitTest.class,
org.alfresco.rest.api.impl.FavouritesImplUnitTest.class,
org.alfresco.rest.api.impl.RestApiDirectUrlConfigUnitTest.class
})
public class AppContext04TestSuite
{

View File

@@ -0,0 +1,134 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2020 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api.impl;
import static java.util.Collections.singleton;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.openMocks;
import static org.alfresco.model.ContentModel.TYPE_CONTENT;
import static org.alfresco.service.cmr.favourites.FavouritesService.Type.FILE;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.alfresco.repo.favourites.PersonFavourite;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.People;
import org.alfresco.rest.api.model.Document;
import org.alfresco.rest.api.model.DocumentTarget;
import org.alfresco.rest.api.model.Favourite;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.service.cmr.favourites.FavouritesService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.NamespaceService;
/**
* Unit tests for {@link FavouritesImpl} class.
*/
public class FavouritesImplUnitTest
{
static final String NODE_ID = "12345678";
static final NodeRef NODE_REF = new NodeRef("favourite://node/" + NODE_ID);
static final String PERSON_ID = "personId";
static final String ASPECT_NAME = "some:aspect";
@InjectMocks
FavouritesImpl favouritesImpl;
@Mock
People people;
@Mock
Nodes nodes;
@Mock
FavouritesService favouritesService;
@Mock
NamespaceService namespaceService;
@Mock
Favourite favourite;
@Mock
Document document;
@Mock
PersonFavourite personFavourite;
@Before
public void setUp()
{
openMocks(this);
when(nodes.getDocument(NODE_REF)).thenReturn(document);
when(nodes.nodeMatches(NODE_REF, singleton(TYPE_CONTENT), null)).thenReturn(true);
when(document.getGuid()).thenReturn(NODE_REF);
when(people.validatePerson(PERSON_ID, true)).thenReturn(PERSON_ID);
when(personFavourite.getNodeRef()).thenReturn(NODE_REF);
when(personFavourite.getType()).thenReturn(FILE);
when(favouritesService.addFavourite(PERSON_ID, NODE_REF)).thenReturn(personFavourite);
when(namespaceService.getPrefixes(anyString())).thenReturn(List.of("prefix"));
}
@Test
public void testAddFavourite()
{
DocumentTarget documentTarget = new DocumentTarget(document);
when(favourite.getTarget()).thenReturn(documentTarget);
Favourite response = favouritesImpl.addFavourite(PERSON_ID, favourite);
Favourite expected = new Favourite();
expected.setTarget(documentTarget);
expected.setTargetGuid(NODE_ID);
assertEquals(expected, response);
}
@Test
public void testAddFavouriteIncludeAspectNames()
{
List<String> includes = List.of("aspectNames");
DocumentTarget documentTarget = new DocumentTarget(document);
when(favourite.getTarget()).thenReturn(documentTarget);
when(nodes.getFolderOrDocument(NODE_REF, null, null, includes, null)).thenReturn(document);
when(document.getAspectNames()).thenReturn(List.of(ASPECT_NAME));
Parameters parameters = mock(Parameters.class);
when(parameters.getInclude()).thenReturn(includes);
Favourite response = favouritesImpl.addFavourite(PERSON_ID, favourite, parameters);
Favourite expected = new Favourite();
expected.setTarget(documentTarget);
expected.setTargetGuid(NODE_ID);
expected.setAspectNames(List.of(ASPECT_NAME));
assertEquals(expected, response);
}
}

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.4.0.36</version>
<version>23.4.0.52</version>
</parent>
<dependencies>

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -23,133 +23,127 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.security.permissions;
import java.util.Collection;
import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultIntroductionAdvisor;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
/**
* Interface for collection-based results that describe permission filtering
* behaviour around cut-off limits.
*
* @author Derek Hulley
* @since 4.0
*/
public interface PermissionCheckCollection<T>
{
/**
* Get the desired number of results. Permission checks can stop once the number of
* return objects reaches this number.
*
* @return the number of results desired
*/
int getTargetResultCount();
/**
* Get the maximum time for permission checks to execute before cutting the results off.
* <br/>Zero: Ignore this value.
*
* @return the time allowed for permission checks before cutoff
*/
long getCutOffAfterTimeMs();
/**
* Get the maximum number of permission checks to perform before cutting the results off
*
* @return the maximum number of permission checks before cutoff
*/
int getCutOffAfterCount();
/**
* Helper 'introduction' to allow simple addition of the {@link PermissionCheckCollection} interface to
* existing collections.
*
* @param <T> the type of the <code>Collection</code> in use
*
* @author Derek Hulley
* @since 4.0
*/
@SuppressWarnings("serial")
public static class PermissionCheckCollectionMixin<T> extends DelegatingIntroductionInterceptor implements PermissionCheckCollection<T>
{
private final int targetResultCount;
private final long cutOffAfterTimeMs;
private final int cutOffAfterCount;
private PermissionCheckCollectionMixin(int targetResultCount, long cutOffAfterTimeMs, int cutOffAfterCount)
{
super();
this.targetResultCount = targetResultCount;
this.cutOffAfterTimeMs = cutOffAfterTimeMs;
this.cutOffAfterCount = cutOffAfterCount;
if (cutOffAfterTimeMs <= 0)
{
cutOffAfterTimeMs = 0;
}
if (cutOffAfterCount <= 0)
{
cutOffAfterCount = 0;
}
}
@Override
public int getTargetResultCount()
{
return targetResultCount;
}
@Override
public long getCutOffAfterTimeMs()
{
return cutOffAfterTimeMs;
}
@Override
public int getCutOffAfterCount()
{
return cutOffAfterCount;
}
/**
* Helper method to create a {@link PermissionCheckCollection} from an existing <code>Collection</code>
*
* @param <TT> the type of the <code>Collection</code>
* @param collection the <code>Collection</code> to proxy
* @param targetResultCount the desired number of results or default to the collection size
* @param cutOffAfterTimeMs the number of milliseconds to wait before cut-off or zero to use the system default
* time-based cut-off.
* @param cutOffAfterCount the number of permission checks to process before cut-off or zero to use the system default
* count-based cut-off.
* @return a <code>Collection</code> of the same type but including the
* {@link PermissionCheckCollection} interface
*/
@SuppressWarnings("unchecked")
public static final <TT> Collection<TT> create(
Collection<TT> collection,
int targetResultCount, long cutOffAfterTimeMs, int cutOffAfterCount)
{
if (targetResultCount <= 0)
{
targetResultCount = collection.size();
}
// Create the mixin
DelegatingIntroductionInterceptor mixin = new PermissionCheckCollectionMixin<Integer>(
targetResultCount,
cutOffAfterTimeMs,
cutOffAfterCount);
// Create the advisor
IntroductionAdvisor advisor = new DefaultIntroductionAdvisor(mixin, PermissionCheckCollection.class);
// Proxy
ProxyFactory pf = new ProxyFactory(collection);
pf.addAdvisor(advisor);
Object proxiedObject = pf.getProxy();
// Done
return (Collection<TT>) proxiedObject;
}
}
}
package org.alfresco.repo.security.permissions;
import java.util.Collection;
import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.support.DefaultIntroductionAdvisor;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
/**
* Interface for collection-based results that describe permission filtering behaviour around cut-off limits.
*
* @author Derek Hulley
* @since 4.0
*/
public interface PermissionCheckCollection<T>
{
/**
* Get the desired number of results. Permission checks can stop once the number of return objects reaches this number.
*
* @return the number of results desired
*/
int getTargetResultCount();
/**
* Get the maximum time for permission checks to execute before cutting the results off. <br/>
* Zero: Ignore this value.
*
* @return the time allowed for permission checks before cutoff
*/
long getCutOffAfterTimeMs();
/**
* Get the maximum number of permission checks to perform before cutting the results off
*
* @return the maximum number of permission checks before cutoff
*/
int getCutOffAfterCount();
/**
* Helper 'introduction' to allow simple addition of the {@link PermissionCheckCollection} interface to existing collections.
*
* @param <T>
* the type of the <code>Collection</code> in use
*
* @author Derek Hulley
* @since 4.0
*/
@SuppressWarnings("serial")
class PermissionCheckCollectionMixin<T> extends DelegatingIntroductionInterceptor implements PermissionCheckCollection<T>
{
private final int targetResultCount;
private final long cutOffAfterTimeMs;
private final int cutOffAfterCount;
private PermissionCheckCollectionMixin(int targetResultCount, long cutOffAfterTimeMs, int cutOffAfterCount)
{
super();
this.targetResultCount = targetResultCount;
this.cutOffAfterTimeMs = cutOffAfterTimeMs;
this.cutOffAfterCount = cutOffAfterCount;
if (cutOffAfterTimeMs <= 0)
{
cutOffAfterTimeMs = 0;
}
if (cutOffAfterCount <= 0)
{
cutOffAfterCount = 0;
}
}
@Override
public int getTargetResultCount()
{
return targetResultCount;
}
@Override
public long getCutOffAfterTimeMs()
{
return cutOffAfterTimeMs;
}
@Override
public int getCutOffAfterCount()
{
return cutOffAfterCount;
}
/**
* Helper method to create a {@link PermissionCheckCollection} from an existing <code>Collection</code>
*
* @param <TT>
* the type of the <code>Collection</code>
* @param collection
* the <code>Collection</code> to proxy
* @param targetResultCount
* the desired number of results or default to the collection size
* @param cutOffAfterTimeMs
* the number of milliseconds to wait before cut-off or zero to use the system default time-based cut-off.
* @param cutOffAfterCount
* the number of permission checks to process before cut-off or zero to use the system default count-based cut-off.
* @return a <code>Collection</code> of the same type but including the {@link PermissionCheckCollection} interface
*/
@SuppressWarnings("unchecked")
public static <TT> Collection<TT> create(
Collection<TT> collection,
int targetResultCount, long cutOffAfterTimeMs, int cutOffAfterCount)
{
if (targetResultCount <= 0)
{
targetResultCount = collection.size();
}
// Create the mixin
DelegatingIntroductionInterceptor mixin = new PermissionCheckCollectionMixin<>(
targetResultCount,
cutOffAfterTimeMs,
cutOffAfterCount);
// Create the advisor
IntroductionAdvisor advisor = new DefaultIntroductionAdvisor(mixin, PermissionCheckCollection.class);
// Create Proxy
return (Collection<TT>) ProxyFactoryUtils.createProxy(collection, advisor);
}
}
}

View File

@@ -2,161 +2,160 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2024 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
* 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.security.permissions;
import java.util.Collection;
import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultIntroductionAdvisor;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
/**
* Interface for collection-based results that carry extra information
* about the state of permission cut-offs.
*
* @author Derek Hulley
* @since 4.0
*/
public interface PermissionCheckedCollection<T>
{
/**
* Check if the results have been truncated by permission check limits.
*
* @return <tt>true</tt> - if the results (usually a collection) have been
* cut off by permission check limits
*/
boolean isCutOff();
/**
* Get the number of objects in the original (unfiltered) collection that did
* <b>not</b> have any permission checks.
*
* @return number of entries from the original collection that were not checked
*/
int sizeUnchecked();
/**
* Get the number of objects in the original (unfiltered) collection.
*
* @return number of entries in the original, pre-checked collection
*/
int sizeOriginal();
/**
* Helper 'introduction' to allow simple addition of the {@link PermissionCheckedCollection} interface to
* existing collections.
*
* @param <T> the type of the <code>Collection</code> in use
*
* @author Derek Hulley
* @since 4.0
*/
@SuppressWarnings("serial")
public static class PermissionCheckedCollectionMixin<T> extends DelegatingIntroductionInterceptor implements PermissionCheckedCollection<T>
{
private final boolean isCutOff;
private final int sizeUnchecked;
private final int sizeOriginal;
private PermissionCheckedCollectionMixin(boolean isCutOff, int sizeUnchecked, int sizeOriginal)
{
super();
this.isCutOff = isCutOff;
this.sizeUnchecked = sizeUnchecked;
this.sizeOriginal = sizeOriginal;
}
@Override
public boolean isCutOff()
{
return isCutOff;
}
@Override
public int sizeUnchecked()
{
return sizeUnchecked;
}
@Override
public int sizeOriginal()
{
return sizeOriginal;
}
/**
* Helper method to create a {@link PermissionCheckedCollection} from an existing <code>Collection</code>
* by applying the same values as present on a potentially permission-checked source. If the
* existing checked source is <b>NOT</b> permission-checked, then the collection will not be
* decorated.
*
* @param <TT> the type of the <code>Collection</code>
* @param collection the <code>Collection</code> to proxy
* @param checkedSource a collection that might implement {@link PermissionCheckedCollection}
* @return a <code>Collection</code> of the same type but including the
* {@link PermissionCheckedCollection} interface
*/
public static final <TT> Collection<TT> create(
Collection<TT> collection, Collection<?> checkedSource)
{
if (checkedSource instanceof PermissionCheckedCollection)
{
PermissionCheckedCollection<?> source = (PermissionCheckedCollection<?>) checkedSource;
return create(collection, source.isCutOff(), source.sizeUnchecked(), source.sizeOriginal());
}
else
{
return collection;
}
}
/**
* Helper method to create a {@link PermissionCheckedCollection} from an existing <code>Collection</code>
*
* @param <TT> the type of the <code>Collection</code>
* @param collection the <code>Collection</code> to proxy
* @param isCutOff <tt>true</tt> if permission checking was cut off before completion
* @param sizeUnchecked number of entries from the original collection that were not checked
* @param sizeOriginal number of entries in the original, pre-checked collection
* @return a <code>Collection</code> of the same type but including the
* {@link PermissionCheckedCollection} interface
*/
@SuppressWarnings("unchecked")
public static final <TT> Collection<TT> create(
Collection<TT> collection,
boolean isCutOff, int sizeUnchecked, int sizeOriginal)
{
// Create the mixin
DelegatingIntroductionInterceptor mixin = new PermissionCheckedCollectionMixin<Integer>(
isCutOff,
sizeUnchecked,
sizeOriginal
);
// Create the advisor
IntroductionAdvisor advisor = new DefaultIntroductionAdvisor(mixin, PermissionCheckedCollection.class);
// Proxy
ProxyFactory pf = new ProxyFactory(collection);
pf.addAdvisor(advisor);
Object proxiedObject = pf.getProxy();
// Done
return (Collection<TT>) proxiedObject;
}
}
}
package org.alfresco.repo.security.permissions;
import java.util.Collection;
import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.support.DefaultIntroductionAdvisor;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
/**
* Interface for collection-based results that carry extra information about the state of permission cut-offs.
*
* @author Derek Hulley
* @since 4.0
*/
public interface PermissionCheckedCollection<T>
{
/**
* Check if the results have been truncated by permission check limits.
*
* @return <tt>true</tt> - if the results (usually a collection) have been cut off by permission check limits
*/
boolean isCutOff();
/**
* Get the number of objects in the original (unfiltered) collection that did <b>not</b> have any permission checks.
*
* @return number of entries from the original collection that were not checked
*/
int sizeUnchecked();
/**
* Get the number of objects in the original (unfiltered) collection.
*
* @return number of entries in the original, pre-checked collection
*/
int sizeOriginal();
/**
* Helper 'introduction' to allow simple addition of the {@link PermissionCheckedCollection} interface to existing collections.
*
* @param <T>
* the type of the <code>Collection</code> in use
*
* @author Derek Hulley
* @since 4.0
*/
@SuppressWarnings("serial")
class PermissionCheckedCollectionMixin<T> extends DelegatingIntroductionInterceptor implements PermissionCheckedCollection<T>
{
private final boolean isCutOff;
private final int sizeUnchecked;
private final int sizeOriginal;
private PermissionCheckedCollectionMixin(boolean isCutOff, int sizeUnchecked, int sizeOriginal)
{
super();
this.isCutOff = isCutOff;
this.sizeUnchecked = sizeUnchecked;
this.sizeOriginal = sizeOriginal;
}
@Override
public boolean isCutOff()
{
return isCutOff;
}
@Override
public int sizeUnchecked()
{
return sizeUnchecked;
}
@Override
public int sizeOriginal()
{
return sizeOriginal;
}
/**
* Helper method to create a {@link PermissionCheckedCollection} from an existing <code>Collection</code> by applying the same values as present on a potentially permission-checked source. If the existing checked source is <b>NOT</b> permission-checked, then the collection will not be decorated.
*
* @param <TT>
* the type of the <code>Collection</code>
* @param collection
* the <code>Collection</code> to proxy
* @param checkedSource
* a collection that might implement {@link PermissionCheckedCollection}
* @return a <code>Collection</code> of the same type but including the {@link PermissionCheckedCollection} interface
*/
public static <TT> Collection<TT> create(
Collection<TT> collection, Collection<?> checkedSource)
{
if (checkedSource instanceof PermissionCheckedCollection)
{
PermissionCheckedCollection<?> source = (PermissionCheckedCollection<?>) checkedSource;
return create(collection, source.isCutOff(), source.sizeUnchecked(), source.sizeOriginal());
}
else
{
return collection;
}
}
/**
* Helper method to create a {@link PermissionCheckedCollection} from an existing <code>Collection</code>
*
* @param <TT>
* the type of the <code>Collection</code>
* @param collection
* the <code>Collection</code> to proxy
* @param isCutOff
* <tt>true</tt> if permission checking was cut off before completion
* @param sizeUnchecked
* number of entries from the original collection that were not checked
* @param sizeOriginal
* number of entries in the original, pre-checked collection
* @return a <code>Collection</code> of the same type but including the {@link PermissionCheckedCollection} interface
*/
@SuppressWarnings("unchecked")
public static <TT> Collection<TT> create(
Collection<TT> collection,
boolean isCutOff, int sizeUnchecked, int sizeOriginal)
{
// Create the mixin
DelegatingIntroductionInterceptor mixin = new PermissionCheckedCollectionMixin<>(
isCutOff,
sizeUnchecked,
sizeOriginal);
// Create the advisor
IntroductionAdvisor advisor = new DefaultIntroductionAdvisor(mixin, PermissionCheckedCollection.class);
// Create Proxy
return (Collection<TT>) ProxyFactoryUtils.createProxy(collection, advisor);
}
}
}

View File

@@ -0,0 +1,59 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2024 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.security.permissions;
import java.util.Collection;
import java.util.Deque;
import java.util.List;
import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.framework.ProxyFactory;
class ProxyFactoryUtils
{
private ProxyFactoryUtils()
{}
/**
* Delegate creation of {@link ProxyFactory} and proxy to have control over it in one place.
*
* @param collection
* given collection for ProxyFactory.
* @param advisor
* given advisor for ProxyFactory.
* @return the proxy object.
*/
protected static Object createProxy(Collection<?> collection, IntroductionAdvisor advisor)
{
ProxyFactory pf = new ProxyFactory(collection);
pf.addAdvisor(advisor);
if (pf.isInterfaceProxied(List.class) && pf.isInterfaceProxied(Deque.class))
{
pf.removeInterface(Deque.class);
}
return pf.getProxy();
}
}

View File

@@ -163,7 +163,7 @@ cache.node.nodesSharedCache.tx.statsEnabled=${caches.tx.statsEnabled}
cache.node.nodesSharedCache.maxItems=250000
cache.node.nodesSharedCache.timeToLiveSeconds=300
cache.node.nodesSharedCache.maxIdleSeconds=0
cache.node.nodesSharedCache.cluster.type=invalidating
cache.node.nodesSharedCache.cluster.type=fully-distributed
cache.node.nodesSharedCache.backup-count=1
cache.node.nodesSharedCache.eviction-policy=LRU
cache.node.nodesSharedCache.merge-policy=com.hazelcast.spi.merge.PutIfAbsentMergePolicy
@@ -709,4 +709,4 @@ cache.ldapInitialDirContextCache.cluster.type=fully-distributed
cache.ldapInitialDirContextCache.backup-count=1
cache.ldapInitialDirContextCache.eviction-policy=NONE
cache.ldapInitialDirContextCache.merge-policy=com.hazelcast.spi.merge.LatestUpdateMergePolicy
cache.ldapInitialDirContextCache.readBackupData=false
cache.ldapInitialDirContextCache.readBackupData=false

View File

@@ -21,391 +21,431 @@
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
* #L%
*/
package org.alfresco.repo.cache.lookup;
import java.sql.Savepoint;
import java.util.Map;
import java.util.TreeMap;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.alfresco.repo.cache.MemoryCache;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAO;
import org.alfresco.repo.domain.control.ControlDAO;
import org.alfresco.util.EqualsHelper;
import org.alfresco.util.Pair;
import org.mockito.Mockito;
import org.springframework.dao.DuplicateKeyException;
/**
* A cache for two-way lookups of database entities. These are characterized by having a unique
* key (perhaps a database ID) and a separate unique key that identifies the object.
* <p>
* The keys must have good <code>equals</code> and </code>hashCode</code> implementations and
* must respect the case-sensitivity of the use-case.
*
* @author Derek Hulley
* @since 3.2
*/
public class EntityLookupCacheTest extends TestCase implements EntityLookupCallbackDAO<Long, Object, String>
{
SimpleCache<Long, Object> cache;
private EntityLookupCache<Long, Object, String> entityLookupCacheA;
private EntityLookupCache<Long, Object, String> entityLookupCacheB;
private TreeMap<Long, String> database;
private ControlDAO controlDAO;
@Override
protected void setUp() throws Exception
{
cache = new MemoryCache<Long, Object>();
entityLookupCacheA = new EntityLookupCache<Long, Object, String>(cache, "A", this);
entityLookupCacheB = new EntityLookupCache<Long, Object, String>(cache, "B", this);
database = new TreeMap<Long, String>();
controlDAO = Mockito.mock(ControlDAO.class);
Mockito.when(controlDAO.createSavepoint(Mockito.anyString())).thenReturn(Mockito.mock(Savepoint.class));
}
public void testLookupsUsingIncorrectValue() throws Exception
{
try
{
// Keep the "database" empty
entityLookupCacheA.getByValue(this);
}
catch (AssertionFailedError e)
{
// Expected
}
}
public void testLookupAgainstEmpty() throws Exception
{
TestValue value = new TestValue("AAA");
Pair<Long, Object> entityPair = entityLookupCacheA.getByValue(value);
assertNull(entityPair);
assertTrue(database.isEmpty());
// Now do lookup or create
entityPair = entityLookupCacheA.getOrCreateByValue(value);
assertNotNull("Expected a value to be found", entityPair);
Long entityId = entityPair.getFirst();
assertTrue("Database ID should have been created", database.containsKey(entityId));
assertEquals("Database value incorrect", value.val, database.get(entityId));
// Do lookup or create again
entityPair = entityLookupCacheA.getOrCreateByValue(value);
assertNotNull("Expected a value to be found", entityPair);
assertEquals("Expected same entity ID", entityId, entityPair.getFirst());
// Look it up using the value
entityPair = entityLookupCacheA.getByValue(value);
assertNotNull("Lookup after create should work", entityPair);
// Look it up using the ID
entityPair = entityLookupCacheA.getByKey(entityId);
assertNotNull("Lookup by key should work after create", entityPair);
assertTrue("Looked-up type incorrect", entityPair.getSecond() instanceof TestValue);
assertEquals("Looked-up type value incorrect", value, entityPair.getSecond());
}
public void testLookupAgainstExisting() throws Exception
{
// Put some values in the "database"
createValue(new TestValue("AAA"));
createValue(new TestValue("BBB"));
createValue(new TestValue("CCC"));
// Look up by value
Pair<Long, Object> entityPair = entityLookupCacheA.getByValue(new TestValue("AAA"));
assertNotNull("Expected value to be found", entityPair);
assertEquals("ID is incorrect", Long.valueOf(1), entityPair.getFirst());
// Look up by ID
entityPair = entityLookupCacheA.getByKey(Long.valueOf(2));
assertNotNull("Expected value to be found", entityPair);
// Do lookup or create
entityPair = entityLookupCacheA.getByValue(new TestValue("CCC"));
assertNotNull("Expected value to be found", entityPair);
assertEquals("ID is incorrect", Long.valueOf(3), entityPair.getFirst());
}
public void testRegions() throws Exception
{
TestValue valueAAA = new TestValue("AAA");
Pair<Long, Object> entityPairAAA = entityLookupCacheA.getOrCreateByValue(valueAAA);
assertNotNull(entityPairAAA);
assertEquals("AAA", database.get(entityPairAAA.getFirst()));
assertEquals(2, cache.getKeys().size());
TestValue valueBBB = new TestValue("BBB");
Pair<Long, Object> entityPairBBB = entityLookupCacheB.getOrCreateByValue(valueBBB);
assertNotNull(entityPairBBB);
assertEquals("BBB", database.get(entityPairBBB.getFirst()));
assertEquals(4, cache.getKeys().size());
// Now cross-check against the caches and make sure that the cache
entityPairBBB = entityLookupCacheA.getByValue(valueBBB);
assertEquals(6, cache.getKeys().size());
entityPairBBB = entityLookupCacheB.getByValue(valueAAA);
assertEquals(8, cache.getKeys().size());
}
public void testNullLookups() throws Exception
{
TestValue valueNull = null;
Pair<Long, Object> entityPairNull = entityLookupCacheA.getOrCreateByValue(valueNull);
assertNotNull(entityPairNull);
assertTrue(database.containsKey(entityPairNull.getFirst()));
assertNull(database.get(entityPairNull.getFirst()));
assertEquals(2, cache.getKeys().size());
// Look it up again
Pair<Long, Object> entityPairCheck = entityLookupCacheA.getOrCreateByValue(valueNull);
assertNotNull(entityPairNull);
assertTrue(database.containsKey(entityPairNull.getFirst()));
assertNull(database.get(entityPairNull.getFirst()));
assertEquals(entityPairNull, entityPairCheck);
}
public void testGetOrCreate() throws Exception
{
TestValue valueOne = new TestValue(getName() + "-ONE");
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
assertNotNull(entityPairOne);
Long id = entityPairOne.getFirst();
assertEquals(valueOne.val, database.get(id));
assertEquals(2, cache.getKeys().size());
Pair<Long, Object> entityPairOneCheck = entityLookupCacheA.getOrCreateByValue(valueOne);
assertNotNull(entityPairOneCheck);
assertEquals(id, entityPairOneCheck.getFirst());
}
public void testCreateOrGet() throws Exception
{
TestValue valueOne = new TestValue(getName() + "-ONE");
Pair<Long, Object> entityPairOne = entityLookupCacheA.createOrGetByValue(valueOne, controlDAO);
assertNotNull(entityPairOne);
Long id = entityPairOne.getFirst();
assertEquals(valueOne.val, database.get(id));
assertEquals(1, cache.getKeys().size());
Pair<Long, Object> entityPairOneCheck = entityLookupCacheA.createOrGetByValue(valueOne, controlDAO);
assertNotNull(entityPairOneCheck);
assertEquals(id, entityPairOneCheck.getFirst());
}
public void testUpdate() throws Exception
{
TestValue valueOne = new TestValue(getName() + "-ONE");
TestValue valueTwo = new TestValue(getName() + "-TWO");
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
assertNotNull(entityPairOne);
Long id = entityPairOne.getFirst();
assertEquals(valueOne.val, database.get(id));
assertEquals(2, cache.getKeys().size());
// Update
int updateCount = entityLookupCacheA.updateValue(id, valueTwo);
assertEquals("Update count was incorrect.", 1, updateCount);
assertEquals(valueTwo.val, database.get(id));
assertEquals(2, cache.getKeys().size());
}
public void testDeleteByKey() throws Exception
{
TestValue valueOne = new TestValue(getName() + "-ONE");
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
assertNotNull(entityPairOne);
Long id = entityPairOne.getFirst();
assertEquals(valueOne.val, database.get(id));
assertEquals(2, cache.getKeys().size());
// Delete
int deleteCount = entityLookupCacheA.deleteByKey(id);
assertEquals("Delete count was incorrect.", 1, deleteCount);
assertNull(database.get(id));
assertEquals(0, cache.getKeys().size());
}
public void testDeleteByValue() throws Exception
{
TestValue valueOne = new TestValue(getName() + "-ONE");
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
assertNotNull(entityPairOne);
Long id = entityPairOne.getFirst();
assertEquals(valueOne.val, database.get(id));
assertEquals(2, cache.getKeys().size());
// Delete
int deleteCount = entityLookupCacheA.deleteByValue(valueOne);
assertEquals("Delete count was incorrect.", 1, deleteCount);
assertNull(database.get(id));
assertEquals(0, cache.getKeys().size());
}
public void testClear() throws Exception
{
TestValue valueOne = new TestValue(getName() + "-ONE");
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
assertNotNull(entityPairOne);
Long id = entityPairOne.getFirst();
assertEquals(valueOne.val, database.get(id));
assertEquals(2, cache.getKeys().size());
// Clear it
entityLookupCacheA.clear();
assertEquals(valueOne.val, database.get(id)); // Must still be in database
assertEquals(0, cache.getKeys().size()); // ... but cache must be empty
}
/**
* Helper class to represent business object
*/
private static class TestValue
{
private final String val;
private TestValue(String val)
{
this.val = val;
}
@Override
public boolean equals(Object obj)
{
if (obj == null || !(obj instanceof TestValue))
{
return false;
}
return val.equals( ((TestValue)obj).val );
}
@Override
public int hashCode()
{
return val.hashCode();
}
}
public String getValueKey(Object value)
{
assertNotNull(value);
assertTrue(value instanceof TestValue);
String dbValue = ((TestValue)value).val;
return dbValue;
}
public Pair<Long, Object> findByKey(Long key)
{
assertNotNull(key);
String dbValue = database.get(key);
if (dbValue == null)
{
return null;
}
// Make a value object
TestValue value = new TestValue(dbValue);
return new Pair<Long, Object>(key, value);
}
public Pair<Long, Object> findByValue(Object value)
{
assertTrue(value == null || value instanceof TestValue);
String dbValue = (value == null) ? null : ((TestValue)value).val;
for (Map.Entry<Long, String> entry : database.entrySet())
{
if (EqualsHelper.nullSafeEquals(entry.getValue(), dbValue))
{
return new Pair<Long, Object>(entry.getKey(), entry.getValue());
}
}
return null;
}
/**
* Simulate creation of a new database entry
*/
public Pair<Long, Object> createValue(Object value)
{
assertTrue(value == null || value instanceof TestValue);
String dbValue = (value == null) ? null : ((TestValue)value).val;
// Kick out any duplicate values
if (database.containsValue(dbValue))
{
throw new DuplicateKeyException("Value is duplicated: " + value);
}
// Get the last key
Long lastKey = database.isEmpty() ? null : database.lastKey();
Long newKey = null;
if (lastKey == null)
{
newKey = Long.valueOf(1);
}
else
{
newKey = Long.valueOf(lastKey.longValue() + 1);
}
database.put(newKey, dbValue);
return new Pair<Long, Object>(newKey, value);
}
public int updateValue(Long key, Object value)
{
assertNotNull(key);
assertTrue(value == null || value instanceof TestValue);
// Find it
Pair<Long, Object> entityPair = findByKey(key);
if (entityPair == null)
{
return 0;
}
else
{
database.put(key, ((TestValue)value).val);
return 1;
}
}
public int deleteByKey(Long key)
{
assertNotNull(key);
if (database.containsKey(key))
{
database.remove(key);
return 1;
}
else
{
return 0;
}
}
public int deleteByValue(Object value)
{
assertTrue(value == null || value instanceof TestValue);
// Find it
Pair<Long, Object> entityPair = findByValue(value);
if (entityPair == null)
{
return 0;
}
else
{
database.remove(entityPair.getFirst());
return 1;
}
}
}
package org.alfresco.repo.cache.lookup;
import static org.junit.Assert.*;
import java.sql.Savepoint;
import java.util.Map;
import java.util.TreeMap;
import junit.framework.AssertionFailedError;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.dao.DuplicateKeyException;
import org.alfresco.repo.cache.MemoryCache;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAO;
import org.alfresco.repo.domain.control.ControlDAO;
import org.alfresco.util.EqualsHelper;
import org.alfresco.util.Pair;
/**
* A cache for two-way lookups of database entities. These are characterized by having a unique key (perhaps a database ID) and a separate unique key that identifies the object.
* <p>
* The keys must have good <code>equals</code> and </code>hashCode</code> implementations and must respect the case-sensitivity of the use-case.
*
* @author Derek Hulley
* @since 3.2
*/
public class EntityLookupCacheTest implements EntityLookupCallbackDAO<Long, Object, String>
{
SimpleCache<Long, Object> cache;
private EntityLookupCache<Long, Object, String> entityLookupCacheA;
private EntityLookupCache<Long, Object, String> entityLookupCacheB;
private TreeMap<Long, String> database;
private ControlDAO controlDAO;
@Before
protected void setUp() throws Exception
{
cache = new MemoryCache<Long, Object>();
entityLookupCacheA = new EntityLookupCache<Long, Object, String>(cache, "A", this);
entityLookupCacheB = new EntityLookupCache<Long, Object, String>(cache, "B", this);
database = new TreeMap<Long, String>();
controlDAO = Mockito.mock(ControlDAO.class);
Mockito.when(controlDAO.createSavepoint(Mockito.anyString())).thenReturn(Mockito.mock(Savepoint.class));
}
@Test
public void testLookupsUsingIncorrectValue() throws Exception
{
try
{
// Keep the "database" empty
entityLookupCacheA.getByValue(this);
}
catch (AssertionFailedError e)
{
// Expected
}
}
@Test
public void testLookupAgainstEmpty() throws Exception
{
TestValue value = new TestValue("AAA");
Pair<Long, Object> entityPair = entityLookupCacheA.getByValue(value);
assertNull(entityPair);
assertTrue(database.isEmpty());
// Now do lookup or create
entityPair = entityLookupCacheA.getOrCreateByValue(value);
assertNotNull("Expected a value to be found", entityPair);
Long entityId = entityPair.getFirst();
assertTrue("Database ID should have been created", database.containsKey(entityId));
assertEquals("Database value incorrect", value.val, database.get(entityId));
// Do lookup or create again
entityPair = entityLookupCacheA.getOrCreateByValue(value);
assertNotNull("Expected a value to be found", entityPair);
assertEquals("Expected same entity ID", entityId, entityPair.getFirst());
// Look it up using the value
entityPair = entityLookupCacheA.getByValue(value);
assertNotNull("Lookup after create should work", entityPair);
// Look it up using the ID
entityPair = entityLookupCacheA.getByKey(entityId);
assertNotNull("Lookup by key should work after create", entityPair);
assertTrue("Looked-up type incorrect", entityPair.getSecond() instanceof TestValue);
assertEquals("Looked-up type value incorrect", value, entityPair.getSecond());
}
@Test
public void testLookupAgainstExisting() throws Exception
{
// Put some values in the "database"
createValue(new TestValue("AAA"));
createValue(new TestValue("BBB"));
createValue(new TestValue("CCC"));
// Look up by value
Pair<Long, Object> entityPair = entityLookupCacheA.getByValue(new TestValue("AAA"));
assertNotNull("Expected value to be found", entityPair);
assertEquals("ID is incorrect", Long.valueOf(1), entityPair.getFirst());
// Look up by ID
entityPair = entityLookupCacheA.getByKey(Long.valueOf(2));
assertNotNull("Expected value to be found", entityPair);
// Do lookup or create
entityPair = entityLookupCacheA.getByValue(new TestValue("CCC"));
assertNotNull("Expected value to be found", entityPair);
assertEquals("ID is incorrect", Long.valueOf(3), entityPair.getFirst());
}
@Test
public void testRegions() throws Exception
{
TestValue valueAAA = new TestValue("AAA");
Pair<Long, Object> entityPairAAA = entityLookupCacheA.getOrCreateByValue(valueAAA);
assertNotNull(entityPairAAA);
assertEquals("AAA", database.get(entityPairAAA.getFirst()));
assertEquals(2, cache.getKeys().size());
TestValue valueBBB = new TestValue("BBB");
Pair<Long, Object> entityPairBBB = entityLookupCacheB.getOrCreateByValue(valueBBB);
assertNotNull(entityPairBBB);
assertEquals("BBB", database.get(entityPairBBB.getFirst()));
assertEquals(4, cache.getKeys().size());
// Now cross-check against the caches and make sure that the cache
entityPairBBB = entityLookupCacheA.getByValue(valueBBB);
assertEquals(6, cache.getKeys().size());
entityPairBBB = entityLookupCacheB.getByValue(valueAAA);
assertEquals(8, cache.getKeys().size());
}
@Test
public void testNullLookups() throws Exception
{
TestValue valueNull = null;
Pair<Long, Object> entityPairNull = entityLookupCacheA.getOrCreateByValue(valueNull);
assertNotNull(entityPairNull);
assertTrue(database.containsKey(entityPairNull.getFirst()));
assertNull(database.get(entityPairNull.getFirst()));
assertEquals(2, cache.getKeys().size());
// Look it up again
Pair<Long, Object> entityPairCheck = entityLookupCacheA.getOrCreateByValue(valueNull);
assertNotNull(entityPairNull);
assertTrue(database.containsKey(entityPairNull.getFirst()));
assertNull(database.get(entityPairNull.getFirst()));
assertEquals(entityPairNull, entityPairCheck);
}
@Test
public void testGetOrCreate() throws Exception
{
TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
assertNotNull(entityPairOne);
Long id = entityPairOne.getFirst();
assertEquals(valueOne.val, database.get(id));
assertEquals(2, cache.getKeys().size());
Pair<Long, Object> entityPairOneCheck = entityLookupCacheA.getOrCreateByValue(valueOne);
assertNotNull(entityPairOneCheck);
assertEquals(id, entityPairOneCheck.getFirst());
}
@Test
public void testCreateOrGet() throws Exception
{
TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
Pair<Long, Object> entityPairOne = entityLookupCacheA.createOrGetByValue(valueOne, controlDAO);
assertNotNull(entityPairOne);
Long id = entityPairOne.getFirst();
assertEquals(valueOne.val, database.get(id));
// We cache both by value and by key, so we should have 2 entries
assertEquals(2, cache.getKeys().size());
Pair<Long, Object> entityPairOneCheck = entityLookupCacheA.createOrGetByValue(valueOne, controlDAO);
assertNotNull(entityPairOneCheck);
assertEquals(id, entityPairOneCheck.getFirst());
}
@Test
public void testUpdate() throws Exception
{
TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
TestValue valueTwo = new TestValue(getClass().getName() + "-TWO");
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
assertNotNull(entityPairOne);
Long id = entityPairOne.getFirst();
assertEquals(valueOne.val, database.get(id));
assertEquals(2, cache.getKeys().size());
// Update
int updateCount = entityLookupCacheA.updateValue(id, valueTwo);
assertEquals("Update count was incorrect.", 1, updateCount);
assertEquals(valueTwo.val, database.get(id));
assertEquals(2, cache.getKeys().size());
}
@Test
public void testDeleteByKey() throws Exception
{
TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
assertNotNull(entityPairOne);
Long id = entityPairOne.getFirst();
assertEquals(valueOne.val, database.get(id));
assertEquals(2, cache.getKeys().size());
// Delete
int deleteCount = entityLookupCacheA.deleteByKey(id);
assertEquals("Delete count was incorrect.", 1, deleteCount);
assertNull(database.get(id));
assertEquals(0, cache.getKeys().size());
}
@Test
public void testDeleteByValue() throws Exception
{
TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
assertNotNull(entityPairOne);
Long id = entityPairOne.getFirst();
assertEquals(valueOne.val, database.get(id));
assertEquals(2, cache.getKeys().size());
// Delete
int deleteCount = entityLookupCacheA.deleteByValue(valueOne);
assertEquals("Delete count was incorrect.", 1, deleteCount);
assertNull(database.get(id));
assertEquals(0, cache.getKeys().size());
}
@Test
public void testClear() throws Exception
{
TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
assertNotNull(entityPairOne);
Long id = entityPairOne.getFirst();
assertEquals(valueOne.val, database.get(id));
assertEquals(2, cache.getKeys().size());
// Clear it
entityLookupCacheA.clear();
assertEquals(valueOne.val, database.get(id)); // Must still be in database
assertEquals(0, cache.getKeys().size()); // ... but cache must be empty
}
@Test
public void testGetCachedValue() throws Exception
{
// Create a new value
TestValue valueCached = new TestValue(getClass().getName() + "-CACHED");
Pair<Long, Object> entityPairOne = entityLookupCacheA.createOrGetByValue(valueCached, controlDAO);
assertNotNull(entityPairOne);
Long id = entityPairOne.getFirst();
// We cache both by value and by key, so we should have 2 entries
assertEquals(2, cache.getKeys().size());
// Check the cache for the previously created value
Pair<Long, Object> entityPairCacheCheck = entityLookupCacheA.getCachedEntityByValue(valueCached);
assertNotNull(entityPairCacheCheck);
assertEquals(id, entityPairCacheCheck.getFirst());
// Clear the cache and attempt to retrieve it again
entityLookupCacheA.clear();
entityPairCacheCheck = entityLookupCacheA.getCachedEntityByValue(valueCached);
// Since we are only retrieving from cache, the value should not be found
assertNull(entityPairCacheCheck);
}
/**
* Helper class to represent business object
*/
private static class TestValue
{
private final String val;
private TestValue(String val)
{
this.val = val;
}
@Override
public boolean equals(Object obj)
{
if (obj == null || !(obj instanceof TestValue))
{
return false;
}
return val.equals(((TestValue) obj).val);
}
@Override
public int hashCode()
{
return val.hashCode();
}
}
public String getValueKey(Object value)
{
assertNotNull(value);
assertTrue(value instanceof TestValue);
String dbValue = ((TestValue) value).val;
return dbValue;
}
public Pair<Long, Object> findByKey(Long key)
{
assertNotNull(key);
String dbValue = database.get(key);
if (dbValue == null)
{
return null;
}
// Make a value object
TestValue value = new TestValue(dbValue);
return new Pair<Long, Object>(key, value);
}
public Pair<Long, Object> findByValue(Object value)
{
assertTrue(value == null || value instanceof TestValue);
String dbValue = (value == null) ? null : ((TestValue) value).val;
for (Map.Entry<Long, String> entry : database.entrySet())
{
if (EqualsHelper.nullSafeEquals(entry.getValue(), dbValue))
{
return new Pair<Long, Object>(entry.getKey(), entry.getValue());
}
}
return null;
}
/**
* Simulate creation of a new database entry
*/
public Pair<Long, Object> createValue(Object value)
{
assertTrue(value == null || value instanceof TestValue);
String dbValue = (value == null) ? null : ((TestValue) value).val;
// Kick out any duplicate values
if (database.containsValue(dbValue))
{
throw new DuplicateKeyException("Value is duplicated: " + value);
}
// Get the last key
Long lastKey = database.isEmpty() ? null : database.lastKey();
Long newKey = null;
if (lastKey == null)
{
newKey = Long.valueOf(1);
}
else
{
newKey = Long.valueOf(lastKey.longValue() + 1);
}
database.put(newKey, dbValue);
return new Pair<Long, Object>(newKey, value);
}
public int updateValue(Long key, Object value)
{
assertNotNull(key);
assertTrue(value == null || value instanceof TestValue);
// Find it
Pair<Long, Object> entityPair = findByKey(key);
if (entityPair == null)
{
return 0;
}
else
{
database.put(key, ((TestValue) value).val);
return 1;
}
}
public int deleteByKey(Long key)
{
assertNotNull(key);
if (database.containsKey(key))
{
database.remove(key);
return 1;
}
else
{
return 0;
}
}
public int deleteByValue(Object value)
{
assertTrue(value == null || value instanceof TestValue);
// Find it
Pair<Long, Object> entityPair = findByValue(value);
if (entityPair == null)
{
return 0;
}
else
{
database.remove(entityPair.getFirst());
return 1;
}
}
}

View File

@@ -59,7 +59,7 @@ services:
CLIENT_SSL_TRUST_STORE_TYPE: "JCEKS"
keycloak:
profiles: ["with-sso"]
image: quay.io/keycloak/keycloak:24.0.3
image: quay.io/keycloak/keycloak:25.0.6
environment:
- KEYCLOAK_ADMIN=admin
- KEYCLOAK_ADMIN_PASSWORD=admin