Compare commits

...

250 Commits

Author SHA1 Message Date
alfresco-build
07cc9aa86e [maven-release-plugin][skip ci] prepare for next development iteration 2023-04-09 00:06:55 +00:00
alfresco-build
7db550cee1 [maven-release-plugin][skip ci] prepare release 20.139 2023-04-09 00:06:52 +00:00
Alfresco CI User
937d80327f [force] Force release for 2023-04-09. 2023-04-09 00:03:20 +00:00
alfresco-build
8abff868cc [maven-release-plugin][skip ci] prepare for next development iteration 2023-04-07 13:02:26 +00:00
alfresco-build
ffae8a62a1 [maven-release-plugin][skip ci] prepare release 20.138 2023-04-07 13:02:23 +00:00
kmagdziarz
0350c966df [ACS-5020] Ignore intermittent tests 2023-04-07 14:14:33 +02:00
kmagdziarz
833a7675bc [ACS-5020] Ignore intermittent test 2023-04-07 11:28:35 +02:00
Kacper Magdziarz
8aa263f377 [ACS-4462] Add connection manager builder for httpClient4Factory (#1864)
Co-authored-by: Marcin Strankowski <74721865+mstrankowski@users.noreply.github.com>
2023-04-06 16:06:03 +02:00
alfresco-build
73f30b7ef2 [maven-release-plugin][skip ci] prepare for next development iteration 2023-04-05 15:55:43 +00:00
alfresco-build
4bf58cc99c [maven-release-plugin][skip ci] prepare release 20.137 2023-04-05 15:55:40 +00:00
MohinishSah
d53881d87f Merge pull request #1862 from Alfresco/fix/MNT-22888
MNT-22888: Fix for WikiPages with Link
2023-04-05 15:10:19 +00:00
rrajoria
937601020d MNT-22888: Fix for WikiPages with Link 2023-04-05 18:51:34 +05:30
alfresco-build
429b389b08 [maven-release-plugin][skip ci] prepare for next development iteration 2023-04-05 12:28:56 +00:00
alfresco-build
c944f3560c [maven-release-plugin][skip ci] prepare release 20.136 2023-04-05 12:28:53 +00:00
Sara
40ff2558f5 Feature/acs 4718 remove rmi email service (#1843)
* ACS-4718 Remove EmailServiceRemotable.java

* ACS-4718 Remove use of Rmi in SubethaEmailMessage classes

* ACS-4718 Remove Rmi classes called by SubethaEmailMessage classes

* ACS-4718 Remove rmi related classes

* ACS-4718 Cleanup email classes after rmi removal

---------

Co-authored-by: pzurek <Piotr.Zurek@hyland.com>
2023-04-05 12:47:30 +01:00
Tom Page
4f69f28586 ACS-4932 Fix issues around renaming tags. (#1847)
* ACS-4932 Rename tag node without the id changing.

* ACS-4932 Use fake tag to fire events for all nodes.

* ACS-4932 Use event generator to create events for renamed tags.

* ACS-4932 Fix unit test.

* ACS-4932 Fix tag update tests.

* ACS-4932 Tag names are always lower case.

* ACS-4932 Update TAS tag tests to use lowercase tag names.
2023-04-05 11:37:16 +01:00
alfresco-build
aa66dd748f [maven-release-plugin][skip ci] prepare for next development iteration 2023-04-05 07:07:10 +00:00
alfresco-build
bf9c8ab870 [maven-release-plugin][skip ci] prepare release 20.135 2023-04-05 07:07:07 +00:00
Kacper Magdziarz
959cf8f13f Feature/acs 4462 mtls e2e test (#1861)
ACS-4462 add empty response handling
2023-04-05 08:22:25 +02:00
alfresco-build
8b4285afbd [maven-release-plugin][skip ci] prepare for next development iteration 2023-04-05 05:19:09 +00:00
alfresco-build
7143f5975f [maven-release-plugin][skip ci] prepare release 20.134 2023-04-05 05:19:05 +00:00
Piotr Żurek
31944bfc0f ACS-4971 Restore IDS public client configuration (#1860) 2023-04-05 06:30:26 +02:00
alfresco-build
c874fa4e51 [maven-release-plugin][skip ci] prepare for next development iteration 2023-04-04 13:19:36 +00:00
alfresco-build
5780871f0c [maven-release-plugin][skip ci] prepare release 20.133 2023-04-04 13:19:33 +00:00
kcichonczyk
85ef347dc3 [ACS-4839] bump ATS versions (#1859) 2023-04-04 14:34:18 +02:00
Domenico Sibilio
0a76858778 ACS-4970 Bump json-path to 2.8.0 and json-smart to 2.4.10 (#1854) 2023-04-04 14:03:18 +02:00
alfresco-build
6db6245e53 [maven-release-plugin][skip ci] prepare for next development iteration 2023-04-04 08:13:27 +00:00
alfresco-build
7b572a3e02 [maven-release-plugin][skip ci] prepare release 20.132 2023-04-04 08:13:24 +00:00
Piotr Żurek
b1cbebacc3 ACS-4891 Configurable Public Key Cache (#1858) 2023-04-04 09:25:22 +02:00
alfresco-build
0bbe73aee1 [maven-release-plugin][skip ci] prepare for next development iteration 2023-04-03 19:15:09 +00:00
alfresco-build
9135fdae8b [maven-release-plugin][skip ci] prepare release 20.131 2023-04-03 19:15:05 +00:00
MohinishSah
0cb0cfa8f6 Merge pull request #1857 from Alfresco/ACA-4689
added the new email template
2023-04-03 16:46:48 +00:00
alfresco-build
5a96cce5bb [maven-release-plugin][skip ci] prepare for next development iteration 2023-04-03 16:20:26 +00:00
alfresco-build
c999fb25b9 [maven-release-plugin][skip ci] prepare release 20.130 2023-04-03 16:20:23 +00:00
Mohinish Sah
3c2643dbf2 added the new email template 2023-04-03 21:09:46 +05:30
Piotr Żurek
106e398393 ACS-4895 Restore support for providing IDS public key (#1856) 2023-04-03 17:35:14 +02:00
alfresco-build
1c45748f9a [maven-release-plugin][skip ci] prepare for next development iteration 2023-04-03 09:15:11 +00:00
alfresco-build
2b7ff213a9 [maven-release-plugin][skip ci] prepare release 20.129 2023-04-03 09:15:08 +00:00
Piotr Żurek
f693c49e87 ACS-4893 ACS-4890 Restore connection/TLS/mTLS IDS configuration (#1853)
* Unify http client for all IDS related activities
* Restore connection configuration
* Restore TLS/mTLS configuration
2023-04-03 10:30:01 +02:00
alfresco-build
0f156ba259 [maven-release-plugin][skip ci] prepare for next development iteration 2023-04-02 00:06:41 +00:00
alfresco-build
31a0a51290 [maven-release-plugin][skip ci] prepare release 20.128 2023-04-02 00:06:38 +00:00
Alfresco CI User
c8fe044993 [force] Force release for 2023-04-02. 2023-04-02 00:03:22 +00:00
alfresco-build
28058864e6 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-31 14:13:44 +00:00
alfresco-build
23ccb397f4 [maven-release-plugin][skip ci] prepare release 20.127 2023-03-31 14:13:40 +00:00
Piotr Żurek
73ae0d0a6e ACS-4847 Remove Keycloak dependencies (#1848) 2023-03-31 15:28:14 +02:00
alfresco-build
f9b5e5a6fe [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-31 10:27:19 +00:00
alfresco-build
e5bfb0298d [maven-release-plugin][skip ci] prepare release 20.126 2023-03-31 10:27:16 +00:00
Piotr Żurek
34cb9bf76a ACS-4847 Bump AOS module (#1846) 2023-03-31 10:38:09 +02:00
alfresco-build
fcbd406170 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-30 12:26:47 +00:00
alfresco-build
730ab768e9 [maven-release-plugin][skip ci] prepare release 20.125 2023-03-30 12:26:43 +00:00
Kacper Magdziarz
fab591eb9b [ACS-4459] Investigate and extend/universalize current custom Solr mTLS implementation in Repository (#1735)
* ACS-4459 Add new HttpClient Factory for Mutual TLS and implement it for Transform Service
* ACS-4462 Add e2e for MTLS
2023-03-30 13:43:42 +02:00
alfresco-build
5bb96729fc [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-29 13:04:41 +00:00
alfresco-build
a80f2c4db7 [maven-release-plugin][skip ci] prepare release 20.124 2023-03-29 13:04:38 +00:00
Piotr Żurek
82df7ce5d4 ACS-4847 Expose authorization code and refresh token grant types for the AOS (#1836) 2023-03-29 14:16:08 +02:00
alfresco-build
73a1de37f6 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-29 11:22:30 +00:00
alfresco-build
63f9a8e142 [maven-release-plugin][skip ci] prepare release 20.123 2023-03-29 11:22:25 +00:00
rrajoria
f668ed9198 Updating webscript version 2023-03-29 15:33:18 +05:30
alfresco-build
ea15673116 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-29 08:46:12 +00:00
alfresco-build
6a2a298445 [maven-release-plugin][skip ci] prepare release 20.122 2023-03-29 08:46:08 +00:00
pksingh41
4b0a6d27f9 Merge pull request #1833 from Alfresco/ACS-4636-CreateTagCountParamSupport
[ACS-4636] create tag count param support
2023-03-29 13:26:45 +05:30
Praveen Singh
b432fdc6a3 Addressed review comments 2023-03-29 11:17:00 +05:30
alfresco-build
7d62c89708 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-28 11:09:25 +00:00
alfresco-build
e79fcf64a5 [maven-release-plugin][skip ci] prepare release 20.121 2023-03-28 11:09:22 +00:00
kcichonczyk
5df4cfff58 [ACS-4929][ACS-4927] transform-service and transform-core version bump (#1837) 2023-03-28 11:42:33 +02:00
alfresco-build
66ab0c4f46 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-28 07:41:54 +00:00
alfresco-build
1d1252438c [maven-release-plugin][skip ci] prepare release 20.120 2023-03-28 07:41:50 +00:00
MohinishSah
e4ac036f9f Merge pull request #1609 from Alfresco/feature/ACA-4619-Self-service-User-password-reset-ADW
Feature/aca 4619 self service user password reset adw
2023-03-28 06:56:49 +00:00
suneet.gupta
a9ec842358 Merge branch 'master' of github.com:Alfresco/alfresco-community-repo into ACS-4636-CreateTagCountParamSupport 2023-03-28 12:18:42 +05:30
alfresco-build
9590051941 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-27 17:12:19 +00:00
alfresco-build
37618bd56a [maven-release-plugin][skip ci] prepare release 20.119 2023-03-27 17:12:14 +00:00
Tom Page
8bb30643d4 Merge pull request #1790 from Alfresco/feature/ACS-4779-Count_isssue_for_GET_call
ACS-4779:Count field issue for GET call
2023-03-27 17:23:27 +01:00
Tom Page
f0e0819bc5 ACS-4779 Remove failing integration test.
There is no way that the changes to TagsImpl should have affected shared links and so this
test must be failing due to interactions with other tests (e.g. a change in ordering, or
unexpected data in the system).

Notably a comment above the test suggests it may be duplicated by testSharedLinkCreateGetDelete
(which is another unweildy test that covers a large number of difference scenarios). The two
tests definitely contain a large amount of duplicated code.
2023-03-27 16:32:13 +01:00
suneet.gupta
5999a448d4 Merge branch 'master' of github.com:Alfresco/alfresco-community-repo into ACS-4636-CreateTagCountParamSupport 2023-03-27 13:57:30 +05:30
Mohinish Sah
8f00b44a03 Merge remote-tracking branch 'origin/master' into feature/ACA-4619-Self-service-User-password-reset-ADW 2023-03-27 13:07:01 +05:30
alfresco-build
c0d1b33201 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-26 00:06:25 +00:00
alfresco-build
4053daaf1a [maven-release-plugin][skip ci] prepare release 20.118 2023-03-26 00:06:23 +00:00
Alfresco CI User
6942294780 [force] Force release for 2023-03-26. 2023-03-26 00:03:13 +00:00
Tom Page
384177a9d9 Merge pull request #1826 from Alfresco/feature/ACS-4863_ValidateNodeWithoutPath
ACS-4863 Add method validateOrLookupNode without path.
2023-03-24 13:53:44 +00:00
alfresco-build
bbdeb5ae97 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-24 12:17:17 +00:00
alfresco-build
95fb286c14 [maven-release-plugin][skip ci] prepare release 20.117 2023-03-24 12:17:14 +00:00
Tom Page
0652cea296 ACS-4863 Move new validation method to interface as a default implementation. 2023-03-24 11:44:55 +00:00
Tom Page
a86a954771 Merge pull request #1829 from Alfresco/feature/ACS-4863_InvestigateTestFailure
ACS-4863 Restore code to allow aliases in tag and category APIs.
2023-03-24 11:37:15 +00:00
Tom Page
c27a44d711 ACS-4863 Remove test for invalid URL.
This test behaves differently against community and enterprise as the double slash is treated differently.
2023-03-24 10:16:28 +00:00
Krystian Dabrowski
ef089472fb ACS-4915: Editing an orphaned tag results in the tag being deleted (#1823)
* ACS-4915: Editing an orphaned tag results in the tag being deleted
2023-03-24 10:55:12 +01:00
alfresco-build
5769cbe54e [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-24 09:03:52 +00:00
alfresco-build
9899f16d61 [maven-release-plugin][skip ci] prepare release 20.116 2023-03-24 09:03:48 +00:00
Tom Page
219acd6c53 Revert "Revert "ACS-4863 Allow referencing nodes by aliases for tag and category application. (#1822)""
This reverts commit 70b7d5a1f8.
2023-03-24 08:21:11 +00:00
Tom Page
70b7d5a1f8 Revert "ACS-4863 Allow referencing nodes by aliases for tag and category application. (#1822)"
This reverts commit dafa77d0a0.
2023-03-24 08:20:50 +00:00
alfresco-build
279679d78a [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-24 07:03:42 +00:00
alfresco-build
6d1dcf6c7e [maven-release-plugin][skip ci] prepare release 20.115 2023-03-24 07:03:39 +00:00
suneet.gupta
18810de7d0 Merge branch 'master' of github.com:Alfresco/alfresco-community-repo into ACS-4636-CreateTagCountParamSupport 2023-03-24 08:15:46 +05:30
Piotr Żurek
fb119565dc ACS-4888 Use Keycloak free tas-utility (#1827) 2023-03-23 18:29:19 +01:00
Tom Page
10b0d7b5f0 ACS-4863 Ensure we don't accidentally introduce circular reference. 2023-03-23 16:01:07 +00:00
Tom Page
36292d749e ACS-4779 Simplify list creation in test. 2023-03-23 13:58:45 +00:00
Tom Page
c1d424b386 Merge master into ACS-4779-Count_isssue_for_GET_call. 2023-03-23 13:56:54 +00:00
Tom Page
06c6efc6c9 ACS-4779 Remove duplicate @BeforeClass method. 2023-03-23 13:51:47 +00:00
suneet.gupta
626e5b34ef [ACS-4636] Added count support for creation of tags to a node 2023-03-23 18:35:12 +05:30
Tom Page
a549859cbe ACS-4863 Add method validateOrLookupNode without path. 2023-03-23 09:11:48 +00:00
alfresco-build
f2d858f911 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-22 17:39:49 +00:00
alfresco-build
808a7c67b7 [maven-release-plugin][skip ci] prepare release 20.114 2023-03-22 17:39:45 +00:00
Tom Page
dafa77d0a0 ACS-4863 Allow referencing nodes by aliases for tag and category application. (#1822) 2023-03-22 16:54:01 +00:00
Tom Page
fd96c90c08 ACS-4779 Remove test for unsupported use case. 2023-03-22 16:02:55 +00:00
Tom Page
a41fcdff0f ACS-4779 Ensure we only include the count for tags in the GET response when requested. 2023-03-22 15:18:37 +00:00
Tom Page
ab461e34af ACS-4779 Tidy test class. 2023-03-22 14:48:01 +00:00
Tom Page
c92ef4393c Merge master into feature branch. 2023-03-22 14:46:15 +00:00
alfresco-build
ebdc9e159e [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-22 13:30:35 +00:00
alfresco-build
d885c47df7 [maven-release-plugin][skip ci] prepare release 20.113 2023-03-22 13:30:31 +00:00
Piotr Żurek
a1ccc14a93 ACS- 4752 keycloak migration - resource server role (#1818) 2023-03-22 12:58:13 +01:00
alfresco-build
0023612623 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-22 10:54:12 +00:00
alfresco-build
abf9bf8d71 [maven-release-plugin][skip ci] prepare release 20.112 2023-03-22 10:54:08 +00:00
Maciej Pichura
162e164a0c ACS-4755: Bump snakeyaml to 2.0 (#1791)
* ACS-4755: Bump snakeyaml to 2.0

* ACS-4755: Bump jackson to 2.15.0-rc1
2023-03-22 11:02:31 +01:00
alfresco-build
55b0044965 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-22 08:37:22 +00:00
alfresco-build
cfe212d52f [maven-release-plugin][skip ci] prepare release 20.111 2023-03-22 08:37:19 +00:00
MohinishSah
a3cafb7c4c updated surf-webscripts version to 8.38 2023-03-22 13:07:41 +05:30
rrajoria
9ddbda377d Test Case for Client Workspace and changes for Review comments 2023-03-22 12:56:45 +05:30
alfresco-build
e42e2b2c8e [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-21 15:42:06 +00:00
alfresco-build
9090dea75b [maven-release-plugin][skip ci] prepare release 20.110 2023-03-21 15:42:03 +00:00
Krystian Dabrowski
a26ac1f778 ACS-4857: Lookup for root tag in DB and NOT using search engine (#1810)
* ACS-4857: Lookup for root tag in DB and NOT using search engine
Also:
- removing commons-collections exclusion within test scope due to:
"Caused by: java.lang.ClassNotFoundException: org.apache.commons.collections.CollectionUtils"
2023-03-21 15:37:19 +01:00
alfresco-build
8e1d4782b4 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-21 09:49:51 +00:00
alfresco-build
4c409c925b [maven-release-plugin][skip ci] prepare release 20.109 2023-03-21 09:49:48 +00:00
Suneet Gupta
7cbfab81ce [ACS-3916][ACS-3917] Added TagId Validation for Get and Delete methods (#1815)
* [ACS-3916][ACS-3917] Added TagId Validation for Get and Delete methods

* Addressed review comments -
1. Moved duplicate code from ValidateTag methods to a private method
2. Updated the test case
2023-03-21 14:03:27 +05:30
suneet-gupta
addc2c202b [ACS-4886] Changed valdateTag method to validateNode in getTags() method in TagsImpl class 2023-03-21 07:12:21 +00:00
alfresco-build
7b523e5ad9 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-20 16:36:45 +00:00
alfresco-build
7567e83955 [maven-release-plugin][skip ci] prepare release 20.108 2023-03-20 16:36:41 +00:00
Damian Ujma
d6082f84ac ACS-4842 Upgrade Camel and Netty stack (#1814)
* ACS-4842 Update camel to 3.20.2

* ACS-4842 Update gytheio to 0.18
2023-03-20 16:07:19 +01:00
alfresco-build
4f58031178 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-19 00:08:08 +00:00
alfresco-build
7aff9366ce [maven-release-plugin][skip ci] prepare release 20.107 2023-03-19 00:08:05 +00:00
Alfresco CI User
58235fd891 [force] Force release for 2023-03-19. 2023-03-19 00:03:20 +00:00
alfresco-build
bb204c4cea [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-16 18:42:27 +00:00
alfresco-build
ae29ae8581 [maven-release-plugin][skip ci] prepare release 20.106 2023-03-16 18:42:24 +00:00
Jared Ottley
4b351fbfdc [MNT-23553] REST API call fails when using Oracle database
- Remove semicolon from sql
2023-03-16 11:56:00 -06:00
alfresco-build
3e9964d53f [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-16 08:22:17 +00:00
alfresco-build
23761f6c56 [maven-release-plugin][skip ci] prepare release 20.105 2023-03-16 08:22:13 +00:00
Piotr Żurek
2f8c283ada ACS-4844 Use matching json-smart version (#1805) 2023-03-16 07:58:25 +01:00
alfresco-build
5232b8c3fe [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-15 16:35:10 +00:00
alfresco-build
b90938bb99 [maven-release-plugin][skip ci] prepare release 20.104 2023-03-15 16:35:07 +00:00
Piotr Żurek
d243ac04c6 ACS-4844 json-path should be provided by the repo to avoid conflicts with AMPs (#1803) 2023-03-15 16:49:17 +01:00
alfresco-build
e651f6e104 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-15 14:55:19 +00:00
alfresco-build
e4776e2594 [maven-release-plugin][skip ci] prepare release 20.103 2023-03-15 14:55:15 +00:00
Tom Page
acc5425d68 ACS-4759 Replace default password hashing algorithm. (#1789)
ACS-4759 Replace default password hashing algorithm with bcrypt10.

Update tests to use sha256 hashing and fix tests that relied on md4 being the default hashing algorithm.

Remove test for default password hashing algorithm since this is being overridden for the tests anyway.
2023-03-15 14:11:25 +00:00
alfresco-build
a96e805d52 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-15 09:37:38 +00:00
alfresco-build
45a9a1ae49 [maven-release-plugin][skip ci] prepare release 20.102 2023-03-15 09:37:34 +00:00
Piotr Żurek
bf18c6b419 ACS-4751 Keycloak Migration - OAuth2 Client Role (#1798) 2023-03-15 09:53:17 +01:00
dependabot[bot]
e728489b69 Bump groovy-json from 3.0.12 to 3.0.16 (#1800)
Bumps [groovy-json](https://github.com/apache/groovy) from 3.0.12 to 3.0.16.
- [Release notes](https://github.com/apache/groovy/releases)
- [Commits](https://github.com/apache/groovy/commits)

---
updated-dependencies:
- dependency-name: org.codehaus.groovy:groovy-json
  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>
2023-03-15 09:52:13 +01:00
Krystian Dabrowski
f2fdf958f2 ACS-4023: Update GET /tags to support getting tags by name (#1766)
* ACS-4023: Update GET /tags to support getting tags by name
2023-03-15 09:46:58 +01:00
alfresco-build
0cb03c2a38 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-14 09:23:52 +00:00
alfresco-build
b13ca1f68b [maven-release-plugin][skip ci] prepare release 20.101 2023-03-14 09:23:49 +00:00
dependabot[bot]
484699b266 Bump groovy from 3.0.12 to 3.0.16 (#1801)
Bumps [groovy](https://github.com/apache/groovy) from 3.0.12 to 3.0.16.
- [Release notes](https://github.com/apache/groovy/releases)
- [Commits](https://github.com/apache/groovy/commits)

---
updated-dependencies:
- dependency-name: org.codehaus.groovy:groovy
  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>
2023-03-14 09:55:22 +01:00
alfresco-build
a15161c872 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-13 21:34:16 +00:00
alfresco-build
b3a6150655 [maven-release-plugin][skip ci] prepare release 20.100 2023-03-13 21:34:13 +00:00
Damian Ujma
42e06da4f8 ACS-4137 Change the 'build-multiarch-docker-image' profile phase to package (#1799)
* ACS-4137 Change the build-multiarch-docker-image profile phase to package

* ACS-4137 Change the build-multiarch-docker-image profile phase to package
2023-03-13 22:05:22 +01:00
alfresco-build
4ac30c2173 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-13 15:30:38 +00:00
alfresco-build
d1d84d849e [maven-release-plugin][skip ci] prepare release 20.99 2023-03-13 15:30:35 +00:00
dependabot[bot]
40af1799fe Bump alfresco/alfresco-base-tomcat from tomcat9-jre17-rockylinux8-202209261711 to tomcat9-jre17-rockylinux8-202303081618 in /packaging/docker-alfresco (#1792)
* Bump alfresco/alfresco-base-tomcat in /packaging/docker-alfresco

Bumps alfresco/alfresco-base-tomcat from tomcat9-jre17-rockylinux8-202209261711 to tomcat9-jre17-rockylinux8-202303081618.

---
updated-dependencies:
- dependency-name: alfresco/alfresco-base-tomcat
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* ACS-4768 Update freetype package for Rocky Linux 8.7

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Damian.Ujma@hyland.com <Damian.Ujma@hyland.com>
2023-03-13 16:03:51 +01:00
alfresco-build
f59e4a044a [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-13 10:24:01 +00:00
alfresco-build
5924263c18 [maven-release-plugin][skip ci] prepare release 20.98 2023-03-13 10:23:58 +00:00
Damian Ujma
082e38692f ACS-4137 Produce Multi-Arch Docker images (#1797)
* Revert "Revert "ACS-4137 Produce Multi-Arch Docker images (#1656)" (#1796)"

This reverts commit ddbaabb713.

* ACS-4137 Remove default multi-arch docker-maven-plugin configuration
2023-03-13 10:44:06 +01:00
alfresco-build
f5c4112e65 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-12 00:06:40 +00:00
alfresco-build
ad8251c054 [maven-release-plugin][skip ci] prepare release 20.97 2023-03-12 00:06:37 +00:00
Alfresco CI User
32d0182096 [force] Force release for 2023-03-12. 2023-03-12 00:03:26 +00:00
alfresco-build
2ac9779a3b [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-10 10:45:43 +00:00
alfresco-build
af999fb0fd [maven-release-plugin][skip ci] prepare release 20.96 2023-03-10 10:45:40 +00:00
Damian Ujma
ddbaabb713 Revert "ACS-4137 Produce Multi-Arch Docker images (#1656)" (#1796)
This reverts commit 212fa9b362.
2023-03-10 11:20:43 +01:00
alfresco-build
b93136f2c0 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-10 09:06:46 +00:00
alfresco-build
923aadb12a [maven-release-plugin][skip ci] prepare release 20.95 2023-03-10 09:06:42 +00:00
Wojtek Świętoń
212fa9b362 ACS-4137 Produce Multi-Arch Docker images (#1656)
* ACS-4137 Update pom.xml to use buildx

* ACS-4137 Update travis.yml to use buildx

* ACS-4137 Update travis.yml to use buildx, newest docker version

* ACS-4137 reverted unnecessary changes in travis.yml file

* ACS-4137 Change deprecated <dockerFileDir> property to <contextDir>. Moved <build> section from subpoms to global pom.

* ACS-4137 POM indentation fixes

* ACS-4137 Fix building a multiarch image (#1697)

* ACS-4137 Test wih GHA [ags][tas]

* ACS-4137 Test wih GHA [ags][tas]

* ACS-4137 Fix setting network [ags][tas]

* ACS-4137 Fix creating builder [ags][tas]

* ACS-4137 Change image tag [ags][tas]

* ACS-4137 Fix starting the registry [ags][tas]

* ACS-4137 Refactor code [ags][tas]

* ACS-4137 Uncomment all jobs [tas][ags]

* ACS-4137 Improve prepare_buildx.sh [tas][ags]

* ACS-4137 Implement timeout + remove hardcoded base image tag [tas][ags]

* ACS-4137 Added exec-maven-plugin to build-push-image for alfresco-governance-repository-community-base image

* ACS-4137 Generalize prepare_buildx.sh + increase registry timeout

* ACS-4137 merged local.registry.host and local.registry.port. Builder name changed to entitled-builder. In prepare_builder script added localhost registry checking

* ACS-4137 added build-multiarch-docker-images maven profile

* ACS-4137 added <BASE_IMAGE> arg to build-docker-images profile

* ACS-4137 added combine.self="override" attribute to configuration in build-docker-images profile to not use buildx. Delete redundant base.image.tag

* ACS-4137 Push docker images to local repository

* ACS-4137 Remove useless scripts

* ACS-4137 Move builder.name and local.registry properties to main pom.xml

* ACS-4137 Remove useless properties definitions

---------

Co-authored-by: Damian.Ujma@hyland.com <Damian.Ujma@hyland.com>
Co-authored-by: Damian Ujma <92095156+damianujma@users.noreply.github.com>
2023-03-10 09:20:22 +01:00
adam.zakrzewski
267bd98d21 ACS-4779: Adding update for tests after Tome's review 2023-03-08 16:14:21 +01:00
adam.zakrzewski
bcaeedb162 ACS-4779:Count field exists in response for get list of tags even when that field is not included. Other issue is that count field is not present when we do GET call with included count for /tags/{tagId} 2023-03-08 14:39:47 +01:00
adam.zakrzewski
f93bced905 ACS-4779:Count field exists in response for get list of tags even when that field is not included. Other issue is that count field is not present when we do GET call with included count for /tags/{tagId} 2023-03-08 14:27:16 +01:00
alfresco-build
c3e37b96b4 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-08 10:53:06 +00:00
alfresco-build
517f40e150 [maven-release-plugin][skip ci] prepare release 20.94 2023-03-08 10:53:02 +00:00
dependabot[bot]
f86d0f1fd2 Bump alfresco-jlan-embed from 7.2 to 7.4 (#1773)
Bumps alfresco-jlan-embed from 7.2 to 7.4.

---
updated-dependencies:
- dependency-name: org.alfresco:alfresco-jlan-embed
  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>
2023-03-08 10:38:40 +01:00
alfresco-build
09da8640a0 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-08 09:14:04 +00:00
alfresco-build
6bd9bf768e [maven-release-plugin][skip ci] prepare release 20.93 2023-03-08 09:14:00 +00:00
pzurek
f75c0c8f9e Just to check if the recent problems are related to Dependabot commits 2023-03-08 09:14:54 +01:00
dependabot[bot]
ff9364a3b1 Bump utility from 3.0.58 to 3.0.61 (#1776) 2023-03-07 19:22:57 +00:00
dependabot[bot]
db832663b4 Bump dependency.activemq.version from 5.17.1 to 5.17.4 (#1778) 2023-03-07 11:39:30 +00:00
alfresco-build
890e1173c5 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-07 11:36:43 +00:00
alfresco-build
9399c38741 [maven-release-plugin][skip ci] prepare release 20.92 2023-03-07 11:36:39 +00:00
mikolajbrzezinski
bf16e843fd ACS-3975 Fix Intermittent Test Failure (#1785)
Fix Intermittent Test Failure
2023-03-07 11:50:33 +01:00
dependabot[bot]
69a5a95dae Bump docker-maven-plugin from 0.40.2 to 0.42.0 (#1784) 2023-03-07 10:50:09 +00:00
alfresco-build
be37440e24 [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-07 10:29:48 +00:00
alfresco-build
6504406861 [maven-release-plugin][skip ci] prepare release 20.91 2023-03-07 10:29:44 +00:00
dependabot[bot]
38d7dbfa00 Bump json from 20220320 to 20230227 (#1779)
Bumps [json](https://github.com/douglascrockford/JSON-java) from 20220320 to 20230227.
- [Release notes](https://github.com/douglascrockford/JSON-java/releases)
- [Changelog](https://github.com/stleary/JSON-java/blob/master/docs/RELEASES.md)
- [Commits](https://github.com/douglascrockford/JSON-java/commits)

---
updated-dependencies:
- dependency-name: org.json:json
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-07 10:46:42 +01:00
dependabot[bot]
1ed635275f Bump commons-fileupload from 1.4 to 1.5 (#1749)
Bumps commons-fileupload from 1.4 to 1.5.

---
updated-dependencies:
- dependency-name: commons-fileupload:commons-fileupload
  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>
2023-03-07 10:46:26 +01:00
Domenico Sibilio
7b87ee628d ACS-4776 Finalize deprecation of BitlyUrlShortenerImpl (#1787) 2023-03-07 10:32:14 +01:00
alfresco-build
9cff80362d [maven-release-plugin][skip ci] prepare for next development iteration 2023-03-05 00:06:40 +00:00
alfresco-build
8311c889bb [maven-release-plugin][skip ci] prepare release 20.90 2023-03-05 00:06:38 +00:00
Alfresco CI User
bd5b5a240e [force] Force release for 2023-03-05. 2023-03-05 00:03:38 +00:00
Mohinish Sah
91f3edf8e9 incorporated review comments 2023-03-02 22:10:54 +05:30
Mohinish Sah
479724365e incorporated review comments 2023-03-02 22:04:50 +05:30
Mohinish Sah
bb1d5899d9 Merge remote-tracking branch 'origin/master' into feature/ACA-4619-Self-service-User-password-reset-ADW 2023-03-02 21:46:53 +05:30
Mohinish Sah
7ab5e8afd0 Updated the client App name 2023-03-01 18:45:08 +05:30
Mohinish Sah
5107fdfe41 resolved review comments 2023-03-01 12:45:15 +05:30
alfresco-build
c2d13e3177 [maven-release-plugin][skip ci] prepare for next development iteration 2023-02-27 11:08:57 +00:00
alfresco-build
e8f50da5a2 [maven-release-plugin][skip ci] prepare release 20.89 2023-02-27 11:08:54 +00:00
Tom Page
2053e3b83a Code freeze testing. 2023-02-27 09:35:11 +00:00
alfresco-build
bcc2eadba6 [maven-release-plugin][skip ci] prepare for next development iteration 2023-02-26 00:06:28 +00:00
alfresco-build
eb40dd3a45 [maven-release-plugin][skip ci] prepare release 20.88 2023-02-26 00:06:25 +00:00
Alfresco CI User
4c511423d5 [force] Force release for 2023-02-26. 2023-02-26 00:03:24 +00:00
alfresco-build
b12b21e773 [maven-release-plugin][skip ci] prepare for next development iteration 2023-02-24 07:28:12 +00:00
alfresco-build
569078f408 [maven-release-plugin][skip ci] prepare release 20.87 2023-02-24 07:28:08 +00:00
Damian Ujma
90de678d7d ACS-3750 Fix cancelled workflow condition (#1768) 2023-02-23 16:11:09 +01:00
alfresco-build
0a75cbbc67 [maven-release-plugin][skip ci] prepare for next development iteration 2023-02-22 11:38:44 +00:00
alfresco-build
c5f174a55d [maven-release-plugin][skip ci] prepare release 20.86 2023-02-22 11:38:42 +00:00
Piotr Żurek
119596647a ACS-4424 AGS Community tests for ES (#1761) 2023-02-22 12:08:30 +01:00
Piyush Joshi
e5d5969fc4 [ACS-3960][ACS-4339] Count is only returned for a tag if it is greater than zero (#1755)
* Added fix for ACS-3960

* Added fix for ACS-3960

* Added fix for ACS-3960

* Added formatting

* Added Fix for ACS-3960

* Revert "Added Fix for ACS-3960"

This reverts commit c06dba2d93.

* Addressed review comment

Co-authored-by: Tom Page <tpage-alfresco@users.noreply.github.com>

* Issue Fixed and added unit test cases.

* Removed extra assertion in testcases.

---------

Co-authored-by: suneet.gupta <suneet.gupta@hyland.com>
Co-authored-by: Tom Page <tpage-alfresco@users.noreply.github.com>
2023-02-22 10:21:59 +05:30
alfresco-build
b79dc538c9 [maven-release-plugin][skip ci] prepare for next development iteration 2023-02-19 00:17:09 +00:00
alfresco-build
72965efb57 [maven-release-plugin][skip ci] prepare release 20.85 2023-02-19 00:17:06 +00:00
Alfresco CI User
d467c3cdb1 [force] Force release for 2023-02-19. 2023-02-19 00:03:32 +00:00
alfresco-build
73d919435c [maven-release-plugin][skip ci] prepare for next development iteration 2023-02-15 17:42:28 +00:00
alfresco-build
5fb86e1bbc [maven-release-plugin][skip ci] prepare release 20.84 2023-02-15 17:42:23 +00:00
Krystian Dabrowski
23de0fefd7 ACS-4633: Duplicated tag is not detected when tag string contains upper case (#1750) 2023-02-15 18:13:19 +01:00
Piyush Joshi
7c3e8a2549 [MNT-22633] Creation, Modification TimeStamp values setted for download as zip (#1717)
* Creation, Modification date timestamp added to zip file

* Addressed review comments

* Addressed review comments

* Revert "Addressed review comments"

This reverts commit 58214ee427.

* Addressed review comments

* Revert "Addressed review comments"

This reverts commit a584f53023.

* Update ZipDownloadExporter.java

Addresed review comments

---------

Co-authored-by: suneet.gupta <suneet.gupta@hyland.com>
2023-02-15 21:45:46 +05:30
alfresco-build
6a3dbff290 [maven-release-plugin][skip ci] prepare for next development iteration 2023-02-13 12:39:27 +00:00
alfresco-build
0ca29a76a9 [maven-release-plugin][skip ci] prepare release 20.83 2023-02-13 12:39:23 +00:00
George Evangelopoulos
561e7330e3 ACS-4027: add E2Es for DELETE /tags/{tagsId} endpoint (#1743)
* ACS-4027: add E2Es and helper methods
2023-02-13 13:31:53 +02:00
alfresco-build
628b267a2b [maven-release-plugin][skip ci] prepare for next development iteration 2023-02-12 00:06:42 +00:00
alfresco-build
ea9687ac33 [maven-release-plugin][skip ci] prepare release 20.82 2023-02-12 00:06:39 +00:00
Alfresco CI User
4292a3ce1c [force] Force release for 2023-02-12. 2023-02-12 00:03:14 +00:00
alfresco-build
09721dca4e [maven-release-plugin][skip ci] prepare for next development iteration 2023-02-11 17:33:49 +00:00
alfresco-build
0e4020452c [maven-release-plugin][skip ci] prepare release 20.81 2023-02-11 17:33:46 +00:00
Krystian Dabrowski
96c1464b6c ACS-4026: Create orphaned tag with POST /tags (#1748)
* ACS-4026: Create orphaned tag with POST /tags
2023-02-10 16:51:22 +01:00
alfresco-build
9457e019ef [maven-release-plugin][skip ci] prepare for next development iteration 2023-02-10 13:03:16 +00:00
alfresco-build
41d82cc5ce [maven-release-plugin][skip ci] prepare release 20.80 2023-02-10 13:03:13 +00:00
Suneet Gupta
439e8587cf Changed line separation formatting to unix (#1741) 2023-02-08 17:47:06 +05:30
George Evangelopoulos
a59234f47f ACS-4027: Introduce changes to support removing tag from all associated nodes (#1709)
* ACS-4027: Introduce changes to support removing tag from all associated nodes

* ACS-4027: fix implementation and add tests
2023-02-07 18:32:52 +02:00
Domenico Sibilio
c9522f299e ACS-3750 Use set Git token instead of cached one (#1736) 2023-02-07 16:20:18 +01:00
alfresco-build
30dd78a6ab [maven-release-plugin][skip ci] prepare for next development iteration 2023-02-07 11:42:09 +00:00
alfresco-build
008134f5b0 [maven-release-plugin][skip ci] prepare release 20.79 2023-02-07 11:42:05 +00:00
Krystian Dabrowski
285ebc29a4 ACS-4154: Add category count to relevant CRUD endpoints for categories (#1724)
* ACS-4154: Add category count to relevant CRUD endpoints for categories
2023-02-07 11:14:35 +01:00
alfresco-build
9c6e8aad9c [maven-release-plugin][skip ci] prepare for next development iteration 2023-02-05 00:06:49 +00:00
alfresco-build
915fe193e2 [maven-release-plugin][skip ci] prepare release 20.78 2023-02-05 00:06:46 +00:00
Alfresco CI User
a2756aa868 [force] Force release for 2023-02-05. 2023-02-05 00:03:19 +00:00
Domenico Sibilio
b28e02c6fa ACS-3750 Improve branch detection in if conditions (#1722) 2023-02-02 17:16:06 +01:00
alfresco-build
8d00a1ab61 [maven-release-plugin][skip ci] prepare for next development iteration 2023-02-02 12:45:11 +00:00
alfresco-build
a86ad3c973 [maven-release-plugin][skip ci] prepare release 20.77 2023-02-02 12:45:08 +00:00
Piotr Żurek
2cdbe6f306 ACS-4180 Add localized properties to the repo event (#1714) 2023-02-02 12:22:08 +01:00
Tom Page
556e1923d1 Merge pull request #1720 from Alfresco/feature/ACS-4155_FixWebscriptsForES
ACS-4155 Update webscripts since we only index full category path in ES.
2023-02-01 13:10:58 +00:00
Tom Page
22e8a506ff ACS-4155 We only index full category path in ES. 2023-02-01 12:19:50 +00:00
alfresco-build
f63c9ee8c4 [maven-release-plugin][skip ci] prepare for next development iteration 2023-02-01 12:16:19 +00:00
alfresco-build
b5b9b9e2c4 [maven-release-plugin][skip ci] prepare release 20.76 2023-02-01 12:16:15 +00:00
pzurek
9e88a6562e Trigger CI 2023-02-01 10:44:42 +01:00
alfresco-build
dbddc08fcc [maven-release-plugin][skip ci] prepare for next development iteration 2023-02-01 09:08:32 +00:00
Mohinish Sah
f59ff23a45 Rebase Branch 2022-12-08 16:26:44 +05:30
rrajoria
5e2f1db714 ACA-4619: Forgot Password Changes 2022-12-08 16:21:29 +05:30
rrajoria
67ee2efc60 ACA-4619: Forgot Password Changes 2022-12-08 16:21:29 +05:30
MohinishSah
1e7dc6ed8d update Gdrive version 2022-11-14 10:54:40 +05:30
rrajoria
e5ea6db30c ACA-4619: Forgot Password Changes 2022-11-09 18:11:16 +05:30
rrajoria
78a613b1de ACA-4619: Forgot Password Changes 2022-11-09 18:10:29 +05:30
181 changed files with 11186 additions and 6140 deletions

View File

@@ -53,24 +53,6 @@ updates:
- dependency-name: org.freemarker:freemarker
versions:
- "> 2.3.20-alfresco-patched-20200421"
- dependency-name: org.keycloak:keycloak-adapter-core
versions:
- "> 12.0.2"
- dependency-name: org.keycloak:keycloak-adapter-spi
versions:
- "> 12.0.2"
- dependency-name: org.keycloak:keycloak-authz-client
versions:
- "> 12.0.2"
- dependency-name: org.keycloak:keycloak-common
versions:
- "> 12.0.2"
- dependency-name: org.keycloak:keycloak-core
versions:
- "> 12.0.2"
- dependency-name: org.keycloak:keycloak-servlet-adapter-spi
versions:
- "> 12.0.2"
- dependency-name: org.eclipse.jetty:jetty-server
versions:
- 9.4.38.v20210224

View File

@@ -49,7 +49,7 @@ jobs:
runs-on: ubuntu-latest
needs: [prepare]
if: >
((github.ref_name == 'master' || contains(github.ref_name, 'release/')) && github.event_name != 'pull_request') &&
((github.ref_name == 'master' || startsWith(github.ref_name, 'release/')) && github.event_name != 'pull_request') &&
!contains(github.event.head_commit.message, '[skip tests]') &&
!contains(github.event.head_commit.message, '[force]')
steps:
@@ -133,7 +133,7 @@ jobs:
runs-on: ubuntu-latest
needs: [prepare]
if: >
(((github.ref_name == 'master' || contains(github.ref_name, 'release/')) &&
(((github.ref_name == 'master' || startsWith(github.ref_name, 'release/')) &&
github.event_name != 'pull_request' &&
!contains(github.event.head_commit.message, '[skip db]')) ||
contains(github.event.head_commit.message, '[db]')) &&
@@ -163,7 +163,7 @@ jobs:
runs-on: ubuntu-latest
needs: [prepare]
if: >
(((github.ref_name == 'master' || contains(github.ref_name, 'release/') || github.event_name == 'pull_request') &&
(((github.ref_name == 'master' || startsWith(github.ref_name, 'release/') || github.event_name == 'pull_request') &&
!contains(github.event.head_commit.message, '[skip db]')) ||
contains(github.event.head_commit.message, '[latest db]') ||
contains(github.event.head_commit.message, '[db]')) &&
@@ -189,7 +189,7 @@ jobs:
runs-on: ubuntu-latest
needs: [prepare]
if: >
(((github.ref_name == 'master' || contains(github.ref_name, 'release/') || github.event_name == 'pull_request') &&
(((github.ref_name == 'master' || startsWith(github.ref_name, 'release/') || github.event_name == 'pull_request') &&
!contains(github.event.head_commit.message, '[skip db]')) ||
contains(github.event.head_commit.message, '[latest db]') ||
contains(github.event.head_commit.message, '[db]')) &&
@@ -215,7 +215,7 @@ jobs:
runs-on: ubuntu-latest
needs: [prepare]
if: >
(((github.ref_name == 'master' || contains(github.ref_name, 'release/')) && github.event_name != 'pull_request' &&
(((github.ref_name == 'master' || startsWith(github.ref_name, 'release/')) && github.event_name != 'pull_request' &&
!contains(github.event.head_commit.message, '[skip db]')) ||
contains(github.event.head_commit.message, '[db]')) &&
!contains(github.event.head_commit.message, '[skip tests]') &&
@@ -313,6 +313,10 @@ jobs:
- testSuite: SearchTestSuite
compose-profile: default
mvn-options: '-Dindex.subsystem.name=solr6'
- testSuite: MTLSTestSuite
compose-profile: with-mtls-transform-core-aio
mtls: true
mvn-options: '-Dencryption.ssl.keystore.location=${GITHUB_WORKSPACE}/keystores/alfresco/alfresco.keystore -Dencryption.ssl.truststore.location=${GITHUB_WORKSPACE}/keystores/alfresco/alfresco.truststore'
steps:
- uses: actions/checkout@v3
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.33.0
@@ -321,6 +325,11 @@ jobs:
run: bash ./scripts/ci/init.sh
- name: "Set transformers tag"
run: echo "TRANSFORMERS_TAG=$(mvn help:evaluate -Dexpression=dependency.alfresco-transform-core.version -q -DforceStdout)" >> $GITHUB_ENV
- name: "Generate Keystores and Truststores for Mutual TLS configuration"
if: ${{ matrix.mtls }}
run: |
git clone -b "master" --depth=1 "https://${{ secrets.BOT_GITHUB_USERNAME }}:${{ secrets.BOT_GITHUB_TOKEN }}@github.com/Alfresco/alfresco-ssl-generator.git"
bash ./scripts/ci/generate_keystores.sh
- name: "Set up the environment"
run: |
if [ -e ./scripts/ci/tests/${{ matrix.testSuite }}-setup.sh ]; then
@@ -337,7 +346,7 @@ jobs:
runs-on: ubuntu-latest
needs: [prepare]
if: >
(((github.ref_name == 'master' || contains(github.ref_name, 'release/') || github.event_name == 'pull_request' ) &&
(((github.ref_name == 'master' || startsWith(github.ref_name, 'release/') || github.event_name == 'pull_request' ) &&
!contains(github.event.head_commit.message, '[skip tas]')) ||
contains(github.event.head_commit.message, '[tas]')) &&
!contains(github.event.head_commit.message, '[skip tests]') &&
@@ -427,7 +436,7 @@ jobs:
runs-on: ubuntu-latest
needs: [prepare]
if: >
(((github.ref_name == 'master' || contains(github.ref_name, 'release/') || github.event_name == 'pull_request' ) &&
(((github.ref_name == 'master' || startsWith(github.ref_name, 'release/') || github.event_name == 'pull_request' ) &&
!contains(github.event.head_commit.message, '[skip ags]')) ||
contains(github.event.head_commit.message, '[ags]')) &&
!contains(github.event.head_commit.message, '[skip tests]') &&
@@ -458,7 +467,7 @@ jobs:
runs-on: ubuntu-latest
needs: [prepare]
if: >
(((github.ref_name == 'master' || contains(github.ref_name, 'release/') || github.event_name == 'pull_request' ) &&
(((github.ref_name == 'master' || startsWith(github.ref_name, 'release/') || github.event_name == 'pull_request' ) &&
!contains(github.event.head_commit.message, '[skip ags]')) ||
contains(github.event.head_commit.message, '[ags on MySQL]')) &&
!contains(github.event.head_commit.message, '[skip tests]') &&
@@ -489,7 +498,7 @@ jobs:
runs-on: ubuntu-latest
needs: [prepare]
if: >
(((github.ref_name == 'master' || contains(github.ref_name, 'release/') || github.event_name == 'pull_request' ) &&
(((github.ref_name == 'master' || startsWith(github.ref_name, 'release/') || github.event_name == 'pull_request' ) &&
!contains(github.event.head_commit.message, '[skip ags]') && !contains(github.event.head_commit.message, '[skip tas]')) ||
(contains(github.event.head_commit.message, '[ags]') && contains(github.event.head_commit.message, '[tas]'))) &&
!contains(github.event.head_commit.message, '[skip tests]') &&

View File

@@ -27,11 +27,13 @@ jobs:
runs-on: ubuntu-latest
needs: [run_ci]
if: >
!failure() &&
!(failure() || cancelled()) &&
!contains(github.event.head_commit.message, '[no release]') &&
github.event_name != 'pull_request'
steps:
- uses: actions/checkout@v3
with:
persist-credentials: false
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.33.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.33.0
- name: "Init"
@@ -53,11 +55,13 @@ jobs:
runs-on: ubuntu-latest
needs: [push_to_nexus]
if: >
!failure() &&
!(failure() || cancelled()) &&
!contains(github.event.head_commit.message, '[no downstream]') &&
github.event_name != 'pull_request'
steps:
- uses: actions/checkout@v3
with:
persist-credentials: false
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.33.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.33.0
- name: "Init"
@@ -72,4 +76,4 @@ jobs:
env:
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
- name: "Clean Maven cache"
run: bash ./scripts/ci/cleanup_cache.sh
run: bash ./scripts/ci/cleanup_cache.sh

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-amps</artifactId>
<version>20.75</version>
<version>20.140-SNAPSHOT</version>
</parent>
<modules>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-parent</artifactId>
<version>20.75</version>
<version>20.140-SNAPSHOT</version>
</parent>
<modules>

View File

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

View File

@@ -33,7 +33,9 @@ import org.alfresco.rest.rm.community.model.record.Record;
import org.alfresco.rest.v0.RecordsAPI;
import org.alfresco.utility.Utility;
import org.alfresco.utility.constants.UserRole;
import org.alfresco.utility.data.RandomData;
import org.alfresco.utility.model.FileModel;
import org.alfresco.utility.model.FileType;
import org.alfresco.utility.model.SiteModel;
import org.alfresco.utility.model.UserModel;
import org.json.JSONObject;
@@ -97,9 +99,10 @@ public class InplaceRecordSearchTests extends BaseRMRestTest {
@Test
public void searchForInplaceRecord() {
// And a document that isn't a record
final String fileName = "File" + RandomData.getRandomAlphanumeric();
uploadedDocbyCollabUser = dataContent.usingSite(privateSite)
.usingUser(siteCollaborator)
.createContent(CMISUtil.DocumentType.TEXT_PLAIN);
.createContent(new FileModel(fileName, FileType.fromName(fileName + "." + CMISUtil.DocumentType.TEXT_PLAIN.extention)));
assertNotNull(uploadedDocbyCollabUser.getNodeRef());

View File

@@ -72,7 +72,7 @@ public class SearchDocumentsV1Test extends BaseRMRestTest
cmisQueryModel.setLanguage("cmis");
RestRequestQueryModel aftsQueryModel = new RestRequestQueryModel();
aftsQueryModel.setQuery("cm:name:*" + SEARCH_TERM);
aftsQueryModel.setQuery("cm:name:*" + SEARCH_TERM + ".txt");
aftsQueryModel.setLanguage("afts");
return new RestRequestQueryModel[][] {
@@ -107,7 +107,7 @@ public class SearchDocumentsV1Test extends BaseRMRestTest
private void waitIndexing() throws Exception
{
RestRequestQueryModel queryType = new RestRequestQueryModel();
queryType.setQuery("cm:name:*" + SEARCH_TERM);
queryType.setQuery("cm:name:*" + SEARCH_TERM + ".txt");
queryType.setLanguage("afts");
Utility.sleep(1000, 80000, () ->
{

View File

@@ -50,6 +50,7 @@ import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;
import org.junit.Ignore;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.AssertJUnit;
import org.testng.annotations.BeforeClass;
@@ -66,7 +67,6 @@ import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanCo
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAspects.CUT_OFF_ASPECT;
import static org.alfresco.rest.rm.community.model.recordcategory.RetentionPeriodProperty.*;
import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
import static org.alfresco.utility.data.RandomData.getRandomName;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -84,26 +84,29 @@ public class DispositionScheduleLinkedRecordsTest extends BaseRMRestTest {
@Autowired
private RecordFoldersAPI recordFoldersAPI;
private final static String TEST_PREFIX = generateTestPrefix(DispositionScheduleLinkedRecordsTest.class);
private RecordCategory Category1,catsameLevel1,catsameLevel2;
private RecordCategory Category1;
private RecordCategoryChild CopyCatFolder,folder1,CatFolder,folder2;
private static final String categoryRM3077 = TEST_PREFIX + "RM-3077_manager_sees_me";
private static final String copyCategoryRM3077 = "Copy_of_" + categoryRM3077;
private static final String folderRM3077 = "RM-3077_folder_"+ categoryRM3077;
private static final String copyFolderRM3077 = "Copy_of_" + folderRM3077;
private final String electronicRecord = "RM-2937 electronic 2 record";
private final String folder = TEST_PREFIX + "RM-2937 folder ghosting";
private static final String categoryRecordsRM2526 = TEST_PREFIX + "RM-2526_category_records_immediately";
private static final String category2RecordsRM2526 = TEST_PREFIX + "RM-2526_category_2_records_1_day";
private static final String firstCategoryRM3060 = TEST_PREFIX + "RM-3060_category_record";
private static final String secondCategoryRM3060 = "Copy_of_" + firstCategoryRM3060;
private static final String firstFolderRM3060 = TEST_PREFIX + "RM-3060_folder";
private static final String secondFolderRM3060 = TEST_PREFIX + "RM-3060_disposition_on_Record_Level";
private static final String electronicRecordRM3060 = TEST_PREFIX + "RM-3060_electronic_1_record";
private static final String nonElectronicRecordRM3060 = TEST_PREFIX + "RM-3060_non-electronic_record";
private static final String firstCategoryRM1622 = TEST_PREFIX + "RM-1622_category_record";
private static final String secondCategoryRM1622 = "Copy_of_" + firstCategoryRM1622;;
private static final String firstFolderRM1622 = TEST_PREFIX + "RM-1622_folder";
private static final String electronicRecordRM1622 = TEST_PREFIX + "RM-1622_electronic_1_record";
private static final String secondFolderRM1622 = TEST_PREFIX + "RM-1622_disposition_on_Record_Level";
private static final String TRANSFER_LOCATION = TEST_PREFIX + "RM-3060_transferred_records";
public static final String TRANSFER_TYPE = "rma:transferred";
private FilePlan filePlanModel;
private UserModel rmAdmin, rmManager;
@BeforeClass(alwaysRun = true)
public void setupDispositionScheduleLinkedRecordsTest() {
createRMSiteIfNotExists();
@@ -133,192 +136,195 @@ public class DispositionScheduleLinkedRecordsTest extends BaseRMRestTest {
* <p>
* <p/> TestRail Test C775<p/>
**/
@Test
@AlfrescoTest(jira = "RM-1622")
public void dispositionScheduleLinkedRecords() throws UnsupportedEncodingException {
STEP("Create record category");
Category1 = createRootCategory(categoryRM3077);
// @Ignore("ACS-5020")
// @Test
// @AlfrescoTest(jira = "RM-1622")
// public void dispositionScheduleLinkedRecords() throws UnsupportedEncodingException {
// STEP("Create record category");
// Category1 = createRootCategory(categoryRM3077);
//
// //create retention schedule
// dispositionScheduleService.createCategoryRetentionSchedule(Category1.getName(), false);
//
// // add cut off step
// dispositionScheduleService.addCutOffAfterPeriodStep(Category1.getName(), "day|2", CREATED_DATE);
//
// //create a copy of the category recordsCategory
// String CopyCategoryId = copyCategory(getAdminUser(),Category1.getId(), copyCategoryRM3077);
//
// // create folders in both categories
// CatFolder = createRecordFolder(Category1.getId(), folderRM3077);
// CopyCatFolder = createRecordFolder(CopyCategoryId, copyFolderRM3077);
//
// // create record files
// String electronicRecord = "RM-2801 electronic record";
// Record elRecord = createElectronicRecord(CatFolder.getId(), electronicRecord);
// String elRecordFullName = recordsAPI.getRecordFullName(getDataUser().usingAdmin().getAdminUser().getUsername(),
// getDataUser().usingAdmin().getAdminUser().getPassword(), CatFolder.getName(), electronicRecord);
//
// String nonElectronicRecord = "RM-2801 non-electronic record";
// Record nonElRecord = createNonElectronicRecord(CatFolder.getId(), nonElectronicRecord);
// String nonElRecordFullName = recordsAPI.getRecordFullName(getDataUser().usingAdmin().getAdminUser().getUsername(),
// getDataUser().usingAdmin().getAdminUser().getPassword(), CatFolder.getName(), nonElectronicRecord);
//
// // link the records to copy folder, then complete them
// List<String> recordLists = new ArrayList<>();
// recordLists.add(NODE_REF_WORKSPACE_SPACES_STORE + elRecord.getId());
// recordLists.add(NODE_REF_WORKSPACE_SPACES_STORE + nonElRecord.getId());
//
// linksAPI.linkRecord(getDataUser().getAdminUser().getUsername(),
// getDataUser().getAdminUser().getPassword(), HttpStatus.SC_OK,copyCategoryRM3077 + "/" +
// copyFolderRM3077, recordLists);
// recordsAPI.completeRecord(rmAdmin.getUsername(), rmAdmin.getPassword(), elRecordFullName);
// recordsAPI.completeRecord(rmAdmin.getUsername(), rmAdmin.getPassword(), nonElRecordFullName);
//
// // edit disposition date
// recordFoldersAPI.postFolderAction(getAdminUser().getUsername(),
// getAdminUser().getPassword(),editDispositionDateJson(),CatFolder.getName());
//
// // cut off the Folder
// recordFoldersAPI.postFolderAction(getAdminUser().getUsername(),
// getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),CatFolder.getName());
//
// // Verify the Content
// Node electronicNode = getNode(elRecord.getId());
// assertTrue("The content of " + electronicRecord + " is available",
// StringUtils.isEmpty(electronicNode.getNodeContent().getResponse().getBody().asString()));
//
// // verify the Properties
// AssertJUnit.assertNull("The properties are present even after cutting off the record.", elRecord.getProperties().getTitle());
//
// // delete precondition
// deleteRecordCategory(Category1.getId());
// deleteRecordCategory(CopyCategoryId);
// }
// /**
// * Test covering RM-3060
// * Check the disposition steps for a record can be executed
// * When the record is linked to a folder with the same disposition schedule
// * */
// @Ignore("ACS-5020")
//// @Test
// @AlfrescoTest (jira = "RM-3060")
// public void sameDispositionScheduleLinkedRecords() throws UnsupportedEncodingException {
//
// // create a category with retention applied on records level
// RecordCategory recordCategory = getRestAPIFactory().getFilePlansAPI(rmAdmin)
// .createRootRecordCategory(RecordCategory.builder().name(firstCategoryRM3060).build(),
// RecordCategory.DEFAULT_FILE_PLAN_ALIAS);
// dispositionScheduleService.createCategoryRetentionSchedule(firstCategoryRM3060, true);
// dispositionScheduleService.addCutOffAfterPeriodStep(firstCategoryRM3060, "week|1", DATE_FILED);
// dispositionScheduleService.addTransferAfterEventStep(firstCategoryRM3060, TRANSFER_LOCATION, RMEvents.CASE_CLOSED.getEventName());
// dispositionScheduleService.addDestroyWithoutGhostingAfterPeriodStep(firstCategoryRM3060, "week|1", CUT_OFF_DATE);
//
// // make a copy of the category created
// String categorySecondId = copyCategory(getAdminUser(), recordCategory.getId(), secondCategoryRM3060);
//
// // create a folder on the category firstCategoryRM3060 with a complete electronic record
// RecordCategoryChild firstFolderRecordCategoryChild = createRecordFolder(recordCategory.getId(),firstFolderRM3060);
// Record firstElectronicRecord = createElectronicRecord(firstFolderRecordCategoryChild.getId(),electronicRecordRM3060);
//
// String elRecordFullName = recordsAPI.getRecordFullName(getDataUser().getAdminUser().getUsername(),
// getDataUser().getAdminUser().getPassword(),firstFolderRM3060, electronicRecordRM3060);
// String elRecordNameNodeRef = recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
// getDataUser().usingAdmin().getAdminUser().getPassword(), elRecordFullName, "/" + firstCategoryRM3060 + "/" + firstFolderRM3060);
//
// recordsAPI.completeRecord(getDataUser().getAdminUser().getUsername(),
// getDataUser().getAdminUser().getPassword(), elRecordFullName);
//
// // create a folder on the category secondCategoryRM3060 with a non electronic record
// RecordCategoryChild secondFolderRecordCategoryChild = createRecordFolder(categorySecondId,secondFolderRM3060);
// Record secondNonElectronicRecord = createNonElectronicRecord(secondFolderRecordCategoryChild.getId(),nonElectronicRecordRM3060);
//
// // link the nonElectronicRecordRM3060 to firstFolderRM3060
// List<String> recordLists = new ArrayList<>();
// recordLists.add(NODE_REF_WORKSPACE_SPACES_STORE + secondNonElectronicRecord.getId());
//
// linksAPI.linkRecord(getDataUser().getAdminUser().getUsername(),
// getDataUser().getAdminUser().getPassword(), HttpStatus.SC_OK,secondCategoryRM3060 + "/" +
// secondFolderRM3060, recordLists);
// String nonElRecordFullName = recordsAPI.getRecordFullName(getDataUser().usingAdmin().getAdminUser().getUsername(),
// getDataUser().usingAdmin().getAdminUser().getPassword(), secondFolderRM3060, secondNonElectronicRecord.getName());
// String nonElRecordNameNodeRef = recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
// getDataUser().usingAdmin().getAdminUser().getPassword(), nonElRecordFullName, "/" + secondCategoryRM3060 + "/" + secondFolderRM3060);
//
// // complete records and cut them off
// recordsAPI.completeRecord(getDataUser().getAdminUser().getUsername(),
// getDataUser().getAdminUser().getPassword(), nonElRecordFullName);
//
// // edit the disposition date
// recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
// getAdminUser().getPassword(),editDispositionDateJson(),nonElRecordNameNodeRef);
//
// // cut off the record
// recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
// getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),nonElRecordNameNodeRef);
//
// //check the record is cut off
// AssertJUnit.assertTrue("The file " + nonElectronicRecordRM3060 + " has not been successfully cut off.", getRestAPIFactory().getRecordsAPI().getRecord(secondNonElectronicRecord.getId()).getAspectNames().contains(CUT_OFF_ASPECT));
//
// // link the electronic record to secondFolderRM3060
// recordLists.clear();
// recordLists.add(NODE_REF_WORKSPACE_SPACES_STORE + secondNonElectronicRecord.getId());
// linksAPI.linkRecord(getDataUser().getAdminUser().getUsername(),
// getDataUser().getAdminUser().getPassword(), HttpStatus.SC_OK,secondCategoryRM3060 + "/" +
// secondFolderRM3060, recordLists);
//
// // edit the disposition date and cut off the record
// recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
// getAdminUser().getPassword(),editDispositionDateJson(),elRecordNameNodeRef);
// recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
// getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),elRecordNameNodeRef);
//
// AssertJUnit.assertTrue("The file " + electronicRecordRM3060 + " has not been successfully cut off.", getRestAPIFactory().getRecordsAPI().getRecord(firstElectronicRecord.getId()).getAspectNames().contains(CUT_OFF_ASPECT));
//
// // open the record and complete the disposition schedule event
// rmRolesAndActionsAPI.completeEvent(getAdminUser().getUsername(),
// getAdminUser().getPassword(), elRecordFullName, RMEvents.CASE_CLOSED, Instant.now());
// rmRolesAndActionsAPI.completeEvent(getAdminUser().getUsername(),
// getAdminUser().getPassword(), nonElRecordFullName, RMEvents.CASE_CLOSED, Instant.now());
//
// // transfer the files & complete transfers
// HttpResponse nonElRecordNameHttpResponse = recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
// getAdminUser().getPassword(),new JSONObject().put("name","transfer"),recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
// getDataUser().usingAdmin().getAdminUser().getPassword(), nonElRecordFullName, "/" + secondCategoryRM3060 + "/" + secondFolderRM3060));
//
// String nonElRecordNameTransferId = getTransferId(nonElRecordNameHttpResponse,nonElRecordNameNodeRef);
// recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
// getAdminUser().getPassword(),new JSONObject().put("name","transferComplete"),nonElRecordNameTransferId);
//
// HttpResponse elRecordNameHttpResponse = recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
// getAdminUser().getPassword(),new JSONObject().put("name","transfer"),recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
// getDataUser().usingAdmin().getAdminUser().getPassword(), elRecordFullName, "/" + firstCategoryRM3060 + "/" + firstFolderRM3060));
//
// String elRecordNameTransferId = getTransferId(elRecordNameHttpResponse,elRecordNameNodeRef);
// recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
// getAdminUser().getPassword(),new JSONObject().put("name","transferComplete"),elRecordNameTransferId);
//
// AssertJUnit.assertTrue("The file " + electronicRecordRM3060 + " has not been successfully transferred", getRestAPIFactory().getRecordsAPI().getRecord(firstElectronicRecord.getId()).getAspectNames().contains(TRANSFER_TYPE));
// AssertJUnit.assertTrue("The file " + nonElectronicRecordRM3060 + " has not been successfully transferred.", getRestAPIFactory().getRecordsAPI().getRecord(secondNonElectronicRecord.getId()).getAspectNames().contains(TRANSFER_TYPE));
//
// // edit the disposition date for nonElectronicRecordRM3060 & electronicRecordRM3060
// recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
// getAdminUser().getPassword(),editDispositionDateJson(),nonElRecordNameNodeRef);
// recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
// getAdminUser().getPassword(),editDispositionDateJson(),elRecordNameNodeRef);
//
// // destroy nonElectronicRecordRM3060 & electronicRecordRM3060 records
// recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
// getAdminUser().getPassword(),new JSONObject().put("name","destroy"),nonElRecordNameNodeRef);
// recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
// getAdminUser().getPassword(),new JSONObject().put("name","destroy"),elRecordNameNodeRef);
//
// // check the file is not displayed
// assertNull("The file " + nonElectronicRecordRM3060 + " has not been successfully destroyed.", secondNonElectronicRecord.getContent());
// assertNull("The file " + electronicRecordRM3060 + " has not been successfully destroyed.", firstElectronicRecord.getContent());
//
// // delete precondition
// deleteRecordCategory(recordCategory.getId());
// deleteRecordCategory(categorySecondId);
// }
//create retention schedule
dispositionScheduleService.createCategoryRetentionSchedule(Category1.getName(), false);
// add cut off step
dispositionScheduleService.addCutOffAfterPeriodStep(Category1.getName(), "day|2", CREATED_DATE);
//create a copy of the category recordsCategory
String CopyCategoryId = copyCategory(getAdminUser(),Category1.getId(), copyCategoryRM3077);
// create folders in both categories
CatFolder = createRecordFolder(Category1.getId(), folderRM3077);
CopyCatFolder = createRecordFolder(CopyCategoryId, copyFolderRM3077);
// create record files
String electronicRecord = "RM-2801 electronic record";
Record elRecord = createElectronicRecord(CatFolder.getId(), electronicRecord);
String elRecordFullName = recordsAPI.getRecordFullName(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), CatFolder.getName(), electronicRecord);
String nonElectronicRecord = "RM-2801 non-electronic record";
Record nonElRecord = createNonElectronicRecord(CatFolder.getId(), nonElectronicRecord);
String nonElRecordFullName = recordsAPI.getRecordFullName(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), CatFolder.getName(), nonElectronicRecord);
// link the records to copy folder, then complete them
List<String> recordLists = new ArrayList<>();
recordLists.add(NODE_REF_WORKSPACE_SPACES_STORE + elRecord.getId());
recordLists.add(NODE_REF_WORKSPACE_SPACES_STORE + nonElRecord.getId());
linksAPI.linkRecord(getDataUser().getAdminUser().getUsername(),
getDataUser().getAdminUser().getPassword(), HttpStatus.SC_OK,copyCategoryRM3077 + "/" +
copyFolderRM3077, recordLists);
recordsAPI.completeRecord(rmAdmin.getUsername(), rmAdmin.getPassword(), elRecordFullName);
recordsAPI.completeRecord(rmAdmin.getUsername(), rmAdmin.getPassword(), nonElRecordFullName);
// edit disposition date
recordFoldersAPI.postFolderAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),editDispositionDateJson(),CatFolder.getName());
// cut off the Folder
recordFoldersAPI.postFolderAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),CatFolder.getName());
// Verify the Content
Node electronicNode = getNode(elRecord.getId());
assertTrue("The content of " + electronicRecord + " is available",
StringUtils.isEmpty(electronicNode.getNodeContent().getResponse().getBody().asString()));
// verify the Properties
AssertJUnit.assertNull("The properties are present even after cutting off the record.", elRecord.getProperties().getTitle());
// delete precondition
deleteRecordCategory(Category1.getId());
deleteRecordCategory(CopyCategoryId);
}
/**
* Test covering RM-3060
* Check the disposition steps for a record can be executed
* When the record is linked to a folder with the same disposition schedule
* */
@Test
@AlfrescoTest (jira = "RM-3060")
public void sameDispositionScheduleLinkedRecords() throws UnsupportedEncodingException {
// create a category with retention applied on records level
RecordCategory recordCategory = getRestAPIFactory().getFilePlansAPI(rmAdmin)
.createRootRecordCategory(RecordCategory.builder().name(firstCategoryRM3060).build(),
RecordCategory.DEFAULT_FILE_PLAN_ALIAS);
dispositionScheduleService.createCategoryRetentionSchedule(firstCategoryRM3060, true);
dispositionScheduleService.addCutOffAfterPeriodStep(firstCategoryRM3060, "week|1", DATE_FILED);
dispositionScheduleService.addTransferAfterEventStep(firstCategoryRM3060, TRANSFER_LOCATION, RMEvents.CASE_CLOSED.getEventName());
dispositionScheduleService.addDestroyWithoutGhostingAfterPeriodStep(firstCategoryRM3060, "week|1", CUT_OFF_DATE);
// make a copy of the category created
String categorySecondId = copyCategory(getAdminUser(), recordCategory.getId(), secondCategoryRM3060);
// create a folder on the category firstCategoryRM3060 with a complete electronic record
RecordCategoryChild firstFolderRecordCategoryChild = createRecordFolder(recordCategory.getId(),firstFolderRM3060);
Record firstElectronicRecord = createElectronicRecord(firstFolderRecordCategoryChild.getId(),electronicRecordRM3060);
String elRecordFullName = recordsAPI.getRecordFullName(getDataUser().getAdminUser().getUsername(),
getDataUser().getAdminUser().getPassword(),firstFolderRM3060, electronicRecordRM3060);
String elRecordNameNodeRef = recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), elRecordFullName, "/" + firstCategoryRM3060 + "/" + firstFolderRM3060);
recordsAPI.completeRecord(getDataUser().getAdminUser().getUsername(),
getDataUser().getAdminUser().getPassword(), elRecordFullName);
// create a folder on the category secondCategoryRM3060 with a non electronic record
RecordCategoryChild secondFolderRecordCategoryChild = createRecordFolder(categorySecondId,secondFolderRM3060);
Record secondNonElectronicRecord = createNonElectronicRecord(secondFolderRecordCategoryChild.getId(),nonElectronicRecordRM3060);
// link the nonElectronicRecordRM3060 to firstFolderRM3060
List<String> recordLists = new ArrayList<>();
recordLists.add(NODE_REF_WORKSPACE_SPACES_STORE + secondNonElectronicRecord.getId());
linksAPI.linkRecord(getDataUser().getAdminUser().getUsername(),
getDataUser().getAdminUser().getPassword(), HttpStatus.SC_OK,secondCategoryRM3060 + "/" +
secondFolderRM3060, recordLists);
String nonElRecordFullName = recordsAPI.getRecordFullName(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), secondFolderRM3060, secondNonElectronicRecord.getName());
String nonElRecordNameNodeRef = recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), nonElRecordFullName, "/" + secondCategoryRM3060 + "/" + secondFolderRM3060);
// complete records and cut them off
recordsAPI.completeRecord(getDataUser().getAdminUser().getUsername(),
getDataUser().getAdminUser().getPassword(), nonElRecordFullName);
// edit the disposition date
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),editDispositionDateJson(),nonElRecordNameNodeRef);
// cut off the record
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),nonElRecordNameNodeRef);
//check the record is cut off
AssertJUnit.assertTrue("The file " + nonElectronicRecordRM3060 + " has not been successfully cut off.", getRestAPIFactory().getRecordsAPI().getRecord(secondNonElectronicRecord.getId()).getAspectNames().contains(CUT_OFF_ASPECT));
// link the electronic record to secondFolderRM3060
recordLists.clear();
recordLists.add(NODE_REF_WORKSPACE_SPACES_STORE + secondNonElectronicRecord.getId());
linksAPI.linkRecord(getDataUser().getAdminUser().getUsername(),
getDataUser().getAdminUser().getPassword(), HttpStatus.SC_OK,secondCategoryRM3060 + "/" +
secondFolderRM3060, recordLists);
// edit the disposition date and cut off the record
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),editDispositionDateJson(),elRecordNameNodeRef);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),elRecordNameNodeRef);
AssertJUnit.assertTrue("The file " + electronicRecordRM3060 + " has not been successfully cut off.", getRestAPIFactory().getRecordsAPI().getRecord(firstElectronicRecord.getId()).getAspectNames().contains(CUT_OFF_ASPECT));
// open the record and complete the disposition schedule event
rmRolesAndActionsAPI.completeEvent(getAdminUser().getUsername(),
getAdminUser().getPassword(), elRecordFullName, RMEvents.CASE_CLOSED, Instant.now());
rmRolesAndActionsAPI.completeEvent(getAdminUser().getUsername(),
getAdminUser().getPassword(), nonElRecordFullName, RMEvents.CASE_CLOSED, Instant.now());
// transfer the files & complete transfers
HttpResponse nonElRecordNameHttpResponse = recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","transfer"),recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), nonElRecordFullName, "/" + secondCategoryRM3060 + "/" + secondFolderRM3060));
String nonElRecordNameTransferId = getTransferId(nonElRecordNameHttpResponse,nonElRecordNameNodeRef);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","transferComplete"),nonElRecordNameTransferId);
HttpResponse elRecordNameHttpResponse = recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","transfer"),recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), elRecordFullName, "/" + firstCategoryRM3060 + "/" + firstFolderRM3060));
String elRecordNameTransferId = getTransferId(elRecordNameHttpResponse,elRecordNameNodeRef);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","transferComplete"),elRecordNameTransferId);
AssertJUnit.assertTrue("The file " + electronicRecordRM3060 + " has not been successfully transferred", getRestAPIFactory().getRecordsAPI().getRecord(firstElectronicRecord.getId()).getAspectNames().contains(TRANSFER_TYPE));
AssertJUnit.assertTrue("The file " + nonElectronicRecordRM3060 + " has not been successfully transferred.", getRestAPIFactory().getRecordsAPI().getRecord(secondNonElectronicRecord.getId()).getAspectNames().contains(TRANSFER_TYPE));
// edit the disposition date for nonElectronicRecordRM3060 & electronicRecordRM3060
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),editDispositionDateJson(),nonElRecordNameNodeRef);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),editDispositionDateJson(),elRecordNameNodeRef);
// destroy nonElectronicRecordRM3060 & electronicRecordRM3060 records
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","destroy"),nonElRecordNameNodeRef);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","destroy"),elRecordNameNodeRef);
// check the file is not displayed
assertNull("The file " + nonElectronicRecordRM3060 + " has not been successfully destroyed.", secondNonElectronicRecord.getContent());
assertNull("The file " + electronicRecordRM3060 + " has not been successfully destroyed.", firstElectronicRecord.getContent());
// delete precondition
deleteRecordCategory(recordCategory.getId());
deleteRecordCategory(categorySecondId);
}
private String copyCategory(UserModel user, String categoryId, String copyName) {
RepoTestModel repoTestModel = new RepoTestModel() {};
repoTestModel.setNodeRef(categoryId);
@@ -368,35 +374,35 @@ public class DispositionScheduleLinkedRecordsTest extends BaseRMRestTest {
// create a category with retention applied on records level
RecordCategory catsameLevel1 = getRestAPIFactory().getFilePlansAPI(rmAdmin)
.createRootRecordCategory(RecordCategory.builder().name(firstCategoryRM3060).build(),
RecordCategory.DEFAULT_FILE_PLAN_ALIAS);
.createRootRecordCategory(RecordCategory.builder().name(firstCategoryRM1622).build(),
RecordCategory.DEFAULT_FILE_PLAN_ALIAS);
RecordCategory catsameLevel2 = getRestAPIFactory().getFilePlansAPI(rmAdmin)
.createRootRecordCategory(RecordCategory.builder().name(secondCategoryRM3060).build(),
RecordCategory.DEFAULT_FILE_PLAN_ALIAS);
.createRootRecordCategory(RecordCategory.builder().name(secondCategoryRM1622).build(),
RecordCategory.DEFAULT_FILE_PLAN_ALIAS);
// create retention schedule applied on records for category 1
dispositionScheduleService.createCategoryRetentionSchedule(firstCategoryRM3060, true);
dispositionScheduleService.createCategoryRetentionSchedule(firstCategoryRM1622, true);
// with retain immediately after record creation date and cut 1 day after record creation date
dispositionScheduleService.addCutOffAfterPeriodStep(firstCategoryRM3060, "day|1", DATE_FILED);
dispositionScheduleService.addCutOffAfterPeriodStep(firstCategoryRM1622, "day|1", DATE_FILED);
// create a folder on the category firstCategoryRM3060 with a complete electronic record
RecordCategoryChild firstFolderRecordCategoryChild = createRecordFolder(catsameLevel1.getId(),firstFolderRM3060);
Record firstElectronicRecord = createElectronicRecord(firstFolderRecordCategoryChild.getId(),electronicRecordRM3060);
// create a folder on the category firstCategoryRM1622 with a complete electronic record
RecordCategoryChild firstFolderRecordCategoryChild = createRecordFolder(catsameLevel1.getId(),firstFolderRM1622);
Record firstElectronicRecord = createElectronicRecord(firstFolderRecordCategoryChild.getId(),electronicRecordRM1622);
String elRecordFullName = recordsAPI.getRecordFullName(getDataUser().getAdminUser().getUsername(),
getDataUser().getAdminUser().getPassword(),firstFolderRM3060, electronicRecordRM3060);
getDataUser().getAdminUser().getPassword(),firstFolderRM1622, electronicRecordRM1622);
String elRecordNameNodeRef = recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), elRecordFullName, "/" + firstCategoryRM3060 + "/" + firstFolderRM3060);
getDataUser().usingAdmin().getAdminUser().getPassword(), elRecordFullName, "/" + firstCategoryRM1622 + "/" + firstFolderRM1622);
recordsAPI.completeRecord(getDataUser().getAdminUser().getUsername(),
getDataUser().getAdminUser().getPassword(), elRecordFullName);
// create a folder on the category secondCategoryRM3060 with a non electronic record
RecordCategoryChild secondFolderRecordCategoryChild = createRecordFolder(catsameLevel2.getId(),secondFolderRM3060);
// create a folder on the category secondCategoryRM1622 with a non electronic record
RecordCategoryChild secondFolderRecordCategoryChild = createRecordFolder(catsameLevel2.getId(),secondFolderRM1622);
String elRecordNameNodeRefs = recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), elRecordFullName, "/" + firstCategoryRM3060 + "/" + firstFolderRM3060);
getDataUser().usingAdmin().getAdminUser().getPassword(), elRecordFullName, "/" + firstCategoryRM1622 + "/" + firstFolderRM1622);
// link it to the folder in second category through the details page
@@ -404,8 +410,8 @@ public class DispositionScheduleLinkedRecordsTest extends BaseRMRestTest {
recordLists.add(NODE_REF_WORKSPACE_SPACES_STORE + firstElectronicRecord.getId());
linksAPI.linkRecord(getDataUser().getAdminUser().getUsername(),
getDataUser().getAdminUser().getPassword(), HttpStatus.SC_OK,secondCategoryRM3060 + "/" +
secondFolderRM3060, recordLists);
getDataUser().getAdminUser().getPassword(), HttpStatus.SC_OK,secondCategoryRM1622 + "/" +
secondFolderRM1622, recordLists);
// edit disposition date
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-parent</artifactId>
<version>20.75</version>
<version>20.140-SNAPSHOT</version>
</parent>
<modules>

View File

@@ -1,3 +1,4 @@
ARG BASE_IMAGE
# BUILD STAGE AGS
FROM debian:11-slim AS AGSBUILDER
@@ -12,7 +13,7 @@ RUN unzip -q /build/gs-api-explorer-*.war -d /build/gs-api-explorer && \
chmod -R g-w,o= /build
# ACTUAL IMAGE
FROM alfresco/alfresco-community-repo-base:${image.tag}
FROM ${BASE_IMAGE}
# Alfresco user does not have permissions to modify webapps or configuration. Switch to root.
# The access will be fixed after all operations are done.

View File

@@ -8,13 +8,15 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
<version>20.75</version>
<version>20.140-SNAPSHOT</version>
</parent>
<properties>
<app.amp.client.war.folder>${project.build.directory}/${project.build.finalName}-war</app.amp.client.war.folder>
<image.name>alfresco/alfresco-governance-repository-community-base</image.name>
<base.image>alfresco/alfresco-community-repo-base</base.image>
<scripts.directory>${project.parent.parent.parent.parent.basedir}/scripts</scripts.directory>
</properties>
<dependencies>
@@ -537,9 +539,43 @@
</build>
</profile>
<profile>
<id>build-docker-images</id>
<!-- builds "image:latest" locally -->
<build>
<plugins>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<configuration>
<images>
<image>
<name>${image.name}:${image.tag}</name>
<build>
<args>
<BASE_IMAGE>${base.image}:${image.tag}</BASE_IMAGE>
</args>
<contextDir>${project.basedir}</contextDir>
</build>
</image>
</images>
</configuration>
<executions>
<execution>
<id>build-image</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>build-docker-images</id>
<!-- builds "image:latest" locally -->
<id>build-multiarch-docker-images</id>
<build>
<plugins>
<plugin>
@@ -548,20 +584,56 @@
<configuration>
<images>
<image>
<name>${image.name}:${image.tag}</name>
<name>${local.registry}/${image.name}:${image.tag}</name>
<build>
<buildx>
<platforms>
<platform>linux/amd64</platform>
<platform>linux/arm64</platform>
</platforms>
<builderName>${builder.name}</builderName>
</buildx>
<contextDir>${project.basedir}</contextDir>
<args>
<BASE_IMAGE>${local.registry}/${base.image}:${image.tag}</BASE_IMAGE>
</args>
</build>
</image>
</images>
</configuration>
<executions>
<execution>
<id>build-image</id>
<id>build-push-image</id>
<phase>package</phase>
<goals>
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>exec-maven-plugin</artifactId>
<groupId>org.codehaus.mojo</groupId>
<executions>
<execution>
<id>prepare-buildx</id>
<phase>generate-sources</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>${scripts.directory}/prepare_buildx.sh</executable>
<arguments>
<argument>${builder.name}</argument>
<argument>${image.registry}</argument>
<argument>${image.name}</argument>
<argument>${image.tag}</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
@@ -578,12 +650,37 @@
<images>
<image>
<!-- Quay image -->
<name>${image.name}:${image.tag}</name>
<registry>${image.registry}</registry>
<name>${image.registry}/${image.name}:${image.tag}</name>
<build>
<buildx>
<platforms>
<platform>linux/amd64</platform>
<platform>linux/arm64</platform>
</platforms>
<builderName>${builder.name}</builderName>
</buildx>
<args>
<BASE_IMAGE>${local.registry}/${base.image}:${image.tag}</BASE_IMAGE>
</args>
<contextDir>${project.basedir}</contextDir>
</build>
</image>
<image>
<!-- DockerHub image -->
<name>${image.name}:${image.tag}</name>
<build>
<buildx>
<platforms>
<platform>linux/amd64</platform>
<platform>linux/arm64</platform>
</platforms>
<builderName>${builder.name}</builderName>
</buildx>
<args>
<BASE_IMAGE>${local.registry}/${base.image}:${image.tag}</BASE_IMAGE>
</args>
<contextDir>${project.basedir}</contextDir>
</build>
</image>
</images>
</configuration>
@@ -598,6 +695,28 @@
</execution>
</executions>
</plugin>
<plugin>
<artifactId>exec-maven-plugin</artifactId>
<groupId>org.codehaus.mojo</groupId>
<executions>
<execution>
<id>prepare-buildx</id>
<phase>generate-sources</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>${scripts.directory}/prepare_buildx.sh</executable>
<arguments>
<argument>${builder.name}</argument>
<argument>${image.registry}</argument>
<argument>${image.name}</argument>
<argument>${image.tag}</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>

View File

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

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>20.75</version>
<version>20.140-SNAPSHOT</version>
</parent>
<modules>

View File

@@ -8,7 +8,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-amps</artifactId>
<version>20.75</version>
<version>20.140-SNAPSHOT</version>
</parent>
<properties>
@@ -121,12 +121,6 @@
<version>${dependency.webscripts.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>

View File

@@ -241,7 +241,7 @@ var Filters =
filterData = filterData.slice(0, -1);
}
filterQuery = this.constructPathQuery(parsedArgs);
filterParams.query = filterQuery + " +PATH:\"/cm:generalclassifiable" + Filters.iso9075EncodePath(filterData) + "/member\"";
filterParams.query = filterQuery + " +PATH:\"/cm:categoryRoot/cm:generalclassifiable" + Filters.iso9075EncodePath(filterData) + "/member\"";
break;
case "aspect":

View File

@@ -230,7 +230,7 @@ var Filters =
filterData = filterData.slice(0, -1);
}
filterQuery = this.constructPathQuery(parsedArgs);
filterParams.query = filterQuery + " +PATH:\"/cm:generalclassifiable" + Filters.iso9075EncodePath(filterData) + "/member\"";
filterParams.query = filterQuery + " +PATH:\"/cm:categoryRoot/cm:generalclassifiable" + Filters.iso9075EncodePath(filterData) + "/member\"";
break;
default: // "path"

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>20.75</version>
<version>20.140-SNAPSHOT</version>
</parent>
<dependencies>
@@ -137,6 +137,10 @@
<artifactId>commons-dbcp2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
</dependencies>
<build>

View File

@@ -0,0 +1,143 @@
/*
* Copyright (C) 2023 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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/>.
*/
package org.alfresco.httpclient;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import org.alfresco.error.AlfrescoRuntimeException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.StandardHttpRequestRetryHandler;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
public class HttpClient4Factory
{
protected static final String TLS_PROTOCOL = "TLS";
protected static final String HTTPS_PROTOCOL = "https";
protected static final String HTTP_TARGET_HOST = "http.target_host";
protected static final String TLS_V_1_2 = "TLSv1.2";
protected static final String TLS_V_1_3 = "TLSv1.3";
private static SSLContext createSSLContext(HttpClientConfig config)
{
KeyManager[] keyManagers = config.getKeyStore().createKeyManagers();
TrustManager[] trustManagers = config.getTrustStore().createTrustManagers();
try
{
SSLContext sslcontext = SSLContext.getInstance(TLS_PROTOCOL);
sslcontext.init(keyManagers, trustManagers, null);
return sslcontext;
}
catch(Throwable e)
{
throw new AlfrescoRuntimeException("Unable to create SSL context", e);
}
}
public static CloseableHttpClient createHttpClient(HttpClientConfig config)
{
return createHttpClient(config, null);
}
public static CloseableHttpClient createHttpClient(HttpClientConfig config, HttpClientConnectionManager connectionManager)
{
HttpClientBuilder clientBuilder = HttpClients.custom();
if(config.isMTLSEnabled())
{
clientBuilder.addInterceptorFirst((HttpRequestInterceptor) (request, context) -> {
if (!((HttpHost) context.getAttribute(HTTP_TARGET_HOST)).getSchemeName().equals(HTTPS_PROTOCOL))
{
String msg = "mTLS is enabled but provided URL does not use a secured protocol";
throw new HttpClientException(msg);
}
});
clientBuilder.setSSLSocketFactory(getSslConnectionSocketFactory(config));
}
if (connectionManager != null)
{
clientBuilder.setConnectionManager(connectionManager);
}
else
{
//Setting a connectionManager overrides these properties
clientBuilder.setMaxConnTotal(config.getMaxTotalConnections());
clientBuilder.setMaxConnPerRoute(config.getMaxHostConnections());
}
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(config.getConnectionTimeout())
.setSocketTimeout(config.getSocketTimeout())
.setConnectionRequestTimeout(config.getConnectionRequestTimeout())
.build();
clientBuilder.setDefaultRequestConfig(requestConfig);
clientBuilder.setRetryHandler(new StandardHttpRequestRetryHandler(5, false));
return clientBuilder.build();
}
private static SSLConnectionSocketFactory getSslConnectionSocketFactory(HttpClientConfig config)
{
return new SSLConnectionSocketFactory(
createSSLContext(config),
new String[] { TLS_V_1_2, TLS_V_1_3 },
null,
config.isHostnameVerificationDisabled() ? new NoopHostnameVerifier() : SSLConnectionSocketFactory.getDefaultHostnameVerifier());
}
public static PoolingHttpClientConnectionManager createPoolingConnectionManager(HttpClientConfig config)
{
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager;
if(config.isMTLSEnabled())
{
poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", getSslConnectionSocketFactory(config))
.build());
}
else
{
poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.build());
}
poolingHttpClientConnectionManager.setMaxTotal(config.getMaxTotalConnections());
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(config.getMaxHostConnections());
return poolingHttpClientConnectionManager;
}
}

View File

@@ -0,0 +1,202 @@
/*
* Copyright (C) 2023 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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/>.
*/
package org.alfresco.httpclient;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.stream.Collectors;
import org.alfresco.encryption.AlfrescoKeyStore;
import org.alfresco.encryption.AlfrescoKeyStoreImpl;
import org.alfresco.encryption.KeyResourceLoader;
import org.alfresco.encryption.ssl.SSLEncryptionParameters;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class HttpClientConfig
{
private static final String HTTPCLIENT_CONFIG = "httpclient.config.";
protected static final Log LOGGER = LogFactory.getLog(HttpClientConfig.class);
private Properties properties;
private String serviceName;
private SSLEncryptionParameters sslEncryptionParameters;
private KeyResourceLoader keyResourceLoader;
private AlfrescoKeyStore keyStore;
private AlfrescoKeyStore trustStore;
private Map<String, String> config;
public void setProperties(Properties properties)
{
this.properties = properties;
}
public void setServiceName(String serviceName)
{
this.serviceName = serviceName;
}
public void setSslEncryptionParameters(SSLEncryptionParameters sslEncryptionParameters)
{
this.sslEncryptionParameters = sslEncryptionParameters;
}
public void setKeyResourceLoader(KeyResourceLoader keyResourceLoader)
{
this.keyResourceLoader = keyResourceLoader;
}
public AlfrescoKeyStore getKeyStore()
{
return keyStore;
}
public AlfrescoKeyStore getTrustStore()
{
return trustStore;
}
public void init()
{
this.keyStore = new AlfrescoKeyStoreImpl(sslEncryptionParameters.getKeyStoreParameters(), keyResourceLoader);
this.trustStore = new AlfrescoKeyStoreImpl(sslEncryptionParameters.getTrustStoreParameters(), keyResourceLoader);
config = retrieveConfig(serviceName);
checkUnsupportedProperties(config);
}
/**
* Method used for retrieving HttpClient config from Global Properties
* @param serviceName name of used service
* @return map of properties
*/
private Map<String, String> retrieveConfig(String serviceName)
{
return properties.keySet().stream()
.filter(key -> key instanceof String)
.map(Object::toString)
.filter(key -> key.startsWith(HTTPCLIENT_CONFIG + serviceName))
.collect(Collectors.toMap(
key -> key.replace(HTTPCLIENT_CONFIG + serviceName + ".", ""),
key -> properties.getProperty(key, null)));
}
private void checkUnsupportedProperties(Map<String, String> config)
{
config.keySet().stream()
.filter(propertyName -> !HttpClientPropertiesEnum.isPropertyNameSupported(propertyName))
.forEach(propertyName -> LOGGER.warn(String.format("For service [%s], an unsupported property [%s] is set", serviceName, propertyName)));
}
private Integer getIntegerProperty(HttpClientPropertiesEnum property)
{
return Integer.parseInt(extractValueFromConfig(property).orElse("0"));
}
private Boolean getBooleanProperty(HttpClientPropertiesEnum property)
{
return Boolean.parseBoolean(extractValueFromConfig(property).orElse("false"));
}
private Optional<String> extractValueFromConfig(HttpClientPropertiesEnum property)
{
Optional<String> optionalProperty = Optional.ofNullable(config.get(property.name));
if(property.isRequired && optionalProperty.isEmpty())
{
String msg = String.format("Required property: '%s' is empty.", property.name);
throw new HttpClientException(msg);
}
return optionalProperty;
}
public Integer getConnectionTimeout()
{
return getIntegerProperty(HttpClientPropertiesEnum.CONNECTION_REQUEST_TIMEOUT);
}
public Integer getSocketTimeout()
{
return getIntegerProperty(HttpClientPropertiesEnum.SOCKET_TIMEOUT);
}
public Integer getConnectionRequestTimeout()
{
return getIntegerProperty(HttpClientPropertiesEnum.CONNECTION_REQUEST_TIMEOUT);
}
public Integer getMaxTotalConnections()
{
return getIntegerProperty(HttpClientPropertiesEnum.MAX_TOTAL_CONNECTIONS);
}
public Integer getMaxHostConnections()
{
return getIntegerProperty(HttpClientPropertiesEnum.MAX_HOST_CONNECTIONS);
}
public Boolean isMTLSEnabled()
{
return getBooleanProperty(HttpClientPropertiesEnum.MTLS_ENABLED);
}
public boolean isHostnameVerificationDisabled()
{
return getBooleanProperty(HttpClientPropertiesEnum.HOSTNAME_VERIFICATION_DISABLED);
}
private enum HttpClientPropertiesEnum
{
CONNECTION_TIMEOUT("connectionTimeout", true),
SOCKET_TIMEOUT("socketTimeout", true),
CONNECTION_REQUEST_TIMEOUT("connectionRequestTimeout", true),
MAX_TOTAL_CONNECTIONS("maxTotalConnections", true),
MAX_HOST_CONNECTIONS("maxHostConnections", true),
HOSTNAME_VERIFICATION_DISABLED("hostnameVerificationDisabled", false),
MTLS_ENABLED("mTLSEnabled", true);
private final String name;
private final Boolean isRequired;
HttpClientPropertiesEnum(String propertyName, Boolean isRequired)
{
this.name = propertyName;
this.isRequired = isRequired;
}
private static final List<String> supportedProperties = new ArrayList<>();
static {
for (HttpClientPropertiesEnum property : HttpClientPropertiesEnum.values()) {
supportedProperties.add(property.name);
}
}
public static boolean isPropertyNameSupported(String propertyName) {
return supportedProperties.contains(propertyName);
}
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2023 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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/>.
*/
package org.alfresco.httpclient;
import org.alfresco.error.AlfrescoRuntimeException;
public class HttpClientException extends AlfrescoRuntimeException
{
public HttpClientException(String msgId)
{
super(msgId);
}
}

View File

@@ -45,6 +45,13 @@ public class ListBackedPagingResults<R> implements PagingResults<R>
size = list.size();
hasMore = false;
}
public ListBackedPagingResults(List<R> list, boolean hasMore)
{
this(list);
this.hasMore = hasMore;
}
public ListBackedPagingResults(List<R> list, PagingRequest paging)
{
// Excerpt

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>20.75</version>
<version>20.140-SNAPSHOT</version>
</parent>
<properties>

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Data model classes
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2023 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -51,6 +51,18 @@ public class MLText extends HashMap<Locale, String>
{
private static final long serialVersionUID = -3696135175650511841L;
/**
* Returns default locale used by the {@link MLText} implementation
*
* @see I18NUtil#getLocale()
*
* @return default locale
*/
public static Locale getDefaultLocale()
{
return I18NUtil.getLocale();
}
public MLText()
{
super(3, 0.75F);
@@ -61,13 +73,13 @@ public class MLText extends HashMap<Locale, String>
*
* @param value the value for the current default locale
*
* @see I18NUtil#getLocale()
* @see #getDefaultLocale()
* @see #MLText(Locale, String)
* @see #getDefaultValue()
*/
public MLText(String value)
{
this(I18NUtil.getLocale(), value);
this(getDefaultLocale(), value);
}
/**
@@ -124,7 +136,7 @@ public class MLText extends HashMap<Locale, String>
/**
* Retrieves a default value from the set of available locales.<br/>
*
* @see I18NUtil#getLocale()
* @see #getDefaultLocale()
* @see #getClosestValue(Locale)
*/
public String getDefaultValue()
@@ -135,7 +147,7 @@ public class MLText extends HashMap<Locale, String>
return null;
}
// There is some hope of getting a match
Locale locale = I18NUtil.getLocale();
Locale locale = getDefaultLocale();
return getClosestValue(locale);
}
@@ -168,7 +180,7 @@ public class MLText extends HashMap<Locale, String>
if (match == null)
{
// No close matches for the locale - go for the default locale
locale = I18NUtil.getLocale();
locale = getDefaultLocale();
match = I18NUtil.getNearestLocale(locale, options);
if (match == null)
{

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>20.75</version>
<version>20.140-SNAPSHOT</version>
</parent>
<dependencies>

View File

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

View File

@@ -1,6 +1,6 @@
# Fetch image based on Tomcat 9.0, Java 17 and Rocky Linux 8
# More infos about this image: https://github.com/Alfresco/alfresco-docker-base-tomcat
FROM alfresco/alfresco-base-tomcat:tomcat9-jre17-rockylinux8-202209261711
FROM alfresco/alfresco-base-tomcat:tomcat9-jre17-rockylinux8-202303081618
# Set default docker_context.
ARG resource_path=target
@@ -65,7 +65,7 @@ RUN sed -i -e "s_appender.rolling.fileName\=alfresco.log_appender.rolling.fileNa
RUN yum install -y fontconfig-2.13.1-4.el8 \
dejavu-fonts-common-2.35-7.el8 \
fontpackages-filesystem-1.44-22.el8 \
freetype-2.9.1-4.el8_3.1 \
freetype-2.9.1-9.el8 \
libpng-1.6.34-5.el8 \
dejavu-sans-fonts-2.35-7.el8 && \
yum clean all

View File

@@ -7,11 +7,12 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>20.75</version>
<version>20.140-SNAPSHOT</version>
</parent>
<properties>
<image.name>alfresco/alfresco-community-repo-base</image.name>
<scripts.directory>${project.parent.parent.basedir}/scripts</scripts.directory>
</properties>
<build>
@@ -156,6 +157,67 @@
</build>
</profile>
<profile>
<id>build-multiarch-docker-images</id>
<build>
<plugins>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<configuration>
<images>
<image>
<name>${local.registry}/${image.name}:${image.tag}</name>
<build>
<buildx>
<platforms>
<platform>linux/amd64</platform>
<platform>linux/arm64</platform>
</platforms>
<builderName>${builder.name}</builderName>
</buildx>
<contextDir>${project.basedir}</contextDir>
</build>
</image>
</images>
</configuration>
<executions>
<execution>
<id>build-push-image</id>
<phase>package</phase>
<goals>
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>exec-maven-plugin</artifactId>
<groupId>org.codehaus.mojo</groupId>
<executions>
<execution>
<id>prepare-buildx</id>
<phase>generate-sources</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>${scripts.directory}/prepare_buildx.sh</executable>
<arguments>
<argument>${builder.name}</argument>
<argument>${image.registry}</argument>
<argument>${image.name}</argument>
<argument>${image.tag}</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>push-docker-images</id>
<!-- publishes "image:latest" on Quay & DockerHub -->
@@ -168,12 +230,29 @@
<images>
<!-- Quay image -->
<image>
<name>${image.name}:${image.tag}</name>
<registry>${image.registry}</registry>
<name>${image.registry}/${image.name}:${image.tag}</name>
<build>
<buildx>
<platforms>
<platform>linux/amd64</platform>
<platform>linux/arm64</platform>
</platforms>
</buildx>
<contextDir>${project.basedir}</contextDir>
</build>
</image>
<!-- DockerHub image -->
<image>
<name>${image.name}:${image.tag}</name>
<build>
<buildx>
<platforms>
<platform>linux/amd64</platform>
<platform>linux/arm64</platform>
</platforms>
</buildx>
<contextDir>${project.basedir}</contextDir>
</build>
</image>
</images>
</configuration>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>20.75</version>
<version>20.140-SNAPSHOT</version>
</parent>
<modules>

View File

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

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>20.75</version>
<version>20.140-SNAPSHOT</version>
</parent>
<organization>

View File

@@ -4,8 +4,6 @@ import org.alfresco.utility.data.AisToken;
import org.alfresco.utility.data.auth.DataAIS;
import org.alfresco.utility.model.UserModel;
import org.apache.chemistry.opencmis.commons.SessionParameter;
import org.keycloak.authorization.client.util.HttpResponseException;
import org.keycloak.representations.AccessTokenResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -86,9 +84,9 @@ public class AuthParameterProviderFactory
parameters.put(SessionParameter.OAUTH_REFRESH_TOKEN, aisToken.getRefreshToken());
parameters.put(SessionParameter.OAUTH_EXPIRATION_TIMESTAMP, String.valueOf(System.currentTimeMillis()
+ (aisToken.getExpiresIn() * 1000))); // getExpiresIn is in seconds
parameters.put(SessionParameter.OAUTH_TOKEN_ENDPOINT, cmisProperties.aisProperty().getAdapterConfig().getAuthServerUrl()
parameters.put(SessionParameter.OAUTH_TOKEN_ENDPOINT, cmisProperties.aisProperty().getAuthServerUrl()
+ "/realms/alfresco/protocol/openid-connect/token");
parameters.put(SessionParameter.OAUTH_CLIENT_ID, cmisProperties.aisProperty().getAdapterConfig().getResource());
parameters.put(SessionParameter.OAUTH_CLIENT_ID, cmisProperties.aisProperty().getResource());
return parameters;
}
@@ -110,10 +108,10 @@ public class AuthParameterProviderFactory
// Attempt to get an access token for userModel from AIS
aisToken = dataAIS.perform().getAccessToken(userModel);
}
catch (HttpResponseException e)
catch (AssertionError e)
{
// Trying to authenticate with invalid user credentials so return an invalid access token
if (e.getStatusCode() == 401)
if (e.getMessage().contains("invalid_grant"))
{
STEP(String.format("%s Invalid user credentials were provided %s:%s. Using invalid token for reqest.",
STEP_PREFIX, userModel.getUsername(), userModel.getPassword()));

View File

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

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>20.75</version>
<version>20.140-SNAPSHOT</version>
</parent>
<developers>
@@ -95,7 +95,6 @@
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>${dependency.jakarta-json-path.version}</version>
</dependency>
</dependencies>

View File

@@ -8,7 +8,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>20.75</version>
<version>20.140-SNAPSHOT</version>
</parent>
<properties>
@@ -165,14 +165,14 @@
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>3.0.12</version>
<version>3.0.16</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.codehaus.groovy/groovy-json-->
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-json</artifactId>
<version>3.0.12</version>
<version>3.0.16</version>
</dependency>
<dependency>

View File

@@ -30,7 +30,6 @@ import static org.alfresco.utility.report.log.Step.STEP;
import org.alfresco.utility.data.AisToken;
import org.alfresco.utility.data.auth.DataAIS;
import org.alfresco.utility.model.UserModel;
import org.keycloak.authorization.client.util.HttpResponseException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -86,12 +85,11 @@ public class RestAisAuthentication
// Attempt to get an access token for userModel from AIS
aisToken = dataAIS.perform().getAccessToken(userModel);
}
catch (HttpResponseException e)
catch (AssertionError e)
{
// Trying to authenticate with invalid user credentials or disabled
// user so return an invalid access token
String httpResponse = new String(e.getBytes());
if (e.getStatusCode() == 401 || httpResponse.contains(USER_DISABLED_MSG))
if (e.getMessage().contains("invalid_grant"))
{
STEP(String.format("%s User disabled or invalid user credentials were provided %s:%s. Using invalid token for request.", STEP_PREFIX,
userModel.getUsername(), userModel.getPassword()));

View File

@@ -680,17 +680,22 @@ public class RestWrapper extends DSLWrapper<RestWrapper>
}
else
{
if (returnedResponse.getContentType().contains("image/png"))
{
LOG.info("On {} {}, received the response with an image and headers: \n{}", restRequest.getHttpMethod(), restRequest.getPath(),
returnedResponse.getHeaders().toString());
}
else if (returnedResponse.getContentType().contains("application/json") && !returnedResponse.asString().isEmpty())
if (returnedResponse.asString().isEmpty())
{
LOG.info("On {} {}, received the following response \n{}", restRequest.getHttpMethod(), restRequest.getPath(),
Utility.prettyPrintJsonString(returnedResponse.asString()));
returnedResponse.getStatusCode());
}
else if (returnedResponse.getContentType().contains("application/xml") && !returnedResponse.asString().isEmpty())
else if (returnedResponse.getContentType().contains("image/png"))
{
LOG.info("On {} {}, received the response with an image and headers: \n{}", restRequest.getHttpMethod(), restRequest.getPath(),
returnedResponse.getHeaders().toString());
}
else if (returnedResponse.getContentType().contains("application/json"))
{
LOG.info("On {} {}, received the following response \n{}", restRequest.getHttpMethod(), restRequest.getPath(),
Utility.prettyPrintJsonString(returnedResponse.asString()));
}
else if (returnedResponse.getContentType().contains("application/xml"))
{
String response = parseXML(returnedResponse);
LOG.info("On {} {}, received the following response \n{}", restRequest.getHttpMethod(), restRequest.getPath(), response);
@@ -698,7 +703,7 @@ public class RestWrapper extends DSLWrapper<RestWrapper>
else
{
LOG.info("On {} {}, received the following response \n{}", restRequest.getHttpMethod(), restRequest.getPath(),
ToStringBuilder.reflectionToString(returnedResponse.asString(), ToStringStyle.MULTI_LINE_STYLE));
ToStringBuilder.reflectionToString(returnedResponse.asString(), ToStringStyle.MULTI_LINE_STYLE));
}
}
}

View File

@@ -30,7 +30,10 @@ import static org.alfresco.utility.report.log.Step.STEP;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.alfresco.rest.core.IRestModelsCollection;
import org.alfresco.utility.exception.TestConfigurationException;
@@ -117,7 +120,7 @@ public class ModelsCollectionAssertion<C>
return (C) modelCollection;
}
@SuppressWarnings("unchecked")
@SuppressWarnings("unchecked")
public C entriesListDoesNotContain(String key, String value)
{
boolean exist = false;
@@ -143,6 +146,53 @@ public class ModelsCollectionAssertion<C>
return (C) modelCollection;
}
public C entrySetContains(String key, String... expectedValues)
{
return entrySetContains(key, Arrays.stream(expectedValues).collect(Collectors.toSet()));
}
@SuppressWarnings("unchecked")
public C entrySetContains(String key, Collection<String> expectedValues)
{
Collection<String> actualValues = ((List<Model>) modelCollection.getEntries()).stream()
.map(model -> extractValueAsString(model, key))
.collect(Collectors.toSet());
Assert.assertTrue(actualValues.containsAll(expectedValues), String.format("Entry with key: \"%s\" is expected to contain values: %s, but actual values are: %s",
key, expectedValues, actualValues));
return (C) modelCollection;
}
@SuppressWarnings("unchecked")
public C entrySetMatches(String key, Collection<String> expectedValues)
{
Collection<String> actualValues = ((List<Model>) modelCollection.getEntries()).stream()
.map(model -> extractValueAsString(model, key))
.collect(Collectors.toSet());
Assert.assertEqualsNoOrder(actualValues, expectedValues, String.format("Entry with key: \"%s\" is expected to match values: %s, but actual values are: %s",
key, expectedValues, actualValues));
return (C) modelCollection;
}
private String extractValueAsString(Model model, String key)
{
String fieldValue;
Object modelObject = loadModel(model);
try {
ObjectMapper mapper = new ObjectMapper();
String jsonInString = mapper.writeValueAsString(modelObject);
fieldValue = JsonPath.with(jsonInString).get(key);
} catch (Exception e) {
throw new TestConfigurationException(String.format(
"You try to assert field [%s] that doesn't exist in class: [%s]. Exception: %s, Please check your code!",
key, getClass().getCanonicalName(), e.getMessage()));
}
return fieldValue;
}
@SuppressWarnings("unchecked")
public C entriesListDoesNotContain(String key)
{

View File

@@ -60,7 +60,7 @@ public class RestErrorModel
public static String INVALID_MAXITEMS = "Invalid paging parameter maxItems:%s";
public static String INVALID_SKIPCOUNT = "Invalid paging parameter skipCount:%s";
public static String INVALID_TAG = "Tag name must not contain %s char sequence";
public static String EMPTY_TAG = "New tag cannot be null";
public static String BLANK_TAG = "New tag cannot be blank";
public static String UNKNOWN_ROLE = "Unknown role '%s'";
public static String ALREADY_Site_MEMBER = "%s is already a member of site %s";
public static String ALREADY_INVITED = "%s is already invited to site %s";

View File

@@ -2,7 +2,7 @@
* #%L
* alfresco-tas-restapi
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* Copyright (C) 2005 - 2023 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -27,6 +27,8 @@ package org.alfresco.rest.model;
import static org.alfresco.utility.report.log.Step.STEP;
import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.alfresco.rest.core.IRestModel;
@@ -73,4 +75,65 @@ public class RestTagModel extends TagModel implements IRestModel<RestTagModel>
this.count = count;
}
@Override
public boolean equals(Object o)
{
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
RestTagModel tagModel = (RestTagModel) o;
return Objects.equals(id, tagModel.id) && Objects.equals(tag, tagModel.tag) && Objects.equals(count, tagModel.count);
}
@Override
public int hashCode()
{
return Objects.hash(id, tag, count);
}
@Override
public String toString()
{
return "RestTagModel{" + "id='" + id + ", tag='" + tag + '\'' + ", count=" + count + '\'' + '}';
}
public static Builder builder()
{
return new Builder();
}
public static class Builder
{
private String id;
private String tag;
private Integer count;
public Builder id(String id)
{
this.id = id;
return this;
}
public Builder tag(String tag)
{
this.tag = tag;
return this;
}
public Builder count(Integer count)
{
this.count = count;
return this;
}
public RestTagModel create()
{
final RestTagModel tag = new RestTagModel();
tag.setId(id);
tag.setTag(this.tag);
tag.setCount(count);
return tag;
}
}
}

View File

@@ -82,4 +82,15 @@ public class Tags extends ModelRequest<Tags>
return restWrapper.processModel(RestTagModel.class, request);
}
/**
* Delete tag.
* - DELETE /tags/{tagId}
*/
public void deleteTag()
{
RestRequest request = RestRequest.
simpleRequest(HttpMethod.DELETE, "/tags/{tagId}", tag.getId());
restWrapper.processEmptyModel(request);
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* alfresco-tas-restapi
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* Copyright (C) 2005 - 2023 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -25,6 +25,12 @@
*/
package org.alfresco.rest.requests.coreAPI;
import static org.alfresco.rest.core.JsonBodyGenerator.arrayToJson;
import java.util.List;
import io.restassured.RestAssured;
import org.alfresco.rest.core.RestRequest;
import org.alfresco.rest.core.RestWrapper;
import org.alfresco.rest.model.RestCategoryModel;
import org.alfresco.rest.model.RestDownloadsModel;
@@ -49,8 +55,7 @@ import org.alfresco.rest.requests.Trashcan;
import org.alfresco.utility.model.RepoTestModel;
import org.alfresco.utility.model.SiteModel;
import org.alfresco.utility.model.UserModel;
import io.restassured.RestAssured;
import org.springframework.http.HttpMethod;
/**
* Defines the entire Rest Core API
@@ -172,6 +177,30 @@ public class RestCoreAPI extends ModelRequest<RestCoreAPI>
return new Networks(restWrapper);
}
/**
* Create a single orphan tag.
*
* @param tag Tag model to create.
* @return Created tag.
*/
public RestTagModel createSingleTag(RestTagModel tag)
{
RestRequest request = RestRequest.requestWithBody(HttpMethod.POST, tag.toJson(), "tags/");
return restWrapper.processModel(RestTagModel.class, request);
}
/**
* Create several orphan tags in one request.
*
* @param tags Tags models to create.
* @return Created tags.
*/
public RestTagModelsCollection createTags(List<RestTagModel> tags)
{
RestRequest request = RestRequest.requestWithBody(HttpMethod.POST, arrayToJson(tags), "tags/");
return restWrapper.processModels(RestTagModelsCollection.class, request);
}
public Tags usingTag(RestTagModel tag)
{
return new Tags(tag, restWrapper);

View File

@@ -0,0 +1,207 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2023 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.categories;
import static org.alfresco.utility.data.RandomData.getRandomName;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.OK;
import static org.testng.Assert.assertTrue;
import org.alfresco.dataprep.CMISUtil;
import org.alfresco.rest.model.RestCategoryModel;
import org.alfresco.rest.model.RestCategoryModelsCollection;
import org.alfresco.utility.Utility;
import org.alfresco.utility.model.FileModel;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.SiteModel;
import org.alfresco.utility.model.TestGroup;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class CategoriesCountTests extends CategoriesRestTest
{
private RestCategoryModel categoryLinkedWithFolder;
private RestCategoryModel categoryLinkedWithFile;
private RestCategoryModel categoryLinkedWithBoth;
private RestCategoryModel notLinkedCategory;
@BeforeClass(alwaysRun = true)
public void dataPreparation() throws Exception
{
STEP("Create user and site");
user = dataUser.createRandomTestUser();
SiteModel site = dataSite.usingUser(user).createPublicRandomSite();
STEP("Create a folder, file in it and few categories");
FolderModel folder = dataContent.usingUser(user).usingSite(site).createFolder();
FileModel file = dataContent.usingUser(user).usingResource(folder).createContent(CMISUtil.DocumentType.TEXT_PLAIN);
categoryLinkedWithFolder = prepareCategoryUnderRoot();
categoryLinkedWithFile = prepareCategoryUnderRoot();
categoryLinkedWithBoth = prepareCategoryUnder(prepareCategoryUnderRoot());
notLinkedCategory = prepareCategoryUnderRoot();
STEP("Link folder and file to categories");
linkContentToCategories(folder, categoryLinkedWithFolder, categoryLinkedWithBoth);
linkContentToCategories(file, categoryLinkedWithFile, categoryLinkedWithBoth);
STEP("Wait for indexing to complete");
Utility.sleep(1000, 60000, () -> restClient.authenticateUser(user)
.withCoreAPI()
.usingCategory(categoryLinkedWithFolder)
.include(INCLUDE_COUNT_PARAM)
.getCategory()
.assertThat()
.field(FIELD_COUNT)
.isNot(0));
}
/**
* Verify count for a category linked with file and folder.
*/
@Test(groups = { TestGroup.REST_API })
public void testGetCategoryById_includeCount()
{
STEP("Get linked category and verify if count is higher than 0");
final RestCategoryModel actualCategory = restClient.authenticateUser(user)
.withCoreAPI()
.usingCategory(categoryLinkedWithBoth)
.include(INCLUDE_COUNT_PARAM)
.getCategory();
restClient.assertStatusCodeIs(OK);
actualCategory.assertThat().field(FIELD_ID).is(categoryLinkedWithBoth.getId());
actualCategory.assertThat().field(FIELD_COUNT).is(2);
}
/**
* Verify count for a category not linked with any content.
*/
@Test(groups = { TestGroup.REST_API })
public void testGetCategoryById_includeCountForNonLinkedCategory()
{
STEP("Get non-linked category and verify if count is 0");
final RestCategoryModel actualCategory = restClient.authenticateUser(user)
.withCoreAPI()
.usingCategory(notLinkedCategory)
.include(INCLUDE_COUNT_PARAM)
.getCategory();
restClient.assertStatusCodeIs(OK);
actualCategory.assertThat().field(FIELD_ID).is(notLinkedCategory.getId());
actualCategory.assertThat().field(FIELD_COUNT).is(0);
}
/**
* Verify count for three categories: linked with file, linked with folder and third not linked to any content.
*/
@Test(groups = { TestGroup.REST_API })
public void testGetCategories_includeCount()
{
STEP("Get few categories and verify its counts");
final RestCategoryModel parentCategory = createCategoryModelWithId(ROOT_CATEGORY_ID);
final RestCategoryModelsCollection actualCategories = restClient.authenticateUser(user)
.withCoreAPI()
.usingCategory(parentCategory)
.include(INCLUDE_COUNT_PARAM)
.getCategoryChildren();
restClient.assertStatusCodeIs(OK);
assertTrue(actualCategories.getEntries().stream()
.map(RestCategoryModel::onModel)
.anyMatch(category -> category.getId().equals(categoryLinkedWithFolder.getId()) && category.getCount() == 1));
assertTrue(actualCategories.getEntries().stream()
.map(RestCategoryModel::onModel)
.anyMatch(category -> category.getId().equals(categoryLinkedWithFile.getId()) && category.getCount() == 1));
assertTrue(actualCategories.getEntries().stream()
.map(RestCategoryModel::onModel)
.anyMatch(category -> category.getId().equals(notLinkedCategory.getId()) && category.getCount() == 0));
}
/**
* Create category and verify that its count is 0.
*/
@Test(groups = { TestGroup.REST_API })
public void testCreateCategory_includingCount()
{
STEP("Create a category under root and verify if count is 0");
final String categoryName = getRandomName("Category");
final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID);
final RestCategoryModel aCategory = createCategoryModelWithName(categoryName);
final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser())
.withCoreAPI()
.include(INCLUDE_COUNT_PARAM)
.usingCategory(rootCategory)
.createSingleCategory(aCategory);
STEP("Create a category under root category (as admin)");
restClient.assertStatusCodeIs(CREATED);
createdCategory.assertThat().field(FIELD_NAME).is(categoryName);
createdCategory.assertThat().field(FIELD_COUNT).is(0);
}
/**
* Update category linked to file and folder and verify that its count is 2.
*/
@Test(groups = { TestGroup.REST_API })
public void testUpdateCategory_includeCount()
{
STEP("Update linked category and verify if count is higher than 0");
final String categoryNewName = getRandomName("NewCategoryName");
final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(categoryNewName);
final RestCategoryModel updatedCategory = restClient.authenticateUser(dataUser.getAdminUser())
.withCoreAPI()
.usingCategory(categoryLinkedWithBoth)
.include(INCLUDE_COUNT_PARAM)
.updateCategory(fixedCategoryModel);
restClient.assertStatusCodeIs(OK);
updatedCategory.assertThat().field(FIELD_ID).is(categoryLinkedWithBoth.getId());
updatedCategory.assertThat().field(FIELD_COUNT).is(2);
}
/**
* Update category not linked to any content and verify that its count is 0.
*/
@Test(groups = { TestGroup.REST_API })
public void testUpdateCategory_includeCountForNonLinkedCategory()
{
STEP("Update non-linked category and verify if count is 0");
final String categoryNewName = getRandomName("NewCategoryName");
final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(categoryNewName);
final RestCategoryModel updatedCategory = restClient.authenticateUser(dataUser.getAdminUser())
.withCoreAPI()
.usingCategory(notLinkedCategory)
.include(INCLUDE_COUNT_PARAM)
.updateCategory(fixedCategoryModel);
restClient.assertStatusCodeIs(OK);
updatedCategory.assertThat().field(FIELD_ID).is(notLinkedCategory.getId());
updatedCategory.assertThat().field(FIELD_COUNT).is(0);
}
}

View File

@@ -30,6 +30,7 @@ import static org.alfresco.utility.data.RandomData.getRandomName;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.springframework.http.HttpStatus.CREATED;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@@ -37,18 +38,21 @@ import java.util.stream.IntStream;
import org.alfresco.rest.RestTest;
import org.alfresco.rest.model.RestCategoryLinkBodyModel;
import org.alfresco.rest.model.RestCategoryModel;
import org.alfresco.rest.model.RestCategoryModelsCollection;
import org.alfresco.utility.model.RepoTestModel;
import org.alfresco.utility.model.UserModel;
import org.testng.annotations.BeforeClass;
abstract class CategoriesRestTest extends RestTest
{
protected static final String INCLUDE_COUNT_PARAM = "count";
protected static final String ROOT_CATEGORY_ID = "-root-";
protected static final String CATEGORY_NAME_PREFIX = "CategoryName";
protected static final String FIELD_NAME = "name";
protected static final String FIELD_ID = "id";
protected static final String FIELD_PARENT_ID = "parentId";
protected static final String FIELD_HAS_CHILDREN = "hasChildren";
protected static final String FIELD_COUNT = "count";
protected UserModel user;
@@ -59,14 +63,26 @@ abstract class CategoriesRestTest extends RestTest
user = dataUser.createRandomTestUser();
}
protected RestCategoryModel prepareCategoryUnderRoot()
protected RestCategoryModelsCollection linkContentToCategories(final RepoTestModel node, final RestCategoryModel... categories)
{
return prepareCategoryUnder(ROOT_CATEGORY_ID);
final List<RestCategoryLinkBodyModel> categoryLinkModels = Arrays.stream(categories)
.map(RestCategoryModel::getId)
.map(this::createCategoryLinkModelWithId)
.collect(Collectors.toList());
final RestCategoryModelsCollection linkedCategories = restClient.authenticateUser(user).withCoreAPI().usingNode(node).linkToCategories(categoryLinkModels);
restClient.assertStatusCodeIs(CREATED);
return linkedCategories;
}
protected RestCategoryModel prepareCategoryUnder(final String parentId)
protected RestCategoryModel prepareCategoryUnderRoot()
{
return prepareCategoryUnder(createCategoryModelWithId(ROOT_CATEGORY_ID));
}
protected RestCategoryModel prepareCategoryUnder(final RestCategoryModel parentCategory)
{
final RestCategoryModel parentCategory = createCategoryModelWithId(parentId);
final RestCategoryModel categoryModel = createCategoryModelWithName(getRandomName(CATEGORY_NAME_PREFIX));
final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser())
.withCoreAPI()

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* Copyright (C) 2005 - 2023 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -26,6 +26,7 @@
package org.alfresco.rest.categories;
import static org.alfresco.utility.data.RandomData.getRandomName;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CREATED;
@@ -38,7 +39,6 @@ import java.util.stream.IntStream;
import org.alfresco.rest.model.RestCategoryModel;
import org.alfresco.rest.model.RestCategoryModelsCollection;
import org.alfresco.utility.data.RandomData;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.SiteModel;
import org.alfresco.utility.model.TestGroup;
@@ -56,7 +56,7 @@ public class CreateCategoriesTests extends CategoriesRestTest
{
STEP("Create a category under root category (as admin)");
final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID);
final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category"));
final RestCategoryModel aCategory = createCategoryModelWithName(getRandomName("Category"));
final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser())
.withCoreAPI()
.usingCategory(rootCategory)
@@ -92,7 +92,7 @@ public class CreateCategoriesTests extends CategoriesRestTest
{
STEP("Create a category under root category (as admin)");
final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID);
final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category"));
final RestCategoryModel aCategory = createCategoryModelWithName(getRandomName("Category"));
final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser())
.withCoreAPI()
.usingCategory(rootCategory)
@@ -140,7 +140,7 @@ public class CreateCategoriesTests extends CategoriesRestTest
{
STEP("Create a category under root category (as admin)");
final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID);
final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category"));
final RestCategoryModel aCategory = createCategoryModelWithName(getRandomName("Category"));
final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser())
.withCoreAPI()
.usingCategory(rootCategory)
@@ -186,7 +186,7 @@ public class CreateCategoriesTests extends CategoriesRestTest
{
STEP("Create a category under root category (as user)");
final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID);
final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category"));
final RestCategoryModel aCategory = createCategoryModelWithName(getRandomName("Category"));
restClient.authenticateUser(user)
.withCoreAPI()
.usingCategory(rootCategory)
@@ -203,7 +203,7 @@ public class CreateCategoriesTests extends CategoriesRestTest
STEP("Create a category under non existing category node (as admin)");
final String id = "non-existing-node-id";
final RestCategoryModel rootCategory = createCategoryModelWithId(id);
final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category"));
final RestCategoryModel aCategory = createCategoryModelWithName(getRandomName("Category"));
restClient.authenticateUser(dataUser.getAdminUser())
.withCoreAPI()
.usingCategory(rootCategory)
@@ -223,7 +223,7 @@ public class CreateCategoriesTests extends CategoriesRestTest
STEP("Create a category under folder node (as admin)");
final RestCategoryModel rootCategory = createCategoryModelWithId(folder.getNodeRef());
final RestCategoryModel aCategory = createCategoryModelWithName(RandomData.getRandomName("Category"));
final RestCategoryModel aCategory = createCategoryModelWithName(getRandomName("Category"));
restClient.authenticateUser(dataUser.getAdminUser())
.withCoreAPI()
.usingCategory(rootCategory)
@@ -231,10 +231,31 @@ public class CreateCategoriesTests extends CategoriesRestTest
restClient.assertStatusCodeIs(BAD_REQUEST).assertLastError().containsSummary("Node id does not refer to a valid category");
}
/**
* Check weather count present in create category request will be ignored.
*/
@Test(groups = { TestGroup.REST_API })
public void testCreateCategoryUnderRoot_verifyIfCountInRequestIsIgnored()
{
STEP("Try to create a category with filled count under root");
final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID);
final RestCategoryModel aCategory = createCategoryModelWithName(getRandomName("Category"));
aCategory.setCount(2);
final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser())
.withCoreAPI()
.usingCategory(rootCategory)
.include(INCLUDE_COUNT_PARAM)
.createSingleCategory(aCategory);
restClient.assertStatusCodeIs(CREATED);
createdCategory.assertThat().field(FIELD_NAME).is(aCategory.getName());
createdCategory.assertThat().field(FIELD_COUNT).is(0);
}
static List<RestCategoryModel> getCategoriesToCreate(final int count)
{
return IntStream.range(0, count)
.mapToObj(i -> RestCategoryModel.builder().name(RandomData.getRandomName("SubCategory")).create())
.mapToObj(i -> RestCategoryModel.builder().name(getRandomName("SubCategory")).create())
.collect(Collectors.toList());
}
}

View File

@@ -79,7 +79,7 @@ public class UpdateCategoriesTests extends CategoriesRestTest
final RestCategoryModel createdCategory = prepareCategoryUnderRoot();
STEP("Prepare as admin a subcategory of root's child category");
final RestCategoryModel createdSubcategory = prepareCategoryUnder(createdCategory.getId());
final RestCategoryModel createdSubcategory = prepareCategoryUnder(createdCategory);
STEP("Update as admin newly created subcategory");
final String categoryNewName = getRandomName(CATEGORY_NEW_NAME_PREFIX);
@@ -233,4 +233,27 @@ public class UpdateCategoriesTests extends CategoriesRestTest
restClient.assertStatusCodeIs(OK);
updatedCategory.assertThat().field(FIELD_NAME).is(categoryNewName);
}
/**
* Check whether count present in update category request will be ignored.
*/
@Test(groups = { TestGroup.REST_API })
public void testUpdateCategory_verifyIfCountInRequestIsIgnored()
{
STEP("Prepare a category under root category");
final RestCategoryModel createdCategory = prepareCategoryUnderRoot();
STEP("Try to update newly created category providing new name and count number");
final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(getRandomName(CATEGORY_NEW_NAME_PREFIX));
fixedCategoryModel.setCount(2);
final RestCategoryModel updatedCategory = restClient.authenticateUser(dataUser.getAdminUser())
.withCoreAPI()
.usingCategory(createdCategory)
.include(INCLUDE_COUNT_PARAM)
.updateCategory(fixedCategoryModel);
restClient.assertStatusCodeIs(OK);
updatedCategory.assertThat().field(FIELD_ID).is(createdCategory.getId());
updatedCategory.assertThat().field(FIELD_COUNT).is(0);
}
}

View File

@@ -14,7 +14,6 @@ import org.alfresco.utility.testrail.annotation.TestRail;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Ignore;
import org.testng.annotations.Test;
public class AddFavoritesTests extends RestTest
@@ -355,7 +354,6 @@ public class AddFavoritesTests extends RestTest
@TestRail(section = { TestGroup.REST_API, TestGroup.FAVORITES }, executionType = ExecutionType.REGRESSION,
description = "Verify add file favorite with tag id returns status code 404")
@Test(groups = { TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION })
@Ignore
public void addFileFavoriteUsingTagId() throws Exception
{
FileModel file = dataContent.usingSite(siteModel).usingUser(adminUserModel).createContent(CMISUtil.DocumentType.TEXT_PLAIN);

View File

@@ -0,0 +1,226 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2023 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.tags;
import static org.alfresco.utility.data.RandomData.getRandomName;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CONFLICT;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.http.HttpStatus.OK;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.alfresco.rest.RestTest;
import org.alfresco.rest.model.RestTagModel;
import org.alfresco.rest.model.RestTagModelsCollection;
import org.alfresco.utility.model.TestGroup;
import org.alfresco.utility.model.UserModel;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class CreateTagsTests extends RestTest
{
private static final String FIELD_ID = "id";
private static final String FIELD_TAG = "tag";
private static final String FIELD_COUNT = "count";
private static final String TAG_NAME_PREFIX = "tag-name";
private UserModel admin;
private UserModel user;
@BeforeClass
public void init()
{
admin = dataUser.getAdminUser();
user = dataUser.createRandomTestUser();
}
/**
* Verify if tag does not exist in the system, create one as admin and check if now it's there.
*/
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS })
public void testCreateSingleTag()
{
STEP("Create single tag as admin");
final RestTagModel tagModel = createTagModelWithName(getRandomName("99gat").toLowerCase());
final RestTagModel createdTag = restClient.authenticateUser(admin).withCoreAPI().createSingleTag(tagModel);
restClient.assertStatusCodeIs(CREATED);
createdTag.assertThat().field(FIELD_TAG).is(tagModel.getTag())
.assertThat().field(FIELD_ID).isNotEmpty();
STEP("Verify that tag does exist in the system");
RestTagModel tag = restClient.authenticateUser(admin).withCoreAPI().getTag(createdTag);
restClient.assertStatusCodeIs(OK);
tag.assertThat().isEqualTo(createdTag);
}
/**
* Create multiple orphan tags.
*/
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS })
public void testCreateMultipleTags()
{
STEP("Create several tags as admin");
final List<RestTagModel> tagModels = IntStream.range(0, 3)
.mapToObj(i -> createTagModelWithName(getRandomName(TAG_NAME_PREFIX + "-" + i).toLowerCase()))
.collect(Collectors.toList());
final RestTagModelsCollection createdTags = restClient.authenticateUser(admin).withCoreAPI().createTags(tagModels);
restClient.assertStatusCodeIs(CREATED);
IntStream.range(0, tagModels.size())
.forEach(i -> createdTags.getEntries().get(i).onModel()
.assertThat().field(FIELD_TAG).is(tagModels.get(i).getTag())
.assertThat().field(FIELD_ID).isNotEmpty()
);
}
/**
* Verify that tag name's case will be lowered.
*/
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS })
public void testCreateSingleTag_usingUppercaseName()
{
STEP("Create single tag as admin using uppercase name");
final RestTagModel tagModel = createTagModelWithName(getRandomName(TAG_NAME_PREFIX).toUpperCase());
final RestTagModel createdTag = restClient.authenticateUser(admin).withCoreAPI().createSingleTag(tagModel);
restClient.assertStatusCodeIs(CREATED);
createdTag.assertThat().field(FIELD_TAG).is(tagModel.getTag().toLowerCase())
.assertThat().field(FIELD_ID).isNotEmpty();
}
/**
* Try to create few tags including repeating ones. Repeated tags should be omitted.
*/
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS })
public void testCreateMultipleTags_withRepeatedName()
{
STEP("Create models of tags");
final String repeatedTagName = getRandomName(TAG_NAME_PREFIX).toLowerCase();
final List<RestTagModel> tagModels = List.of(
createTagModelWithName(repeatedTagName),
createTagModelWithName(getRandomName(TAG_NAME_PREFIX).toLowerCase()),
createTagModelWithName(repeatedTagName)
);
STEP("Create several tags skipping repeating names");
final RestTagModelsCollection createdTags = restClient.authenticateUser(admin).withCoreAPI().createTags(tagModels);
restClient.assertStatusCodeIs(CREATED);
createdTags.assertThat().entriesListCountIs(2);
createdTags.assertThat().entriesListContains(FIELD_TAG, tagModels.get(0).getTag())
.and().entriesListContains(FIELD_TAG, tagModels.get(1).getTag());
}
/**
* Try to create a tag as a common user and expect 403 (Forbidden)
*/
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS })
public void testCreateTag_asUser()
{
STEP("Try to create single tag as a common user and expect 403");
final RestTagModel tagModel = createTagModelWithRandomName();
restClient.authenticateUser(user).withCoreAPI().createSingleTag(tagModel);
restClient.assertStatusCodeIs(FORBIDDEN);
}
/**
* Try to call create tag API passing empty list and expect 400 (Bad Request)
*/
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS })
public void testCreateTags_passingEmptyList()
{
STEP("Pass empty list while creating tags and expect 400");
restClient.authenticateUser(admin).withCoreAPI().createTags(Collections.emptyList());
restClient.assertStatusCodeIs(BAD_REQUEST);
}
/**
* Try to create a tag, which already exists in the system and expect 409 (Conflict)
*/
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS })
public void testCreateTag_usingAlreadyExistingTagName()
{
STEP("Create some tag in the system");
final RestTagModel tagToCreate = createTagModelWithRandomName();
final RestTagModel alreadyExistingTag = prepareOrphanTag(tagToCreate);
// set original name instead the case lowered one
alreadyExistingTag.setTag(tagToCreate.getTag());
STEP("Try to use already existing tag to create duplicate and expect 409");
restClient.authenticateUser(admin).withCoreAPI().createSingleTag(alreadyExistingTag);
restClient
.assertStatusCodeIs(CONFLICT)
.assertLastError().containsSummary("Duplicate child name not allowed: " + alreadyExistingTag.getTag().toLowerCase());
}
/**
* Verify if count field is 0 for newly created tags.
*/
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS })
public void testCreateTag_includingCount()
{
STEP("Create single tag as admin including count and verify if it is 0");
final RestTagModel tagModel = createTagModelWithName(getRandomName(TAG_NAME_PREFIX).toLowerCase());
final RestTagModel createdTag = restClient.authenticateUser(admin).withCoreAPI().include(FIELD_COUNT).createSingleTag(tagModel);
restClient.assertStatusCodeIs(CREATED);
createdTag.assertThat().field(FIELD_TAG).is(tagModel.getTag())
.assertThat().field(FIELD_ID).isNotEmpty()
.assertThat().field(FIELD_COUNT).is(0);
}
private RestTagModel prepareOrphanTagWithRandomName()
{
return prepareOrphanTag(createTagModelWithRandomName());
}
private RestTagModel prepareOrphanTag(final RestTagModel tagModel)
{
final RestTagModel tag = restClient.authenticateUser(admin).withCoreAPI().createSingleTag(tagModel);
restClient.assertStatusCodeIs(CREATED);
return tag;
}
private static RestTagModel createTagModelWithRandomName()
{
return createTagModelWithName(getRandomName(TAG_NAME_PREFIX));
}
private static RestTagModel createTagModelWithName(final String tagName)
{
return RestTagModel.builder().tag(tagName).create();
}
}

View File

@@ -0,0 +1,88 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2023 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.tags;
import org.alfresco.dataprep.CMISUtil;
import org.alfresco.rest.model.RestTagModel;
import org.alfresco.utility.constants.UserRole;
import org.alfresco.utility.model.TestGroup;
import org.testng.annotations.Test;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.HttpStatus.NO_CONTENT;
public class DeleteTagsTests extends TagsDataPrep
{
/**
* Check we can delete a tag by its id.
*/
@Test(groups = {TestGroup.REST_API})
public void testDeleteTag()
{
STEP("Create a tag assigned to a document and send a request to delete it.");
document = dataContent.usingUser(adminUserModel).usingSite(siteModel).createContent(CMISUtil.DocumentType.TEXT_PLAIN);
RestTagModel aTag = createTagForDocument(document);
restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI().usingTag(aTag).deleteTag();
restClient.assertStatusCodeIs(NO_CONTENT);
STEP("Ensure that the tag has been deleted by sending a GET request and receiving 404.");
restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI().getTag(aTag);
restClient.assertStatusCodeIs(NOT_FOUND);
}
/**
* Attempt to delete a tag as a site manager and receive 403 error.
* Other user roles have fewer permissions than a SiteManager and thus would also be forbidden from deleting a tag.
*/
@Test(groups = {TestGroup.REST_API})
public void testDeleteTagAsSiteManager_andFail()
{
STEP("Create a tag assigned to a document and attempt to delete as a site manager");
document = dataContent.usingUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager)).usingSite(siteModel).createContent(CMISUtil.DocumentType.TEXT_PLAIN);
RestTagModel aTag = createTagForDocument(document);
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager)).withCoreAPI().usingTag(aTag).deleteTag();
restClient.assertStatusCodeIs(FORBIDDEN).assertLastError().containsSummary("Current user does not have permission to manage a tag");
}
/**
* Check we receive 404 error when trying to delete a tag with a non-existent id
*/
@Test(groups = {TestGroup.REST_API})
public void testDeleteNonExistentTag()
{
STEP("Attempt to delete tag with non-existent id and receive 404 error");
final String id = "non-existing-dummy-id";
final RestTagModel tagModel = createTagModelWithId(id);
restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI().usingTag(tagModel).deleteTag();
restClient.assertStatusCodeIs(NOT_FOUND);
}
}

View File

@@ -1,5 +1,10 @@
package org.alfresco.rest.tags;
import static org.alfresco.utility.data.RandomData.getRandomName;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.OK;
import org.alfresco.rest.model.RestErrorModel;
import org.alfresco.rest.model.RestTagModel;
import org.alfresco.utility.constants.UserRole;
@@ -14,6 +19,11 @@ import org.testng.annotations.Test;
public class GetTagTests extends TagsDataPrep
{
private static final String FIELD_ID = "id";
private static final String FIELD_TAG = "tag";
private static final String FIELD_COUNT = "count";
private static final String TAG_NAME_PREFIX = "tag-name";
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, description = "Verify admin user gets tag using REST API and status code is OK (200)")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void adminIsAbleToGetTag() throws Exception
@@ -128,4 +138,25 @@ public class GetTagTests extends TagsDataPrep
.descriptionURLIs(RestErrorModel.RESTAPIEXPLORER)
.stackTraceIs(RestErrorModel.STACKTRACE);
}
}
/**
* Verify that count field is not present for searched tag.
*/
@Test(groups = {TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION})
public void testGetTag_notIncludingCount()
{
STEP("Create single tag as admin");
final RestTagModel tagModel = createTagModelWithName(getRandomName(TAG_NAME_PREFIX).toLowerCase());
final RestTagModel createdTag = restClient.authenticateUser(adminUserModel).withCoreAPI().createSingleTag(tagModel);
restClient.assertStatusCodeIs(CREATED);
STEP("Get a single tag, not including count and verify if it is not present in the response");
final RestTagModel searchedTag = restClient.withCoreAPI().getTag(createdTag);
restClient.assertStatusCodeIs(OK);
searchedTag.assertThat().field(FIELD_TAG).is(tagModel.getTag())
.assertThat().field(FIELD_ID).isNotEmpty()
.assertThat().field(FIELD_COUNT).isNull();
}
}

View File

@@ -1,79 +1,81 @@
package org.alfresco.rest.tags;
import static org.alfresco.utility.data.RandomData.getRandomName;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.springframework.http.HttpStatus.OK;
import java.util.Set;
import org.alfresco.rest.model.RestErrorModel;
import org.alfresco.rest.model.RestTagModel;
import org.alfresco.rest.model.RestTagModelsCollection;
import org.alfresco.utility.constants.UserRole;
import org.alfresco.utility.data.RandomData;
import org.alfresco.utility.model.TestGroup;
import org.alfresco.utility.testrail.ExecutionType;
import org.alfresco.utility.testrail.annotation.TestRail;
import org.springframework.http.HttpStatus;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@Test(groups = {TestGroup.REQUIRE_SOLR})
public class GetTagsTests extends TagsDataPrep
{
@BeforeClass(alwaysRun = true)
public void dataPreparation() throws Exception
{
init();
}
private static final String FIELD_ID = "id";
private static final String FIELD_TAG = "tag";
private static final String FIELD_COUNT = "count";
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.SANITY, description = "Verify user with Manager role gets tags using REST API and status code is OK (200)")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.SANITY })
public void getTagsWithManagerRole() throws Exception
public void getTagsWithManagerRole()
{
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager));
returnedCollection = restClient.withParams("maxItems=10000").withCoreAPI().getTags();
restClient.assertStatusCodeIs(HttpStatus.OK);
restClient.assertStatusCodeIs(OK);
returnedCollection.assertThat().entriesListIsNotEmpty()
.and().entriesListContains("tag", documentTagValue.toLowerCase())
.and().entriesListContains("tag", documentTagValue2.toLowerCase());
.and().entriesListContains("tag", documentTagValue)
.and().entriesListContains("tag", documentTagValue2);
}
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, description = "Verify user with Collaborator role gets tags using REST API and status code is OK (200)")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void getTagsWithCollaboratorRole() throws Exception
public void getTagsWithCollaboratorRole()
{
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteCollaborator));
returnedCollection = restClient.withParams("maxItems=10000").withCoreAPI().getTags();
restClient.assertStatusCodeIs(HttpStatus.OK);
restClient.assertStatusCodeIs(OK);
returnedCollection.assertThat().entriesListIsNotEmpty()
.and().entriesListContains("tag", documentTagValue.toLowerCase())
.and().entriesListContains("tag", documentTagValue2.toLowerCase());
.and().entriesListContains("tag", documentTagValue)
.and().entriesListContains("tag", documentTagValue2);
}
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, description = "Verify user with Contributor role gets tags using REST API and status code is OK (200)")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void getTagsWithContributorRole() throws Exception
public void getTagsWithContributorRole()
{
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteContributor));
returnedCollection = restClient.withParams("maxItems=10000").withCoreAPI().getTags();
restClient.assertStatusCodeIs(HttpStatus.OK);
restClient.assertStatusCodeIs(OK);
returnedCollection.assertThat().entriesListIsNotEmpty()
.and().entriesListContains("tag", documentTagValue.toLowerCase())
.and().entriesListContains("tag", documentTagValue2.toLowerCase());
.and().entriesListContains("tag", documentTagValue)
.and().entriesListContains("tag", documentTagValue2);
}
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, description = "Verify user with Consumer role gets tags using REST API and status code is OK (200)")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void getTagsWithConsumerRole() throws Exception
public void getTagsWithConsumerRole()
{
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteConsumer));
returnedCollection = restClient.withParams("maxItems=10000").withCoreAPI().getTags();
restClient.assertStatusCodeIs(HttpStatus.OK);
restClient.assertStatusCodeIs(OK);
returnedCollection.assertThat().entriesListIsNotEmpty()
.and().entriesListContains("tag", documentTagValue.toLowerCase())
.and().entriesListContains("tag", documentTagValue2.toLowerCase());
.and().entriesListContains("tag", documentTagValue)
.and().entriesListContains("tag", documentTagValue2);
}
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.SANITY, description = "Failed authentication get tags call returns status code 401 with Manager role")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.SANITY })
// @Bug(id="MNT-16904", description = "It fails only on environment with tenants")
public void failedAuthenticationReturnsUnauthorizedStatus() throws Exception
public void failedAuthenticationReturnsUnauthorizedStatus()
{
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager));
userModel = dataUser.createRandomTestUser();
@@ -87,7 +89,7 @@ public class GetTagsTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that if maxItems is invalid status code returned is 400")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION})
public void maxItemsInvalidValueTest() throws Exception
public void maxItemsInvalidValueTest()
{
restClient.authenticateUser(adminUserModel).withParams("maxItems=abc").withCoreAPI().getTags();
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST).assertLastError().containsSummary(String.format(RestErrorModel.INVALID_MAXITEMS, "abc"));
@@ -96,7 +98,7 @@ public class GetTagsTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that if skipCount is invalid status code returned is 400")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION})
public void skipCountInvalidValueTest() throws Exception
public void skipCountInvalidValueTest()
{
restClient.authenticateUser(adminUserModel).withParams("skipCount=abc").withCoreAPI().getTags();
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST).assertLastError().containsSummary(String.format(RestErrorModel.INVALID_SKIPCOUNT, "abc"));
@@ -105,55 +107,55 @@ public class GetTagsTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that file tag is retrieved")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION})
public void fileTagIsRetrieved() throws Exception
public void fileTagIsRetrieved()
{
restClient.authenticateUser(adminUserModel);
returnedCollection = restClient.withParams("maxItems=10000").withCoreAPI().getTags();
restClient.assertStatusCodeIs(HttpStatus.OK);
restClient.assertStatusCodeIs(OK);
returnedCollection.assertThat().entriesListIsNotEmpty()
.and().entriesListContains("tag", documentTagValue.toLowerCase())
.and().entriesListContains("tag", documentTagValue2.toLowerCase());
.and().entriesListContains("tag", documentTagValue)
.and().entriesListContains("tag", documentTagValue2);
}
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that folder tag is retrieved")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION})
public void folderTagIsRetrieved() throws Exception
public void folderTagIsRetrieved()
{
restClient.authenticateUser(adminUserModel);
returnedCollection = restClient.withParams("maxItems=10000").withCoreAPI().getTags();
restClient.assertStatusCodeIs(HttpStatus.OK);
restClient.assertStatusCodeIs(OK);
returnedCollection.assertThat().entriesListIsNotEmpty()
.and().entriesListContains("tag", folderTagValue.toLowerCase());
.and().entriesListContains("tag", folderTagValue);
}
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify site Manager is able to get tags using properties parameter."
+ "Check that properties filter is applied.")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void siteManagerIsAbleToRetrieveTagsWithPropertiesParameter() throws Exception
public void siteManagerIsAbleToRetrieveTagsWithPropertiesParameter()
{
returnedCollection = restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager))
.withParams("maxItems=5000&properties=tag").withCoreAPI().getTags();
restClient.assertStatusCodeIs(HttpStatus.OK);
restClient.assertStatusCodeIs(OK);
returnedCollection.assertThat().entriesListIsNotEmpty()
.and().entriesListContains("tag", documentTagValue.toLowerCase())
.and().entriesListContains("tag", documentTagValue2.toLowerCase())
.and().entriesListContains("tag", documentTagValue)
.and().entriesListContains("tag", documentTagValue2)
.and().entriesListDoesNotContain("id");
}
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "With admin get tags and use skipCount parameter. Check pagination")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void useSkipCountCheckPagination() throws Exception
public void useSkipCountCheckPagination()
{
returnedCollection = restClient.authenticateUser(adminUserModel).withCoreAPI().getTags();
restClient.assertStatusCodeIs(HttpStatus.OK);
restClient.assertStatusCodeIs(OK);
RestTagModel firstTag = returnedCollection.getEntries().get(0).onModel();
RestTagModel secondTag = returnedCollection.getEntries().get(1).onModel();
RestTagModelsCollection tagsWithSkipCount = restClient.withParams("skipCount=2").withCoreAPI().getTags();
restClient.assertStatusCodeIs(HttpStatus.OK);
restClient.assertStatusCodeIs(OK);
tagsWithSkipCount.assertThat().entriesListDoesNotContain("tag", firstTag.getTag())
.assertThat().entriesListDoesNotContain("tag", secondTag.getTag());
@@ -163,15 +165,15 @@ public class GetTagsTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "With admin get tags and use maxItems parameter. Check pagination")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void useMaxItemsParameterCheckPagination() throws Exception
public void useMaxItemsParameterCheckPagination()
{
returnedCollection = restClient.authenticateUser(adminUserModel).withCoreAPI().getTags();
restClient.assertStatusCodeIs(HttpStatus.OK);
restClient.assertStatusCodeIs(OK);
RestTagModel firstTag = returnedCollection.getEntries().get(0).onModel();
RestTagModel secondTag = returnedCollection.getEntries().get(1).onModel();
RestTagModelsCollection tagsWithMaxItems = restClient.withParams("maxItems=2").withCoreAPI().getTags();
restClient.assertStatusCodeIs(HttpStatus.OK);
restClient.assertStatusCodeIs(OK);
tagsWithMaxItems.assertThat().entriesListContains("tag", firstTag.getTag())
.assertThat().entriesListContains("tag", secondTag.getTag())
@@ -182,23 +184,23 @@ public class GetTagsTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "With manager get tags and use high skipCount parameter. Check pagination")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void useHighSkipCountCheckPagination() throws Exception
public void useHighSkipCountCheckPagination()
{
returnedCollection = restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager))
.withParams("skipCount=20000").withCoreAPI().getTags();
restClient.assertStatusCodeIs(HttpStatus.OK);
restClient.assertStatusCodeIs(OK);
returnedCollection.assertThat().entriesListIsEmpty()
.getPagination().assertThat().field("maxItems").is(100)
.and().field("hasMoreItems").is("false")
.and().field("count").is("0")
.and().field("skipCount").is(20000)
.and().field("totalItems").isNull();
.and().field("totalItems").is(0);
}
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "With Collaborator user get tags and use maxItems with value zero. Check default error model schema")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void useMaxItemsWithValueZeroCheckDefaultErrorModelSchema() throws Exception
public void useMaxItemsWithValueZeroCheckDefaultErrorModelSchema()
{
returnedCollection = restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteCollaborator))
.withParams("maxItems=0").withCoreAPI().getTags();
@@ -212,18 +214,167 @@ public class GetTagsTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "With Manager user delete tag. Check it is not retrieved anymore.")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void checkThatDeletedTagIsNotRetrievedAnymore() throws Exception
public void checkThatDeletedTagIsNotRetrievedAnymore()
{
String removedTag = RandomData.getRandomName("tag3");
String removedTag = getRandomName("tag3");
RestTagModel deletedTag = restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager))
.withCoreAPI().usingResource(document).addTag(removedTag);
restClient.withCoreAPI().usingResource(document).deleteTag(deletedTag);
restClient.authenticateUser(adminUserModel).withCoreAPI().usingTag(deletedTag).deleteTag();
restClient.assertStatusCodeIs(HttpStatus.NO_CONTENT);
returnedCollection = restClient.withParams("maxItems=10000").withCoreAPI().getTags();
returnedCollection.assertThat().entriesListIsNotEmpty()
.and().entriesListDoesNotContain("tag", removedTag.toLowerCase());
.and().entriesListDoesNotContain("tag", removedTag);
}
/**
* Verify if exact name filter can be applied.
*/
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void testGetTags_withSingleNameFilter()
{
STEP("Get tags with names filter using EQUALS and expect one item in result");
returnedCollection = restClient.authenticateUser(adminUserModel)
.withParams("where=(tag='" + documentTag.getTag() + "')")
.withCoreAPI()
.getTags();
restClient.assertStatusCodeIs(HttpStatus.OK);
returnedCollection.assertThat()
.entrySetMatches("tag", Set.of(documentTagValue));
}
/**
* Verify if multiple names can be applied as a filter.
*/
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void testGetTags_withTwoNameFilters()
{
STEP("Get tags with names filter using IN and expect two items in result");
returnedCollection = restClient.authenticateUser(adminUserModel)
.withParams("where=(tag IN ('" + documentTag.getTag() + "', '" + folderTag.getTag() + "'))")
.withCoreAPI()
.getTags();
restClient.assertStatusCodeIs(HttpStatus.OK);
returnedCollection.assertThat()
.entrySetMatches("tag", Set.of(documentTagValue, folderTagValue));
}
/**
* Verify if alike name filter can be applied.
*/
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void testGetTags_whichNamesStartsWithOrphan()
{
STEP("Get tags with names filter using MATCHES and expect one item in result");
returnedCollection = restClient.authenticateUser(adminUserModel)
.withParams("where=(tag MATCHES ('orphan*'))", "maxItems=10000")
.withCoreAPI()
.getTags();
restClient.assertStatusCodeIs(HttpStatus.OK);
returnedCollection.assertThat()
.entrySetContains("tag", orphanTag.getTag());
}
/**
* Verify that tags can be filtered by exact name and alike name at the same time.
*/
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void testGetTags_withExactNameAndAlikeFilters()
{
STEP("Get tags with names filter using EQUALS and MATCHES and expect four items in result");
returnedCollection = restClient.authenticateUser(adminUserModel)
.withParams("where=(tag MATCHES ('*tag*'))", "maxItems=10000")
.withCoreAPI()
.getTags();
restClient.assertStatusCodeIs(HttpStatus.OK);
returnedCollection.assertThat()
.entrySetContains("tag", documentTagValue, documentTagValue2, folderTagValue, orphanTag.getTag());
}
/**
* Verify if multiple alike filters can be applied.
*/
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void testGetTags_withTwoAlikeFilters()
{
STEP("Get tags applying names filter using MATCHES twice and expect four items in result");
returnedCollection = restClient.authenticateUser(adminUserModel)
.withParams("where=(tag MATCHES ('orphan*') OR tag MATCHES ('tag*'))", "maxItems=10000")
.withCoreAPI()
.getTags();
restClient.assertStatusCodeIs(HttpStatus.OK);
returnedCollection.assertThat()
.entrySetContains("tag", documentTagValue, documentTagValue2, folderTagValue, orphanTag.getTag());
}
/**
* Verify that providing incorrect field name in where query will result with 400 (Bad Request).
*/
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void testGetTags_withWrongWherePropertyNameAndExpect400()
{
STEP("Try to get tags with names filter using EQUALS and wrong property name and expect 400");
returnedCollection = restClient.authenticateUser(adminUserModel)
.withParams("where=(name=gat)")
.withCoreAPI()
.getTags();
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST)
.assertLastError().containsSummary("Where query error: property with name: name is not expected");
}
/**
* Verify tht AND operator is not supported in where query and expect 400 (Bad Request).
*/
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void testGetTags_queryAndOperatorNotSupported()
{
STEP("Try to get tags applying names filter using AND operator and expect 400");
returnedCollection = restClient.authenticateUser(adminUserModel)
.withParams("where=(name=tag AND name IN ('tag-', 'gat'))")
.withCoreAPI()
.getTags();
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST)
.assertLastError().containsSummary("An invalid WHERE query was received. Unsupported Predicate");
}
/**
* Verify if count field is present for searched tags.
*/
@Test(groups = {TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION})
public void testGetTags_includingCount()
{
STEP("Get tags including count and verify if it is present in the response");
final RestTagModelsCollection searchedTags = restClient.withCoreAPI().include(FIELD_COUNT).getTags();
restClient.assertStatusCodeIs(OK);
searchedTags.assertThat().entriesListIsNotEmpty()
.assertThat().entriesListContains(FIELD_COUNT)
.assertThat().entriesListContains(FIELD_TAG)
.assertThat().entriesListContains(FIELD_ID);
}
/**
* Verify if count field is not present for searched tags.
*/
@Test(groups = {TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION})
public void testGetTags_notIncludingCount()
{
STEP("Get tags, not including count and verify if it is not in the response");
final RestTagModelsCollection searchedTags = restClient.withCoreAPI().getTags();
restClient.assertStatusCodeIs(OK);
searchedTags.assertThat().entriesListIsNotEmpty()
.assertThat().entriesListDoesNotContain(FIELD_COUNT)
.assertThat().entriesListContains(FIELD_TAG)
.assertThat().entriesListContains(FIELD_ID);
}
}

View File

@@ -1,6 +1,5 @@
package org.alfresco.rest.tags;
import java.util.Date;
import org.alfresco.dataprep.CMISUtil;
import org.alfresco.rest.RestTest;
import org.alfresco.rest.model.RestTagModel;
@@ -12,12 +11,9 @@ import org.alfresco.utility.data.RandomData;
import org.alfresco.utility.model.FileModel;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.SiteModel;
import org.alfresco.utility.model.TestGroup;
import org.alfresco.utility.model.UserModel;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@Test(groups = {TestGroup.REQUIRE_SOLR})
public class TagsDataPrep extends RestTest
{
@@ -28,36 +24,64 @@ public class TagsDataPrep extends RestTest
protected static FileModel document;
protected static FolderModel folder;
protected static String documentTagValue, documentTagValue2, folderTagValue;
protected static RestTagModel documentTag, documentTag2, folderTag, returnedModel;
protected static RestTagModel documentTag, documentTag2, folderTag, orphanTag, returnedModel;
protected static RestTagModelsCollection returnedCollection;
@BeforeClass
public void init() throws Exception
{
//Create users
adminUserModel = dataUser.getAdminUser();
userModel = dataUser.createRandomTestUser();
//Create public site
siteModel = dataSite.usingUser(adminUserModel).createPublicRandomSite();
usersWithRoles = dataUser.usingAdmin().addUsersWithRolesToSite(siteModel, UserRole.SiteManager, UserRole.SiteCollaborator, UserRole.SiteConsumer, UserRole.SiteContributor);
document = dataContent.usingUser(adminUserModel).usingSite(siteModel).createContent(CMISUtil.DocumentType.TEXT_PLAIN);
folder = dataContent.usingUser(adminUserModel).usingSite(siteModel).createFolder();
documentTagValue = RandomData.getRandomName("tag");
documentTagValue2 = RandomData.getRandomName("tag");
folderTagValue = RandomData.getRandomName("tag");
documentTagValue = RandomData.getRandomName("tag").toLowerCase();
documentTagValue2 = RandomData.getRandomName("tag").toLowerCase();
folderTagValue = RandomData.getRandomName("tag").toLowerCase();
restClient.authenticateUser(adminUserModel);
documentTag = restClient.withCoreAPI().usingResource(document).addTag(documentTagValue);
documentTag2 = restClient.withCoreAPI().usingResource(document).addTag(documentTagValue2);
folderTag = restClient.withCoreAPI().usingResource(folder).addTag(folderTagValue);
orphanTag = restClient.withCoreAPI().createSingleTag(RestTagModel.builder().tag(RandomData.getRandomName("orphan-tag").toLowerCase()).create());
// Allow indexing to complete.
Utility.sleep(500, 60000, () ->
{
returnedCollection = restClient.withParams("maxItems=10000").withCoreAPI().getTags();
returnedCollection.assertThat().entriesListContains("tag", documentTagValue.toLowerCase())
.and().entriesListContains("tag", documentTagValue2.toLowerCase())
.and().entriesListContains("tag", folderTagValue.toLowerCase());
});
{
returnedCollection = restClient.withParams("maxItems=10000", "where=(tag MATCHES ('*tag*'))")
.withCoreAPI().getTags();
returnedCollection.assertThat().entriesListContains("tag", documentTagValue)
.and().entriesListContains("tag", documentTagValue2)
.and().entriesListContains("tag", folderTagValue)
.and().entriesListContains("tag", orphanTag.getTag());
});
}
protected RestTagModel createTagForDocument(FileModel document)
{
String documentTagValue = RandomData.getRandomName("tag");
return restClient.withCoreAPI().usingResource(document).addTag(documentTagValue);
}
protected RestTagModel createTagModelWithId(final String id)
{
return createTagModelWithIdAndName(id, RandomData.getRandomName("tag"));
}
protected RestTagModel createTagModelWithIdAndName(final String id, final String tag)
{
return RestTagModel.builder()
.id(id)
.tag(tag)
.create();
}
protected RestTagModel createTagModelWithName(final String tagName)
{
return RestTagModel.builder().tag(tagName).create();
}
}

View File

@@ -1,5 +1,8 @@
package org.alfresco.rest.tags;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.testng.Assert.fail;
import org.alfresco.rest.model.RestErrorModel;
import org.alfresco.rest.model.RestTagModel;
import org.alfresco.utility.Utility;
@@ -13,6 +16,7 @@ import org.alfresco.utility.testrail.annotation.TestRail;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.http.HttpStatus;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Ignore;
import org.testng.annotations.Test;
/**
@@ -25,28 +29,29 @@ public class UpdateTagTests extends TagsDataPrep
private String randomTag = "";
@BeforeMethod(alwaysRun=true)
public void addTagToDocument() throws Exception
public void addTagToDocument()
{
restClient.authenticateUser(adminUserModel);
oldTag = restClient.withCoreAPI().usingResource(document).addTag(RandomData.getRandomName("old"));
randomTag = RandomData.getRandomName("tag");
oldTag = restClient.withCoreAPI().usingResource(document).addTag(RandomData.getRandomName("old").toLowerCase());
randomTag = RandomData.getRandomName("tag").toLowerCase();
}
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.SANITY, description = "Verify Admin user updates tags and status code is 200")
@Bug(id="REPO-1828")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.SANITY })
public void adminIsAbleToUpdateTags() throws Exception
public void adminIsAbleToUpdateTags()
{
restClient.authenticateUser(adminUserModel);
returnedModel = restClient.withCoreAPI().usingTag(oldTag).update(randomTag);
restClient.assertStatusCodeIs(HttpStatus.OK);
returnedModel.assertThat().field("tag").is(randomTag);
returnedModel.assertThat().field("id").isNotNull();
}
@TestRail(section = { TestGroup.REST_API,
TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, description = "Verify Manager user can't update tags with Rest API and status code is 403")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void managerIsNotAbleToUpdateTag() throws Exception
public void managerIsNotAbleToUpdateTag()
{
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager));
restClient.withCoreAPI().usingTag(oldTag).update(randomTag);
@@ -56,7 +61,7 @@ public class UpdateTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS },
executionType = ExecutionType.REGRESSION, description = "Verify Collaborator user can't update tags with Rest API and status code is 403")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void collaboratorIsNotAbleToUpdateTagCheckDefaultErrorModelSchema() throws Exception
public void collaboratorIsNotAbleToUpdateTagCheckDefaultErrorModelSchema()
{
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteCollaborator));
restClient.withCoreAPI().usingTag(oldTag).update(randomTag);
@@ -69,7 +74,7 @@ public class UpdateTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS },
executionType = ExecutionType.REGRESSION, description = "Verify Contributor user can't update tags with Rest API and status code is 403")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void contributorIsNotAbleToUpdateTag() throws Exception
public void contributorIsNotAbleToUpdateTag()
{
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteContributor));
restClient.withCoreAPI().usingTag(oldTag).update(randomTag);
@@ -79,7 +84,7 @@ public class UpdateTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS },
executionType = ExecutionType.SANITY, description = "Verify Consumer user can't update tags with Rest API and status code is 403")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void consumerIsNotAbleToUpdateTag() throws Exception
public void consumerIsNotAbleToUpdateTag()
{
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteConsumer));
restClient.withCoreAPI().usingTag(oldTag).update(randomTag);
@@ -90,7 +95,7 @@ public class UpdateTagTests extends TagsDataPrep
executionType = ExecutionType.SANITY, description = "Verify user gets status code 401 if authentication call fails")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.SANITY })
// @Bug(id="MNT-16904", description = "It fails only on environment with tenants")
public void userIsNotAbleToUpdateTagIfAuthenticationFails() throws Exception
public void userIsNotAbleToUpdateTagIfAuthenticationFails()
{
UserModel siteManager = usersWithRoles.getOneUserWithRole(UserRole.SiteManager);
String managerPassword = siteManager.getPassword();
@@ -103,118 +108,140 @@ public class UpdateTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, description = "Verify admin is not able to update tag with invalid id")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void adminIsNotAbleToUpdateTagWithInvalidId() throws Exception
public void adminIsNotAbleToUpdateTagWithInvalidId()
{
String invalidTagId = "invalid-id";
RestTagModel tag = restClient.authenticateUser(adminUserModel).withCoreAPI().usingResource(document).addTag(RandomData.getRandomName("tag"));
RestTagModel tag = restClient.authenticateUser(adminUserModel).withCoreAPI().usingResource(document).addTag(RandomData.getRandomName("tag").toLowerCase());
tag.setId(invalidTagId);
restClient.withCoreAPI().usingTag(tag).update(RandomData.getRandomName("tag"));
restClient.withCoreAPI().usingTag(tag).update(RandomData.getRandomName("tag").toLowerCase());
restClient.assertStatusCodeIs(HttpStatus.NOT_FOUND)
.assertLastError().containsSummary(String.format(RestErrorModel.ENTITY_NOT_FOUND, invalidTagId));
}
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, description = "Verify admin is not able to update tag with empty id")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void adminIsNotAbleToUpdateTagWithEmptyId() throws Exception
public void adminIsNotAbleToUpdateTagWithEmptyId()
{
RestTagModel tag = restClient.authenticateUser(adminUserModel).withCoreAPI().usingResource(document).addTag(RandomData.getRandomName("tag"));
RestTagModel tag = restClient.authenticateUser(adminUserModel).withCoreAPI().usingResource(document).addTag(RandomData.getRandomName("tag").toLowerCase());
tag.setId("");
restClient.withCoreAPI().usingTag(tag).update(RandomData.getRandomName("tag"));
restClient.withCoreAPI().usingTag(tag).update(RandomData.getRandomName("tag").toLowerCase());
restClient.assertStatusCodeIs(HttpStatus.METHOD_NOT_ALLOWED)
.assertLastError().containsSummary(RestErrorModel.PUT_EMPTY_ARGUMENT);
}
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, description = "Verify admin is not able to update tag with invalid body")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void adminIsNotAbleToUpdateTagWithEmptyBody() throws Exception
public void adminIsNotAbleToUpdateTagWithEmptyBody()
{
RestTagModel tag = restClient.authenticateUser(adminUserModel).withCoreAPI().usingResource(document).addTag(RandomData.getRandomName("tag"));
RestTagModel tag = restClient.authenticateUser(adminUserModel).withCoreAPI().usingResource(document).addTag(RandomData.getRandomName("tag").toLowerCase());
restClient.withCoreAPI().usingTag(tag).update("");
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST)
.assertLastError().containsSummary(RestErrorModel.EMPTY_TAG);
.assertLastError().containsSummary(RestErrorModel.BLANK_TAG);
}
@Bug(id="ACE-5629")
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify admin is not able to update tag with invalid body containing '|' symbol")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void adminIsNotAbleToUpdateTagWithInvalidBodyScenario1() throws Exception
@Ignore
public void adminIsNotAbleToUpdateTagWithInvalidBodyScenario1()
{
String invalidTagBody = "|.\"/<>*";
RestTagModel tag = restClient.authenticateUser(adminUserModel).withCoreAPI().usingResource(document).addTag(RandomData.getRandomName("tag"));
Utility.sleep(500, 20000, () ->
RestTagModel tag = restClient.authenticateUser(adminUserModel).withCoreAPI().usingResource(document).addTag(RandomData.getRandomName("tag").toLowerCase());
try
{
restClient.withCoreAPI().usingTag(tag).update(invalidTagBody);
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST)
.assertLastError().containsSummary(String.format(RestErrorModel.INVALID_TAG, invalidTagBody));
});
Utility.sleep(500, 20000, () ->
{
restClient.withCoreAPI().usingTag(tag).update(invalidTagBody);
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST)
.assertLastError().containsSummary(String.format(RestErrorModel.INVALID_TAG, invalidTagBody));
});
}
catch (InterruptedException e)
{
fail("Test interrupted while waiting for error status code.");
}
}
@Bug(id="ACE-5629")
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify admin is not able to update tag with invalid body without '|' symbol")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void adminIsNotAbleToUpdateTagWithInvalidBodyScenario2() throws Exception
@Ignore
public void adminIsNotAbleToUpdateTagWithInvalidBodyScenario2()
{
String invalidTagBody = ".\"/<>*";
RestTagModel tag = restClient.authenticateUser(adminUserModel).withCoreAPI().usingResource(document).addTag(RandomData.getRandomName("tag"));
try
{
Utility.sleep(500, 20000, () ->
{
restClient.withCoreAPI().usingTag(tag).update(invalidTagBody);
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST)
restClient.withCoreAPI().usingTag(tag).update(invalidTagBody);
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST)
.assertLastError().containsSummary(String.format(RestErrorModel.INVALID_TAG, invalidTagBody));
});
});
}
catch (InterruptedException e)
{
fail("Test interrupted while waiting for error status code.");
}
}
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify admin user can provide large string for new tag value.")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
@Bug(id="REPO-1828")
public void adminIsAbleToUpdateTagsProvideLargeStringTag() throws Exception
@Ignore
public void adminIsAbleToUpdateTagsProvideLargeStringTag()
{
String largeStringTag = RandomStringUtils.randomAlphanumeric(10000);
String largeStringTag = RandomStringUtils.randomAlphanumeric(10000).toLowerCase();
restClient.authenticateUser(adminUserModel);
returnedModel = restClient.withCoreAPI().usingTag(oldTag).update(largeStringTag);
restClient.assertStatusCodeIs(HttpStatus.OK);
returnedModel.assertThat().field("tag").is(largeStringTag);
returnedModel.assertThat().field("id").isNotNull();
}
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify admin user can provide short string for new tag value.")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
@Bug(id="REPO-1828")
public void adminIsAbleToUpdateTagsProvideShortStringTag() throws Exception
public void adminIsAbleToUpdateTagsProvideShortStringTag()
{
String shortStringTag = RandomStringUtils.randomAlphanumeric(2);
String shortStringTag = RandomStringUtils.randomAlphanumeric(2).toLowerCase();
restClient.authenticateUser(adminUserModel);
returnedModel = restClient.withCoreAPI().usingTag(oldTag).update(shortStringTag);
restClient.assertStatusCodeIs(HttpStatus.OK);
returnedModel.assertThat().field("tag").is(shortStringTag);
returnedModel.assertThat().field("id").isNotNull();
}
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify admin user can provide string with special chars for new tag value.")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
@Bug(id="REPO-1828")
public void adminIsAbleToUpdateTagsProvideSpecialCharsStringTag() throws Exception
@Ignore
public void adminIsAbleToUpdateTagsProvideSpecialCharsStringTag()
{
String specialCharsString = "!@#$%^&*()'\".,<>-_+=|\\";
String specialCharsString = RandomData.getRandomName("!@#$%^&*()'\".,<>-_+=|\\").toLowerCase();
restClient.authenticateUser(adminUserModel);
returnedModel = restClient.withCoreAPI().usingTag(oldTag).update(specialCharsString);
restClient.assertStatusCodeIs(HttpStatus.OK);
returnedModel.assertThat().field("tag").is(specialCharsString);
returnedModel.assertThat().field("id").isNotNull();
}
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify Admin user can provide existing tag for new tag value.")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
@Bug(id="REPO-1828")
public void adminIsAbleToUpdateTagsProvideExistingTag() throws Exception
@Ignore
public void adminIsAbleToUpdateTagsProvideExistingTag()
{
String existingTag = "oldTag";
String existingTag = RandomData.getRandomName("oldTag").toLowerCase();
RestTagModel oldExistingTag = restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager))
.withCoreAPI().usingResource(document).addTag(existingTag);
restClient.assertStatusCodeIs(HttpStatus.CREATED);
@@ -223,38 +250,43 @@ public class UpdateTagTests extends TagsDataPrep
returnedModel = restClient.withCoreAPI().usingTag(oldExistingTag).update(existingTag);
restClient.assertStatusCodeIs(HttpStatus.OK);
returnedModel.assertThat().field("tag").is(existingTag);
returnedModel.assertThat().field("id").isNotNull();
}
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify Admin user can delete a tag, add tag and update it.")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
@Bug(id="REPO-1828")
public void adminDeleteTagAddTagUpdateTag() throws Exception
@Ignore
public void adminDeleteTagAddTagUpdateTag()
{
restClient.authenticateUser(adminUserModel)
.withCoreAPI().usingResource(document).deleteTag(oldTag);
restClient.assertStatusCodeIs(HttpStatus.NO_CONTENT);
String newTag = "addTag";
String newTag = RandomData.getRandomName("addTag").toLowerCase();
RestTagModel newTagModel = restClient.withCoreAPI().usingResource(document).addTag(newTag);
restClient.assertStatusCodeIs(HttpStatus.CREATED);
returnedModel = restClient.withCoreAPI().usingTag(newTagModel).update(newTag);
restClient.assertStatusCodeIs(HttpStatus.OK);
returnedModel.assertThat().field("tag").is(newTag);
returnedModel.assertThat().field("id").isNotNull();
}
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify Admin user can update a tag, delete tag and add it.")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
@Bug(id="REPO-1828")
public void adminUpdateTagDeleteTagAddTag() throws Exception
@Ignore
public void adminUpdateTagDeleteTagAddTag()
{
String newTag = "addTag";
String newTag = RandomData.getRandomName("addTag").toLowerCase();
returnedModel = restClient.authenticateUser(adminUserModel).withCoreAPI().usingTag(oldTag).update(newTag);
restClient.assertStatusCodeIs(HttpStatus.OK);
returnedModel.assertThat().field("tag").is(newTag);
returnedModel.assertThat().field("id").isNotNull();
restClient.withCoreAPI().usingResource(document).deleteTag(returnedModel);
restClient.assertStatusCodeIs(HttpStatus.NO_CONTENT);
@@ -262,4 +294,18 @@ public class UpdateTagTests extends TagsDataPrep
restClient.withCoreAPI().usingResource(document).addTag(newTag);
restClient.assertStatusCodeIs(HttpStatus.CREATED);
}
}
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.SANITY,
description = "Verify Admin user updates orphan tags and status code is 200")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.SANITY })
public void adminIsAbleToUpdateOrphanTag()
{
STEP("Update orphan tag and expect 200");
final String newTagName = RandomData.getRandomName("new").toLowerCase();
returnedModel = restClient.authenticateUser(adminUserModel).withCoreAPI().usingTag(orphanTag).update(newTagName);
restClient.assertStatusCodeIs(HttpStatus.OK);
returnedModel.assertThat().field("tag").is(newTagName);
returnedModel.assertThat().field("id").isNotNull();
}
}

View File

@@ -33,13 +33,13 @@ public class AddTagTests extends TagsDataPrep
@BeforeMethod(alwaysRun = true)
public void generateRandomTag()
{
tagValue = RandomData.getRandomName("tag");
tagValue = RandomData.getRandomName("tag").toLowerCase();
}
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify admin user adds tags with Rest API and status code is 201")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void adminIsAbleToAddTag() throws Exception
public void adminIsAbleToAddTag()
{
restClient.authenticateUser(adminUserModel);
returnedModel = restClient.withCoreAPI().usingResource(document).addTag(tagValue);
@@ -51,7 +51,7 @@ public class AddTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.SANITY,
description = "Verify Manager user adds tags with Rest API and status code is 201")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.SANITY })
public void managerIsAbleToTagAFile() throws Exception
public void managerIsAbleToTagAFile()
{
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager));
returnedModel = restClient.withCoreAPI().usingResource(document).addTag(tagValue);
@@ -63,7 +63,7 @@ public class AddTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify Collaborator user adds tags with Rest API and status code is 201")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void collaboratorIsAbleToTagAFile() throws Exception
public void collaboratorIsAbleToTagAFile()
{
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteCollaborator));
returnedModel = restClient.withCoreAPI().usingResource(document).addTag(tagValue);
@@ -75,7 +75,7 @@ public class AddTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify Contributor user doesn't have permission to add tags with Rest API and status code is 403")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void contributorIsNotAbleToAddTagToAnotherContent() throws Exception
public void contributorIsNotAbleToAddTagToAnotherContent()
{
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteContributor));
restClient.withCoreAPI().usingResource(document).addTag(tagValue);
@@ -85,7 +85,7 @@ public class AddTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify Contributor user adds tags to his content with Rest API and status code is 201")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void contributorIsAbleToAddTagToHisContent() throws Exception
public void contributorIsAbleToAddTagToHisContent()
{
userModel = usersWithRoles.getOneUserWithRole(UserRole.SiteContributor);
restClient.authenticateUser(userModel);
@@ -99,7 +99,7 @@ public class AddTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify Consumer user doesn't have permission to add tags with Rest API and status code is 403")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void consumerIsNotAbleToTagAFile() throws Exception
public void consumerIsNotAbleToTagAFile()
{
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteConsumer));
restClient.withCoreAPI().usingResource(document).addTag(tagValue);
@@ -110,7 +110,7 @@ public class AddTagTests extends TagsDataPrep
description = "Verify user gets status code 401 if authentication call fails")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.SANITY })
// @Bug(id="MNT-16904", description = "It fails only on environment with tenants")
public void userIsNotAbleToAddTagIfAuthenticationFails() throws Exception
public void userIsNotAbleToAddTagIfAuthenticationFails()
{
UserModel siteManager = usersWithRoles.getOneUserWithRole(UserRole.SiteManager);
String managerPassword = siteManager.getPassword();
@@ -124,7 +124,7 @@ public class AddTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that adding empty tag returns status code 400")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void emptyTagTest() throws Exception
public void emptyTagTest()
{
restClient.authenticateUser(adminUserModel);
returnedModel = restClient.withCoreAPI().usingResource(document).addTag("");
@@ -134,7 +134,7 @@ public class AddTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that adding tag with user that has no permissions returns status code 403")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void addTagWithUserThatDoesNotHavePermissions() throws Exception
public void addTagWithUserThatDoesNotHavePermissions()
{
restClient.authenticateUser(dataUser.createRandomTestUser());
returnedModel = restClient.withCoreAPI().usingResource(document).addTag(tagValue);
@@ -144,7 +144,7 @@ public class AddTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that adding tag to a node that does not exist returns status code 404")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void addTagToInexistentNode() throws Exception
public void addTagToInexistentNode()
{
String oldNodeRef = document.getNodeRef();
String nodeRef = RandomStringUtils.randomAlphanumeric(10);
@@ -159,7 +159,7 @@ public class AddTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.SANITY,
description = "Verify that manager is able to tag a folder")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.SANITY })
public void managerIsAbleToTagAFolder() throws Exception
public void managerIsAbleToTagAFolder()
{
FolderModel folderModel = dataContent.usingUser(adminUserModel).usingSite(siteModel).createFolder();
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager));
@@ -171,7 +171,7 @@ public class AddTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that tagged file can be tagged again")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void addTagToATaggedFile() throws Exception
public void addTagToATaggedFile()
{
restClient.authenticateUser(adminUserModel);
@@ -195,7 +195,7 @@ public class AddTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that user cannot add invalid tag")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void addInvalidTag() throws Exception
public void addInvalidTag()
{
restClient.authenticateUser(adminUserModel);
returnedModel = restClient.withCoreAPI().usingResource(document).addTag("-1~!|@#$%^&*()_=");
@@ -205,7 +205,7 @@ public class AddTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that contributor is able to tag a folder created by self")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void contributorIsAbleToTagAFolderCreatedBySelf() throws Exception
public void contributorIsAbleToTagAFolderCreatedBySelf()
{
FolderModel folderModel = dataContent.usingUser(usersWithRoles.getOneUserWithRole(UserRole.SiteContributor)).usingSite(siteModel).createFolder();
@@ -218,7 +218,7 @@ public class AddTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that collaborator is able to tag a folder")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void collaboratorIsAbleToTagAFolder() throws Exception
public void collaboratorIsAbleToTagAFolder()
{
FolderModel folderModel = dataContent.usingUser(adminUserModel).usingSite(siteModel).createFolder();
@@ -231,7 +231,7 @@ public class AddTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that consumer is not able to tag a folder. Check default error model schema.")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void consumerIsNotAbleToTagAFolder() throws Exception
public void consumerIsNotAbleToTagAFolder()
{
FolderModel folderModel = dataContent.usingUser(adminUserModel).usingSite(siteModel).createFolder();
@@ -246,7 +246,7 @@ public class AddTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that tagged folder can be tagged again")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void addTagToATaggedFolder() throws Exception
public void addTagToATaggedFolder()
{
FolderModel folderModel = dataContent.usingUser(adminUserModel).usingSite(siteModel).createFolder();
@@ -269,11 +269,11 @@ public class AddTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Using collaborator provide more than one tag element")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void provideMoreThanOneTagElement() throws Exception
public void provideMoreThanOneTagElement()
{
FolderModel folderModel = dataContent.usingUser(adminUserModel).usingSite(siteModel).createFolder();
String tagValue1 = RandomData.getRandomName("tag1");
String tagValue2 = RandomData.getRandomName("tag2");
String tagValue1 = RandomData.getRandomName("tag1").toLowerCase();
String tagValue2 = RandomData.getRandomName("tag2").toLowerCase();
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteCollaborator));
@@ -288,7 +288,7 @@ public class AddTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that manager cannot add tag with special characters.")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void addTagWithSpecialCharacters() throws Exception
public void addTagWithSpecialCharacters()
{
FolderModel folderModel = dataContent.usingUser(adminUserModel).usingSite(siteModel).createFolder();
String specialCharsTag = "!@#$%^&*()'\".,<>-_+=|\\";
@@ -301,7 +301,7 @@ public class AddTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that you cannot tag a comment and it returns status code 405")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void addTagToAComment() throws Exception
public void addTagToAComment()
{
FileModel file = dataContent.usingSite(siteModel).usingUser(adminUserModel).createContent(CMISUtil.DocumentType.TEXT_PLAIN);
String comment = "comment for a tag";
@@ -316,7 +316,7 @@ public class AddTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that you cannot tag a tag and it returns status code 405")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void addTagToATag() throws Exception
public void addTagToATag()
{
FileModel file = dataContent.usingSite(siteModel).usingUser(adminUserModel).createContent(CMISUtil.DocumentType.TEXT_PLAIN);
@@ -326,4 +326,17 @@ public class AddTagTests extends TagsDataPrep
restClient.withCoreAPI().usingResource(file).addTag(tagValue);
restClient.assertStatusCodeIs(HttpStatus.METHOD_NOT_ALLOWED).assertLastError().containsSummary(RestErrorModel.CANNOT_TAG);
}
}
@TestRail (section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify the tag name is converted to lower case before being applied")
@Test (groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void tagNameIsConvertedToLowerCase()
{
restClient.authenticateUser(adminUserModel);
String tagName = RandomData.getRandomName("TaG-oNe");
returnedModel = restClient.withCoreAPI().usingResource(document).addTag(tagName);
restClient.assertStatusCodeIs(HttpStatus.CREATED);
returnedModel.assertThat().field("tag").is(tagName.toLowerCase())
.and().field("id").isNotEmpty();
}
}

View File

@@ -30,14 +30,14 @@ public class AddTagsTests extends TagsDataPrep
@BeforeMethod(alwaysRun = true)
public void generateRandomTagsList()
{
tag1 = RandomData.getRandomName("tag");
tag2 = RandomData.getRandomName("tag");
tag1 = RandomData.getRandomName("tag").toLowerCase();
tag2 = RandomData.getRandomName("tag").toLowerCase();
}
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify admin user adds multiple tags with Rest API and status code is 201")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void adminIsAbleToAddTags() throws Exception
public void adminIsAbleToAddTags()
{
restClient.authenticateUser(adminUserModel);
returnedCollection = restClient.withCoreAPI().usingResource(document).addTags(tag1, tag2);
@@ -49,7 +49,7 @@ public class AddTagsTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.SANITY,
description = "Verify Manager user adds multiple tags with Rest API and status code is 201")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.SANITY })
public void managerIsAbleToAddTags() throws Exception
public void managerIsAbleToAddTags()
{
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager));
returnedCollection = restClient.withCoreAPI().usingResource(document).addTags(tag1, tag2);
@@ -61,7 +61,7 @@ public class AddTagsTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS },
executionType = ExecutionType.SANITY, description = "Verify Collaborator user adds multiple tags with Rest API and status code is 201")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void collaboratorIsAbleToAddTags() throws Exception
public void collaboratorIsAbleToAddTags()
{
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteCollaborator));
returnedCollection = restClient.withCoreAPI().usingResource(document).addTags(tag1, tag2);
@@ -73,7 +73,7 @@ public class AddTagsTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify Contributor user doesn't have permission to add multiple tags with Rest API and status code is 403")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void contributorIsNotAbleToAddTagsToAnotherContent() throws Exception
public void contributorIsNotAbleToAddTagsToAnotherContent()
{
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteContributor));
restClient.withCoreAPI().usingResource(document).addTags(tag1, tag2);
@@ -83,7 +83,7 @@ public class AddTagsTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify Contributor user adds multiple tags to his content with Rest API and status code is 201")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void contributorIsAbleToAddTagsToHisContent() throws Exception
public void contributorIsAbleToAddTagsToHisContent()
{
userModel = usersWithRoles.getOneUserWithRole(UserRole.SiteContributor);
restClient.authenticateUser(userModel);
@@ -97,7 +97,7 @@ public class AddTagsTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify Consumer user doesn't have permission to add multiple tags with Rest API and status code is 403")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void consumerIsNotAbleToAddTags() throws Exception
public void consumerIsNotAbleToAddTags()
{
restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteConsumer)).withCoreAPI().usingResource(document).addTags(tag1, tag2);
restClient.assertStatusCodeIs(HttpStatus.FORBIDDEN).assertLastError().containsSummary(RestErrorModel.PERMISSION_WAS_DENIED);
@@ -107,7 +107,7 @@ public class AddTagsTests extends TagsDataPrep
description = "Verify user gets status code 401 if authentication call fails")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.SANITY })
// @Bug(id="MNT-16904", description = "It fails only on environment with tenants")
public void userIsNotAbleToAddTagsIfAuthenticationFails() throws Exception
public void userIsNotAbleToAddTagsIfAuthenticationFails()
{
UserModel siteManager = usersWithRoles.getOneUserWithRole(UserRole.SiteManager);
String managerPassword = siteManager.getPassword();
@@ -120,7 +120,7 @@ public class AddTagsTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API,
TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, description = "Verify include count parameter")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void getTagsUsingCountParam() throws Exception
public void getTagsUsingCountParam()
{
FileModel file = dataContent.usingSite(siteModel).usingUser(adminUserModel).createContent(CMISUtil.DocumentType.TEXT_PLAIN);
String tagName = RandomData.getRandomName("tag");
@@ -139,4 +139,4 @@ public class AddTagsTests extends TagsDataPrep
}
}
}
}

View File

@@ -15,6 +15,7 @@ import org.alfresco.utility.testrail.ExecutionType;
import org.alfresco.utility.testrail.annotation.TestRail;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.http.HttpStatus;
import org.testng.annotations.Ignore;
import org.testng.annotations.Test;
/**
@@ -29,7 +30,7 @@ public class DeleteTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify Admin user deletes tags with Rest API and status code is 204")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void adminIsAbleToDeleteTags() throws Exception
public void adminIsAbleToDeleteTags()
{
restClient.authenticateUser(adminUserModel);
tag = restClient.withCoreAPI().usingResource(document).addTag(RandomData.getRandomName("tag"));
@@ -43,7 +44,7 @@ public class DeleteTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.SANITY,
description = "Verify Manager user deletes tags created by admin user with Rest API and status code is 204")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.SANITY })
public void managerIsAbleToDeleteTags() throws Exception
public void managerIsAbleToDeleteTags()
{
restClient.authenticateUser(adminUserModel);
tag = restClient.withCoreAPI().usingResource(document).addTag(RandomData.getRandomName("tag"));
@@ -56,7 +57,7 @@ public class DeleteTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify Collaborator user deletes tags created by admin user with Rest API and status code is 204")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void collaboratorIsAbleToDeleteTags() throws Exception
public void collaboratorIsAbleToDeleteTags()
{
restClient.authenticateUser(adminUserModel);
tag = restClient.withCoreAPI().usingResource(document).addTag(RandomData.getRandomName("tag"));
@@ -69,7 +70,7 @@ public class DeleteTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify Contributor user can't delete tags created by admin user with Rest API and status code is 403")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void contributorIsNotAbleToDeleteTagsForAnotherUserContent() throws Exception
public void contributorIsNotAbleToDeleteTagsForAnotherUserContent()
{
restClient.authenticateUser(adminUserModel);
tag = restClient.withCoreAPI().usingResource(document).addTag(RandomData.getRandomName("tag"));
@@ -82,7 +83,7 @@ public class DeleteTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify Contributor user deletes tags created by him with Rest API and status code is 204")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void contributorIsAbleToDeleteTagsForHisContent() throws Exception
public void contributorIsAbleToDeleteTagsForHisContent()
{
userModel = usersWithRoles.getOneUserWithRole(UserRole.SiteContributor);
restClient.authenticateUser(userModel);
@@ -96,7 +97,7 @@ public class DeleteTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify Consumer user can't delete tags created by admin user with Rest API and status code is 403")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void consumerIsNotAbleToDeleteTags() throws Exception
public void consumerIsNotAbleToDeleteTags()
{
restClient.authenticateUser(adminUserModel);
tag = restClient.withCoreAPI().usingResource(document).addTag(RandomData.getRandomName("tag"));
@@ -110,7 +111,7 @@ public class DeleteTagTests extends TagsDataPrep
description = "Verify user gets status code 401 if authentication call fails")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.SANITY })
// @Bug(id="MNT-16904", description = "It fails only on environment with tenants")
public void userIsNotAbleToDeleteTagIfAuthenticationFails() throws Exception
public void userIsNotAbleToDeleteTagIfAuthenticationFails()
{
restClient.authenticateUser(adminUserModel);
tag = restClient.withCoreAPI().usingResource(document).addTag(RandomData.getRandomName("tag"));
@@ -127,7 +128,7 @@ public class DeleteTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that if user has no permission to remove tag returned status code is 403. Check default error model schema")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void deleteTagWithUserWithoutPermissionCheckDefaultErrorModelSchema() throws Exception
public void deleteTagWithUserWithoutPermissionCheckDefaultErrorModelSchema()
{
restClient.authenticateUser(adminUserModel);
RestTagModel tag = restClient.withCoreAPI().usingResource(document).addTag(RandomData.getRandomName("tag"));
@@ -144,7 +145,7 @@ public class DeleteTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that if node does not exist returned status code is 404")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void deleteTagForANonexistentNode() throws Exception
public void deleteTagForANonexistentNode()
{
restClient.authenticateUser(adminUserModel);
RestTagModel tag = restClient.withCoreAPI().usingResource(document).addTag(RandomData.getRandomName("tag"));
@@ -159,7 +160,7 @@ public class DeleteTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that if tag does not exist returned status code is 404")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void deleteTagThatDoesNotExist() throws Exception
public void deleteTagThatDoesNotExist()
{
restClient.authenticateUser(adminUserModel);
RestTagModel tag = restClient.withCoreAPI().usingResource(document).addTag(RandomData.getRandomName("tag"));
@@ -172,7 +173,7 @@ public class DeleteTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that if tag id is empty returned status code is 405")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void deleteTagWithEmptyId() throws Exception
public void deleteTagWithEmptyId()
{
restClient.authenticateUser(adminUserModel);
RestTagModel tag = restClient.withCoreAPI().usingResource(document).addTag(RandomData.getRandomName("tag"));
@@ -185,7 +186,7 @@ public class DeleteTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that folder tag can be deleted")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void deleteFolderTag() throws Exception
public void deleteFolderTag()
{
FolderModel folderModel = dataContent.usingUser(adminUserModel).usingSite(siteModel).createFolder();;
restClient.authenticateUser(adminUserModel);
@@ -200,7 +201,8 @@ public class DeleteTagTests extends TagsDataPrep
executionType = ExecutionType.REGRESSION, description = "Verify Manager user can't delete deleted tag.")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
@Bug(id = "ACE-5455")
public void managerCannotDeleteDeletedTag() throws Exception
@Ignore
public void managerCannotDeleteDeletedTag()
{
tag = restClient.authenticateUser(adminUserModel)
.withCoreAPI().usingResource(document).addTag(RandomData.getRandomName("tag"));
@@ -216,7 +218,7 @@ public class DeleteTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS },
executionType = ExecutionType.REGRESSION, description = "Verify Collaborator user can delete long tag.")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void userCollaboratorCanDeleteLongTag() throws Exception
public void userCollaboratorCanDeleteLongTag()
{
String longTag = RandomStringUtils.randomAlphanumeric(800);
@@ -231,7 +233,7 @@ public class DeleteTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS },
executionType = ExecutionType.REGRESSION, description = "Verify Manager user can delete short tag.")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void managerCanDeleteShortTag() throws Exception
public void managerCanDeleteShortTag()
{
String shortTag = RandomStringUtils.randomAlphanumeric(10);
@@ -246,7 +248,7 @@ public class DeleteTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS },
executionType = ExecutionType.REGRESSION, description = "Verify Admin can delete tag then add it again.")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void adminRemovesTagAndAddsItAgain() throws Exception
public void adminRemovesTagAndAddsItAgain()
{
String tagValue = RandomStringUtils.randomAlphanumeric(10);
@@ -267,7 +269,7 @@ public class DeleteTagTests extends TagsDataPrep
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS },
executionType = ExecutionType.REGRESSION, description = "Verify Manager user can delete tag added by another user.")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION })
public void managerCanDeleteTagAddedByAnotherUser() throws Exception
public void managerCanDeleteTagAddedByAnotherUser()
{
tag = restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteCollaborator))
.withCoreAPI().usingResource(document).addTag(RandomStringUtils.randomAlphanumeric(10));
@@ -276,4 +278,4 @@ public class DeleteTagTests extends TagsDataPrep
.withCoreAPI().usingResource(document).deleteTag(tag);
restClient.assertStatusCodeIs(HttpStatus.NO_CONTENT);
}
}
}

View File

@@ -150,18 +150,6 @@ public class GetNodeTagsTests extends TagsDataPrep
restClient.assertStatusCodeIs(HttpStatus.NOT_FOUND).assertLastError().containsSummary(String.format(RestErrorModel.ENTITY_NOT_FOUND, nodeRef));
}
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify that if node id is empty returns status code 403")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION})
public void emptyNodeIdTest() throws Exception
{
FileModel badDocument = dataContent.usingSite(siteModel).usingUser(adminUserModel).createContent(CMISUtil.DocumentType.TEXT_PLAIN);
badDocument.setNodeRef("");
restClient.authenticateUser(adminUserModel).withCoreAPI().usingResource(badDocument).getNodeTags();
restClient.assertStatusCodeIs(HttpStatus.NOT_FOUND).assertLastError().containsSummary(String.format(RestErrorModel.ENTITY_NOT_FOUND, ""));
}
@TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION,
description = "Verify folder tags")
@Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION})
@@ -303,4 +291,4 @@ public class GetNodeTagsTests extends TagsDataPrep
.and().field("skipCount").is("10000");
returnedCollection.assertThat().entriesListCountIs(0);
}
}
}

View File

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

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>20.75</version>
<version>20.140-SNAPSHOT</version>
</parent>
<properties>

66
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>20.75</version>
<version>20.140-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Alfresco Community Repo Parent</name>
@@ -35,6 +35,8 @@
<build-number>local</build-number>
<image.tag>latest</image.tag>
<image.registry>quay.io</image.registry>
<builder.name>entitled-builder</builder.name>
<local.registry>127.0.0.1:5000</local.registry>
<java.version>11</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
@@ -45,26 +47,26 @@
<dependency.alfresco-hb-data-sender.version>1.0.12</dependency.alfresco-hb-data-sender.version>
<dependency.alfresco-trashcan-cleaner.version>2.4.1</dependency.alfresco-trashcan-cleaner.version>
<dependency.alfresco-jlan.version>7.2</dependency.alfresco-jlan.version>
<dependency.alfresco-jlan.version>7.4</dependency.alfresco-jlan.version>
<dependency.alfresco-server-root.version>6.0.1</dependency.alfresco-server-root.version>
<dependency.alfresco-messaging-repo.version>1.2.20</dependency.alfresco-messaging-repo.version>
<dependency.activiti-engine.version>5.23.0</dependency.activiti-engine.version>
<dependency.activiti.version>5.23.0</dependency.activiti.version>
<dependency.alfresco-transform-service.version>2.0.0</dependency.alfresco-transform-service.version>
<dependency.alfresco-transform-core.version>3.0.0</dependency.alfresco-transform-core.version>
<dependency.alfresco-transform-service.version>2.1.0-A5</dependency.alfresco-transform-service.version>
<dependency.alfresco-transform-core.version>3.1.0-A5</dependency.alfresco-transform-core.version>
<dependency.alfresco-greenmail.version>6.5</dependency.alfresco-greenmail.version>
<dependency.acs-event-model.version>0.0.18</dependency.acs-event-model.version>
<dependency.spring.version>5.3.25</dependency.spring.version>
<dependency.antlr.version>3.5.3</dependency.antlr.version>
<dependency.jackson.version>2.14.0</dependency.jackson.version>
<dependency.jackson.version>2.15.0-rc1</dependency.jackson.version>
<dependency.cxf.version>3.5.5</dependency.cxf.version>
<dependency.opencmis.version>1.0.0</dependency.opencmis.version>
<dependency.webscripts.version>8.33</dependency.webscripts.version>
<dependency.webscripts.version>8.40</dependency.webscripts.version>
<dependency.bouncycastle.version>1.70</dependency.bouncycastle.version>
<dependency.mockito-core.version>4.9.0</dependency.mockito-core.version>
<dependency.assertj.version>3.24.2</dependency.assertj.version>
<dependency.org-json.version>20220320</dependency.org-json.version>
<dependency.org-json.version>20230227</dependency.org-json.version>
<dependency.commons-dbcp.version>2.9.0</dependency.commons-dbcp.version>
<dependency.commons-io.version>2.11.0</dependency.commons-io.version>
<dependency.gson.version>2.8.9</dependency.gson.version>
@@ -74,20 +76,19 @@
<dependency.xercesImpl.version>2.12.2</dependency.xercesImpl.version>
<dependency.slf4j.version>2.0.3</dependency.slf4j.version>
<dependency.log4j.version>2.19.0</dependency.log4j.version>
<dependency.gytheio.version>0.17</dependency.gytheio.version>
<dependency.groovy.version>3.0.12</dependency.groovy.version>
<dependency.gytheio.version>0.18</dependency.gytheio.version>
<dependency.groovy.version>3.0.16</dependency.groovy.version>
<dependency.tika.version>2.4.1</dependency.tika.version>
<dependency.spring-security.version>5.7.5</dependency.spring-security.version>
<dependency.spring-security.version>5.8.2</dependency.spring-security.version>
<dependency.truezip.version>7.7.10</dependency.truezip.version>
<dependency.poi.version>5.2.2</dependency.poi.version>
<dependency.poi-ooxml-lite.version>5.2.3</dependency.poi-ooxml-lite.version>
<dependency.keycloak.version>18.0.0</dependency.keycloak.version>
<dependency.jboss.logging.version>3.5.0.Final</dependency.jboss.logging.version>
<dependency.camel.version>3.18.2</dependency.camel.version> <!-- when bumping this version, please keep track/sync with included netty.io dependencies -->
<dependency.netty.version>4.1.79.Final</dependency.netty.version> <!-- must be in sync with camels transitive dependencies, e.g.: netty-common -->
<dependency.netty.qpid.version>4.1.72.Final</dependency.netty.qpid.version> <!-- must be in sync with camels transitive dependencies: native-unix-common/native-epoll/native-kqueue -->
<dependency.netty-tcnative.version>2.0.53.Final</dependency.netty-tcnative.version> <!-- must be in sync with camels transitive dependencies -->
<dependency.activemq.version>5.17.1</dependency.activemq.version>
<dependency.camel.version>3.20.2</dependency.camel.version> <!-- when bumping this version, please keep track/sync with included netty.io dependencies -->
<dependency.netty.version>4.1.87.Final</dependency.netty.version> <!-- must be in sync with camels transitive dependencies, e.g.: netty-common -->
<dependency.netty.qpid.version>4.1.82.Final</dependency.netty.qpid.version> <!-- must be in sync with camels transitive dependencies: native-unix-common/native-epoll/native-kqueue -->
<dependency.netty-tcnative.version>2.0.56.Final</dependency.netty-tcnative.version> <!-- must be in sync with camels transitive dependencies -->
<dependency.activemq.version>5.17.4</dependency.activemq.version>
<dependency.apache-compress.version>1.22</dependency.apache-compress.version>
<dependency.apache.taglibs.version>1.2.5</dependency.apache.taglibs.version>
<dependency.awaitility.version>4.2.0</dependency.awaitility.version>
@@ -106,11 +107,12 @@
<dependency.jakarta-jws-api.version>2.1.0</dependency.jakarta-jws-api.version>
<dependency.jakarta-mail-api.version>1.6.5</dependency.jakarta-mail-api.version>
<dependency.jakarta-json-api.version>1.1.6</dependency.jakarta-json-api.version>
<dependency.jakarta-json-path.version>2.7.0</dependency.jakarta-json-path.version>
<dependency.jakarta-json-path.version>2.8.0</dependency.jakarta-json-path.version>
<dependency.json-smart.version>2.4.10</dependency.json-smart.version>
<dependency.jakarta-rpc-api.version>1.1.4</dependency.jakarta-rpc-api.version>
<alfresco.googledrive.version>3.4.0-M1</alfresco.googledrive.version>
<alfresco.aos-module.version>1.6.0-M1</alfresco.aos-module.version>
<alfresco.aos-module.version>1.6.0-A4</alfresco.aos-module.version>
<alfresco.api-explorer.version>7.3.0</alfresco.api-explorer.version> <!-- Also in alfresco-enterprise-share -->
<alfresco.maven-plugin.version>2.2.0</alfresco.maven-plugin.version>
@@ -120,7 +122,7 @@
<dependency.mysql.version>8.0.30</dependency.mysql.version>
<dependency.mysql-image.version>8</dependency.mysql-image.version>
<dependency.mariadb.version>2.7.4</dependency.mariadb.version>
<dependency.tas-utility.version>3.0.58</dependency.tas-utility.version>
<dependency.tas-utility.version>4.0.0</dependency.tas-utility.version>
<dependency.rest-assured.version>5.2.0</dependency.rest-assured.version>
<dependency.tas-email.version>1.11</dependency.tas-email.version>
<dependency.tas-webdav.version>1.7</dependency.tas-webdav.version>
@@ -148,7 +150,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>20.75</tag>
<tag>HEAD</tag>
</scm>
<distributionManagement>
@@ -239,6 +241,17 @@
<version>${dependency.jakarta-json-api.version}</version>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>${dependency.jakarta-json-path.version}</version>
</dependency>
<dependency>
<groupId> net.minidev</groupId>
<artifactId>json-smart</artifactId>
<version>${dependency.json-smart.version}</version>
</dependency>
<dependency>
<groupId>jakarta.xml.rpc</groupId>
<artifactId>jakarta.xml.rpc-api</artifactId>
@@ -384,7 +397,7 @@
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
<version>1.5</version>
</dependency>
<dependency>
@@ -516,7 +529,7 @@
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.32</version>
<version>2.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
@@ -899,6 +912,13 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-bom</artifactId>
<version>${dependency.spring-security.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
@@ -916,7 +936,7 @@
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.40.2</version>
<version>0.42.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>20.75</version>
<version>20.140-SNAPSHOT</version>
</parent>
<dependencies>

View File

@@ -26,57 +26,101 @@
package org.alfresco.rest.api;
import static org.alfresco.service.cmr.repository.StoreRef.STORE_REF_WORKSPACE_SPACESSTORE;
import java.util.List;
import org.alfresco.rest.api.model.Category;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.repository.StoreRef;
@Experimental
public interface Categories
{
Category getCategoryById(String id, Parameters parameters);
Category getCategoryById(StoreRef storeRef, String id, Parameters parameters);
List<Category> createSubcategories(String parentCategoryId, List<Category> categories, Parameters parameters);
default Category getCategoryById(String id, Parameters parameters)
{
return getCategoryById(STORE_REF_WORKSPACE_SPACESSTORE, id, parameters);
}
CollectionWithPagingInfo<Category> getCategoryChildren(String parentCategoryId, Parameters parameters);
List<Category> createSubcategories(StoreRef storeRef, String parentCategoryId, List<Category> categories, Parameters parameters);
default List<Category> createSubcategories(String parentCategoryId, List<Category> categories, Parameters parameters)
{
return createSubcategories(STORE_REF_WORKSPACE_SPACESSTORE, parentCategoryId, categories, parameters);
}
List<Category> getCategoryChildren(StoreRef storeRef, String parentCategoryId, Parameters parameters);
default List<Category> getCategoryChildren(String parentCategoryId, Parameters parameters)
{
return getCategoryChildren(STORE_REF_WORKSPACE_SPACESSTORE, parentCategoryId, parameters);
}
/**
* Update category by ID. Currently, it's possible only to update the name of category.
*
* @param storeRef Reference to node store.
* @param id Category ID.
* @param fixedCategoryModel Fixed category model.
* @param parameters Additional parameters.
* @return Updated category.
*/
Category updateCategoryById(String id, Category fixedCategoryModel);
Category updateCategoryById(StoreRef storeRef, String id, Category fixedCategoryModel, Parameters parameters);
void deleteCategoryById(String id, Parameters parameters);
default Category updateCategoryById(String id, Category fixedCategoryModel, Parameters parameters)
{
return updateCategoryById(STORE_REF_WORKSPACE_SPACESSTORE, id, fixedCategoryModel, parameters);
}
void deleteCategoryById(StoreRef storeRef, String id, Parameters parameters);
default void deleteCategoryById(String id, Parameters parameters)
{
deleteCategoryById(STORE_REF_WORKSPACE_SPACESSTORE, id, parameters);
}
/**
* Get categories linked from node. Read permission on node is required.
* Node type is restricted to specified vales from: {@link org.alfresco.util.TypeConstraint}.
*
* @param nodeId Node ID.
* @param parameters Additional parameters.
* @return Categories linked from node.
*/
List<Category> listCategoriesForNode(String nodeId);
List<Category> listCategoriesForNode(String nodeId, Parameters parameters);
/**
* Link node to categories. Change permission on node is required.
* Node types allowed for categorization are specified within {@link org.alfresco.util.TypeConstraint}.
*
* @param storeRef Reference to node store.
* @param nodeId Node ID.
* @param categoryLinks Category IDs to which content should be linked to.
* @param parameters Additional parameters.
* @return Linked to categories.
*/
List<Category> linkNodeToCategories(String nodeId, List<Category> categoryLinks);
List<Category> linkNodeToCategories(StoreRef storeRef, String nodeId, List<Category> categoryLinks, Parameters parameters);
default List<Category> linkNodeToCategories(String nodeId, List<Category> categoryLinks, Parameters parameters)
{
return linkNodeToCategories(STORE_REF_WORKSPACE_SPACESSTORE, nodeId, categoryLinks, parameters);
}
/**
* Unlink node from a category.
*
* @param storeRef Reference to node store.
* @param nodeId Node ID.
* @param categoryId Category ID from which content node should be unlinked from.
* @param parameters Additional parameters.
*/
void unlinkNodeFromCategory(String nodeId, String categoryId, Parameters parameters);
void unlinkNodeFromCategory(StoreRef storeRef, String nodeId, String categoryId, Parameters parameters);
default void unlinkNodeFromCategory(String nodeId, String categoryId, Parameters parameters)
{
unlinkNodeFromCategory(STORE_REF_WORKSPACE_SPACESSTORE, nodeId, categoryId, parameters);
}
}

View File

@@ -39,6 +39,8 @@ import org.alfresco.rest.api.model.LockInfo;
import org.alfresco.rest.api.model.Node;
import org.alfresco.rest.api.model.PathInfo;
import org.alfresco.rest.api.model.UserInfo;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.resource.content.BasicContentInfo;
import org.alfresco.rest.framework.resource.content.BinaryResource;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
@@ -208,6 +210,19 @@ public interface Nodes
NodeRef validateNode(StoreRef storeRef, String nodeId);
NodeRef validateNode(String nodeId);
NodeRef validateNode(NodeRef nodeRef);
/**
* Check that the specified id refers to a valid node.
*
* @param nodeId The node id to look up using SpacesStore or an alias like -root-.
* @return The node ref.
* @throws InvalidArgumentException if the specified node id is not a valid format.
* @throws EntityNotFoundException if the specified node was not found in the database.
*/
default NodeRef validateOrLookupNode(String nodeId)
{
return validateOrLookupNode(nodeId, null);
}
NodeRef validateOrLookupNode(String nodeId, String path);
boolean nodeMatches(NodeRef nodeRef, Set<QName> expectedTypes, Set<QName> excludedTypes);

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2023 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,22 +23,35 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api;
import java.util.List;
import org.alfresco.rest.api.model.Tag;
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.service.cmr.repository.StoreRef;
public interface Tags
{
public List<Tag> addTags(String nodeId, List<Tag> tags);
public Tag getTag(StoreRef storeRef, String tagId);
public void deleteTag(String nodeId, String tagId);
public CollectionWithPagingInfo<Tag> getTags(StoreRef storeRef, Parameters params);
public Tag changeTag(StoreRef storeRef, String tagId, Tag tag);
public CollectionWithPagingInfo<Tag> getTags(String nodeId, Parameters params);
}
package org.alfresco.rest.api;
import static org.alfresco.service.cmr.repository.StoreRef.STORE_REF_WORKSPACE_SPACESSTORE;
import java.util.List;
import org.alfresco.rest.api.model.Tag;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.repository.StoreRef;
public interface Tags
{
List<Tag> addTags(String nodeId, List<Tag> tags, Parameters parameters);
Tag getTag(StoreRef storeRef, String tagId);
void deleteTag(String nodeId, String tagId);
CollectionWithPagingInfo<Tag> getTags(StoreRef storeRef, Parameters params);
Tag changeTag(StoreRef storeRef, String tagId, Tag tag);
CollectionWithPagingInfo<Tag> getTags(String nodeId, Parameters params);
@Experimental
List<Tag> createTags(StoreRef storeRef, List<Tag> tags, Parameters parameters);
@Experimental
default List<Tag> createTags(List<Tag> tags, Parameters parameters)
{
return createTags(STORE_REF_WORKSPACE_SPACESSTORE, tags, parameters);
}
void deleteTagById(StoreRef storeRef, String tagId);
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2023 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -45,6 +45,7 @@ public class CategoriesEntityResource implements EntityResourceAction.ReadById<C
EntityResourceAction.Update<Category>,
EntityResourceAction.Delete
{
private final Categories categories;
public CategoriesEntityResource(Categories categories)
@@ -77,7 +78,7 @@ public class CategoriesEntityResource implements EntityResourceAction.ReadById<C
@Override
public Category update(String id, Category categoryModel, Parameters parameters)
{
return categories.updateCategoryById(id, categoryModel);
return categories.updateCategoryById(id, categoryModel, parameters);
}
/**

View File

@@ -63,7 +63,7 @@ public class NodesCategoryLinksRelation implements RelationshipResourceAction.Cr
@Override
public CollectionWithPagingInfo<Category> readAll(String nodeId, Parameters parameters)
{
return ListPage.of(categories.listCategoriesForNode(nodeId), parameters.getPaging());
return ListPage.of(categories.listCategoriesForNode(nodeId, parameters), parameters.getPaging());
}
/**
@@ -77,7 +77,7 @@ public class NodesCategoryLinksRelation implements RelationshipResourceAction.Cr
@Override
public List<Category> create(String nodeId, List<Category> categoryLinks, Parameters parameters)
{
return categories.linkNodeToCategories(nodeId, categoryLinks);
return categories.linkNodeToCategories(nodeId, categoryLinks, parameters);
}
/**
@@ -93,5 +93,4 @@ public class NodesCategoryLinksRelation implements RelationshipResourceAction.Cr
{
categories.unlinkNodeFromCategory(nodeId, categoryId, parameters);
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* Copyright (C) 2005 - 2023 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -26,9 +26,8 @@
package org.alfresco.rest.api.categories;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import org.alfresco.rest.api.Categories;
import org.alfresco.rest.api.model.Category;
@@ -36,6 +35,7 @@ import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.resource.RelationshipResource;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.ListPage;
import org.alfresco.rest.framework.resource.parameters.Parameters;
@RelationshipResource(name = "subcategories", entityResource = CategoriesEntityResource.class, title = "Subcategories")
@@ -69,8 +69,8 @@ public class SubcategoriesRelation implements RelationshipResourceAction.Create<
description = "Lists direct children of a parent category",
successStatus = HttpServletResponse.SC_OK)
@Override
public CollectionWithPagingInfo<Category> readAll(String parentCategoryId, Parameters params)
public CollectionWithPagingInfo<Category> readAll(String parentCategoryId, Parameters parameters)
{
return categories.getCategoryChildren(parentCategoryId, params);
return ListPage.of(categories.getCategoryChildren(parentCategoryId, parameters), parameters.getPaging());
}
}

View File

@@ -46,10 +46,8 @@ import org.alfresco.rest.api.model.Category;
import org.alfresco.rest.api.model.Node;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException;
import org.alfresco.rest.framework.core.exceptions.InvalidNodeTypeException;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.ListPage;
import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
@@ -62,6 +60,7 @@ import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.util.Pair;
import org.alfresco.util.TypeConstraint;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
@@ -69,6 +68,7 @@ import org.apache.commons.lang3.StringUtils;
@Experimental
public class CategoriesImpl implements Categories
{
static final String INCLUDE_COUNT_PARAM = "count";
static final String NOT_A_VALID_CATEGORY = "Node id does not refer to a valid category";
static final String NO_PERMISSION_TO_MANAGE_A_CATEGORY = "Current user does not have permission to manage a category";
static final String NO_PERMISSION_TO_READ_CONTENT = "Current user does not have read permission to content";
@@ -83,8 +83,8 @@ public class CategoriesImpl implements Categories
private final PermissionService permissionService;
private final TypeConstraint typeConstraint;
public CategoriesImpl(AuthorityService authorityService, CategoryService categoryService, Nodes nodes, NodeService nodeService, PermissionService permissionService,
TypeConstraint typeConstraint)
public CategoriesImpl(AuthorityService authorityService, CategoryService categoryService, Nodes nodes, NodeService nodeService,
PermissionService permissionService, TypeConstraint typeConstraint)
{
this.authorityService = authorityService;
this.categoryService = categoryService;
@@ -95,63 +95,89 @@ public class CategoriesImpl implements Categories
}
@Override
public Category getCategoryById(final String id, final Parameters params)
public Category getCategoryById(final StoreRef storeRef, final String id, final Parameters parameters)
{
final NodeRef nodeRef = getCategoryNodeRef(id);
final NodeRef nodeRef = getCategoryNodeRef(storeRef, id);
if (isRootCategory(nodeRef))
{
throw new InvalidArgumentException(NOT_A_VALID_CATEGORY, new String[]{id});
}
return mapToCategory(nodeRef);
final Category category = mapToCategory(nodeRef);
if (parameters.getInclude().contains(INCLUDE_COUNT_PARAM))
{
final Map<String, Integer> categoriesCount = getCategoriesCount(storeRef);
category.setCount(categoriesCount.getOrDefault(category.getId(), 0));
}
return category;
}
@Override
public List<Category> createSubcategories(String parentCategoryId, List<Category> categories, Parameters parameters)
public List<Category> createSubcategories(final StoreRef storeRef, final String parentCategoryId, final List<Category> categories, final Parameters parameters)
{
verifyAdminAuthority();
final NodeRef parentNodeRef = getCategoryNodeRef(parentCategoryId);
final List<NodeRef> categoryNodeRefs = categories.stream()
final NodeRef parentNodeRef = getCategoryNodeRef(storeRef, parentCategoryId);
return categories.stream()
.map(c -> createCategoryNodeRef(parentNodeRef, c))
.collect(Collectors.toList());
return categoryNodeRefs.stream()
.map(this::mapToCategory)
.peek(category -> {
if (parameters.getInclude().contains(INCLUDE_COUNT_PARAM))
{
category.setCount(0);
}
})
.collect(Collectors.toList());
}
@Override
public CollectionWithPagingInfo<Category> getCategoryChildren(String parentCategoryId, Parameters params)
public List<Category> getCategoryChildren(final StoreRef storeRef, final String parentCategoryId, final Parameters parameters)
{
final NodeRef parentNodeRef = getCategoryNodeRef(parentCategoryId);
final List<ChildAssociationRef> childCategoriesAssocs = nodeService.getChildAssocs(parentNodeRef).stream()
.filter(ca -> ContentModel.ASSOC_SUBCATEGORIES.equals(ca.getTypeQName()))
.collect(Collectors.toList());
final List<Category> categories = childCategoriesAssocs.stream()
.map(c -> mapToCategory(c.getChildRef()))
.collect(Collectors.toList());
return ListPage.of(categories, params.getPaging());
final NodeRef parentNodeRef = getCategoryNodeRef(storeRef, parentCategoryId);
final List<Category> categories = nodeService.getChildAssocs(parentNodeRef).stream()
.filter(ca -> ContentModel.ASSOC_SUBCATEGORIES.equals(ca.getTypeQName()))
.map(ChildAssociationRef::getChildRef)
.map(this::mapToCategory)
.collect(Collectors.toList());
if (parameters.getInclude().contains(INCLUDE_COUNT_PARAM))
{
final Map<String, Integer> categoriesCount = getCategoriesCount(storeRef);
categories.forEach(category -> category.setCount(categoriesCount.getOrDefault(category.getId(), 0)));
}
return categories;
}
@Override
public Category updateCategoryById(final String id, final Category fixedCategoryModel)
public Category updateCategoryById(final StoreRef storeRef, final String id, final Category fixedCategoryModel, final Parameters parameters)
{
verifyAdminAuthority();
final NodeRef categoryNodeRef = getCategoryNodeRef(id);
final NodeRef categoryNodeRef = getCategoryNodeRef(storeRef, id);
if (isRootCategory(categoryNodeRef))
{
throw new InvalidArgumentException(NOT_A_VALID_CATEGORY, new String[]{id});
}
validateCategoryFields(fixedCategoryModel);
final Category category = mapToCategory(changeCategoryName(categoryNodeRef, fixedCategoryModel.getName()));
return mapToCategory(changeCategoryName(categoryNodeRef, fixedCategoryModel.getName()));
if (parameters.getInclude().contains(INCLUDE_COUNT_PARAM))
{
final Map<String, Integer> categoriesCount = getCategoriesCount(storeRef);
category.setCount(categoriesCount.getOrDefault(category.getId(), 0));
}
return category;
}
@Override
public void deleteCategoryById(String id, Parameters parameters)
public void deleteCategoryById(final StoreRef storeRef, final String id, final Parameters parameters)
{
verifyAdminAuthority();
final NodeRef nodeRef = getCategoryNodeRef(id);
final NodeRef nodeRef = getCategoryNodeRef(storeRef, id);
if (isRootCategory(nodeRef))
{
throw new InvalidArgumentException(NOT_A_VALID_CATEGORY, new String[]{id});
@@ -161,9 +187,9 @@ public class CategoriesImpl implements Categories
}
@Override
public List<Category> listCategoriesForNode(final String nodeId)
public List<Category> listCategoriesForNode(final String nodeId, final Parameters parameters)
{
final NodeRef contentNodeRef = nodes.validateNode(nodeId);
final NodeRef contentNodeRef = nodes.validateOrLookupNode(nodeId);
verifyReadPermission(contentNodeRef);
verifyNodeType(contentNodeRef);
@@ -178,14 +204,14 @@ public class CategoriesImpl implements Categories
}
@Override
public List<Category> linkNodeToCategories(final String nodeId, final List<Category> categoryLinks)
public List<Category> linkNodeToCategories(final StoreRef storeRef, final String nodeId, final List<Category> categoryLinks, final Parameters parameters)
{
if (CollectionUtils.isEmpty(categoryLinks))
{
throw new InvalidArgumentException(NOT_A_VALID_CATEGORY);
}
final NodeRef contentNodeRef = nodes.validateNode(nodeId);
final NodeRef contentNodeRef = nodes.validateOrLookupNode(nodeId);
verifyChangePermission(contentNodeRef);
verifyNodeType(contentNodeRef);
@@ -194,7 +220,7 @@ public class CategoriesImpl implements Categories
.map(Category::getId)
.filter(StringUtils::isNotEmpty)
.distinct()
.map(this::getCategoryNodeRef)
.map(id -> getCategoryNodeRef(storeRef, id))
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(categoryNodeRefs) || isRootCategoryPresent(categoryNodeRefs))
@@ -208,10 +234,10 @@ public class CategoriesImpl implements Categories
}
@Override
public void unlinkNodeFromCategory(final String nodeId, final String categoryId, Parameters parameters)
public void unlinkNodeFromCategory(final StoreRef storeRef, final String nodeId, final String categoryId, final Parameters parameters)
{
final NodeRef categoryNodeRef = getCategoryNodeRef(categoryId);
final NodeRef contentNodeRef = nodes.validateNode(nodeId);
final NodeRef categoryNodeRef = getCategoryNodeRef(storeRef, categoryId);
final NodeRef contentNodeRef = nodes.validateOrLookupNode(nodeId);
verifyChangePermission(contentNodeRef);
verifyNodeType(contentNodeRef);
@@ -272,13 +298,14 @@ public class CategoriesImpl implements Categories
* This method gets category NodeRef for a given category id.
* If '-root-' is passed as category id, then it's retrieved as a call to {@link org.alfresco.service.cmr.search.CategoryService#getRootCategoryNodeRef}
* In all other cases it's retrieved as a node of a category type {@link #validateCategoryNode(String)}
* @param storeRef Reference to node store.
* @param nodeId category node id
* @return NodRef of category node
*/
private NodeRef getCategoryNodeRef(String nodeId)
private NodeRef getCategoryNodeRef(StoreRef storeRef, String nodeId)
{
return PATH_ROOT.equals(nodeId) ?
categoryService.getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE)
categoryService.getRootCategoryNodeRef(storeRef)
.orElseThrow(() -> new EntityNotFoundException(nodeId)) :
validateCategoryNode(nodeId);
}
@@ -434,4 +461,18 @@ public class CategoriesImpl implements Categories
nodeService.setProperty(nodeRef, ContentModel.PROP_CATEGORIES, (Serializable) allCategories);
}
}
/**
* Get categories by usage count. Result is a map of category IDs (short form - UUID) as key and usage count as value.
*
* @param storeRef Reference to node store.
* @return Map of categories IDs and usage count.
*/
private Map<String, Integer> getCategoriesCount(final StoreRef storeRef)
{
final String idPrefix = storeRef + "/";
return categoryService.getTopCategories(storeRef, ContentModel.ASPECT_GEN_CLASSIFIABLE, Integer.MAX_VALUE)
.stream()
.collect(Collectors.toMap(pair -> pair.getFirst().toString().replace(idPrefix, StringUtils.EMPTY), Pair::getSecond));
}
}

View File

@@ -1356,7 +1356,7 @@ public class NodesImpl implements Nodes
private void calculateRelativePath(String parentFolderNodeId, Node node)
{
NodeRef rootNodeRef = validateOrLookupNode(parentFolderNodeId, null);
NodeRef rootNodeRef = validateOrLookupNode(parentFolderNodeId);
try
{
// get the path elements
@@ -1741,7 +1741,7 @@ public class NodesImpl implements Nodes
@Override
public void deleteNode(String nodeId, Parameters parameters)
{
NodeRef nodeRef = validateOrLookupNode(nodeId, null);
NodeRef nodeRef = validateOrLookupNode(nodeId);
if (isSpecialNode(nodeRef, getNodeType(nodeRef)))
{
@@ -1785,7 +1785,7 @@ public class NodesImpl implements Nodes
validateProperties(nodeInfo.getProperties(), EXCLUDED_NS, Arrays.asList());
// check that requested parent node exists and it's type is a (sub-)type of folder
NodeRef parentNodeRef = validateOrLookupNode(parentFolderNodeId, null);
NodeRef parentNodeRef = validateOrLookupNode(parentFolderNodeId);
// node name - mandatory
String nodeName = nodeInfo.getName();
@@ -2290,7 +2290,7 @@ public class NodesImpl implements Nodes
validateAspects(nodeInfo.getAspectNames(), EXCLUDED_NS, EXCLUDED_ASPECTS);
validateProperties(nodeInfo.getProperties(), EXCLUDED_NS, Arrays.asList());
final NodeRef nodeRef = validateOrLookupNode(nodeId, null);
final NodeRef nodeRef = validateOrLookupNode(nodeId);
QName nodeTypeQName = getNodeType(nodeRef);
@@ -2523,8 +2523,8 @@ public class NodesImpl implements Nodes
throw new InvalidArgumentException("Missing targetParentId");
}
final NodeRef parentNodeRef = validateOrLookupNode(targetParentId, null);
final NodeRef sourceNodeRef = validateOrLookupNode(sourceNodeId, null);
final NodeRef parentNodeRef = validateOrLookupNode(targetParentId);
final NodeRef sourceNodeRef = validateOrLookupNode(sourceNodeId);
FileInfo fi = moveOrCopyImpl(sourceNodeRef, parentNodeRef, name, isCopy);
return getFolderOrDocument(fi.getNodeRef().getId(), parameters);
@@ -2954,7 +2954,7 @@ public class NodesImpl implements Nodes
throw new InvalidArgumentException("The request content-type is not multipart: "+parentFolderNodeId);
}
NodeRef parentNodeRef = validateOrLookupNode(parentFolderNodeId, null);
NodeRef parentNodeRef = validateOrLookupNode(parentFolderNodeId);
if (!nodeMatches(parentNodeRef, Collections.singleton(ContentModel.TYPE_FOLDER), null, false))
{
throw new InvalidArgumentException("NodeId of folder is expected: " + parentNodeRef.getId());
@@ -3385,7 +3385,7 @@ public class NodesImpl implements Nodes
@Override
public Node lock(String nodeId, LockInfo lockInfo, Parameters parameters)
{
NodeRef nodeRef = validateOrLookupNode(nodeId, null);
NodeRef nodeRef = validateOrLookupNode(nodeId);
if (isSpecialNode(nodeRef, getNodeType(nodeRef)))
{
@@ -3424,7 +3424,7 @@ public class NodesImpl implements Nodes
@Override
public Node unlock(String nodeId, Parameters parameters)
{
NodeRef nodeRef = validateOrLookupNode(nodeId, null);
NodeRef nodeRef = validateOrLookupNode(nodeId);
if (isSpecialNode(nodeRef, getNodeType(nodeRef)))
{

View File

@@ -185,7 +185,7 @@ public class QueriesImpl implements Queries, InitializingBean
String rootNodeId = parameters.getParameter(PARAM_ROOT_NODE_ID);
if (rootNodeId != null)
{
NodeRef nodeRef = nodes.validateOrLookupNode(rootNodeId, null);
NodeRef nodeRef = nodes.validateOrLookupNode(rootNodeId);
query.append("PATH:\"").append(getQNamePath(nodeRef.getId())).append("//*\" AND (");
}
if (term != null)

View File

@@ -695,7 +695,7 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware
{
if (versionLabelId != null)
{
nodeRef = nodes.validateOrLookupNode(nodeRef.getId(), null);
nodeRef = nodes.validateOrLookupNode(nodeRef.getId());
VersionHistory vh = versionService.getVersionHistory(nodeRef);
if (vh != null)
{

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2023 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,125 +23,166 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api.impl;
import java.util.AbstractList;
package org.alfresco.rest.api.impl;
import static java.util.stream.Collectors.toList;
import static org.alfresco.rest.antlr.WhereClauseParser.EQUALS;
import static org.alfresco.rest.antlr.WhereClauseParser.IN;
import static org.alfresco.rest.antlr.WhereClauseParser.MATCHES;
import static org.alfresco.service.cmr.tagging.TaggingService.TAG_ROOT_NODE_REF;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import org.alfresco.query.PagingResults;
import org.alfresco.repo.tagging.NonExistentTagException;
import org.alfresco.repo.tagging.TagExistsException;
import org.alfresco.repo.tagging.TaggingException;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.Tags;
import org.alfresco.rest.api.model.Tag;
import org.alfresco.rest.framework.core.exceptions.ConstraintViolatedException;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.core.exceptions.NotFoundException;
import org.alfresco.rest.framework.core.exceptions.UnsupportedResourceOperationException;
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.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.tagging.TaggingService;
import org.alfresco.util.Pair;
import org.alfresco.util.TypeConstraint;
/**
* Centralises access to tag services and maps between representations.
*
* @author steveglover
* @since publicapi1.0
*/
public class TagsImpl implements Tags
{
private static final Object PARAM_INCLUDE_COUNT = "count";
private Nodes nodes;
private TaggingService taggingService;
private TypeConstraint typeConstraint;
public void setTypeConstraint(TypeConstraint typeConstraint)
{
this.typeConstraint = typeConstraint;
}
public void setNodes(Nodes nodes)
{
this.nodes = nodes;
}
public void setTaggingService(TaggingService taggingService)
{
this.taggingService = taggingService;
}
public List<Tag> addTags(String nodeId, final List<Tag> tags)
{
NodeRef nodeRef = nodes.validateNode(nodeId);
if(!typeConstraint.matches(nodeRef))
{
throw new UnsupportedResourceOperationException("Cannot tag this node");
}
List<String> tagValues = new AbstractList<String>()
{
@Override
public String get(int arg0)
{
String tag = tags.get(arg0).getTag();
return tag;
}
@Override
public int size()
{
return tags.size();
}
};
try
{
List<Pair<String, NodeRef>> tagNodeRefs = taggingService.addTags(nodeRef, tagValues);
List<Tag> ret = new ArrayList<Tag>(tags.size());
for(Pair<String, NodeRef> pair : tagNodeRefs)
{
ret.add(new Tag(pair.getSecond(), pair.getFirst()));
}
return ret;
}
catch(IllegalArgumentException e)
{
throw new InvalidArgumentException(e.getMessage());
}
}
public void deleteTag(String nodeId, String tagId)
{
NodeRef nodeRef = nodes.validateNode(nodeId);
getTag(tagId);
NodeRef existingTagNodeRef = validateTag(tagId);
String tagValue = taggingService.getTagName(existingTagNodeRef);
taggingService.removeTag(nodeRef, tagValue);
}
import org.alfresco.query.PagingResults;
import org.alfresco.repo.tagging.NonExistentTagException;
import org.alfresco.repo.tagging.TagExistsException;
import org.alfresco.repo.tagging.TaggingException;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.Tags;
import org.alfresco.rest.api.model.Tag;
import org.alfresco.rest.framework.core.exceptions.ConstraintViolatedException;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.core.exceptions.NotFoundException;
import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException;
import org.alfresco.rest.framework.core.exceptions.UnsupportedResourceOperationException;
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.where.Query;
import org.alfresco.rest.framework.resource.parameters.where.QueryHelper;
import org.alfresco.rest.framework.resource.parameters.where.QueryImpl;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.tagging.TaggingService;
import org.alfresco.util.Pair;
import org.alfresco.util.TypeConstraint;
import org.apache.commons.collections.CollectionUtils;
/**
* Centralises access to tag services and maps between representations.
*
* @author steveglover
* @since publicapi1.0
*/
public class TagsImpl implements Tags
{
private static final String PARAM_INCLUDE_COUNT = "count";
private static final String PARAM_WHERE_TAG = "tag";
static final String NOT_A_VALID_TAG = "An invalid parameter has been supplied";
static final String NO_PERMISSION_TO_MANAGE_A_TAG = "Current user does not have permission to manage a tag";
private Nodes nodes;
private NodeService nodeService;
private TaggingService taggingService;
private TypeConstraint typeConstraint;
private AuthorityService authorityService;
public void setTypeConstraint(TypeConstraint typeConstraint)
{
this.typeConstraint = typeConstraint;
}
public void setNodes(Nodes nodes)
{
this.nodes = nodes;
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setTaggingService(TaggingService taggingService)
{
this.taggingService = taggingService;
}
public void setAuthorityService(AuthorityService authorityService)
{
this.authorityService = authorityService;
}
public List<Tag> addTags(String nodeId, final List<Tag> tags, final Parameters parameters)
{
NodeRef nodeRef = nodes.validateOrLookupNode(nodeId);
if (!typeConstraint.matches(nodeRef))
{
throw new UnsupportedResourceOperationException("Cannot tag this node");
}
List<String> tagValues = tags.stream().map(Tag::getTag).collect(toList());
try
{
List<Pair<String, NodeRef>> tagNodeRefs = taggingService.addTags(nodeRef, tagValues);
List<Tag> ret = new ArrayList<>(tags.size());
List<Pair<String, Integer>> tagsCountPairList = taggingService.findTaggedNodesAndCountByTagName(nodeRef.getStoreRef());
Map<String, Integer> tagsCountMap = tagsCountPairList.stream().collect(Collectors.toMap(Pair::getFirst,Pair::getSecond));
for (Pair<String, NodeRef> pair : tagNodeRefs)
{
Tag createdTag = new Tag(pair.getSecond(), pair.getFirst());
if (parameters.getInclude().contains(PARAM_INCLUDE_COUNT)) {
createdTag.setCount(Optional.ofNullable(tagsCountMap.get(createdTag.getTag())).orElse(0) + 1);
}
ret.add(createdTag);
}
return ret;
}
catch (IllegalArgumentException e)
{
throw new InvalidArgumentException(e.getMessage());
}
}
public void deleteTag(String nodeId, String tagId)
{
NodeRef nodeRef = nodes.validateNode(nodeId);
getTag(tagId);
NodeRef existingTagNodeRef = validateTag(tagId);
String tagValue = taggingService.getTagName(existingTagNodeRef);
taggingService.removeTag(nodeRef, tagValue);
}
@Override
public void deleteTagById(StoreRef storeRef, String tagId) {
verifyAdminAuthority();
NodeRef tagNodeRef = validateTag(storeRef, tagId);
String tagValue = taggingService.getTagName(tagNodeRef);
taggingService.deleteTag(storeRef, tagValue);
}
@Override
public CollectionWithPagingInfo<Tag> getTags(StoreRef storeRef, Parameters params)
{
Paging paging = params.getPaging();
PagingResults<Pair<NodeRef, String>> results = taggingService.getTags(storeRef, Util.getPagingRequest(paging));
taggingService.getPagedTags(storeRef, 0, paging.getMaxItems());
Map<Integer, Collection<String>> namesFilters = resolveTagNamesQuery(params.getQuery());
PagingResults<Pair<NodeRef, String>> results = taggingService.getTags(storeRef, Util.getPagingRequest(paging), namesFilters.get(EQUALS), namesFilters.get(MATCHES));
Integer totalItems = results.getTotalResultCount().getFirst();
List<Pair<NodeRef, String>> page = results.getPage();
List<Tag> tags = new ArrayList<Tag>(page.size());
List<Pair<String, Integer>> tagsByCount = null;
Map<String, Integer> tagsByCountMap = new HashMap<String, Integer>();
List<Tag> tags = new ArrayList<>(page.size());
for (Pair<NodeRef, String> pair : page)
{
Tag selectedTag = new Tag(pair.getFirst(), pair.getSecond());
tags.add(selectedTag);
}
if (params.getInclude().contains(PARAM_INCLUDE_COUNT))
{
tagsByCount = taggingService.findTaggedNodesAndCountByTagName(storeRef);
List<Pair<String, Integer>> tagsByCount = taggingService.findTaggedNodesAndCountByTagName(storeRef);
Map<String, Integer> tagsByCountMap = new HashMap<>();
if (tagsByCount != null)
{
for (Pair<String, Integer> tagByCountElem : tagsByCount)
@@ -149,86 +190,149 @@ public class TagsImpl implements Tags
tagsByCountMap.put(tagByCountElem.getFirst(), tagByCountElem.getSecond());
}
}
}
for (Pair<NodeRef, String> pair : page)
{
Tag selectedTag = new Tag(pair.getFirst(), pair.getSecond());
selectedTag.setCount(tagsByCountMap.get(selectedTag.getTag()));
tags.add(selectedTag);
tags.forEach(tag -> tag.setCount(Optional.ofNullable(tagsByCountMap.get(tag.getTag())).orElse(0)));
}
return CollectionWithPagingInfo.asPaged(paging, tags, results.hasMoreItems(), (totalItems == null ? null : totalItems.intValue()));
return CollectionWithPagingInfo.asPaged(paging, tags, results.hasMoreItems(), totalItems);
}
public NodeRef validateTag(String tagId)
{
NodeRef tagNodeRef = nodes.validateNode(tagId);
if(tagNodeRef == null)
{
throw new EntityNotFoundException(tagId);
}
return tagNodeRef;
}
public NodeRef validateTag(StoreRef storeRef, String tagId)
{
NodeRef tagNodeRef = nodes.validateNode(storeRef, tagId);
if(tagNodeRef == null)
{
throw new EntityNotFoundException(tagId);
}
return tagNodeRef;
}
public Tag changeTag(StoreRef storeRef, String tagId, Tag tag)
{
try
{
NodeRef existingTagNodeRef = validateTag(storeRef, tagId);
String existingTagName = taggingService.getTagName(existingTagNodeRef);
String newTagName = tag.getTag();
NodeRef newTagNodeRef = taggingService.changeTag(storeRef, existingTagName, newTagName);
return new Tag(newTagNodeRef, newTagName);
}
catch(NonExistentTagException e)
{
throw new NotFoundException(e.getMessage());
}
catch(TagExistsException e)
{
throw new ConstraintViolatedException(e.getMessage());
}
catch(TaggingException e)
{
throw new InvalidArgumentException(e.getMessage());
}
}
public Tag getTag(String tagId)
{
return getTag(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, tagId);
}
public Tag getTag(StoreRef storeRef, String tagId)
{
NodeRef tagNodeRef = validateTag(storeRef, tagId);
String tagValue = taggingService.getTagName(tagNodeRef);
return new Tag(tagNodeRef, tagValue);
}
public CollectionWithPagingInfo<Tag> getTags(String nodeId, Parameters params)
{
NodeRef nodeRef = validateTag(nodeId);
PagingResults<Pair<NodeRef, String>> results = taggingService.getTags(nodeRef, Util.getPagingRequest(params.getPaging()));
Integer totalItems = results.getTotalResultCount().getFirst();
List<Pair<NodeRef, String>> page = results.getPage();
List<Tag> tags = new ArrayList<Tag>(page.size());
for(Pair<NodeRef, String> pair : page)
{
tags.add(new Tag(pair.getFirst(), pair.getSecond()));
}
return CollectionWithPagingInfo.asPaged(params.getPaging(), tags, results.hasMoreItems(), (totalItems == null ? null : totalItems.intValue()));
}
}
public NodeRef validateTag(String tagId)
{
NodeRef tagNodeRef = nodes.validateNode(tagId);
return checkTagRootAsNodePrimaryParent(tagId, tagNodeRef);
}
public NodeRef validateTag(StoreRef storeRef, String tagId)
{
NodeRef tagNodeRef = nodes.validateNode(storeRef, tagId);
return checkTagRootAsNodePrimaryParent(tagId, tagNodeRef);
}
public Tag changeTag(StoreRef storeRef, String tagId, Tag tag)
{
try
{
NodeRef existingTagNodeRef = validateTag(storeRef, tagId);
String existingTagName = taggingService.getTagName(existingTagNodeRef);
String newTagName = tag.getTag();
NodeRef newTagNodeRef = taggingService.changeTag(storeRef, existingTagName, newTagName);
return new Tag(newTagNodeRef, newTagName);
}
catch(NonExistentTagException e)
{
throw new NotFoundException(e.getMessage());
}
catch(TagExistsException e)
{
throw new ConstraintViolatedException(e.getMessage());
}
catch(TaggingException e)
{
throw new InvalidArgumentException(e.getMessage());
}
}
public Tag getTag(String tagId)
{
return getTag(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, tagId);
}
public Tag getTag(StoreRef storeRef, String tagId)
{
NodeRef tagNodeRef = validateTag(storeRef, tagId);
String tagValue = taggingService.getTagName(tagNodeRef);
return new Tag(tagNodeRef, tagValue);
}
public CollectionWithPagingInfo<Tag> getTags(String nodeId, Parameters params)
{
NodeRef nodeRef = nodes.validateOrLookupNode(nodeId);
PagingResults<Pair<NodeRef, String>> results = taggingService.getTags(nodeRef, Util.getPagingRequest(params.getPaging()));
Integer totalItems = results.getTotalResultCount().getFirst();
List<Pair<NodeRef, String>> page = results.getPage();
List<Tag> tags = new ArrayList<>(page.size());
for(Pair<NodeRef, String> pair : page)
{
tags.add(new Tag(pair.getFirst(), pair.getSecond()));
}
return CollectionWithPagingInfo.asPaged(params.getPaging(), tags, results.hasMoreItems(), (totalItems == null ? null : totalItems.intValue()));
}
@Experimental
@Override
public List<Tag> createTags(final StoreRef storeRef, final List<Tag> tags, final Parameters parameters)
{
verifyAdminAuthority();
final List<String> tagNames = Optional.ofNullable(tags).orElse(Collections.emptyList()).stream()
.filter(Objects::nonNull)
.map(Tag::getTag)
.distinct()
.collect(toList());
if (CollectionUtils.isEmpty(tagNames))
{
throw new InvalidArgumentException(NOT_A_VALID_TAG);
}
return taggingService.createTags(storeRef, tagNames).stream()
.map(pair -> Tag.builder().tag(pair.getFirst()).nodeRef(pair.getSecond()).create())
.peek(tag -> {
if (parameters.getInclude().contains(PARAM_INCLUDE_COUNT))
{
tag.setCount(0);
}
}).collect(toList());
}
private void verifyAdminAuthority()
{
if (!authorityService.hasAdminAuthority())
{
throw new PermissionDeniedException(NO_PERMISSION_TO_MANAGE_A_TAG);
}
}
/**
* Method resolves where query looking for clauses: EQUALS, IN or MATCHES.
* Expected values for EQUALS and IN will be merged under EQUALS clause.
* @param namesQuery Where query with expected tag name(s).
* @return Map of expected exact and alike tag names.
*/
private Map<Integer, Collection<String>> resolveTagNamesQuery(final Query namesQuery)
{
if (namesQuery == null || namesQuery == QueryImpl.EMPTY)
{
return Collections.emptyMap();
}
final Map<Integer, Collection<String>> properties = QueryHelper
.resolve(namesQuery)
.usingOrOperator()
.withoutNegations()
.getProperty(PARAM_WHERE_TAG)
.getExpectedValuesForAnyOf(EQUALS, IN, MATCHES)
.skipNegated();
return properties.entrySet().stream()
.collect(Collectors.groupingBy((entry) -> {
if (entry.getKey() == EQUALS || entry.getKey() == IN)
{
return EQUALS;
}
else
{
return MATCHES;
}
}, Collectors.flatMapping((entry) -> entry.getValue().stream().map(String::toLowerCase), Collectors.toCollection(HashSet::new))));
}
private NodeRef checkTagRootAsNodePrimaryParent(String tagId, NodeRef tagNodeRef)
{
if ( tagNodeRef == null || !nodeService.getPrimaryParent(tagNodeRef).getParentRef().equals(TAG_ROOT_NODE_REF))
{
throw new EntityNotFoundException(tagId);
}
return tagNodeRef;
}
}

View File

@@ -134,7 +134,7 @@ public class RestRuleModelMapper implements RestModelMapper<Rule, org.alfresco.s
public org.alfresco.service.cmr.rule.Rule toServiceModel(Rule restRuleModel)
{
final org.alfresco.service.cmr.rule.Rule serviceRule = new org.alfresco.service.cmr.rule.Rule();
final NodeRef nodeRef = (restRuleModel.getId() != null) ? nodes.validateOrLookupNode(restRuleModel.getId(), null) : null;
final NodeRef nodeRef = (restRuleModel.getId() != null) ? nodes.validateOrLookupNode(restRuleModel.getId()) : null;
serviceRule.setNodeRef(nodeRef);
serviceRule.setTitle(restRuleModel.getName());
serviceRule.setDescription(restRuleModel.getDescription());

View File

@@ -129,7 +129,7 @@ public class RestRuleSimpleConditionModelMapper implements RestModelMapper<Simpl
parameterValues.put(InCategoryEvaluator.PARAM_CATEGORY_ASPECT, ContentModel.ASPECT_GEN_CLASSIFIABLE);
try
{
parameterValues.put(InCategoryEvaluator.PARAM_CATEGORY_VALUE, nodes.validateOrLookupNode(parameter, null));
parameterValues.put(InCategoryEvaluator.PARAM_CATEGORY_VALUE, nodes.validateOrLookupNode(parameter));
} catch (EntityNotFoundException e) {
throw new InvalidArgumentException(CATEGORY_INVALID_MSG);
}

View File

@@ -173,7 +173,7 @@ public class ActionParameterConverter
}
else if (typeQName.isMatch(DataTypeDefinition.NODE_REF))
{
NodeRef nodeRef = nodes.validateOrLookupNode(stringValue, null);
NodeRef nodeRef = nodes.validateOrLookupNode(stringValue);
if (permissionService.hasReadPermission(nodeRef) != ALLOWED)
{
throw new EntityNotFoundException(stringValue);

View File

@@ -72,7 +72,7 @@ public class NodeValidator
{
try
{
final NodeRef nodeRef = nodes.validateOrLookupNode(folderNodeId, null);
final NodeRef nodeRef = nodes.validateOrLookupNode(folderNodeId);
validatePermission(requireChangePermission, nodeRef);
verifyNodeType(nodeRef, ContentModel.TYPE_FOLDER, null);

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* Copyright (C) 2005 - 2023 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -34,6 +34,7 @@ public class Category
private String name;
private String parentId;
private boolean hasChildren;
private Integer count;
public String getId()
{
@@ -80,20 +81,38 @@ public class Category
this.hasChildren = hasChildren;
}
public Integer getCount()
{
return count;
}
public void setCount(Integer count)
{
this.count = count;
}
@Override
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Category category = (Category) o;
return hasChildren == category.hasChildren && Objects.equals(id, category.id) && name.equals(category.name) &&
Objects.equals(parentId, category.parentId);
return hasChildren == category.hasChildren && Objects.equals(id, category.id) && Objects.equals(name, category.name) && Objects.equals(parentId, category.parentId)
&& Objects.equals(count, category.count);
}
@Override
public int hashCode()
{
return Objects.hash(id, name, parentId, hasChildren);
return Objects.hash(id, name, parentId, hasChildren, count);
}
@Override
public String toString()
{
return "Category{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", parentId='" + parentId + '\'' + ", hasChildren=" + hasChildren + ", count=" + count + '}';
}
public static Builder builder()
@@ -107,6 +126,7 @@ public class Category
private String name;
private String parentId;
private boolean hasChildren;
private Integer count;
public Builder id(String id)
{
@@ -132,6 +152,12 @@ public class Category
return this;
}
public Builder count(Integer count)
{
this.count = count;
return this;
}
public Category create()
{
final Category category = new Category();
@@ -139,6 +165,7 @@ public class Category
category.setName(name);
category.setParentId(parentId);
category.setHasChildren(hasChildren);
category.setCount(count);
return category;
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2023 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -25,6 +25,9 @@
*/
package org.alfresco.rest.api.model;
import java.util.Objects;
import java.util.Optional;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.alfresco.rest.framework.resource.UniqueId;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -48,7 +51,7 @@ public class Tag implements Comparable<Tag>
public Tag(NodeRef nodeRef, String tag)
{
this.nodeRef = nodeRef;
this.tag = tag;
setTag(tag);
}
@JsonProperty("id")
@@ -70,7 +73,7 @@ public class Tag implements Comparable<Tag>
public void setTag(String tag)
{
this.tag = tag;
this.tag = Optional.ofNullable(tag).map(String::toLowerCase).orElse(null);
}
public Integer getCount()
@@ -99,42 +102,64 @@ public class Tag implements Comparable<Tag>
}
@Override
public int hashCode()
public boolean equals(Object o)
{
final int prime = 31;
int result = 1;
result = prime * result + ((nodeRef == null) ? 0 : nodeRef.hashCode());
return result;
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Tag tag1 = (Tag) o;
return Objects.equals(nodeRef, tag1.nodeRef) && Objects.equals(tag, tag1.tag) && Objects.equals(count, tag1.count);
}
/*
* Tags are equal if they have the same NodeRef
*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj)
public int hashCode()
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Tag other = (Tag) obj;
if (nodeRef == null) {
if (other.nodeRef != null)
return false;
} else if (!nodeRef.equals(other.nodeRef))
return false;
return true;
return Objects.hash(nodeRef, tag, count);
}
@Override
public String toString()
{
return "Tag [nodeRef=" + nodeRef + ", tag=" + tag + "]";
return "Tag{" + "nodeRef=" + nodeRef + ", tag='" + tag + '\'' + ", count=" + count + '}';
}
public static Builder builder()
{
return new Builder();
}
public static class Builder
{
private NodeRef nodeRef;
private String tag;
private Integer count;
public Builder nodeRef(NodeRef nodeRef)
{
this.nodeRef = nodeRef;
return this;
}
public Builder tag(String tag)
{
this.tag = tag;
return this;
}
public Builder count(Integer count)
{
this.count = count;
return this;
}
public Tag create()
{
final Tag tag = new Tag();
tag.setNodeRef(nodeRef);
tag.setTag(this.tag);
tag.setCount(count);
return tag;
}
}
}

View File

@@ -31,13 +31,9 @@ import org.alfresco.rest.framework.resource.RelationshipResource;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rest.framework.resource.parameters.SortColumn;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.ParameterCheck;
import java.util.List;
import java.util.stream.Collectors;
@RelationshipResource(name = "action-definitions", entityResource = NodesEntityResource.class, title = "Node action definitions")
public class NodeActionDefinitionsRelation extends AbstractNodeRelation
implements RelationshipResourceAction.Read<ActionDefinition>
@@ -59,7 +55,7 @@ public class NodeActionDefinitionsRelation extends AbstractNodeRelation
@Override
public CollectionWithPagingInfo<ActionDefinition> readAll(String entityResourceId, Parameters params)
{
NodeRef parentNodeRef = nodes.validateOrLookupNode(entityResourceId, null);
NodeRef parentNodeRef = nodes.validateOrLookupNode(entityResourceId);
return actions.getActionDefinitions(parentNodeRef, params);
}
}

View File

@@ -25,6 +25,11 @@
*/
package org.alfresco.rest.api.nodes;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.alfresco.rest.antlr.WhereClauseParser;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.model.Node;
@@ -41,11 +46,6 @@ import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QNamePattern;
import org.alfresco.service.namespace.RegexQNamePattern;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Node Parents
*
@@ -67,7 +67,7 @@ public class NodeParentsRelation extends AbstractNodeRelation implements Relatio
@WebApiDescription(title = "Return a list of parent nodes based on child assocs")
public CollectionWithPagingInfo<Node> readAll(String childNodeId, Parameters parameters)
{
NodeRef childNodeRef = nodes.validateOrLookupNode(childNodeId, null);
NodeRef childNodeRef = nodes.validateOrLookupNode(childNodeId);
QNamePattern assocTypeQNameParam = RegexQNamePattern.MATCH_ALL;

View File

@@ -25,6 +25,8 @@
*/
package org.alfresco.rest.api.nodes;
import java.util.List;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.model.AssocChild;
import org.alfresco.rest.api.model.Node;
@@ -41,8 +43,6 @@ import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.QNamePattern;
import org.alfresco.service.namespace.RegexQNamePattern;
import java.util.List;
/**
* Node Secondary Children
*
@@ -71,7 +71,7 @@ public class NodeSecondaryChildrenRelation extends AbstractNodeRelation implemen
@WebApiDescription(title = "Return a paged list of secondary child nodes based on child assocs")
public CollectionWithPagingInfo<Node> readAll(String parentNodeId, Parameters parameters)
{
NodeRef parentNodeRef = nodes.validateOrLookupNode(parentNodeId, null);
NodeRef parentNodeRef = nodes.validateOrLookupNode(parentNodeId);
QNamePattern assocTypeQNameParam = getAssocTypeFromWhereElseAll(parameters);

View File

@@ -25,6 +25,8 @@
*/
package org.alfresco.rest.api.nodes;
import java.util.List;
import org.alfresco.rest.api.model.Node;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.resource.RelationshipResource;
@@ -35,8 +37,6 @@ import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QNamePattern;
import java.util.List;
/**
* Node Sources - list node (peer) associations from target to sources
*
@@ -54,7 +54,7 @@ public class NodeSourcesRelation extends AbstractNodeRelation implements Relatio
@WebApiDescription(title = "Return a paged list of sources nodes based on (peer) assocs")
public CollectionWithPagingInfo<Node> readAll(String targetNodeId, Parameters parameters)
{
NodeRef targetNodeRef = nodes.validateOrLookupNode(targetNodeId, null);
NodeRef targetNodeRef = nodes.validateOrLookupNode(targetNodeId);
QNamePattern assocTypeQNameParam = getAssocTypeFromWhereElseAll(parameters);

View File

@@ -1,28 +1,28 @@
/*
* #%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%
*/
/*
* #%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.nodes;
import java.util.List;
@@ -61,7 +61,7 @@ public class NodeTagsRelation implements RelationshipResourceAction.Create<Tag>,
@WebApiDescription(title="Adds one or more tags to the node with id 'nodeId'.")
public List<Tag> create(String nodeId, List<Tag> tagsToCreate, Parameters parameters)
{
return tags.addTags(nodeId, tagsToCreate);
return tags.addTags(nodeId, tagsToCreate, parameters);
}
@Override

View File

@@ -25,6 +25,8 @@
*/
package org.alfresco.rest.api.nodes;
import java.util.List;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.model.AssocTarget;
import org.alfresco.rest.api.model.Node;
@@ -40,8 +42,6 @@ import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.QNamePattern;
import org.alfresco.service.namespace.RegexQNamePattern;
import java.util.List;
/**
* Node Targets
*
@@ -64,7 +64,7 @@ public class NodeTargetsRelation extends AbstractNodeRelation implements
@WebApiDescription(title = "Return a paged list of target nodes based on (peer) assocs")
public CollectionWithPagingInfo<Node> readAll(String sourceNodeId, Parameters parameters)
{
NodeRef sourceNodeRef = nodes.validateOrLookupNode(sourceNodeId, null);
NodeRef sourceNodeRef = nodes.validateOrLookupNode(sourceNodeId);
QNamePattern assocTypeQNameParam = getAssocTypeFromWhereElseAll(parameters);

View File

@@ -25,6 +25,13 @@
*/
package org.alfresco.rest.api.nodes;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.directurl.DirectAccessUrlDisabledException;
import org.alfresco.repo.node.integrity.IntegrityException;
@@ -65,13 +72,6 @@ import org.alfresco.util.ParameterCheck;
import org.alfresco.util.PropertyCheck;
import org.springframework.beans.factory.InitializingBean;
import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Node Versions - version history
*
@@ -117,7 +117,7 @@ public class NodeVersionsRelation extends AbstractNodeRelation implements
@WebApiDescription(title = "Return version history as a paged list of version node infos")
public CollectionWithPagingInfo<Node> readAll(String nodeId, Parameters parameters)
{
NodeRef nodeRef = nodes.validateOrLookupNode(nodeId, null);
NodeRef nodeRef = nodes.validateOrLookupNode(nodeId);
VersionHistory vh = versionService.getVersionHistory(nodeRef);
@@ -293,7 +293,7 @@ public class NodeVersionsRelation extends AbstractNodeRelation implements
public Version findVersion(String nodeId, String versionLabelId)
{
NodeRef nodeRef = nodes.validateOrLookupNode(nodeId, null);
NodeRef nodeRef = nodes.validateOrLookupNode(nodeId);
VersionHistory vh = versionService.getVersionHistory(nodeRef);
if (vh != null)
{

View File

@@ -1,30 +1,33 @@
/*
* #%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%
*/
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2023 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.tags;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import org.alfresco.rest.api.Tags;
import org.alfresco.rest.api.model.Tag;
import org.alfresco.rest.framework.WebApiDescription;
@@ -33,12 +36,14 @@ import org.alfresco.rest.framework.resource.EntityResource;
import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.util.ParameterCheck;
import org.springframework.beans.factory.InitializingBean;
@EntityResource(name="tags", title = "Tags")
public class TagsEntityResource implements EntityResourceAction.Read<Tag>, EntityResourceAction.ReadById<Tag>, EntityResourceAction.Update<Tag>, InitializingBean
public class TagsEntityResource implements EntityResourceAction.Read<Tag>,
EntityResourceAction.ReadById<Tag>, EntityResourceAction.Update<Tag>, EntityResourceAction.Create<Tag>, EntityResourceAction.Delete, InitializingBean
{
private Tags tags;
@@ -54,9 +59,8 @@ public class TagsEntityResource implements EntityResourceAction.Read<Tag>, Entit
}
/**
*
* Returns a paged list of all currently used tags in the store workspace://SpacesStore for the current tenant.
*
* GET /tags
*/
@Override
@WebApiDescription(title="A paged list of all tags in the network.")
@@ -77,4 +81,25 @@ public class TagsEntityResource implements EntityResourceAction.Read<Tag>, Entit
{
return tags.getTag(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, id);
}
/**
* POST /tags
*/
@Experimental
@WebApiDescription(
title = "Create an orphan tag",
description = "Creates a tag, which is not associated with any node",
successStatus = HttpServletResponse.SC_CREATED
)
@Override
public List<Tag> create(List<Tag> tags, Parameters parameters)
{
return this.tags.createTags(tags, parameters);
}
@Override
public void delete(String id, Parameters parameters)
{
tags.deleteTagById(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, id);
}
}

View File

@@ -0,0 +1,259 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2023 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.framework.resource.parameters.where;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.alfresco.rest.antlr.WhereClauseParser;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
/**
* Basic implementation of {@link QueryHelper.WalkerCallbackAdapter} providing universal handling of Where query clauses.
* This implementation supports AND operator and all clause types.
* Be default, walker verifies strictly if expected or unexpected properties, and it's comparison types are present in query
* and throws {@link InvalidQueryException} if they are missing.
*/
public class BasicQueryWalker extends QueryHelper.WalkerCallbackAdapter
{
private static final String EQUALS_AND_IN_NOT_ALLOWED_TOGETHER = "Where query error: cannot use '=' (EQUALS) AND 'IN' clauses with same property: %s";
private static final String MISSING_PROPERTY = "Where query error: property with name: %s not present";
static final String MISSING_CLAUSE_TYPE = "Where query error: property with name: %s expects clause: %s";
static final String MISSING_ANY_CLAUSE_OF_TYPE = "Where query error: property with name: %s expects at least one of clauses: %s";
private static final String PROPERTY_NOT_EXPECTED = "Where query error: property with name: %s is not expected";
private static final String PROPERTY_NOT_NEGATABLE = "Where query error: property with name: %s cannot be negated";
private static final String PROPERTY_NAMES_EMPTY = "Cannot verify WHERE query without expected property names";
private Collection<String> expectedPropertyNames;
private final Map<String, WhereProperty> properties;
protected boolean clausesNegatable = true;
protected boolean validateStrictly = true;
public BasicQueryWalker()
{
this.properties = new HashMap<>();
}
public BasicQueryWalker(final String... expectedPropertyNames)
{
this();
this.expectedPropertyNames = Set.of(expectedPropertyNames);
}
public BasicQueryWalker(final Collection<String> expectedPropertyNames)
{
this();
this.expectedPropertyNames = expectedPropertyNames;
}
public void setClausesNegatable(final boolean clausesNegatable)
{
this.clausesNegatable = clausesNegatable;
}
public void setValidateStrictly(boolean validateStrictly)
{
this.validateStrictly = validateStrictly;
}
@Override
public void exists(String propertyName, boolean negated)
{
verifyPropertyExpectedness(propertyName);
verifyClausesNegatability(negated, propertyName);
addProperties(propertyName, WhereClauseParser.EXISTS, negated);
}
@Override
public void between(String propertyName, String firstValue, String secondValue, boolean negated)
{
verifyPropertyExpectedness(propertyName);
verifyClausesNegatability(negated, propertyName);
addProperties(propertyName, WhereClauseParser.BETWEEN, negated, firstValue, secondValue);
}
@Override
public void comparison(int type, String propertyName, String propertyValue, boolean negated)
{
verifyPropertyExpectedness(propertyName);
verifyClausesNegatability(negated, propertyName);
if (WhereClauseParser.EQUALS == type && isAndSupported() && containsProperty(propertyName, WhereClauseParser.IN, negated))
{
throw new InvalidQueryException(String.format(EQUALS_AND_IN_NOT_ALLOWED_TOGETHER, propertyName));
}
addProperties(propertyName, type, negated, propertyValue);
}
@Override
public void in(String propertyName, boolean negated, String... propertyValues)
{
verifyPropertyExpectedness(propertyName);
verifyClausesNegatability(negated, propertyName);
if (isAndSupported() && containsProperty(propertyName, WhereClauseParser.EQUALS, negated))
{
throw new InvalidQueryException(String.format(EQUALS_AND_IN_NOT_ALLOWED_TOGETHER, propertyName));
}
addProperties(propertyName, WhereClauseParser.IN, negated, propertyValues);
}
@Override
public void matches(final String propertyName, String propertyValue, boolean negated)
{
verifyPropertyExpectedness(propertyName);
verifyClausesNegatability(negated, propertyName);
addProperties(propertyName, WhereClauseParser.MATCHES, negated, propertyValue);
}
@Override
public void and()
{
// Don't need to do anything here - it's enough to enable AND operator.
// OR is not supported at the same time.
}
/**
* Verify if property is expected, if not throws {@link InvalidQueryException}.
*/
protected void verifyPropertyExpectedness(final String propertyName)
{
if (validateStrictly && CollectionUtils.isNotEmpty(expectedPropertyNames) && !this.expectedPropertyNames.contains(propertyName))
{
throw new InvalidQueryException(String.format(PROPERTY_NOT_EXPECTED, propertyName));
}
else if (validateStrictly && CollectionUtils.isEmpty(expectedPropertyNames))
{
throw new IllegalStateException(PROPERTY_NAMES_EMPTY);
}
}
/**
* Verify if clause negations are allowed, if not throws {@link InvalidQueryException}.
*/
protected void verifyClausesNegatability(final boolean negated, final String propertyName)
{
if (!clausesNegatable && negated)
{
throw new InvalidQueryException(String.format(PROPERTY_NOT_NEGATABLE, propertyName));
}
}
protected boolean isAndSupported()
{
try
{
and();
return true;
}
catch (InvalidQueryException ignore)
{
return false;
}
}
protected void addProperties(final String propertyName, final int clauseType, final String... propertyValues)
{
this.addProperties(propertyName, clauseType, false, propertyValues);
}
protected void addProperties(final String propertyName, final int clauseType, final boolean negated, final String... propertyValues)
{
final WhereProperty.ClauseType type = WhereProperty.ClauseType.of(clauseType, negated);
final Set<String> propertiesToAdd = Optional.ofNullable(propertyValues).map(Set::of).orElse(Collections.emptySet());
if (this.containsProperty(propertyName))
{
this.properties.get(propertyName).addValuesToType(type, propertiesToAdd);
}
else
{
this.properties.put(propertyName, new WhereProperty(propertyName, type, propertiesToAdd, validateStrictly));
}
}
protected boolean containsProperty(final String propertyName)
{
return this.properties.containsKey(propertyName);
}
protected boolean containsProperty(final String propertyName, final int clauseType, final boolean negated)
{
return this.properties.containsKey(propertyName) && this.properties.get(propertyName).containsType(clauseType, negated);
}
@Override
public Collection<String> getProperty(String propertyName, int type, boolean negated)
{
return this.getProperty(propertyName).getExpectedValuesFor(type, negated);
}
public WhereProperty getProperty(final String propertyName)
{
if (validateStrictly && !this.containsProperty(propertyName))
{
throw new InvalidQueryException(String.format(MISSING_PROPERTY, propertyName));
}
return this.properties.get(propertyName);
}
public List<WhereProperty> getProperties(final String... propertyNames)
{
return Arrays.stream(propertyNames)
.filter(StringUtils::isNotBlank)
.distinct()
.peek(propertyName -> {
if (validateStrictly && !this.containsProperty(propertyName))
{
throw new InvalidQueryException(String.format(MISSING_PROPERTY, propertyName));
}
})
.map(this.properties::get)
.collect(Collectors.toList());
}
public Map<String, WhereProperty> getPropertiesAsMap(final String... propertyNames)
{
return Arrays.stream(propertyNames)
.filter(StringUtils::isNotBlank)
.distinct()
.peek(propertyName -> {
if (validateStrictly && !this.containsProperty(propertyName))
{
throw new InvalidQueryException(String.format(MISSING_PROPERTY, propertyName));
}
})
.collect(Collectors.toMap(propertyName -> propertyName, this.properties::get));
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2023 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -25,10 +25,19 @@
*/
package org.alfresco.rest.framework.resource.parameters.where;
import static org.alfresco.rest.antlr.WhereClauseParser.BETWEEN;
import static org.alfresco.rest.antlr.WhereClauseParser.EQUALS;
import static org.alfresco.rest.antlr.WhereClauseParser.EXISTS;
import static org.alfresco.rest.antlr.WhereClauseParser.IN;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import org.alfresco.rest.antlr.WhereClauseParser;
import org.antlr.runtime.tree.CommonTree;
@@ -45,14 +54,19 @@ public abstract class QueryHelper
/**
* An interface used when walking a query tree. Calls are made to methods when the particular clause is encountered.
*/
public static interface WalkerCallback
public interface WalkerCallback
{
InvalidQueryException UNSUPPORTED = new InvalidQueryException("Unsupported Predicate");
/**
* Called any time an EXISTS clause is encountered.
* @param propertyName Name of the property
* @param negated returns true if "NOT EXISTS" was used
*/
void exists(String propertyName, boolean negated);
default void exists(String propertyName, boolean negated)
{
throw UNSUPPORTED;
}
/**
* Called any time a BETWEEN clause is encountered.
@@ -61,12 +75,18 @@ public abstract class QueryHelper
* @param secondValue String
* @param negated returns true if "NOT BETWEEN" was used
*/
void between(String propertyName, String firstValue, String secondValue, boolean negated);
default void between(String propertyName, String firstValue, String secondValue, boolean negated)
{
throw UNSUPPORTED;
}
/**
* One of EQUALS LESSTHAN GREATERTHAN LESSTHANOREQUALS GREATERTHANOREQUALS;
*/
void comparison(int type, String propertyName, String propertyValue, boolean negated);
default void comparison(int type, String propertyName, String propertyValue, boolean negated)
{
throw UNSUPPORTED;
}
/**
* Called any time an IN clause is encountered.
@@ -74,7 +94,10 @@ public abstract class QueryHelper
* @param negated returns true if "NOT IN" was used
* @param propertyValues the property values
*/
void in(String property, boolean negated, String... propertyValues);
default void in(String property, boolean negated, String... propertyValues)
{
throw UNSUPPORTED;
}
/**
* Called any time a MATCHES clause is encountered.
@@ -82,42 +105,37 @@ public abstract class QueryHelper
* @param propertyValue String
* @param negated returns true if "NOT MATCHES" was used
*/
void matches(String property, String propertyValue, boolean negated);
default void matches(String property, String propertyValue, boolean negated)
{
throw UNSUPPORTED;
}
/**
* Called any time an AND is encountered.
*/
void and();
*/
default void and()
{
throw UNSUPPORTED;
}
/**
* Called any time an OR is encountered.
*/
void or();
*/
default void or()
{
throw UNSUPPORTED;
}
default Collection<String> getProperty(String propertyName, int type, boolean negated)
{
throw UNSUPPORTED;
}
}
/**
* Default implementation. Override the methods you are interested in. If you don't
* override the methods then an InvalidQueryException will be thrown.
*/
public static class WalkerCallbackAdapter implements WalkerCallback
{
private static final String UNSUPPORTED_TEXT = "Unsupported Predicate";
protected static final InvalidQueryException UNSUPPORTED = new InvalidQueryException(UNSUPPORTED_TEXT);
@Override
public void exists(String propertyName, boolean negated) { throw UNSUPPORTED;}
@Override
public void between(String propertyName, String firstValue, String secondValue, boolean negated) { throw UNSUPPORTED;}
@Override
public void comparison(int type, String propertyName, String propertyValue, boolean negated) { throw UNSUPPORTED;}
@Override
public void in(String propertyName, boolean negated, String... propertyValues) { throw UNSUPPORTED;}
@Override
public void matches(String property, String value, boolean negated) { throw UNSUPPORTED;}
@Override
public void and() {throw UNSUPPORTED;}
@Override
public void or() {throw UNSUPPORTED;}
}
public static class WalkerCallbackAdapter implements WalkerCallback {}
/**
* Walks a query with a callback for each operation
@@ -146,7 +164,7 @@ public abstract class QueryHelper
if (tree != null)
{
switch (tree.getType()) {
case WhereClauseParser.EXISTS:
case EXISTS:
if (WhereClauseParser.PROPERTYNAME == tree.getChild(0).getType())
{
callback.exists(tree.getChild(0).getText(), negated);
@@ -160,7 +178,7 @@ public abstract class QueryHelper
return;
}
break;
case WhereClauseParser.IN:
case IN:
if (WhereClauseParser.PROPERTYNAME == tree.getChild(0).getType())
{
List<Tree> children = getChildren(tree);
@@ -174,14 +192,14 @@ public abstract class QueryHelper
return;
}
break;
case WhereClauseParser.BETWEEN:
case BETWEEN:
if (WhereClauseParser.PROPERTYNAME == tree.getChild(0).getType())
{
callback.between(tree.getChild(0).getText(), stripQuotes(tree.getChild(1).getText()), stripQuotes(tree.getChild(2).getText()), negated);
return;
}
break;
case WhereClauseParser.EQUALS: //fall through (comparison)
case EQUALS: //fall through (comparison)
case WhereClauseParser.LESSTHAN: //fall through (comparison)
case WhereClauseParser.GREATERTHAN: //fall through (comparison)
case WhereClauseParser.LESSTHANOREQUALS: //fall through (comparison)
@@ -286,4 +304,180 @@ public abstract class QueryHelper
}
return toBeStripped; //default to return the String unchanged.
}
public static QueryResolver.WalkerSpecifier resolve(final Query query)
{
return new QueryResolver.WalkerSpecifier(query);
}
/**
* Helper class allowing WHERE query resolving using query walker. By default {@link BasicQueryWalker} is used, but different walker can be supplied.
*/
public static abstract class QueryResolver<S extends QueryResolver<?>>
{
private final Query query;
protected WalkerCallback queryWalker;
protected Function<Collection<String>, BasicQueryWalker> orQueryWalkerSupplier;
protected boolean clausesNegatable = true;
protected boolean validateLeniently = false;
protected abstract S self();
public QueryResolver(Query query)
{
this.query = query;
}
/**
* Get property expected values.
* @param propertyName Property name.
* @param clauseType Property comparison type.
* @param negated Comparison type negation.
* @return Map composed of all comparators and compared values.
*/
public Collection<String> getProperty(final String propertyName, final int clauseType, final boolean negated)
{
processQuery(propertyName);
return queryWalker.getProperty(propertyName, clauseType, negated);
}
protected void processQuery(final String... propertyNames)
{
if (queryWalker == null)
{
if (orQueryWalkerSupplier != null)
{
queryWalker = orQueryWalkerSupplier.apply(Set.of(propertyNames));
}
else
{
queryWalker = new BasicQueryWalker(propertyNames);
}
}
if (queryWalker instanceof BasicQueryWalker)
{
((BasicQueryWalker) queryWalker).setClausesNegatable(clausesNegatable);
((BasicQueryWalker) queryWalker).setValidateStrictly(!validateLeniently);
}
walk(query, queryWalker);
}
/**
* Helper class providing methods related with default query walker {@link BasicQueryWalker}.
*/
public static class DefaultWalkerOperations<R extends DefaultWalkerOperations<?>> extends QueryResolver<R>
{
public DefaultWalkerOperations(Query query)
{
super(query);
}
@SuppressWarnings("unchecked")
@Override
protected R self()
{
return (R) this;
}
/**
* Specifies that query properties and comparison types should NOT be verified strictly.
*/
public R leniently()
{
this.validateLeniently = true;
return self();
}
/**
* Specifies that clause types negations are not allowed in query.
*/
public R withoutNegations()
{
this.clausesNegatable = false;
return self();
}
/**
* Get property with expected values.
* @param propertyName Property name.
* @return Map composed of all comparators and compared values.
*/
public WhereProperty getProperty(final String propertyName)
{
processQuery(propertyName);
return ((BasicQueryWalker) this.queryWalker).getProperty(propertyName);
}
/**
* Get multiple properties with it's expected values.
* @param propertyNames Property names.
* @return List of maps composed of all comparators and compared values.
*/
public List<WhereProperty> getProperties(final String... propertyNames)
{
processQuery(propertyNames);
return ((BasicQueryWalker) this.queryWalker).getProperties(propertyNames);
}
/**
* Get multiple properties with it's expected values.
* @param propertyNames Property names.
* @return Map composed of property names and maps composed of all comparators and compared values.
*/
public Map<String, WhereProperty> getPropertiesAsMap(final String... propertyNames)
{
processQuery(propertyNames);
return ((BasicQueryWalker) this.queryWalker).getPropertiesAsMap(propertyNames);
}
}
/**
* Helper class allowing to specify custom {@link WalkerCallback} implementation or {@link BasicQueryWalker} extension.
*/
public static class WalkerSpecifier extends DefaultWalkerOperations<WalkerSpecifier>
{
public WalkerSpecifier(Query query)
{
super(query);
}
@Override
protected WalkerSpecifier self()
{
return this;
}
/**
* Specifies that OR operator instead of AND should be used while resolving the query.
*/
public DefaultWalkerOperations<? extends DefaultWalkerOperations<?>> usingOrOperator()
{
this.orQueryWalkerSupplier = (propertyNames) -> new BasicQueryWalker(propertyNames)
{
@Override
public void or() {/*Enable OR support, disable AND support*/}
@Override
public void and() {throw UNSUPPORTED;}
};
return this;
}
/**
* Allows to specify custom {@link BasicQueryWalker} extension, which should be used to resolve the query.
*/
public <T extends BasicQueryWalker> DefaultWalkerOperations<? extends DefaultWalkerOperations<?>> usingWalker(final T queryWalker)
{
this.queryWalker = queryWalker;
return this;
}
/**
* Allows to specify custom {@link WalkerCallback} implementation, which should be used to resolve the query.
*/
public <T extends WalkerCallback> QueryResolver<? extends QueryResolver<?>> usingWalker(final T queryWalker)
{
this.queryWalker = queryWalker;
return this;
}
}
}
}

View File

@@ -0,0 +1,351 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2023 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.framework.resource.parameters.where;
import static java.util.function.Predicate.not;
import static org.alfresco.rest.framework.resource.parameters.where.BasicQueryWalker.MISSING_ANY_CLAUSE_OF_TYPE;
import static org.alfresco.rest.framework.resource.parameters.where.BasicQueryWalker.MISSING_CLAUSE_TYPE;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.alfresco.rest.antlr.WhereClauseParser;
/**
* Map composed of property comparison type and compared values.
* Map key is clause (comparison) type.
*/
public class WhereProperty extends HashMap<WhereProperty.ClauseType, Collection<String>>
{
private final String name;
private boolean validateStrictly;
public WhereProperty(final String name, final ClauseType clauseType, final Collection<String> values)
{
super(Map.of(clauseType, new HashSet<>(values)));
this.name = name;
this.validateStrictly = true;
}
public WhereProperty(final String name, final ClauseType clauseType, final Collection<String> values, final boolean validateStrictly)
{
this(name, clauseType, values);
this.validateStrictly = validateStrictly;
}
public String getName()
{
return name;
}
public void addValuesToType(final ClauseType clauseType, final Collection<String> values)
{
if (this.containsKey(clauseType))
{
this.get(clauseType).addAll(values);
}
else
{
this.put(clauseType, new HashSet<>(values));
}
}
public boolean containsType(final ClauseType clauseType)
{
return this.containsKey(clauseType);
}
public boolean containsType(final int clauseType, final boolean negated)
{
return this.containsKey(ClauseType.of(clauseType, negated));
}
public boolean containsAllTypes(final ClauseType... clauseType)
{
return Arrays.stream(clauseType).distinct().filter(this::containsKey).count() == clauseType.length;
}
public boolean containsAnyOfTypes(final ClauseType... clauseType)
{
return Arrays.stream(clauseType).distinct().anyMatch(this::containsKey);
}
public Collection<String> getExpectedValuesFor(final ClauseType clauseType)
{
verifyAllClausesPresence(clauseType);
return this.get(clauseType);
}
public HashMap<ClauseType, Collection<String>> getExpectedValuesForAllOf(final ClauseType... clauseTypes)
{
verifyAllClausesPresence(clauseTypes);
return Arrays.stream(clauseTypes)
.distinct()
.collect(Collectors.toMap(type -> type, this::get, (type1, type2) -> type1, MultiTypeNegatableValuesMap::new));
}
public HashMap<ClauseType, Collection<String>> getExpectedValuesForAnyOf(final ClauseType... clauseTypes)
{
verifyAnyClausesPresence(clauseTypes);
return Arrays.stream(clauseTypes)
.distinct()
.collect(Collectors.toMap(type -> type, this::get, (type1, type2) -> type1, MultiTypeNegatableValuesMap::new));
}
public Collection<String> getExpectedValuesFor(final int clauseType, final boolean negated)
{
verifyAllClausesPresence(ClauseType.of(clauseType, negated));
return this.get(ClauseType.of(clauseType, negated));
}
public NegatableValuesMap getExpectedValuesFor(final int clauseType)
{
verifyAllClausesPresence(clauseType);
final NegatableValuesMap values = new NegatableValuesMap();
final ClauseType type = ClauseType.of(clauseType);
final ClauseType negatedType = type.negate();
if (this.containsKey(type))
{
values.put(false, this.get(type));
}
if (this.containsKey(negatedType))
{
values.put(true, this.get(negatedType));
}
return values;
}
public MultiTypeNegatableValuesMap getExpectedValuesForAllOf(final int... clauseTypes)
{
verifyAllClausesPresence(clauseTypes);
return getExpectedValuesFor(clauseTypes);
}
public MultiTypeNegatableValuesMap getExpectedValuesForAnyOf(final int... clauseTypes)
{
verifyAnyClausesPresence(clauseTypes);
return getExpectedValuesFor(clauseTypes);
}
private MultiTypeNegatableValuesMap getExpectedValuesFor(final int... clauseTypes)
{
final MultiTypeNegatableValuesMap values = new MultiTypeNegatableValuesMap();
Arrays.stream(clauseTypes).distinct().forEach(clauseType -> {
final ClauseType type = ClauseType.of(clauseType);
final ClauseType negatedType = type.negate();
if (this.containsKey(type))
{
values.put(type, this.get(type));
}
if (this.containsKey(negatedType))
{
values.put(negatedType, this.get(negatedType));
}
});
return values;
}
/**
* Verify if all specified clause types are present in this map, if not than throw {@link InvalidQueryException}.
*/
private void verifyAllClausesPresence(final ClauseType... clauseTypes)
{
if (validateStrictly)
{
Arrays.stream(clauseTypes).distinct().forEach(clauseType -> {
if (!this.containsType(clauseType))
{
throw new InvalidQueryException(String.format(MISSING_CLAUSE_TYPE, this.name, WhereClauseParser.tokenNames[clauseType.getTypeNumber()]));
}
});
}
}
/**
* Verify if all specified clause types are present in this map, if not than throw {@link InvalidQueryException}.
* Exception is thrown when both, negated and non-negated types are missing.
*/
private void verifyAllClausesPresence(final int... clauseTypes)
{
if (validateStrictly)
{
Arrays.stream(clauseTypes).distinct().forEach(clauseType -> {
if (!this.containsType(clauseType, false) && !this.containsType(clauseType, true))
{
throw new InvalidQueryException(String.format(MISSING_CLAUSE_TYPE, this.name, WhereClauseParser.tokenNames[clauseType]));
}
});
}
}
/**
* Verify if any of specified clause types are present in this map, if not than throw {@link InvalidQueryException}.
*/
private void verifyAnyClausesPresence(final ClauseType... clauseTypes)
{
if (validateStrictly)
{
if (!this.containsAnyOfTypes(clauseTypes))
{
throw new InvalidQueryException(String.format(MISSING_ANY_CLAUSE_OF_TYPE,
this.name, Arrays.stream(clauseTypes).map(type -> WhereClauseParser.tokenNames[type.getTypeNumber()]).collect(Collectors.toList())));
}
}
}
/**
* Verify if any of specified clause types are present in this map, if not than throw {@link InvalidQueryException}.
* Exception is thrown when both, negated and non-negated types are missing.
*/
private void verifyAnyClausesPresence(final int... clauseTypes)
{
if (validateStrictly)
{
final Collection<ClauseType> expectedTypes = Arrays.stream(clauseTypes)
.distinct()
.boxed()
.flatMap(type -> Stream.of(ClauseType.of(type), ClauseType.of(type, true)))
.collect(Collectors.toSet());
if (!this.containsAnyOfTypes(expectedTypes.toArray(ClauseType[]::new)))
{
throw new InvalidQueryException(String.format(MISSING_ANY_CLAUSE_OF_TYPE,
this.name, Arrays.stream(clauseTypes).mapToObj(type -> WhereClauseParser.tokenNames[type]).collect(Collectors.toList())));
}
}
}
public enum ClauseType
{
EQUALS(WhereClauseParser.EQUALS),
NOT_EQUALS(WhereClauseParser.EQUALS, true),
GREATER_THAN(WhereClauseParser.GREATERTHAN),
NOT_GREATER_THAN(WhereClauseParser.GREATERTHAN, true),
LESS_THAN(WhereClauseParser.LESSTHAN),
NOT_LESS_THAN(WhereClauseParser.LESSTHAN, true),
GREATER_THAN_OR_EQUALS(WhereClauseParser.GREATERTHANOREQUALS),
NOT_GREATER_THAN_OR_EQUALS(WhereClauseParser.GREATERTHANOREQUALS, true),
LESS_THAN_OR_EQUALS(WhereClauseParser.LESSTHANOREQUALS),
NOT_LESS_THAN_OR_EQUALS(WhereClauseParser.LESSTHANOREQUALS, true),
BETWEEN(WhereClauseParser.BETWEEN),
NOT_BETWEEN(WhereClauseParser.BETWEEN, true),
IN(WhereClauseParser.IN),
NOT_IN(WhereClauseParser.IN, true),
MATCHES(WhereClauseParser.MATCHES),
NOT_MATCHES(WhereClauseParser.MATCHES, true),
EXISTS(WhereClauseParser.EXISTS),
NOT_EXISTS(WhereClauseParser.EXISTS, true);
private final int typeNumber;
private final boolean negated;
ClauseType(final int typeNumber)
{
this.typeNumber = typeNumber;
this.negated = false;
}
ClauseType(final int typeNumber, final boolean negated)
{
this.typeNumber = typeNumber;
this.negated = negated;
}
public static ClauseType of(final int type)
{
return of(type, false);
}
public static ClauseType of(final int type, final boolean negated)
{
return Arrays.stream(ClauseType.values())
.filter(clauseType -> clauseType.typeNumber == type && clauseType.negated == negated)
.findFirst()
.orElseThrow();
}
public ClauseType negate()
{
return of(typeNumber, !negated);
}
public int getTypeNumber()
{
return typeNumber;
}
public boolean isNegated()
{
return negated;
}
}
public static class NegatableValuesMap extends HashMap<Boolean, Collection<String>>
{
public Collection<String> skipNegated()
{
return this.get(false);
}
public Collection<String> onlyNegated()
{
return this.get(true);
}
}
public static class MultiTypeNegatableValuesMap extends HashMap<ClauseType, Collection<String>>
{
public Map<Integer, Collection<String>> skipNegated()
{
return this.keySet().stream()
.filter(not(ClauseType::isNegated))
.collect(Collectors.toMap(key -> key.typeNumber, this::get));
}
public Collection<String> skipNegated(final int clauseType)
{
return this.get(ClauseType.of(clauseType));
}
public Map<Integer, Collection<String>> onlyNegated()
{
return this.keySet().stream()
.filter(not(ClauseType::isNegated))
.collect(Collectors.toMap(key -> key.typeNumber, this::get));
}
public Collection<String> onlyNegated(final int clauseType)
{
return this.get(ClauseType.of(clauseType, true));
}
}
}

View File

@@ -1,32 +1,33 @@
/*
* #%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%
*/
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2023 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.workflow.api.impl;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -51,7 +52,7 @@ import org.apache.commons.beanutils.ConvertUtils;
* {@link InvalidArgumentException} is thrown unless the method
* {@link #handleUnmatchedComparison(int, String, String)} returns true (default
* implementation returns false).
*
*
* @author Frederik Heremans
* @author Tijs Rademakers
*/
@@ -72,21 +73,21 @@ public class MapBasedQueryWalker extends WalkerCallbackAdapter
private Map<String, String> equalsProperties;
private Map<String, String> matchesProperties;
private Map<String, String> greaterThanProperties;
private Map<String, String> greaterThanOrEqualProperties;
private Map<String, String> lessThanProperties;
private Map<String, String> lessThanOrEqualProperties;
private List<QueryVariableHolder> variableProperties;
private boolean variablesEnabled;
private NamespaceService namespaceService;
private DictionaryService dictionaryService;
public MapBasedQueryWalker(Set<String> supportedEqualsParameters, Set<String> supportedMatchesParameters)
@@ -132,7 +133,7 @@ public class MapBasedQueryWalker extends WalkerCallbackAdapter
lessThanOrEqualProperties = new HashMap<String, String>();
}
}
public void enableVariablesSupport(NamespaceService namespaceService, DictionaryService dictionaryService)
{
variablesEnabled = true;
@@ -148,7 +149,7 @@ public class MapBasedQueryWalker extends WalkerCallbackAdapter
this.dictionaryService = dictionaryService;
variableProperties = new ArrayList<QueryVariableHolder>();
}
public List<QueryVariableHolder> getVariableProperties() {
return variableProperties;
}
@@ -158,9 +159,9 @@ public class MapBasedQueryWalker extends WalkerCallbackAdapter
{
if(negated)
{
throw new InvalidArgumentException("Cannot use negated matching for property: " + property);
throw new InvalidArgumentException("Cannot use negated matching for property: " + property);
}
if (variablesEnabled && property.startsWith("variables/"))
if (variablesEnabled && property.startsWith("variables/"))
{
processVariable(property, value, WhereClauseParser.MATCHES);
}
@@ -170,19 +171,19 @@ public class MapBasedQueryWalker extends WalkerCallbackAdapter
}
else
{
throw new InvalidArgumentException("Cannot use matching for property: " + property);
throw new InvalidArgumentException("Cannot use matching for property: " + property);
}
}
@Override
public void comparison(int type, String propertyName, String propertyValue, boolean negated)
{
if (variablesEnabled && propertyName.startsWith("variables/"))
if (variablesEnabled && propertyName.startsWith("variables/"))
{
processVariable(propertyName, propertyValue, type);
processVariable(propertyName, propertyValue, type);
return;
}
}
boolean throwError = false;
if (type == WhereClauseParser.EQUALS)
{
@@ -192,7 +193,7 @@ public class MapBasedQueryWalker extends WalkerCallbackAdapter
}
else
{
throwError = !handleUnmatchedComparison(type, propertyName, propertyValue);
throwError = !handleUnmatchedComparison(type, propertyName, propertyValue);
}
}
else if (type == WhereClauseParser.MATCHES)
@@ -203,7 +204,7 @@ public class MapBasedQueryWalker extends WalkerCallbackAdapter
}
else
{
throwError = !handleUnmatchedComparison(type, propertyName, propertyValue);
throwError = !handleUnmatchedComparison(type, propertyName, propertyValue);
}
}
else if (type == WhereClauseParser.GREATERTHAN)
@@ -214,7 +215,7 @@ public class MapBasedQueryWalker extends WalkerCallbackAdapter
}
else
{
throwError = !handleUnmatchedComparison(type, propertyName, propertyValue);
throwError = !handleUnmatchedComparison(type, propertyName, propertyValue);
}
}
else if (type == WhereClauseParser.GREATERTHANOREQUALS)
@@ -225,7 +226,7 @@ public class MapBasedQueryWalker extends WalkerCallbackAdapter
}
else
{
throwError = !handleUnmatchedComparison(type, propertyName, propertyValue);
throwError = !handleUnmatchedComparison(type, propertyName, propertyValue);
}
}
else if (type == WhereClauseParser.LESSTHAN)
@@ -236,7 +237,7 @@ public class MapBasedQueryWalker extends WalkerCallbackAdapter
}
else
{
throwError = !handleUnmatchedComparison(type, propertyName, propertyValue);
throwError = !handleUnmatchedComparison(type, propertyName, propertyValue);
}
}
else if (type == WhereClauseParser.LESSTHANOREQUALS)
@@ -247,7 +248,7 @@ public class MapBasedQueryWalker extends WalkerCallbackAdapter
}
else
{
throwError = !handleUnmatchedComparison(type, propertyName, propertyValue);
throwError = !handleUnmatchedComparison(type, propertyName, propertyValue);
}
}
else
@@ -255,15 +256,24 @@ public class MapBasedQueryWalker extends WalkerCallbackAdapter
throwError = !handleUnmatchedComparison(type, propertyName, propertyValue);
}
if (throwError)
{
throw new InvalidArgumentException("framework.exception.InvalidProperty", new Object[] {propertyName, propertyValue, WhereClauseParser.tokenNames[type]});
}
else if (negated)
{
// Throw error for the unsupported negation only if the property was valid for comparison, show the more meaningful error first.
throw new InvalidArgumentException("Cannot use NOT for " + WhereClauseParser.tokenNames[type] + " comparison.");
if (throwError)
{
throw new InvalidArgumentException("framework.exception.InvalidProperty", new Object[] {propertyName, propertyValue, WhereClauseParser.tokenNames[type]});
}
else if (negated)
{
// Throw error for the unsupported negation only if the property was valid for comparison, show the more meaningful error first.
throw new InvalidArgumentException("Cannot use NOT for " + WhereClauseParser.tokenNames[type] + " comparison.");
}
}
/**
* Get expected value for property and comparison type. This class supports only non-negated comparisons, thus parameter negated is ignored in bellow method.
*/
@Override
public Collection<String> getProperty(String propertyName, int type, boolean negated)
{
return Set.of(this.getProperty(propertyName, type));
}
public String getProperty(String propertyName, int type)
@@ -300,7 +310,7 @@ public class MapBasedQueryWalker extends WalkerCallbackAdapter
/**
* Get the property value, converted to the requested type.
*
*
* @param propertyName name of the parameter
* @param type int
* @param returnType type of object to return
@@ -334,7 +344,7 @@ public class MapBasedQueryWalker extends WalkerCallbackAdapter
{
// Conversion failed, wrap in Illegal
throw new InvalidArgumentException("Query property value for '" + propertyName + "' should be a valid "
+ returnType.getSimpleName());
+ returnType.getSimpleName());
}
}
@@ -345,7 +355,7 @@ public class MapBasedQueryWalker extends WalkerCallbackAdapter
// method indicates that AND is
// supported. OR is not supported at the same time.
}
protected void processVariable(String propertyName, String propertyValue, int type)
{
String localPropertyName = propertyName.replaceFirst("variables/", "");
@@ -353,25 +363,25 @@ public class MapBasedQueryWalker extends WalkerCallbackAdapter
DataTypeDefinition dataTypeDefinition = null;
// variable scope global is default
String scopeDef = "global";
// look for variable scope
if (localPropertyName.contains("local/"))
{
scopeDef = "local";
localPropertyName = localPropertyName.replaceFirst("local/", "");
}
if (localPropertyName.contains("global/"))
{
localPropertyName = localPropertyName.replaceFirst("global/", "");
}
// look for variable type definition
if ((propertyValue.contains("_") || propertyValue.contains(":")) && propertyValue.contains(" "))
if ((propertyValue.contains("_") || propertyValue.contains(":")) && propertyValue.contains(" "))
{
int indexOfSpace = propertyValue.indexOf(' ');
if ((propertyValue.contains("_") && indexOfSpace > propertyValue.indexOf("_")) ||
(propertyValue.contains(":") && indexOfSpace > propertyValue.indexOf(":")))
if ((propertyValue.contains("_") && indexOfSpace > propertyValue.indexOf("_")) ||
(propertyValue.contains(":") && indexOfSpace > propertyValue.indexOf(":")))
{
String typeDef = propertyValue.substring(0, indexOfSpace);
try
@@ -386,7 +396,7 @@ public class MapBasedQueryWalker extends WalkerCallbackAdapter
}
}
}
if (dataTypeDefinition != null && "java.util.Date".equalsIgnoreCase(dataTypeDefinition.getJavaClassName()))
{
// fix for different ISO 8601 Date format classes in Alfresco (org.alfresco.util and Spring Surf)
@@ -396,18 +406,18 @@ public class MapBasedQueryWalker extends WalkerCallbackAdapter
{
actualValue = DefaultTypeConverter.INSTANCE.convert(dataTypeDefinition, propertyValue);
}
else
else
{
actualValue = propertyValue;
}
variableProperties.add(new QueryVariableHolder(localPropertyName, type, actualValue, scopeDef));
}
/**
* Called when unsupported property is encountered or comparison operator
* other than equals.
*
*
* @return true, if the comparison is handles successfully. False, if an
* exception should be thrown because the comparison can't be
* handled.
@@ -416,25 +426,25 @@ public class MapBasedQueryWalker extends WalkerCallbackAdapter
{
return false;
}
public static class QueryVariableHolder implements Serializable
{
private static final long serialVersionUID = 1L;
private String propertyName;
private int operator;
private Object propertyValue;
private String scope;
public QueryVariableHolder() {}
public QueryVariableHolder(String propertyName, int operator, Object propertyValue, String scope) {
this.propertyName = propertyName;
this.operator = operator;
this.propertyValue = propertyValue;
this.scope = scope;
}
public String getPropertyName()
{
return propertyName;

View File

@@ -856,7 +856,9 @@
<bean id="tags" class="org.alfresco.rest.api.impl.TagsImpl">
<property name="nodes" ref="nodes" />
<property name="taggingService" ref="TaggingService" />
<property name="authorityService" ref="AuthorityService" />
<property name="typeConstraint" ref="nodeTypeConstraint" />
<property name="nodeService" ref="NodeService"/>
</bean>
<bean id="Tags" class="org.springframework.aop.framework.ProxyFactoryBean">

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* Copyright (C) 2005 - 2023 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -49,6 +49,8 @@ import org.junit.runners.Suite;
org.alfresco.repo.webdav.WebDAVHelperTest.class,
org.alfresco.repo.webdav.WebDAVLockServiceImplTest.class,
org.alfresco.rest.api.RulesUnitTests.class,
org.alfresco.rest.api.CategoriesUnitTests.class,
org.alfresco.rest.api.TagsUnitTests.class,
org.alfresco.rest.api.impl.ContentStorageInformationImplTest.class,
org.alfresco.rest.api.nodes.NodeStorageInfoRelationTest.class,
org.alfresco.rest.api.search.ResultMapperTests.class,

View File

@@ -0,0 +1,47 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2023 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;
import org.alfresco.rest.api.categories.CategoriesEntityResourceTest;
import org.alfresco.rest.api.categories.NodesCategoryLinksRelationTest;
import org.alfresco.rest.api.categories.SubcategoriesRelationTest;
import org.alfresco.rest.api.impl.CategoriesImplTest;
import org.alfresco.service.Experimental;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@Experimental
@RunWith(Suite.class)
@Suite.SuiteClasses({
CategoriesImplTest.class,
CategoriesEntityResourceTest.class,
SubcategoriesRelationTest.class,
NodesCategoryLinksRelationTest.class
})
public class CategoriesUnitTests
{
}

View File

@@ -0,0 +1,42 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2023 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;
import org.alfresco.rest.api.impl.TagsImplTest;
import org.alfresco.rest.api.tags.TagsEntityResourceTest;
import org.alfresco.service.Experimental;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@Experimental
@RunWith(Suite.class)
@Suite.SuiteClasses({
TagsImplTest.class,
TagsEntityResourceTest.class
})
public class TagsUnitTests
{
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* Copyright (C) 2005 - 2023 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -26,15 +26,21 @@
package org.alfresco.rest.api.categories;
import static org.alfresco.service.cmr.repository.StoreRef.STORE_REF_WORKSPACE_SPACESSTORE;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.BDDMockito.willCallRealMethod;
import java.util.List;
import org.alfresco.rest.api.Categories;
import org.alfresco.rest.api.model.Category;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
@@ -45,6 +51,7 @@ import org.mockito.junit.MockitoJUnitRunner;
public class CategoriesEntityResourceTest
{
private static final String CATEGORY_ID = "category-node-id";
@Mock
private Categories categoriesMock;
@Mock
@@ -58,26 +65,37 @@ public class CategoriesEntityResourceTest
@Test
public void testReadCategoryById()
{
given(categoriesMock.getCategoryById(CATEGORY_ID, parametersMock)).willReturn(categoryMock);
given(categoriesMock.getCategoryById(any(), any())).willCallRealMethod();
given(categoriesMock.getCategoryById(any(), any(), any())).willReturn(categoryMock);
//when
final Category category = objectUnderTest.readById(CATEGORY_ID, parametersMock);
then(categoriesMock).should().getCategoryById(CATEGORY_ID, parametersMock);
then(categoriesMock).shouldHaveNoMoreInteractions();
then(categoriesMock).should().getCategoryById(STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID, parametersMock);
assertEquals(categoryMock, category);
}
@Test
public void testUpdateCategoryById()
{
given(categoriesMock.updateCategoryById(any(), any())).willReturn(categoryMock);
given(categoriesMock.updateCategoryById(any(), any(), any())).willCallRealMethod();
given(categoriesMock.updateCategoryById(any(), any(), any(), any())).willReturn(categoryMock);
// when
final Category actualCategory = objectUnderTest.update(CATEGORY_ID, categoryMock, parametersMock);
then(categoriesMock).should().updateCategoryById(CATEGORY_ID, categoryMock);
then(categoriesMock).shouldHaveNoMoreInteractions();
then(categoriesMock).should().updateCategoryById(STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID, categoryMock, parametersMock);
assertThat(actualCategory).isNotNull();
}
@Test
public void testDeleteCategoryById()
{
willCallRealMethod().given(categoriesMock).deleteCategoryById(any(), any());
// when
objectUnderTest.delete(CATEGORY_ID, parametersMock);
then(categoriesMock).should().deleteCategoryById(STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID, parametersMock);
}
}

View File

@@ -47,6 +47,7 @@ import org.mockito.junit.MockitoJUnitRunner;
public class NodesCategoryLinksRelationTest
{
private static final String CONTENT_ID = "content-node-id";
private static final String CATEGORY_ID = "category-id";
@Mock
private Categories categoriesMock;
@@ -61,12 +62,12 @@ public class NodesCategoryLinksRelationTest
@Test
public void testReadAll()
{
given(categoriesMock.listCategoriesForNode(any())).willReturn(List.of(categoryMock));
given(categoriesMock.listCategoriesForNode(any(), any())).willReturn(List.of(categoryMock));
// when
final CollectionWithPagingInfo<Category> actualCategoriesPage = objectUnderTest.readAll(CONTENT_ID, parametersMock);
then(categoriesMock).should().listCategoriesForNode(CONTENT_ID);
then(categoriesMock).should().listCategoriesForNode(CONTENT_ID, parametersMock);
then(categoriesMock).shouldHaveNoMoreInteractions();
assertThat(actualCategoriesPage)
.isNotNull()
@@ -77,15 +78,25 @@ public class NodesCategoryLinksRelationTest
@Test
public void testCreate()
{
given(categoriesMock.linkNodeToCategories(any(), any())).willReturn(List.of(categoryMock));
given(categoriesMock.linkNodeToCategories(any(), any(), any())).willReturn(List.of(categoryMock));
// when
final List<Category> actualCategories = objectUnderTest.create(CONTENT_ID, List.of(categoryMock), parametersMock);
then(categoriesMock).should().linkNodeToCategories(CONTENT_ID, List.of(categoryMock));
then(categoriesMock).should().linkNodeToCategories(CONTENT_ID, List.of(categoryMock), parametersMock);
then(categoriesMock).shouldHaveNoMoreInteractions();
assertThat(actualCategories)
.isNotNull()
.isEqualTo(List.of(categoryMock));
}
@Test
public void testDelete()
{
// when
objectUnderTest.delete(CONTENT_ID, CATEGORY_ID, parametersMock);
then(categoriesMock).should().unlinkNodeFromCategory(CONTENT_ID, CATEGORY_ID, parametersMock);
then(categoriesMock).shouldHaveNoMoreInteractions();
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* Copyright (C) 2005 - 2023 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -26,7 +26,10 @@
package org.alfresco.rest.api.categories;
import static org.alfresco.service.cmr.repository.StoreRef.STORE_REF_WORKSPACE_SPACESSTORE;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
@@ -37,8 +40,8 @@ import java.util.stream.IntStream;
import org.alfresco.rest.api.Categories;
import org.alfresco.rest.api.model.Category;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Paging;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
@@ -67,39 +70,37 @@ public class SubcategoriesRelationTest
final Category categoryToCreate = Category.builder().name(CATEGORY_NAME).create();
final Category category = Category.builder().name(CATEGORY_NAME).parentId(PARENT_CATEGORY_ID).hasChildren(false).id(CATEGORY_ID).create();
final List<Category> categoriesToCreate = List.of(categoryToCreate);
given(categoriesMock.createSubcategories(PARENT_CATEGORY_ID, categoriesToCreate, parametersMock)).willReturn(List.of(category));
given(categoriesMock.createSubcategories(any(), any(), any())).willCallRealMethod();
given(categoriesMock.createSubcategories(any(), any(), any(), any())).willReturn(List.of(category));
//when
List<Category> categories = objectUnderTest.create(PARENT_CATEGORY_ID, categoriesToCreate, parametersMock);
then(categoriesMock).should().createSubcategories(PARENT_CATEGORY_ID, categoriesToCreate, parametersMock);
then(categoriesMock).shouldHaveNoMoreInteractions();
then(categoriesMock).should().createSubcategories(STORE_REF_WORKSPACE_SPACESSTORE, PARENT_CATEGORY_ID, categoriesToCreate, parametersMock);
assertEquals(List.of(category), categories);
}
@Test
public void testGetCategoryChildren() {
final CollectionWithPagingInfo<Category> categoryChildren = getCategories(3);
given(categoriesMock.getCategoryChildren(PARENT_CATEGORY_ID, parametersMock)).willReturn(categoryChildren);
final List<Category> categoryChildren = getCategories(3);
given(categoriesMock.getCategoryChildren(any(), any())).willCallRealMethod();
given(categoriesMock.getCategoryChildren(any(), any(), any())).willReturn(categoryChildren);
//when
final CollectionWithPagingInfo<Category> returnedChildren = objectUnderTest.readAll(PARENT_CATEGORY_ID, parametersMock);
then(categoriesMock).should().getCategoryChildren(PARENT_CATEGORY_ID, parametersMock);
then(categoriesMock).shouldHaveNoMoreInteractions();
assertEquals(categoryChildren, returnedChildren);
then(categoriesMock).should().getCategoryChildren(STORE_REF_WORKSPACE_SPACESSTORE, PARENT_CATEGORY_ID, parametersMock);
assertEquals(categoryChildren, returnedChildren.getCollection());
}
private CollectionWithPagingInfo<Category> getCategories(final int count)
private List<Category> getCategories(final int count)
{
return CollectionWithPagingInfo.asPaged(Paging.DEFAULT,
IntStream.range(0, count)
.mapToObj(i -> Category.builder().name(SUBCATEGORY_NAME_PREFIX + "-" + i)
.parentId(PARENT_CATEGORY_ID)
.hasChildren(false)
.id(CATEGORY_ID + "-" + i)
.create())
.collect(Collectors.toList())
);
return IntStream.range(0, count)
.mapToObj(i -> Category.builder().name(SUBCATEGORY_NAME_PREFIX + "-" + i)
.parentId(PARENT_CATEGORY_ID)
.hasChildren(false)
.id(CATEGORY_ID + "-" + i)
.create())
.collect(Collectors.toList());
}
}

View File

@@ -27,11 +27,13 @@
package org.alfresco.rest.api.impl;
import static org.alfresco.rest.api.Nodes.PATH_ROOT;
import static org.alfresco.rest.api.impl.CategoriesImpl.INCLUDE_COUNT_PARAM;
import static org.alfresco.rest.api.impl.CategoriesImpl.INVALID_NODE_TYPE;
import static org.alfresco.rest.api.impl.CategoriesImpl.NOT_A_VALID_CATEGORY;
import static org.alfresco.rest.api.impl.CategoriesImpl.NOT_NULL_OR_EMPTY;
import static org.alfresco.rest.api.impl.CategoriesImpl.NO_PERMISSION_TO_CHANGE_CONTENT;
import static org.alfresco.rest.api.impl.CategoriesImpl.NO_PERMISSION_TO_READ_CONTENT;
import static org.alfresco.service.cmr.repository.StoreRef.STORE_REF_WORKSPACE_SPACESSTORE;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.catchThrowable;
@@ -39,6 +41,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
@@ -64,7 +67,6 @@ import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.core.exceptions.InvalidNodeTypeException;
import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -77,6 +79,7 @@ import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.util.Pair;
import org.alfresco.util.TypeConstraint;
import org.apache.commons.lang3.StringUtils;
import org.junit.Before;
@@ -94,7 +97,7 @@ public class CategoriesImplTest
private static final String PARENT_ID = "parent-node-id";
private static final String CAT_ROOT_NODE_ID = "cat-root-node-id";
private static final NodeRef CATEGORY_NODE_REF = createNodeRefWithId(CATEGORY_ID);
private static final Category CATEGORY = createDefaultCategoryWithName(CATEGORY_NAME);
private static final Category CATEGORY = createDefaultCategory();
private static final String CONTENT_NODE_ID = "content-node-id";
private static final NodeRef CONTENT_NODE_REF = createNodeRefWithId(CONTENT_NODE_ID);
@@ -103,8 +106,6 @@ public class CategoriesImplTest
@Mock
private NodeService nodeServiceMock;
@Mock
private Parameters parametersMock;
@Mock
private AuthorityService authorityServiceMock;
@Mock
private CategoryService categoryServiceMock;
@@ -116,6 +117,8 @@ public class CategoriesImplTest
private PermissionService permissionServiceMock;
@Mock
private TypeConstraint typeConstraint;
@Mock
private Parameters parametersMock;
@InjectMocks
private CategoriesImpl objectUnderTest;
@@ -125,7 +128,7 @@ public class CategoriesImplTest
{
given(authorityServiceMock.hasAdminAuthority()).willReturn(true);
given(nodesMock.validateNode(CATEGORY_ID)).willReturn(CATEGORY_NODE_REF);
given(nodesMock.validateNode(CONTENT_NODE_ID)).willReturn(CONTENT_NODE_REF);
given(nodesMock.validateOrLookupNode(CONTENT_NODE_ID)).willReturn(CONTENT_NODE_REF);
given(nodesMock.isSubClass(any(), any(), anyBoolean())).willReturn(true);
given(typeConstraint.matches(any())).willReturn(true);
given(permissionServiceMock.hasReadPermission(any())).willReturn(AccessStatus.ALLOWED);
@@ -135,7 +138,7 @@ public class CategoriesImplTest
@Test
public void shouldNotGetRootCategoryById()
{
final NodeRef categoryRootNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CAT_ROOT_NODE_ID);
final NodeRef categoryRootNodeRef = createNodeRefWithId(CAT_ROOT_NODE_ID);
given(nodesMock.validateNode(CAT_ROOT_NODE_ID)).willReturn(categoryRootNodeRef);
given(categoryChildAssociationRefMock.getQName()).willReturn(ContentModel.ASPECT_GEN_CLASSIFIABLE);
given(nodeServiceMock.getParentAssocs(categoryRootNodeRef)).willReturn(List.of(categoryChildAssociationRefMock));
@@ -155,31 +158,26 @@ public class CategoriesImplTest
@Test
public void testGetCategoryById_withChildren()
{
final NodeRef categoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID);
given(nodesMock.validateNode(CATEGORY_ID)).willReturn(categoryNodeRef);
final Node categoryNode = new Node();
categoryNode.setName(CATEGORY_NAME);
categoryNode.setNodeId(CATEGORY_ID);
final NodeRef parentNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID);
categoryNode.setParentId(parentNodeRef);
final NodeRef parentNodeRef = createNodeRefWithId(PARENT_ID);
final Node categoryNode = createNode(CATEGORY_NAME, CATEGORY_ID, parentNodeRef);
given(nodesMock.getNode(CATEGORY_ID)).willReturn(categoryNode);
final ChildAssociationRef parentAssoc = new ChildAssociationRef(null, parentNodeRef, null, categoryNodeRef);
given(nodeServiceMock.getPrimaryParent(categoryNodeRef)).willReturn(parentAssoc);
final ChildAssociationRef parentAssoc = new ChildAssociationRef(null, parentNodeRef, null, CATEGORY_NODE_REF);
given(nodeServiceMock.getPrimaryParent(CATEGORY_NODE_REF)).willReturn(parentAssoc);
final List<ChildAssociationRef> dummyChildren = List.of(dummyChildAssociationRefMock);
given(nodeServiceMock.getChildAssocs(categoryNodeRef, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false))
given(nodeServiceMock.getChildAssocs(CATEGORY_NODE_REF, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false))
.willReturn(dummyChildren);
//when
final Category category = objectUnderTest.getCategoryById(CATEGORY_ID, parametersMock);
then(nodesMock).should().validateNode(CATEGORY_ID);
then(nodesMock).should().isSubClass(categoryNodeRef, ContentModel.TYPE_CATEGORY, false);
then(nodesMock).should().isSubClass(CATEGORY_NODE_REF, ContentModel.TYPE_CATEGORY, false);
then(nodesMock).should().getNode(CATEGORY_ID);
then(nodesMock).shouldHaveNoMoreInteractions();
then(nodeServiceMock).should().getPrimaryParent(categoryNodeRef);
then(nodeServiceMock).should().getPrimaryParent(CATEGORY_NODE_REF);
then(nodeServiceMock).should().getParentAssocs(parentNodeRef);
then(nodeServiceMock).should().getParentAssocs(categoryNodeRef);
then(nodeServiceMock).should().getChildAssocs(categoryNodeRef, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false);
then(nodeServiceMock).should().getParentAssocs(CATEGORY_NODE_REF);
then(nodeServiceMock).should().getChildAssocs(CATEGORY_NODE_REF, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false);
then(nodeServiceMock).shouldHaveNoMoreInteractions();
then(categoryServiceMock).shouldHaveNoInteractions();
@@ -197,30 +195,25 @@ public class CategoriesImplTest
@Test
public void testGetCategoryById_withoutChildren()
{
final NodeRef categoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID);
given(nodesMock.validateNode(CATEGORY_ID)).willReturn(categoryNodeRef);
final Node categoryNode = new Node();
categoryNode.setName(CATEGORY_NAME);
categoryNode.setNodeId(CATEGORY_ID);
final NodeRef parentNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID);
categoryNode.setParentId(parentNodeRef);
final NodeRef parentNodeRef = createNodeRefWithId(PARENT_ID);
final Node categoryNode = createNode(CATEGORY_NAME, CATEGORY_ID, parentNodeRef);
given(nodesMock.getNode(CATEGORY_ID)).willReturn(categoryNode);
final ChildAssociationRef parentAssoc = new ChildAssociationRef(null, parentNodeRef, null, categoryNodeRef);
given(nodeServiceMock.getPrimaryParent(categoryNodeRef)).willReturn(parentAssoc);
given(nodeServiceMock.getChildAssocs(categoryNodeRef, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false))
final ChildAssociationRef parentAssoc = new ChildAssociationRef(null, parentNodeRef, null, CATEGORY_NODE_REF);
given(nodeServiceMock.getPrimaryParent(CATEGORY_NODE_REF)).willReturn(parentAssoc);
given(nodeServiceMock.getChildAssocs(CATEGORY_NODE_REF, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false))
.willReturn(Collections.emptyList());
//when
final Category category = objectUnderTest.getCategoryById(CATEGORY_ID, parametersMock);
then(nodesMock).should().validateNode(CATEGORY_ID);
then(nodesMock).should().isSubClass(categoryNodeRef, ContentModel.TYPE_CATEGORY, false);
then(nodesMock).should().isSubClass(CATEGORY_NODE_REF, ContentModel.TYPE_CATEGORY, false);
then(nodesMock).should().getNode(CATEGORY_ID);
then(nodesMock).shouldHaveNoMoreInteractions();
then(nodeServiceMock).should().getPrimaryParent(categoryNodeRef);
then(nodeServiceMock).should().getPrimaryParent(CATEGORY_NODE_REF);
then(nodeServiceMock).should().getParentAssocs(parentNodeRef);
then(nodeServiceMock).should().getParentAssocs(categoryNodeRef);
then(nodeServiceMock).should().getChildAssocs(categoryNodeRef, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false);
then(nodeServiceMock).should().getParentAssocs(CATEGORY_NODE_REF);
then(nodeServiceMock).should().getChildAssocs(CATEGORY_NODE_REF, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false);
then(nodeServiceMock).shouldHaveNoMoreInteractions();
then(categoryServiceMock).shouldHaveNoInteractions();
@@ -235,6 +228,30 @@ public class CategoriesImplTest
assertEquals(expectedCategory, category);
}
@Test
public void testGetCategoryById_includeCount()
{
final QName categoryQName = createCmQNameOf(CATEGORY_NAME);
final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID);
final ChildAssociationRef parentAssociation = createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, categoryQName);
given(nodesMock.getNode(any())).willReturn(createNode());
given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation);
given(parametersMock.getInclude()).willReturn(List.of(INCLUDE_COUNT_PARAM));
given(categoryServiceMock.getTopCategories(any(), any(), anyInt())).willReturn(List.of(new Pair<>(CATEGORY_NODE_REF, 1)));
// when
final Category actualCategory = objectUnderTest.getCategoryById(CATEGORY_ID, parametersMock);
then(categoryServiceMock).should().getTopCategories(STORE_REF_WORKSPACE_SPACESSTORE, ContentModel.ASPECT_GEN_CLASSIFIABLE, Integer.MAX_VALUE);
then(categoryServiceMock).shouldHaveNoMoreInteractions();
assertThat(actualCategory)
.isNotNull()
.extracting(Category::getCount)
.isNotNull()
.isEqualTo(1);
}
@Test
public void testGetCategoryById_notACategory()
{
@@ -274,14 +291,9 @@ public class CategoriesImplTest
public void testDeleteCategoryById_asAdmin()
{
given(authorityServiceMock.hasAdminAuthority()).willReturn(true);
final NodeRef categoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID);
final NodeRef categoryNodeRef = createNodeRefWithId(CATEGORY_ID);
given(nodesMock.validateNode(CATEGORY_ID)).willReturn(categoryNodeRef);
given(nodesMock.isSubClass(categoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(true);
final Node categoryNode = new Node();
categoryNode.setName(CATEGORY_NAME);
categoryNode.setNodeId(CATEGORY_ID);
final NodeRef parentNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID);
categoryNode.setParentId(parentNodeRef);
//when
objectUnderTest.deleteCategoryById(CATEGORY_ID, parametersMock);
@@ -317,7 +329,7 @@ public class CategoriesImplTest
public void testDeleteCategoryById_nonCategoryId()
{
given(authorityServiceMock.hasAdminAuthority()).willReturn(true);
final NodeRef categoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID);
final NodeRef categoryNodeRef = createNodeRefWithId(CATEGORY_ID);
given(nodesMock.validateNode(CATEGORY_ID)).willReturn(categoryNodeRef);
given(nodesMock.isSubClass(categoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(false);
@@ -336,7 +348,7 @@ public class CategoriesImplTest
public void testDeleteCategoryById_rootCategory()
{
given(authorityServiceMock.hasAdminAuthority()).willReturn(true);
final NodeRef categoryRootNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CAT_ROOT_NODE_ID);
final NodeRef categoryRootNodeRef = createNodeRefWithId(CAT_ROOT_NODE_ID);
given(nodesMock.validateNode(CAT_ROOT_NODE_ID)).willReturn(categoryRootNodeRef);
given(nodesMock.isSubClass(categoryRootNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(true);
given(categoryChildAssociationRefMock.getQName()).willReturn(ContentModel.ASPECT_GEN_CLASSIFIABLE);
@@ -359,13 +371,13 @@ public class CategoriesImplTest
@Test
public void testCreateCategoryUnderRoot()
{
final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PATH_ROOT);
final NodeRef parentCategoryNodeRef = createNodeRefWithId(PATH_ROOT);
given(categoryServiceMock.getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE))
.willReturn(Optional.of(parentCategoryNodeRef));
final NodeRef categoryNodeRef = createNodeRefWithId(CATEGORY_ID);
given(categoryServiceMock.createCategory(parentCategoryNodeRef, CATEGORY_NAME)).willReturn(categoryNodeRef);
given(nodesMock.getNode(CATEGORY_ID)).willReturn(prepareCategoryNode());
final ChildAssociationRef parentAssoc = new ChildAssociationRef(null, parentCategoryNodeRef, null, categoryNodeRef);
given(nodesMock.getNode(CATEGORY_ID)).willReturn(createNode());
final ChildAssociationRef parentAssoc = createAssociationOf(parentCategoryNodeRef, categoryNodeRef);
given(nodeServiceMock.getPrimaryParent(categoryNodeRef)).willReturn(parentAssoc);
given(nodeServiceMock.getParentAssocs(parentCategoryNodeRef)).willReturn(List.of(parentAssoc));
given(nodeServiceMock.getChildAssocs(categoryNodeRef, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false))
@@ -400,12 +412,12 @@ public class CategoriesImplTest
@Test
public void testCreateCategory()
{
final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID);
final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID);
given(nodesMock.validateNode(PARENT_ID)).willReturn(parentCategoryNodeRef);
final NodeRef categoryNodeRef = createNodeRefWithId(CATEGORY_ID);
given(categoryServiceMock.createCategory(parentCategoryNodeRef, CATEGORY_NAME)).willReturn(categoryNodeRef);
given(nodesMock.getNode(CATEGORY_ID)).willReturn(prepareCategoryNode());
final ChildAssociationRef parentAssoc = new ChildAssociationRef(null, parentCategoryNodeRef, null, categoryNodeRef);
given(nodesMock.getNode(CATEGORY_ID)).willReturn(createNode());
final ChildAssociationRef parentAssoc = createAssociationOf(parentCategoryNodeRef, categoryNodeRef);
given(nodeServiceMock.getPrimaryParent(categoryNodeRef)).willReturn(parentAssoc);
given(nodeServiceMock.getParentAssocs(parentCategoryNodeRef)).willReturn(List.of(parentAssoc));
given(nodeServiceMock.getChildAssocs(categoryNodeRef, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false))
@@ -438,6 +450,35 @@ public class CategoriesImplTest
assertEquals(expectedCategory, createdCategory);
}
@Test
public void testCreateCategory_includeCount()
{
final QName categoryQName = createCmQNameOf(CATEGORY_NAME);
final NodeRef categoryNodeRef = createNodeRefWithId(CATEGORY_ID);
final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID);
final ChildAssociationRef parentAssociation = createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, categoryQName);
given(nodesMock.validateNode(PARENT_ID)).willReturn(parentCategoryNodeRef);
given(categoryServiceMock.createCategory(parentCategoryNodeRef, CATEGORY_NAME)).willReturn(categoryNodeRef);
given(nodesMock.getNode(any())).willReturn(createNode());
given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation);
given(parametersMock.getInclude()).willReturn(List.of(INCLUDE_COUNT_PARAM));
final List<Category> categoryModels = prepareCategories().stream().peek(category -> category.setCount(1)).collect(Collectors.toList());
// when
final List<Category> actualCreatedCategories = objectUnderTest.createSubcategories(PARENT_ID, categoryModels, parametersMock);
then(categoryServiceMock).should().createCategory(any(), any());
then(categoryServiceMock).shouldHaveNoMoreInteractions();
assertThat(actualCreatedCategories)
.isNotNull()
.hasSize(1)
.element(0)
.extracting(Category::getCount)
.isNotNull()
.isEqualTo(0);
}
@Test
public void testCreateCategories_noPermissions()
{
@@ -494,7 +535,7 @@ public class CategoriesImplTest
@Test
public void testGetRootCategoryChildren()
{
final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PATH_ROOT);
final NodeRef parentCategoryNodeRef = createNodeRefWithId(PATH_ROOT);
given(categoryServiceMock.getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE))
.willReturn(Optional.of(parentCategoryNodeRef));
final int childrenCount = 3;
@@ -503,7 +544,7 @@ public class CategoriesImplTest
childAssociationRefMocks.forEach(this::prepareCategoryNodeMocks);
//when
final CollectionWithPagingInfo<Category> categoryChildren = objectUnderTest.getCategoryChildren(PATH_ROOT, parametersMock);
final List<Category> categoryChildren = objectUnderTest.getCategoryChildren(PATH_ROOT, parametersMock);
then(categoryServiceMock).should().getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
then(categoryServiceMock).shouldHaveNoMoreInteractions();
@@ -521,16 +562,14 @@ public class CategoriesImplTest
then(authorityServiceMock).shouldHaveNoInteractions();
assertEquals(childAssociationRefMocks.size(), categoryChildren.getTotalItems().intValue());
final List<Category> categoryChildrenList = new ArrayList<>(categoryChildren.getCollection());
assertEquals(childAssociationRefMocks.size(), categoryChildrenList.size());
IntStream.range(0, childrenCount).forEach(i -> doCategoryAssertions(categoryChildrenList.get(i), i, PATH_ROOT));
assertEquals(childAssociationRefMocks.size(), categoryChildren.size());
IntStream.range(0, childrenCount).forEach(i -> doCategoryAssertions(categoryChildren.get(i), i, PATH_ROOT));
}
@Test
public void testGetCategoryChildren()
{
final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID);
final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID);
given(nodesMock.validateNode(PARENT_ID)).willReturn(parentCategoryNodeRef);
given(nodesMock.isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(true);
final int childrenCount = 3;
@@ -539,7 +578,7 @@ public class CategoriesImplTest
childAssociationRefMocks.forEach(this::prepareCategoryNodeMocks);
//when
final CollectionWithPagingInfo<Category> categoryChildren = objectUnderTest.getCategoryChildren(PARENT_ID, parametersMock);
final List<Category> categoryChildren = objectUnderTest.getCategoryChildren(PARENT_ID, parametersMock);
then(nodesMock).should().validateNode(PARENT_ID);
then(nodesMock).should().isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false);
@@ -558,24 +597,47 @@ public class CategoriesImplTest
then(authorityServiceMock).shouldHaveNoInteractions();
then(categoryServiceMock).shouldHaveNoInteractions();
assertEquals(childAssociationRefMocks.size(), categoryChildren.getTotalItems().intValue());
final List<Category> categoryChildrenList = new ArrayList<>(categoryChildren.getCollection());
assertEquals(childAssociationRefMocks.size(), categoryChildrenList.size());
IntStream.range(0, childrenCount).forEach(i -> doCategoryAssertions(categoryChildrenList.get(i), i, PARENT_ID));
assertEquals(childAssociationRefMocks.size(), categoryChildren.size());
IntStream.range(0, childrenCount).forEach(i -> doCategoryAssertions(categoryChildren.get(i), i, PARENT_ID));
}
@Test
public void testGetCategoryChildren_includeCount()
{
final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID);
given(nodesMock.validateNode(PARENT_ID)).willReturn(parentCategoryNodeRef);
given(nodesMock.isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(true);
final int childrenCount = 3;
final List<ChildAssociationRef> childAssociationRefMocks = prepareChildAssocMocks(childrenCount, parentCategoryNodeRef);
given(nodeServiceMock.getChildAssocs(parentCategoryNodeRef)).willReturn(childAssociationRefMocks);
childAssociationRefMocks.forEach(this::prepareCategoryNodeMocks);
given(parametersMock.getInclude()).willReturn(List.of(INCLUDE_COUNT_PARAM));
given(categoryServiceMock.getTopCategories(any(), any(), anyInt())).willReturn(List.of(new Pair<>(createNodeRefWithId(CATEGORY_ID.concat("-1")), 2)));
// when
final List<Category> actualCategoryChildren = objectUnderTest.getCategoryChildren(PARENT_ID, parametersMock);
then(categoryServiceMock).should().getTopCategories(STORE_REF_WORKSPACE_SPACESSTORE, ContentModel.ASPECT_GEN_CLASSIFIABLE, Integer.MAX_VALUE);
then(categoryServiceMock).shouldHaveNoMoreInteractions();
assertThat(actualCategoryChildren)
.isNotNull()
.hasSize(3)
.extracting(Category::getCount)
.isNotNull()
.isEqualTo(List.of(0, 2, 0));
}
@Test
public void testGetCategoryChildren_noChildren()
{
final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID);
final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID);
given(nodesMock.validateNode(PARENT_ID)).willReturn(parentCategoryNodeRef);
given(nodesMock.isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(true);
given(nodeServiceMock.getChildAssocs(parentCategoryNodeRef)).willReturn(Collections.emptyList());
//when
final CollectionWithPagingInfo<Category> categoryChildren = objectUnderTest.getCategoryChildren(PARENT_ID, parametersMock);
final List<Category> categoryChildren = objectUnderTest.getCategoryChildren(PARENT_ID, parametersMock);
then(nodesMock).should().validateNode(PARENT_ID);
then(nodesMock).should().isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false);
@@ -586,13 +648,13 @@ public class CategoriesImplTest
then(authorityServiceMock).shouldHaveNoInteractions();
then(categoryServiceMock).shouldHaveNoInteractions();
assertEquals(0, categoryChildren.getTotalItems().intValue());
assertEquals(0, categoryChildren.size());
}
@Test
public void testGetCategoryChildren_wrongParentNodeType()
{
final NodeRef parentCategoryNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, PARENT_ID);
final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID);
given(nodesMock.validateNode(PARENT_ID)).willReturn(parentCategoryNodeRef);
given(nodesMock.isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(false);
@@ -633,12 +695,12 @@ public class CategoriesImplTest
final QName categoryQName = createCmQNameOf(CATEGORY_NAME);
final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID);
final ChildAssociationRef parentAssociation = createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, categoryQName);
given(nodesMock.getNode(any())).willReturn(prepareCategoryNode(categoryNewName));
given(nodesMock.getNode(any())).willReturn(createNode(categoryNewName));
given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation);
given(nodeServiceMock.moveNode(any(), any(), any(), any())).willReturn(createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, createCmQNameOf(categoryNewName)));
// when
final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, fixedCategory);
final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, fixedCategory, parametersMock);
then(authorityServiceMock).should().hasAdminAuthority();
then(authorityServiceMock).shouldHaveNoMoreInteractions();
@@ -661,13 +723,41 @@ public class CategoriesImplTest
.isEqualTo(expectedCategory);
}
@Test
public void testUpdateCategoryById_includeCount()
{
final String categoryNewName = "categoryNewName";
final Category fixedCategory = createCategoryOnlyWithName(categoryNewName);
fixedCategory.setCount(9);
final QName categoryQName = createCmQNameOf(CATEGORY_NAME);
final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID);
final ChildAssociationRef parentAssociation = createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, categoryQName);
given(nodesMock.getNode(any())).willReturn(createNode(categoryNewName));
given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation);
given(nodeServiceMock.moveNode(any(), any(), any(), any())).willReturn(createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, createCmQNameOf(categoryNewName)));
given(parametersMock.getInclude()).willReturn(List.of(INCLUDE_COUNT_PARAM));
given(categoryServiceMock.getTopCategories(any(), any(), anyInt())).willReturn(List.of(new Pair<>(CATEGORY_NODE_REF, 1)));
// when
final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, fixedCategory, parametersMock);
then(categoryServiceMock).should().getTopCategories(STORE_REF_WORKSPACE_SPACESSTORE, ContentModel.ASPECT_GEN_CLASSIFIABLE, Integer.MAX_VALUE);
then(categoryServiceMock).shouldHaveNoMoreInteractions();
assertThat(actualCategory)
.isNotNull()
.extracting(Category::getCount)
.isNotNull()
.isEqualTo(1);
}
@Test
public void testUpdateCategoryById_noPermission()
{
given(authorityServiceMock.hasAdminAuthority()).willReturn(false);
// when
assertThatExceptionOfType(PermissionDeniedException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, CATEGORY));
assertThatExceptionOfType(PermissionDeniedException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, CATEGORY, parametersMock));
then(nodesMock).shouldHaveNoInteractions();
then(nodeServiceMock).shouldHaveNoInteractions();
@@ -679,7 +769,7 @@ public class CategoriesImplTest
given(nodesMock.validateNode(any(String.class))).willThrow(EntityNotFoundException.class);
// when
assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, CATEGORY));
assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, CATEGORY, parametersMock));
then(nodeServiceMock).shouldHaveNoInteractions();
}
@@ -690,7 +780,7 @@ public class CategoriesImplTest
given(nodesMock.isSubClass(any(), any(), eq(false))).willReturn(false);
// when
assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, CATEGORY))
assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, CATEGORY, parametersMock))
.withMessageContaining(NOT_A_VALID_CATEGORY);
then(nodeServiceMock).shouldHaveNoInteractions();
@@ -704,7 +794,7 @@ public class CategoriesImplTest
given(categoryChildAssociationRefMock.getQName()).willReturn(ContentModel.ASPECT_GEN_CLASSIFIABLE);
// when
assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(PATH_ROOT, CATEGORY))
assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(PATH_ROOT, CATEGORY, parametersMock))
.withMessageContaining(NOT_A_VALID_CATEGORY);
then(categoryServiceMock).should().getRootCategoryNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
@@ -727,7 +817,7 @@ public class CategoriesImplTest
final Category categoryWithoutName = createCategoryOnlyWithName(invalidName);
// when
assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, categoryWithoutName))
assertThatExceptionOfType(InvalidArgumentException.class).isThrownBy(() -> objectUnderTest.updateCategoryById(CATEGORY_ID, categoryWithoutName, parametersMock))
.withMessageContaining(NOT_NULL_OR_EMPTY);
}
}
@@ -741,12 +831,12 @@ public class CategoriesImplTest
final QName categoryQName = createCmQNameOf(CATEGORY_NAME);
final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID);
final ChildAssociationRef parentAssociation = createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, categoryQName);
given(nodesMock.getNode(any())).willReturn(prepareCategoryNode(categoryNewName));
given(nodesMock.getNode(any())).willReturn(createNode(categoryNewName));
given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation);
given(nodeServiceMock.moveNode(any(), any(), any(), any())).willReturn(createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, createCmQNameOf(categoryNewName)));
// when
final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, categoryWithInvalidId);
final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, categoryWithInvalidId, parametersMock);
final Category expectedCategory = createDefaultCategoryWithName(categoryNewName);
assertThat(actualCategory)
@@ -763,12 +853,12 @@ public class CategoriesImplTest
final QName categoryQName = createCmQNameOf(CATEGORY_NAME);
final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID);
final ChildAssociationRef parentAssociation = createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, categoryQName);
given(nodesMock.getNode(any())).willReturn(prepareCategoryNode(categoryNewName));
given(nodesMock.getNode(any())).willReturn(createNode(categoryNewName));
given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation);
given(nodeServiceMock.moveNode(any(), any(), any(), any())).willReturn(createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, createCmQNameOf(categoryNewName)));
// when
final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, categoryWithInvalidParentId);
final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, categoryWithInvalidParentId, parametersMock);
final Category expectedCategory = createDefaultCategoryWithName(categoryNewName);
assertThat(actualCategory)
@@ -785,12 +875,12 @@ public class CategoriesImplTest
final QName categoryQName = createCmQNameOf(CATEGORY_NAME);
final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID);
final ChildAssociationRef parentAssociation = createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, categoryQName);
given(nodesMock.getNode(any())).willReturn(prepareCategoryNode(categoryNewName));
given(nodesMock.getNode(any())).willReturn(createNode(categoryNewName));
given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation);
given(nodeServiceMock.moveNode(any(), any(), any(), any())).willReturn(createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, createCmQNameOf(categoryNewName)));
// when
final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, categoryWithInvalidHasChildren);
final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, categoryWithInvalidHasChildren, parametersMock);
final Category expectedCategory = createDefaultCategoryWithName(categoryNewName);
assertThat(actualCategory)
@@ -804,13 +894,13 @@ public class CategoriesImplTest
final List<Category> categoryLinks = List.of(CATEGORY);
final NodeRef categoryParentNodeRef = createNodeRefWithId(PARENT_ID);
final ChildAssociationRef parentAssociation = createAssociationOf(categoryParentNodeRef, CATEGORY_NODE_REF);
given(nodesMock.getNode(any())).willReturn(prepareCategoryNode());
given(nodesMock.getNode(any())).willReturn(createNode());
given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation);
// when
final List<Category> actualLinkedCategories = objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, categoryLinks);
final List<Category> actualLinkedCategories = objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, categoryLinks, parametersMock);
then(nodesMock).should().validateNode(CONTENT_NODE_ID);
then(nodesMock).should().validateOrLookupNode(CONTENT_NODE_ID);
then(permissionServiceMock).should().hasPermission(CONTENT_NODE_REF, PermissionService.CHANGE_PERMISSIONS);
then(permissionServiceMock).shouldHaveNoMoreInteractions();
then(typeConstraint).should().matches(CONTENT_NODE_REF);
@@ -838,12 +928,12 @@ public class CategoriesImplTest
{
final NodeRef categoryParentNodeRef = createNodeRefWithId(PARENT_ID);
final ChildAssociationRef parentAssociation = createAssociationOf(categoryParentNodeRef, CATEGORY_NODE_REF);
given(nodesMock.getNode(any())).willReturn(prepareCategoryNode());
given(nodesMock.getNode(any())).willReturn(createNode());
given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation);
given(nodeServiceMock.hasAspect(any(), any())).willReturn(true);
// when
final List<Category> actualLinkedCategories = objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, List.of(CATEGORY));
final List<Category> actualLinkedCategories = objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, List.of(CATEGORY), parametersMock);
then(nodesMock).should().getNode(CATEGORY_ID);
then(nodeServiceMock).should().getChildAssocs(CATEGORY_NODE_REF, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false);
@@ -873,11 +963,11 @@ public class CategoriesImplTest
final ChildAssociationRef categoryParentAssociation = createAssociationOf(categoryParentNodeRef, CATEGORY_NODE_REF);
final ChildAssociationRef secondCategoryParentAssociation = createAssociationOf(categoryParentNodeRef, secondCategoryNodeRef);
given(nodesMock.validateNode(secondCategoryId)).willReturn(secondCategoryNodeRef);
given(nodesMock.getNode(any())).willReturn(prepareCategoryNode(), prepareCategoryNode(secondCategoryName));
given(nodesMock.getNode(any())).willReturn(createNode(), createNode(secondCategoryName));
given(nodeServiceMock.getPrimaryParent(any())).willReturn(categoryParentAssociation, secondCategoryParentAssociation);
// when
final List<Category> actualLinkedCategories = objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, categoryLinks);
final List<Category> actualLinkedCategories = objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, categoryLinks, parametersMock);
then(nodesMock).should().validateNode(CATEGORY_ID);
then(nodesMock).should().validateNode(secondCategoryId);
@@ -902,13 +992,13 @@ public class CategoriesImplTest
final Serializable previousCategories = (Serializable) List.of(otherCategoryNodeRef);
final NodeRef categoryParentNodeRef = createNodeRefWithId(PARENT_ID);
final ChildAssociationRef parentAssociation = createAssociationOf(categoryParentNodeRef, CATEGORY_NODE_REF);
given(nodesMock.getNode(any())).willReturn(prepareCategoryNode());
given(nodesMock.getNode(any())).willReturn(createNode());
given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation);
given(nodeServiceMock.hasAspect(any(), any())).willReturn(true);
given(nodeServiceMock.getProperty(any(), eq(ContentModel.PROP_CATEGORIES))).willReturn(previousCategories);
// when
final List<Category> actualLinkedCategories = objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, List.of(CATEGORY));
final List<Category> actualLinkedCategories = objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, List.of(CATEGORY), parametersMock);
final Serializable expectedCategories = (Serializable) Set.of(otherCategoryNodeRef, CATEGORY_NODE_REF);
then(nodeServiceMock).should().setProperty(CONTENT_NODE_REF, ContentModel.PROP_CATEGORIES, expectedCategories);
@@ -921,12 +1011,12 @@ public class CategoriesImplTest
@Test
public void testLinkNodeToCategories_withInvalidNodeId()
{
given(nodesMock.validateNode(CONTENT_NODE_ID)).willThrow(EntityNotFoundException.class);
given(nodesMock.validateOrLookupNode(CONTENT_NODE_ID)).willThrow(EntityNotFoundException.class);
// when
final Throwable actualException = catchThrowable(() -> objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, List.of(CATEGORY)));
final Throwable actualException = catchThrowable(() -> objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, List.of(CATEGORY), parametersMock));
then(nodesMock).should().validateNode(CONTENT_NODE_ID);
then(nodesMock).should().validateOrLookupNode(CONTENT_NODE_ID);
then(permissionServiceMock).shouldHaveNoInteractions();
then(nodeServiceMock).shouldHaveNoInteractions();
assertThat(actualException)
@@ -939,9 +1029,9 @@ public class CategoriesImplTest
given(permissionServiceMock.hasPermission(any(), any())).willReturn(AccessStatus.DENIED);
// when
final Throwable actualException = catchThrowable(() -> objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, List.of(CATEGORY)));
final Throwable actualException = catchThrowable(() -> objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, List.of(CATEGORY), parametersMock));
then(nodesMock).should().validateNode(CONTENT_NODE_ID);
then(nodesMock).should().validateOrLookupNode(CONTENT_NODE_ID);
then(permissionServiceMock).should().hasPermission(CONTENT_NODE_REF, PermissionService.CHANGE_PERMISSIONS);
then(nodeServiceMock).shouldHaveNoInteractions();
assertThat(actualException)
@@ -954,9 +1044,8 @@ public class CategoriesImplTest
{
given(typeConstraint.matches(any())).willReturn(false);
// when
final Throwable actualException = catchThrowable(() -> objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, List.of(CATEGORY)));
final Throwable actualException = catchThrowable(() -> objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, List.of(CATEGORY), parametersMock));
then(typeConstraint).should().matches(CONTENT_NODE_REF);
then(nodeServiceMock).shouldHaveNoInteractions();
@@ -971,7 +1060,7 @@ public class CategoriesImplTest
final List<Category> categoryLinks = Collections.emptyList();
// when
final Throwable actualException = catchThrowable(() -> objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, categoryLinks));
final Throwable actualException = catchThrowable(() -> objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, categoryLinks, parametersMock));
then(nodesMock).shouldHaveNoInteractions();
then(permissionServiceMock).shouldHaveNoInteractions();
@@ -992,7 +1081,7 @@ public class CategoriesImplTest
categoryLinks.add(categoryLinkWithEmptyId);
// when
final Throwable actualException = catchThrowable(() -> objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, categoryLinks));
final Throwable actualException = catchThrowable(() -> objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, categoryLinks, parametersMock));
then(nodeServiceMock).shouldHaveNoInteractions();
assertThat(actualException)
@@ -1006,11 +1095,11 @@ public class CategoriesImplTest
final List<Category> categoryLinks = List.of(CATEGORY, CATEGORY);
final NodeRef categoryParentNodeRef = createNodeRefWithId(PARENT_ID);
final ChildAssociationRef parentAssociation = createAssociationOf(categoryParentNodeRef, CATEGORY_NODE_REF);
given(nodesMock.getNode(any())).willReturn(prepareCategoryNode());
given(nodesMock.getNode(any())).willReturn(createNode());
given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation);
// when
final List<Category> actualLinkedCategories = objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, categoryLinks);
final List<Category> actualLinkedCategories = objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, categoryLinks, parametersMock);
final Map<QName, Serializable> expectedProperties = Map.of(ContentModel.PROP_CATEGORIES, (Serializable) List.of(CATEGORY_NODE_REF));
then(nodeServiceMock).should().addAspect(CONTENT_NODE_REF, ContentModel.ASPECT_GEN_CLASSIFIABLE, expectedProperties);
@@ -1029,7 +1118,7 @@ public class CategoriesImplTest
objectUnderTest.unlinkNodeFromCategory(CONTENT_NODE_ID, CATEGORY_ID, parametersMock);
then(nodesMock).should().validateNode(CATEGORY_ID);
then(nodesMock).should().validateNode(CONTENT_NODE_ID);
then(nodesMock).should().validateOrLookupNode(CONTENT_NODE_ID);
then(permissionServiceMock).should().hasPermission(CONTENT_NODE_REF, PermissionService.CHANGE_PERMISSIONS);
then(permissionServiceMock).shouldHaveNoMoreInteractions();
then(typeConstraint).should().matches(CONTENT_NODE_REF);
@@ -1045,7 +1134,7 @@ public class CategoriesImplTest
given(nodeServiceMock.hasAspect(CONTENT_NODE_REF, ContentModel.ASPECT_GEN_CLASSIFIABLE)).willReturn(false);
//when
final Throwable actualException = catchThrowable(() -> objectUnderTest.unlinkNodeFromCategory(CONTENT_NODE_ID,CATEGORY_ID, parametersMock));
final Throwable actualException = catchThrowable(() -> objectUnderTest.unlinkNodeFromCategory(CONTENT_NODE_ID, CATEGORY_ID, parametersMock));
then(nodeServiceMock).should().hasAspect(CONTENT_NODE_REF,ContentModel.ASPECT_GEN_CLASSIFIABLE);
then(nodeServiceMock).shouldHaveNoMoreInteractions();
@@ -1060,13 +1149,13 @@ public class CategoriesImplTest
final NodeRef categoryParentNodeRef = createNodeRefWithId(PARENT_ID);
final ChildAssociationRef parentAssociation = createAssociationOf(categoryParentNodeRef, CATEGORY_NODE_REF);
given(nodeServiceMock.getProperty(any(), eq(ContentModel.PROP_CATEGORIES))).willReturn((Serializable) List.of(CATEGORY_NODE_REF));
given(nodesMock.getNode(any())).willReturn(prepareCategoryNode());
given(nodesMock.getNode(any())).willReturn(createNode());
given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation);
// when
final List<Category> actualCategories = objectUnderTest.listCategoriesForNode(CONTENT_NODE_ID);
final List<Category> actualCategories = objectUnderTest.listCategoriesForNode(CONTENT_NODE_ID, parametersMock);
then(nodesMock).should().validateNode(CONTENT_NODE_ID);
then(nodesMock).should().validateOrLookupNode(CONTENT_NODE_ID);
then(permissionServiceMock).should().hasReadPermission(CONTENT_NODE_REF);
then(permissionServiceMock).shouldHaveNoMoreInteractions();
then(typeConstraint).should().matches(CONTENT_NODE_REF);
@@ -1087,12 +1176,12 @@ public class CategoriesImplTest
@Test
public void testListCategoriesForNode_withInvalidNodeId()
{
given(nodesMock.validateNode(CONTENT_NODE_ID)).willThrow(EntityNotFoundException.class);
given(nodesMock.validateOrLookupNode(CONTENT_NODE_ID)).willThrow(EntityNotFoundException.class);
// when
final Throwable actualException = catchThrowable(() -> objectUnderTest.listCategoriesForNode(CONTENT_NODE_ID));
final Throwable actualException = catchThrowable(() -> objectUnderTest.listCategoriesForNode(CONTENT_NODE_ID, parametersMock));
then(nodesMock).should().validateNode(CONTENT_NODE_ID);
then(nodesMock).should().validateOrLookupNode(CONTENT_NODE_ID);
then(nodeServiceMock).shouldHaveNoInteractions();
assertThat(actualException)
.isInstanceOf(EntityNotFoundException.class);
@@ -1104,9 +1193,9 @@ public class CategoriesImplTest
given(permissionServiceMock.hasReadPermission(any())).willReturn(AccessStatus.DENIED);
// when
final Throwable actualException = catchThrowable(() -> objectUnderTest.listCategoriesForNode(CONTENT_NODE_ID));
final Throwable actualException = catchThrowable(() -> objectUnderTest.listCategoriesForNode(CONTENT_NODE_ID, parametersMock));
then(nodesMock).should().validateNode(CONTENT_NODE_ID);
then(nodesMock).should().validateOrLookupNode(CONTENT_NODE_ID);
then(permissionServiceMock).should().hasReadPermission(CONTENT_NODE_REF);
then(nodeServiceMock).shouldHaveNoInteractions();
assertThat(actualException)
@@ -1120,7 +1209,7 @@ public class CategoriesImplTest
given(typeConstraint.matches(any())).willReturn(false);
// when
final Throwable actualException = catchThrowable(() -> objectUnderTest.listCategoriesForNode(CONTENT_NODE_ID));
final Throwable actualException = catchThrowable(() -> objectUnderTest.listCategoriesForNode(CONTENT_NODE_ID, parametersMock));
then(typeConstraint).should().matches(CONTENT_NODE_REF);
then(nodeServiceMock).shouldHaveNoInteractions();
@@ -1136,45 +1225,19 @@ public class CategoriesImplTest
given(nodeServiceMock.getProperty(any(), eq(ContentModel.PROP_CATEGORIES))).willReturn((Serializable) nullOrEmptyList);
// when
final List<Category> actualCategories = objectUnderTest.listCategoriesForNode(CONTENT_NODE_ID);
final List<Category> actualCategories = objectUnderTest.listCategoriesForNode(CONTENT_NODE_ID, parametersMock);
assertThat(actualCategories).isNotNull().isEmpty();
});
}
private Node prepareCategoryNode(final String name, final String id, final NodeRef parentNodeRef)
{
final Node categoryNode = new Node();
categoryNode.setName(name);
categoryNode.setNodeId(id);
categoryNode.setParentId(parentNodeRef);
return categoryNode;
}
private Node prepareCategoryNode(final String name)
{
return prepareCategoryNode(name, CATEGORY_ID, createNodeRefWithId(PARENT_ID));
}
private Node prepareCategoryNode()
{
return prepareCategoryNode(CATEGORY_NAME);
}
private List<Category> prepareCategories()
{
return List.of(Category.builder()
.name(CATEGORY_NAME)
.create());
}
private List<ChildAssociationRef> prepareChildAssocMocks(final int count, NodeRef parentCategoryNodeRef)
{
return IntStream.range(0, count).mapToObj(i -> {
ChildAssociationRef dummyChildAssocMock = mock(ChildAssociationRef.class);
given(dummyChildAssocMock.getTypeQName()).willReturn(ContentModel.ASSOC_SUBCATEGORIES);
given(dummyChildAssocMock.getChildRef())
.willReturn(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, CATEGORY_ID + "-" + i));
.willReturn(createNodeRefWithId(CATEGORY_ID + "-" + i));
given(dummyChildAssocMock.getParentRef()).willReturn(parentCategoryNodeRef);
return dummyChildAssocMock;
}).collect(Collectors.toList());
@@ -1186,8 +1249,8 @@ public class CategoriesImplTest
final String id = childRef.getId();
final String name = id.replace(CATEGORY_ID, CATEGORY_NAME);
final NodeRef parentRef = childAssociationRef.getParentRef();
given(nodesMock.getNode(id)).willReturn(prepareCategoryNode(name, id, parentRef));
final ChildAssociationRef parentAssoc = new ChildAssociationRef(null, parentRef, null, childRef);
given(nodesMock.getNode(id)).willReturn(createNode(name, id, parentRef));
final ChildAssociationRef parentAssoc = createAssociationOf(parentRef, childRef);
given(nodeServiceMock.getPrimaryParent(childRef)).willReturn(parentAssoc);
given(nodeServiceMock.getParentAssocs(parentRef)).willReturn(List.of(parentAssoc));
}
@@ -1195,14 +1258,38 @@ public class CategoriesImplTest
private void doCategoryAssertions(final Category category, final int index, final String parentId)
{
final Category expectedCategory = Category.builder()
.id(CATEGORY_ID + "-" + index)
.name(CATEGORY_NAME + "-" + index)
.parentId(parentId)
.hasChildren(false)
.create();
.id(CATEGORY_ID + "-" + index)
.name(CATEGORY_NAME + "-" + index)
.parentId(parentId)
.hasChildren(false)
.create();
assertEquals(expectedCategory, category);
}
private List<Category> prepareCategories()
{
return List.of(createCategoryOnlyWithName(CATEGORY_NAME));
}
private static Node createNode(final String name, final String id, final NodeRef parentNodeRef)
{
final Node categoryNode = new Node();
categoryNode.setName(name);
categoryNode.setNodeId(id);
categoryNode.setParentId(parentNodeRef);
return categoryNode;
}
private static Node createNode(final String name)
{
return createNode(name, CATEGORY_ID, createNodeRefWithId(PARENT_ID));
}
private static Node createNode()
{
return createNode(CATEGORY_NAME);
}
private static NodeRef createNodeRefWithId(final String id)
{
return new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, id);
@@ -1213,6 +1300,11 @@ public class CategoriesImplTest
return Category.builder().name(name).create();
}
private static Category createDefaultCategory()
{
return createDefaultCategoryWithName(CATEGORY_NAME);
}
private static Category createDefaultCategoryWithName(final String name)
{
return Category.builder()

View File

@@ -0,0 +1,547 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2023 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.stream.Collectors.toList;
import static org.alfresco.rest.api.impl.TagsImpl.NOT_A_VALID_TAG;
import static org.alfresco.rest.api.impl.TagsImpl.NO_PERMISSION_TO_MANAGE_A_TAG;
import static org.alfresco.service.cmr.repository.StoreRef.STORE_REF_WORKSPACE_SPACESSTORE;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.alfresco.query.PagingRequest;
import org.alfresco.query.PagingResults;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.model.Tag;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException;
import org.alfresco.rest.framework.core.exceptions.UnsupportedResourceOperationException;
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.where.InvalidQueryException;
import org.alfresco.rest.framework.tools.RecognizedParamsExtractor;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.tagging.TaggingService;
import org.alfresco.util.Pair;
import org.alfresco.util.TypeConstraint;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class TagsImplTest
{
private static final String TAG_ID = "tag-node-id";
private static final String PARENT_NODE_ID = "tag:tag-root";
private static final String TAG_NAME = "tag-dummy-name";
private static final NodeRef TAG_NODE_REF = new NodeRef(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID.concat("-").concat(TAG_NAME));
private static final NodeRef TAG_PARENT_NODE_REF = new NodeRef(STORE_REF_WORKSPACE_SPACESSTORE, PARENT_NODE_ID);
private static final String CONTENT_NODE_ID = "content-node-id";
private static final NodeRef CONTENT_NODE_REF = new NodeRef(STORE_REF_WORKSPACE_SPACESSTORE, CONTENT_NODE_ID);
private final RecognizedParamsExtractor queryExtractor = new RecognizedParamsExtractor() {};
@Mock
private Nodes nodesMock;
@Mock
private ChildAssociationRef primaryParentMock;
@Mock
private NodeService nodeServiceMock;
@Mock
private AuthorityService authorityServiceMock;
@Mock
private TaggingService taggingServiceMock;
@Mock
private Parameters parametersMock;
@Mock
private Paging pagingMock;
@Mock
private PagingResults<Pair<NodeRef, String>> pagingResultsMock;
@Mock
private TypeConstraint typeConstraintMock;
@InjectMocks
private TagsImpl objectUnderTest;
@Before
public void setup()
{
given(authorityServiceMock.hasAdminAuthority()).willReturn(true);
given(nodesMock.validateNode(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID)).willReturn(TAG_NODE_REF);
given(taggingServiceMock.getTagName(TAG_NODE_REF)).willReturn(TAG_NAME);
given(nodeServiceMock.getPrimaryParent(TAG_NODE_REF)).willReturn(primaryParentMock);
}
@Test
public void testGetTags()
{
given(parametersMock.getPaging()).willReturn(pagingMock);
given(taggingServiceMock.getTags(any(StoreRef.class), any(PagingRequest.class), any(), any())).willReturn(pagingResultsMock);
given(pagingResultsMock.getTotalResultCount()).willReturn(new Pair<>(Integer.MAX_VALUE, 0));
given(pagingResultsMock.getPage()).willReturn(List.of(new Pair<>(TAG_NODE_REF, TAG_NAME)));
final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock);
then(taggingServiceMock).should().getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE), any(PagingRequest.class), isNull(), isNull());
then(taggingServiceMock).shouldHaveNoMoreInteractions();
final List<Tag> expectedTags = createTagsWithNodeRefs(List.of(TAG_NAME));
assertEquals(expectedTags, actualTags.getCollection());
}
@Test
public void testGetTags_verifyIfCountIsZero()
{
given(parametersMock.getPaging()).willReturn(pagingMock);
given(taggingServiceMock.getTags(any(StoreRef.class), any(PagingRequest.class), any(), any())).willReturn(pagingResultsMock);
given(pagingResultsMock.getTotalResultCount()).willReturn(new Pair<>(Integer.MAX_VALUE, 0));
given(pagingResultsMock.getPage()).willReturn(List.of(new Pair<>(TAG_NODE_REF, TAG_NAME)));
given(parametersMock.getInclude()).willReturn(List.of("count"));
final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock);
then(taggingServiceMock).should().findTaggedNodesAndCountByTagName(STORE_REF_WORKSPACE_SPACESSTORE);
final List<Tag> expectedTags = createTagsWithNodeRefs(List.of(TAG_NAME)).stream()
.peek(tag -> tag.setCount(0))
.collect(toList());
assertEquals(expectedTags, actualTags.getCollection());
}
/** Check that we can get counts for two tags - one in use and one not applied to any nodes. */
@Test
public void testGetTags_verifyCountPopulatedCorrectly()
{
NodeRef tagNodeA = new NodeRef("tag://A/");
NodeRef tagNodeB = new NodeRef("tag://B/");
List<Pair<NodeRef, String>> tagPairs = List.of(new Pair<>(tagNodeA, "taga"), new Pair<>(tagNodeB, "tagb"));
given(parametersMock.getPaging()).willReturn(pagingMock);
given(taggingServiceMock.getTags(any(StoreRef.class), any(PagingRequest.class), any(), any())).willReturn(pagingResultsMock);
given(pagingResultsMock.getTotalResultCount()).willReturn(new Pair<>(Integer.MAX_VALUE, 0));
given(pagingResultsMock.getPage()).willReturn(tagPairs);
given(parametersMock.getInclude()).willReturn(List.of("count"));
// Only taga is included in the returned list since tagb is not in use.
given(taggingServiceMock.findTaggedNodesAndCountByTagName(STORE_REF_WORKSPACE_SPACESSTORE)).willReturn(List.of(new Pair<>("taga", 5)));
final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock);
then(taggingServiceMock).should().findTaggedNodesAndCountByTagName(STORE_REF_WORKSPACE_SPACESSTORE);
final List<Tag> expectedTags = List.of(Tag.builder().tag("taga").nodeRef(tagNodeA).count(5).create(),
Tag.builder().tag("tagb").nodeRef(tagNodeB).count(0).create());
assertEquals(expectedTags, actualTags.getCollection());
}
@Test
public void testGetTags_withEqualsClauseWhereQuery()
{
given(parametersMock.getPaging()).willReturn(pagingMock);
given(parametersMock.getQuery()).willReturn(queryExtractor.getWhereClause("(tag=expectedName)"));
given(taggingServiceMock.getTags(any(StoreRef.class), any(PagingRequest.class), any(), any())).willReturn(pagingResultsMock);
given(pagingResultsMock.getTotalResultCount()).willReturn(new Pair<>(Integer.MAX_VALUE, 0));
//when
final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock);
then(taggingServiceMock).should().getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE), any(PagingRequest.class), eq(Set.of("expectedname")), isNull());
then(taggingServiceMock).shouldHaveNoMoreInteractions();
assertThat(actualTags).isNotNull();
}
@Test
public void testGetTags_withInClauseWhereQuery()
{
given(parametersMock.getPaging()).willReturn(pagingMock);
given(parametersMock.getQuery()).willReturn(queryExtractor.getWhereClause("(tag IN (expectedName1, expectedName2))"));
given(taggingServiceMock.getTags(any(StoreRef.class), any(PagingRequest.class), any(), any())).willReturn(pagingResultsMock);
given(pagingResultsMock.getTotalResultCount()).willReturn(new Pair<>(Integer.MAX_VALUE, 0));
//when
final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock);
then(taggingServiceMock).should().getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE), any(PagingRequest.class), eq(Set.of("expectedname1", "expectedname2")), isNull());
then(taggingServiceMock).shouldHaveNoMoreInteractions();
assertThat(actualTags).isNotNull();
}
@Test
public void testGetTags_withMatchesClauseWhereQuery()
{
given(parametersMock.getPaging()).willReturn(pagingMock);
given(parametersMock.getQuery()).willReturn(queryExtractor.getWhereClause("(tag MATCHES ('expectedName*'))"));
given(taggingServiceMock.getTags(any(StoreRef.class), any(PagingRequest.class), any(), any())).willReturn(pagingResultsMock);
given(pagingResultsMock.getTotalResultCount()).willReturn(new Pair<>(Integer.MAX_VALUE, 0));
//when
final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock);
then(taggingServiceMock).should().getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE), any(PagingRequest.class), isNull(), eq(Set.of("expectedname*")));
then(taggingServiceMock).shouldHaveNoMoreInteractions();
assertThat(actualTags).isNotNull();
}
@Test
public void testGetTags_withBothInAndEqualsClausesInSingleWhereQuery()
{
given(parametersMock.getPaging()).willReturn(pagingMock);
given(parametersMock.getQuery()).willReturn(queryExtractor.getWhereClause("(tag=expectedName AND tag IN (expectedName1, expectedName2))"));
//when
final Throwable actualException = catchThrowable(() -> objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock));
then(taggingServiceMock).shouldHaveNoInteractions();
assertThat(actualException).isInstanceOf(InvalidQueryException.class);
}
@Test
public void testGetTags_withOtherClauseInWhereQuery()
{
given(parametersMock.getPaging()).willReturn(pagingMock);
given(parametersMock.getQuery()).willReturn(queryExtractor.getWhereClause("(tag BETWEEN ('expectedName', 'expectedName2'))"));
//when
final Throwable actualException = catchThrowable(() -> objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock));
then(taggingServiceMock).shouldHaveNoInteractions();
assertThat(actualException).isInstanceOf(InvalidQueryException.class);
}
@Test
public void testGetTags_withNotEqualsClauseInWhereQuery()
{
given(parametersMock.getPaging()).willReturn(pagingMock);
given(parametersMock.getQuery()).willReturn(queryExtractor.getWhereClause("(NOT tag=expectedName)"));
//when
final Throwable actualException = catchThrowable(() -> objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock));
then(taggingServiceMock).shouldHaveNoInteractions();
assertThat(actualException).isInstanceOf(InvalidQueryException.class);
}
@Test
public void testDeleteTagById()
{
//when
given(primaryParentMock.getParentRef()).willReturn(TAG_PARENT_NODE_REF);
objectUnderTest.deleteTagById(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID);
then(authorityServiceMock).should().hasAdminAuthority();
then(authorityServiceMock).shouldHaveNoMoreInteractions();
then(nodesMock).should().validateNode(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID);
then(nodesMock).shouldHaveNoMoreInteractions();
then(taggingServiceMock).should().getTagName(TAG_NODE_REF);
then(taggingServiceMock).should().deleteTag(STORE_REF_WORKSPACE_SPACESSTORE, TAG_NAME);
then(taggingServiceMock).shouldHaveNoMoreInteractions();
}
@Test
public void testDeleteTagById_asNonAdminUser()
{
given(authorityServiceMock.hasAdminAuthority()).willReturn(false);
//when
assertThrows(PermissionDeniedException.class, () -> objectUnderTest.deleteTagById(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID));
then(authorityServiceMock).should().hasAdminAuthority();
then(authorityServiceMock).shouldHaveNoMoreInteractions();
then(nodesMock).shouldHaveNoInteractions();
then(taggingServiceMock).shouldHaveNoInteractions();
}
@Test
public void testDeleteTagById_nonExistentTag()
{
//when
assertThrows(EntityNotFoundException.class, () -> objectUnderTest.deleteTagById(STORE_REF_WORKSPACE_SPACESSTORE, "dummy-id"));
then(authorityServiceMock).should().hasAdminAuthority();
then(authorityServiceMock).shouldHaveNoMoreInteractions();
then(nodesMock).should().validateNode(STORE_REF_WORKSPACE_SPACESSTORE, "dummy-id");
then(nodesMock).shouldHaveNoMoreInteractions();
then(taggingServiceMock).shouldHaveNoInteractions();
}
@Test
public void testCreateTags()
{
final List<String> tagNames = List.of("tag1", "99gat");
final List<Tag> tagsToCreate = createTags(tagNames);
given(taggingServiceMock.createTags(any(), any())).willAnswer(invocation -> createTagAndNodeRefPairs(invocation.getArgument(1)));
//when
final List<Tag> actualCreatedTags = objectUnderTest.createTags(tagsToCreate, parametersMock);
then(authorityServiceMock).should().hasAdminAuthority();
then(authorityServiceMock).shouldHaveNoMoreInteractions();
then(taggingServiceMock).should().createTags(STORE_REF_WORKSPACE_SPACESSTORE, tagNames);
then(taggingServiceMock).shouldHaveNoMoreInteractions();
final List<Tag> expectedTags = createTagsWithNodeRefs(tagNames);
assertThat(actualCreatedTags)
.isNotNull().usingRecursiveComparison()
.isEqualTo(expectedTags);
}
@Test
public void testCreateTags_withoutPermission()
{
given(authorityServiceMock.hasAdminAuthority()).willReturn(false);
//when
final Throwable actualException = catchThrowable(() -> objectUnderTest.createTags(List.of(createTag(TAG_NAME)), parametersMock));
then(authorityServiceMock).should().hasAdminAuthority();
then(authorityServiceMock).shouldHaveNoMoreInteractions();
then(taggingServiceMock).shouldHaveNoInteractions();
assertThat(actualException)
.isInstanceOf(PermissionDeniedException.class)
.hasMessageContaining(NO_PERMISSION_TO_MANAGE_A_TAG);
}
@Test
public void testCreateTags_passingNullInsteadList()
{
//when
final Throwable actualException = catchThrowable(() -> objectUnderTest.createTags(null, parametersMock));
then(taggingServiceMock).shouldHaveNoInteractions();
assertThat(actualException)
.isInstanceOf(InvalidArgumentException.class)
.hasMessageContaining(NOT_A_VALID_TAG);
}
@Test
public void testCreateTags_passingEmptyList()
{
//when
final Throwable actualException = catchThrowable(() -> objectUnderTest.createTags(Collections.emptyList(), parametersMock));
then(taggingServiceMock).shouldHaveNoInteractions();
assertThat(actualException)
.isInstanceOf(InvalidArgumentException.class)
.hasMessageContaining(NOT_A_VALID_TAG);
}
@Test
public void testCreateTags_passingListOfNulls()
{
//when
final Throwable actualException = catchThrowable(() -> objectUnderTest.createTags(Collections.singletonList(null), parametersMock));
then(taggingServiceMock).shouldHaveNoInteractions();
assertThat(actualException)
.isInstanceOf(InvalidArgumentException.class)
.hasMessageContaining(NOT_A_VALID_TAG);
}
@Test
public void testCreateTags_whileTagAlreadyExists()
{
given(taggingServiceMock.createTags(any(), any())).willThrow(new DuplicateChildNodeNameException(null, null, TAG_NAME, null));
//when
final Throwable actualException = catchThrowable(() -> objectUnderTest.createTags(List.of(createTag(TAG_NAME)), parametersMock));
then(taggingServiceMock).should().createTags(STORE_REF_WORKSPACE_SPACESSTORE, List.of(TAG_NAME));
then(taggingServiceMock).shouldHaveNoMoreInteractions();
assertThat(actualException).isInstanceOf(DuplicateChildNodeNameException.class);
}
@Test
public void testCreateTags_withRepeatedTagName()
{
final List<String> tagNames = List.of(TAG_NAME, TAG_NAME);
final List<Tag> tagsToCreate = createTags(tagNames);
given(taggingServiceMock.createTags(any(), any())).willAnswer(invocation -> createTagAndNodeRefPairs(invocation.getArgument(1)));
//when
final List<Tag> actualCreatedTags = objectUnderTest.createTags(tagsToCreate, parametersMock);
then(taggingServiceMock).should().createTags(STORE_REF_WORKSPACE_SPACESSTORE, List.of(TAG_NAME));
final List<Tag> expectedTags = List.of(createTagWithNodeRef(TAG_NAME));
assertThat(actualCreatedTags)
.isNotNull()
.isEqualTo(expectedTags);
}
@Test
public void testCreateTags_includingCount()
{
final List<String> tagNames = List.of("tag1", "99gat");
final List<Tag> tagsToCreate = createTags(tagNames);
given(taggingServiceMock.createTags(any(), any())).willAnswer(invocation -> createTagAndNodeRefPairs(invocation.getArgument(1)));
given(parametersMock.getInclude()).willReturn(List.of("count"));
//when
final List<Tag> actualCreatedTags = objectUnderTest.createTags(tagsToCreate, parametersMock);
final List<Tag> expectedTags = createTagsWithNodeRefs(tagNames).stream()
.peek(tag -> tag.setCount(0))
.collect(toList());
assertThat(actualCreatedTags)
.isNotNull()
.isEqualTo(expectedTags);
}
@Test(expected = EntityNotFoundException.class)
public void testGetTagByIdNotFoundValidation()
{
given(primaryParentMock.getParentRef()).willReturn(TAG_NODE_REF);
objectUnderTest.getTag(STORE_REF_WORKSPACE_SPACESSTORE,TAG_ID);
then(nodeServiceMock).shouldHaveNoInteractions();
then(nodesMock).should().validateNode(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID);
then(nodesMock).shouldHaveNoMoreInteractions();
then(taggingServiceMock).shouldHaveNoInteractions();
}
@Test
public void testAddTags()
{
NodeRef tagNodeA = new NodeRef("tag://A/");
NodeRef tagNodeB = new NodeRef("tag://B/");
given(nodesMock.validateOrLookupNode(CONTENT_NODE_ID)).willReturn(CONTENT_NODE_REF);
given(typeConstraintMock.matches(CONTENT_NODE_REF)).willReturn(true);
List<Pair<String, NodeRef>> pairs = List.of(new Pair<>("taga", new NodeRef("tag://A/")), new Pair<>("tagb", new NodeRef("tag://B/")));
List<String> tagNames = pairs.stream().map(Pair::getFirst).collect(toList());
List<Tag> tags = tagNames.stream().map(name -> Tag.builder().tag(name).create()).collect(toList());
given(taggingServiceMock.addTags(CONTENT_NODE_REF, tagNames)).willReturn(pairs);
given(taggingServiceMock.findTaggedNodesAndCountByTagName(STORE_REF_WORKSPACE_SPACESSTORE)).willReturn(List.of(new Pair<>("taga", 4)));
given(parametersMock.getInclude()).willReturn(List.of("count"));
List<Tag> actual = objectUnderTest.addTags(CONTENT_NODE_ID, tags, parametersMock);
final List<Tag> expected = List.of(Tag.builder().tag("taga").nodeRef(tagNodeA).count(5).create(),
Tag.builder().tag("tagb").nodeRef(tagNodeB).count(1).create());
assertEquals("Unexpected tags returned.", expected, actual);
}
@Test(expected = InvalidArgumentException.class)
public void testAddTagsToInvalidNode()
{
given(nodesMock.validateOrLookupNode(CONTENT_NODE_ID)).willThrow(new InvalidArgumentException());
List<Tag> tags = List.of(Tag.builder().tag("tag1").create());
objectUnderTest.addTags(CONTENT_NODE_ID, tags, parametersMock);
}
@Test(expected = UnsupportedResourceOperationException.class)
public void testAddTagsToWrongTypeOfNode()
{
given(nodesMock.validateOrLookupNode(CONTENT_NODE_ID)).willReturn(CONTENT_NODE_REF);
given(typeConstraintMock.matches(CONTENT_NODE_REF)).willReturn(false);
List<Tag> tags = List.of(Tag.builder().tag("tag1").create());
objectUnderTest.addTags(CONTENT_NODE_ID, tags, parametersMock);
}
@Test
public void testGetTagsForNode()
{
given(nodesMock.validateOrLookupNode(CONTENT_NODE_ID)).willReturn(CONTENT_NODE_REF);
given(parametersMock.getPaging()).willReturn(pagingMock);
List<Pair<NodeRef, String>> pairs = List.of(new Pair<>(new NodeRef("tag://A/"), "taga"), new Pair<>(new NodeRef("tag://B/"), "tagb"));
given(taggingServiceMock.getTags(eq(CONTENT_NODE_REF), any(PagingRequest.class))).willReturn(pagingResultsMock);
given(pagingResultsMock.getTotalResultCount()).willReturn(new Pair<>(null, null));
given(pagingResultsMock.getPage()).willReturn(pairs);
CollectionWithPagingInfo<Tag> actual = objectUnderTest.getTags(CONTENT_NODE_ID, parametersMock);
List<Tag> tags = pairs.stream().map(pair -> Tag.builder().tag(pair.getSecond()).nodeRef(pair.getFirst()).create()).collect(toList());
assertEquals(actual.getCollection(), tags);
}
@Test (expected = InvalidArgumentException.class)
public void testGetTagsFromInvalidNode()
{
given(nodesMock.validateOrLookupNode(CONTENT_NODE_ID)).willThrow(new InvalidArgumentException());
objectUnderTest.getTags(CONTENT_NODE_ID, parametersMock);
}
private static List<Pair<String, NodeRef>> createTagAndNodeRefPairs(final List<String> tagNames)
{
return tagNames.stream()
.map(tagName -> createPair(tagName, new NodeRef(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID.concat("-").concat(tagName))))
.collect(toList());
}
private static Pair<String, NodeRef> createPair(final String tagName, final NodeRef nodeRef)
{
return new Pair<>(tagName, nodeRef);
}
private static List<Tag> createTags(final List<String> tagNames)
{
return tagNames.stream().map(TagsImplTest::createTag).collect(toList());
}
private static List<Tag> createTagsWithNodeRefs(final List<String> tagNames)
{
return tagNames.stream().map(TagsImplTest::createTagWithNodeRef).collect(toList());
}
private static Tag createTag(final String tagName)
{
return Tag.builder()
.tag(tagName)
.create();
}
private static Tag createTagWithNodeRef(final String tagName)
{
return Tag.builder()
.nodeRef(new NodeRef(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID.concat("-").concat(tagName)))
.tag(tagName)
.create();
}
}

View File

@@ -134,7 +134,7 @@ public class RestRuleModelMapperTest
// when
final org.alfresco.service.cmr.rule.Rule actualRuleModel = objectUnderTest.toServiceModel(rule);
then(nodesMock).should().validateOrLookupNode(RULE_ID, null);
then(nodesMock).should().validateOrLookupNode(RULE_ID);
then(nodesMock).shouldHaveNoMoreInteractions();
then(actionMapperMock).should().toServiceModel(List.of(action));
then(actionMapperMock).shouldHaveNoMoreInteractions();

Some files were not shown because too many files have changed in this diff Show More